Summary
docling's native (openai-whisper) ASR backend forwards almost none of whisper's decoding parameters to transcribe(). In particular beam_size, condition_on_previous_text, and temperature are not fields on InlineAsrNativeWhisperOptions and silently fall back to whisper's defaults. On long-form audio those defaults trigger whisper's well-known runaway-repetition failure mode — the model gets stuck re-decoding and transcription slows to roughly real-time or never finishes. Because docling exposes no override, there is no workaround through the docling API.
Root cause (isolated empirically)
I tested large-v3 on an 18.7-min clean, single-speaker TED talk (TED-LIUM, Elizabeth Gilbert — deliberately not "hard" audio), driving raw whisper.transcribe() directly while holding everything at docling's settings (auto-detected language, condition_on_previous_text=True, word_timestamps=False) and varying only beam_size:
beam_size |
spirals (4 runs each) |
completion time |
None — greedy decode (whisper's default, i.e. what docling uses) |
2 / 4 hung |
~150 s (slow) |
1 — beam search |
0 / 4 |
~90–150 s (faster) |
The dominant, isolated trigger is whisper's default greedy decoding (beam_size=None). Beam search (even width 1) terminates sequences differently, avoids the runaway repetition, and sidesteps the costly temperature-retry fallback — so it is both more reliable and faster. The failure is non-deterministic (whisper's temperature fallback samples without a fixed seed), which is why it doesn't reproduce on every run — but at the greedy default the rate is high enough to make the native backend unreliable for long-form.
condition_on_previous_text=False is whisper's documented mitigation for this repetition mode (it stops one bad window from seeding the next); I didn't isolate it independently here because beam_size >= 1 already prevented the spiral, but it's worth exposing alongside.
Additional data:
- Reproduces on hard audio too: native
tiny on a ~19.8-min earnings22 clip couldn't finish a single pass in 17+ minutes (RTF ≈ 1×).
- For contrast, the WhisperS2T (CTranslate2) backend transcribes the same clips in seconds and never exhibits this, because it doesn't use this decode path. (Transcription quality not assessed here.)
What docling currently does
In docling/pipeline/asr_pipeline.py (native path):
result = self.model.transcribe(str(fpath), verbose=self.verbose, word_timestamps=self.word_timestamps)
Only verbose and word_timestamps are forwarded. beam_size, condition_on_previous_text, and temperature are not fields on InlineAsrNativeWhisperOptions, so they take whisper's defaults with no way to override.
Requested change
Expose whisper's decoding parameters on InlineAsrNativeWhisperOptions and forward them in the native transcribe() call — at minimum:
beam_size — the most impactful; consider defaulting to 1+ rather than greedy.
condition_on_previous_text
temperature
(language would be a useful nice-to-have too — docling currently auto-detects on every call.)
Secondary
docling's native option also defaults word_timestamps=True, overriding whisper's own default of False and adding DTW word-alignment cost out of the box. Suggest exposing it but defaulting to whisper's False.
Discovered while working on #2938.
Summary
docling's native (openai-whisper) ASR backend forwards almost none of whisper's decoding parameters to
transcribe(). In particularbeam_size,condition_on_previous_text, andtemperatureare not fields onInlineAsrNativeWhisperOptionsand silently fall back to whisper's defaults. On long-form audio those defaults trigger whisper's well-known runaway-repetition failure mode — the model gets stuck re-decoding and transcription slows to roughly real-time or never finishes. Because docling exposes no override, there is no workaround through the docling API.Root cause (isolated empirically)
I tested
large-v3on an 18.7-min clean, single-speaker TED talk (TED-LIUM, Elizabeth Gilbert — deliberately not "hard" audio), driving rawwhisper.transcribe()directly while holding everything at docling's settings (auto-detectedlanguage,condition_on_previous_text=True,word_timestamps=False) and varying onlybeam_size:beam_sizeNone— greedy decode (whisper's default, i.e. what docling uses)1— beam searchThe dominant, isolated trigger is whisper's default greedy decoding (
beam_size=None). Beam search (even width 1) terminates sequences differently, avoids the runaway repetition, and sidesteps the costly temperature-retry fallback — so it is both more reliable and faster. The failure is non-deterministic (whisper's temperature fallback samples without a fixed seed), which is why it doesn't reproduce on every run — but at the greedy default the rate is high enough to make the native backend unreliable for long-form.condition_on_previous_text=Falseis whisper's documented mitigation for this repetition mode (it stops one bad window from seeding the next); I didn't isolate it independently here becausebeam_size >= 1already prevented the spiral, but it's worth exposing alongside.Additional data:
tinyon a ~19.8-minearnings22clip couldn't finish a single pass in 17+ minutes (RTF ≈ 1×).What docling currently does
In
docling/pipeline/asr_pipeline.py(native path):Only
verboseandword_timestampsare forwarded.beam_size,condition_on_previous_text, andtemperatureare not fields onInlineAsrNativeWhisperOptions, so they take whisper's defaults with no way to override.Requested change
Expose whisper's decoding parameters on
InlineAsrNativeWhisperOptionsand forward them in the nativetranscribe()call — at minimum:beam_size— the most impactful; consider defaulting to1+ rather than greedy.condition_on_previous_texttemperature(
languagewould be a useful nice-to-have too — docling currently auto-detects on every call.)Secondary
docling's native option also defaults
word_timestamps=True, overriding whisper's own default ofFalseand adding DTW word-alignment cost out of the box. Suggest exposing it but defaulting to whisper'sFalse.Discovered while working on #2938.