Files
Effi/docs/ARCHITECTURE-LITELLM.md
T
Folzo 2a9f721c4e feat: LiteLLM multi-backends + jean-proxy avec WoL
Architecture (voir docs/ARCHITECTURE-LITELLM.md):
  open-webui -> LiteLLM:4000
                 |- qwen3.6-27b -> jean-proxy:3012 -> Jean (.11:8080)
                 |- llama3.1-8b -> wol-proxy:3011  -> Ollama (.11:11434)

Nouveaux fichiers:
- wol-proxy/ (versionne l'app.py de prod deja en place)
- jean-proxy/ (clone du wol-proxy avec HEALTH_PATH=/health + retry post-WoL
  pour services qui mettent du temps a demarrer apres WoL, ex: Jean qui
  charge 14 Go en VRAM)
- litellm/config.yaml (routing par nom de modele, drop_params=true)
- docker-compose.litellm.yml (override ADDITIF: ajoute uniquement jean-proxy
  et litellm, ne redemarre PAS open-webui/wol-proxy/hermes)

Teste en prod sur .27: Jean + Ollama repondent via LiteLLM:4000 avec
path-through de chat_template_kwargs.enable_thinking (raisonnement Qwen3.6
desactivable par requete). Zero impact hermes-webui/wol-proxy.
Basculer open-webui reste une etape manuelle (step 3 de la doc).
2026-06-27 16:19:08 +02:00

8.6 KiB
Raw Blame History

Architecture LLM multi-backends — LiteLLM + WOL

État avant

open-webui (:3000)  ─► OLLAMA_BASE_URL=host.docker.internal:3011
                             │
                       wol-proxy (:3011, host network)
                             │
                             ▼  (WOL paquet + forward TCP)
                       PC gaming .11 (s'éteint la nuit)
                             └─ Ollama :11434

Problème : backend unique (Ollama), pas moyen d'utiliser Jean (Qwen3.6-27B + MTP sur ROCm) sans changer la config à la main.

État après — LiteLLM + double proxy WOL

open-webui (:3000)  ─► OLLAMA_BASE_URL=host.docker.internal:4000   ← optional: à basculer manuellement
                             │
                       LiteLLM proxy (:4000, host network)       ← NOUVEAU
                             │  routing par nom de modèle
                             ├── "qwen3.6-27b" / "qwen3.6"  ─► jean-proxy (:3012)  ─► Jean (.11:8080)
                             └── "llama3.1-8b" / "llama3.1"  ─► wol-proxy  (:3011)  ─► Ollama (.11:11434)
                                                                    │
                                                                    └──  WOL paquet (MAC PC gaming) si cible DOWN

Composants

wol-proxy (/wol-proxy/) — EXISTANT, inchangé

Reverse-proxy Flask générique vers Ollama + déclenchement WoL.

  • Écoute :3011 sur le host (network_mode: host).
  • Health-check : GET /api/version sur Ollama (HEALTH_PATH codé = /api/version).
  • Si cible DOWN → envoi 3 paquets magic sur BROADCAST_IP:9 avec TARGET_MAC, attente jusqu'à WAKE_TIMEOUT (60 s).
  • Route ensuite toutes les requêtes vers http://${OLLAMA_HOST}:11434.

jean-proxy (/jean-proxy/) — NOUVEAU, clone du wol-proxy

Même mécanique, adapté pour Jean (llama.cpp).

  • Écoute :3012 sur le host.
  • Health-check : GET /health sur Jean (HEALTH_PATH=/health par défaut, paramétrable via env).
  • Si cible DOWN → même WoL (même TARGET_MAC, même BROADCAST_IP).
  • Bonus : retry post-WoL — Jean met ~30 s à charger le modèle 14 Go après le boot du PC. Si la connexion est refusée (Jean n'a pas encore ouvert son port), jean-proxy attend jusqu'à WAKE_TIMEOUT et retry toutes les CHECK_INTERVAL s.
  • Route toutes les requêtes vers http://${OLLAMA_HOST}:8080.
  • Voir jean-proxy/app.py : variables TARGET_PORT=8080, HEALTH_PATH=/health, LISTEN_PORT=3012.

litellm (/litellm/) — NOUVEAU

Proxy OpenAI-compatible unique pour open-webui.

  • Image officielle : ghcr.io/berriai/litellm:main-stable.
  • Écoute :4000 sur le host (network_mode: host pour joindre les proxies :301x).
  • Routing par nom de modèle via litellm/config.yaml :
Model name (visible open-webui) Route vers Backend réel
qwen3.6-27b, qwen3.6 http://localhost:3012/v1 (jean-proxy) Jean (llama.cpp OpenAI API)
llama3.1-8b, llama3.1 http://localhost:3011 (wol-proxy) Ollama (via /api/chat façon Ollama)
  • Master key : sk-effi-litellm-master (à mettre dans .env si on veut la surcharger — voir plus bas).
  • drop_params: true : supprime les params non reconnus par le backend (évite les erreurs sur champs OpenAI exotiques).
  • Pass-through du chat_template_kwargs.enable_thinking : LiteLLM le transmet tel quel à Jean — c'est ainsi qu'on peut désactiver le raisonnement Qwen3.6 par requête sans toucher au config.env de Jean.

Fichiers

  • wol-proxy/app.py — code existant (préalablement non versionné, désormais commit).
  • wol-proxy/Dockerfile — idem.
  • jean-proxy/app.py — clone du wol-proxy avec HEALTH_PATH paramétrable + retry post-WoL pour services lents à démarrer.
  • jean-proxy/Dockerfile — idem que wol-proxy.
  • litellm/config.yaml — routing des modèles, master key.
  • docker-compose.litellm.yml — override : ajoute uniquement les services jean-proxy et litellm. Ne redéfinit pas les services existants, donc docker compose up -d ne redémarre ni open-webui, ni hermes-webui, ni wol-proxy (zero impact).

Déploiement

Pré-requis

Déjà validé sur la machine de prod :

  • Serveur Effi .27 accessible en SSH (ssh effi-server).
  • Docker + compose installés.
  • Repo Effi à /home/effi/effi.
  • .env contient OLLAMA_HOST, PC_GAMING_MAC, BROADCAST_IP.

Commandes (à lancer sur .27)

cd /home/effi/effi
# Démarre uniquement litellm + jean-proxy, n' touche pas au reste
docker compose -f docker-compose.yml -f docker-compose.litellm.yml up -d --build jean-proxy litellm

# Test 1 — Jean route
curl -s -H "Authorization: Bearer sk-effi-litellm-master" \
  http://localhost:4000/v1/models

# Test 2 — chat Qwen3.6 via Jean (reasoning off)
curl -s -X POST http://localhost:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-effi-litellm-master" \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3.6-27b","messages":[{"role":"user","content":"Bonjour"}],
       "chat_template_kwargs":{"enable_thinking":false},"max_tokens":50}'

# Test 3 — chat llama3.1 via Ollama (provoque WoL si .11 éteint)
curl -s -X POST http://localhost:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-effi-litellm-master" \
  -H "Content-Type: application/json" \
  -d '{"model":"llama3.1","messages":[{"role":"user","content":"Bonjour"}],"max_tokens":30}'

Commutation de open-webui vers LiteLLM (étape utilisateur)

Une fois les tests validés :

  1. Éditer /home/effi/effi/docker-compose.yml (seulement cette ligne) :
    OLLAMA_BASE_URL=http://host.docker.internal:4000   # LiteLLM au lieu de :3011
    
  2. Ajouter la master key LiteLLM dans /home/effi/effi/.env :
    LITELLM_MASTER_KEY=sk-effi-litellm-master
    
  3. Redémarrer open-webui uniquement :
    docker compose -f docker-compose.yml -f docker-compose.litellm.yml up -d open-webui
    
  4. Dans open-webui : Admin → Settings → Connections → l'URL est déjà http://host.docker.internal:4000. Vérifier que les deux modèles apparaissent dans le dropdown.

ℹ️ Cette étape est volontairement laissée manuelle — c'est l'unique impact sur le service en prod (1 redémarrage open-webui). Hermes, wol-proxy, qdrant etc. ne sont pas touchés.

En cas de rollback

Revenir à OLLAMA_BASE_URL=http://host.docker.internal:3011 et redémarrer open-webui. LiteLLM et jean-proxy peuvent rester UP (en override, ils ne gênent rien) ou être arrêtés :

docker compose -f docker-compose.yml -f docker-compose.litellm.yml stop litellm jean-proxy

Comportement WoL —要点

  • Les deux proxies (wol-proxy et jean-proxy) partagent la même adresse MAC cible (PC_GAMING_MAC).
  • Quel que soit le backend demandé, si le PC gaming est DOWN, un seul WoL est envoyé (par le proxy concerné).
  • Pendant la phase de wake-up (WAKE_TIMEOUT=60s), le proxy bloque la requête HTTP jusqu'à ce que la cible réponde — l'utilisateur voit une latence plus longue mais pas d'erreur.
  • Après WoL réussi :
    • Ollama répond en ~10-20 s (lancement container + warmup).
    • Jean répond en ~30-60 s (charger le modèle 14 Go en VRAM). jean-proxy a un retry supplémentaire pour couvrir ce cas.

Limitations / à surveiller

  • n_parallel=1 côté Jean : une seule requête à la fois. Si open-webui + un script Effi appellent Jean en parallèle, la 2e attend. Pour la prod multi-tenant, mettre en place une queue ou ouvrir un 2e slot (mais VRAM saturée).
  • drop_params: true : LiteLLM supprime tout paramètre non standard OpenAI. Si on veut passer chat_template_kwargs à Jean, vérifier que LiteLLM le transmet — testé OK, mais à revalider si on ajoute d'autres flags exotiques.
  • Pas de failover dans la config actuelle : si jean-proxy retourne 502 (cible injoignable après WoL), LiteLLM renvoie l'erreur au client. On pourrait ajouter un fallback LiteLLM : qwen3.6 → llama3.1 si Jean down.
  • Master key en clair dans config.yaml : acceptable en interne, mais pour un déploiement plus sclériser, déplacer la clé dans .env via LITELLM_MASTER_KEY et la référencer dans config.yaml via ${LITELLM_MASTER_KEY} (LiteLLM supporte l'expansion env vars).

Tests de non-régression

Après déploiement, vérifier que :

  • hermes-webui est toujours Up et healthy.
  • open-webui toujours Up et healthy (jusqu'à la commutation optionnelle).
  • wol-proxy toujours Up (inchangé).
  • curl http://192.168.1.27:8787/ (Hermes UI) répond toujours.
  • curl http://192.168.1.27:3000/ (open-webui) répond toujours.
  • curl http://localhost:3011/api/version (ollama via wol-proxy direct) répond toujours.