feat(stt): auto-start via stt --install-service (phase 6)

Ajoute `stt --install-service` / `--uninstall-service` : génère et active un
service systemd --user (lié à graphical-session.target) qui lance le HUD + voix
à l'ouverture de session. Le kiosk existant (ui.kiosk) fournit l'« écran Jarvis ».

- cli : _service_unit (génération du unit, fonction pure testable),
  _cmd_install_service (écrit le unit + import-environment DISPLAY/WAYLAND +
  daemon-reload/enable/start), _cmd_uninstall_service.
- Docs : admin/ia/stt.md (phase 6 → , section Auto-start + caveat DISPLAY),
  stt/README.
- Bump client 0.2.1 → 0.3.0.

Phase 6 de la roadmap STT. Reste : test sur poste, outils Hermes (7).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qjq5jHiqNepnobJpHYpCa
This commit is contained in:
Claude 2026-06-18 20:18:39 +00:00
parent 2658f92c2f
commit d1bf20c86d
No known key found for this signature in database
4 changed files with 131 additions and 9 deletions

View file

@ -12,10 +12,11 @@ Objectif ultérieur : « agir sur le homelab » via les outils de Hermes (phase
> ✅ **2026-06-17** — déployé in-cluster (ArgoCD), mémoire court-terme (5a) + long-terme (5b)
> **validées sur cible**, embeddings dédiés nomic `:1238` (5d) + fix thinking Qwen3 `/no_think`
> (5e).
> ✅ **2026-06-18** — HUD avancé (4) implémenté (design **Claude Design**) : avatar portrait
> réactif (anneau/ping/spinner), transcript à bulles, drawer Réglages **câblé au backend**
> (reset / mode cerveau / mot de réveil à chaud). Reste : test audio bout-en-bout sur le poste
> (phase 1), portraits réels + test HUD sur cible, auto-start (6), outils Hermes (7).
> ✅ **2026-06-18** — HUD avancé (4) + auto-start (6). HUD : design **Claude Design** (avatar
> portrait réactif, transcript à bulles, drawer Réglages **câblé au backend** : reset / mode
> cerveau / mot de réveil à chaud). Auto-start : `stt --install-service` (systemd --user,
> `graphical-session.target`). Reste : test audio bout-en-bout (phase 1), portraits réels +
> test sur cible, outils Hermes (7).
---
@ -29,7 +30,7 @@ Objectif ultérieur : « agir sur le homelab » via les outils de Hermes (phase
| 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é |
| Démarrage auto au boot | `stt --install-service` (systemd --user) — repris du pattern `tools/hermes-voice/` | ✅ |
| Mémoire sémantique | Qdrant `:6333` + RAG (`rag-query`/`rag-ingest`) sur s01 | ✅ Qdrant réparé 17/06 (RAG `funk-docs` à re-ingérer) |
Maillon réellement manquant : **l'interface graphique HUD** + le packaging + la mémoire perso.
@ -157,6 +158,30 @@ Côté client, pilotable depuis `stt/client/config/` + l'écran de réglages du
---
## Auto-start (service systemd --user)
Lancer le HUD + voix automatiquement à l'ouverture de session (écran « Jarvis ») :
```bash
stt --install-service # écrit ~/.config/systemd/user/stt.service, enable + start
stt --uninstall-service # retire le service
```
Le unit est lié à `graphical-session.target` (démarre avec la session graphique, s'arrête avec).
Suivi : `systemctl --user status|restart stt`, `journalctl --user -u stt -f`.
**Caveat DISPLAY** : un service `systemd --user` n'hérite de `DISPLAY`/`WAYLAND_DISPLAY` que si la
session graphique les a importés dans le gestionnaire user. `--install-service` exécute un
`systemctl --user import-environment DISPLAY WAYLAND_DISPLAY XAUTHORITY` (best-effort). Si le kiosk
ne s'ouvre pas : vérifier que le bureau peuple `graphical-session.target` (GNOME/KDE le font), ou
lancer `stt` via les « applications au démarrage » du bureau. Le kiosk est piloté par `[ui].kiosk`
(défaut `true`).
**Boot sans login** (borne dédiée) : `sudo loginctl enable-linger "$USER"` + auto-login du bureau —
la session graphique démarre au boot et tire le service.
---
## Roadmap
| Phase | Objectif | État |
@ -171,7 +196,7 @@ Côté client, pilotable depuis `stt/client/config/` + l'écran de réglages du
| **5c — Réparation Qdrant** | drop `funk-docs` corrompu + restart (s01) | ✅ (17/06) |
| **5d — Embeddings dédiés** | `nomic-embed-text` (dim 768) sur gpu-01 `:1238` → recherche plus précise | ✅ (rôle llama_server + migration auto collection) |
| **5e — Fix thinking Qwen3** | `/no_think` (content vide / timeout 502) + fallback reasoning_content | ✅ (17/06) |
| **6 — Auto-start client** | `install-service.sh` (systemd --user) + kiosk | ⏳ |
| **6 — Auto-start client** | `stt --install-service` (systemd --user, `graphical-session.target`) + kiosk | ✅ (à tester sur poste) |
| **7 — Outils Hermes** | « agir sur Funk » via gateway `:8080` (API à spécifier) | ⏳ |
### État (validé sur cible)
@ -179,8 +204,9 @@ Côté client, pilotable depuis `stt/client/config/` + l'écran de réglages du
Serveur déployé in-cluster via ArgoCD (image `sha-<commit>` gérée par CI). Mémoire **5a + 5b
validées en prod** : court-terme (historique de session) et long-terme (rappel cross-session
« Felix », `points_count` confirmé via `/v1/memory/health`). HUD avancé (4) **implémenté**
(design Claude Design, câblé au websocket backend). Reste côté poste : test audio bout-en-bout
(micro → STT → serveur → TTS) et test du HUD avec portraits réels.
(design Claude Design, câblé au websocket backend) ; auto-start (6) via `stt --install-service`.
Reste côté poste : test audio bout-en-bout (micro → STT → serveur → TTS) et test du HUD/kiosk
avec portraits réels.
---

View file

@ -30,6 +30,7 @@ stt --setup # génère ~/.config/stt/stt.toml (dont [server] url)
stt --text # chat texte simple (sans micro ni HUD) — idéal pour tester
stt # voix + HUD ; dis "Ok Hermès …"
stt --model claude # choisir le modèle : hermes (défaut) | claude | qwen | opus
stt --install-service # auto-start au login (systemd --user, kiosk « écran Jarvis »)
```
En mode texte : commandes `/model <nom>`, `/models`, `/quit`.
Prérequis voix : micro, Piper + voix dans `~/.local/share/piper/`, `aplay` (alsa-utils).

View file

@ -1,6 +1,6 @@
[project]
name = "stt"
version = "0.2.1"
version = "0.3.0"
description = "STT — client vocal Jarvis du homelab Funk (voix + HUD, parle au STT-server)"
requires-python = ">=3.11"
readme = "README.md"

View file

@ -4,6 +4,7 @@ from __future__ import annotations
import argparse
import asyncio
import os
import sys
@ -22,6 +23,92 @@ def _cmd_setup() -> int:
return 0
SERVICE_NAME = "stt.service"
def _service_unit(stt_bin: str) -> str:
"""Contenu du unit systemd --user : lance `stt` (HUD + voix) avec la session graphique."""
return f"""[Unit]
Description=STT Assistant vocal Jarvis (HUD + voix)
After=graphical-session.target
PartOf=graphical-session.target
[Service]
Type=simple
ExecStart={stt_bin}
Restart=on-failure
RestartSec=5
Environment=PYTHONUNBUFFERED=1
StandardOutput=journal
StandardError=journal
SyslogIdentifier=stt
[Install]
WantedBy=graphical-session.target
"""
def _cmd_install_service() -> int:
"""Installe et démarre le service systemd --user (auto-start au login + kiosk)."""
import shutil
import subprocess
from pathlib import Path
stt_bin = shutil.which("stt") or os.path.realpath(sys.argv[0])
unit_dir = Path.home() / ".config" / "systemd" / "user"
unit_dir.mkdir(parents=True, exist_ok=True)
unit_path = unit_dir / SERVICE_NAME
unit_path.write_text(_service_unit(stt_bin))
print(f"✓ Unit écrit : {unit_path}")
print(f" ExecStart = {stt_bin}")
def sc(*args):
subprocess.run(["systemctl", "--user", *args], check=False)
# Rendre DISPLAY/WAYLAND dispo au gestionnaire systemd --user (pour le kiosk)
subprocess.run(
["systemctl", "--user", "import-environment",
"DISPLAY", "WAYLAND_DISPLAY", "XAUTHORITY"],
check=False,
)
sc("daemon-reload")
sc("enable", SERVICE_NAME)
sc("start", SERVICE_NAME)
print("\n✓ Service activé et démarré (systemd --user).")
print("\nCommandes utiles :")
print(" systemctl --user status stt")
print(" journalctl --user -u stt -f")
print(" systemctl --user restart stt")
print(" stt --uninstall-service")
print("\nDémarrer dès le boot (kiosk dédié, sans login manuel) :")
print(' sudo loginctl enable-linger "$USER" # + auto-login du bureau')
print("\nLe HUD s'ouvre en kiosk si [ui].kiosk = true (défaut).")
print("Si l'écran ne s'ouvre pas : voir le caveat DISPLAY dans admin/ia/stt.md.")
return 0
def _cmd_uninstall_service() -> int:
"""Arrête, désactive et supprime le service systemd --user."""
import subprocess
from pathlib import Path
def sc(*args):
subprocess.run(["systemctl", "--user", *args], check=False)
sc("stop", SERVICE_NAME)
sc("disable", SERVICE_NAME)
unit_path = Path.home() / ".config" / "systemd" / "user" / SERVICE_NAME
if unit_path.exists():
unit_path.unlink()
print(f"✓ Unit supprimé : {unit_path}")
else:
print("(unit déjà absent)")
sc("daemon-reload")
print("✓ Service STT désinstallé.")
return 0
def _make_client(args):
from stt.api import ServerClient
from stt.config import load_config, resolve_model
@ -116,10 +203,18 @@ def main(argv: list[str] | None = None) -> int:
p.add_argument("--model", help="Modèle : hermes, claude, qwen, opus (ou alias LiteLLM)")
p.add_argument("--server", help="Force l'URL du STT-server (sinon config)")
p.add_argument("--no-tts", action="store_true", help="Réponses texte seulement (voix)")
p.add_argument("--install-service", action="store_true",
help="Installe le service systemd --user (auto-start au login + kiosk)")
p.add_argument("--uninstall-service", action="store_true",
help="Désinstalle le service systemd --user")
args = p.parse_args(argv)
if args.setup:
return _cmd_setup()
if args.install_service:
return _cmd_install_service()
if args.uninstall_service:
return _cmd_uninstall_service()
if args.text:
return _cmd_text(args)
return _cmd_voice(args)