Back to DevLog

Vosk grammar constraints require dynamic-graph models

3 min read

I raised the model tiers for both wake and command recognition. Vosk went from small to full (vosk-model-en-us-0.22), and faster-whisper from base to small. The Whisper self-test passed identically on both tiers because clean Kokoro TTS does not discriminate between them. The test proved non-regression, not a gain.

The full Vosk model loaded in 16.05 seconds and added 5.07 GB resident memory.

Grammar-constrained recognition

Live testing with the full model scored around 1/10. The partials showed the language model was preferring ordinary English over the proper noun: pay for me, the have, hey format. A grammar removes the competition. The decoder may emit only listed phrases or [unk].

Only a dynamic-graph model can take a runtime grammar. The 1.8 GB full model has a static graph and cannot be constrained.

I made lgraph the default. It loads in 0.83 seconds at +352 MB resident, 19× faster and 14× smaller than the full model. I kept the open-vocabulary shape-matching path as the fallback for full, so selecting wake_model: full still works instead of silently breaking. Three tiers are now selectable via a new wake_model setting.

Name audition harness

Grammar generation became style-aware. From a name, six styles (bare, hey, okay, good morning, good evening, are you there) are crossed with every expected spelling. 24 phrases per finalist.

python foreman.py audition <name> drives the real detector on a candidate, counts wakes, and attributes each one to the entry that caught it. A clean hit on the name is distinguishable from a rescue by a variant spelling.

Critical finding

The bare-name style plus the incumbent name measured 4/6 false wakes on ordinary trade speech. With odin it measured 1/6.

It fired on "the foreman said the job was done", "we are forming a new crew tomorrow", "what is the format of that file". The style correctly reported that the wake name is a common noun in this user's own working vocabulary.

Production wake is degraded until the rename lands or the bare style row is deleted from _WAKE_STYLES.

Matching rule

Matching is complete-entry tail-anchored, never "any non-[unk] result fires". A grammar constrains vocabulary but not length, so partials are prefixes of legal phrases. A bare "hey" or "good morning" would fire under the literal rule.

[unk] is not stripped before matching. "The site had a foreman on it" returns [unk] hey foreman [unk] and stripping would leave a clean phrase that wakes her mid-sentence.

Audition dry run

On synthesized speech all three finalists woke 5/5 across all six styles with zero silence or noise wakes. Apollo resolved to its canonical spelling every time. Odin and Janus were carried entirely by the variants oden and janice.

Share this post