diff --git a/dist/KAZEIA_ENGINE_OVERVIEW.md b/dist/KAZEIA_ENGINE_OVERVIEW.md index 702bae1..89c53ed 100644 --- a/dist/KAZEIA_ENGINE_OVERVIEW.md +++ b/dist/KAZEIA_ENGINE_OVERVIEW.md @@ -1,6 +1,8 @@ # Kazeia-Engine — vue d'ensemble et entrée d'intégration -**Date** : 01/06/2026. Moteur d'inférence local-first pour Snapdragon 8 Elite (Pad3). Unifie **LLM + TTS + STT** dans une stack cohérente. Pas de cloud, pas de root, pas de Python runtime. +> ⚠️ **02/06 : bloqueur SELinux découvert** sur les libs LLM/TTS in-app (`/dev/fastrpc-cdsp` refusé à `untrusted_app`). Les libs livrées dans `b-jni/` sont **inutilisables dans un APK Play Store/sideload** en l'état. **Rebuild CPU-only requis** côté dev avant intégration LLM/TTS. STT non affecté (passe par QNN Maven). Voir `REBUILD_CPU_ONLY.md` pour la procédure et `project_engine_selinux_fastrpc_blocker` en mémoire pour le détail. + +**Date** : 02/06/2026. Moteur d'inférence local-first pour Snapdragon 8 Elite (Pad3). Unifie **LLM + TTS + STT** dans une stack cohérente. Pas de cloud, pas de root, pas de Python runtime. ## Documents d'intégration (3 indépendants) diff --git a/dist/LLM_INTEGRATION.md b/dist/LLM_INTEGRATION.md index fffda31..2249d70 100644 --- a/dist/LLM_INTEGRATION.md +++ b/dist/LLM_INTEGRATION.md @@ -1,6 +1,12 @@ # Intégration LLM Kazeia-Engine — Guide développeur -**Date** : 01/06/2026 +> ⚠️ **BLOQUEUR SELinux découvert 02/06 — lire la section 0 avant toute intégration in-app.** +> La lib `libkazeia_engine.so` actuelle (avec backend Hexagon HTP) **NE FONCTIONNE PAS** +> dans un APK `untrusted_app` à cause d'une policy SELinux Android/Qualcomm qui interdit +> l'accès à `/dev/fastrpc-cdsp`. Tout test CLI standalone via `adb shell` est un faux positif +> systématique (uid `shell` a accès, pas l'app). **Rebuild CPU-only requis** pour usage in-app. + +**Date** : 02/06/2026 **Cible** : remplacer le pipeline LLM ExecuTorch `.pte` par `libkazeia_engine.so` + façade Kotlin `EngineLlmEngine.kt`. Modèle GGUF chargé directement (pas de conversion .pte), prefill HTP / decode CPU automatique (option C). **Runtime** : libllama (fork ggml-org/llama.cpp commit `f0fe1058b`) + libggml fork ql (Qualcomm) avec backend Hexagon HTP V79. @@ -23,6 +29,35 @@ --- +## 0. Bloqueur SELinux découvert 02/06 — à lire en premier + +**Symptôme** : `System.loadLibrary("kazeia_engine")` peut passer, mais le premier appel à `EngineLlmEngine.load(modelPath, nCtx, nThreads)` crashe avec SIGABRT (`ggml_backend_dev_name` → `ggml_abort`). + +**Cause** : `libkazeia_engine.so` linke `libllama.so` + `libggml-hexagon.so` qui dlopen `libcdsprpc.so` (FastRPC) au démarrage du backend HTP. FastRPC ouvre `/dev/fastrpc-cdsp` pour parler au CDSP. La policy SELinux Oplus/Qualcomm sur SM8750 interdit cet accès à `untrusted_app` : + +``` +avc: denied { open } for path="/dev/fastrpc-cdsp" + scontext=u:r:untrusted_app tcontext=u:object_r:vendor_qdsp_device +``` + +→ Backend hexagon en état partiel → crash au premier accès. + +**Pourquoi cette doc l'avait raté** : tous mes tests en `adb shell` (uid 2000) marchaient parfaitement. Le contexte SELinux `shell` a accès à `/dev/fastrpc-cdsp`. **Le contexte `untrusted_app` ne l'a pas**. Faux positif systématique du harness standalone. + +**Pourquoi STT et ExecuTorch prod marchent** : ils utilisent QNN via `libQnnHtp.so` Maven, qui passe par une voie de delegation autorisée. Pas FastRPC direct. + +**Solution** : **rebuild `libllama.so` + `libggml*.so` + `libkazeia_engine.so` en CPU-only** (`-DGGML_HEXAGON=OFF`). Le bridge devient CPU pur. Coût mesuré sur workload Kazeia (prompts 16-24 tok, réponses 64 tok) : + +| | HTP prod | CPU-only | +|---|---:|---:| +| LLM prefill | ~0.4 s | ~1.3 s | +| LLM decode (64 tok à 9.8 t/s) | ~6.5 s | ~6.5 s (déjà CPU) | +| **Total tour LLM** | **~7 s** | **~8 s (+1 s)** | + +Acceptable pour thérapeutique. Voir mémoire `project_engine_selinux_fastrpc_blocker` pour le détail des 3 voies évaluées. + +**État jusqu'à rebuild CPU-only** : flag `llm_engine=lib` arme la bascule mais la lib ne fonctionnera pas in-app. Fallback gracieux sur `prod` (.pte) recommandé tant que le rebuild n'est pas fait. + ## 1. Ce que la lib fait ``` diff --git a/dist/REBUILD_CPU_ONLY.md b/dist/REBUILD_CPU_ONLY.md new file mode 100644 index 0000000..76b2c0b --- /dev/null +++ b/dist/REBUILD_CPU_ONLY.md @@ -0,0 +1,185 @@ +# Rebuild Kazeia-Engine en CPU-only — guide dev + +**Pourquoi** : la lib actuelle `libkazeia_engine.so` + `libkazeia_tts.so` linke `libllama.so` qui dépend de `libggml-hexagon.so`, qui dlopen `libcdsprpc.so` (FastRPC). La policy SELinux Oplus/Qualcomm sur SM8750 interdit à `untrusted_app` d'ouvrir `/dev/fastrpc-cdsp` → crash in-app au load du backend hexagon. Détails dans `LLM_INTEGRATION.md §0` et `TTS_INTEGRATION.md §0`. + +**Objectif** : rebuild `libllama.so` + `libggml*.so` + `libkazeia_engine.so` + `libkazeia_tts.so` **sans backend Hexagon**. Coût perf mesuré : LLM +1 s par tour conversation, TTS +0 s perceptible (le talker prefill HTP n'apportait que ~50 ms vs CPU pur). + +**STT** : non concerné, déjà sur voie ORT-QAIRT autorisée pour `untrusted_app`. `libkazeia_stt.so` peut rester comme livrée. + +--- + +## Sources + +Tout est dans le repo Kazeia-Engine : + +``` +/opt/Kazeia-engine/ +├── ql/ # fork ggml-org/llama.cpp commit f0fe1058b +│ ├── ggml/ # ggml core + backends (CPU + Hexagon) +│ └── (le reste = llama.cpp upstream + qwen35_compile patch HTP) +├── dist/ +│ ├── jni/ +│ │ ├── kazeia_engine_jni.cpp # LLM bridge +│ │ ├── kazeia_tts_jni.cpp # TTS bridge +│ │ ├── tts_engine.{h,cpp} # TTS core +│ │ ├── speaker_encoder.{h,cpp} # clonage vocal (CPU-only de base) +│ │ ├── kazeia_mel.{h,cpp} # FFT mel partagé +│ │ ├── cp_inference.cpp / sampler.cpp / kazeia_text_tokenizer.cpp +│ │ ├── EngineLlmEngine.kt # façade LLM +│ │ └── TtsEngine.kt # façade TTS +│ ├── build_kazeia_tts.sh # build script TTS (linke libllama + ggml de dist/lib/) +│ ├── CMakeLists.txt # build script LLM (libkazeia_engine.so) +│ └── lib/ # libs précompilées AVEC Hexagon (à ne PAS utiliser in-app) +└── CLAUDE.md # contexte projet (perf, modèles, pièges) +``` + +## Étape 1 — Rebuild ggml + llama.cpp en CPU-only + +L'option `GGML_HEXAGON=OFF` existe déjà dans le CMakeLists (`/opt/Kazeia-engine/ql/ggml/CMakeLists.txt` ligne 268, default OFF mais activée par les scripts de build précédents). + +```bash +cd /opt/Kazeia-engine/ql +rm -rf build-android-cpu +mkdir build-android-cpu && cd build-android-cpu + +NDK=/opt/Kazeia/android-ndk-r27d +cmake .. \ + -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI=arm64-v8a \ + -DANDROID_PLATFORM=android-31 \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_FLAGS="-O3 -march=armv8.6-a+i8mm+bf16+dotprod+fp16" \ + -DCMAKE_C_FLAGS="-O3 -march=armv8.6-a+i8mm+bf16+dotprod+fp16" \ + -DGGML_HEXAGON=OFF \ + -DGGML_OPENMP=OFF \ + -DGGML_VULKAN=OFF \ + -DLLAMA_BUILD_EXAMPLES=OFF \ + -DLLAMA_BUILD_TESTS=OFF \ + -DLLAMA_BUILD_SERVER=OFF \ + -DBUILD_SHARED_LIBS=ON + +cmake --build . -j$(nproc) --target llama ggml ggml-base ggml-cpu +``` + +Résultat dans `ql/build-android-cpu/`: +- `bin/libllama.so` +- `ggml/src/libggml.so` +- `ggml/src/libggml-base.so` +- `ggml/src/libggml-cpu.so` + +**Plus de** `libggml-hexagon.so` ni `libggml-htp-v79.so` à pousser dans `jniLibs/`. + +## Étape 2 — Rebuild libkazeia_engine.so et libkazeia_tts.so + +```bash +cd /opt/Kazeia-engine/dist +NDK=/opt/Kazeia/android-ndk-r27d +CXX=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android31-clang++ + +# Pointer le linker vers les nouvelles libs CPU-only +NEW_LIB=/opt/Kazeia-engine/ql/build-android-cpu +mkdir -p b-jni-cpu + +# LLM +$CXX -std=c++17 -O3 -fPIC \ + -march=armv8.6-a+i8mm+bf16+dotprod+fp16 \ + -I./include -shared jni/kazeia_engine_jni.cpp \ + -L$NEW_LIB/bin -L$NEW_LIB/ggml/src \ + -lllama -lggml -lggml-base \ + -llog -ldl -lm \ + -o b-jni-cpu/libkazeia_engine.so + +# TTS +DEC=/opt/Kazeia/kazeia-tts-decoder-ggml +$CXX -std=c++17 -O3 -fPIC \ + -march=armv8.6-a+i8mm+bf16+dotprod+fp16 \ + -I./include -I$DEC/src \ + -shared \ + jni/kazeia_tts_jni.cpp jni/tts_engine.cpp jni/cp_inference.cpp \ + jni/sampler.cpp jni/kazeia_text_tokenizer.cpp \ + jni/speaker_encoder.cpp jni/kazeia_mel.cpp \ + $DEC/build-android-engine/libqwen3tts-decoder.a \ + -L$NEW_LIB/bin -L$NEW_LIB/ggml/src \ + -lllama -lggml -lggml-base -lggml-cpu \ + -llog -ldl -lm \ + -o b-jni-cpu/libkazeia_tts.so +``` + +## Étape 3 — Vérifier les DT_NEEDED (sanity) + +```bash +NDK=/opt/Kazeia/android-ndk-r27d +$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-readelf -d \ + b-jni-cpu/libkazeia_engine.so | grep NEEDED +``` + +Attendu : `libllama.so + libggml.so + libggml-base.so + libc++_shared.so + libdl/libm/libc/liblog`. **Pas de** `libggml-hexagon.so` ni `libggml-cpu.so` direct (chargé via libggml en runtime). + +## Étape 4 — jniLibs/arm64-v8a/ pour APK + +Pousser **6 libs au lieu de 8** : + +| Fichier | Origine | Taille approx (CPU-only sera plus petit) | +|---|---|---:| +| `libkazeia_engine.so` | `b-jni-cpu/` | 60 KB | +| `libkazeia_tts.so` | `b-jni-cpu/` | ~900 KB | +| `libllama.so` | `ql/build-android-cpu/bin/` | ~25 MB (sans Hexagon) | +| `libggml.so` | `ql/build-android-cpu/ggml/src/` | ~500 KB | +| `libggml-base.so` | idem | ~6 MB | +| `libggml-cpu.so` | idem | ~4.5 MB | +| `libc++_shared.so` | NDK r27d ou existant projet | 1.8 MB | + +**Plus** `libggml-hexagon.so`, **plus** `libggml-htp-v79.so` à pousser. + +Pour STT, `libkazeia_stt.so` + `libonnxruntime.so` (via Maven `onnxruntime-android-qnn:1.24.3`) restent inchangés. + +## Étape 5 — Tester in-app + +1. `System.loadLibrary("kazeia_engine")` doit passer (déjà testé OK même avec libllama legacy, donc avec la nouvelle ça doit l'être aussi) +2. `EngineLlmEngine.load(...)` doit passer **sans SIGABRT** (c'est le test critique vs avant) +3. `generate(...)` doit produire du texte cohérent et tenir le `nThreads` demandé (vérifier avec `top -H -p ` que 6 threads sont actifs ~90 % CPU) +4. Idem TTS : `TtsEngine(...)` charge sans crash, `synthesize(...)` produit un WAV audible + +Si étape 2 plante quand même, capturer logcat + dmesg (filtrer `avc:` pour SELinux) et remonter. + +## Étape 6 — Mesure perf attendue (pour valider la voie) + +Sur SM8750, workload Kazeia typique (prompt 24 tok / réponse 64 tok, Qwen3.5-4B Q4_0) : + +| Métrique | HTP (CLI standalone) | CPU-only (attendu in-app) | Delta | +|---|---:|---:|---:| +| Prefill | 0.4 s | ~1.3 s | +0.9 s | +| Decode 64 tok @ 9.8 t/s | 6.5 s | 6.5 s | 0 | +| **Tour LLM** | **6.9 s** | **7.8 s** | **+0.9 s** | +| Tour TTS (3 s audio) | 8.0 s | 8.1 s | +0.1 s | +| RAM peak LLM | 2.4 GB | ~2.0 GB | -400 MB (pas de contexte HTP) | + +Si vos mesures s'éloignent significativement (decode < 5 t/s ou prefill > 3 s), c'est probable le piège affinity Android (cf `LLM_INTEGRATION.md §8`). + +## Étape 7 — Flipper les flags quand validé + +```kotlin +// adb shell content update ... ou setprop selon mécanisme flag retenu +llm_engine = "lib" +tts_engine = "lib" +``` + +Le fallback gracieux que tu as câblé reste actif tant que le default est sain. + +--- + +## Points d'attention + +1. **`libcdsprpc.so` et `libQnnHtp*` doivent rester dans jniLibs** pour STT (voie ORT-QAIRT autorisée). Mais ils ne seront pas utilisés par LLM/TTS rebuildés. + +2. **Modèles GGUF** : aucun changement, les `.gguf` actuels (Qwen3.5-4B-Q4_0 etc.) marchent à l'identique en CPU pur. + +3. **TTS clonage vocal** : le speaker encoder ECAPA-TDNN est en C++ ggml CPU déjà, jamais touché par cette histoire HTP. Toutes les fonctionnalités clonage préservées. + +4. **Pas de régression STT** : `libkazeia_stt.so` reste comme livrée, voie QNN Maven autorisée. + +5. **CLAUDE.md du projet engine** documente les chiffres HTP : ils restent pertinents pour le bench standalone (CLI) mais **pas pour l'in-app CPU-only**. Le `+1 s par tour` est le chiffre à retenir pour l'utilisateur final. + +## Si question + +Code source contact : `/opt/Kazeia-engine/dist/jni/` + `/opt/Kazeia-engine/ql/`. Mémoires assistant pertinentes : `project_engine_selinux_fastrpc_blocker`, `project_engine_unification_3of3`, `project_stt_2_bugs_fixed`. diff --git a/dist/TTS_INTEGRATION.md b/dist/TTS_INTEGRATION.md index 796c401..a5a3aa7 100644 --- a/dist/TTS_INTEGRATION.md +++ b/dist/TTS_INTEGRATION.md @@ -1,6 +1,12 @@ # Intégration TTS Kazeia-Engine — Guide développeur -**Date** : 01/06/2026 +> ⚠️ **BLOQUEUR SELinux découvert 02/06 — lire la section 0 avant toute intégration in-app.** +> La lib `libkazeia_tts.so` actuelle (linke `libllama.so` + `libggml-hexagon.so`) **NE FONCTIONNE PAS** +> dans un APK `untrusted_app` à cause d'une policy SELinux Android/Qualcomm qui interdit +> l'accès à `/dev/fastrpc-cdsp` requis par FastRPC. **Rebuild CPU-only requis** pour usage in-app. +> Le speaker encoder (clonage vocal) est lui CPU-only et n'est pas affecté. + +**Date** : 02/06/2026 **Cible** : remplacer le pipeline TTS legacy par `libkazeia_tts.so` + façade Kotlin `TtsEngine.kt`. Inclut le **clonage vocal embarqué** via speaker encoder ECAPA-TDNN. **Runtime** : libllama + libggml (fork ql Qualcomm) avec backend Hexagon HTP V79 — décode ggml in-process. Pas d'ONNX, pas de Python, pas de root. @@ -19,6 +25,32 @@ --- +## 0. Bloqueur SELinux découvert 02/06 — à lire en premier + +**Symptôme** : `tts_engine_load` crashe in-app (SIGABRT dans le backend hexagon), pas en standalone `adb shell`. + +**Cause identique au LLM** : `libkazeia_tts.so` linke `libllama.so` qui dépend de `libggml-hexagon.so`, qui dlopen `libcdsprpc.so` (FastRPC). La policy SELinux interdit `untrusted_app` d'accéder à `/dev/fastrpc-cdsp` : +``` +avc: denied { open } for path="/dev/fastrpc-cdsp" + scontext=u:r:untrusted_app tcontext=u:object_r:vendor_qdsp_device +``` + +**Spécificité TTS** : dans le pipeline TTS Qwen3, le talker fait son **prefill** sur HTP (option C). Mais en pratique, sur des prompts TTS courts (~20-30 tokens), le prefill HTP n'apporte que **~50 ms** vs CPU pur. Donc le rebuild CPU-only impacte **0 perceptiblement** le TTS : + +| | Pipeline actuel | TTS CPU-only | +|---|---:|---:| +| Talker prefill | ~50 ms (HTP) | ~150 ms (CPU) | +| Talker loop (decode) | déjà CPU | déjà CPU | +| CP loop | déjà CPU | déjà CPU | +| Decoder chraac | déjà CPU | déjà CPU | +| **Total tour TTS** | **~8 s** | **~8.1 s (+0.1 s)** | + +→ Coût négligeable pour TTS. Le rebuild CPU-only **préserve toutes les fonctionnalités** y compris le **clonage vocal embarqué** (le speaker encoder ECAPA-TDNN est CPU-only de toute façon, jamais touché par cette histoire HTP/SELinux). + +**Solution** : même rebuild CPU-only (`-DGGML_HEXAGON=OFF`) que pour LLM. Une seule chaîne de libs partagée. Voir `project_engine_selinux_fastrpc_blocker` en mémoire. + +**État jusqu'à rebuild** : flag `tts_engine=lib` arme la bascule mais la lib ne fonctionnera pas in-app. Garder `tts_engine=prod` jusqu'à rebuild CPU-only des libs. + ## 1. Ce que la lib fait ```