Le dev Kazeia a découvert au test de dégel TTS / toggle llm_engine=lib :
SIGABRT in-app au tts_engine_load / kazeia_engine_load, jamais en standalone.
Trace dmesg :
avc: denied { open } for path="/dev/fastrpc-cdsp"
scontext=u:r:untrusted_app tcontext=u:object_r:vendor_qdsp_device
Cause : libggml-hexagon.so dlopen libcdsprpc.so (FastRPC) au init backend HTP,
qui ouvre /dev/fastrpc-cdsp. Policy SELinux Oplus/Qualcomm sur SM8750 interdit
cet accès à untrusted_app (UID 10xxx). Seul shell (UID 2000) y a accès.
Conséquence : tout mon harness CLI standalone via adb shell était un FAUX
positif systématique. Le test "loadLibrary OK" + "calls C++ OK" ne révèle
rien du comportement in-app pour le backend HTP/DSP.
Pourquoi STT marche : passe par libQnnHtp.so Maven (delegation autorisée
untrusted_app), pas FastRPC direct.
Pourquoi ExecuTorch .pte marche : idem libQnn Maven.
3 voies évaluées (cf project_engine_selinux_fastrpc_blocker memoire) :
1. Rebuild CPU-only (GGML_HEXAGON=OFF) -- réaliste, +1s/tour LLM, +0 TTS
2. Port ORT-QAIRT pour LLM -- non réaliste (pas de Qwen QAIRT context)
3. Statu quo .pte -- régression ambition projet
Décision user : le dev rebuilde de son côté en environnement APK réel
(évite mon harness biaisé). Reprise des sources Kazeia-Engine et build.
Livrables cette session :
- dist/REBUILD_CPU_ONLY.md : guide complet 7 étapes (sources, flags CMake,
build libllama/ggml CPU-only, rebuild kazeia_engine/tts, jniLibs allégé,
tests in-app, perf attendue, points d'attention)
- dist/LLM_INTEGRATION.md §0 : bloqueur SELinux documenté + référence
rebuild CPU-only + chiffres perf attendus
- dist/TTS_INTEGRATION.md §0 : idem côté TTS, noter que le clonage vocal
(speaker encoder ECAPA-TDNN) est CPU-only de base, donc préservé sans
impact
- dist/KAZEIA_ENGINE_OVERVIEW.md : warning en tête + pointer REBUILD doc
- project_engine_selinux_fastrpc_blocker.md : diagnostic complet pour
contexte futur
Coût mesuré attendu (vs CLI HTP) :
- LLM prefill : 0.4s -> 1.3s (+0.9s)
- LLM decode : inchangé (déjà CPU NEON option C)
- TTS talker prefill : 50ms -> 150ms (négligeable in-mix)
- Tour LLM total : +1s (acceptable thérapeutique)
- Tour TTS total : +0.1s (imperceptible)
Leçon de méthode gravée : un harness CLI via adb shell sera TOUJOURS
faux positif pour tout test impliquant FastRPC/DSP/contexte vendor.
Tout test validation engine pour intégration in-app DOIT passer par
APK debug ou androidTest instrumenté.
Reproche du user juste : j'ai livré STT complet mais pas TTS ni LLM au même
niveau. Cette session corrige : 3 docs d'intégration équivalentes en qualité,
2 bugs in-app investigués et l'un fixé.
Investigation TTS "crashe au load in-app" :
- DT_NEEDED de libkazeia_tts.so = libllama.so + libggml.so + libggml-base.so
+ libggml-cpu.so + libc++_shared.so + libdl/libm/libc
- Test reproduction sur tablette : lib charge OK avec les 7 deps présentes,
crash avec "library libllama.so not found" si une dep manque
- Conclusion : c'est juste 7 libs à pousser dans jniLibs/arm64-v8a/, pas un
bug code. Liste exhaustive donnée dans TTS_INTEGRATION.md §2.b
Investigation LLM "ne tient pas 8 threads in-app" :
- kazeia_engine_jni.cpp ligne 117/120/124/131 : n_threads HARDCODE à 4
pour le decode CPU. Pas exposé côté Kotlin.
- Fix : ajouter param nThreads à load(), N_DECODE défaut 6 (sweet spot r3
mesuré : Qwen3.5-4B 9.8 tok/s à t=6 vs 7.4 à t=8 contention)
- Update EngineJni.load(model, ctx, nThreads) + EngineLlmEngine(ctx, nThreads=6)
- Rebuild libkazeia_engine.so 60 KB
Docs livrées (format identique STT_INTEGRATION.md) :
- TTS_INTEGRATION.md (10 KB) : libs à pousser, façade, clonage vocal,
sampling, codes erreur, bench, checklist, pièges
- LLM_INTEGRATION.md (10 KB) : option C HTP/CPU, n_threads sweet spot,
modèles supportés (Qwen3 dense / Qwen3.5 hybride GDN), cascade
Speaker+Thinker, piège affinity Android (decode 9.8 -> 0.32 tok/s in-app
si scheduler pin sur 1 cœur)
- KAZEIA_ENGINE_OVERVIEW.md actualisé : entrée unique pointant les 3 docs,
pattern dispatch flag (mirror STT pour LLM+TTS), état réel 3/3 prêt
Reste côté dev (cf KAZEIA_ENGINE_OVERVIEW.md):
1. Pousser les libs jniLibs (8 fichiers pour TTS/LLM, partagées entre eux)
2. Copier les 3 façades Kotlin
3. Câbler 2 flags dispatch supplémentaires (llmEngine + ttsEngine, mirror STT)
4. Valider checklist §7 de chaque doc (System.loadLibrary + 1 call)
5. Bench A/B prod vs lib sur audios/prompts fixtures
Le pattern STT a marché parce qu'il avait tout (lib + façade + doc + checklist).
Maintenant TTS et LLM aussi.