diff --git a/_INDEX.md b/_INDEX.md index 59f1a15..6537aff 100644 --- a/_INDEX.md +++ b/_INDEX.md @@ -10,6 +10,7 @@ | Runbook | Date | Tags | |---------|------|------| +| [Job cron veille Nexum : pin provider bypassant bifrost-proxy, bascule silencieuse sur DeepSeek/OpenRouter (ticket infra-2026-09-037)](common/veille-cron-pin-bypass-bifrost-20260910.md) | 2026-09-10 | infra, bifrost, opencode-go, cron, hermes-nyora, veille-nexum | | [Déploiement serveur TTS local Kokoro-82M sur NAS (image hwdsl2/kokoro-server, port 3088, voix ff_siwis, auth Bearer, validation NAS et VPS via Tailscale) (ticket infra-2026-09-029)](common/kokoro-tts-deploiement.md) | 2026-09-09 | infra, kokoro, tts, speech, nas, docker, audio, tailscale | | [Bascule complète nyora-doc-api et nyora-convert-api vers VPS, relais socat NAS et libération RAM pour Kokoro TTS (ticket infra-2026-09-028)](common/ports-registry.md) | 2026-09-09 | infra, nyora-doc-api, nyora-convert-api, vps, tailscale, socat, ram, kokoro | | [Relocalisation de bifrost-proxy sur VPS (port 3086), découplage total de Sfax pour hermes-nabil et dsh-vps, relais socat NAS et retrait du stub bifrost 3085 (tickets infra-2026-09-030 et 031)](common/ports-registry.md) | 2026-09-09 | infra, bifrost, bifrost-proxy, vps, tailscale, socat, hermes-nabil, dsh | diff --git a/common/PROTOCOL-INFRA.md b/common/PROTOCOL-INFRA.md index 917c6b4..24e87d2 100644 --- a/common/PROTOCOL-INFRA.md +++ b/common/PROTOCOL-INFRA.md @@ -456,3 +456,15 @@ Une fois la regle DSM `baserow-schema.bolbol.tn` -> 3101 creee (cert Let's Encry Détail complet : `common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md`. + +## FIX -- Job cron veille Nexum bascule silencieusement sur DeepSeek/OpenRouter par pin de provider bypassant bifrost-proxy (2026-09-10) + +**Symptome** : Nabil signale que les 3 veilles quotidiennes (07h/12h/20h Tunis) tournent sur DeepSeek V4 Flash via OpenRouter au lieu de MiMo-V2.5 via OpenCode -- aucune erreur visible cote Telegram/site, le contenu est simplement genere par un autre modele. + +**Cause racine** : le job cron `veille-ia-nexum-4ee61494` portait un pin explicite au niveau du job (`model=mimo-v2.5`, `provider=opencode-go`) qui court-circuite le `model.default` de config.yaml (`provider: custom`, `base_url: http://bifrost-proxy/v1`). Ce pin envoie donc la requete DIRECTEMENT a `https://opencode.ai/zen/go/v1` sans passer par bifrost-proxy -- or c'est precisement bifrost-proxy qui injecte le header `x-bf-eh-x-opencode-session` requis par l'API OpenCode Go (regle du 04/09, piege-virtual-key-nabil-key-bifrost-hermes). Sans ce header : `HTTP 400 MissingSessionID`. Le meme echec se reproduit sur le `fallback_providers` du job (opencode-go/deepseek-v4-flash, meme chemin direct), et la requete finit par toucher le dernier filet `fallback_model: openrouter/deepseek-v4-flash` de config.yaml -- qui, lui, fonctionne, d'ou le contenu genere sans erreur visible. + +**Diagnostic** : `docker logs hermes-agent-nyora` montre la sequence complete (2 echecs MissingSessionID puis `Switched to fallback model: deepseek-v4-flash via opencode-go -> deepseek/deepseek-v4-flash via openrouter`). Comparaison avec les autres jobs cron actifs : le seul autre job active (`35d81e5260bb`) a `model=None/provider=None` et herite correctement de config.yaml. + +**Fix** : `hermes cron edit veille-ia-nexum-4ee61494 --model "" --provider ""` pour retirer le pin et faire heriter le job du `model.default` de config.yaml (custom -> bifrost-proxy -> mimo-v2.5). Verifie par appel direct `curl` vers `http://bifrost-proxy/v1/chat/completions` depuis le conteneur : HTTP 200, `resolved_model_used: mimo-v2.5` via provider `opencode` (donc bifrost-proxy actif et sain). + +**REFLEXE** : tout job cron / profil Hermes avec un `model`/`provider` pin explicite qui pointe `opencode-go` EN DIRECT (au lieu de `custom` + `base_url: http://bifrost-proxy/v1`) perd l'injection du header de session et tombe silencieusement dans la chaine de fallback jusqu'a DeepSeek/OpenRouter -- sans erreur remontee a l'utilisateur puisque le fallback reussit techniquement. Auditer systematiquement tout pin `provider=opencode-go` au niveau job/profil (ex: `hermes cron list` puis verifier chaque job avec model/provider non-null) : soit le retirer (heriter de config.yaml), soit le reecrire en `provider=custom` + `base_url=http://bifrost-proxy/v1`. Ticket infra-2026-09-037. Detail : common/veille-cron-pin-bypass-bifrost-20260910.md. diff --git a/common/veille-cron-pin-bypass-bifrost-20260910.md b/common/veille-cron-pin-bypass-bifrost-20260910.md new file mode 100644 index 0000000..7061901 --- /dev/null +++ b/common/veille-cron-pin-bypass-bifrost-20260910.md @@ -0,0 +1,78 @@ +# Job cron veille Nexum : bascule silencieuse sur DeepSeek/OpenRouter par pin de provider bypassant bifrost-proxy + +**Instance auteur** : hermes-nyora +**Date** : 2026-09-10 +**Tags** : [infra, bifrost, opencode-go, cron, hermes-nyora, veille-nexum] +**Statut** : validé + +--- + +## Problème + +Nabil signale que les 3 veilles quotidiennes Dr. Nexum (07h/12h/20h Tunis, job `veille-ia-nexum-4ee61494`) tournent depuis un moment sur DeepSeek V4 Flash via OpenRouter au lieu de MiMo-V2.5 via OpenCode. Aucune erreur visible côté Telegram ou côté site veille.bolbol.tn : le contenu est simplement généré par un modèle différent de celui attendu, sans signal d'échec. + +--- + +## Contexte et contraintes + +- `config.yaml` de hermes-agent-nyora route par défaut via `model.default` (`provider: custom`, `base_url: http://bifrost-proxy/v1`) avec un `fallback_providers` (opencode-go/deepseek-v4-flash) puis un `fallback_model` final (openrouter/deepseek-v4-flash) — une chaîne de secours à 3 niveaux, conçue pour ne jamais bloquer un run, pas pour signaler une dégradation. +- Depuis la règle du 04/09 (`piege-virtual-key-nabil-key-bifrost-hermes-20260904.md`), l'API OpenCode Go exige un header `x-bf-eh-x-opencode-session`, injecté uniquement par `bifrost-proxy`. Un appel direct à `https://opencode.ai/zen/go/v1` sans passer par bifrost-proxy échoue systématiquement. +- Chaque job cron Hermes peut porter un pin `model`/`provider` au niveau du job, qui prend le pas sur `model.default` de config.yaml. + +--- + +## Ce qui NE fonctionne PAS + +| Tentative | Erreur obtenue | Raison de l'échec | +|-----------|----------------|-------------------| +| Job cron avec pin `provider=opencode-go` direct | `HTTP 400 MissingSessionID: Request is missing x-opencode-session` | Le pin bypass bifrost-proxy, qui est le seul point d'injection du header de session requis par l'API OpenCode Go | +| `fallback_providers: opencode-go/deepseek-v4-flash` du job | Même erreur `MissingSessionID` | Même chemin direct que ci-dessus, hérite du même défaut | + +--- + +## Solution validée + +Diagnostic (`docker logs hermes-agent-nyora`) : +``` +WARNING agent.conversation_loop: API call failed (attempt 1/3) provider=opencode-go base_url=https://opencode.ai/zen/go/v1 model=mimo-v2.5 summary=HTTP 400: Error from provider (Console Go): Request is missing x-opencode-session... +WARNING agent.conversation_loop: API call failed (attempt 1/3) provider=opencode-go base_url=https://opencode.ai/zen/go/v1/ model=deepseek-v4-flash summary=HTTP 400: ... +🔄 Switched to fallback model: deepseek-v4-flash via opencode-go → deepseek/deepseek-v4-flash via openrouter +``` + +Comparaison avec `hermes cron list` / lecture directe de `jobs.json` : seul `veille-ia-nexum-4ee61494` portait un pin `model=mimo-v2.5, provider=opencode-go` au niveau du job. Le seul autre job actif (`35d81e5260bb`) avait `model=None, provider=None` et héritait correctement de `config.yaml`. + +Fix : +```bash +docker exec hermes-agent-nyora hermes cron edit veille-ia-nexum-4ee61494 --model "" --provider "" +``` +Cette commande retire le pin ; le job hérite désormais de `model.default` (custom → bifrost-proxy → mimo-v2.5). + +--- + +## Vérification + +```bash +docker exec hermes-agent-nyora curl -s -X POST http://bifrost-proxy/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{"model":"mimo-v2.5","max_tokens":20,"messages":[{"role":"user","content":"Reponds juste: OK"}]}' +# Résultat obtenu : HTTP 200, resolved_model_used: mimo-v2.5, provider: opencode (bifrost-proxy sain et actif) +``` +Job vérifié post-edit : `model=None, provider=None` dans jobs.json, identique au pattern du job sain. + +--- + +## Pièges spécifiques DSM / NAS + +- Ce mode de panne est **silencieux par construction** : la chaîne de fallback à 3 niveaux de config.yaml est conçue pour toujours produire une réponse, donc un job cassé au niveau du premier maillon (pin direct opencode-go) continue de délivrer un résultat "normal" en apparence, juste avec un modèle différent et moins cher. Aucune alerte ne se déclenche. +- Réflexe d'audit : tout job cron / profil Hermes avec un pin `provider=opencode-go` **en direct** (au lieu de `provider=custom` + `base_url=http://bifrost-proxy/v1`) est suspect. Auditer avec `hermes cron list` puis vérifier chaque job avec `model`/`provider` non-null (`jobs.json`, champs `model`/`provider`/`base_url`/`profile`). +- Un profil Hermes (`/opt/data/profiles//config.yaml`) peut porter le même défaut indépendamment des jobs cron — vérifier aussi ce fichier si un job utilise `--profile`. +- Ticket Baserow : infra-2026-09-037. + +--- + +## Références + +- `common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md` (règle du header x-opencode-session) +- `common/bifrost-opencode-routing-fix-20260907.md` (fix précédent du routage bifrost-proxy → opencode.ai) +- `common/migration-vps-bifrost-stub-forwarder-20260904.md` (relocalisation bifrost-proxy VPS)