Skip to content

fix(whisperkit): streaming translate slows after ~30-40s — task-aware confirmation lag - #230

Merged
initcore0 merged 1 commit into
mainfrom
fix/whisperkit-translate-latency
Jul 31, 2026
Merged

fix(whisperkit): streaming translate slows after ~30-40s — task-aware confirmation lag#230
initcore0 merged 1 commit into
mainfrom
fix/whisperkit-translate-latency

Conversation

@initcore0

Copy link
Copy Markdown
Owner

Symptom

WhisperKit backend, Russian speech, translate-to-English on: works well at first, gets noticeably sluggish after ~30–40 s of continuous dictation.

Root cause (researched, fork + app both mapped)

The streaming loop (AudioStreamTranscriber) re-decodes everything after lastConfirmedSegmentEndSeconds on every pass, and confirmation permanently holds back the newest requiredSegmentsForConfirmation (2) segments — so the re-decoded tail settles at ~2–3 segments (10–25 s of audio). On task: .translate every pass must also re-generate English for that whole tail (more tokens, lower confidence, more decoder fallbacks than same-language transcribe), so once the tail fills out, per-pass cost outgrows the loop's hard-coded ≥1 s new-audio cadence and partials lag — exactly a "fine at first, slow after 30–40 s" curve. The knob was never passed at our call site; both tasks silently ran the upstream default.

Fix

WhisperKitStreamingDecodePolicy.requiredSegmentsForConfirmation(translate:) — translate holds back 1 segment (clip point advances a segment sooner → steady-state re-decode window roughly halves), transcribe keeps the upstream default of 2. Passed explicitly in makeStreamHandle. Tests pin the per-task values, the ≥1 floor (0 would freeze half-heard words with no revision room), and that the translate threshold still satisfies the #222 confirmed-end advancement invariant (no 30 s-truncation regression).

Also fixes docs/WHISPERKIT_PILOT.md, which still documented withoutTimestamps: true — the exact configuration #222 removed — plus its stale "file transcription only" limitation.

Testing

  • swift test: 1937 tests green (3 new in WhisperKitStreamingDecodePolicyTests)
  • ./build.sh (WhisperKit default): clean, no new warnings
  • ⚠️ Needs a live pass: Russian dictation with translate on, 60–90 s continuous, watching whether partial cadence stays steady past the 40 s mark. Not merging until that's done.

Follow-ups (researched, out of scope here)

  • Bigger win available: transcribe natively + translate the text via the Apple Translation path (PR feat(translate): on-device text translation via Apple Translation framework (macOS 15+) #223's engine-agnostic mechanism, currently policy-gated to ASR-only engines). Constant low latency regardless of session length; tradeoff is the live preview showing the source language until paste.
  • Fork-side levers if needed later: the loop's hard-coded ≥1 s cadence, and the full-session buffer copy per pass (negligible below ~5 min).
  • MAK-72 still tracks the missing streaming-latency instrumentation.

🤖 Generated with Claude Code

…e confirmation lag

Streaming decode re-decodes everything after lastConfirmedSegmentEndSeconds
every pass, and AudioStreamTranscriber always holds back the newest
requiredSegmentsForConfirmation (2) segments. On task=.translate each pass
must re-GENERATE English for that whole unconfirmed tail (more tokens,
lower confidence, more fallbacks than same-language transcribe), so after
~30-40s of continuous speech the pass cost outgrows the loop's 1s
new-audio cadence and partials visibly lag.

Make the threshold task-aware (WhisperKitStreamingDecodePolicy
.requiredSegmentsForConfirmation(translate:)): translate holds back 1
segment, roughly halving the steady-state re-decode window; transcribe
keeps the upstream default of 2. Tests pin the values, the >=1 floor, and
that the translate threshold still satisfies the #222 confirmed-end
advancement invariant (no 30s truncation regression).

Also corrects docs/WHISPERKIT_PILOT.md, which still documented
withoutTimestamps: true (the exact config #222 removed) and 'file
transcription only'.

Co-Authored-By: Claude <noreply@anthropic.com>
@initcore0
initcore0 merged commit c438db0 into main Jul 31, 2026
10 checks passed
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.

1 participant