mirror of
https://github.com/R0m1k3/EveFlow.git
synced 2026-10-19 05:09:30 +02:00
feat: fin de phrase Silero, vision d'écran et actions locales (v2.3.0) (#13)
É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>
This commit is contained in:
1 parent
4f6074183f
commit
af8bdc5e9d
24 files changed
+691
-47
No files matched your search
+29
-4
@@ -14,6 +14,8 @@ import type {
|
||||
import type {
|
||||
KwsDetection,
|
||||
KwsStartRequest,
|
||||
VadEvent,
|
||||
VadStartRequest,
|
||||
SynthesizeRequest,
|
||||
SynthesizeResult,
|
||||
TranscribeRequest,
|
||||
@@ -46,10 +48,6 @@ export interface EveFlowBridge {
|
||||
streamAbort: (id: string) => void;
|
||||
onStreamEvent: (cb: (event: HttpStreamEvent) => void) => Unsubscribe;
|
||||
};
|
||||
system: {
|
||||
metrics: () => Promise<SystemMetrics>;
|
||||
appInfo: () => Promise<AppInfo>;
|
||||
};
|
||||
files: {
|
||||
readLocal: (filePath: string) => Promise<string>;
|
||||
writeShared: (filename: string, content: string, isBase64?: boolean) => Promise<{ path: string; url: string }>;
|
||||
@@ -79,5 +77,32 @@ export interface EveFlowBridge {
|
||||
/** Fire-and-forget 16-bit PCM frames for the keyword spotter. */
|
||||
kwsAudio: (pcm: Uint8Array, sampleRate: number) => void;
|
||||
onKwsDetected: (cb: (detection: KwsDetection) => void) => Unsubscribe;
|
||||
vadStart: (req: VadStartRequest) => Promise<void>;
|
||||
vadStop: () => Promise<void>;
|
||||
vadAudio: (pcm: Uint8Array, sampleRate: number) => void;
|
||||
onVadEvent: (cb: (event: VadEvent) => void) => Unsubscribe;
|
||||
};
|
||||
system: {
|
||||
metrics: () => Promise<SystemMetrics>;
|
||||
appInfo: () => Promise<AppInfo>;
|
||||
/** Screenshot of the primary display as a JPEG data URL. */
|
||||
captureScreen: (maxWidth?: number) => Promise<string>;
|
||||
/** Allow-listed local actions (lock session, open app/url, media keys, clipboard). */
|
||||
action: (action: SystemAction) => Promise<SystemActionResult>;
|
||||
};
|
||||
}
|
||||
|
||||
export type SystemAction =
|
||||
| { type: 'lock' }
|
||||
| { type: 'open-app'; name: string }
|
||||
| { type: 'open-url'; url: string }
|
||||
| { type: 'media'; key: 'volume-up' | 'volume-down' | 'mute' | 'play-pause' | 'next' | 'previous' }
|
||||
| { type: 'clipboard-read' }
|
||||
| { type: 'clipboard-write'; text: string }
|
||||
| { type: 'find-files'; query: string };
|
||||
|
||||
export interface SystemActionResult {
|
||||
ok: boolean;
|
||||
message?: string;
|
||||
data?: unknown;
|
||||
}
|
||||
@@ -111,6 +111,8 @@ export const IPC = {
|
||||
httpStreamEvent: 'http:stream:event',
|
||||
metrics: 'system:metrics',
|
||||
appInfo: 'app:info',
|
||||
screenCapture: 'system:screen-capture',
|
||||
systemAction: 'system:action',
|
||||
readLocalFile: 'files:read-local',
|
||||
writeSharedFile: 'files:write-shared',
|
||||
openPath: 'files:open-path',
|
||||
|
||||
+21
-3
@@ -1,7 +1,7 @@
|
||||
/** Local voice engine contract (sherpa-onnx in a utility process). Shared by main and renderer. */
|
||||
|
||||
export type VoiceModelKind = 'stt' | 'tts' | 'kws';
|
||||
export type VoiceEngineKind = 'whisper' | 'sense-voice' | 'nemo-transducer' | 'kokoro' | 'piper' | 'kws-transducer';
|
||||
export type VoiceModelKind = 'stt' | 'tts' | 'kws' | 'vad';
|
||||
export type VoiceEngineKind = 'whisper' | 'sense-voice' | 'nemo-transducer' | 'kokoro' | 'piper' | 'kws-transducer' | 'silero';
|
||||
|
||||
export interface VoiceSpeaker {
|
||||
id: number;
|
||||
@@ -82,6 +82,20 @@ export interface KwsDetection {
|
||||
at: number;
|
||||
}
|
||||
|
||||
export interface VadStartRequest {
|
||||
modelId: string;
|
||||
/** Silence that ends an utterance, in ms. */
|
||||
silenceMs: number;
|
||||
/** Detection threshold 0..1 (0.5 default). */
|
||||
threshold: number;
|
||||
maxUtteranceSec: number;
|
||||
}
|
||||
|
||||
export type VadEvent =
|
||||
| { type: 'speech-start' }
|
||||
| { type: 'segment'; wav: Uint8Array; durationSec: number }
|
||||
| { type: 'error'; message: string };
|
||||
|
||||
export interface VoiceEngineStatus {
|
||||
available: boolean;
|
||||
error?: string;
|
||||
@@ -103,5 +117,9 @@ export const VOICE_IPC = {
|
||||
kwsStart: 'voice:kws:start',
|
||||
kwsStop: 'voice:kws:stop',
|
||||
kwsAudio: 'voice:kws:audio',
|
||||
kwsDetected: 'voice:kws:detected'
|
||||
kwsDetected: 'voice:kws:detected',
|
||||
vadStart: 'voice:vad:start',
|
||||
vadStop: 'voice:vad:stop',
|
||||
vadAudio: 'voice:vad:audio',
|
||||
vadEvent: 'voice:vad:event'
|
||||
} as const;
|
||||
Reference in new issue
Block a user