# Kazeia-Engine — handoff intégration (MAJ 26/05/2026)

Point d'entrée unique. Remplace le LLM ExecuTorch/Genie par llama.cpp (fork ql) + Hexagon.
GGUF, pas de `.pte`. STT reste ORT-QAIRT (inchangé).

**`.pte` retiré (plus de pré-compilé) → l'engine est le SEUL runtime LLM.** Le dense (qwen3) tourne donc
sur l'engine en **HVX** (~40 prefill, stable) ; le passer en **HMX** (~295) = chantier `CHANTIER_HMX_DENSE.md`
(crash k=4096 localisé, non-bloquant). Le Speaker retenu (Qwen3.5 `q35-lmq4`) tourne en option C (HMX), inchangé.

## Générique : fait tourner N'IMPORTE QUEL LLM (détecté à l'archi, au `load`, les deux sur HTP)
Le JNI lit `general.architecture` dans l'en-tête GGUF **avant l'init backend** (pour régler `USE_HMX`), puis route :
- **Hybride GDN (qwen3.5 / qwen3next)** → **option C** : prefill HTP (**HMX on**) → transfert KV → decode CPU
  (le decode GDN sur HTP est lent, on le garde CPU). Prefill ~110-180. Validé (mono-tour + mémoire).
- **Dense (qwen3, …)** → **HTP contexte-unique, matmul HVX (`USE_HMX=0`)** : prefill+decode sur HTP, ~40 prefill
  (≈3× le CPU 14), decode ~11. Validé (qwen3-4B cohérent, plus de crash).
- **pas de HTP / échec** → CPU pur (repli universel).

⚠ **Root cause du crash dense-HTP (élucidé)** : le **matmul HMX (fp16 tile)** faute (`dspqueue_read 0x2e`) sur les
**activations réelles** du dense (« massive activations » de Qwen3 > plage fp16 ; invisible en bench synthétique,
déclenché par du vrai texte > ~14 tokens). Confirmé : `GGML_HEXAGON_USE_HMX=0` (matmul HVX) supprime le crash,
`=1` le reproduit ; et `OPFILTER=MUL_MAT` (matmuls→CPU) aussi. → dense en HVX sur HTP. La 3.5 garde HMX (elle ne
faute pas). TODO backend possible : rendre le matmul HMX robuste aux activations hors plage fp16 (clamp/scale).
Les deux cibles tournent sur HTP, validées via `jni/test_jni_native.cpp`.

## Décisions figées cette session
- **Modèle Speaker = Qwen3.5-4B en variante `q35-lmq4.gguf`** (embeds en Q4 au lieu du Q6_K
  par défaut → 2.38 GB, decode +5%, qualité ≈ Q4_0 mesurée). Choisi sur l'éval qualité
  (`../eval/VERDICT.md`) : gagne le SAFETY (cite 3114/15) et la majorité du SUPPORT vs le dense.
- **9B écarté** (decode ~4-5 t/s réel, trop lent ; pas de gain qualité justifiant). Cascade
  éventuelle = Guard-4B (thinker) + 4B (speaker), pas 9B.
- **Kernel GDN = chantier clos** : son calcul n'est PAS le goulot (cf RAPPORT_RD §7). Ne pas
  y retoucher pour la perf. Decode au plafond BW CPU (~25 GB/s).

