Local voice: Kokoro TTS + Whisper large v3 STT
Task
Run speech-to-text and text-to-speech against a self-hosted server on your own machine — no cloud provider, no API key.
Result
Ethos transcribes voice input with Whisper large v3 and speaks replies with Kokoro, using two local OpenAI-compatible endpoints wired through auxiliary.asr and auxiliary.tts.
Prereqs
ethosonPATH(Node 24+). Runethos --versionto confirm.- A machine that can run the voice servers (a GPU helps Whisper large v3; Kokoro runs on CPU).
- The endpoints are OpenAI-compatible, so any server that speaks
POST /v1/audio/transcriptions(STT) andPOST /v1/audio/speech(TTS) works — not just the two below.
Run the servers
Two local servers, each exposing the OpenAI audio routes.
- TTS — kokoro-fastapi exposes
POST /v1/audio/speech. Default port 8880. - STT — an OpenAI-compatible Whisper server such as Speaches (formerly faster-whisper-server) exposes
POST /v1/audio/transcriptions. Default port 8000.
Follow each project's own install guide to start the server; the ports above are the defaults Ethos assumes and both are overridable. Confirm both are up before wiring Ethos:
curl -s http://localhost:8880/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{"model":"kokoro","voice":"af_bella","input":"hello"}' --output /tmp/hello.wav
curl -sI http://localhost:8000/v1/models
HTTP/1.1 200 OK
Configure — two ways
Pick one. The web form writes the same auxiliary.* keys the YAML block below sets.
Web Settings → Voice
- Open the web dashboard and go to Settings → Voice.
- Set STT Provider to
local-stt. STT Base URL and STT Model prefill tohttp://localhost:8000/v1andwhisper-large-v3— change them if your server differs. Leave STT API key blank. - Set TTS Provider to
local-tts. TTS Base URL and TTS Model prefill tohttp://localhost:8880/v1andkokoro. Leave TTS API key blank. - Type a Voice ID into the free-form field — for Kokoro, e.g.
af_bella. - Save.
config.yaml
~/.ethos/config.yaml uses flat dotted keys. Add the two blocks — no apiKey line, because a local server needs none:
auxiliary.asr.provider: local-stt
auxiliary.asr.baseUrl: http://localhost:8000/v1
auxiliary.asr.model: whisper-large-v3
auxiliary.tts.provider: local-tts
auxiliary.tts.baseUrl: http://localhost:8880/v1
auxiliary.tts.model: kokoro
auxiliary.tts.voice: af_bella
Every field except provider is optional and falls back to the default shown. Restart ethos (or the gateway) after editing the file.
Voice ids are server-specific
The Voice ID field is free-form on purpose — every server and model names its voices differently. Kokoro ships af_bella, am_adam, and others; a different TTS server will use its own ids. Read your server's voice list (kokoro-fastapi serves it at GET /v1/audio/voices) and paste the id you want. Ethos does not validate it against a fixed list.
The model field is free-form too
STT model names vary by server: some accept whisper-large-v3, others want the fully qualified Systran/faster-whisper-large-v3. Use whatever id your server expects — Ethos passes it through unchanged. The same applies to the TTS model field.
Verify
- TTS — in the web chat, click the Play button on an assistant message. It should speak the reply in the configured voice. If the button reports "TTS not configured," the
auxiliary.ttsblock did not load — recheck the provider value and restart. - STT — hold the microphone button in the composer, speak, and release. The transcript should appear in the input box.
- Both routes hit your local servers only; no request leaves the machine.
Troubleshoot
Voice not configured — add auxiliary.asr to ~/.ethos/config.yaml— the STT block is missing or the provider value is wrong. Confirmauxiliary.asr.provider: local-sttand restart.- Connection refused / no audio — the server is down or on a different port. Re-run the
curlchecks above; fix the port in the matchingbaseUrl. model not foundfrom the server — the server wants a different model id. Try the fully qualified name (e.g.Systran/faster-whisper-large-v3) in themodelfield.- Unknown or silent voice — the Voice ID is not one your TTS server ships. Fetch the server's voice list and use an id from it.
See also
- Qualify a local model — score a local text model before trusting it with work.
- Configure providers — wire the main LLM provider, including local OpenAI-compatible endpoints.