wyoming-mlx
Apple-Silicon-native TTS (Kokoro) and streaming STT (Whisper via WhisperLiveKit) for Home Assistant and OpenAI-compatible clients.
Why?
If you have a Mac on your network, it can be your voice server. wyoming-mlx turns it into a fast, fully local speech-to-text and text-to-speech service:
- Private by construction. Audio never leaves your network — no cloud speech APIs, no per-request pricing, nothing to subscribe to. Models run entirely on your machine.
- Uses hardware you already own. Apple Silicon's GPU and unified memory run Whisper and Kokoro comfortably alongside whatever else the Mac is doing — no dedicated GPU server, no idle power draw of a CUDA box.
- One service, two ecosystems. Home Assistant talks to it natively over
the Wyoming protocol (drop-in
replacement for
wyoming-faster-whisper/wyoming-pipersatellites), while anything that speaks the OpenAI audio API — scripts, editors, chat UIs — can use the same instance via/v1/audio/transcriptionsand/v1/audio/speech. - Set-and-forget. Install with Homebrew, run as a launchd service via
brew services, and models are fetched once into the Hugging Face cache.
Speech-to-text streams through WhisperLiveKit's SimulStreaming (AlignAtt) policy on MLX, so partial transcripts arrive while you're still speaking; text-to-speech runs Kokoro on Metal via PyTorch. The real backends therefore require an Apple Silicon Mac. Everything else (config, HTTP API, Wyoming protocol handling, fake backends, tests) is portable, and CI runs on Linux against the fake backends.
Install (Homebrew)
brew tap rnorth/tap
brew install wyoming-mlx
Run it in the foreground with wyoming-mlx, or as a launchd service:
brew services start wyoming-mlx
Logs go to $(brew --prefix)/var/log/wyoming-mlx.log. Apple Silicon only.
[!WARNING] Upgraders: the
models.whisperconfig value (TOML[models] whisper = ..., CLI--whisper-model, envWYOMING_MLX_MODELS__WHISPER) changed meaning in this release. It used to be a Hugging Face repo ID (e.g.mlx-community/distil-whisper-large-v3); it is now a WhisperLiveKit model-size name:tiny,base,small,medium,large-v3, orlarge-v3-turbo(defaultlarge-v3-turbo). An old repo-ID value (anything containing/) is rejected at startup with a clear error. Update your config, for example:[models] # before: whisper = "mlx-community/distil-whisper-large-v3" whisper = "large-v3-turbo"
Quick start (dev)
mise install
uv sync
uv run pytest
Works on Linux too: CPU-only torch is selected automatically (the fake
backends need no GPU). Integration tests against real models are skipped by
default; run them with uv run pytest --integration (Apple Silicon only).
Run locally (fake backends)
uv run python scripts/dev_run.py
The dev server uses the API key dev.
Run locally (real MLX backends)
uv run wyoming-mlx
By default it loads:
- whisper-large-v3-turbo (MLX, streaming) on Wyoming port 10300 / HTTP
/v1/audio/transcriptions - Kokoro-82M (MLX) on Wyoming port 10200 / HTTP
/v1/audio/speech - HTTP on port 10400 with API-key auth
Models are stored in the Hugging Face cache. The STT model is loaded at startup (so a bad model name or download failure surfaces immediately); the Kokoro TTS model downloads on first synthesis.
API keys
HTTP endpoints require a bearer token. Keys are read at startup from
~/.config/wyoming-mlx/apikeys (override with --http-api-keys-file),
one key per line, # comments allowed. The file should be mode 0600.
If the file is missing or empty, all HTTP requests are rejected with 401.
Note that the HTTP API listens on all interfaces by default (set
WYOMING_MLX_HTTP__HOST=127.0.0.1 to restrict it), and GET /v1/models
is unauthenticated, matching OpenAI API behaviour.
mkdir -p ~/.config/wyoming-mlx
(umask 077; openssl rand -hex 32 > ~/.config/wyoming-mlx/apikeys)
HTTP API
List models
curl http://localhost:10400/v1/models
Transcribe an audio file
curl http://localhost:10400/v1/audio/transcriptions \
-H "Authorization: Bearer $API_KEY" \
-F file=@some.wav
Synthesize speech
curl http://localhost:10400/v1/audio/speech \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"Hello there.","voice":"af_heart"}' \
--output /tmp/out.wav
Home Assistant integration
Settings → Integrations → Wyoming Protocol → Add:
- STT:
<host>:10300 - TTS:
<host>:10200
No keys, no TLS (HA convention, trusted LAN).
Streaming transcripts (live partial results) require Home Assistant 2025.7 or newer; older versions still receive the final transcript exactly as before.
Configuration
Pass --config /path/to/config.toml or set env vars with the
WYOMING_MLX_ prefix and __ for nesting (e.g.
WYOMING_MLX_HTTP__PORT=10401). See src/wyoming_mlx/config.py for the
full schema.
License
Metadata
Release files for wyoming-mlx 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| wyoming_mlx-0.2.0.tar.gz | 183.1 kB | Without provenance | Details for wyoming_mlx-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| wyoming_mlx-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for wyoming_mlx-0.2.0-py3-none-any.whl |
Total release size: 208.3 kB
Release files / wyoming_mlx-0.2.0.tar.gz
| Download URL | wyoming_mlx-0.2.0.tar.gz |
|---|---|
| Size | 183.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
336ce384fc38b867cc9e57abc0f8a118d0f7d1f861ebff20794d78ca5cb505fb
|
|
BLAKE2b-256 checksum How to use checksums |
656bfad7ce65a2ea29a51fde8590d0f61b6c004bf8de51598627f47c67e34395
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / wyoming_mlx-0.2.0-py3-none-any.whl
| Download URL | wyoming_mlx-0.2.0-py3-none-any.whl |
|---|---|
| Size | 25.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b02eec8e9a6ad1d59d304769b78c30b75600ffbea3c0326cb86219718a057827
|
|
BLAKE2b-256 checksum How to use checksums |
ae4be3aee05c3de792fa5d8580e8fcd66c26a881ac86d606fc9ebbecc3ff9d1b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for wyoming-mlx 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.