mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 21:14:42 +02:00
feat(voice): améliorer hermes-voice + fix hermes-agent
hermes-voice :
- mode SSH : appel `hermes -z` via storage-01 (soul + skills + RAG + mémoire)
- mode litellm : fallback avec historique de session (10 échanges)
- Whisper large-v3 : meilleure reconnaissance FR (distil-large-v3 dérivait vers l'anglais)
- forcer français dans les requêtes SSH
- historique conversationnel + reset vocal ("oublie tout")
hermes-agent :
- fix boucle de redémarrage (510 restarts) : ajouter --replace à ExecStart
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6fc5b5984e
commit
a3ba3089e2
3 changed files with 119 additions and 45 deletions
|
|
@ -39,7 +39,7 @@ VOICE_THRESH = 0.012 # seuil RMS normalisé pour détecter la parole
|
|||
SILENCE_SEC = 1.5 # silence pour terminer l'enregistrement
|
||||
MIN_SPEECH = 0.8 # durée minimale traitée (filtre les bruits courts)
|
||||
|
||||
WHISPER_SIZE = "small" # base=rapide, small=équilibré, medium=précis
|
||||
WHISPER_SIZE = "large-v3" # large-v3 : meilleure reconnaissance FR (distil-large-v3 dérive vers l'anglais)
|
||||
KEYWORD = "hermes"
|
||||
KEYWORD_POS = 5 # le mot-clé doit être dans les N premiers mots
|
||||
|
||||
|
|
@ -48,8 +48,18 @@ KEYWORD_POS = 5 # le mot-clé doit être dans les N premiers mots
|
|||
PIPER_BIN = Path.home() / ".local/share/piper-runtime/piper"
|
||||
PIPER_VOICE = Path.home() / ".local/share/piper/fr_FR-upmc-medium.onnx"
|
||||
|
||||
MAX_HISTORY = 10 # nombre d'échanges (user+assistant) conservés en mémoire
|
||||
|
||||
# Mode "full Hermes" via SSH (soul + skills + RAG + mémoire long-terme)
|
||||
# Passer HERMES_MODE = "ssh" pour router par `hermes -z` sur storage-01
|
||||
# Passer HERMES_MODE = "litellm" pour appel direct LiteLLM (session history uniquement)
|
||||
HERMES_MODE = "ssh" # "litellm" | "ssh"
|
||||
HERMES_SSH = "s01" # alias SSH défini dans ~/.ssh/config
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
_history: list[dict] = [] # historique session (mode litellm uniquement)
|
||||
|
||||
|
||||
def beep(freq: int = 880, duration: float = 0.12):
|
||||
t = np.linspace(0, duration, int(SAMPLE_RATE * duration), endpoint=False)
|
||||
|
|
@ -80,20 +90,56 @@ def detect_keyword(text: str) -> tuple[bool, str]:
|
|||
def transcribe(whisper_model, audio: np.ndarray) -> str:
|
||||
audio_f32 = audio.astype(np.float32) / 32768.0
|
||||
segments, _ = whisper_model.transcribe(
|
||||
audio_f32, language="fr", beam_size=3, vad_filter=True
|
||||
audio_f32,
|
||||
language="fr",
|
||||
task="transcribe",
|
||||
beam_size=5,
|
||||
vad_filter=True,
|
||||
initial_prompt="Transcription en français.",
|
||||
)
|
||||
return " ".join(seg.text for seg in segments).strip()
|
||||
|
||||
|
||||
def ask_hermes(text: str) -> str:
|
||||
if HERMES_MODE == "ssh":
|
||||
return _ask_via_ssh(text)
|
||||
return _ask_via_litellm(text)
|
||||
|
||||
|
||||
def _ask_via_ssh(text: str) -> str:
|
||||
"""Route par le vrai Hermes sur storage-01 (soul + skills + RAG + mémoire)."""
|
||||
import subprocess
|
||||
# Forcer le français + échapper les guillemets simples pour bash
|
||||
query = f"Réponds en français. {text}"
|
||||
safe = query.replace("'", "'\\''")
|
||||
cmd = [
|
||||
"ssh", HERMES_SSH,
|
||||
f"sudo -i -u hermes bash -c 'HERMES_HOME=/srv/data/hermes hermes --profile funk-ai -z \"{safe}\"'"
|
||||
]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
||||
output = result.stdout.strip()
|
||||
if not output and result.returncode != 0:
|
||||
raise RuntimeError(result.stderr.strip() or "hermes -z a échoué")
|
||||
return output
|
||||
|
||||
|
||||
def _ask_via_litellm(text: str) -> str:
|
||||
"""Appel direct LiteLLM avec historique de session."""
|
||||
global _history
|
||||
|
||||
lower = text.lower().strip()
|
||||
if any(w in lower for w in ("reset", "nouveau contexte", "oublie tout", "efface")):
|
||||
_history.clear()
|
||||
return "Contexte effacé, on repart de zéro."
|
||||
|
||||
_history.append({"role": "user", "content": text})
|
||||
window = _history[-(MAX_HISTORY * 2):]
|
||||
|
||||
resp = requests.post(
|
||||
LITELLM_URL,
|
||||
json={
|
||||
"model": HERMES_MODEL,
|
||||
"messages": [
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{"role": "user", "content": text},
|
||||
],
|
||||
"messages": [{"role": "system", "content": SYSTEM_PROMPT}] + window,
|
||||
"max_tokens": 300,
|
||||
"temperature": 0.7,
|
||||
},
|
||||
|
|
@ -101,7 +147,9 @@ def ask_hermes(text: str) -> str:
|
|||
timeout=30,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()["choices"][0]["message"]["content"].strip()
|
||||
response = resp.json()["choices"][0]["message"]["content"].strip()
|
||||
_history.append({"role": "assistant", "content": response})
|
||||
return response
|
||||
|
||||
|
||||
def speak(text: str):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue