diff --git a/README.md b/README.md index 3ef0b99..0ba7729 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # EveFlow 2 — Interface vocale JARVIS pour Hermes Agent [](https://github.com/R0m1k3/EveFlow/actions) -[](https://github.com/R0m1k3/EveFlow/releases) +[](https://github.com/R0m1k3/EveFlow/releases) [](LICENSE) **EveFlow** est un compagnon de bureau Windows qui transforme [Hermes Agent](https://hermes-agent.nousresearch.com/) en assistant vocal à la JARVIS : un noyau holographique réactif au son, une conversation en streaming, les outils, sous-agents, approbations, crons, skills et sessions d'Hermes pilotés depuis un seul HUD. @@ -30,6 +30,8 @@ La version 2 est une réécriture complète : plus de robot 3D, un pipeline voca * **Heures calmes et priorités** : plage horaire pendant laquelle les messages poussés s'affichent sans être lus ni faire clignoter le noyau (badge « non lus » à la place), thème nuit automatique, mots prioritaires lus quand même, résumé vocal des rapports longs (les premières phrases seulement). * **Mode mission** : un bouton dans la barre de commande bascule sur un second modèle Hermes (plus puissant) pour les tâches longues ; le modèle rapide reste utilisé pour la conversation courante. * **Widget compact « glanceable »** : état (veille, écoute, réflexion, parle), dernière phrase de l'assistant, badge de non-lus, indicateurs heures calmes et mission. +* **Voix JARVIS** : préréglage en un clic (Paramètres → Voix) : voix française masculine locale (Piper Tom, téléchargée automatiquement), timbre « JARVIS » (légèrement plus grave et posé, chaleur, présence, courte réverbération d'intercom), débit calme. Kokoro n'a pas de voix française masculine. +* **Reconnaissance française de référence** : Parakeet TDT 0.6B v3 (NVIDIA NeMo, 25 langues européennes) dans le catalogue, plus précis et bien plus rapide que Whisper sur processeur, avec ponctuation. Whisper base/small/turbo restent disponibles. * **Voix masculine ou féminine** : un réglage unique (Paramètres → Voix) appliqué à tous les moteurs. En local, Piper Tom ou Pierre (UPMC) pour le masculin, téléchargé automatiquement si aucune voix masculine n'est installée ; onyx / nova pour les API compatibles OpenAI ; Paul / Hortense pour les voix Windows. * **Barge-in** : en mains libres, parler par-dessus l'assistant coupe sa voix ; le seuil est relevé pendant qu'il parle pour ignorer l'écho du haut-parleur. * **Écoute permanente** : un détecteur de mot-clé de 3 Mo (sherpa-onnx, keyword spotting) tourne en continu sur le micro, quasi gratuit en CPU. « Jarvis » (ou n'importe quel mot-clé) ouvre l'écoute, « Jarvis, allume… » envoie directement la commande, et le mot coupe la voix en cours. Alternative : filtre du mot après transcription en mains libres. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index e9c1978..c51f7c0 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -77,9 +77,12 @@ Sources : [jarvis-desktop-ai](https://github.com/ccarloshenri/jarvis-desktop-ai) | Silero VAD sur phrase Kokoro de 2,3 s (2.3.0) | un seul segment, début et fin détectés, 6,7 s d'audio traités en 190 ms | | Capture d'écran → Hermes (2.3.0) | JPEG de 107 ko reçu côté Hermes (mock chat completions) | | Intention locale « coupe le son » (2.3.0) | traitée sans Hermes, résultat affiché dans le fil | +| Voix JARVIS et Parakeet (2.4.1) | timbre JARVIS (Web Audio : pitch, EQ, compression, réverbération courte), préréglage voix masculine française, Parakeet TDT v3 pour le français, worklets audio livrés en fichiers statiques (l'écoute permanente échouait sous la CSP stricte en version installée), rembourrage de silence avant la reconnaissance | | Correctif 2.4.0.3 | un portail qui laisse passer /health mais renvoie une page web sur /v1 est traité comme une liaison en échec et déclenche la recherche de l'API (sondes /v1/capabilities, /v1/models) | | Correctif 2.4.0.2 | une page web (portail de connexion) reçue à la place de l'API Hermes est reconnue et expliquée ; l'API est recherchée automatiquement sur le même hôte (port 8642, /api, hermes.…) et l'URL corrigée | | Correctifs 2.4.0.1 | réponse vide en chat completions désormais expliquée (JSON non streamé ou erreur HTTP 200), la liaison ne passe plus en « dégradé » quand seule l'API des crons échoue, transcriptions parasites (« (cliquant) », « *Claire* ») ignorées, préférence de voix masculine/féminine | +| Parakeet v3 vs Whisper base sur trois phrases Piper (fr) (2.4.1) | Parakeet : 3/3 exactes avec ponctuation, 0,4 à 0,5 s à chaud (6,7 s au premier appel) ; Whisper base : erreurs sur « Jarvis », « Peux-tu », 0,7 à 0,9 s | +| Worklets audio sous CSP stricte (2.4.1) | chargement des modules statiques OK dans l'application empaquetée | | Serveur MCP (2.4.0) | initialize, tools/list (14 outils), tools/call côté principal (presse-papiers, capture image) et côté renderer (état, message dans le fil) | Sur un PC à 28 cœurs les temps sont nettement plus courts. Whisper small est maintenant recommandé pour le français. diff --git a/electron/voice/catalog.ts b/electron/voice/catalog.ts index 7fa51b7..39efb7a 100644 --- a/electron/voice/catalog.ts +++ b/electron/voice/catalog.ts @@ -44,12 +44,25 @@ export const VOICE_CATALOG: VoiceModelSpec[] = [ files: ['small-encoder.int8.onnx', 'small-decoder.int8.onnx', 'small-tokens.txt'], recommended: true }, + { + id: 'parakeet-v3', + kind: 'stt', + engine: 'nemo-transducer', + name: 'Parakeet TDT 0.6B v3 (25 langues européennes, français)', + description: 'Le plus précis en français et nettement plus rapide que Whisper sur processeur (NVIDIA NeMo, int8). Ponctuation et majuscules incluses.', + languages: ['fr', 'en', 'de', 'es', 'it', 'multi'], + sizeMb: 640, + url: `${ASR}/sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8.tar.bz2`, + dir: 'sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8', + files: ['encoder.int8.onnx', 'decoder.int8.onnx', 'joiner.int8.onnx', 'tokens.txt'], + recommended: true + }, { id: 'whisper-turbo', kind: 'stt', engine: 'whisper', name: 'Whisper large-v3 turbo (multilingue)', - description: 'La meilleure précision ; demande un processeur puissant.', + description: 'Très bonne précision multilingue ; 2 à 4 s par phrase sur un processeur récent. Parakeet est plus rapide en français.', languages: ['fr', 'en', 'multi'], sizeMb: 564, url: `${ASR}/sherpa-onnx-whisper-turbo.tar.bz2`, diff --git a/electron/voice/worker.ts b/electron/voice/worker.ts index 1acaaab..c14ebf9 100644 --- a/electron/voice/worker.ts +++ b/electron/voice/worker.ts @@ -379,8 +379,13 @@ function handle(req: Request): unknown { // Audio crosses the process boundary as base64: V8 refuses to serialize external buffers. const bytes = typeof req.wav === 'string' ? new Uint8Array(Buffer.from(req.wav, 'base64')) : req.wav; const { samples, sampleRate } = decodeWav(bytes); - const pcm = resampleTo16k(samples, sampleRate); - if (pcm.length < 1600) throw new Error('Audio trop court'); + const raw = resampleTo16k(samples, sampleRate); + if (raw.length < 1600) throw new Error('Audio trop court'); + // 0.4 s of silence on both sides: utterances cut close to the words lose the first/last syllable + // and short clips make Whisper hallucinate. + const pad = 6400; + const pcm = new Float32Array(raw.length + 2 * pad); + pcm.set(raw, pad); const recognizer = getRecognizer(req.model, req.language); const stream = recognizer.createStream(); stream.acceptWaveform({ sampleRate: 16000, samples: pcm }); diff --git a/package.json b/package.json index f12975e..da1d289 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "eveflow", - "version": "2.4.0", - "releaseVersion": "2.4.0.3", + "version": "2.4.1", + "releaseVersion": "2.4.1", "description": "JARVIS-style desktop HUD for Hermes Agent: voice, streaming runs, scheduled jobs, skills and telemetry", "main": "dist-electron/main.js", "private": true, diff --git a/public/worklets/eveflow-capture.js b/public/worklets/eveflow-capture.js new file mode 100644 index 0000000..2c9e95b --- /dev/null +++ b/public/worklets/eveflow-capture.js @@ -0,0 +1,27 @@ +class EveFlowCaptureProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.buffer = new Float32Array(2048); + this.offset = 0; + } + process(inputs) { + const input = inputs[0]; + if (!input || input.length === 0) return true; + const channel = input[0]; + if (!channel) return true; + let i = 0; + while (i < channel.length) { + const n = Math.min(channel.length - i, this.buffer.length - this.offset); + this.buffer.set(channel.subarray(i, i + n), this.offset); + this.offset += n; + i += n; + if (this.offset === this.buffer.length) { + this.port.postMessage(this.buffer, [this.buffer.buffer]); + this.buffer = new Float32Array(2048); + this.offset = 0; + } + } + return true; + } +} +registerProcessor('eveflow-capture', EveFlowCaptureProcessor); diff --git a/public/worklets/eveflow-wake.js b/public/worklets/eveflow-wake.js new file mode 100644 index 0000000..1512af7 --- /dev/null +++ b/public/worklets/eveflow-wake.js @@ -0,0 +1,19 @@ +class EveFlowWakeProcessor extends AudioWorkletProcessor { + constructor() { super(); this.buffer = new Float32Array(2048); this.offset = 0; } + process(inputs) { + const channel = inputs[0] && inputs[0][0]; + if (!channel) return true; + let i = 0; + while (i < channel.length) { + const n = Math.min(channel.length - i, this.buffer.length - this.offset); + this.buffer.set(channel.subarray(i, i + n), this.offset); + this.offset += n; i += n; + if (this.offset === this.buffer.length) { + this.port.postMessage(this.buffer, [this.buffer.buffer]); + this.buffer = new Float32Array(2048); this.offset = 0; + } + } + return true; + } +} +registerProcessor('eveflow-wake', EveFlowWakeProcessor); diff --git a/src/components/settings/SettingsDrawer.tsx b/src/components/settings/SettingsDrawer.tsx index de98da6..84a0f98 100644 --- a/src/components/settings/SettingsDrawer.tsx +++ b/src/components/settings/SettingsDrawer.tsx @@ -374,7 +374,32 @@ export function SettingsDrawer({ onClose }: Props) { - S’applique à tous les moteurs : voix locale (Piper Tom ou Pierre pour le masculin, téléchargée automatiquement si besoin), voix OpenAI (onyx / nova), voix système Windows (Paul / Hortense). + S’applique à tous les moteurs : voix locale (Piper Tom ou Pierre pour le masculin, téléchargée automatiquement si besoin), voix OpenAI (onyx / nova), voix système Windows (Paul / Hortense). Kokoro n’a pas de voix française masculine. + +