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

161 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
```bash
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) :
```yaml
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 :
```bash
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 :
```bash
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.