Commit Graph

21 Commits

Author SHA1 Message Date
Kazeia Team a2fc01987b release(app): 0.2.3 / versionCode 15 (APK lean sans natifs JNA)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 09:38:51 +02:00
Kazeia Team 22dbd2145f build: exclure les natifs JNA cross-plateforme (-2,3 Mo, APK = binaires utiles)
JNA (transitif sqlcipher/DJL) embarquait des natifs aix/darwin/win inutiles
(aucun android-aarch64 fourni, JNA non utilisé dans le code). APK ne contient
plus que les binaires nécessaires + le code ; tous les modèles passent par
l'OTA (rappel : l'APK ne doit contenir aucun modèle — vérifié, 0 fichier modèle).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 09:20:13 +02:00
Kazeia Team 66e81f0668 refactor(tts): bascule CosyVoice = seul TTS + purge legacy (≈322 Mo libs)
Bascule définitive vers CosyVoice (clonage vocal 9 langues, CPU-only) :
- ConfigStore défaut ttsEngine = "cosyvoice" ; KazeiaService dispatch CosyVoice
  uniquement ; chemins TTS-B/streaming Qwen3 retirés (CosyVoice = batch
  speakText qui streame en interne) ; KazeiaPipeline.speakText simplifié.

Purge du superseded (archivé /opt/Kazeia/archive/2026-06-18_cosyvoice-bascule/) :
- Kotlin tts/ : Qwen3TtsEngine, KazeiaTtsEngine, TtsEngine(lib), TtsPipeline,
  Qwen3TtsGgmlDecoder, TtsTalkerCpuJni, TtsCpCpuJni, NeonOps, BenchTtsHarness,
  Qwen3BpeTokenizer. (Gardés : CosyVoiceTtsEngine, SentenceStreamer, core.TtsEngine.)
- v2/ (rewrite abandonné) + entrées manifest ChatActivityV2/KazeiaServiceV2.
- Handlers d'intents dev Qwen3 (run_pipeline/stream_*/full_pipeline/decode_codes).
- jniLibs (-322 Mo) : libexecutorch_jni(185M), libexecutorch, libtts_pipeline(51M),
  libQnnGpu*(3), libkazeia_tts, libfbjni, libllama-common(74M).
- CMake : neon_ops/tts_talker_cpu/tts_cp_cpu/tts_decoder_ggml + ggml/llama IMPORTED
  (gardé : mel_extractor pour STT).
- Gradle : executorch.jar + fbjni + soloader.
- Manifest jniLibs re-figé (27→18 libs).

Survivants prod : LLM GGUF (kazeia_engine+llama+ggml) + LLM .pte (kazeia_pte+QnnHtp)
+ STT (kazeia_stt+ORT+mel_extractor) + TTS (cosyvoice). Build debug+release vert.

Bump 0.2.2 / versionCode 14.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 09:07:41 +02:00
Kazeia Team 3d002318d5 feat(updates): MAJ manuelles pilotées depuis l'admin (plus d'auto-update)
L'app patient ne vérifie/installe plus de MAJ au lancement. OnboardingActivity
démarre directement si le cœur est présent (offline) ; le catalogue n'est
sollicité que pour la 1ʳᵉ installation. Plus de prompt APK ni de worker
d'update en arrière-plan au boot.

Côté patient :
- UpdateStatus (état live) + UpdateWorker (check / install) réutilisant
  ProvisioningManager (contenu) + ApkUpdater (APK, PackageInstaller).
- ContentProvider : call("update_check"|"update_install") enqueue le worker ;
  query /updates expose phase + dispo APK + nb composants + progression.

Côté admin (Kazeia Admin) :
- écran « Mises à jour » (nav) : bouton Vérifier + Installer, état + barre de
  progression (poll /updates), via KazeiaUpdateClient.
- L'install APK ouvre le dialogue système PackageInstaller sur la tablette.

