- Add SESSION-2026-07-20-JEAN-ROUTER.md (complete session journal) - Add ARCHITECTURE-JEAN-ROUTER.md (new architecture documentation) - Remove ARCHITECTURE-LITELLM.md (replaced by jean-router) - Update README.md with jean-router, Jean backend, and VRAM management - Document dynamic preset discovery and VRAM switching logic
257 lines
9.0 KiB
Markdown
257 lines
9.0 KiB
Markdown
# Session 2026-07-20 : jean-router et gestion VRAM dynamique
|
|
|
|
## Résumé
|
|
|
|
Création de **jean-router**, un proxy dynamique qui remplace LiteLLM pour router intelligemment entre Jean (llama.cpp) et Ollama, avec gestion automatique de la VRAM partagée.
|
|
|
|
## Contexte
|
|
|
|
### Problème initial
|
|
- OpenWebUI ne voyait pas les modèles Jean (Ornith) car configuré uniquement pour Ollama
|
|
- LiteLLM avait une configuration statique des modèles (codée en dur dans `litellm/config.yaml`)
|
|
- Nouveaux presets Jean nécessitaient de modifier manuellement la config LiteLLM
|
|
- Jean et Ollama partagent la même VRAM (RX 7800 XT, 16GB) → contention mémoire
|
|
|
|
### Architecture avant
|
|
```
|
|
OpenWebUI → LiteLLM:4000 (config statique)
|
|
├→ jean-proxy:3012 → Jean (.11:8080)
|
|
└→ wol-proxy:3011 → Ollama (.11:11434)
|
|
```
|
|
|
|
**Problèmes :**
|
|
- LiteLLM ne faisait pas de pass-through de `/v1/models`
|
|
- Configuration statique : `qwen3.6-reason`, `qwen3.6-27b`, etc. codés en dur
|
|
- Jean faisait tourner **Ornith** mais LiteLLM attendait Qwen3.6
|
|
- Pas de gestion VRAM : Jean et Ollama pouvaient charger simultanément → OOM
|
|
|
|
## Solution : jean-router
|
|
|
|
### Architecture après
|
|
```
|
|
OpenWebUI → jean-router:3014 (routing dynamique)
|
|
├→ jean-proxy:3012 → Jean (.11:8080)
|
|
└→ wol-proxy:3011 → Ollama (.11:11434)
|
|
```
|
|
|
|
### Fonctionnalités
|
|
|
|
**1. Découverte dynamique des modèles**
|
|
- Interroge `/api/presets` de Jean (port 8090) pour lister les presets disponibles
|
|
- Interroge `/api/tags` d'Ollama (port 11434) pour lister les modèles
|
|
- Expose tout via `/v1/models` (API OpenAI-compatible)
|
|
- **Nouveaux presets Jean automatiquement visibles dans OpenWebUI**
|
|
|
|
**2. Routing intelligent**
|
|
- Si le modèle demandé est un preset Jean → switch automatique + route vers Jean
|
|
- Si c'est un modèle Ollama → route vers Ollama
|
|
- Sinon → erreur 404
|
|
|
|
**3. Gestion VRAM (RX 7800 XT, 16GB partagés)**
|
|
|
|
| Backend | Modèle typique | VRAM utilisée |
|
|
|---------|---------------|---------------|
|
|
| Jean | Ornith 35B Q6_K | ~28.5GB (déborde en RAM) |
|
|
| Ollama | mistral/qwen3.5 | ~4-6GB |
|
|
|
|
**Logique de gestion :**
|
|
|
|
**Switch Jean → Ollama :**
|
|
1. `stop_jean()` arrête Jean via son API web (port 8090)
|
|
2. Attend confirmation que Jean est arrêté (max 30s)
|
|
3. Route la requête vers Ollama
|
|
|
|
**Switch Ollama → Jean :**
|
|
1. Programme le déchargement d'Ollama dans 5 minutes (timer)
|
|
2. `start_jean()` redémarre Jean
|
|
3. Attend que le modèle soit chargé (max 90s)
|
|
4. Route la 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 pour libérer VRAM
|
|
|
|
## Fichiers créés/modifiés
|
|
|
|
### Nouveau : `jean-router/`
|
|
- `app.py` (320 lignes) : Service Flask avec routing dynamique + gestion VRAM
|
|
- `Dockerfile` : Container Python 3.12-slim
|
|
|
|
### Modifiés :
|
|
- `docker-compose.litellm.yml` :
|
|
- Ajout service jean-router
|
|
- Suppression service litellm
|
|
- OpenWebUI pointe directement vers jean-router:3014
|
|
- `litellm/config.yaml` : Supprimé (plus nécessaire)
|
|
|
|
### Configuration jean-router
|
|
```yaml
|
|
environment:
|
|
- JEAN_WEB_HOST=${OLLAMA_HOST}
|
|
- JEAN_WEB_PORT=8090
|
|
- JEAN_PROXY_HOST=localhost
|
|
- JEAN_PROXY_PORT=3012
|
|
- OLLAMA_PROXY_HOST=localhost
|
|
- OLLAMA_PROXY_PORT=3011
|
|
- OLLAMA_DIRECT_HOST=${OLLAMA_HOST}
|
|
- OLLAMA_DIRECT_PORT=11434
|
|
- ENABLE_OLLAMA=true
|
|
- ENABLE_VRAM_MANAGEMENT=true
|
|
- OLLAMA_KEEP_ALIVE_MINUTES=5
|
|
- LISTEN_PORT=3014
|
|
- SWITCH_TIMEOUT=90
|
|
- CHECK_INTERVAL=2
|
|
- JEAN_STOP_TIMEOUT=30
|
|
- OLLAMA_UNLOAD_TIMEOUT=15
|
|
```
|
|
|
|
## Déploiement sur effi-server
|
|
|
|
### Initialisation du repo git
|
|
Le dossier `/home/effi/effi` n'était pas un repo git. Initialisation :
|
|
```bash
|
|
cd /home/effi/effi
|
|
git init
|
|
git remote add origin https://gitea.folzocorp.fr/Folzo/Effi.git
|
|
git fetch origin
|
|
git checkout -b main origin/main
|
|
```
|
|
|
|
**Note :** Conflits avec fichiers existants (docker-compose.yml, jean-proxy/, etc.) résolus en les déplaçant temporairement dans `/tmp/effi-backup/`.
|
|
|
|
### Commandes de déploiement
|
|
```bash
|
|
cd /home/effi/effi
|
|
git pull
|
|
docker compose -f docker-compose.yml -f docker-compose.litellm.yml up -d --build jean-router
|
|
docker compose -f docker-compose.yml -f docker-compose.litellm.yml up -d --force-recreate open-webui
|
|
docker stop litellm && docker rm litellm # Suppression LiteLLM
|
|
```
|
|
|
|
## Tests et validation
|
|
|
|
### Modèles visibles dans OpenWebUI
|
|
|
|
**Jean (5 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
|
|
|
|
**Ollama :**
|
|
- mistral (7.2B, Q4_K_M, tools)
|
|
- qwen3.5:9b (9.7B, Q4_K_M, vision, thinking)
|
|
|
|
### 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
|
|
```
|
|
|
|
## Avantages
|
|
|
|
1. **Dynamique** : Nouveaux presets Jean automatiquement visibles sans modifier de config
|
|
2. **Simplifié** : Une couche de moins (LiteLLM supprimé)
|
|
3. **VRAM-safe** : Pas de contention mémoire entre Jean et Ollama
|
|
4. **Intelligent** : Ollama reste en mémoire 5 min pour les usages répétés
|
|
5. **Transparent** : OpenWebUI voit tous les modèles via un seul endpoint
|
|
|
|
## Limitations et points d'attention
|
|
|
|
### 1. 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
|
|
|
|
### 2. 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
|
|
|
|
### 3. 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`
|
|
|
|
### 4. Pas de failover
|
|
- Si Jean échoue (timeout, erreur), pas de fallback automatique vers Ollama
|
|
- **À considérer** : Ajouter un fallback dans jean-router si besoin
|
|
|
|
## Commits Gitea
|
|
|
|
```
|
|
8b7c4fb feat: add VRAM management for Jean/Ollama switching
|
|
20767c2 feat: keep Ollama models in memory for 5 minutes
|
|
babe4ee chore: add OLLAMA_KEEP_ALIVE_MINUTES=5 to docker-compose
|
|
43aacc0 refactor: OpenWebUI points directly to jean-router (no LiteLLM needed)
|
|
856ba4a refactor: remove LiteLLM, jean-router handles all routing
|
|
fcad415 feat: jean-router — routing dynamique Jean + Ollama
|
|
b441f34 fix: jean-router syntax error (missing except block)
|
|
```
|
|
|
|
## Réflexions et décisions
|
|
|
|
### Pourquoi supprimer LiteLLM ?
|
|
- LiteLLM ne faisait pas de pass-through de `/v1/models`
|
|
- Configuration statique incompatible avec les presets dynamiques de Jean
|
|
- Couche supplémentaire inutile : jean-router fait le même travail
|
|
- **Décision** : OpenWebUI pointe directement vers jean-router:3014
|
|
|
|
### Pourquoi garder Ollama en mémoire 5 min ?
|
|
- Ollama sert principalement pour la vision (modèles que Jean ne supporte pas)
|
|
- Usage typique : quelques requêtes vision consécutives, puis retour à Jean
|
|
- 5 min = bon compromis entre réactivité et libération VRAM
|
|
- **Configurable** : `OLLAMA_KEEP_ALIVE_MINUTES` dans docker-compose
|
|
|
|
### Pourquoi arrêter Jean complètement ?
|
|
- Jean n'a pas d'API pour décharger le modèle sans arrêter le service
|
|
- Le modèle reste en VRAM tant que llama-server tourne
|
|
- **Alternative envisagée** : `systemctl stop jean` (mais nécessite sudo)
|
|
- **Solution retenue** : API web de Jean (`/api/stop`) plus simple
|
|
|
|
### Architecture future possible
|
|
- **JeanOrchestrator** : Jean pourrait gérer lui-même le switch entre presets
|
|
- **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.)
|
|
|
|
## Prochaines étapes
|
|
|
|
1. **Monitorer** : Vérifier que la gestion VRAM fonctionne bien en production
|
|
2. **Ajuster** : Modifier `OLLAMA_KEEP_ALIVE_MINUTES` selon l'usage réel
|
|
3. **Documenter** : Mettre à jour `docs/ARCHITECTURE-LITELLM.md` (à renommer `ARCHITECTURE-JEAN-ROUTER.md`)
|
|
4. **Tester** : Valider que les nouveaux presets Jean apparaissent bien automatiquement
|
|
|
|
## Conclusion
|
|
|
|
**jean-router** remplace avantageusement LiteLLM avec :
|
|
- Routing dynamique basé sur les presets Jean
|
|
- Gestion automatique de la VRAM partagée
|
|
- Architecture simplifiée (une couche de moins)
|
|
- Nouveaux modèles automatiquement disponibles
|
|
|
|
L'architecture est maintenant prête pour Effi : quand un nouveau preset Jean est créé, il apparaît immédiatement dans OpenWebUI sans aucune configuration supplémentaire.
|