* fix(dub): surface real ASR/model-load failures instead of dropping the stream (#255)
When the transcribe SSE stream died before emitting any event, the UI showed a
misleading generic "Transcribe stream dropped before emitting any segments.
Likely ASR backend failed to load" — hiding the real cause (e.g. a faster-
whisper/CTranslate2 cuDNN load failure, or a missing pkg_resources).
The per-chunk transcribe was already wrapped, but two preflight/setup calls in
the stream generator were not — if either raised, the connection dropped with
no structured error event:
- `get_model()` (preflight) — now wrapped; failures emit a structured `error`
event built via `core.failure.build_failure` (sanitized reason + actionable
hint, e.g. the pkg_resources→setuptools hint).
- `offload_tts_for_asr()` — now non-fatal; an offload hiccup logs and continues
rather than killing the stream.
- The empty-segments guard now sanitizes each chunk error (no home-path/token
leakage) and appends the recognized-failure-class hint.
Adds a regression test: a raising `get_model()` must yield a structured `error`
SSE event carrying the real message, not a dropped connection.
Does NOT close#255 — this makes the underlying cause visible (pending the
reporter's backend log) rather than asserting a specific Windows-CUDA fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(dub): drive transcribe-stream gen directly (avoid cross-loop teardown)
The regression test for #255 hit the SSE streaming endpoint through TestClient,
whose lifespan created an asyncio Queue bound to a different event loop than the
streaming request — erroring at teardown in the full-suite run. Drive the
route's async generator directly instead: the preflight-error path yields a
single event with no executor/Queue, so it stays isolated from any app loop.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>