Skip to content

fix(cli): transcribe --model large-v3 crashes on unknown DTW preset#2086

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/transcribe-dtw-preset
Jul 8, 2026
Merged

fix(cli): transcribe --model large-v3 crashes on unknown DTW preset#2086
miguel-heygen merged 1 commit into
mainfrom
fix/transcribe-dtw-preset

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Problem

hyperframes transcribe audio.mp3 --model large-v3 (and large-v2/large-v1) fails with:

error: unknown DTW preset 'large-v3'
→ Transcription failed

Root cause

packages/cli/src/whisper/transcribe.ts passed the ggml model file-stem straight to whisper.cpp's --dtw flag. But those are two different namespaces:

  • model files are hyphenated: ggml-large-v3.bin
  • --dtw alignment-heads presets are dotted: large.v3, large.v3.turbo

They coincide for tiny / base / small / medium (+.en) — the models most people use — which is why it went unnoticed. They diverge for the large-v* family, so --dtw large-v3 hits whisper-cli's unknown-preset guard and aborts.

Fix

A small exported dtwPresetForModel(model) that maps the file-stem to the preset (-.): a no-op for the tiny/base/small/medium families, correct for large-v1/v2/v3 and large-v3-turbo. Used only for the --dtw arg; --model still gets the file path.

Also fixes media-use transcription — its whisper fallback shells to hyperframes transcribe, so it inherited the same crash for large models.

Test

packages/cli/src/whisper/transcribe.test.ts — table-driven cases for the large family (hyphen→dot) and the preset-identical families (unchanged). vitest run 9/9.

Verified: cli tsc --noEmit exit 0, oxlint/oxfmt clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv

… large-v3)

`hyperframes transcribe --model large-v3` aborted with "unknown DTW preset
'large-v3'". whisper.cpp's --dtw flag wants a dotted alignment-heads preset
(large.v3), but we passed the hyphenated ggml file stem (large-v3). They
coincide for tiny/base/small/medium(+.en) — why it slipped through — but
diverge for the large-v* family. Map stem -> preset (- to .) so large-v1/v2/v3
(and large-v3-turbo) work; no-op for the others. Also fixes media-use, which
shells to `hyperframes transcribe`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R1 — hyperframes #2086 at f691e081

