mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-05-29 21:11:50 +00:00
LLM integration - LiteLLM config - UI tweaks (#4134)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
Binary file not shown.
@@ -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"
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
Reference in New Issue
Block a user