Bump 0.2.1 / versionCode 13. Round-trip device validé (check → AVAILABLE,
1 composant GGUF 2.38 Go détecté).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 08:27:41 +02:00
Kazeia Team a2f82f69f9 release(app): refonte 0.2.0 (versionCode 12)
Jalon refonte : moteur LLM unifié GGUF+.pte (UnifiedLlmAdapter), presets
sampling + toggle debug admin, OTA catalogue v7 (Speaker GGUF). Bump pour
publication self-update aux tablettes de prod.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 08:09:06 +02:00
Kazeia Team 0a72654093 refonte(llm): moteur LLM unifié GGUF + .pte NPU (UnifiedLlmAdapter)
Remplace la couche LLM par la dernière version de kazeia-engine
(cf dist/LLM_INTEGRATION.md). Chargement par magic-byte :
- GGUF ("GGUF"@0)  -> EngineLlmEngine (libllama CPU i8mm, session cache-préfixe KV)
- .pte  ("ET12"@4) -> PteLlmEngine (ExecuTorch+QNN, NPU V79, sans root)

- LlmLoader.kt copié de l'engine + fix encode DJL (encode(p,false,false))
- UnifiedLlmAdapter : pont com.kazeia.core.LlmEngine, generateWithSystem par tour
- ModelRegistry : 5 modèles (Qwen3.5-4B GGUF défaut + 4 .pte en sous-dossiers)
- KazeiaService recâblé ; DEFAULT_SYSTEM_PROMPT -> ConfigStore.DEFAULT_SPEAKER_PROMPT
- DJL tokenizer arm64 Android : tokenizers + tokenizer-native alignés 0.33.0
  (natif libdjl_tokenizer.so dans l'AAR ; natifs desktop exclus, -23 Mo APK)
- jniLibs : +libkazeia_pte.so, +libQnnHtpNetRunExtensions.so,
  libqnn_executorch_backend.so -> QAIRT 2.42 ; manifest re-figé (28 libs)
- fichiers morts archivés (ExecuTorchLlmEngine, GenieLlmEngine)

Phase 1 (#287) : build debug green. Phase 2 = validation device.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 22:47:32 +02:00
Kazeia Team 3b58fcb467 feat(tts): CosyVoice rapide (hiftf16, RTF 0.88 + cache) + guard init — v0.1.10
Nouveau jeu CosyVoice livré par l'engine (mêmes 5 symboles JNI, façade inchangée,
toujours auto-contenu) : 2 .so swappés (libkazeia_cosyvoice + libcosyvoice ;
libomp/libc++_shared identiques). Modèle cv3_distilled_30k_hiftf16.gguf (953 Mo)
remplace cv3_distilled_30k.gguf — MODEL mis à jour dans l'adaptateur.

Validé device : charge OK, synthèse OK, cache de voix entre appels →
1er son tour 1 ~10,6s → tour 2 ~6,7s (-37%). (RTF dev annoncé 0.88 ; sur cette
tablette thermiquement saturée on mesure ~1,1-1,7 — à revérifier device froid.)

Bonus robustesse : guard `if (!::voiceCommands.isInitialized) return false` dans
handleVoiceCommand — une entrée arrivée avant la fin de l'init du service ne
crashe plus (UninitializedPropertyAccessException, vu en test en tirant un intent
trop tôt).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:50:42 +02:00
Kazeia Team db0e56b26c perf(tts): NE PAS streamer LLM→CosyVoice (contention CPU) — documenté — v0.1.9
« Reste optionnel » : tenté le streaming LLM→TTS pour CosyVoice (synthèse de la
1ʳᵉ phrase pendant que le LLM décode, comme le chemin Qwen TTS-B).

Mesuré device : CONTRE-PRODUCTIF. LLM decode (6 threads CPU) et synthèse
CosyVoice (8 threads CPU) saturent tous les cœurs ; les chevaucher écroule le
LLM (17 → 0,55 tok/s, tour 1,1s → 41s, 1er son à 43s). Reverté.

Le streaming LLM→TTS n'a de sens que si les 2 étages utilisent des compute
distincts (Qwen3 NPU + TTS CPU). Pour CosyVoice (tout CPU) : laisser le LLM
finir vite PUIS synthesizeAndPlay, qui streame déjà phrase-par-phrase EN INTERNE
(synthèse N+1 pendant lecture N, sans contention). Net diff = un commentaire
garde-fou dans KazeiaService pour ne pas re-tenter ; code de streaming retiré.

Re-validé batch : LLM 17,1 tok/s (1,9s), TTS 1er son ~8s (RTF CosyVoice inhérent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:29:25 +02:00
Kazeia Team b822974b99 fix(llm): sessionReset natif corrigé (archi hybride) — reset de borne propre — v0.1.8
Bug : sessionReset()+sessionAsk() renvoyait VIDE. Cause = q35-lmq4 est l'archi
hybride Qwen3.5 (DeltaNet/SSM) ; llama_memory_seq_rm d'un suffixe « Returns false
if a partial sequence cannot be removed » (l'état récurrent ne se rembobine pas
partiellement). Le code ignorait ce retour → KV incohérent → tour suivant vide.

Fix natif (kazeia-engine dist/jni/kazeia_engine_jni.cpp, .so rebuildé CPU-only,
drop-in) : sessionReset teste le retour de seq_rm ; s'il échoue (récurrent/
hybride) → reset COMPLET (llama_memory_clear + re-prefill du system mémorisé) +
llama_sampler_reset. sessionStart mémorise désormais les tokens system.

App : EngineLlmAdapter.resetSession() rebascule sur le session.reset() natif
(au lieu du contournement newSession). Validé device : après reset, le tour
n'est plus vide ET la mémoire est effacée (ne connaît plus le prénom),
prefill ~285ms, decode ~17 tok/s, tours suivants OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:26:00 +02:00
Kazeia Team 8d548b1ca6 feat(llm): session cache-préfixe KV + streaming + mémoire conv. — v0.1.7
Intègre les 3 APIs livrées par l'engine (libkazeia_engine.so drop-in, façade
EngineLlmEngine.kt : generateStream / newSession / LlmSession / GenStats).

