Appearance
Native Whistle speech recognition
Whistle runs directly in Swift/MLX from the original Cactus-Compute archive.
bash
mere.run model pull speech-asr-whistle
mere.run speech transcribe recording.wav --backend whistle
mere.run speech transcribe recording.wav --model speech-asr-whistle --language de
mere.run speech transcribe recording.wav --backend whistle --beam-size 3 --decoder-depth 4
mere.run speech transcribe recording.wav --backend whistle --keyword "Siobhan" --keyword "Mere Run"
mere.run speech listen --model speech-asr-whistle --keyword "Siobhan"The managed download pins revision ca5287601bef25af26dcf2e1b2bdc0843a7c19e5 and downloads about 17 MB. A local model directory needs config.json and whistle.cact. The archive supplies CQ2/CQ4 neural weights, FP16 scales and small tensors, the exact tokenizer, and the audio filterbank. Native Metal matrix operations read packed indices directly; Engram tables expand only the requested rows. On Linux, portable MLX operations expand projection or requested embedding rows on the CUDA device; they do not expand the full Engram table. The runtime does not execute an upstream binary or use ONNX.
For the original FP32 reference path, also place checkpoints/whistle.safetensors in that directory and select it explicitly:
bash
mere.run speech transcribe recording.wav --backend whistle --model /path/to/whistle \
--whistle-weights fp32 --beam-size 1 --no-timestampsLinux CUDA configuration
Packed Whistle uses portable MLX operations on Linux. Run it with CUDA graph capture disabled because the pinned MLX backend's graph cache can exhaust its entry limit during beam decoding:
bash
MLX_USE_CUDA_GRAPHS=0 mere.run speech transcribe recording.wav --backend whistle
MLX_USE_CUDA_GRAPHS=0 mere.run speech listen --model speech-asr-whistle --jsonlSet the same environment variable before initializing MLX when embedding the runtime in another process. This is eager CUDA execution; it retains GPU tensor operations. Linux performance and long-session behavior need broader qualification.
Decoding controls
- English, German, French, Spanish, Italian, Dutch, and Polish transcription, with automatic language detection or a supported
--languagehint. - Audio decoding and 16 kHz mono resampling through the shared AudioCodecs reader.
- Thirty-second inference windows with one-second overlap for longer files.
- Cached beam search:
--beam-size 1...8, default 5. Size 1 is greedy. --decoder-depth 2...8, default 8, selects physical blocks by endpoint-preserving bisection. Depth 4 uses blocks 0, 3, 5, and 7; the encoder always uses all eight.- Repeated
--keywordphrases bias initial tokens by +2 and matching continuations by +5 logits. Up to 64 phrases, each up to 128 Unicode scalars. --max-tokenscan lower the 318-text-token limit per window.- Word timestamps use normalized cross-attention and dynamic time warping on an 80 ms encoder grid. They are acoustic estimates.
--no-timestampsskips alignment. - Exact digital silence produces an empty transcript.
- Shared transcription operation records through
--run-dirinclude the chosen Whistle controls and alignment results.
--backend auto keeps the existing Parakeet default. Naming the Whistle model selects Whistle. Translation, unsupported language hints, and Core ML execution are rejected. Whistle remains CLI-only.
Live audio
bash
mere.run speech transcribe recording.wav --backend whistle --stream
mere.run speech transcribe - --backend whistle \
--stream --input-format pcm-s16le --sample-rate 16000 --jsonl
mere.run speech listen --model speech-asr-whistle --jsonlLive sessions keep one model resident and use bounded VAD utterances, a bounded input queue, partial revisions at --stream-decode-ms / --decode-ms cadence, commit events, statistics, and one final event. They re-decode the complete utterance at commit, including audio arriving after its partial. Cancellation discards the active utterance. Live events carry utterance times; word alignment is computed for file transcription only. Whistle is a windowed speech model, so this is utterance streaming rather than causal encoder inference.
Qualification boundary
The native packed path uses the archive's CQ2/CQ4 weights with FP32 activations and KV arithmetic. It does not reproduce the C++ engine's A8 activation/cache quantization, beam heuristics, or noise rejection. The upstream footprint, five-beam accuracy, and CPU latency measurements describe that engine; they are not measurements of this runtime's total memory, recognition accuracy, or latency.
Numerical tests compare encoder cross-K/V and four cached decoder steps against independent graphs for both original FP32 and independently dequantized CQ weights. Asset-free tests exercise CQ2/CQ4 Metal matrix products, sparse row gathers, decoder layer selection, keyword biasing, and the frontend against a scalar DFT. Multilingual accuracy, long-file overlap quality, timestamp accuracy, and real microphone capture need a broader corpus and device qualification before claiming parity with the upstream engine.
Numerical checks
An optional complete-checkpoint comparison accepts MERERUN_WHISTLE_PARITY_DIR, containing whistle.safetensors, whistle.cact, whistle-reference.json, and whistle-cq-reference.json. Generate the JSON references with scripts/fixtures/export-whistle-reference.py, using --cact for the CQ oracle. The fixture's README documents asset hashes and regeneration. Graphs are validation inputs only.
bash
MERERUN_WHISTLE_PARITY_DIR=/path/to/parity swift test --filter Whistle
./scripts/check.shSee the upstream architecture and container notes for model conventions.
Local qualification
On 2026-10-10 the pinned archive was checked with the public space's 4.259-second English example. Packed greedy and five-beam decoding both produced “Please turn off the kitchen lights and set an alarm for 7.30 tomorrow morning.” Five-beam word alignment returned 14 ordered word spans within the audio duration. The explicit FP32 greedy path produced the same words, spelling the time “730”.
Paced PCM stdin produced a partial, a complete commit, statistics, and exactly one EOF final event. The four-layer greedy path ran but misrecognized “set an” as “said in” on this example, so reduced depth is a speed/accuracy tradeoff, not a qualified replacement for eight layers. A fresh managed install validated at 16.9 MB without downloading the FP32 checkpoint. These are bounded smoke results, not corpus accuracy or real-microphone qualification.
Optimized release builds also passed five-beam word alignment and resident PCM streaming from the compact install, with the same complete transcript.