Commit Graph

18 Commits

Author SHA1 Message Date
Richard Loyer 8f6253204c chantier B STT #2 : S2 + S3 livrés (stt_engine ORT QNN + JNI + façade Kotlin + doc)
S2 port complet WhisperHybridEngine.kt -> C++ :
 - stt_engine.cpp impl complète (ORT 1.24.3 + QNN EP HTP V79, decoder KV-cache
   autoregressif 200 steps, override translate->transcribe, parsers JSON inline
   pour mel_filters et vocab.json, tokenizer BPE byte-level GPT-2/Whisper)
 - kazeia_stt_cli : binaire test (1.3 MB statique)
 - Bench bit-correct vs prod : transcription FR identique sur 3 runs déterministes
   ("Elle mat est un casque de chantier connecté et modulable.")
 - Perf SM8750 : RTF 0.41-0.43 (vs prod 0.51), RAM peak 378 MB (vs 545 MB)
 - Mel C++ 60 ms (FFT) vs prod 189 ms (DFT). Decoder per-token 100 ms vs 23 ms
   prod (overhead memcpy KV; optim IO bindings ORT non-bloquant)

S3 JNI + façade + doc :
 - kazeia_stt_jni.cpp : 7 fonctions JNI exportées (nativeLoad/Transcribe/Free
   + nativeVadNew/Push/Reset/Free). Sortie pipe-séparée parseable côté Kotlin.
 - libkazeia_stt.so : 168 KB SHARED, prête pour jniLibs/arm64-v8a/
 - SttEngine.kt : façade Kotlin minimale (~100 l) parallèle à TtsEngine.kt
   (SttJni / SttEngine / SttVad / SttResult)
 - Suppression côté app : WhisperHybridEngine.kt (527L), MelExtractor.kt,
   VadStage.kt, mel_extractor.cpp, libmel_extractor.so (documentée)

Doc livrable dev :
 - STT_INTEGRATION.md (10 KB) : guide complet migration + perf + erreurs +
   bench rapide + roadmap optim
 - KAZEIA_ENGINE_OVERVIEW.md : vue globale moteur unifié (LLM + TTS + STT),
   architecture des libs, API Kotlin unifiée, empreinte cascade complète
 - CLAUDE.md : section "STT unifié engine (31/05)" + verdict A invalidé

Runtime ORT 1.24.3 + QNN libs : extraites cache Gradle Maven (cf README_ORT.md
+ README_QNN.md dans dist/lib/). Pas de tarball ext. Modèles whisper-small-sm8750
prod inchangés (545 MB QAIRT contexte conservé).
2026-05-31 22:15:58 +02:00
Richard Loyer 4b43d9e3d7 chantier B STT #1 : S1 unification engine (mel partagé + skeleton stt + VAD C++)
S1.1 mel extractor unifié (kazeia_mel.{h,cpp}) :
 - Paramétrable : window (sym/periodic), compression (log/log10), post-norm
   (none/whisper), FFT auto radix-2 (zero-pad N_FFT pas puissance de 2)
 - Configs pré-faites : config_qwen3_tts_speaker() + config_whisper()
 - speaker_encoder.cpp migré dessus : régression damien cos=0.9997 et bit-exact
   (max abs diff = 0) vs avant. E2E clonage in-process intact (RTF inchangé).