EngineLlmAdapter : crée une LlmSession au load (system prefillé 1×), et chaque
generate() fait session.ask() en STREAMING (onToken câblé au pipeline existant).
Le system n'est plus re-prefillé à chaque tour.

Mesuré device (q35-lmq4, t=6, v0.1.7) :
- tour : 2991→~1100 ms (prefill 2700→~300 ms, system caché). Decode réel
  17 tok/s (le « 3,2 » était l'artefact prefill-inclus ; tps vient maintenant
  de getLastStats, débit décode).
- streaming callback OK sous vraie JVM (le seul point que le dev n'avait pas
  pu tester) — aucun crash.
- mémoire conversationnelle (bonus) : rappelle « Richard » sur 3 tours.

Confidentialité : la mémoire est vidée aux bornes de conversation —
EngineLlmAdapter.resetSession() (recrée la session) appelé sur CLEAR_CHAT et
sur changement de profil actif (onProfileStoreChanged). Validé : après switch
de profil, le LLM ne connaît plus le prénom.

⚠ Bug natif à corriger côté engine : sessionReset()+sessionAsk() renvoie vide
(n_past non restauré à n_sys après seq_rm). Contourné en recréant la session
(newSession). Le mode mémoire (ask→ask sans reset) est, lui, OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:12:11 +02:00
Kazeia Team 2acf3922c9 feat(llm): défaut kazeia-engine GGUF (lib), plus de .pte au runtime — v0.1.6
Directive 2026-06-15 : si on passe par kazeia-engine, on n'utilise plus de .pte.

- ConfigStore.llmEngine défaut "prod"(.pte) → "lib" (EngineLlmAdapter +
  libkazeia_engine + GGUF). Le .pte n'est plus chargé au runtime ; il reste
  câblé en repli d'urgence seulement (et sur cette plateforme son runner QNN
  échoue de toute façon : « Failed to load llm runner [1] »).
- ModelRegistry.defaultSpeaker() "qwen3-4b-seq1024"(.pte) → "qwen3.5-4b" (GGUF,
  q35-lmq4) : config cohérente, le mode lib charge sp.ggufPath() directement
  sans repli, zéro référence .pte.

