Leokani server
One Docker image with two servers: Wyoming for Home Assistant, and an OpenAI-compatible speech API. For 64-bit Linux on x86_64 or ARM.
Install
Intel or AMD:
curl -O https://milliwattlabs.com/models/leokani-tts/downloads/leokani-server-1.1-amd64.tar.gz
echo "d1697a3d7fb7bb185503e0a0fc65b96f979466b5ffd8eee8a4d7c2e25d8c10c2 leokani-server-1.1-amd64.tar.gz" | sha256sum -c
docker load -i leokani-server-1.1-amd64.tar.gz64-bit ARM:
curl -O https://milliwattlabs.com/models/leokani-tts/downloads/leokani-server-1.1-arm64.tar.gz
echo "8df107b624f44d35c604e100268e7ab98929d72aaf5aabc3ce6278245496af1f leokani-server-1.1-arm64.tar.gz" | sha256sum -c
docker load -i leokani-server-1.1-arm64.tar.gzOn a Mac, check with shasum -a 256 -c instead of sha256sum -c.
Run
Start one server or both:
docker run -d --name leokani-wyoming --restart unless-stopped -p 10200:10200 leokani-server:1.1 wyoming
docker run -d --name leokani-openai --restart unless-stopped -p 8880:8880 leokani-server:1.1 openaiOr with Docker Compose, save this as docker-compose.yml and run docker compose up -d:
services:
leokani-wyoming:
image: leokani-server:1.1
command: ["wyoming", "--zeroconf", "leokani"]
network_mode: host
restart: unless-stopped
leokani-openai:
image: leokani-server:1.1
command: ["openai"]
ports: ["8880:8880"]
restart: unless-stoppedHome Assistant
In Home Assistant, go to Settings > Devices & services > Add integration > Wyoming Protocol. Enter the server's IP address and port 10200.
Then go to Settings > Voice assistants, open your assistant, and choose leokani as the text-to-speech voice.
With host networking and wyoming --zeroconf, as in the Compose file, Home Assistant finds the server by itself: click Configure on the discovered device instead. On a machine with several network interfaces, add --zeroconf-host IP to choose the address it announces.
To try it, in Developer tools > Actions:
action: tts.speak
target:
entity_id: tts.leokani
data:
media_player_entity_id: media_player.kitchen
message: Dinner is ready.| Option | |
|---|---|
--port N | TCP port. Default 10200. |
--speed N | 0.5 to 2.0. Default 1.0. |
--zeroconf [NAME] | Announce on the network. Needs host networking. |
--zeroconf-host IP | The address to announce. |
docker run --rm leokani-server:1.1 wyoming --helpOpenAI speech API
Point any OpenAI speech client at http://HOST:8880/v1.
curl http://localhost:8880/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model": "leokani-tts", "voice": "leokani", "input": "Hello there!", "response_format": "mp3"}' \
-o hello.mp3With the openai Python package:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8880/v1", api_key="unused")
client.audio.speech.create(model="leokani-tts", voice="leokani", input="Hello there!").write_to_file("hello.mp3")POST /v1/audio/speech takes:
| Field | |
|---|---|
model | leokani-tts. tts-1, tts-1-hd and gpt-4o-mini-tts also work. |
input | The text, up to 4,096 characters. |
voice | leokani. OpenAI's voice names also work and give the same voice. |
response_format | mp3 (default), opus, aac, flac, wav or pcm. |
speed | 0.25 to 4.0, default 1.0. The voice speaks from 0.5 to 2.0. |
stream_format | audio (default) or sse. |
Every format streams. pcm is raw 16-bit little-endian mono at 24 kHz.
GET /v1/models lists leokani-tts. GET /health returns {"status": "ok"}.
To require a key, start it with -e LEOKANI_API_KEY=KEY and send Authorization: Bearer KEY.
Licenses
The image includes espeak-ng (GPL-3.0), built from this source: license, also at /usr/share/doc/espeak-ng/espeak-ng-COPYING in the image.
Leokani-tts is CC BY-NC 4.0. For a commercial license or a custom voice, email hello@milliwattlabs.com.