Funk-lab/progress/2026-06-22.md
ALI YESILKAYA 610d32f1e9
feat(stt-client): voix Kokoro (TTS enfichable) + commande --install-kokoro (#55)
Ajoute un moteur TTS plus naturel (Kokoro, ONNX/CPU) au choix, Piper restant
le défaut et le repli.

- engine.py : synthèse enfichable (_synthesize → _synth_piper | _synth_kokoro).
  Kokoro chargé une fois (préchargé au boot si sélectionné), WAV via wave stdlib
  (numpy → PCM16), lecture/interruption inchangées. Repli SILENCIEUX si le modèle
  ou le paquet kokoro-onnx manque (jamais de crash).
- config : [voice].tts_engine ("piper"|"kokoro") + kokoro_voice (ff_siwis, FR),
  kokoro_speed, kokoro_lang.
- cli : `stt --install-kokoro` télécharge kokoro-v1.0.onnx + voices-v1.0.bin dans
  ~/.local/share/kokoro et imprime les étapes (pipx inject kokoro-onnx, espeak-ng).
- pyproject : extra optionnel [kokoro] ; client 0.15.0.
- doc : stt.example.toml + admin/ia/stt.md + journal.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:57:27 +02:00

131 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 2026-06-22 — STT : transcription live + veille/réveil vocal ; relevé cluster (la veille)
## STT — streaming partiel & contrôle vocal veille/réveil (client 0.13.0 → 0.14.0)
Deux fonctionnalités demandées sur l'assistant vocal Asa.
### Transcription partielle live dans le HUD
En éveil, pendant qu'on parle, le buffer audio en cours est **re-transcrit périodiquement**
(`partial_interval_sec`, défaut 0,5 s) et poussé au HUD via un événement `partial` → le texte
**se forme en direct** dans une bulle dédiée (italique + curseur clignotant), puis la bulle
« user » finale la remplace.
- Auto-activée seulement avec l'ASR **onnx** (Parakeet ~11× temps réel en CPU) ; whisper large-v3
CPU est trop lent → laissé off (forçable via `[voice].partial_transcription`).
- Bornée : `partial_min_sec` (anti-bruit) … `partial_max_sec` (coût ; au-delà, seul le final suit).
- Transcription inline dans la boucle audio (thread unique → pas d'accès concurrent au modèle ASR).
### Veille / réveil vocal (« Asa stop » / « Asa »)
Refonte de la boucle en **machine à états** explicite veille (`asleep`) ↔ éveil :
- **Veille** : Asa transcrit chaque énoncé **uniquement** pour repérer le wake word ; ne répond à
rien d'autre. HUD : portrait atténué/désaturé, label « SOMMEIL ».
- **« Asa »** (mot entier, alias ASR tolérés) → réveil. « Asa, <question> » exécute la question.
- **« Asa stop »** (ou `stop`/`dors`/`au revoir`… = `sleep_words`) → retour veille (+ « Je me mets
en veille »). Timeout d'inactivité aussi.
- Wake word par défaut **`asa`** (était `hermes`) ; `wake_word`/`wake_aliases`/`sleep_words` config.
- Détection wake durcie : **mot entier** (plus de sous-chaîne — « asa » est court et matchait
« casa », « phrase »…).
Validé : tests unitaires (wake/sleep/alias) + Playwright (rendu `asleep`, bulle partielle mise à
jour en place puis remplacée par le final, nettoyage du partiel au changement d'état).
Bump client **0.14.0**. Doc : `admin/ia/stt.md` (section « Interaction vocale »).
> 🐛 **Trouvaille ASR (test sur poste)** : Parakeet FR ne transcrit **pas** « Asa » comme un mot
> — il l'entend **« a so » / « à ça » / « ah ça »** (deux tokens). La détection mot-entier ne
> matchait donc jamais → « rien ne se passe ». **Corrigé** : on **joint les 1-2 premiers mots**
> (déaccentués, sans espace) et on compare en égalité exacte à des alias compacts (`aso`, `aca`,
> `ahca`…). Faux positifs évités (« à cause » → `acause` ≠ alias). Ajout d'un log diagnostic
> `[stt.voice]` (journalctl, `STT_VOICE_LOG=0` pour couper) qui montre la transcription réelle.
> Validé en live : « Asa » réveille, « Asa stop » endort.
---
## (Rappel) Relevé d'état réel + RAG re-ingéré — fait la veille (2026-06-21, PR #44)
Pour mémoire, cette même journée de travail a aussi produit (PR #44, mergée) : alignement de la
doc sur l'état réel du cluster (`admin/ops/etat-cluster.md`), re-ingestion du RAG **436/436 chunks**
et le **fix du batch embed** (`llama-embed` ubatch 512 → 2048). Détail : `progress/2026-06-21.md`.
---
## Réconciliation `make apply-gpu` + durcissement IaC (soir)
-**Drop-in `llama-embed` réconcilié.** Apply ciblé `--tags llama_server` (commit llama.cpp pinné
→ zéro rebuild, seul `llama-embed` redémarre) : l'unit rendu par le rôle porte maintenant
`--batch-size/--ubatch-size 2048`. Drop-in temporaire `batch.conf` **supprimé**. `ubatch 2048`
effectif sur le process, `rag-query` OK → RAG intact.
- 🐛 **Repo `amdgpu` cassé sur gpu-01** (cause du blocage `make apply-gpu` complet) : le `dnf upgrade`
du rôle `common` avait bumpé l'hôte en AlmaLinux **9.8**, mais radeon.com ne publie pas de
`rhel/9.8` (ni 9.5) — seulement 9.4/9.6/**9.7**. Le repo package-shipped (`$amdgpudistro`) tombait
en 404. **Soigné en live** (`/etc/dnf/vars/amdgpudistro=9.7``dnf repolist` propre) et **durci en
IaC** : `rocm_rhel_version` 9.4 → 9.7, `llama_server_commit` pinné, pièges documentés dans les
README de rôle. (PR `fix/gpu-apply-amdgpu-repo-and-commit-pin`.)
## Asa agentique — boucle d'outils / function calling (Phase 1)
Asa ne répondait qu'à partir d'un contexte **figé** (RAG doc ou blocs live pré-câblés, à
présélectionner) → « gpu-01 tourne bien ? » tombait sur « la doc ne le précise pas » (état live
absent de la doc). Bascule vers une **boucle de function-calling** : le modèle décide quels outils
appeler, puis répond à partir des résultats.
- **Validé empiriquement avant de coder** : Qwen3-8B via llama.cpp fait du tool-calling natif
(pas besoin de `--jinja`), LiteLLM transmet bien les `tools`, et le tool-calling **survit au
`/no_think`**. → 100 % local, pas d'escalade Claude (choix utilisateur).
- **Nouveau contexte `asa`** (`STT_DEFAULT_CONTEXT=asa`, défaut prod) + `tools.py` (registre
schémas + exécuteurs) + `brain.ask_with_tools` (boucle bornée `STT_TOOL_MAX_ITERS=4`, réponse
forcée au-delà). Outils **Phase 1, lecture seule** : `search_docs`, `host_health`,
`cluster_status`, `prometheus_query`. Trace des outils → visualiseur HUD.
- **PromQL validés** contre le vrai Prometheus in-cluster (labels `gpu-01-node`/`storage-01`/
`llama-server-gpu` confirmés ; up/load/mem renvoient des données réelles). 3 tests unitaires de
la boucle (hors-ligne). (PR `feat/asa-tool-loop`.)
- **Choix** : web search = **SearXNG in-cluster** (Phase 2) ; modèle = **local uniquement**.
### Validation prod (#48 mergé) + Phase 2
-**Déployé et testé en prod** : « gpu-01 tourne bien ? » → outil `host_health` → *« en ligne,
charge 0.08, 48 % RAM, llama-server actif »*. « le cluster va bien ? » → `cluster_status`.
(Rollout : ArgoCD s'était calé sur le commit de merge ; un `refresh=hard` l'a fait rouler vers
l'image CI `sha-7cafc06`.)
- 🐛 **Fix `cluster_status`** : les pods de CronJob **terminés** (`sacrifice-assign-renfort`,
Succeeded) comptaient comme « non prêts » → fausse alarme. Join `kube_pod_status_phase{phase=~
"Running|Pending"}` → 3 faux positifs → 0 (validé in-cluster).
-**Phase 2 — recherche web** : SearXNG self-host (`k8s/apps/searxng/`, Application ArgoCD,
ns `ai`, interne) + outil `web_search` (`STT_SEARXNG_URL`). Gotcha clé : `search.formats` doit
inclure `json`. Image pinnée, conf via initContainer→emptyDir (mount RO contourné), PodSecurity
restricted. (PR `feat/asa-web-search`, serveur 0.8.0.)
### Vérif prod #49 + fix SearXNG + Phase 3
-**`cluster_status` corrigé vérifié en prod** : « le cluster va bien ? » → *« 38 pods prêts, un
seul non prêt : searxng-… »* → ignore bien les pods cron terminés ET pointe le vrai souci.
- 🐛 **SearXNG crashait** (`'tcp://…:8080' is not a valid integer`) : le Service `searxng` faisait
injecter l'env service-link `SEARXNG_PORT=tcp://…`, lu comme port de bind. Fix `enableServiceLinks:
false` + `SEARXNG_PORT`/`SEARXNG_BIND_ADDRESS` explicites (**PR `fix/searxng-port-collision`**).
Vérif end-to-end `web_search` **après merge** de ce fix.
-**Phase 3 — actions admin pilotées par le LLM** : outil `admin_action(description)` (contexte
`asa`) qui **ne fait que proposer** — la boucle s'arrête (`confirm_tools`), Asa demande confirmation,
le tour suivant « confirme » → `hermes-exec`. Réutilise le handshake + jeton du contexte `agent` ;
exposé seulement si `STT_ACTIONS_ENABLED` + jeton. 1 test unitaire de plus. (PR `feat/asa-admin-actions`,
serveur 0.9.0.)
## Voix d'Asa — TTS Kokoro enfichable (client 0.15.0)
- **TTS enfichable** côté client (`engine._synthesize` → piper | kokoro), Piper conservé en **repli**.
Kokoro (ONNX/CPU) = voix bien plus naturelle. Config `[voice].tts_engine` + `kokoro_voice`
(`ff_siwis`, FR femme) / `kokoro_speed` / `kokoro_lang`.
- Nouvelle commande **`stt --install-kokoro`** (télécharge `kokoro-v1.0.onnx` + `voices-v1.0.bin`).
Dépendance optionnelle `kokoro-onnx` (extra `[kokoro]`) ; phonémisation FR via `espeak-ng`.
- Préchargement au boot si sélectionné ; **repli silencieux** si modèle/paquet absent (pas de crash).
- (PR `feat/asa-kokoro-tts`.)
## Prochaines étapes
- **Tester Kokoro sur le poste** : `stt --install-kokoro` + `pipx inject stt kokoro-onnx` +
`espeak-ng` + `tts_engine = "kokoro"` + `stt --restart`. Ajuster `kokoro_speed` au goût.
- **Vérifier au déploiement** : SearXNG Ready + `web_search` réel ; puis un essai `admin_action`
(« redémarre … » → confirme → exécution Hermes).
- Si le wake word « asa » génère des faux positifs/négatifs, ajuster `wake_aliases`.
- **1er test d'action vocale** (#43, contexte 🤖 Agent → `hermes-exec`).