fix(llm): ordre des modèles imposé par l'admin, diagnostic des 429 et descriptions plus complètes - #61
Merged
Merged
Conversation
Google renvoie un HTTP 429 aussi bien pour un débit dépassé que pour un compte bloqué (« Your prepayment credits are depleted »). Les deux étaient présentés comme « QUOTA OU DÉBIT DÉPASSÉ », ce qui envoie l'utilisateur consulter des compteurs de quota restés au vert — le solde prépayé n'y figure pas. - Un 429 portant un marqueur de facturation est désormais libellé « FACTURATION BLOQUÉE », avec l'action corrective (recharger le projet). - La cascade s'arrête au premier échec de ce type : le blocage est au niveau du compte, les modèles suivants échoueront à l'identique. Économise 4 appels inutiles par étape LLM, sur les quatre cascades (batch, sélection, synthèse, rapport). - Les messages de repli n'annoncent plus « tous les modèles ont échoué » quand un seul a été essayé. Constaté en production : les 5 modèles renvoyaient le même 429 de facturation, ce qu'aucun problème de modèle ou de SDK ne produirait. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014s387wqjrmhJ34u8js1otm
gemini-3.1-flash-lite (0,25 $/1,50 $ par Mtok) passe devant gemini-3.5-flash (1,50 $/9,00 $), soit 6x moins cher en entrée comme en sortie. L'essentiel du travail — reformulation de dépêches, extraction de mots-clés, rapport d'exécution — ne justifie pas le modèle haut de gamme, qui reste en second comme repli qualité. Réordonner la constante ne suffisait pas : l'ordre stocké en Firestore l'emportait sans condition, si bien que la mise à jour de cascade de juillet n'a jamais pris effet en production (l'ordre observé dans les rapports diffère de DEFAULT_MODEL_PRIORITY). Ajout de MODEL_PRIORITY_VERSION : quand la version stockée est périmée, l'ordre par défaut s'applique une seule fois, puis le choix fait dans l'admin redevient prioritaire. - `merge_model_priority()` centralise la règle côté collector ; main.py et analyze_logs.py l'utilisent au lieu de leur propre copie. - `GlobalSettings.model_priority_version` a pour défaut la version courante : un PUT qui omettrait le champ ne doit pas rejouer la migration. - Quatrième copie de la liste alignée (article_summarizer.py), qui n'était pas documentée ; un test vérifie désormais que les copies ne divergent pas. - Étiquettes de l'admin annotées du coût par million de tokens. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014s387wqjrmhJ34u8js1otm
Applique la règle demandée : les modèles sont sollicités du moins cher au plus cher, et on ne monte en gamme que lorsque le modèle courant refuse. Ordre établi sur les tarifs réels ($ par million de tokens, entrée/sortie) : gemma-4-26b (0,07/0,30) → gemma-4-31b (0,09/0,34) → gemini-3.1-flash-lite (0,25/1,50, GA) → gemini-3-flash-preview (0,25/1,50) → gemini-3.5-flash (1,50/9,00). À prix égal, le modèle GA précède le preview. MODEL_PRIORITY_VERSION passe à 3 pour que l'ordre s'applique en production. Correction d'un trou qui rendait cet ordre risqué : le JSON était analysé en dehors de la cascade, si bien qu'une réponse malformée ne déclenchait aucune montée d'un cran et faisait basculer tout le run vers les articles bruts. La validité du JSON est désormais vérifiée dans `_call_llm` et compte comme un refus du modèle — un petit modèle qui répond du texte libre cède donc sa place au suivant. Les motifs de montée sont journalisés distinctement : « dépassement » pour un 429 de quota ou de débit (cas nominal), « anomalie » pour un modèle inconnu, une requête invalide ou une réponse illisible (défaut à corriger). Le blocage de facturation continue d'arrêter la cascade. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014s387wqjrmhJ34u8js1otm
This was referenced Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Regroupe toutes les corrections apportées depuis le merge de la #60. Sept commits, issus du diagnostic de la panne Gemini de début août.
Contexte
La panne d'origine était un blocage de facturation (
prepayment credits are depleted), pas un dépassement de quota — Google renvoie un 429 dans les deux cas, ce qui expliquait l'écart avec les compteurs de la console. Les commits suivants corrigent ce que ce diagnostic a mis au jour.1. L'ordre des modèles est celui de la page admin, littéralement
Règle : aucune déduction du code. L'ordre défini dans
/adminest sollicité tel quel à chaque appel LLM — ni tri, ni purge des modèles inconnus, ni insertion automatique des nouveaux.resolve_model_priority()remplace l'ancien mécanisme de migration (MODEL_PRIORITY_VERSION,merge_model_priority(), champmodel_priority_version), entièrement supprimé. Son seul comportement par défaut : amorcer un projet neuf surDEFAULT_MODEL_PRIORITYquand aucun ordre n'a encore été choisi.GET /admin/settingsredevient en lecture seule — il ne réécrit plusmodel_priorityen base, seul le PUT le fait.Les trois chemins qui lisent ce réglage (page admin, collecte/synthèse, bouton « Régénérer le résumé IA ») passent désormais tous par la même fonction. Le troisième lisait l'ordre brut de Firestore sans contrôle : il pouvait solliciter un autre modèle que la collecte.
Conséquence assumée : modifier
DEFAULT_MODEL_PRIORITYn'a plus aucun effet sur un projet existant, et un nouveau modèle ajouté côté code n'apparaît pas tout seul dans la liste.2. Un 429 de débit n'est plus pris pour un compte bloqué
Le marqueur
billingétait trop large : tous les 429 de Gemini contiennent « check your plan and billing details », y compris un simple dépassement de débit. Un projet correctement crédité voyait donc sa cascade interrompue avec un message l'invitant à recharger son compte.Marqueurs restreints aux signaux spécifiques (crédits prépayés épuisés, compte suspendu), plus un veto explicite : un message nommant une métrique de quota décrit un débit dépassé, la cascade doit monter d'un cran. En cas de doute, on ne bloque pas.
3. Diagnostic et robustesse de la cascade
_call_llm. Elle était contrôlée en aval : une réponse malformée ne déclenchait aucune montée d'un cran et faisait basculer tout le run verssave_raw_articles.4. Descriptions d'articles trop courtes
MAX_ARTICLE_CONTENT_FOR_BATCHpasse de 1500 à 4000 caractères : le modèle n'avait pas assez de matière source pour développer les 4 à 6 phrases attendues. Surcoût de l'ordre de 0,006 $ par run.Dette réduite au passage
DEFAULT_MODEL_PRIORITYexistait en quatre exemplaires, dont un non documenté (article_summarizer.py) resté sur un ordre périmé. Il n'en reste que deux — un par service — et un test vérifie qu'ils ne divergent pas.admin.py,collector/main.pyetanalyze_logs.pyimportent.Limite connue
La cascade ne bascule que sur erreur, jamais sur qualité. Si un modèle renvoie du JSON valide mais médiocre, il traitera tout le flux sans alerte. Les libellés de l'admin affichent le coût par million de tokens pour arbitrer.
Tests
31 tests ajoutés : diagnostic des erreurs, distinction facturation / débit sur les messages réels de production, interruption de cascade, ordre appliqué littéralement, amorçage sur liste vide, non-divergence des copies, montée sur JSON malformé.
87 passedsur la suite hors acceptance. Les 14 échecs restants sont identiques avant et après ces commits, vérifié par diff nom par nom — ils viennent de l'environnement de développement (absence de.env, d'émulateur Firestore et de réseau).