feat(stt): cadrage + squelette assistant vocal Jarvis

Conception validée du projet STT — assistant vocal/HUD du homelab Funk :
- HUD web sur-mesure + STT/TTS local (faster-whisper + Piper)
- Packaging commande pipx (stt), démarrage auto systemd --user
- Cerveau 3 modes + auto-détection LAN : hermes / local-direct / claude-direct
- Mémoire 3 tiers : SQLite local + Qdrant s01 + GitHub (distillée, versionnée)

Réutilise tools/hermes-voice, LiteLLM, Hermes Agent. Squelette + doc admin/ia/stt.md,
implémentation par phases (roadmap dans le doc).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013FmcxGsyXZXogiAHQLjnZT
This commit is contained in:
Claude 2026-06-17 09:04:53 +00:00
parent 22d40023e1
commit 8096ba208d
No known key found for this signature in database
10 changed files with 395 additions and 0 deletions

167
admin/ia/stt.md Normal file
View file

@ -0,0 +1,167 @@
# STT — Assistant vocal "Jarvis" du homelab Funk
**STT** est l'interface vocale et graphique de Funk : un assistant type *Jarvis / Iron Man*
qui écoute, parle, affiche un HUD animé, et **agit sur le homelab** via Hermes Agent.
Il s'installe sur le poste perso comme **une commande unique** (`stt`) et démarre au boot.
> **Nom de code** : `STT`. À l'origine « Speech-To-Text », mais le projet couvre toute la
> chaîne voix → cerveau → voix + interface + mémoire. Renommable plus tard sans impact
> technique (FRIDAY, etc.) — c'est juste un identifiant de répertoire/commande.
> ⚠️ **Conception validée (2026-06-17)** — squelette en place, implémentation par phases
> (voir Roadmap). Aucun composant n'est encore déployé sur le poste.
---
## Principe directeur : réutiliser, ne pas réinventer
~70 % du backend existe déjà dans Funk. STT n'ajoute que **le visage** (HUD), la
**personnalisation**, la **mémoire multi-tiers** et le **packaging en commande**.
| Besoin Jarvis | Brique Funk réutilisée | État |
|---|---|---|
| Modèle local **ou** Claude | LiteLLM `:4000` + `hermes-default` + `hermes-switch qwen\|claude` | ✅ opérationnel |
| Agir sur le homelab | Hermes Agent (`:8080`, profils funk-ai/system/monitor/brain) | ✅ opérationnel |
| Voix (STT + TTS + wake word) | `tools/hermes-voice/` — faster-whisper + Piper + webrtcvad | ✅ existe (CLI only) |
| Démarrage auto au boot | pattern `systemd --user` (`tools/hermes-voice/install-service.sh`) | ✅ éprouvé |
| Mémoire sémantique | Qdrant `:6333` + RAG (`rag-query`/`rag-ingest`) sur s01 | ⚠️ **HS depuis 05/06** |
Maillon réellement manquant : **l'interface graphique HUD** + le packaging + la mémoire perso.
---
## Décisions d'architecture (verrouillées 2026-06-17)
| Décision | Choix retenu |
|---|---|
| Interface | **HUD web sur-mesure** (canvas/WebGL) + backend Python local |
| STT / TTS | **Local sur le poste** (faster-whisper CPU + Piper) |
| Packaging | **Commande `stt` via pipx** (`pipx install git+…#subdirectory=stt`) |
| Cerveau | **3 modes + auto-détection LAN** (voir ci-dessous) |
| Mémoire | **3 tiers** : local (SQLite) + s01 (Qdrant) + GitHub (distillée, versionnée) |
---
## Architecture
```
┌─ POSTE PERSO — commande `stt` (systemd --user, démarrage auto) ─────┐
│ │
│ ┌──────────────┐ websocket ┌───────────────────────────┐ │
│ │ HUD web │ ◀────────────▶ │ backend `stt` (Python) │ │
│ │ (kiosk) │ états + │ • micro + VAD + wake word │ │
│ │ • visualiseur│ transcript │ • faster-whisper (STT) │ │
│ │ • avatar │ + réponses │ • Piper (TTS) │ │
│ │ • thème │ │ • routeur cerveau (3 modes)│ │
│ │ • réglages │ │ • gestionnaire mémoire │ │
│ └──────────────┘ └───┬─────────────┬──────────┘ │
│ │ │ │
│ mémoire │ │ cerveau │
│ ┌────────────────────┘ │ │
│ ▼ ▼ │
│ SQLite local + cache (selon mode + LAN) │
│ git pull/push GitHub │
└───────────────────────────────────────────────────┼─────────────────┘
│ LAN (192.168.1.200)
┌───────────────────────────────────┼──────────────────┐
▼ ▼ ▼ ▼
Hermes :8080 LiteLLM :4000 Qdrant :6333 API Anthropic
(mode hermes) (mode local-direct) (mémoire s01) (mode claude-direct,
agit sur Funk chat g01/Claude sémantique hors-LAN, chat seul)
```
### Le cerveau — 3 modes + auto-détection
Au démarrage (et périodiquement), `stt` ping s01 (`192.168.1.200`) :
| Mode | Déclenché quand | Chemin | Agit sur Funk ? | Coût |
|---|---|---|---|---|
| **`hermes`** | LAN OK (défaut maison) | PC → Hermes `:8080` (route Qwen3 g01 ou Claude via `hermes-switch`) | ✅ oui | gratuit/local sauf si switch claude |
| **`local-direct`** | LAN OK, chat pur voulu | PC → LiteLLM `:4000` (Qwen3 g01) | ❌ chat seul | gratuit |
| **`claude-direct`** | hors-LAN (nomade) | PC → API Anthropic (clé locale) | ❌ (Funk injoignable) | facturé |
> **Clé de lecture** : « agir sur Funk » vit dans Hermes (sur s01). À la maison, même pour
> l'intelligence de Claude, on passe par Hermes (`hermes-switch claude`) → pouvoirs Funk
> conservés. Le mode `claude-direct` est le fallback nomade (Funk inatteignable de toute façon).
> Auto-détection par défaut, **override manuel** dans les réglages.
### La mémoire — 3 tiers
| Tier | Support | Contenu | Disponibilité |
|---|---|---|---|
| **Local** | SQLite + cache (`stt/memory/` local, gitignoré) | Historique brut récent, working memory | Toujours (offline) |
| **s01** | Qdrant `:6333`, collection dédiée STT | Mémoire sémantique long-terme, partagée avec le RAG Funk | Sur LAN |
| **GitHub** | `stt/memory/distilled/` (versionné) | Mémoire **distillée** : faits, préférences, résumés — source de vérité portable | Via git pull/push |
**Flux de synchro :**
1. Démarrage → `git pull` la mémoire distillée GitHub → charge en local.
2. Pendant l'usage → écriture immédiate en SQLite local.
3. Périodiquement / à l'arrêt → distillation (résumé via le cerveau) → `git commit` GitHub
**+** (si LAN) embedding dans Qdrant s01.
4. Sur nouveau poste / réinstall → GitHub re-seed local + Qdrant.
> ⚠️ **Vie privée** : seule la mémoire **distillée** est committée (faits/préférences).
> Les transcripts bruts restent locaux et **gitignorés**. Repo privé impératif.
---
## Composants
| Composant | Emplacement | Description |
|---|---|---|
| Commande `stt` | `stt/pyproject.toml` → entrypoint | `stt` (lance), `stt --setup` (install Whisper/Piper/voix), `stt --stop`, `stt --mode <hermes\|local\|claude>` |
| Backend | `stt/stt/` (package Python) | Extension de `tools/hermes-voice.py` : wake word, STT, TTS, routeur cerveau, mémoire, serveur websocket |
| HUD web | `stt/hud/` | Front statique (HTML/JS/canvas) : visualiseur animé, conversation, écran de réglages. Servi localement, ouvert en kiosk |
| Config | `stt/config/` | `stt.example.toml` + thèmes, avatars, voix Piper |
| Mémoire | `stt/memory/` | `distilled/` versionné + raw local gitignoré |
| Auto-start | `stt/install-service.sh` | `systemd --user` : backend + ouverture HUD au login |
| (opt.) cluster | `k8s/apps/stt/` | Phase 6 : STT/TTS déportés en Wyoming sur gpu-01 |
---
## Personnalisation (« l'image, le design, la voix »)
Pilotable depuis `stt/config/` + l'écran de réglages du HUD, sans recompiler :
- **Avatar / image** : asset swappable (`config/avatars/`).
- **Thème / design** : couleurs, fond, style du visualiseur (`config/themes/`).
- **Voix** : modèle Piper `.onnx` interchangeable (`config/voices/`), fr_FR par défaut.
- **Wake word** : configurable (défaut « Ok Hermès »).
- **Cerveau** : mode par défaut + override, clé API Anthropic locale (pour `claude-direct`).
---
## Prérequis / dépendances
- **Poste** : micro, Python 3.11+, pipx, navigateur (kiosk). `stt --setup` gère Whisper/Piper/voix.
- **LAN** : accès `192.168.1.200` (s01) pour modes `hermes`/`local-direct` + Qdrant.
- **Qdrant réparé** requis pour la mémoire tier s01 (crash-loop depuis 05/06, voir
`admin/incidents.md`). **Non bloquant** : MVP fonctionne avec mémoire locale + GitHub seules.
- **Clé Anthropic** locale (poste) pour le mode `claude-direct` nomade.
---
## Roadmap
| Phase | Objectif | Livrable |
|---|---|---|
| **0 — Cadrage** | Conception validée | ✅ ce doc + squelette `stt/` |
| **1 — Commande + backend** | `stt`/`stt --setup` lancent le moteur vocal + websocket | `stt/pyproject.toml`, `stt/stt/` (hermes-voice + ws + états) |
| **2 — Routeur cerveau** | 3 modes + auto-détection LAN | module `brain` (hermes/local-direct/claude-direct) |
| **3 — HUD MVP** | Visualiseur + conversation à l'écran | `stt/hud/` relié au websocket |
| **4 — Mémoire 3 tiers** | local + GitHub (+ Qdrant si dispo) | module `memory` + sync git + distillation |
| **5 — Personnalisation** | Thèmes / avatar / voix / wake word | écran réglages + `stt/config/` |
| **6 — Auto-start** | Démarre au boot du poste | `install-service.sh` + kiosk |
| **7 — Cluster (opt.)** | STT/TTS sur GPU | Wyoming sur gpu-01 + `k8s/apps/stt/` |
---
## Projets de référence (inspiration, non forkés)
Aucun ne se branche proprement sur LiteLLM + Hermes (ils embarquent leur propre LLM/agent),
mais bons pour le design et le code à piocher :
- [AlexandreSajus/JARVIS](https://github.com/AlexandreSajus/JARVIS) — Voice→LLM→Speech, interface web (proche du front visé).
- [InterGenJLU/jarvis](https://github.com/InterGenJLU/jarvis) — AMD ROCm (comme la RX 6700XT), HUD santé, streaming TTS.
- [novik133/jarvis](https://github.com/novik133/jarvis) — whisper.cpp + Piper (même TTS) + monitoring système.
- [rhasspy/wyoming-addons](https://github.com/rhasspy/wyoming-addons) — faster-whisper / Piper conteneurisés (option phase 7).

57
stt/README.md Normal file
View file

@ -0,0 +1,57 @@
# STT — Assistant vocal "Jarvis" de Funk
Assistant vocal + HUD pour le poste perso, branché sur le homelab Funk.
Écoute (wake word « Ok Hermès »), parle (Piper), affiche un HUD animé, et **agit sur Funk**
via Hermes Agent — ou discute avec Claude directement en déplacement.
> 📐 **Conception complète** : `admin/ia/stt.md`. Ce dossier est le squelette du projet ;
> l'implémentation se fait par phases (voir la Roadmap dans le doc de conception).
## Installation (cible : poste perso)
```bash
pipx install "git+https://github.com/Alkatrazz24/Funk-lab.git#subdirectory=stt"
stt --setup # télécharge le modèle Whisper, Piper et la voix fr_FR
stt # lance le backend + ouvre le HUD
```
## Commandes prévues
| Commande | Effet |
|---|---|
| `stt` | Lance le backend vocal + ouvre le HUD en kiosk |
| `stt --setup` | Première install : Whisper, Piper, voix, config par défaut |
| `stt --mode hermes\|local\|claude` | Force le mode cerveau (sinon auto-détection LAN) |
| `stt --no-tts` | Réponses texte seulement (debug) |
| `stt --stop` | Arrête le service |
## Structure
```
stt/
├── pyproject.toml # définit la commande `stt` (entrypoint pipx)
├── stt/ # package Python — backend (à implémenter, phases 1-4)
│ ├── voice/ # wake word, STT (faster-whisper), TTS (Piper)
│ ├── brain/ # routeur 3 modes : hermes / local-direct / claude-direct
│ ├── memory/ # gestionnaire mémoire 3 tiers (local/s01/GitHub)
│ └── server/ # serveur websocket vers le HUD
├── hud/ # front web (HTML/JS/canvas) — phase 3
├── config/ # stt.example.toml + thèmes / avatars / voix
├── memory/ # distilled/ (versionné) + raw local (gitignoré)
└── install-service.sh # systemd --user (démarrage auto) — phase 6
```
## Cerveau — 3 modes
Auto-détection au démarrage (ping s01 `192.168.1.200`) :
- **`hermes`** (LAN) → Hermes `:8080`, agit sur Funk.
- **`local-direct`** (LAN) → LiteLLM `:4000`, chat Qwen3 sur g01.
- **`claude-direct`** (hors-LAN) → API Anthropic, chat seul.
## Mémoire — 3 tiers
- **Local** : SQLite, historique brut, offline (gitignoré).
- **s01** : Qdrant `:6333`, sémantique long-terme (partagée RAG Funk, sur LAN).
- **GitHub** : `memory/distilled/`, mémoire distillée versionnée, portable.
> ⚠️ Seule la mémoire distillée est committée. Transcripts bruts gitignorés. Repo privé.

25
stt/config/README.md Normal file
View file

@ -0,0 +1,25 @@
# stt/config — personnalisation
Tout ce qui se change **sans recompiler**. `stt --setup` copie `stt.example.toml` vers
`~/.config/stt/stt.toml`.
```
config/
├── stt.example.toml # config de référence (modes cerveau, voix, thème, mémoire)
├── themes/ # thèmes HUD : couleurs, fond, style du visualiseur (CSS/JSON)
├── avatars/ # images / avatars affichés dans le HUD
└── voices/ # modèles Piper .onnx (TTS) — fr_FR par défaut
```
## Points de personnalisation
| Quoi | Où | Note |
|---|---|---|
| Image / avatar | `avatars/` + `ui.avatar` | swappable à chaud |
| Thème / design | `themes/` + `ui.theme` | couleurs, fond, visualiseur |
| Voix | `voices/*.onnx` + `voice.piper_voice` | géré par `stt --setup` |
| Wake word | `voice.wake_word` | défaut « Ok Hermès » |
| Cerveau | `[brain]` | mode + override + endpoints |
> 🔒 La clé Anthropic (mode `claude-direct`) passe par la variable d'env `ANTHROPIC_API_KEY`,
> **jamais** dans ce fichier (qui peut finir versionné).

View file

@ -0,0 +1,40 @@
# Configuration STT — copier vers ~/.config/stt/stt.toml (via `stt --setup`)
[brain]
# Mode cerveau : "auto" (détecte le LAN), "hermes", "local", "claude"
mode = "auto"
# Hôte testé pour décider LAN vs hors-LAN
lan_probe_host = "192.168.1.200"
[brain.hermes] # mode hermes (LAN) — agit sur Funk
url = "http://192.168.1.200:8080"
profile = "funk-ai"
[brain.local] # mode local-direct (LAN) — chat Qwen3 sur g01
url = "http://192.168.1.200:4000/v1/chat/completions"
api_key = "lm-studio"
model = "hermes-default"
[brain.claude] # mode claude-direct (hors-LAN) — chat seul
# Clé via variable d'env ANTHROPIC_API_KEY (ne JAMAIS committer la clé ici)
model = "claude-sonnet-4-6"
[voice]
wake_word = "hermes" # "Ok Hermès"
whisper_model = "large-v3" # STT (faster-whisper, CPU int8)
piper_voice = "fr_FR-upmc-medium" # TTS — voix dans config/voices/
language = "fr"
silence_sec = 1.2 # silence avant fin d'enregistrement
chat_timeout_sec = 60 # retour en veille sans interaction
[ui]
theme = "arc-reactor" # thème dans config/themes/
avatar = "default" # image dans config/avatars/
kiosk = true # ouvre le HUD en plein écran au lancement
[memory]
enabled = true
local_db = "~/.local/share/stt/memory.sqlite" # tier local (gitignoré)
distill_on_exit = true # distille → GitHub à l'arrêt
qdrant_url = "http://192.168.1.200:6333" # tier s01 (si LAN + Qdrant up)
qdrant_collection = "stt-memory"

20
stt/hud/README.md Normal file
View file

@ -0,0 +1,20 @@
# stt/hud — interface HUD web (à implémenter, phase 3)
Front statique servi localement par le backend, ouvert en kiosk (`ui.kiosk = true`).
Se connecte au serveur websocket du backend.
## Contenu prévu
- **Visualiseur** : canvas/WebGL animé (arc-reactor / waveform) réagissant aux états
`idle / listening / thinking / speaking`.
- **Conversation** : transcript utilisateur + réponses de l'assistant.
- **Réglages** : thème, avatar, voix, wake word, mode cerveau — écrit dans `~/.config/stt/stt.toml`.
## Design
S'inspire de [AlexandreSajus/JARVIS](https://github.com/AlexandreSajus/JARVIS) (interface web)
et de l'esthétique Iron Man. Thèmes pilotés par `stt/config/themes/`, avatar par
`stt/config/avatars/`.
Pas de framework lourd imposé — HTML/JS + canvas suffit pour le MVP. Le choix (vanilla vs
petit framework) sera tranché en phase 3.

5
stt/memory/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# Mémoire brute — ne quitte jamais le poste
raw/
*.sqlite
*.sqlite-*
*.db

26
stt/memory/README.md Normal file
View file

@ -0,0 +1,26 @@
# stt/memory — mémoire 3 tiers
```
memory/
├── distilled/ # mémoire DISTILLÉE — versionnée sur GitHub (faits, préférences, résumés)
└── raw/ # historique BRUT local — gitignoré, ne quitte jamais le poste
```
## Les 3 tiers
| Tier | Support | Contenu | Versionné ? |
|---|---|---|---|
| **Local** | SQLite (`~/.local/share/stt/`) + `raw/` | Historique brut, working memory | ❌ gitignoré |
| **s01** | Qdrant `:6333` (collection `stt-memory`) | Sémantique long-terme, partagée RAG Funk | — (sur s01) |
| **GitHub** | `distilled/` | Faits, préférences, résumés distillés | ✅ committé |
## Synchro
1. **Démarrage**`git pull` `distilled/` → charge en local.
2. **Usage** → écriture immédiate en SQLite local.
3. **Arrêt / périodique** → distillation (résumé via le cerveau) → `git commit` `distilled/`
**+** embedding dans Qdrant s01 (si LAN).
4. **Nouveau poste**`distilled/` re-seed le local + Qdrant.
> ⚠️ **Vie privée** : ne committer QUE de la mémoire distillée (pas de transcripts bruts).
> `raw/` et la SQLite sont gitignorés. Le repo doit rester privé.

View file

@ -0,0 +1 @@
# Mémoire distillée versionnée (faits, préférences, résumés). Rempli par le backend.

30
stt/pyproject.toml Normal file
View file

@ -0,0 +1,30 @@
[project]
name = "stt"
version = "0.0.1"
description = "STT — assistant vocal Jarvis du homelab Funk (voix + HUD + Hermes/Claude)"
requires-python = ">=3.11"
readme = "README.md"
# Réutilise les dépendances éprouvées de tools/hermes-voice + ajouts STT
dependencies = [
"faster-whisper>=1.0.0", # STT local
"sounddevice>=0.4.6", # capture micro
"numpy>=1.24.0",
"requests>=2.31.0", # appels Hermes / LiteLLM
"webrtcvad-wheels>=2.0.10",# VAD / wake word
"websockets>=12.0", # backend ↔ HUD
"anthropic>=0.40.0", # mode claude-direct (hors-LAN)
"qdrant-client>=1.13.0", # mémoire tier s01
"tomli-w>=1.0.0", # écriture config TOML
]
[project.scripts]
stt = "stt.cli:main"
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find]
where = ["."]
include = ["stt*"]

24
stt/stt/README.md Normal file
View file

@ -0,0 +1,24 @@
# stt/stt — package backend (à implémenter)
Cœur Python de la commande `stt`. Réutilise le moteur vocal de `tools/hermes-voice.py`.
```
stt/
├── cli.py # entrypoint `stt` (--setup, --mode, --no-tts, --stop) [phase 1]
├── voice/ # wake word + VAD, STT faster-whisper, TTS Piper [phase 1]
├── brain/ # routeur 3 modes : hermes / local-direct / claude-direct
│ # + auto-détection LAN (ping s01) [phase 2]
├── memory/ # mémoire 3 tiers : SQLite local, Qdrant s01, git GitHub [phase 4]
└── server/ # serveur websocket : pousse états + transcript + réponses au HUD [phase 1]
```
## États exposés au HUD (via websocket)
`idle` (veille) → `listening` (écoute) → `thinking` (réflexion cerveau) → `speaking` (TTS) → `idle`.
Le HUD anime le visualiseur selon l'état et affiche transcript utilisateur + réponse.
## Point de départ
Le module `voice/` part de `tools/hermes-voice.py` (déjà fonctionnel : wake word « Ok Hermès »,
faster-whisper, Piper, webrtcvad). À refactorer en classe + callbacks d'état pour le websocket,
puis brancher `brain/` (routeur) à la place de l'appel direct actuel.