Validé device (release v0.1.6, SM8750) : libkazeia_engine charge q35-lmq4.gguf
en 1.8 s (backend='lib'), 2 générations FR thérapeutiques cohérentes
(« C'est compréhensible, je suis là avec toi… » / « C'est vraiment épuisant,
je te comprends. »). Règle aussi le mode perroquet observé avec le .pte.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 08:15:27 +02:00
Kazeia Team 359252bf5e feat(voice): voix pilotée par profil (app admin) — retrait du sélecteur patient — v0.1.5
La voix n'est plus choisie côté patient ; elle est définie PAR PROFIL dans l'app
admin (le champ Profile.voiceId + le dropdown admin existaient déjà ; seul le
runtime ne l'appliquait pas).

App patient :
- ChatActivity : suppression de setupVoiceSelector() + des listes voiceFiles/
  voiceNames/voiceColors. activity_chat.xml : suppression de la voiceBar (spinner),
  l'orbe se contraint désormais sous tvStatus (couleur = défaut service).
- KazeiaService : nouvelle source unique = le profil actif.
  - applyActiveProfileVoice() lit ProfileStore.activeProfile().voiceId (défaut
    DEFAULT_VOICE_ID="damien" en invité), appelée à l'init ET via un listener
    ProfileStore → propagation LIVE quand l'admin édite la voix / change de profil.
  - setVoiceId(id) résout selon le moteur actif : CosyVoice → cosyvoice/<id>.cvps ;
    Qwen3/lib → ../voix/<id>.wav. setVoice(pathOrId) conservé (intents test) délègue.

Validé device (release v0.1.5, SM8750, moteur cosyvoice) :
- plus de barre de voix dans l'UI patient (confirmé).
- profil voice_id=elodie → service charge 'elodie' (≠ défaut) ; changement live
  via provider voice_id=damien → ré-appliqué sans redémarrage.
- moteur 'lib' charge toujours (pas de régression du stack gelé).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 07:51:42 +02:00
Kazeia Team cca38ef50f feat(tts): intégrer libs CosyVoice3 auto-contenues + validation device — v0.1.4
Blocage libggml levé côté engine : nouvelles libs auto-contenues (ggml statique
+ visibilité hidden dans libcosyvoice.so, 0 symbole ggml_ exporté). 3 .so ajoutés
à jniLibs/arm64-v8a/ (libkazeia_cosyvoice, libcosyvoice ~20 Mo, libc++_shared ;
libomp déjà présent identique). Manifest régénéré (26 libs).

Validé on-device (release v0.1.4, SM8750) :
- chargement libkazeia_cosyvoice.so OK, modèle cv3_distilled_30k.gguf chargé en
  997 ms, voix 'damien' chargée, aucune collision libggml ni crash.
- nativeSynthesize (le marshalling JNI String→FloatArray, seul point non testé
  par le dev) CONFIRMÉ : phrase FR → 214080 samples 24 kHz (8.92 s), RTF ≈ 1.0.
  Audio sain (RMS 2303, pic 19741, 51% non-silence), voix clonée audible.

jniLibs gitignorés (whitelist) : seul le manifest est versionné. Tarball
build-artifacts à régénérer pour les machines tierces (suivi).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 21:31:42 +02:00
Kazeia Team 16d56bfd4d fix: défaut ttsEngine="lib" (crash-loop install fraîche) + ragCorpusDir externe-d'abord — v0.1.3
Deux bugs de prod trouvés en validant le cycle OTA corpus r2 :

1. ttsEngine default "prod" → toute install fraîche (release patient) chargeait
   Qwen3TtsEngine legacy → SIGSEGV déterministe dans llama_context ctor
   (libtts_talker_cpu compilé contre llama-upstream, libllama.so embarqué =
   fork ql depuis 2026-06-02, dérive ABI llama_context_params). La tablette
   release crash-loopait au démarrage du service depuis la migration ; la
   tablette dev ne le voyait pas (config tts_engine=lib persistée).
   → default "lib" (seul backend compatible avec les jniLibs livrés).

2. Le corpus RAG était résolu via MODELS_DIR/../rag_corpus : sur tablette dev,
   MODELS_DIR = legacy /data/local/tmp (modèles adb) → syncDir lisait l'ancien
   corpus alors que l'OTA dépose le composant rag_corpus dans le stockage
   externe. → KazeiaPaths.ragCorpusDir avec priorité INVERSE des modèles :
   l'externe (installeur/OTA) prime dès qu'il est non vide, fallback legacy.

versionCode 4 / 0.1.3, publié catalog v6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 23:32:16 +02:00
Kazeia Team d47206bff8 release: bump v0.1.1 (versionCode 2) — premier cycle OTA réel validé
Migration tablette dev debug→release + cycle OTA complet exercé de bout en bout
pour la première fois :
- uninstall debug → install release v1 (signé clé Kazeia, cf RELEASE_SIGNING.md)
- bump versionCode 2 / versionName 0.1.1, assembleRelease (verifyJniLibs OK)
- publication Nextcloud : APK v2 + composants RAG (e5 + corpus) + catalog v3
- sur device : Onboarding détecte « Mise à jour disponible v0.1.1 », télécharge
  (106 Mo, SHA-256 vérifié), PackageInstaller installe in-place → versionCode=2.

PREUVE CLÉ (la raison d'être de la signature release) : un marqueur de config
posé en v1 (rag_enabled=1, rag_threshold=0.85) a SURVÉCU à la mise à jour v2 →
signature cohérente entre versions → une MAJ ne détruit pas les données patient.

Note : catalog.spec.json/dist non suivis (miroir data). versionCode=2 est la
seule trace git ; la prochaine release repart de là.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 11:20:39 +02:00
Kazeia Team 9c6f4e8838 build: reproductibilité des jniLibs — manifest sha256 + script + garde release
Les 23 .so de app/src/main/jniLibs/ (474 Mo, 5 chaînes de build : kazeia-engine,
ExecuTorch, QNN SDK 2.42, Genie, TTS pipeline) sont des artefacts gitignorés
posés à la main — « ça ne buildait que sur cette machine ». On ne les versionne
pas (l'historique gonflerait de ~500 Mo par update engine) ; on versionne leur
DÉFINITION :

- scripts/jnilibs.MANIFEST.sha256 : état attendu (sha256 + provenances).
- scripts/jnilibs.sh : verify (CI/Gradle) | sync-engine (rafraîchit le
  sous-ensemble kazeia-engine depuis /opt/Kazeia-engine/dist) | update-manifest
  (re-fige, à committer) | export/import (tarball pour autre machine).
- Garde Gradle : preReleaseBuild dépend de verifyJniLibs → un build RELEASE avec
  des libs manquantes/modifiées/non déclarées ÉCHOUE. Debug reste libre.

Validé : release passe avec libs conformes ; échoue sur lib altérée (MODIFIÉ
libomp.so) ; verify OK après restauration. Ménage au passage : 3 .bak morts
(~293 Mo) sortis de jniLibs vers _jnilibs_backup_baks/.

Nouvelle machine : git clone + ./scripts/jnilibs.sh import <tarball> (export
déposé sur box.kazeia.com privé) → build garanti conforme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 09:31:00 +02:00
Kazeia Team e5b48e4e0f test: tests unitaires JVM — CrisisGuard (sécurité vitale) + VectorIndex + Chunker
Premier harnais de tests du projet. testImplementation junit:4.13.2, src/test/.

CrisisGuardTest — NON-RÉGRESSION DE SÉCURITÉ : 26 formulations d'idéation qui
DOIVENT déclencher (toutes les familles de motifs, casse, variantes STT collées
validées device), 10 messages de détresse ordinaire qui ne doivent PAS déclencher
(tristesse, insomnie, colère, deuil d'autrui, « envie de dormir »…), 2 limites
ASSUMÉES documentées (sensibilité d'abord : « en finir avec ces insomnies » et
mention indirecte du suicide déclenchent), invariants de la réponse (112,
honnêteté « programme », anti-isolement). Toute retouche des PATTERNS doit garder
ces tests verts ; le clinicien ÉTEND les cas, ne supprime pas de positif.

VectorIndexTest : classement cosinus, top-k, seuil, index vide/dim incohérente.
ChunkerTest : tag source, découpe+overlap sous la limite ubatch, texte vide.

./gradlew :app:testDebugUnitTest → 11 tests, 0 échec, <5 s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 08:44:09 +02:00
Kazeia Team 93f09c87d3 build: signature release (keystore partagé patient+admin) + doc de gestion
Keystore release créé : /opt/Kazeia/keystore/kazeia-release.jks (RSA 4096,
alias kazeia, valide 2056), credentials dans keystore/credentials.properties
(chmod 600, hors git — la liste blanche du .gitignore racine l'exclut d'office).

Les deux modules lisent credentials.properties : présent → assembleRelease signe
en release ; absent (autre machine/CI) → repli debug + warning, le build ne casse
pas. MÊME clé pour com.kazeia et com.kazeia.admin → permettra la
signature-permission sur le ContentProvider (plan admin).

Validé : assembleRelease des deux apps OK, apksigner confirme le certificat
CN=Kazeia (SHA-256 4b408d9d…) sur les deux APK.

docs/RELEASE_SIGNING.md : gestion complète — câblage, procédure de release via
catalogue (bump versionCode obligatoire), SAUVEGARDE du keystore (le point qui
ne pardonne pas), scénarios de panne (perte clé/mdp/machine/compromission),
migration one-shot des tablettes debug→release, dev quotidien reste en debug.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 08:32:29 +02:00
Kazeia Team 6f45a75197 chore: snapshot migration moteur lib (préservation avant dégraissage)
Capture l'état courant de kazeia-android (migration vers le moteur lib unifié :
suppression des anciens AudioCaptureManager/VadEngine/SileroVad, WhisperJni/
WhisperLiteRt/WhisperNpu/AndroidStt, KazeiaLlmJni/LlamaCppLlmEngine,
AndroidTts/ChatterboxTts ; édits app/JNI/gradle associés). L'app construit et
tourne sur device dans cet état. Commit de préservation pour ne rien perdre
avant de restreindre le périmètre de suivi du dépôt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 21:19:24 +02:00
Kazeia Team de878ddf5c TTS tremor investigation: identify cross-arch numerical floor, gate diag flags
Extensive investigation of the audible "tremor" in the generated voice-cloned
audio. Conclusion is architectural, not a bug:

  * Hexagon HMX fp16 talker logits correlate with PyTorch fp32 at 0.999998
  * ONNX Runtime CP V2 is bit-identical to PyTorch greedy CP (0.24% residual
    divergence measured by injecting Python's captured cb0 at each step —
    14/16 codebooks match 100%, cb14/cb15 miss 1 token out of 53)
  * BigVGAN decoder is bit-identical to PyTorch (validated earlier)
  * Therefore the tremor is caused entirely by the ~28% of cb0 argmax flips
    where the tiny fp16 logits drift crosses the top-1/top-2 margin. This
    cascades through the autoregressive chain into a trajectory the model
    never saw at training time → incoherent artifacts.

Cross-architecture test (x86 AVX-512 / ARM64 NEON+HMX) cannot be zeroed by
any runtime swap — LibTorch Android would use NEON kernels with a different
reduction order than PyTorch x86, same class of error, smaller but non-zero
residual. Temperature tweaking (0.3 → 0.9) and greedy-vs-sample gave no
perceptual difference: the floor is numeric, not in the sampling layer.

Accepted for MVP. Documented in project_tts_cross_arch_limit.md — this is a
thesis-relevant finding about on-device TTS deployment limits.

Cleanup:
  * All diagnostic flags (force_inject_pycb0, force_greedy_cb0, cb0_temp,
    force_python_codes, force_cpu_talker, force_cpu_talker_gguf) now gated
    behind BuildConfig.DEBUG via diagFlag()/diagFile() helpers. Release
    builds JIT-eliminate the file checks; debug builds keep the whole
    experimental toolchain for re-running the analysis for demos/thesis.
  * force_hexagon + force_cp_v2 stay unconditional — production routing.
  * Prefill cb0 now respects force_greedy_cb0 (was always sampleTopK 0.9).
  * Native TTS pipeline (executorch-custom/jni_layer_tts.cpp,
    app/src/main/jni/tts_pipeline.cpp): pad-zone sampling switched to
    greedy argmax so EOS gets a fair chance (temp 0.9 top-k kept producing
    audio past EOS where Python's seeded sampler terminated naturally).
  * scripts/prepare_tts_voiceclone.py: new script that captures Python
    greedy-CP reference (stochastic talker for EOS, deterministic CP) for
    token-by-token comparison.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:15:14 +02:00
Kazeia Team 389ffa7c61 Initial commit: Kazeia TTS pipeline on NPU via ExecuTorch
Full Qwen3-TTS-0.6B pipeline running on Snapdragon 8 Elite NPU:
  - Talker (28L) and Code Predictor (5L) as .pte on QNN HTP fp16
  - JNI integration, no root required
  - Validated audio quality: RTF 3.9

  Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 08:42:11 +02:00