## État du bridge JNI (`jni/kazeia_engine_jni.cpp`) — OPTION C, validé end-to-end (27/05)
Le JNI implémente **C** (prefill HTP / decode CPU). **Validé bout-en-bout sur device** via `jni/test_jni_native.cpp`
(dlopen + JNIEnv mock → `load`/`generate`/`generateRaw`/`free` : sortie FR cohérente, mémoire conversationnelle OK)
et `jni/dual_ctx_mt.cpp` (3 tours, prefill HTP 103-144 t/s, decode CPU ~7-9).
- `load()` : pose `GGML_HEXAGON_GDN_PREFILL=1` (le fallback CPU du conv1d multi-token est dans le backend, pas
  d'OPFILTER), charge le modèle **2×** : instance HTP (ngl99, device HTP0, t8) + instance CPU (ngl0, t4). ~4.7 GB.
- **API** : `generate(sys,usr,max)` (mono-tour) **OU `generateRaw(prompt,max)`** (multi-tour, prompt pré-formaté).
  `EngineLlmEngine.kt` fournit **`ChatSession`** (accumule l'historique + construit le ChatML validé → l'app n'a
  pas à connaître le template). Cœur : prefill HTP → `llama_state_seq_get/set_data` (transfert KV) → decode CPU.
  Sans état entre appels (re-prefill de l'historique complet sur HTP, rapide).
- KV **f16**, flash_attn ON, thinking OFF (`<think></think>` vide), greedy. API : `load/generate/generateRaw/reset/free`.
- `lib/libkazeia_engine.so` **à jour (option C, 5 symboles JNI, SHARED)** + `libggml-hexagon.so`
  (fix SSM_CONV) + `libggml-htp-v79.so` à jour. Prebuilts utilisables tels quels (NDK r27d) ; **rebuild depuis
  `jni/` dans ton build app recommandé** pour garantir l'ABI/STL (CMakeLists.txt fourni, linke llama+ggml+ggml-base+log).

## Décisions (vérifiées 27/05, batterie 90%, device froid)
1. **KV = f16 (RÉSOLU, corrigé dans le JNI).** Mesuré : decode q35-lmq4 f16=**10.9** vs q8_0=**6.5**
   (−40%, idem à d=512). Le déquant KV en flash-attn coûte plus que le BW épargné. `type_k/v`
   repassé en `GGML_TYPE_F16` (vérifié `jni/kazeia_engine_jni.cpp:41`). → `lib/libkazeia_engine.so`
   (27/05) **déjà rebuildé en f16**, prêt. q8_0 seulement si OOM KV en très long contexte.
2. **Prefill HTP : CORRIGÉ (fix intégré au backend).** Cause du charabia localisée à **une seule op,
   SSM_CONV (conv1d), cassée sur HTP en multi-token (prefill) pour certains d_inner (1024/2048 ; 1536 OK)**
   — bug données/marshalling côté DSP (HVX ET scalaire HTP faux, ggml-cpu correct), non localisé à la ligne.
   **Fix livré dans `libggml-hexagon.so`** (`ggml_hexagon_supported_ssm_conv` : `n_t>1 → CPU`) : le conv1d
   prefill tourne sur ggml-cpu (correct, bon marché), le reste du prefill sur HTP, decode conv sur HTP (n_t=1).
   Plus besoin de `OPFILTER`. Validé : oracle SSM_CONV 18/18, cli `-dev HTP0` cohérent, multi-tour cohérent+mémoire.
   Mesuré : **prefill HTP 110-180 t/s** (×8-13 vs CPU 14), sortie correcte. Libs prebuilts à jour dans `lib/`.
   Trois options (le JNI implémente C) :
   - **A CPU-only** : prefill 14, decode 10.9, 2.4 GB. Simple.
   - **B mono-contexte HTP** : prefill **181**, decode HTP **6.4**, 2.4 GB (+ION). Gros gain prefill,
     decode + lent, aucune RAM en plus, peu de code (ngl99 + `GDN_PREFILL=1`). **Recommandé** pour prompts longs/multi-tour.
   - **C dual-contexte (prefill HTP → transfert KV → decode CPU)** : prefill 181, decode **10.9**,
     **+2.3 GB** RAM (2e instance). Optimal mais + complexe — **c'est ce que le JNI implémente**. Harness : `jni/dual_ctx.cpp`.
   (Plus d'`OPFILTER` : le conv1d multi-token tombe sur CPU via le gate backend `supported_ssm_conv`.)
   TODO backend (optionnel) : corriger SSM_CONV HTP multi-token (gate `test-backend-ops -o SSM_CONV` = 45/45)
   pour faire tourner le conv1d prefill **sur** HTP au lieu du fallback CPU actuel.

## Perf (sains, 27/05, device froid)
| | prefill | decode | RAM |
|---|--:|--:|--:|
| q35-lmq4 — **C: HTP-prefill / CPU-decode (CÂBLÉ JNI)** | **103-180** (HTP, monte avec ctx) | **~7-9** (CPU, profondeur) | 4.7 GB |
| q35-lmq4 — A: CPU-only | 14 | 10.9 (ctx court) | 2.4 GB |
| q35-lmq4 — B: HTP mono-ctx | 181 | 6.4 (HTP) | 2.4 GB +ION |
| q35-lmq4 — HTP **sans le fix SSM_CONV** (historique) | 189 *(sortie CASSÉE)* | — | — |

Multi-tour mesuré (C) : tour1 63tok→prefill 103/decode 9 ; tour3 268tok→prefill 144/decode 6.7 ; mémoire OK.
Decode CPU baisse avec la profondeur de contexte (normal). Un tour ~80 tok ≈ prefill 1-3 s + decode 9-12 s.

Decode = ce que l'utilisateur ressent (10.9, OK). Prefill CPU 14 t/s → prompt 200 tok ≈ 14 s ;
garder le system prompt + l'historique courts. Le prefill HTP rapide existe mais sort du charabia (cf décision 2).

## Système (prompt) — `system_fr.txt` fourni
Inclut les garde-fous (3114, pas de prescription) + 2 correctifs issus de l'éval :
tutoiement constant, et « ne présume pas du pire, fais préciser » (le modèle lisait
« le départ de ma fille » comme un décès). À passer tel quel en `sys` de `generate()`.

## Paquet à intégrer
- `lib/*.so` → `app/src/main/jniLibs/arm64-v8a/` (libkazeia_engine.so = **SHARED**, 40 KB, NEEDED
  libllama/libggml/libggml-base/libc++_shared → **shipper TOUT le set `lib/` ensemble**, l'ABI doit
  matcher, sinon crash ABI-drift comme #277 ; **rebuild app-side recommandé** pour le garantir).
  htp-v79 = celui de cette session, fp16 derrière env `KZ_F16` OFF par défaut = comportement inchangé.
- `jni/kazeia_engine_jni.cpp` + `jni/EngineLlmEngine.kt` + `include/` → projet app.
- `q35-lmq4.gguf` → external storage (2.38 GB, hors git ; le pousser ou le reproduire, cf MODELS.md).
- `./package.sh` réassemble `lib/` depuis le build `ql/b` + recopie le prompt.

ℹ️ `libggml-hexagon.so` embarque l'instrumentation **dormante** du chantier dense-HMX (OPLOG/DUMP,
gâchées par `GGML_HEXAGON_OPLOG`/`GGML_HEXAGON_DUMP`, off par défaut). Coût quand off = 2 `getenv`/op
(microsecondes, négligeable). Rien à retirer ; utile pour rouvrir `CHANTIER_HMX_DENSE.md`.

## Détails
INTEGRATION.md (build/CMake), MODELS.md (modèle + recette), PERF.md (mesures+leviers morts),
PITFALLS.md (pièges vécus), STATUS.md (limites). Verdict qualité : `../eval/VERDICT.md`.
