mirror of
https://github.com/R0m1k3/EveFlow.git
synced 2026-10-22 14:48:15 +02:00
Écoute permanente - Silero VAD dans le worker sherpa-onnx (vad.start/audio/stop) ; le renderer envoie des trames de 128 ms pendant la commande et reçoit le segment WAV complet (speech-start / segment). Repli sur le VAD énergétique si le modèle n'est pas installé ou si l'option est désactivée. - Modèle « silero-vad » (0,6 Mo) dans le catalogue, section « Fin de phrase » dans Modèles locaux, réglage dans Paramètres → Micro. Vision d'écran - IPC system:screen-capture (desktopCapturer, JPEG 1600 px) ; bouton dans la barre de commande ; « regarde mon écran… » joint la capture à la question envoyée à Hermes (transport chat completions pour les images). Actions locales - IPC system:action à liste blanche : verrouiller la session, touches média (volume, mute, lecture, piste), ouvrir une application connue ou une URL http(s), presse-papiers, recherche de fichiers dans les dossiers utilisateur. - Routeur d'intentions FR/EN exécuté avant Hermes (« coupe le son », « ouvre Spotify », « verrouille la session »…), résultat affiché et lu ; désactivable. Correctif - Chat completions : les premiers fragments SSE arrivés avant l'événement de démarrage étaient perdus (premier mot manquant) ; ils sont maintenant rejoués. Tests : 26 tests unitaires (intentions locales ajoutées), e2e Electron sous Xvfb avec les vrais modèles (Silero : un segment par phrase, 6,7 s d'audio traités en 190 ms ; capture 109 ko reçue par Hermes ; intention locale traitée sans Hermes). Claude-Session: https://claude.ai/code/session_017Wn5VX9HNbJ7N54hR24u9Y Co-authored-by: Claude <noreply@anthropic.com>
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { create } from 'zustand';
|
|
import type { TtsState } from '../services/voice/tts';
|
|
|
|
export type ListenPhase = 'off' | 'arming' | 'listening' | 'speech' | 'transcribing';
|
|
export type WakeState = 'off' | 'starting' | 'spotting' | 'error';
|
|
|
|
interface VoiceStore {
|
|
phase: ListenPhase;
|
|
inputLevel: number;
|
|
tts: TtsState;
|
|
handsFree: boolean;
|
|
lastTranscript: string;
|
|
interim: string;
|
|
error: string | null;
|
|
micDevices: Array<{ deviceId: string; label: string }>;
|
|
wake: WakeState;
|
|
wakeKeywords: string[];
|
|
neuralVad: boolean;
|
|
setNeuralVad: (on: boolean) => void;
|
|
setWake: (state: WakeState, keywords?: string[]) => void;
|
|
setPhase: (phase: ListenPhase) => void;
|
|
setInputLevel: (level: number) => void;
|
|
setTts: (state: TtsState) => void;
|
|
setHandsFree: (on: boolean) => void;
|
|
setTranscript: (text: string) => void;
|
|
setInterim: (text: string) => void;
|
|
setError: (error: string | null) => void;
|
|
setMicDevices: (devices: Array<{ deviceId: string; label: string }>) => void;
|
|
}
|
|
|
|
export const useVoice = create<VoiceStore>((set) => ({
|
|
phase: 'off',
|
|
inputLevel: 0,
|
|
tts: 'idle',
|
|
handsFree: false,
|
|
lastTranscript: '',
|
|
interim: '',
|
|
error: null,
|
|
micDevices: [],
|
|
wake: 'off',
|
|
wakeKeywords: [],
|
|
neuralVad: false,
|
|
setNeuralVad: (neuralVad) => set({ neuralVad }),
|
|
setWake: (wake, wakeKeywords) => set(wakeKeywords ? { wake, wakeKeywords } : { wake }),
|
|
setPhase: (phase) => set({ phase }),
|
|
setInputLevel: (inputLevel) => set({ inputLevel }),
|
|
setTts: (tts) => set({ tts }),
|
|
setHandsFree: (handsFree) => set({ handsFree }),
|
|
setTranscript: (lastTranscript) => set({ lastTranscript }),
|
|
setInterim: (interim) => set({ interim }),
|
|
setError: (error) => set({ error }),
|
|
setMicDevices: (micDevices) => set({ micDevices })
|
|
}));
|