Whistle's 86,174-utterance benchmark: test your own clips first
Cactus Whistle's card reports word error rates over 86,174 utterances. Score 20 of your own clips against a human transcript before choosing it over hosted STT.

Treat a model card benchmark as a filter, not a verdict. Whistle's Hugging Face card says its word error rates were measured over 86,174 utterances, with comparisons to Whisper and Moonshine models. Your audio has its own accents, music beds and product names, so score 20 clips of your own against a human-checked transcript before you commit.
Whistle facts (16.9 MB, seven languages, Apache 2.0, 16 kHz mono input) are from the card read on 2026-10-13. The Sume steps use video-inspect and remix-media as documented on origin/main b28f3b0.
| Step | What to do | Sume surface |
|---|---|---|
| 1. Pick clips | 20 clips from real jobs, mixed noise and speakers | Your library |
| 2. Reference | Human-correct one transcript per clip | Optional: start from a Sume transcript |
| 3. Candidate A | Run Whistle on 16 kHz mono audio | audio-detach with sample_rate: 16000, channels: mono |
| 4. Candidate B | Hosted transcript with a language hint | POST /v1/video-inspect with transcribe: true |
| 5. Score | Word error rate per clip, then the worst five | Your script |
What should I look at besides the average?
Names, numbers and brand terms: an average can hide a model that gets product names wrong in every clip. Whistle handles one pass up to 30 seconds and windows beyond that, so include a few clips longer than 30 seconds to see whether the seams cost words.
How do I get a draft reference transcript?
Run video-inspect with transcribe: true and segmentation.mode: "sentence" and correct the output by hand. For heavy reference work, the remix media transcribe verb returns a word-timed transcript with per-word confidence; its default engine is WhisperX, and the billed scribe_v2 engine needs allow_billed_stt: true.
# word error rate between a reference and a hypothesis (pure Python)
def wer(ref: str, hyp: str) -> float:
r, h = ref.lower().split(), hyp.lower().split()
d = [[0] * (len(h) + 1) for _ in range(len(r) + 1)]
for i in range(len(r) + 1):
d[i][0] = i
for j in range(len(h) + 1):
d[0][j] = j
for i in range(1, len(r) + 1):
for j in range(1, len(h) + 1):
cost = 0 if r[i - 1] == h[j - 1] else 1
d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost)
return d[-1][-1] / max(len(r), 1)
print(wer("say hello to sume", "say hello to some"))Sources
Related posts
More in Models
- Whistle speech model: Apache 2.0, 16.9 MB, offline or Sume STT?
Cactus Whistle is a 16.9 MB Apache 2.0 speech-to-text model for seven languages. Run it on-device, or send the clip to Sume STT for other languages.
- An OpenRouter-compatible video API: sume/auto or a pinned model
Sume's POST /v1/videos follows OpenRouter's video generation API field for field. Let sume/auto pick the model, or pin a catalog id like seedance-2.5.
- Image generation API with reference images: POST /v1/images
Send a prompt plus public HTTPS reference images to Sume's POST /v1/images. Pin a catalog model or send sume/auto; the catalog lists each model's limits.
- Video 1.0 and Image 1.0 are retiring soon: move to sume/auto
Sume Video 1.0 and Image 1.0 are retiring soon and already run as aliases for the Auto path. New integrations call /v1/videos or /v1/images with sume/auto.
Written by Sume