# Architecture LLM — jean-router (routing dynamique + gestion VRAM) ## Vue d'ensemble ``` ┌─────────────────────────────────────────────────────────────────┐ │ OpenWebUI (:3000) │ │ http://192.168.1.27:3000 │ └────────────────────────┬────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ jean-router (:3014) │ │ Routing dynamique + Gestion VRAM automatique │ │ │ │ • Interroge Jean /api/presets (découverte dynamique) │ │ • Interroge Ollama /api/tags (modèles vision) │ │ • Switch automatique de preset Jean │ │ • Arrête Jean quand on passe sur Ollama (libère VRAM) │ │ • Garde Ollama 5 min en mémoire avant déchargement │ └────────┬──────────────────────────────────────┬────────────────┘ │ │ ▼ ▼ ┌────────────────────────┐ ┌────────────────────────┐ │ jean-proxy (:3012) │ │ wol-proxy (:3011) │ │ + WOL si PC éteint │ │ + WOL si PC éteint │ └────────┬───────────────┘ └────────┬───────────────┘ │ │ ▼ ▼ ┌────────────────────────┐ ┌────────────────────────┐ │ Jean (.11:8080) │ │ Ollama (.11:11434) │ │ llama.cpp server │ │ Modèles vision │ │ Presets dynamiques │ │ mistral, qwen3.5 │ │ Ornith, Qwen3.6... │ │ │ └────────────────────────┘ └────────────────────────┘ ``` ## Composants ### jean-router (`/jean-router/`) — Proxy dynamique **Rôle :** Router intelligemment entre Jean et Ollama avec gestion VRAM **Fonctionnalités :** 1. **Découverte dynamique** : Interroge `/api/presets` (Jean) et `/api/tags` (Ollama) 2. **Routing intelligent** : Route vers Jean ou Ollama selon le modèle demandé 3. **Switch automatique** : Change de preset Jean si nécessaire 4. **Gestion VRAM** : Évite la contention mémoire entre Jean et Ollama **Endpoints :** - `GET /v1/models` : Liste tous les modèles (Jean + Ollama) - `POST /v1/chat/completions` : Route vers le bon backend **Configuration :** ```bash JEAN_WEB_HOST=192.168.1.11 # Jean Web UI (API presets) JEAN_WEB_PORT=8090 JEAN_PROXY_HOST=localhost # jean-proxy (backend Jean) JEAN_PROXY_PORT=3012 OLLAMA_PROXY_HOST=localhost # wol-proxy (backend Ollama) OLLAMA_PROXY_PORT=3011 OLLAMA_DIRECT_HOST=192.168.1.11 # Ollama direct (pour unload) OLLAMA_DIRECT_PORT=11434 ENABLE_OLLAMA=true ENABLE_VRAM_MANAGEMENT=true OLLAMA_KEEP_ALIVE_MINUTES=5 # Ollama reste 5 min en mémoire SWITCH_TIMEOUT=90 # Timeout chargement Jean (s) JEAN_STOP_TIMEOUT=30 # Timeout arrêt Jean (s) LISTEN_PORT=3014 ``` ### jean-proxy (`/jean-proxy/`) — Reverse-proxy Jean + WOL **Rôle :** Proxy vers Jean avec Wake-on-LAN si le PC gaming est éteint **Fonctionnalités :** - Écoute `:3012` sur le host (`network_mode: host`) - Health-check : `GET /health` sur Jean - Si cible DOWN → envoi paquets WOL vers `TARGET_MAC` - Attente jusqu'à `WAKE_TIMEOUT` (60s) - Route toutes les requêtes vers `http://${OLLAMA_HOST}:8080` **Configuration :** ```bash TARGET_HOST=192.168.1.11 TARGET_PORT=8080 TARGET_MAC=04:D9:F5:D1:EC:DF LISTEN_PORT=3012 WAKE_TIMEOUT=60 CHECK_INTERVAL=5 BROADCAST_IP=192.168.1.255 HEALTH_PATH=/health ``` ### wol-proxy (`/wol-proxy/`) — Reverse-proxy Ollama + WOL **Rôle :** Proxy vers Ollama avec Wake-on-LAN **Fonctionnalités :** - Écoute `:3011` sur le host - Health-check : `GET /api/version` sur Ollama - Si cible DOWN → envoi paquets WOL - Route toutes les requêtes vers `http://${OLLAMA_HOST}:11434` **Configuration :** ```bash TARGET_HOST=192.168.1.11 TARGET_PORT=11434 TARGET_MAC=04:D9:F5:D1:EC:DF LISTEN_PORT=3011 WAKE_TIMEOUT=180 CHECK_INTERVAL=5 BROADCAST_IP=192.168.1.255 ``` ## Gestion VRAM ### Problème Jean et Ollama partagent la même carte graphique (RX 7800 XT, 16GB VRAM). Charger les deux simultanément cause une contention mémoire. | Backend | Modèle typique | VRAM utilisée | |---------|---------------|---------------| | Jean | Ornith 35B Q6_K | ~28.5GB (déborde en RAM) | | Ollama | mistral 7B Q4_K_M | ~4GB | | Ollama | qwen3.5:9b Q4_K_M | ~6GB | ### Solution : Switch automatique **Switch Jean → Ollama :** ``` 1. Arrêt de Jean via API web (/api/stop) 2. Attente confirmation (max 30s) 3. Route requête vers Ollama ``` **Switch Ollama → Jean :** ``` 1. Programme déchargement Ollama dans 5 min (timer) 2. Démarrage de Jean via API web (/api/start) 3. Attente chargement modèle (max 90s) 4. Route requête vers Jean ``` **Timer Ollama :** - Ollama reste en mémoire pendant **5 minutes** après le dernier usage - Chaque requête vers Ollama annule le timer - Si retour sur Ollama avant expiration → déchargement annulé - Après 5 min d'inactivité → déchargement automatique ### Logs observés **Switch Jean → Ollama :** ``` [VRAM] Switch Jean → Ollama [VRAM] Arrêt de Jean pour libérer la VRAM... [VRAM] Jean arrêté via API web [VRAM] Jean confirmé arrêté [Router] → Ollama (gemma4:e4b) ``` **Switch Ollama → Jean :** ``` [VRAM] Switch Ollama → Jean [VRAM] Déchargement Ollama programmé dans 5 minutes [VRAM] Démarrage de Jean... [VRAM] Jean prêt après 45s [Router] → Jean (ornith-35b-q6) ``` **Timer Ollama :** ``` [VRAM] Timer écoulé (5min), déchargement Ollama... [VRAM] Ollama déchargé via API ``` ## Modèles disponibles ### Jean (presets dynamiques) Les presets sont découverts automatiquement via `/api/presets` : - **ornith-35b-q6** (actif, Q6_K, reasoning) - **qwen3.6-27b-mtp** (IQ4_XS, reasoning) - **qwen3.6-35b-moe-compact** - **qwen3.6-35b-moe-compact-reason** (reasoning) - **qwen3.6-35b-moe-nano** **Note :** Nouveaux presets Jean apparaissent automatiquement dans OpenWebUI sans configuration. ### Ollama (modèles vision) - **mistral** (7.2B, Q4_K_M, tools) - **qwen3.5:9b** (9.7B, Q4_K_M, vision, thinking) **Note :** Ollama sert principalement pour les modèles avec vision que Jean ne supporte pas. ## Déploiement ### Commandes (sur effi-server .27) ```bash cd /home/effi/effi git pull # Build et démarrage jean-router docker compose -f docker-compose.yml -f docker-compose.litellm.yml up -d --build jean-router # Recréer open-webui avec nouvelle config docker compose -f docker-compose.yml -f docker-compose.litellm.yml up -d --force-recreate open-webui # Vérifier docker logs jean-router --tail 20 curl -s http://localhost:3014/v1/models | jq ``` ### Tests **1. Liste des modèles :** ```bash curl -s http://localhost:3014/v1/models | python3 -m json.tool ``` **2. Requête Jean :** ```bash curl -X POST http://localhost:3014/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "ornith-35b-q6", "messages": [{"role": "user", "content": "Bonjour"}], "max_tokens": 50 }' ``` **3. Requête Ollama :** ```bash curl -X POST http://localhost:3014/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "mistral", "messages": [{"role": "user", "content": "Hello"}], "max_tokens": 30 }' ``` **4. Vérifier logs VRAM :** ```bash docker logs jean-router -f | grep -E "VRAM|Router" ``` ## Avantages vs LiteLLM | Aspect | LiteLLM | jean-router | |--------|---------|-------------| | Configuration | Statique (config.yaml) | Dynamique (API Jean) | | Nouveaux presets | Modifier config + restart | Automatique | | Pass-through /v1/models | Non | Oui | | Gestion VRAM | Aucune | Automatique | | Couches | 2 (LiteLLM + jean-proxy) | 1 (jean-router) | | Complexité | Élevée | Faible | ## Limitations ### 1. Latence de switch - Jean → Ollama : ~3s (arrêt Jean) - Ollama → Jean : ~45-90s (démarrage + chargement modèle) - **Impact** : Première requête après switch plus lente ### 2. Timer Ollama non-persistant - Si jean-router redémarre, le timer est perdu - Ollama restera en mémoire indéfiniment (jusqu'à son propre timeout) - **Acceptable** : Ollama gère son propre `keep_alive` ### 3. Pas de failover - Si Jean échoue (timeout, erreur), pas de fallback automatique vers Ollama - **À considérer** : Ajouter un fallback dans jean-router si besoin ### 4. Jean n'a pas d'auto-unload natif - Ollama décharge automatiquement après `OLLAMA_KEEP_ALIVE` (défaut 5min) - Jean garde son modèle chargé tant que le service tourne - **Solution actuelle** : Arrêt complet de Jean via API web ## Évolution possible ### Court terme - **Monitorer** : Vérifier que la gestion VRAM fonctionne bien en production - **Ajuster** : Modifier `OLLAMA_KEEP_ALIVE_MINUTES` selon l'usage réel - **Tester** : Valider que les nouveaux presets Jean apparaissent bien automatiquement ### Moyen terme - **VRAM monitoring** : jean-router pourrait vérifier la VRAM disponible avant de router - **Fallback automatique** : Si Jean échoue, router vers Ollama - **Metrics** : Exposer des statistiques (temps de switch, VRAM utilisée, etc.) ### Long terme - **JeanOrchestrator** : Jean pourrait gérer lui-même le switch entre presets - **Multi-GPU** : Si plusieurs GPU, router vers celui avec le plus de VRAM libre - **Load balancing** : Si plusieurs instances Jean/Ollama, répartir la charge ## Historique **2026-06-27** : Architecture initiale avec LiteLLM - Routing statique par nom de modèle - Configuration codée en dur dans `litellm/config.yaml` **2026-07-20** : Migration vers jean-router - Routing dynamique basé sur les presets Jean - Gestion automatique de la VRAM - Suppression de LiteLLM (une couche de moins) - Nouveaux modèles automatiquement disponibles ## Références - **Code jean-router** : `/jean-router/app.py` - **Docker compose** : `/docker-compose.litellm.yml` - **Journal de session** : `/docs/05-journal/SESSION-2026-07-20-JEAN-ROUTER.md` - **Repo Gitea** : https://gitea.folzocorp.fr/Folzo/Effi