🟢 LGTM. Targeted fix for the X-reported large-v3 transcription crash (@IChooseAI's Chinese-translated report: "HyperFrame Transcribe throws an error when using the large-v3 model"). Root-cause analysis reads clean — whisper.cpp's --dtw alignment-heads presets are dotted (large.v3, large.v3.turbo) while ggml model file stems are hyphenated (large-v3, large-v3-turbo), and they coincide on tiny/base/small/medium(+.en) which is exactly why the divergence stayed hidden until a large-v* user hit it.

Verified

  • Mapping is correct for the reported failure surface. dtwPresetForModel("large-v3") === "large.v3", dtwPresetForModel("large-v3-turbo") === "large.v3.turbo". Both are documented whisper.cpp preset names.
  • Preset-identical models untouched. No-op for tiny, base.en, small, small.en, medium.en — tests cover.
  • Namespace kept separate at argv layer. --model still gets the file stem (effectiveModel); only --dtw gets the transform. transcribe.ts:299 swap is minimal.
  • Downstream reach. PR body's claim that this also fixes media-use's whisper fallback (which shells to hyperframes transcribe) is correct — media-use inherits the fix without a media-use PR.
  • Test shape. 4 hyphenated large-family cases + 5 preset-identical (tiny/base.en/small.en/medium.en/small) via test.each. Regression can't reintroduce without a test change.

Observation (non-blocking)

O1 — Global -. replace is broader than the target namespace. For a model with a hyphen that ISN'T in whisper.cpp's DTW preset table (a fine-tuned my-domain-model, a hypothetical future large-v4 before whisper.cpp lists large.v4, or a quantized variant like large-v3-q5_0large.v3.q5_0), the mapping runs and produces a preset whisper-cli still doesn't recognize. Result: same crash, different error text ("unknown DTW preset ''" vs "unknown DTW preset ''"). Doesn't regress the reported case; doesn't cover the adjacent unknown-preset case either.

A whitelist-with-fallback shape (Map<model, preset> for known families, skip --dtw entirely when unknown) would trade the two lines for graceful degradation on custom / future / quantized models — silent no-DTW-alignment instead of whisper-cli crash. Not a merge-blocker; the dtwPresetForModel name already advertises the whitelist API, so this is a swap for later. Flagging now so a next-model crash-report doesn't come as a surprise.

CI

In-flight at review time: Build, Test (the load-bearing one — runs the new vitest cases), Typecheck, CodeQL js/ts, Windows render, Preview parity, CLI smoke (win/required). Green already: Format, Lint, Fallow, CLI shim ubuntu+macOS, SDK unit/contract/smoke, Runtime contract, Studio load smoke, Player perf. My 🟢 assumes Test clears.

R1 by Via

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at f691e08.

Nice narrow crash-fix — the diagnosis (hyphenated model stem vs dotted DTW preset) is spot-on, and dtwPresetForModel is minimal in the best way. Traced through transcribe.ts; the split between --model <path> and --dtw <preset> is preserved, .en → multilingual switchover at L230-238 still passes the stem through dtwPresetForModel, and the test asserts both the fix and the "unchanged for preset-identical families" invariant (not just a "should throw" enshrining the bug — good).

Concerns

  • Class-of-crashes coverage is partial (packages/cli/src/whisper/transcribe.ts:216). The tweeted crash + all documented models (tiny.en, base.en, small.en, medium.en, large-v3) are covered, plus large-v1/v2 and large-v3-turbo. But whisper.cpp's known DTW presets end there — the model-download machinery in manager.ts:17 doesn't validate against a supported set, it just fetches ggml-${model}.bin, so quantized variants that Hugging Face publishes are technically reachable. --model large-v3-q5_0dtwPresetForModellarge.v3.q5_0 → whisper-cli aborts with the same "unknown DTW preset" class-of-error. Same for medium-q5_0, small.en-tdrz, etc. Not blocking (these aren't advertised), but worth naming as a follow-up: either validate --model against a known set at the CLI layer, or make dtwPresetForModel a real dispatch that falls back to a safe preset for quantized/variant families. (per feedback_bandaid_vs_longterm_rubric — narrow crash-fix vs class fix, and feedback_dispatch_map_silent_drop_default_types — currently the .replace() is a silent-map that produces syntactically-plausible-but-invalid presets.)

  • PR-body claim I couldn't verify: "media-use transcription — its whisper fallback shells to hyperframes transcribe". Grepped skills/media-use/ — the SKILL.md and scripts/ don't shell to hyperframes transcribe anywhere I could find. The one hyperframes transcribe reference I did find in skills/hyperframes-media/scripts/heygen-tts.mjs is a printed hint, not a subprocess. If the "media-use also fixed" claim refers to a caller in another repo (e.g. an internal skill using the CLI), fine — but if it's about skills/media-use/ in this repo, I don't see the shell-out. Non-blocking; just noting for your own claim-verification.

Nits

  • Doc drift (packages/cli/src/commands/transcribe.ts:38, docs/packages/cli.mdx). --model help still lists "Options: tiny.en, base.en, small.en, medium.en, large-v3". This fix newly makes large-v3-turbo / large-v2 / large-v1 actually work — worth surfacing at least large-v3-turbo in the help text so users know it's a supported option now (previously it crashed the same way, so keeping it hidden made sense; now it doesn't). One-line update in the help + mdx.

  • Observability on failure-path (packages/cli/src/commands/transcribe.ts:171-179). The catch just prints Transcription failed: ${message} and exits 1. No telemetry event captures which model / which error class — so the next "unknown DTW preset" (or any similar class-of-crashes) recurrence surfaces via Twitter, not via your own signals. Pre-existing, not this PR's job to fix, but the current bug was the exact shape a telemetry gap creates. Follow-up ticket, not a blocker here.

What I didn't verify

  • I didn't run vitest locally; taking the PR body's "9/9" at face value.
  • I didn't confirm whisper.cpp's current preset list against upstream source — I'm going on the PR body's characterization (large.v1, large.v2, large.v3, large.v3.turbo) matching the error message. If whisper.cpp added a new preset since, the tests would still be right but the .replace may map further exotic variants correctly or not.

Small, well-scoped fix for a real customer-facing bug — LGTM from my side, leaving as a comment.

Review by Rames D Jusso

@miguel-heygen miguel-heygen merged commit 8e04d01 into main Jul 8, 2026
41 checks passed
@miguel-heygen miguel-heygen deleted the fix/transcribe-dtw-preset branch July 8, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants