# Migration provider IA → OpenCode Go (+ fallback OpenRouter) **Instance auteur** : hermes-perso (transverse aux 3) **Date** : 2026-06-14 **Tags** : infra, opencode, hermes, family-help, llm, fallback **Statut** : valide --- ## Probleme Basculer le provider IA par defaut de OpenRouter vers OpenCode Go (endpoint `zen/go/v1`), tout en gardant DeepSeek V4 Flash et un fallback OpenRouter. Concerne les 3 agents Hermes (gateway) + l'app family-help (module medical). --- ## Contexte et contraintes - Image `nousresearch/hermes-agent:latest`, source montee dans `/opt/hermes`. - Les 3 Hermes sont des **stacks Portainer** : 291=nyora, 292=perso (path-based, compose sur le FS), 294=TT (compose **inline** dans Portainer). Le `docker-compose.yml` top-level de `hermes-platform/` est OBSOLETE. - 1 seul abonnement OpenCode Go, plusieurs cles tirant du meme pool (~60$/mois). - `/Volumes/docker` (Mac) = mount SMB du NAS `/volume1/docker`. --- ## Ce qui NE fonctionne PAS | Tentative | Erreur obtenue | Raison de l'echec | |-----------|----------------|-------------------| | `docker compose up` depuis `hermes-platform/` (top-level) | `Conflict. The container name is already in use` | Les containers appartiennent aux stacks per-instance, pas au compose top-level | | `hermes model` / `hermes fallback add` en SSH | `requires an interactive terminal` | Pickers interactifs → editer le YAML directement | | Provider custom dans `providers:` | inutile | `opencode-go` est deja BUILT-IN (models.dev) | | family-help medical `max_tokens: 512` | `finish_reason=length`, `content` VIDE | Modele de raisonnement : reasoning_content mange le budget avant le content | --- ## Solution validee Provider built-in `opencode-go` : base_url `https://opencode.ai/zen/go/v1`, cle via env var **`OPENCODE_API_KEY`**, model `deepseek-v4-flash` (sans prefixe). OpenRouter (fallback) garde `deepseek/deepseek-v4-flash`. ```yaml # data/config.yaml (gateway) — pour chaque instance model: default: deepseek-v4-flash provider: opencode-go fallback_providers: - provider: openrouter model: deepseek/deepseek-v4-flash key_env: OPENROUTER_API_KEY ``` ```bash # perso / nyora (stacks path-based) : ajouter OPENCODE_API_KEY au .env + au compose # - OPENCODE_API_KEY=${OPENCODE_API_KEY} cd /volume1/docker/hermes-platform/hermes-perso docker compose up -d --force-recreate hermes-agent-perso # TT (stack 294 inline) : via API Portainer # GET /api/stacks/294/file → inserer "- OPENCODE_API_KEY=" → PUT /api/stacks/294?endpointId=2 ``` family-help : `medical_service.py` → helper `_llm_complete()` (OpenCode primaire, OpenRouter fallback), **`max_tokens: 2048`**, cle `BIFROST_OPENCODE_KEY`. `docker compose build --no-cache && docker compose up -d --force-recreate`. --- ## Verification ```bash docker exec hermes-agent- hermes fallback ls # Attendu : Primary deepseek-v4-flash (via opencode-go) # Test end-to-end docker exec hermes-agent- curl -s -H "Authorization: Bearer " \ -H 'Content-Type: application/json' \ -d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"dis OK"}],"max_tokens":40}' \ http://localhost:8642/v1/chat/completions # Attendu : HTTP 200, content non vide # Test fallback : pointer OPENCODE_GO_BASE_URL vers un hote injoignable → reponse via OpenRouter (200) ``` --- ## Pieges specifiques DSM / NAS - `docker` n'est PAS dans le PATH SSH → utiliser `/usr/local/bin/docker`. - Ne jamais editer le docker-compose top-level (obsolete) ; identifier la vraie source via `docker inspect --format '{{index .Config.Labels "com.docker.compose.project.config_files"}}'`. - TT = stack inline Portainer → API Portainer, pas le FS (sudo requis sinon). - DeepSeek V4 Flash (OpenCode) = reasoning model → `max_tokens` >= 2048 pour les apps maison qui parsent du JSON (Hermes gere nativement). --- ## References - https://opencode.ai/docs/zen - https://hermes-agent.nousresearch.com/docs/user-guide/features/fallback-providers - NyoraNotes : note 416da848 (capitalisation), note 26c38f8c (missions)