LLM integration - LiteLLM config - UI tweaks (#4134)

This commit is contained in:
dgtlmoon
2026-05-12 19:33:11 +10:00
committed by GitHub
parent 972d1206e8
commit a2fa9a9e7b
19 changed files with 79 additions and 85 deletions
+61 -6
View File
@@ -1,4 +1,7 @@
import json
import logging
import os
import re
from flask import Blueprint, jsonify, redirect, url_for, flash
from flask_babel import gettext
@@ -8,6 +11,44 @@ from changedetectionio.store import ChangeDetectionStore
from changedetectionio.auth_decorator import login_optionally_required
class _LiteLLMWarningCapture(logging.Handler):
"""Capture warnings emitted on the 'LiteLLM' stdlib logger during a single call.
litellm.get_valid_models() catches HTTP/auth errors internally, logs a warning,
and returns []. Without capturing that warning we can't tell the user *why*
no models came back (bad key vs. offline vs. genuinely empty model list).
"""
def __init__(self):
super().__init__(level=logging.WARNING)
self.messages = []
def emit(self, record):
try:
self.messages.append(record.getMessage())
except Exception:
pass
def _humanize_litellm_error(raw: str) -> str:
# litellm warnings typically look like:
# "Error getting valid models: Failed to get models: { 'error': { 'message': '...' } }"
# Pull the inner provider message when present; otherwise trim the boilerplate.
if not raw:
return raw
m = re.search(r'\{.*\}', raw, re.DOTALL)
if m:
try:
body = json.loads(m.group(0))
inner = (body.get('error') or {}).get('message') or body.get('message')
if inner:
return inner
except Exception:
pass
cleaned = re.sub(r'^Error getting valid models:\s*', '', raw)
cleaned = re.sub(r'^Failed to get models:\s*', '', cleaned).strip()
return cleaned[:500]
def construct_llm_blueprint(datastore: ChangeDetectionStore):
llm_blueprint = Blueprint('llm', __name__)
@@ -41,13 +82,27 @@ def construct_llm_blueprint(datastore: ChangeDetectionStore):
try:
import litellm
logger.debug(f"LLM model list: calling litellm.get_valid_models provider={provider!r} (litellm={litellm_provider!r}) api_base={api_base!r}")
raw = litellm.get_valid_models(
check_provider_endpoint=True,
custom_llm_provider=litellm_provider,
api_key=api_key or None,
api_base=api_base or None,
) or []
capture = _LiteLLMWarningCapture()
litellm_logger = logging.getLogger('LiteLLM')
litellm_logger.addHandler(capture)
try:
raw = litellm.get_valid_models(
check_provider_endpoint=True,
custom_llm_provider=litellm_provider,
api_key=api_key or None,
api_base=api_base or None,
) or []
finally:
litellm_logger.removeHandler(capture)
models = sorted({(m if m.startswith(prefix) else prefix + m) for m in raw})
if not models and capture.messages:
err = _humanize_litellm_error(capture.messages[-1])
logger.debug(f"LLM model list: 0 models, surfacing captured litellm warning: {err!r}")
return jsonify({'models': [], 'error': err}), 400
logger.debug(f"LLM model list: got {len(models)} models for provider={provider!r}")
return jsonify({'models': models, 'error': None})
except Exception as e:
@@ -469,7 +469,7 @@
if (!data.models || data.models.length === 0) {
statusEl.style.color = '#e67e22';
statusEl.textContent = '{{ _("No models returned — check your API key.") }}';
statusEl.textContent = '{{ _("No models returned by the provider.") }}';
selGroup.style.display = 'none';
return;
}
-1
View File
@@ -1106,7 +1106,6 @@ class globalSettingsLLMForm(Form):
_l('API Key'),
validators=[validators.Optional()],
render_kw={
"placeholder": _l('Leave blank to use LITELLM_API_KEY env var'),
"autocomplete": "off",
"style": "width: 24em;",
},
@@ -1121,7 +1121,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3134,10 +3134,6 @@ msgstr ""
msgid "API Key"
msgstr "API klíč"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1137,7 +1137,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3186,10 +3186,6 @@ msgstr ""
msgid "API Key"
msgstr "API-Schlüssel"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1119,7 +1119,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3128,10 +3128,6 @@ msgstr ""
msgid "API Key"
msgstr ""
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1119,7 +1119,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3128,10 +3128,6 @@ msgstr ""
msgid "API Key"
msgstr ""
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1157,7 +1157,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3201,10 +3201,6 @@ msgstr ""
msgid "API Key"
msgstr "Clave API"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1125,7 +1125,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3141,10 +3141,6 @@ msgstr ""
msgid "API Key"
msgstr "Clé API"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1121,7 +1121,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3130,10 +3130,6 @@ msgstr ""
msgid "API Key"
msgstr "Chiave API"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1126,7 +1126,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3147,10 +3147,6 @@ msgstr ""
msgid "API Key"
msgstr "APIキー"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1127,8 +1127,8 @@ msgid "Loading…"
msgstr "불러오는 중..."
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgstr "반환된 모델이 없습니다. API 키를 확인하세요."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "— choose a model —"
@@ -3138,10 +3138,6 @@ msgstr "모델"
msgid "API Key"
msgstr "API 키"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr "LITELLM_API_KEY 환경 변수를 사용하려면 비워 두세요"
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr "API 기본 URL"
+2 -6
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: changedetection.io 0.55.3\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-05-02 18:29+0900\n"
"POT-Creation-Date: 2026-05-12 11:08+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1118,7 +1118,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3127,10 +3127,6 @@ msgstr ""
msgid "API Key"
msgstr ""
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1144,7 +1144,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3178,10 +3178,6 @@ msgstr ""
msgid "API Key"
msgstr "Chave da API"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1154,7 +1154,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3181,10 +3181,6 @@ msgstr ""
msgid "API Key"
msgstr "API Anahtarı"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1134,7 +1134,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3160,10 +3160,6 @@ msgstr ""
msgid "API Key"
msgstr "Ключ API"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1123,7 +1123,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3133,10 +3133,6 @@ msgstr ""
msgid "API Key"
msgstr "API密钥"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""
@@ -1122,7 +1122,7 @@ msgid "Loading…"
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
msgid "No models returned — check your API key."
msgid "No models returned by the provider."
msgstr ""
#: changedetectionio/blueprint/settings/templates/settings_llm_tab.html
@@ -3132,10 +3132,6 @@ msgstr ""
msgid "API Key"
msgstr "API 金鑰"
#: changedetectionio/forms.py
msgid "Leave blank to use LITELLM_API_KEY env var"
msgstr ""
#: changedetectionio/forms.py
msgid "API Base URL"
msgstr ""