Skip to content

Setting Up Her Voice

File: configs/tts.yaml
Command: kenzy-tts [config_path]

This service is Kenzy's voice — text in, speech out. It can speak from a cloud provider or entirely from your own hardware.

Cloud or local?

Cloud (OpenAI) Local (Kokoro)
What it needs An API key. No downloads. The kokoro extra plus one system package. Happy on a CPU.
What leaves your network Only what Kenzy says — never what you say. Nothing.
What it costs Per-character billing from the provider. Some CPU while she's speaking.

The default is cloud — zero downloads and it sounds good on day one.

Local speech unlocks spoken secrets

Lockbox secrets are only ever spoken aloud when speech stays on-box. With a cloud voice, asking for a secret gets a polite deflection to the dashboard instead. This is mechanical, not a setting — it fails closed.

Deciding this across all the services at once? Start at Running Fully Local.

Set it up

Switching is also a dropdown in the dashboard (Fleet → tts).

provider: "openai"

openai:
  model: "gpt-4o-mini-tts"
  voice: "nova"
  speed: 1.1

Requires: OPENAI_API_KEY in ~/.config/kenzy/.env, set from the dashboard under Settings → API keys.

Voices, speed range and the automatic Kokoro failover are in OpenAI provider below.

sudo apt-get install espeak-ng          # phonemization library
pip install 'kenzy[kokoro]'             # the extra: kokoro + PyTorch
kenzy-setup                             # pre-download the model weights
provider: "kokoro"

kokoro:
  voice: "af_heart"
  device: "auto"
  speed: 1.0

Requires: no API key and no internet at runtime. In a source checkout the extra is pip install -e ".[kokoro]".

Voice names, languages and device selection are in Kokoro provider below; the end-to-end walkthrough is Running Fully Local.

Pulled from the server

kenzy-tts pulls this config from the server at boot — it discovers the server via mDNS (or KENZY_SERVER_URL) and blocks until it answers, so start the server first. Edit it from the dashboard's Services tab (writes configs/services/tts.yaml on the server and restarts the service). Passing an explicit path loads locally instead (dev/offline). log_level (console) and log_capture_level (dashboard viewer depth, default debug) work like every service. See central config for backend services.

Advanced

Everything below is the full reference.

Service

Key Default Description
host "127.0.0.1" Bind address
port 8769 HTTP port

Provider selection

Key Default Description
provider "openai" TTS backend: openai or kokoro

OpenAI provider

Long responses are automatically split at sentence boundaries and concatenated, so there is no effective limit on response length.

Key Default Description
openai.model "gpt-4o-mini-tts-2025-03-20" (shipped config) OpenAI TTS model
openai.voice "sage" Voice persona (see below)
openai.speed 1.0 Playback speed multiplier, 0.25–4.0
openai.fallback true On a cloud failure, silently retry with local Kokoro — only takes effect when the kokoro extra is installed (see below); otherwise the failure surfaces as the error cue.

Available voices

alloy · ash · ballad · coral · echo · fable · nova · onyx · sage · shimmer


Kokoro provider

Kokoro runs entirely locally with no API key or internet connection required at runtime. It produces high-quality speech and outputs at 24 kHz mono — the same format as the OpenAI provider, so audio handling downstream is unchanged. One behavior does depend on the provider: lockbox secrets are only ever spoken when speech stays on-box — with a cloud provider, asking for a secret gets a polite deflection to the dashboard instead. Local speech is what unlocks spoken secret read-backs.

Note

The voice_prompt style instruction generated by the LLM (e.g. "speak warmly at a conversational pace") is an OpenAI-specific feature. It is silently ignored when using Kokoro.

Key Default Description
kokoro.voice "af_heart" Kokoro voice name (see below)
kokoro.device "auto" Inference device (see below)
kokoro.speed 1.0 Playback speed multiplier, 0.5–2.0
kokoro.lang_code (from voice) Language code. Derived automatically from the first character of the voice name if omitted.

Device options

Value Description
auto Detects the best available device at startup: CUDA → MPS → CPU (recommended)
cpu Always use CPU
cuda NVIDIA GPU. Also covers AMD GPUs when using a ROCm-enabled PyTorch build.
mps Apple Silicon GPU (M1/M2/M3/M4)

Voice names and languages

The voice name prefix determines the language:

Prefix Language Example voices
af_ American English (female) af_heart, af_bella, af_sky
am_ American English (male) am_adam, am_michael
bf_ British English (female) bf_emma, bf_isabella
bm_ British English (male) bm_lewis, bm_george

The lang_code is derived from the first character of the voice name (af_heart'a', bf_emma'b'). Set it explicitly only if you need to override this.

Wyoming listener (Home Assistant voice pipelines)

Expose this service as a native HA text-to-speech provider, so Assist replies on your phone are spoken in Kenzy's actual voice — see On Your Phone for the full setup.

Key Default Description
wyoming.enabled false Start the Wyoming protocol listener alongside the HTTP service. Uses the exact same synthesis path (provider, voice, fallback chain) as /speak. Requires the wyoming package (included in the tts extra).
wyoming.port 10200 Listener port (the Piper convention, so HA operators guess right).

Wyoming is plain, unauthenticated TCP — the listener follows the service bind, so it stays loopback-only unless you've deliberately opened the service to the LAN (KENZY_BIND=0.0.0.0 / --listen-all).