Rewrite setup instructions for non-specialist readers
The audience for this is Home Assistant users, who are not necessarily developers. The instructions assumed too much and buried the step most likely to trip someone up. Biggest fix: connecting to Home Assistant is two steps, not one. Adding the Wyoming integration does nothing on its own -- the assistant keeps using whatever speech-to-text it used before until you change it under Settings -> Voice assistants. That was previously half a sentence at the end of a paragraph, and its failure mode is silent: everything looks installed and nothing improves. It is now its own numbered step with an explanation. Also adds: - "Check it's working" and "Troubleshooting", the latter drawn from failures actually hit while building and deploying this, including the wrong-Python error and the service-did-not-start-after-changing-user case. - IP address guidance that survives contact with a real machine. Checking on the deployment host showed five addresses -- two Parallels interfaces, a Tailscale one, and two real ones -- so the docs now say which to ignore. The obvious `ipconfig getifaddr en0` was rejected: it returns nothing there. - Requirements now lead with `brew install python@3.13` rather than an explanation of lzma; the reasoning moved into a collapsed block. - A plain-English guide to reading the benchmark table, and an explicit note that the numbers are a snapshot nothing re-runs automatically. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,20 @@ means are skewed by first-request kernel compilation.
|
||||
| mlx-whisper large-v3 | MLX | 1170 ms | 1251 ms | 48/48 | 21/21 | `"thank you"` |
|
||||
| faster-whisper distil-large-v3 | CPU int8 | 4269 ms | 4304 ms | 46/48 | 21/21 | `"thank you"` |
|
||||
|
||||
### How to read this
|
||||
|
||||
- **Median** — the typical time to transcribe one command. This is the number
|
||||
you feel when talking to your assistant.
|
||||
- **p90** — nine times out of ten it was at least this fast. A p90 close to the
|
||||
median means consistent; a long tail is worse than the median suggests.
|
||||
- **Exact** — how many of the 48 clips came back word-for-word correct.
|
||||
- **Digits** — of the 21 clips containing a number, how many wrote it as `21`
|
||||
rather than `twenty one`. This matters more than it looks; see
|
||||
[Model choice](README.md#model-choice-v2-not-v3).
|
||||
- **Silence** — what came back for three seconds of pure silence. Anything
|
||||
other than nothing is the model inventing words, which your assistant then
|
||||
tries to act on.
|
||||
|
||||
**Exact** is a strict string match after normalising case, punctuation and
|
||||
whitespace. **Digits** counts how many of the 21 number-bearing clips came
|
||||
back with digits rather than spelled-out words — see
|
||||
@@ -59,6 +73,11 @@ What the numbers say:
|
||||
- **Latency is raw inference**, excluding Wyoming protocol overhead. End to
|
||||
end through this server, expect roughly 15–35 ms on top.
|
||||
- One machine, one run each. Treat differences of a few percent as noise.
|
||||
- **These are a point-in-time snapshot.** Nothing re-runs them automatically,
|
||||
and the test suite cannot detect drift, so treat them as stale after any
|
||||
change to the audio path, a `parakeet-mlx`/MLX upgrade, or a macOS release
|
||||
(which changes the TTS voices the clips are built from). Re-run the sweep
|
||||
and update the table in the same commit as the change.
|
||||
|
||||
## Reproducing
|
||||
|
||||
|
||||
@@ -40,41 +40,147 @@ latency).
|
||||
|
||||
## Requirements
|
||||
|
||||
- Apple Silicon Mac (MLX is Metal/ANE-backed)
|
||||
- Python 3.10+ **with the `lzma` module** — `librosa` pulls in `pooch`, which
|
||||
imports it. Pythons built without `xz` (a common pyenv default) pass every
|
||||
version check and then fail at import time with
|
||||
`ModuleNotFoundError: _lzma`. Homebrew's Python is fine.
|
||||
- ~2.3 GB disk for the model, ~600 MB for MLX wheels
|
||||
- **An Apple Silicon Mac** (M1 or newer) that stays on. This does not run on
|
||||
Intel Macs, a Raspberry Pi, or inside Home Assistant OS.
|
||||
- **Homebrew Python.** If you are not sure you have it:
|
||||
```bash
|
||||
brew install python@3.13
|
||||
```
|
||||
- **About 3 GB of free disk** — 2.3 GB of model, 600 MB of libraries.
|
||||
|
||||
`ffmpeg` is **not** required — Wyoming already delivers 16 kHz mono PCM, so
|
||||
the mel spectrogram is built directly.
|
||||
<details>
|
||||
<summary>Why Homebrew Python specifically</summary>
|
||||
|
||||
The installer needs Python 3.10 or newer that includes the `lzma` module.
|
||||
macOS ships Python 3.9, which is too old, and `pyenv` frequently builds
|
||||
Python without `lzma` (it needs `xz` present at build time). Both fail — the
|
||||
pyenv case unhelpfully, passing every version check and then dying at import
|
||||
time with `ModuleNotFoundError: _lzma`. Homebrew's builds are fine.
|
||||
|
||||
`ffmpeg` is **not** required, unlike most speech-to-text tooling. Wyoming
|
||||
already delivers audio in the right format, so this builds the mel
|
||||
spectrogram directly.
|
||||
</details>
|
||||
|
||||
## Install
|
||||
|
||||
**1. Download and run the installer.**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/adamhf/wyoming-parakeet-mlx
|
||||
cd wyoming-parakeet-mlx
|
||||
./install.sh
|
||||
```
|
||||
|
||||
This creates a virtualenv, runs the unit tests, pre-downloads the model, and
|
||||
registers a LaunchDaemon on port 7892 that starts at boot without needing a
|
||||
GUI login. It installs *in place*, so keep the checkout somewhere permanent.
|
||||
It will ask for your password once, to register the background service.
|
||||
|
||||
Options: `--port`, `--model`, `--user`, `--python`, `--no-daemon`,
|
||||
`--no-download`.
|
||||
Expect it to take a few minutes — most of that is downloading the model. It
|
||||
sets up its own Python environment, runs the test suite, downloads the model,
|
||||
and registers a background service on port 7892 that starts automatically when
|
||||
the Mac boots, with no need to log in.
|
||||
|
||||
Then in Home Assistant: **Settings → Devices & Services → Add Integration →
|
||||
Wyoming Protocol**, enter the host and port, and select the new engine as the
|
||||
speech-to-text step of your Assist pipeline.
|
||||
**Keep this folder where it is.** The installer sets things up inside it, so
|
||||
moving or deleting the folder afterwards breaks the service.
|
||||
|
||||
Remove it with `./uninstall.sh`.
|
||||
Remove it later with `./uninstall.sh`.
|
||||
|
||||
### Server options
|
||||
<details>
|
||||
<summary>Installer options</summary>
|
||||
|
||||
`install.sh` bakes these into the plist; run `script/run --help` to see them
|
||||
all. The ones worth knowing:
|
||||
`--port`, `--model`, `--user`, `--python`, `--no-daemon`, `--no-download`.
|
||||
Run `./install.sh --help` for details. See [Security](#security) for why
|
||||
`--user` is worth using.
|
||||
</details>
|
||||
|
||||
## Connect it to Home Assistant
|
||||
|
||||
Installing it is not enough on its own — Home Assistant has to be told about
|
||||
it, and then told to *use* it. That is two separate steps, and the second one
|
||||
is easy to miss.
|
||||
|
||||
**1. Find your Mac's IP address.** System Settings → Network → click your
|
||||
active connection → Details → the "IP address" field. Or in a terminal:
|
||||
|
||||
```bash
|
||||
ifconfig | grep "inet " | grep -v 127.0.0.1
|
||||
```
|
||||
|
||||
That command often lists more addresses than you expect. Ignore anything
|
||||
belonging to virtualisation software (Parallels, VMware, Docker — commonly
|
||||
`10.211.55.x`, `10.37.129.x`, `172.17.x.x`) or a VPN (Tailscale uses
|
||||
`100.x.x.x`); those are not reachable from Home Assistant in the usual case.
|
||||
You want the one on the same network as Home Assistant itself — if Home
|
||||
Assistant is on `192.168.1.50`, you want the Mac's `192.168.1.x` address.
|
||||
|
||||
If your Mac has both Wi-Fi and Ethernet active you will see two candidates.
|
||||
Either works, but pick one that will stay put — a wired address, ideally with
|
||||
a DHCP reservation on your router — because if that interface goes away, Home
|
||||
Assistant loses speech-to-text.
|
||||
|
||||
**2. Add the integration.** In Home Assistant: **Settings → Devices &
|
||||
Services → Add Integration → Wyoming Protocol**. Enter the IP address from
|
||||
step 1 and port **7892**.
|
||||
|
||||
If it succeeds you get a new entry called `parakeet-mlx`. If it says it cannot
|
||||
connect, see [Troubleshooting](#troubleshooting).
|
||||
|
||||
**3. Actually use it.** Go to **Settings → Voice assistants**, click your
|
||||
assistant, and set **Speech-to-text** to `parakeet-mlx`. Save.
|
||||
|
||||
Until you do this, nothing changes — you will have installed it and Home
|
||||
Assistant will carry on using whatever it used before. If you have more than
|
||||
one assistant configured, change each one you care about.
|
||||
|
||||
**4. Try it.** Click the Assist icon (top right of Home Assistant), press the
|
||||
microphone, and say something like "turn off the kitchen lights".
|
||||
|
||||
## Check it's working
|
||||
|
||||
Is the service running?
|
||||
|
||||
```bash
|
||||
sudo launchctl print system/local.wyoming-parakeet | grep "state ="
|
||||
```
|
||||
|
||||
Expect `state = running`.
|
||||
|
||||
Watch it work — leave this running, then speak to Assist:
|
||||
|
||||
```bash
|
||||
tail -f /tmp/local.wyoming-parakeet.stderr
|
||||
```
|
||||
|
||||
Each command produces a line like
|
||||
`2.70s audio -> 124ms, 45 chars`. The `124ms` is how long transcription took.
|
||||
The text itself is deliberately not logged; see [Security](#security).
|
||||
|
||||
Test it without speaking, using generated audio:
|
||||
|
||||
```bash
|
||||
./test/make-clips.sh
|
||||
.venv/bin/python test/wy-test.py test/clips/*.wav
|
||||
```
|
||||
|
||||
Expect ten commands transcribed correctly, `silence.wav` coming back empty,
|
||||
and times around 100–150 ms.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| What you see | What it usually means |
|
||||
|---|---|
|
||||
| Home Assistant says **"Failed to connect"** when adding the integration | Wrong IP, or the service is not running. Check `state = running` above, confirm the IP with `ifconfig`, and make sure both machines are on the same network. |
|
||||
| Integration added fine, but **Assist is no worse and no faster** | You missed step 3 — the assistant is still using its old speech-to-text engine. Settings → Voice assistants → Speech-to-text. |
|
||||
| **`No matching distribution found for parakeet-mlx`** during install | Wrong Python. Run `brew install python@3.13` and try again, or point the installer at it: `./install.sh --python /opt/homebrew/bin/python3.13`. |
|
||||
| **`error: service did not start`** at the end of install | Look at `/tmp/local.wyoming-parakeet.stderr` for the reason. If you changed `--user`, re-run `./install.sh` rather than editing the service file by hand. |
|
||||
| The **first command after a reboot is slow** (~300 ms), then it is fast | Expected. The graphics framework compiles its kernels on first use. It settles by the second or third command. |
|
||||
| Everything works, then **stops after a few weeks** | Check the Mac has not gone to sleep. System Settings → Displays → Advanced → prevent automatic sleeping. |
|
||||
| You want to see **what it actually heard** | Add `--debug`, but read [Security](#security) first — the log then contains everything spoken near your microphones. |
|
||||
|
||||
## Server options
|
||||
|
||||
Most people never need these. `install.sh` bakes them into the service
|
||||
definition, so to change one, re-run the installer rather than editing the
|
||||
service file by hand. Run `script/run --help` for the full list.
|
||||
|
||||
| Flag | Default | |
|
||||
|---|---|---|
|
||||
|
||||
Reference in New Issue
Block a user