Add always-on wake word detection to the voice assistant pipeline using livekit-wakeword. The wake word is "hey crix".
Install livekit-wakeword:
uv add git+https://github.com/livekit/livekit-wakeword.gitInstall system dependencies:
sudo apt install espeak-ng libsndfile1 ffmpeg sox portaudio19-devDownload base models:
uv run livekit-wakeword setupCreate configs/hey_crix.yaml:
model_name: hey_crix
target_phrases:
- "hey crix"
n_samples: 10000
model:
model_type: conv_attention
model_size: small
steps: 50000
target_fp_per_hour: 0.2Train and export:
uv run livekit-wakeword run configs/hey_crix.yamlThis produces hey_crix.onnx. Place it in models/hey_crix.onnx.
Create a WakeWordDetector class that:
- Loads
WakeWordModelwithhey_crix.onnxonce at init (not per-call) - Exposes an async method
wait_for_wake_word()that blocks until detection fires - Uses
WakeWordListeneras an async context manager internally - Accepts a configurable
threshold(default0.5) anddebounce(default2.0seconds)
from livekit.wakeword import WakeWordModel, WakeWordListener
class WakeWordDetector:
def __init__(self, model_path: str, threshold: float = 0.5, debounce: float = 2.0):
...
async def wait_for_wake_word(self) -> str:
# returns the name of the detected wake word
...The pipeline state machine should:
- Start in
IDLEstate - Call
detector.wait_for_wake_word()— this blocks until wake word fires - On detection → transition to
LISTENINGstate, start VAD + audio recording - Rest of pipeline continues as normal (STT → LLM → TTS)
- After TTS finishes → return to
IDLE, callwait_for_wake_word()again
IDLE
└── await wait_for_wake_word()
└── LISTENING → TRANSCRIBING → THINKING → SPEAKING
└── (done) → IDLE
The wake word detector must keep listening during SPEAKING state too — if wake word fires mid-response, interrupt TTS and go back to LISTENING.
Add to .env:
WAKE_WORD_MODEL_PATH=models/hey_crix.onnx
WAKE_WORD_THRESHOLD=0.5
WAKE_WORD_DEBOUNCE=2.0Load in config.py alongside existing env vars.
WakeWordListeneropens the mic stream internally via portaudio — do NOT open a separate sounddevice stream simultaneously or there will be a conflict. Close the wake word listener before opening the recording stream, then reopen it after TTS finishes.- Model loads once at startup, not per detection cycle.
livekit-wakewordis backward compatible withopenWakeWord—.onnxmodels are interchangeable if needed.- Training is a one-time step. Once
hey_crix.onnxexists, skip Step 1.