Skip to content

fix(llm): ordre des modèles imposé par l'admin, diagnostic des 429 et descriptions plus complètes - #61

Merged
ijdan merged 3 commits into
mainfrom
claude/gemini-quota-issue-w6h299
Aug 3, 2026
Merged

fix(llm): ordre des modèles imposé par l'admin, diagnostic des 429 et descriptions plus complètes#61
ijdan merged 3 commits into
mainfrom
claude/gemini-quota-issue-w6h299

Conversation

@ijdan

@ijdan ijdan commented Aug 3, 2026

Copy link
Copy Markdown
Owner

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 /admin est 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(), champ model_priority_version), entièrement supprimé. Son seul comportement par défaut : amorcer un projet neuf sur DEFAULT_MODEL_PRIORITY quand aucun ordre n'a encore été choisi.

GET /admin/settings redevient en lecture seule — il ne réécrit plus model_priority en 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_PRIORITY n'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

  • Un 429 de facturation est libellé comme tel, avec l'action corrective, et arrête la cascade — aucun modèle ne peut aboutir, inutile de rejouer 4 fois le même refus.
  • La validité du JSON est vérifiée dans _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 vers save_raw_articles.
  • Les motifs de montée sont journalisés distinctement : « dépassement » (429 de quota ou de débit, cas nominal) contre « anomalie » (modèle inconnu, requête invalide, réponse illisible — défaut à corriger).

4. Descriptions d'articles trop courtes

  • MAX_ARTICLE_CONTENT_FOR_BATCH passe 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.
  • Longueurs minimales rendues impératives dans le prompt d'enrichissement.
  • Les Gemma quittent la tête de la liste d'amorçage : les moins chers du catalogue, mais trop laconiques pour la fiche article. La cascade ne bascule que sur erreur, jamais sur qualité — un modèle trop bref traiterait tout le flux sans que rien ne l'indique.

Dette réduite au passage

DEFAULT_MODEL_PRIORITY existait 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.py et analyze_logs.py importent.

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 passed sur 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).

ijdan and others added 3 commits August 3, 2026 10:05
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
@ijdan
ijdan merged commit d9d5c08 into main Aug 3, 2026
10 checks passed
@ijdan
ijdan deleted the claude/gemini-quota-issue-w6h299 branch August 3, 2026 10:14
@ijdan ijdan changed the title fix(llm): cascade Gemini triée par coût, diagnostic facturation et validation JSON fix(llm): ordre des modèles imposé par l'admin, diagnostic des 429 et descriptions plus complètes Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant