fix(agent): validation robuste des modeles Nvidia (payload production + repli 404/410) + logo noir/blanc
Validation: - _validate_chat_models utilise le payload de production (temperature 0.2, sans max_tokens) : un 200 a la validation == 200 a l'usage reel. - Race corrigee : payload reconstruit par modele (plus de noms croises entre requetes concurrentes) — la v5.1.8 retombait par intermittence sur la liste brute (68 modeles dont la plupart en 404/410). - Retente une fois sur timeout/5xx (les modeles lents mais fonctionnels survivent) ; rejette les 4xx (404 inconnu, 410 retire) ; garde les 429. - Verifie en live contre l'API NVIDIA : 12 modeles valides au lieu de 68. Repli runtime: - LLMClient._http_complete : sur 404/410, retente une fois avec le modele par defaut du provider et marque un notice dans LLMResponse. - AgentEngine emet un evenement SSE "notice" (bandeau .fd-ap-notice dans le panneau Agent, reset a chaque conversation) ; le modele reel est persistee. UI: - FAB (rond bas droite) et logo header du panneau : rond noir/blanc qui suit le theme clair/sombre (--text-primary / --bg-primary), eclair monochrome SVG a la place de l'emoji robot. Tests: - 2 nouveaux tests (429 garde / 410 retire ; repli runtime sur 410). - 278 tests passent.
This commit is contained in:
@@ -85,6 +85,7 @@ class LLMResponse:
|
||||
tool_calls: list[dict] = field(default_factory=list)
|
||||
model: str = ""
|
||||
usage: dict = field(default_factory=dict)
|
||||
notice: str = ""
|
||||
|
||||
|
||||
class LLMClient:
|
||||
@@ -161,7 +162,7 @@ class LLMClient:
|
||||
def _endpoint(self) -> str:
|
||||
return f"{self.api_base.rstrip('/')}/chat/completions"
|
||||
|
||||
async def _http_complete(self, messages, model, tools) -> LLMResponse:
|
||||
async def _http_complete(self, messages, model, tools, *, _noticer: str = "") -> LLMResponse:
|
||||
payload: dict = {
|
||||
"model": model,
|
||||
"messages": messages,
|
||||
@@ -173,11 +174,34 @@ class LLMClient:
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if self.api_key:
|
||||
headers["Authorization"] = f"Bearer {self.api_key}"
|
||||
async with httpx.AsyncClient(timeout=settings.agent_run_timeout_seconds) as client:
|
||||
resp = await client.post(self._endpoint(), json=payload, headers=headers)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=settings.agent_run_timeout_seconds) as client:
|
||||
resp = await client.post(self._endpoint(), json=payload, headers=headers)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
except httpx.HTTPStatusError as exc:
|
||||
# Repli robuste : le modèle choisi a été retiré / n'existe plus
|
||||
# (404 « model not found » / 410 « has reached its end of life »).
|
||||
# Au lieu d'échouer, on retente UNE fois avec le modèle par défaut du
|
||||
# provider et on signale le basculement — la liste validée peut avoir
|
||||
# vieilli (modèle déprécié entre deux rafraîchissements).
|
||||
if exc.response.status_code in (404, 410) \
|
||||
and model and self.default_model and model != self.default_model:
|
||||
logger.warning(
|
||||
"Model '%s' unavailable (%s) on %s — retrying with default '%s'",
|
||||
model, exc.response.status_code, self.provider, self.default_model,
|
||||
)
|
||||
return await self._http_complete(messages, self.default_model, tools, _noticer=(
|
||||
f"Le modèle « {model} » n'est plus disponible ({exc.response.status_code}). "
|
||||
f"Réponse générée avec « {self.default_model} » à la place."
|
||||
))
|
||||
raise
|
||||
|
||||
response = self._parse_response(data, model)
|
||||
response.notice = _noticer or ""
|
||||
return response
|
||||
|
||||
def _parse_response(self, data: dict, model: str) -> LLMResponse:
|
||||
choice = data["choices"][0]["message"]
|
||||
text = choice.get("content") or ""
|
||||
tool_calls = []
|
||||
@@ -193,7 +217,6 @@ class LLMClient:
|
||||
"arguments": args,
|
||||
"arguments_raw": fn.get("arguments") or "",
|
||||
})
|
||||
|
||||
return LLMResponse(
|
||||
text=text,
|
||||
tool_calls=tool_calls,
|
||||
|
||||
Reference in New Issue
Block a user