# llama-server — Inférence LLM (gpu-01)

llama-server (llama.cpp compilé with ROCm 7.x) tourne en **deux instances systemd** sur **gpu-01** (`192.168.10.20`).
API 100% compatible OpenAI — remplace LM Studio pour l'inférence.

---

## Deux 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-embed` | 1238 | nomic-embed-text-v1.5 f16 | CPU | Embeddings dédiés (mémoire STT + RAG funk-docs, dim 768) |

Les modèles sont sur NFS monté depuis storage-01 (`/mnt/models`).

> **Instances CPU retirées (2026-06-20)** : `llama-server-system` (:1236), `llama-server-monitor`
> (:1237) et `llama-server-dev` (:1235) — services **manuels** inutilisés (déjà orphelins de
> LiteLLM) — ont été supprimés. Les profils Hermès `system`/`monitor` n'ont plus de backend dédié.

> `llama-embed` (`:1238`) est **géré par Ansible** (rôle `llama_server`, `llama_embed_enabled`).
> Sert un modèle d'embedding **dédié** (`nomic-embed-text`, dim 768), bien plus discriminant que
> Qwen3-8B réutilisé. Tourne en **CPU** (`--n-gpu-layers 0`) pour ne pas entrer en contention GPU
> avec `:1234` (la contention causait une latence bimodale 16 ms/9-12 s → `docs=0` du STT, cf.
> `admin/incidents-llm-gpu.md`).

---

## Service systemd

```bash
# Instance GPU (principale)
sudo systemctl status llama-server
sudo systemctl restart llama-server
sudo journalctl -u llama-server -f

# (instances CPU system/monitor/dev retirées le 2026-06-20)

# Instance CPU — 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 instance embed (CPU)
curl -s http://192.168.10.20:1238/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 le job `llama-server-gpu` défini dans `k8s/infra/monitoring/values.yaml` (jobs `llama-server-system`/`-monitor` retirés le 2026-06-20).

---

## 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
```

---

## Instances CPU retirées (2026-06-20)

Le rôle `ansible/roles/llama_server/` déploie l'instance **GPU** (`llama-server.service` :1234) et l'instance **embeddings** (`llama-embed` :1238, activée par `llama_embed_enabled`).

Les anciennes instances CPU **manuelles** `llama-server-system` (:1236), `llama-server-monitor` (:1237) et `llama-server-dev` (:1235) ont été **supprimées** : inutilisées et déjà orphelines de LiteLLM (aucun alias dans `/etc/litellm/config.yaml`). Suppression = `systemctl disable --now` + `rm` des units + ports firewall 1235/1236/1237 fermés + scrape/alertes Prometheus retirés. Pour réactiver un profil `system`/`monitor`, repointer son alias LiteLLM sur `qwen3-8b` (GPU).

**Axe d'amélioration** : étendre le rôle `llama_server` pour déployer aussi les 2 instances CPU 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 services échouent au démarrage |
| 2 services indépendants | `llama-server` (:1234 GPU) et `llama-embed` (:1238 CPU) ont chacun leur service systemd |
| Instances CPU retirées | llama-server-system/-monitor/-dev supprimés le 2026-06-20 (inutilisés) — voir section dédiée ci-dessus |
