diff --git a/common/bifrost-routage-par-cout.md b/common/bifrost-routage-par-cout.md new file mode 100644 index 0000000..d41375c --- /dev/null +++ b/common/bifrost-routage-par-cout.md @@ -0,0 +1,62 @@ +# Bifrost — routage texte par coût : état de la mesure (18/07/2026) + +## Découverte clé : la mesure existe déjà +Bifrost journalise TOUT dans `/volume1/docker/bifrost/data/logs.db` (SQLite, table `logs`) : +`virtual_key_name`, `model`, `prompt_tokens`, `completion_tokens`, `cost` ($ réels), +`latency` (ms), `status`, `error_details`, `fallback_index`, `number_of_retries`. +Aucune instrumentation à développer. Lecture en RO : +``` +sqlite3 'file:/volume1/docker/bifrost/data/logs.db?mode=ro' +``` +Depuis mcp-nas : chemin `/mnt/docker/bifrost/data/logs.db`, via `python3 -c` (sqlite3 CLI absent). + +## Requête de référence — coût 30 j par clé × modèle +```sql +SELECT COALESCE(virtual_key_name,'(aucune)'), model, COUNT(*), + SUM(prompt_tokens), SUM(completion_tokens), ROUND(SUM(cost),4), + SUM(CASE WHEN status!='success' THEN 1 ELSE 0 END) +FROM logs WHERE timestamp >= datetime('now','-30 days') +GROUP BY 1,2 ORDER BY 6 DESC; +``` +Coût par tâche aboutie : `AVG(cost) WHERE status='success' AND cost>0`, par modèle. + +## Constats (30 j au 18/07/2026) +- Dépense totale ≈ **1,47 $/30 j** (~15 % du plafond OpenCode 10 $). Pas d'urgence budgétaire. +- deepseek-v4-flash : 774 succès, **1,38 m$/tâche**, 1 942 tok sortie moy, 22,6 s. +- kimi-k2.6 : 93 succès, **3,99 m$/tâche** (2,9× flash) pour 909 tok sortie — confirme que + le prix affiché au token ne prédit pas le coût par tâche. +- 22× HTTP 429 `GoUsageLimitError` sur flash : le plafond OpenCode se fait déjà toucher. +- 270 replis `fallback_index=1` sur flash ; `attempt_trail` vide → destination du repli non tracée. + +## BLOQUANT attribution : une seule clé pour tout +Seules 2 clés virtuelles actives (`Nabil-Key`, `Yesmine-Key`). Tous les consommateurs +(hermes ×3, crons veille, n8n, apps) partagent Nabil-Key → coût par consommateur IMPOSSIBLE. +`metadata` vide, `raw_request` vide. Prérequis n°1 avant tout routage : +**une clé virtuelle Bifrost par consommateur** (hermes-tt / hermes-nyora / hermes-perso / +n8n / veille-infra / veille-nexum / redaction-pro / family-help / nyora-veille / +gsparc-mezzouna / reglement-definitif). Zéro code — config governance Bifrost + .env clients. + +## Incident en cours : 879× 401 en 30 j +`virtual_key_not_found` (784) + `virtual_key_required` (95), encore 4–26/jour cette semaine, +pic 328 le 11/07. Le client demande `deepseek-v4-flash` avec une clé absente de la DB — +probablement l'ancienne clé `yesmine-hermes-desktop` (vue dans les logs, absente de config.db) +ou une clé rotée. Un service croit appeler le LLM et échoue en silence depuis des semaines. +Identification : `docker logs bifrost` → IP source 172.27.x.x → container (réseau n8n). + +## Protocole de mesure retenu (AVANT toute ligne de routage) +1. Créer les clés virtuelles par consommateur, distribuer dans les .env, redémarrer les clients. +2. Trouver et corriger l'émetteur des 401. +3. 7 jours de collecte attribuée, puis tableau coût/mois par consommateur (requête ci-dessus). +4. Définir les besoins texte à partir des données mesurées (piste : exécution-outils / + rédaction / raisonnement-agentique / vision-OCR / extraction structurée). +5. Seulement ensuite : sélecteur type nyora_models.py côté Bifrost, avec repli journalisé. + +## Pièges +- `attempt_trail` est vide dans cette version de Bifrost : la cible réelle d'un repli ne se lit + pas dans logs.db — journaliser côté sélecteur. +- Comparer m$/tâche entre modèles n'a de sens qu'À BESOIN ÉGAL (mimo-v2.5 affiche 0,53 m$/tâche + mais sur des tâches à 84 tok de sortie — biais de sélection). +- config.db : clés provider en `plain_text` — ne jamais SELECT les colonnes `value` en clair. + +Gabarit du sélecteur : VPS hermes-nabil `/data/nyora/nyora_models.py` + section 7 de +`common/hermes-nabil-images-flyers-navigateur.md`.