# llama-server — Inférence LLM (gpu-01) llama-server (llama.cpp compilé with ROCm 7.x) tourne en **quatre instances systemd** sur **gpu-01** (`192.168.10.20`). API 100% compatible OpenAI — remplace LM Studio pour l'inférence. --- ## Quatre instances | Service systemd | Port | Modèle | Backend | Usage | |---|---|---|---|---| | `llama-server` | 1234 | Qwen3-8B Q4_K_M | GPU (RX 6700XT) | Inférence principale — Hermes + LiteLLM | | `llama-server-system` | 1236 | Qwen3-1.7B Q4_K_M | CPU | Profil Hermes `system` (tâches légères) | | `llama-server-monitor` | 1237 | Qwen3-1.7B Q4_K_M | CPU | Profil Hermes `monitor` (supervision) | | `llama-embed` | 1238 | nomic-embed-text-v1.5 f16 | GPU | Embeddings dédiés (mémoire STT, dim 768) | Les instances CPU (`system`, `monitor`) partagent le même binaire mais des configs distinctes. Les modèles sont sur NFS monté depuis storage-01 (`/mnt/models`). > `llama-embed` (`:1238`) est **géré par Ansible** (rôle `llama_server`, `llama_embed_enabled`) — > contrairement aux instances CPU (gap IaC, voir plus bas). Sert un modèle d'embedding **dédié** > (`nomic-embed-text`, dim 768), bien plus discriminant que Qwen3-8B réutilisé. Partage le GPU > avec `:1234` (~300 Mo VRAM). --- ## Service systemd ```bash # Instance GPU (principale) sudo systemctl status llama-server sudo systemctl restart llama-server sudo journalctl -u llama-server -f # Instance CPU — profil system sudo systemctl status llama-server-system sudo systemctl restart llama-server-system sudo journalctl -u llama-server-system -f # Instance CPU — profil monitor sudo systemctl status llama-server-monitor sudo systemctl restart llama-server-monitor sudo journalctl -u llama-server-monitor -f # Instance GPU — embeddings dédiés (nomic-embed-text :1238) sudo systemctl status llama-embed sudo systemctl restart llama-embed sudo journalctl -u llama-embed -f ``` --- ## Modèle actuel (GPU) | Paramètre | Valeur | |---|---| | Modèle | Qwen3-8B Q4_K_M | | Alias API | `qwen3-8b` | | Chemin | `/mnt/models/bartowski/Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf` | | Contexte | 32768 tokens | | GPU offload | 99 layers (tout sur GPU) | | Parallélisme | 1 slot (requis pour Hermes — voir bug parallel) | | Embeddings | activés (`--embeddings --pooling mean`) | ### Contrainte VRAM (12 GB RX 6700XT) | Élément | VRAM | |---|---| | Modèle Q4_K_M 8B | ~5 GB | | KV cache à 32768 ctx | ~4 GB | | **Total** | **~9 GB** ✅ | --- ## Embeddings llama-server expose `/v1/embeddings` avec `--embeddings --pooling mean`. Le flag `--pooling mean` est obligatoire pour les LLM causaux (sinon : erreur 400 `Pooling type 'none' is not OAI compatible`). **Deux sources d'embeddings :** | Endpoint | Modèle | Dim | Usage | |---|---|---|---| | `:1234/v1/embeddings` | Qwen3-8B (chat réutilisé) | 4096 | RAG (`funk-docs`) — peu discriminant | | `:1238/v1/embeddings` | nomic-embed-text (dédié) | 768 | Mémoire STT (`stt-memory`) — précis | > Qwen3-8B n'est **pas** un modèle d'embedding dédié (cosinus uniformément hauts → recherche > peu discriminante). L'instance `llama-embed` (`:1238`) sert `nomic-embed-text`, spécialisé. > Le RAG pourra y basculer quand il sera réparé (voir `admin/ia/rag.md`). ```bash # Test embeddings dédiés nomic (:1238) — depuis le LAN cluster curl -s -X POST http://192.168.10.20:1238/v1/embeddings \ -H "Content-Type: application/json" \ -d '{"model":"nomic-embed-text","input":["test"]}' | jq '.data[0].embedding | length' # → 768 (dimension nomic-embed-text-v1.5) # Test embeddings Qwen3 (:1234) — RAG curl -s -X POST http://192.168.10.20:1234/v1/embeddings \ -H "Content-Type: application/json" \ -d '{"model":"qwen3-8b","input":["test"]}' | jq '.data[0].embedding | length' # → 4096 (dimension Qwen3-8B) ``` --- ## Performances (RX 6700XT, ROCm 7.x) | Métrique | Qwen3-1.7B (CPU) | Qwen3-8B (GPU) | |---|---|---| | Prefill | ~50 tok/s | ~300 tok/s | | Génération | ~15 tok/s | ~60 tok/s | | TTFT Hermes (15k tokens) | ~300s | ~50s | | VRAM modèle | — (CPU only) | ~5 GB | --- ## Validation API ```bash # Depuis storage-01 ou n'importe quelle machine du LAN cluster curl -s http://192.168.10.20:1234/v1/models | jq '.data[].id' # Test inférence rapide (GPU) curl http://192.168.10.20:1234/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-8b", "messages": [{"role": "user", "content": "Dis bonjour"}], "max_tokens": 50 }' # Test instances CPU curl -s http://192.168.10.20:1236/v1/models | jq '.data[].id' curl -s http://192.168.10.20:1237/v1/models | jq '.data[].id' ``` --- ## Métriques Prometheus Le flag `--metrics` est requis dans le service systemd pour activer l'endpoint `/metrics`. Sans lui, llama-server retourne `501 Not Implemented`. ```bash # Vérifier que les métriques sont disponibles curl -s http://192.168.10.20:1234/metrics | grep llamacpp # Métriques clés exposées curl -s http://192.168.10.20:1234/metrics | grep -E \ "llamacpp:prompt_tokens_total|llamacpp:tokens_predicted_total|llamacpp:requests_deferred|llamacpp:kv_cache_usage_ratio" ``` | Métrique | Description | |---|---| | `llamacpp:prompt_tokens_total` | Tokens de prompt traités (cumul) | | `llamacpp:tokens_predicted_total` | Tokens générés (cumul) | | `llamacpp:prompt_tokens_seconds` | Débit prefill (tok/s) | | `llamacpp:predicted_tokens_seconds` | Débit génération (tok/s) | | `llamacpp:requests_deferred` | Requêtes en file d'attente (backpressure) | | `llamacpp:kv_cache_usage_ratio` | Occupation KV cache (0–1) | | `llamacpp:kv_cache_tokens` | Tokens actuellement en KV cache | Ces métriques sont scrapées par Prometheus via les jobs `llama-server-gpu`, `llama-server-system`, `llama-server-monitor` définis dans `k8s/infra/monitoring/values.yaml`. --- ## Changer de modèle 1. Modifier `host_vars/gpu-01/vars.yml` : ```yaml llama_model_path: "/mnt/models/bartowski/NouveauModele.gguf" llama_model_alias: "nouveau-alias" llama_ctx_size: 32768 # ajuster selon VRAM disponible ``` 2. Redéployer : ```bash ansible-playbook -i inventory.yml playbooks/gpu-01.yml --tags llama_server ``` 3. Mettre à jour LiteLLM (alias `hermes-default`) : ```bash ansible-playbook -i inventory.yml playbooks/storage-01.yml --tags litellm ``` --- ## Build (source) ```bash # Sur gpu-01 — rebuild si besoin cd /opt/llama.cpp git pull cmake -B build \ -DGGML_HIP=ON \ -DAMDGPU_TARGETS=gfx1030 \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_HIP_COMPILER=/opt/rocm/llvm/bin/clang++ \ -DCMAKE_PREFIX_PATH=/opt/rocm cmake --build build --target llama-server -j$(nproc) sudo systemctl restart llama-server ``` --- ## ⚠️ Gap IaC — instances CPU non couvertes par Ansible Le rôle `ansible/roles/llama_server/` ne déploie que l'instance **GPU** (`llama-server.service` sur :1234). Les instances CPU (`llama-server-system` :1236 et `llama-server-monitor` :1237) ont été créées **manuellement** et ne sont pas encore dans le code Ansible. Conséquence : un `make apply-gpu` complet sur un hôte vierge ne déploierait PAS les instances CPU. **Axe d'amélioration** : étendre le rôle `llama_server` pour déployer les 3 instances via une boucle (`with_items` ou `loop` Ansible), avec des variables dédiées dans `host_vars/gpu-01/vars.yml`. --- ## Points d'attention | Sujet | Détail | |---|---| | GPU non officiel | RX 6700XT (gfx1031) — `HSA_OVERRIDE_GFX_VERSION=10.3.0` dans le service | | ROCm version | 7.2.3 — libs dans `/opt/rocm` | | --metrics obligatoire | Sans ce flag, `/metrics` retourne `501 Not Implemented` — Prometheus ne scrappe rien | | parallel=1 obligatoire | `--parallel 4` divise le ctx en slots → contexte insuffisant pour Hermes | | pooling mean obligatoire | Sans `--pooling mean`, l'endpoint `/v1/embeddings` retourne erreur 400 | | Qwen3 = modèle « thinking » | Par défaut Qwen3 raisonne avant de répondre → consomme le budget `max_tokens` en `reasoning_content` (`content` vide) ou réfléchit longtemps (timeout). Ajouter **`/no_think`** au prompt système pour le désactiver. STT le fait (`STT_DISABLE_THINKING`) ; à considérer pour tout consommateur de Qwen3 à `max_tokens` faible (ask-agent, profils Hermes). | | Gemma 4 interdit | Crash ROCm sur prompts >800 tokens | | Modèles sur NFS | `/mnt/models` monté depuis storage-01 (NFS) — si NFS down, les 3 services échouent au démarrage | | 3 services indépendants | Chaque instance a son propre service systemd — redémarrer l'un n'affecte pas les autres | | Instances CPU hors Ansible | llama-server-system et llama-server-monitor créés manuellement — voir section Gap IaC ci-dessus |