* feat(voice): add a Voice settings page with provider choice and a local model catalog Each host gets a Voice page under host settings. Dictation and voice mode each pick a provider (Local or OpenAI) and a model. Provider settings sit in one group of collapsible rows: local speech-to-text and text-to-speech list every catalog model with its size, languages and description before download, and OpenAI takes a write-only API key and an optional base URL. The local catalog grows from Parakeet/Kokoro to Whisper, SenseVoice, Dolphin, GigaAM, Parakeet Japanese, multilingual Kokoro, Kitten and Piper voices. Each entry declares its sherpa engine family, so the recognizer and TTS build the matching model config and the downloader verifies the files that family needs. Kitten v0.8 is left out: it crashes the pinned sherpa-onnx 1.12.28. Voice mode is now opt-in. The default lived only in code (true), never in config.json, so hosts that never toggled it find it off after upgrading. OpenAI speech-to-text resolved one model for both features, with voice mode's choice taking precedence, so dictation silently used voice mode's model. The resolved config now carries a model per feature; STT_MODEL still overrides both. Rows revealed under a collapsible settings row are a nested density level set by the container: tighter padding, smaller text, and xs controls, with controls taking their size from the row instead of each caller. * test(app): pre-bundle the JSX dev runtime for browser tests Compiling test JSX with the automatic runtime imports react/jsx-dev-runtime. Vite discovered it mid-run on a cold cache and reloaded the page, which failed the first browser test file to import it. * refactor(voice): keep speech provider settings at each use site Store credentials and endpoint options beside the speech feature that consumes them so saved page values cannot be shadowed by a separate provider block. Normalize existing config on load and mirror the inline shape on config reads and patches. This is the config, resolver and wire phase; the provider sheet follows separately. * fix(voice): preserve saved keys when resaving provider settings Forms send the selected provider on each save but cannot read back its API key. Merge patches for the existing provider so these saves retain credentials; replace the site only when the provider changes. * feat(voice): configure each speech use site from a provider sheet The Voice page now lists Speech-to-text, Listening and Speaking as one-line summaries. Tapping one opens a sheet with the provider choice and only that provider's settings: the local catalog (size, languages and description before download, download state per model) or the OpenAI key, base URL, model and language or voice. The sheet's form model sends only the fields the user changed, so re-saving never clears a saved key. Fields an env var decides are read-only with a "Set by" hint; the API key and base URL stay editable because a saved value wins over the env fallback. Save failures show a toast and keep the sheet open with the input. E2E daemons pin speech providers through env vars to avoid model downloads, so the browser spec covers the locked-provider path, the key fallback and the failed save; provider switching is covered by the form model tests. * refactor(voice): press settings and catalog rows through PressHighlight Settings rows that lead somewhere and local model rows now use the standard row press primitive, so native shows the finger-down flash the rest of the app's rows have. Web hover and press styling are unchanged. * fix(voice): preserve STT language for local providers Language belongs to every STT use site. Preserve legacy local language settings through config loading and saving, resolve them without provider gating, and expose them in the provider form.
6.5 KiB
title, description, nav, order, category
| title | description | nav | order | category |
|---|---|---|---|---|
| Voice | Paseo voice architecture, local-first model execution, and provider configuration. | Voice | 41 | Configuration |
Voice
Paseo has first-class voice support for dictation and voice mode conversations with your coding environment.
Philosophy
Voice is local-first. You can run speech fully on-device, or choose OpenAI for speech features. For voice reasoning/orchestration, Paseo reuses agent providers already installed and authenticated on your machine.
This keeps credentials and execution in your environment and avoids introducing a separate cloud-only voice stack.
Architecture
- Speech I/O: STT and TTS providers per feature (
localoropenai) - Local speech runtime: ONNX models executed on CPU by default
- Voice LLM orchestration: hidden agent session using your configured provider (
claude,codex, oropencode) - Tooling path: MCP stdio bridge for voice tools and agent control
Local Speech
Local speech defaults to model IDs parakeet-tdt-0.6b-v2-int8 (STT) and kokoro-en-v0_19 (TTS, speaker 0 / voice 00).
Dictation is on by default. Voice mode is off until you turn it on in Settings > Voice or set features.voiceMode.enabled: true.
Missing models for enabled features are downloaded at daemon startup into $PASEO_HOME/models/local-speech. Downloads happen only for missing files. Settings > Voice lists every local model with its size and languages, and lets you download or delete each one.
Local STT models and language support
Settings > Voice lists every local STT model with the languages it supports. The default, parakeet-tdt-0.6b-v2-int8, is English-only. For other languages, pick a multilingual model such as parakeet-tdt-0.6b-v3-int8 (25 European languages) or whisper-base (99 languages). The language field is passed to STT sessions for both providers. Local recognizers currently detect the spoken language themselves; setting language does not expand a model’s supported languages.
{
"version": 1,
"features": {
"dictation": {
"stt": { "provider": "local", "model": "parakeet-tdt-0.6b-v2-int8" }
},
"voiceMode": {
"llm": { "provider": "claude", "model": "haiku" },
"stt": { "provider": "local", "model": "parakeet-tdt-0.6b-v2-int8" },
"tts": { "provider": "local", "model": "kokoro-en-v0_19", "speakerId": 0 }
}
},
"providers": {
"local": {
"modelsDir": "~/.paseo/models/local-speech"
}
}
}
For multilingual local dictation, set a multilingual model. No language field is needed:
{
"version": 1,
"features": {
"dictation": {
"stt": { "provider": "local", "model": "parakeet-tdt-0.6b-v3-int8" }
}
}
}
For either STT provider, set features.dictation.stt.language for dictation and features.voiceMode.stt.language for voice mode. If voice language is omitted, Paseo uses the dictation language before falling back to en.
OpenAI Voice Option
Set provider to openai and enter credentials at each use site that needs them.
Dictation, listening and speaking each own their key, base URL and model:
{
"version": 1,
"features": {
"dictation": {
"stt": { "provider": "openai", "apiKey": "sk-...", "model": "gpt-4o-mini-transcribe" }
},
"voiceMode": {
"enabled": true,
"stt": {
"provider": "openai",
"apiKey": "sk-...",
"baseUrl": "https://api.openai.com/v1",
"model": "gpt-4o-transcribe",
"language": "en"
},
"tts": { "provider": "openai", "apiKey": "sk-...", "model": "tts-1", "voice": "alloy" }
}
}
}
| Provider | Speech-to-text fields | Text-to-speech fields |
|---|---|---|
local |
model, language |
model, speakerId, speed |
openai |
apiKey, baseUrl, model, language, confidenceThreshold |
apiKey, baseUrl, model, voice |
Fields other than provider are optional. Omit model to use the provider's default.
OpenAI model and voice names accept custom IDs from compatible servers. Keys saved
on the host are write-only in the app.
A saved apiKey or baseUrl wins over env settings. Without a saved value, STT
uses OPENAI_STT_API_KEY / OPENAI_STT_BASE_URL, TTS uses
OPENAI_TTS_API_KEY / OPENAI_TTS_BASE_URL, and both fall back to
OPENAI_API_KEY / OPENAI_BASE_URL. These settings affect Paseo speech traffic.
Existing providers.openai settings load automatically into OpenAI use sites that
lack inline values. Endpoint settings take precedence over the old shared settings.
The next save writes the inline shape. A legacy key without an OpenAI site to
receive it is dropped with a warning; enter a key when you later select OpenAI.
Paseo uses these paths under the configured OpenAI base URL:
- dictation STT:
/v1/audio/transcriptions - voice mode STT:
/v1/audio/transcriptions - voice mode TTS:
/v1/audio/speech
Environment Variables
Provider, enablement, model, language, voice and tuning env vars override config. Keys and base URLs follow the fallback rule above.
-
PASEO_DICTATION_ENABLED,PASEO_VOICE_MODE_ENABLED, feature enablement -
STT_MODEL, model override for both OpenAI STT sites -
STT_CONFIDENCE_THRESHOLD, confidence threshold for both OpenAI STT sites -
TTS_MODEL,TTS_VOICE, OpenAI speaking model and voice -
PASEO_VOICE_LLM_PROVIDER, voice agent provider override -
PASEO_DICTATION_STT_PROVIDER,PASEO_VOICE_STT_PROVIDER,PASEO_VOICE_TTS_PROVIDER, speech provider selection (localoropenai) -
OPENAI_STT_API_KEY,OPENAI_STT_BASE_URL, OpenAI speech-to-text endpoint (dictation + voice mode STT) -
OPENAI_TTS_API_KEY,OPENAI_TTS_BASE_URL, OpenAI text-to-speech endpoint (voice mode TTS) -
PASEO_LOCAL_MODELS_DIR, local model storage directory -
PASEO_DICTATION_LOCAL_STT_MODEL, local dictation STT model ID -
PASEO_VOICE_LOCAL_STT_MODEL,PASEO_VOICE_LOCAL_TTS_MODEL, local voice STT/TTS model IDs -
PASEO_DICTATION_LANGUAGE, dictation STT language -
PASEO_VOICE_LANGUAGE, voice mode STT language; falls back toPASEO_DICTATION_LANGUAGEwhen unset -
PASEO_VOICE_LOCAL_TTS_SPEAKER_ID,PASEO_VOICE_LOCAL_TTS_SPEED, optional local voice TTS tuning
Operational Notes
Voice mode can launch and control agents. Treat voice prompts with the same care as direct agent instructions, especially when specifying working directories or destructive operations.