docs: add jean-router architecture and session journal
- 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
This commit is contained in:
@@ -10,8 +10,8 @@ Effi est un assistant IA personnel **100% auto-hébergé**, avec mémoire persis
|
|||||||
|
|
||||||
| Machine | IP | Rôle | Services |
|
| Machine | IP | Rôle | Services |
|
||||||
|---------|-----|------|----------|
|
|---------|-----|------|----------|
|
||||||
| **effi-server** | `192.168.1.27` | Serveur Docker principal | OpenWebUI, Qdrant, n8n, Ollama proxy |
|
| **effi-server** | `192.168.1.27` | Serveur Docker principal | OpenWebUI, Qdrant, n8n, jean-router |
|
||||||
| **folzo-Corp** | `192.168.1.11` | LLM + STT + TTS + Gen | Ollama, ComfyUI, effi-stt, openedai-speech, kreuzberg |
|
| **folzo-Corp** | `192.168.1.11` | LLM + STT + TTS + Gen | Jean, Ollama, ComfyUI, effi-stt, openedai-speech, kreuzberg |
|
||||||
| **NAS Synology** | `192.168.1.25` | Monitoring | Beszel Hub |
|
| **NAS Synology** | `192.168.1.25` | Monitoring | Beszel Hub |
|
||||||
|
|
||||||
### Schéma Global
|
### Schéma Global
|
||||||
@@ -28,26 +28,30 @@ Effi est un assistant IA personnel **100% auto-hébergé**, avec mémoire persis
|
|||||||
│ EFFI SERVER (192.168.1.27) │ │ FOLZO-CORP (192.168.1.11) │
|
│ EFFI SERVER (192.168.1.27) │ │ FOLZO-CORP (192.168.1.11) │
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
│ ┌──────────────┐ ┌──────────┐ │ │ ┌──────────────┐ ┌──────────────┐ │
|
│ ┌──────────────┐ ┌──────────┐ │ │ ┌──────────────┐ ┌──────────────┐ │
|
||||||
│ │ Open WebUI │ │ Qdrant │ │ │ │ Ollama │ │ ComfyUI │ │
|
│ │ Open WebUI │ │ Qdrant │ │ │ │ Jean │ │ Ollama │ │
|
||||||
│ │ Port 3000 │ │ Port 6333│ │ │ │ Port 11434 │ │ Port 8188 │ │
|
│ │ Port 3000 │ │ Port 6333│ │ │ │ Port 8080 │ │ Port 11434 │ │
|
||||||
│ └──────────────┘ └──────────┘ │ │ │ qwen3:8b │ │ Image/Audio │ │
|
│ └──────────────┘ └──────────┘ │ │ │ llama.cpp │ │ Vision │ │
|
||||||
│ │ │ └──────────────┘ └──────────────┘ │
|
│ │ │ │ Ornith 35B │ │ mistral, │ │
|
||||||
│ ┌──────────────┐ ┌──────────┐ │ │ │
|
│ ┌──────────────┐ ┌──────────┐ │ │ │ Qwen3.6... │ │ qwen3.5:9b │ │
|
||||||
│ │ n8n │ │ wol-proxy│ │ │ ┌──────────────┐ ┌──────────────┐ │
|
│ │ n8n │ │jean-router│ │ │ └──────────────┘ └──────────────┘ │
|
||||||
│ │ Port 5678 │ │ Port 3011│ │ │ │ effi-stt │ │ openedai │ │
|
│ │ Port 5678 │ │ Port 3014│ │ │ │
|
||||||
│ └──────────────┘ └──────────┘ │ │ │ Port 8200 │ │ Port 8100 │ │
|
|
||||||
│ │ │ │ Whisper STT │ │ TTS │ │
|
|
||||||
│ ┌──────────────┐ ┌──────────┐ │ │ └──────────────┘ └──────────────┘ │
|
|
||||||
│ │ Beszel Agent │ │ Watchtower│ │ │ │
|
|
||||||
│ └──────────────┘ └──────────┘ │ │ ┌──────────────┐ ┌──────────────┐ │
|
│ └──────────────┘ └──────────┘ │ │ ┌──────────────┐ ┌──────────────┐ │
|
||||||
└─────────────────────────────────┘ │ │ kreuzberg │ │ Beszel Agent │ │
|
│ │ │ │ ComfyUI │ │ effi-stt │ │
|
||||||
│ │ Port 8300 │ │ Monitoring │ │
|
│ ┌──────────────┐ ┌──────────┐ │ │ │ Port 8188 │ │ Port 8200 │ │
|
||||||
│ │ Doc Extract │ └──────────────┘ │
|
│ │ Beszel Agent │ │ Watchtower│ │ │ │ Image/Audio │ │ Whisper STT │ │
|
||||||
│ └──────────────┘ │
|
│ └──────────────┘ └──────────┘ │ │ └──────────────┘ └──────────────┘ │
|
||||||
│ RX 7800 XT (VRAM 17GB) │
|
└─────────────────────────────────┘ │ │
|
||||||
|
│ ┌──────────────┐ ┌──────────────┐ │
|
||||||
|
│ │ openedai │ │ kreuzberg │ │
|
||||||
|
│ │ Port 8100 │ │ Port 8300 │ │
|
||||||
|
│ │ TTS │ │ Doc Extract │ │
|
||||||
|
│ └──────────────┘ └──────────────┘ │
|
||||||
|
│ RX 7800 XT (VRAM 16GB) │
|
||||||
└─────────────────────────────────────────┘
|
└─────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note :** jean-router gère le routing dynamique entre Jean et Ollama avec gestion automatique de la VRAM partagée. Voir [ARCHITECTURE-JEAN-ROUTER.md](docs/ARCHITECTURE-JEAN-ROUTER.md) pour les détails.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 Services
|
## 📦 Services
|
||||||
@@ -56,13 +60,16 @@ Effi est un assistant IA personnel **100% auto-hébergé**, avec mémoire persis
|
|||||||
|
|
||||||
| Service | Port | Image | Rôle | RAM/VRAM | Auto-unload |
|
| Service | Port | Image | Rôle | RAM/VRAM | Auto-unload |
|
||||||
|---------|------|-------|------|----------|-------------|
|
|---------|------|-------|------|----------|-------------|
|
||||||
| **Ollama** | 11434 | Native | LLM inference (gemma4:e4b) | ~10GB | systemd auto-start |
|
| **Jean** | 8080 | Native | LLM inference (llama.cpp, presets dynamiques) | ~28GB | Arrêt via API |
|
||||||
|
| **Ollama** | 11434 | Native | LLM vision (mistral, qwen3.5:9b) | ~4-6GB | 5min après dernier usage |
|
||||||
| **ComfyUI** | 8188 | `yanwk/comfyui-boot:rocm` | Image + Audio generation | 18GB shm | ✅ après 10min idle |
|
| **ComfyUI** | 8188 | `yanwk/comfyui-boot:rocm` | Image + Audio generation | 18GB shm | ✅ après 10min idle |
|
||||||
| **effi-stt** | 8200 | Build local | Whisper STT | ~20MB | ✅ après 5min idle |
|
| **effi-stt** | 8200 | Build local | Whisper STT | ~20MB | ✅ après 5min idle |
|
||||||
| **openedai-speech** | 8100 | `ghcr.io/matatonic/openedai-speech` | TTS | ~15MB | Non (GPU) |
|
| **openedai-speech** | 8100 | `ghcr.io/matatonic/openedai-speech` | TTS | ~15MB | Non (GPU) |
|
||||||
| **kreuzberg** | 8300 | `ghcr.io/kreuzberg-dev/kreuzberg:latest` | Document extraction | ~5MB | ✅ après 10min idle |
|
| **kreuzberg** | 8300 | `ghcr.io/kreuzberg-dev/kreuzberg:latest` | Document extraction | ~5MB | ✅ après 10min idle |
|
||||||
| **beszel-agent** | - | `henrygd/beszel-agent:latest` | Monitoring | ~5MB | Auto-start |
|
| **beszel-agent** | - | `henrygd/beszel-agent:latest` | Monitoring | ~5MB | Auto-start |
|
||||||
|
|
||||||
|
**Note :** Jean et Ollama partagent la VRAM (RX 7800 XT, 16GB). jean-router gère automatiquement le switch pour éviter la contention mémoire.
|
||||||
|
|
||||||
### EffiServer (192.168.1.27)
|
### EffiServer (192.168.1.27)
|
||||||
|
|
||||||
| Service | Port | Image | Rôle |
|
| Service | Port | Image | Rôle |
|
||||||
@@ -70,6 +77,8 @@ Effi est un assistant IA personnel **100% auto-hébergé**, avec mémoire persis
|
|||||||
| **Open WebUI** | 3000 | `ghcr.io/open-webui/open-webui:main` | Interface IA |
|
| **Open WebUI** | 3000 | `ghcr.io/open-webui/open-webui:main` | Interface IA |
|
||||||
| **Qdrant** | 6333 | `qdrant/qdrant:latest` | Vector DB (RAG) |
|
| **Qdrant** | 6333 | `qdrant/qdrant:latest` | Vector DB (RAG) |
|
||||||
| **n8n** | 5678 | `docker.n8n.io/n8nio/n8n:latest` | Automatisation |
|
| **n8n** | 5678 | `docker.n8n.io/n8nio/n8n:latest` | Automatisation |
|
||||||
|
| **jean-router** | 3014 | Build local | Routing dynamique Jean/Ollama + gestion VRAM |
|
||||||
|
| **jean-proxy** | 3012 | Build local | Wake-on-LAN + Jean proxy |
|
||||||
| **wol-proxy** | 3011 | Build local | Wake-on-LAN + Ollama proxy |
|
| **wol-proxy** | 3011 | Build local | Wake-on-LAN + Ollama proxy |
|
||||||
| **beszel-agent** | - | `henrygd/beszel-agent:latest` | Monitoring |
|
| **beszel-agent** | - | `henrygd/beszel-agent:latest` | Monitoring |
|
||||||
|
|
||||||
@@ -85,9 +94,27 @@ Effi est un assistant IA personnel **100% auto-hébergé**, avec mémoire persis
|
|||||||
|
|
||||||
### 💬 Chat IA
|
### 💬 Chat IA
|
||||||
- **Interface**: Open WebUI
|
- **Interface**: Open WebUI
|
||||||
- **LLM principal**: gemma4:e4b (via Ollama, 9.6GB, 128K context)
|
- **LLM principal**: Jean (llama.cpp) avec presets dynamiques
|
||||||
- **LLM secondaire**: qwen3:8b (tâches légères)
|
- **ornith-35b-q6** (35B, Q6_K, reasoning) — modèle principal
|
||||||
- **Autres modèles**: qwen3.5, gemma4:26b, deepseek-r1, magistral, etc.
|
- **qwen3.6-27b-mtp** (27B, IQ4_XS, reasoning, MTP)
|
||||||
|
- **qwen3.6-35b-moe-compact** (35B MoE, compact)
|
||||||
|
- **qwen3.6-35b-moe-compact-reason** (35B MoE, reasoning)
|
||||||
|
- **qwen3.6-35b-moe-nano** (35B MoE, nano)
|
||||||
|
- **LLM vision**: Ollama (mistral, qwen3.5:9b) pour les tâches avec images
|
||||||
|
- **Routing automatique**: jean-router détecte le modèle demandé et route vers Jean ou Ollama
|
||||||
|
- **Gestion VRAM**: Switch automatique entre Jean et Ollama pour éviter la contention mémoire
|
||||||
|
|
||||||
|
**Note :** Nouveaux presets Jean apparaissent automatiquement dans OpenWebUI sans configuration. Voir [ARCHITECTURE-JEAN-ROUTER.md](docs/ARCHITECTURE-JEAN-ROUTER.md) pour les détails.
|
||||||
|
|
||||||
|
### 🚀 jean-router — Routing dynamique LLM
|
||||||
|
- **Découverte automatique**: Interroge Jean et Ollama pour lister les modèles disponibles
|
||||||
|
- **Routing intelligent**: Route vers Jean (presets) ou Ollama (vision) selon le modèle demandé
|
||||||
|
- **Switch automatique**: Change de preset Jean si nécessaire (via API web)
|
||||||
|
- **Gestion VRAM**:
|
||||||
|
- Arrête Jean quand on passe sur Ollama (libère VRAM)
|
||||||
|
- Garde Ollama en mémoire 5 min avant déchargement automatique
|
||||||
|
- Évite la contention mémoire entre les deux backends
|
||||||
|
- **API OpenAI-compatible**: `/v1/models` et `/v1/chat/completions`
|
||||||
|
|
||||||
### 🎤 Pipeline Vocal
|
### 🎤 Pipeline Vocal
|
||||||
- **STT**: Whisper large-v3 via effi-stt (CPU, déchargement auto)
|
- **STT**: Whisper large-v3 via effi-stt (CPU, déchargement auto)
|
||||||
@@ -164,6 +191,8 @@ python3 scripts/beszel_client.py --system folzocorp --check stats
|
|||||||
|
|
||||||
## 📚 Documentation
|
## 📚 Documentation
|
||||||
|
|
||||||
|
- [Architecture jean-router](./docs/ARCHITECTURE-JEAN-ROUTER.md) — Routing dynamique LLM + gestion VRAM
|
||||||
|
- [Session 2026-07-20](./docs/05-journal/SESSION-2026-07-20-JEAN-ROUTER.md) — Création de jean-router
|
||||||
- [Installation Beszel](./docs/BESZEL-INSTALLATION.md)
|
- [Installation Beszel](./docs/BESZEL-INSTALLATION.md)
|
||||||
- [Gestion Mémoire](./docs/MEMOIRE-GESTION.md)
|
- [Gestion Mémoire](./docs/MEMOIRE-GESTION.md)
|
||||||
- [Session 2026-04-12](./docs/SESSION-2026-04-12-RAM-BESZEL.md)
|
- [Session 2026-04-12](./docs/SESSION-2026-04-12-RAM-BESZEL.md)
|
||||||
@@ -176,7 +205,8 @@ python3 scripts/beszel_client.py --system folzocorp --check stats
|
|||||||
|
|
||||||
| Domaine | Technologies |
|
| Domaine | Technologies |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| **LLM** | Ollama, qwen3:8b |
|
| **LLM** | Jean (llama.cpp), Ollama (vision) |
|
||||||
|
| **Routing** | jean-router (Flask, routing dynamique + gestion VRAM) |
|
||||||
| **Interface** | Open WebUI |
|
| **Interface** | Open WebUI |
|
||||||
| **STT** | Faster Whisper (CPU) |
|
| **STT** | Faster Whisper (CPU) |
|
||||||
| **TTS** | XTTS v2 (ROCm GPU) |
|
| **TTS** | XTTS v2 (ROCm GPU) |
|
||||||
@@ -238,6 +268,23 @@ effi/
|
|||||||
|
|
||||||
## 📝 Historique
|
## 📝 Historique
|
||||||
|
|
||||||
|
**Juillet 2026** - jean-router : routing dynamique LLM + gestion VRAM
|
||||||
|
|
||||||
|
### jean-router ✅
|
||||||
|
- Création de jean-router (Flask) pour routing dynamique entre Jean et Ollama
|
||||||
|
- Découverte automatique des presets Jean via `/api/presets`
|
||||||
|
- Découverte automatique des modèles Ollama via `/api/tags`
|
||||||
|
- Nouveaux presets Jean automatiquement visibles dans OpenWebUI
|
||||||
|
- Gestion VRAM : arrêt Jean quand on passe sur Ollama, timer 5min pour Ollama
|
||||||
|
- Suppression de LiteLLM (une couche de moins, config statique obsolète)
|
||||||
|
- OpenWebUI pointe directement vers jean-router:3014
|
||||||
|
|
||||||
|
### Jean (llama.cpp) ✅
|
||||||
|
- Migration vers Jean comme backend LLM principal
|
||||||
|
- Presets dynamiques : ornith-35b-q6, qwen3.6-27b-mtp, qwen3.6-35b-moe-compact...
|
||||||
|
- Service systemd avec démarrage au boot (sans login)
|
||||||
|
- API web pour gestion (start/stop/switch presets)
|
||||||
|
|
||||||
**Avril 2026** - Optimisation RAM + Monitoring + Tool OpenWebUI
|
**Avril 2026** - Optimisation RAM + Monitoring + Tool OpenWebUI
|
||||||
|
|
||||||
### RAM Management ✅
|
### RAM Management ✅
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
# 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
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
# 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 — réalisée
|
|
||||||
|
|
||||||
⚠️ LiteLLM expose l'**API OpenAI** (`/v1/*`), **pas** l'API Ollama (`/api/*`).
|
|
||||||
open-webui ne peut donc pas brancher LiteLLM sur sa section "Ollama" (≠ protocole).
|
|
||||||
|
|
||||||
L'override `docker-compose.litellm.yml` configure donc open-webui via la section **OpenAI API** :
|
|
||||||
- `OPENAI_API_BASE_URL=http://host.docker.internal:4000/v1`
|
|
||||||
- `OPENAI_API_KEY=sk-effi-litellm-master`
|
|
||||||
- `ENABLE_OPENAI_API=True`
|
|
||||||
- `OLLAMA_BASE_URL=http://host.docker.internal:3011` (conservé pour rétrocompat — mais le PC gaming OOM si Jean tourne, donc ce backend ne remonte pas de modèles)
|
|
||||||
|
|
||||||
Appliqué via :
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.yml -f docker-compose.litellm.yml up -d --force-recreate open-webui
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans l'UI open-webui : **Admin → Settings → Connections → "OpenAI API"**. L'URL est pré-remplie (`http://host.docker.internal:4000/v1`) et la clé `sk-effi-litellm-master`. Les 4 modèles Qwen3.6 (`qwen3.6-reason`, `qwen3.6-27b`, `qwen3.6-fast`, `qwen3.6`) apparaissent dans le dropdown du chat.
|
|
||||||
|
|
||||||
### Modèles exposés (profil Qwen3.6 — vramp oblige)
|
|
||||||
|
|
||||||
| Modèle dans open-webui | Raisonnement | Quand l'utiliser | Latence TTFT |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `qwen3.6-fast` / `qwen3.6` | OFF | Chat, mail, extraction simple | ~0.4-1 s |
|
|
||||||
| `qwen3.6-reason` / `qwen3.6-27b` | ON | Code, analyse, rapports, RAG, tâches complexes | 10-40 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.
|
|
||||||
Reference in New Issue
Block a user