S1.2 audit ORT C++ :
 - libonnxruntime.so + headers C++ trouvés dans cache Gradle (AAR Maven
   onnxruntime-android-qnn:1.24.3). API_VERSION=24, QNN EP intégré.
 - headers/ copiés dans dist/include/onnxruntime/ (versionné, ~1 MB)
 - libonnxruntime.so copiée dans dist/lib/ (gitignore, 20 MB, README pour
   reproduire l'extraction).

S1.3 skeleton stt_engine :
 - stt_engine.{h,cpp} : API parallèle à tts_engine.h
   (SttEngineLoadCfg / SttTranscribeCfg / SttTranscribeResult)
 - VAD RMS C++ complet (port de VadStage.kt prod : frame=1600, seuil=150,
   3 frames speech, 8 frames silence)
 - stt_engine_load + stt_engine_transcribe : stubs err=-99 jusqu'au port ORT
   QNN en S2 (encoder NPU + decoder loop KV-cache + tokenizer BPE)
 - Binaire kazeia_stt_test (1.2 MB statique) valide mel Whisper [80,3000] +
   VAD sur audio FR 5s, plage de valeurs cohérente.

Reste S2 : port encoder/decoder NPU via Ort::Session + QNN EP, port tokenizer
BPE byte-level, override transcribe. Quand S2 done : S3 = JNI + façade Kotlin
+ suppression libmel_extractor.so / VadStage.kt / WhisperHybridEngine.kt.
2026-05-31 19:19:28 +02:00
Richard Loyer 12df0d0ba3 chantier B TTS #2: fix M-RoPE positions en embeds-mode + validation bit-exact
Bug subtil dans llm_graph_input_pos::set_input (llama-graph.cpp:121-123) : pour
M-RoPE (n_pos_per_embd=4), la conversion 1D->4D des positions n'est faite QUE
pour ubatch.token. En embeds-mode (ubatch.embd != nullptr), la branche else fait
un memcpy raw de ubatch.pos avec taille T*4, lisant T*3 entiers out-of-bounds
au-dela du buffer T-only que j'allouais. -> positions garbage -> sortie cassee
(cos=-0.02 vs Python avant fix, soit pire qu'aleatoire).

Fix cote JNI (chantier P1) : detecte rope_type via llama_model_rope_type et
alloue T*4 positions quand MROPE/IMROPE, en remplissant les 3 premiers axes
avec la position et le 4eme avec 0 (text-only) -- exactement ce que le framework
fait pour les tokens. Memoire : decode_embd_batch dans kazeia_engine_jni.cpp.

Smoke test bit-exact Python vs engine (test_talker_replay.cpp, teacher-forced) :
- 19 positions prefill + 26 steps decode sur talker_f32.gguf (Bonjour je m'appelle Kazeia)
- hidden : mean_cos=1.000000, mean_rmse=2.9e-3
- logits : mean_cos=1.000000, mean_rmse=1.8e-3
- argmax engine == argmax Python sur les 26 steps (info only -- la divergence
  RMSE residuelle est du f32 noise, ordre de sommation).

Le port Talker engine est valide bit-exact (precision f32) sur tablette CPU.
Reste P3 etape 3 : orchestration finale (BPE tokenizer + x-vector encoder +
cablage CP runner + decoder ggml) en mode standalone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 13:54:03 +02:00
Richard Loyer 50814f3bca chantier B TTS #1: API JNI embeds-only + M-RoPE qwen3 dense
Premières 2 pieces du chantier B (porter le Talker Qwen3-TTS sur l'engine
pour TTS standalone tablette, sans dépendre du pipeline Python).

P1 - API embeds-only dans le JNI (dist/jni/kazeia_engine_jni.cpp)
Le Talker a vocab=3072 codes audio (pas de BPE), entrée = embeds
pré-mélangés (text+x-vector au prefill, sum 16 codecs + tts_pad au decode),
sortie = logits[3072] + hidden[1024] (pour le Code Predictor).
Solution: pas un appel monolithique, 5 primitives qui laissent l'orchestration
côté caller:
  - nEmbd(h), nVocab(h) - dimensionnement des buffers Kotlin
  - resetEmbeds(h)       - KV clear + pos=0
  - prefillEmbeds(h, embds[T*n_embd], T, outHidden[n_embd])
  - decodeEmbed(h, embd[n_embd], outLogits[vocab], outHidden[n_embd])
Implementation: llama_set_embeddings(ctx, true), llama_batch.embd au lieu
de .token, logits=1 sur la derniere position seulement (economie KV).
KEngine porte un compteur pos_embd interne. Kotlin wrapper miroir dans
dist/jni/EngineLlmEngine.kt.

P2 - M-RoPE qwen3 dense (sous-module ql, commit c1609a1)
Pointeur sous-module avance pour embarquer le support.

Validation device: dist/jni/test_talker.cpp charge talker_f32.gguf, lance
llama_decode embeds-only T=4 prefill puis 1 step, recupere hidden+logits
sans crash. Log device: print_info rope_type=8, mrope sections=[24,20,20,0],
prefill OK / step OK. argmax stable (entree bidon, juste smoke).

Libs rebuiltes: dist/lib/libllama.so + libkazeia_engine.so + libggml-base.so.
Reste P3 = cablage Talker->CP->Decoder cote orchestration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 13:37:17 +02:00
Richard Loyer 464d4e69c0 chantier dense-HMX: avancement #2 (replay) — root = fault HMX non-déterministe, instrumentation DSP requise (bloquée sans root). Tools committés.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:35:14 +02:00
Richard Loyer 224b375a10 chantier dense-HMX: avancement — ni magnitude, ni NaN, ni shape pure (value/context-spécifique)
3 hypothèses éliminées par mesure: overflow fp16 (act=9.8), NaN/Inf inputs (0), shape pure (isolation
synthétique k=4096 ne crashe pas). Op fautive = o_proj MUL_MAT q4_0 k=4096 après FLASH_ATTN_EXT.
Prochaines étapes révisées (dump-and-replay value-vs-context, instrumentation DSP log-vers-buffer).
OPLOG mis à jour (nan/inf) dans dist/lib. ql -> commit OPLOG.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:09:52 +02:00
Richard Loyer 5aa4457567 chantier: instrumentation crash dense-HTP-HMX + .pte retiré (engine seul runtime)
CHANTIER_HMX_DENSE.md: analyse + outils pour le crash matmul HMX dense. Finding: PAS un overflow
fp16 (activation du matmul fautif = 9.8) — c'est un bug de tiling/buffer HMX pour k=4096 (o_proj
après FLASH_ATTN_EXT). Localisé via OPLOG CPU-side (FARF DSP bloqué sans root). Étapes + repro documentés.

HANDOFF: .pte retiré -> engine seul runtime LLM. Dense=HVX (40) sur l'engine; HMX (295)=ce chantier.
dist/lib refreshé (libggml-hexagon.so avec OPLOG dormant, htp-v79 propre). ql -> 7d4cade.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:29:32 +02:00
Richard Loyer 0407ac8b71 dist: ROOT CAUSE crash dense-HTP = matmul HMX (fp16) ; fix = HVX pour le dense
Analyse complète (méthode SSM_CONV) du crash dense-HTP:
- bench HTP synthétique (pp512, +tg8) = OK ; cli/engine texte réel crashe 0x2e > ~14 tokens.
- OPFILTER bisect: SOFT_MAX/ROPE/RMS_NORM/GET_ROWS/CPY/CONT/ADD n'arrêtent PAS le crash ; MUL_MAT->CPU OUI.
- GGML_HEXAGON_USE_HMX=0 (matmul HVX) supprime le crash, =1 le reproduit. => le matmul HMX (fp16 tile)
  faute sur les activations réelles du dense (massive activations Qwen3 > plage fp16 ; bench synthétique
  ne déclenche pas l'overflow). Analogue du SSM_CONV pour la 3.5, mais ici c'est le matmul HMX coeur.

Câblage: lecture general.architecture dans le GGUF AVANT init backend -> hybride (qwen35/qwen3next):
HMX on + option C (préservé) ; dense: USE_HMX=0 + HTP contexte-unique (HVX, ~40 prefill =3x CPU, decode ~11,
stable). Repli CPU si pas de HTP. Validé test_jni_native: dense (HVX HTP) + 3.5 (option C) cohérents.
TODO backend: matmul HMX robuste aux activations hors plage fp16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:42:15 +02:00
Richard Loyer c166391f49 dist: routage par structure (is_hybrid) — 3.5->option C, dense->CPU robuste
Détection au load via llama_model_is_hybrid/is_recurrent (structure, pas string d'archi):
- hybride GDN (qwen3.5/qwen3next) -> option C (prefill HTP/decode CPU), préservé+validé.
- dense (qwen3, etc.) / pas de HTP -> CPU pur (universel, robuste).
Charge l'instance CPU d'abord (détection + universel), n'ajoute l'instance HTP que pour l'hybride.

Diagnostic dense-HTP: le prefill dense sur HTP CRASHE (dspqueue_read 0x2e) sur prompt réaliste
(reproduit aussi en llama-cli -dev HTP0 -fa 1; le "ça marche" antérieur = prompt minuscule sans fa,
cas chanceux). Bug backend HTP attention dense multi-token, non corrigé. Dense a un .pte (451) de
toute façon -> dense=CPU robuste. Validé test_jni_native: dense (CPU) + 3.5 (option C) cohérents.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:30:43 +02:00
Richard Loyer eeffbc350f dist: JNI générique (auto-détecte archi) — tout LLM, sans dégrader la 3.5
Le JNI lit general.architecture: qwen35/qwen3next -> option C (prefill HTP/decode CPU, préservé
et revalidé cohérent+mémoire). Tout le reste (qwen3 dense, etc.) -> CPU pur (universel, jamais de
crash). Dense Qwen3-4B validé via le moteur (CPU, cohérent). 3.5 non régressée.

Diagnostic crash dense-HTP: ce n'est PAS le HTP (dense single-context HTP marche: cli 36.8/11.4
cohérent, llama-bench pp512=98). Le crash 0x2e (dspqueue_read, flush_pending) survient UNIQUEMENT
en dual-load (option C: 2 instances modèle) appliqué au dense. Or le dense n'a pas besoin de
l'option C (decode HTP 11.4 ≈ CPU 11.7, pas de pénalité GDN). Donc dense -> CPU (ou single-ctx HTP).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:11:44 +02:00
Richard Loyer 771d64a998 dist: multi-tour propre (generateRaw + ChatSession) + JNI validé end-to-end natif
Ferme les 2 derniers trous d'intégration:
- generateRaw(prompt) exposé (cœur option C partagé avec generate). EngineLlmEngine.kt: ChatSession
  gère l'historique + construit le ChatML Qwen3.5 validé -> multi-tour propre sans que l'app connaisse
  le template (structure identique à dual_ctx_mt).
- test_jni_native.cpp: dlopen + JNIEnv mock teste le .so shippé bout-en-bout sur device
  (load/generate/generateRaw/free). Résultat: FR cohérent + mémoire conversationnelle OK
  ("Marc, je me souviens parfaitement de ton prénom"). Marshalling JNI réel validé.
libkazeia_engine.so reconstruit (5 symboles). Paquet dist/ prêt à intégrer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 11:18:20 +02:00
Richard Loyer 6935089459 dist: libkazeia_engine.so rebuild option C (paquet cohérent), notes rebuild MAJ
libkazeia_engine.so reconstruit depuis le JNI option C (37KB strippé, 4 symboles JNI, SHARED,
NEEDED llama/ggml/ggml-base/log/c++_shared). Le paquet dist/ est désormais interne-cohérent:
sources + prebuilts alignés (option C + fix SSM_CONV). Docs: prebuilts à jour, rebuild app-side
recommandé pour ABI/STL. Logique validée via dual_ctx_mt (JNI non testé via Java ici).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 10:45:47 +02:00
Richard Loyer 2699b631a3 dist: prefill HTP corrigé (SSM_CONV gate backend), JNI option C sans OPFILTER
Fix intégré dans libggml-hexagon.so (ql e872623): ggml_hexagon_supported_ssm_conv route le
conv1d multi-token (prefill) sur ggml-cpu (le kernel HTP est faux multi-token pour d_inner
1024/2048). Plus besoin de GGML_HEXAGON_OPFILTER -> retiré du JNI. Validé: oracle SSM_CONV 18/18,
cli -dev HTP0 cohérent, multi-tour cohérent+mémoire, prefill HTP 110-180 t/s. libggml-hexagon.so
rafraîchi. Rebuild requis avant ship: libggml-hexagon.so + libkazeia_engine.so. Docs MAJ.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 10:39:08 +02:00
Richard Loyer b2fc564f04 Engine sur fork qualcomm: decode dense 15.5>pte, 3.5 ~11, KVq8+t4+fa+lm_head-Q4; prefill 93/285; RAG cross 24-28k
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 15:53:14 +02:00
Richard Loyer 7a5780ca1d Bridge v3 utilisable: thinking-off ChatML, sys+usr, cap; static intact 2026-05-25 09:41:04 +02:00
Richard Loyer 76f21a21cb Bridge STATIC autonome (42MB): zero NEEDED partage, fix collision TTS libllama, valide device 2026-05-24 22:43:45 +02:00
Richard Loyer 15b9baf6b2 JNI bridge complet: generate() prefill+decode, build arm64 OK (libkazeia_engine.so 209K) 2026-05-24 21:58:00 +02:00
Richard Loyer 0f92fa723f Dist Kazeia-Engine: libs arm64+headers+JNI bridge+INTEGRATION/TTS doc; STT reste ORT 2026-05-24 21:55:01 +02:00