nas-runbooks/common/migration-provider-opencode...

4.0 KiB

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.

# 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
# 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=<cle>" → 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

docker exec hermes-agent-<inst> hermes fallback ls
# Attendu : Primary  deepseek-v4-flash  (via opencode-go)

# Test end-to-end
docker exec hermes-agent-<inst> curl -s -H "Authorization: Bearer <API_SERVER_KEY>" \
  -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 <c> --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