Jarvis Voice — Canal vocal Voxtral (STT + TTS)
Architecture
Navigateur Plugin jarvis-voice (OpenClaw)
┌─────────┐ WS ┌────────────────────────────────────────┐
│ ● micro │──────►│ Voxtral STT ──► OpenClaw agent │
│ ■ stop │ │ (realtime) (Mistral Large) │
│ ✕ coupe │◄──────│ Voxtral TTS ◄── + tools (gog, mail) │
│ 🔊 audio│ WS │ (streaming) │
│ ⌨️ texte │──────►│ │
└─────────┘ └────────────────────────────────────────┘
Flux de données
Micro (16kHz PCM) → WebSocket → Voxtral STT realtime → texte
│
▼
OpenClaw agent (Mistral Large) ──► texte streamé
│
▼
Voxtral TTS streaming (24kHz PCM) → WebSocket → Navigateur
Configuration
openclaw.json — Section tts
{
"tts": {
"provider": "mistral-voxtral",
"mode": "final",
"providers": {
"mistral-voxtral": {
"apiKey": "mstrl_...",
"voice": "gb_oliver_neutral",
"model": "voxtral-mini-tts-2603",
"language": "fr",
"responseFormat": "pcm"
}
}
}
}openclaw.json — Section channels.jarvis
{
"channels": {
"jarvis": {
"enabled": true,
"apiKey": "mstrl_...",
"voiceId": "gb_oliver_neutral",
"ttsModel": "voxtral-mini-tts-2603",
"sttModel": "voxtral-mini-transcribe-realtime-2602",
"language": "fr",
"dmPolicy": "open",
"allowFrom": ["*"]
}
}
}openclaw.json — Section plugins
{
"plugins": {
"entries": {
"jarvis-voice": { "enabled": true }
},
"load": {
"paths": ["/home/ubuntu/voice-assistant/jarvis-voice"]
}
}
}Fichiers du plugin
voice-assistant/jarvis-voice/
├── index.js — Entrée plugin, auth, routes HTTP
├── openclaw.plugin.json — Manifest plugin
├── package.json — Dépendances (ws)
└── src/
├── accounts.js — Config canal, DM policy
├── channel.js — Plugin channel OpenClaw
├── config.js — Schéma config
├── dispatch.js — Dispatch tour vers agent
├── speech-provider.js— Provider TTS Voxtral (pour autres canaux)
├── ui.js — Interface web (HTML/CSS/JS)
├── voice-server.js — Serveur WebSocket, STT/TTS streaming
├── voxtral-stt.js — Session STT Voxtral realtime
└── voxtral-tts.js — Streaming TTS Voxtral
Points clés d’implémentation
STT — Voxtral realtime (voxtral-stt.js)
- URL :
wss://api.mistral.ai/v1/audio/transcriptions/realtime - Modèle :
voxtral-mini-transcribe-realtime-2602 - Audio : PCM s16le 16kHz mono, base64
- Paramètres :
model,target_streaming_delay_ms=400 - ⚠️ Pas de paramètre
language— le modèle ne supporte pas le “language forcing” - Événements :
session.created,transcription.text.delta(partials),transcription.segment,transcription.done
TTS — Voxtral streaming (voxtral-tts.js)
- URL :
POST https://api.mistral.ai/v1/audio/speech - Modèle :
voxtral-mini-tts-2603 - Voix :
gb_oliver_neutral(britannique neutre) - Format :
pcm(streaming SSE) oump3(buffered) - Sample rate : 24kHz s16le mono
- Streaming : SSE avec événements
speech.audio.delta(base64) etspeech.audio.done
Nettoyage TTS (voice-server.js)
La fonction cleanForTts() retire avant synthèse :
- Emojis (🤖, 😀, etc.) — Voxtral rejette le texte vide après sanitization
- Markdown (
*,#, backticks) - Espaces multiples
function cleanForTts(text) {
return text
.replace(/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2190}-\u{21FF}\u{FE0F}\u{200D}]/gu, "")
.replace(/\*+/g, "")
.replace(/#{1,6}\s*/g, "")
.replace(/`[^`]*`/g, "")
.replace(/\s+/g, " ")
.trim();
}Interface web
- URL :
http://<host>:18789/jarvis?token=<gateway-token> - Auth : Bearer token ou
?token=query param - Features :
- Bouton micro (push-to-talk)
- Visualisation onde animée (record/speak/idle)
- Transcription partielle en temps réel
- Streaming réponse texte + audio
- Champ texte (fallback)
- Bouton stop (interruption / barge-in)
- Bouton mute
- Reconnexion automatique
Tests effectués (2026-10-13)
| Test | Statut | Détails |
|---|---|---|
| Connexion WebSocket | ✅ | Session créée, voix gb_oliver_neutral |
| Texte → Agent → TTS | ✅ | 8 chunks audio, réponse correcte |
| Audio → STT Voxtral | ✅ | Transcription partielle streamée, finale correcte |
| STT → Agent → TTS | ✅ | 32 chunks audio, réponse adaptée au contenu audio |
| Barge-in (interruption) | ✅ | Nouveau message interrompt le tour en cours |
| Nettoyage emojis TTS | ✅ | Plus d’erreur Voxtral sur 🤖 |
Commandes utiles
# Redémarrer le gateway
systemctl --user restart openclaw-gateway
# Voir les logs
journalctl --user -u openclaw-gateway -f
# Statut
openclaw status
# Tester le canal (script Node.js)
node /tmp/test-jarvis-e2e.jsQuota et coûts
- Mistral API : Voxtral STT + TTS consomment des crédits
- Surveiller sur https://console.mistral.ai
- Voix
gb_oliver_neutral: voix britannique neutre
Ancien système (à désactiver à terme)
voice-assistant/app.py— Port 7860 — Whisper local + ElevenLabs- Service :
jarvis-voice.service(systemd user) - À remplacer entièrement par le plugin jarvis-voice
Prochaines étapes possibles
- Voix française : Tester les voix
fr_*vialistVoxtralVoices() - Ancien système : Arrêter
jarvis-voice.service(port 7860) - Talk mode : Activer le mode talk (TTS auto sur Telegram)
- Personas : Configurer des personas TTS (majordome, etc.)