fix(agent): purge les api_base LLM obsoletes (Mistral/Cohere /v2)
FlowDeck CI / lint (push) Successful in 53s
FlowDeck CI / test (push) Successful in 5m46s
FlowDeck CI / docker (push) Successful in 48s

Un api_base stocke (ex. https://api.mistral.ai/v2) ecrasait l'URL par defaut corrigee et faisait echouer le test de connexion. Desormais: normalisation a l'ecriture (une base egale au defaut n'est pas stockee), possibilite de vider le champ (api_base='' vs None), et migration 14 qui efface les bases obsoletes/redondantes dans user_llm_keys et llm_config.

Tests de non-regression ajoutes.
This commit is contained in:
2026-09-14 23:04:39 -04:00
parent a0db4d6e65
commit 88e4ae1db8
4 changed files with 123 additions and 6 deletions
+35 -5
View File
@@ -101,8 +101,12 @@ def set_llm_config(*, provider: str | None = None, model: str | None = None,
cfg["verified"] = 0
cfg["verified_model"] = ""
cfg["last_error"] = ""
if api_base is not None and api_base.strip():
cfg["api_base"] = api_base.strip()
if api_base is not None:
# Normalise so a base equal to the provider default is stored as "use
# default" — a later correction of PROVIDERS then applies automatically.
cfg["api_base"] = _normalize_api_base(
provider or cfg.get("provider") or "offline", api_base
)
if clear_keys:
cfg["api_key"] = ""
cfg["api_base"] = ""
@@ -222,18 +226,44 @@ def list_user_llm_keys(user_id: int) -> list[dict]:
return [_mask_key(r) for r in rows]
def _default_api_base(provider: str) -> str:
"""The OpenAI-compatible base URL the app uses by default for a provider."""
base = (PROVIDERS.get(provider.lower()) or (None, None))[0]
return (base or "").rstrip("/")
def _normalize_api_base(provider: str, api_base: str) -> str:
"""Drop an api_base that just repeats the provider default.
Storing the default as a per-user override freezes it: a later correction
of the provider URL (e.g. Cohere `/v2` → `/compatibility/v1`) would never
apply. An empty value means "use the provider default".
"""
value = (api_base or "").strip()
if value.rstrip("/") == _default_api_base(provider):
return ""
return value
def upsert_user_llm_key(user_id: int, provider: str, *, api_key: str = "",
api_base: str = "", default_model: str = "",
api_base: str | None = None, default_model: str = "",
models: list[str] | None = None) -> dict:
"""Upsert a user's provider key. Empty api_key keeps the existing one
(allows saving model/base without re-typing the key). Saving a *different*
key resets the `verified` flag so the provider must pass a test again."""
key resets the `verified` flag so the provider must pass a test again.
``api_base`` uses ``None`` to mean "keep the stored value" and an empty
string to explicitly reset it to the provider default.
"""
from app.db import get_conn
provider = provider.lower()
existing = get_user_llm_key(user_id, provider)
new_key = api_key if api_key else (existing.get("api_key", "") if existing else "")
new_base = api_base if api_base else (existing.get("api_base", "") if existing else "")
if api_base is None:
new_base = (existing.get("api_base", "") if existing else "")
else:
new_base = _normalize_api_base(provider, api_base)
new_model = default_model if default_model else (existing.get("default_model", "") if existing else "")
new_models = models if models is not None else (
json.loads(existing["models_json"]) if existing and existing.get("models_json") else []