Skip to content

[#145] Surface a model-download indicator when an alignment model is fetched mid-transcription - #149

Merged
julien731 merged 6 commits into
mainfrom
feature/145-align-model-download-indicator
Sep 4, 2026
Merged

julien731 merged 6 commits into
mainfrom
feature/145-align-model-download-indicator

Conversation

@julien731

Copy link
Copy Markdown
Member

Closes #145

Summary

Follow-up to #141. Pre-provisioning covers a configured set of HuggingFace alignment models (default ["th"]), but auto-detect can land on an HF-backed language that wasn't pre-provisioned, or a language outside align_languages can be used. In those cases whisperx.load_align_model downloads the model inside the align stage, leaving the job frozen at stage=aligning, progress=50 ("Aligning timestamps… 50%") — indistinguishable from a hang.

This surfaces a distinct downloading_align_model job stage while such a model is being fetched, so the progress bar reads "Downloading alignment model…" instead of a frozen "Aligning…". The ALIGNMENT_TIMEOUT_SEC watchdog remains the safety net.

Approach

  • New job stage JobStage.DOWNLOADING_ALIGN_MODEL (issue option 1), preferred over reusing provisioning's separate DownloadState lifecycle or gating up front (the detected language is unknown until transcription runs).
  • Cache-presence probe align_models.align_model_cached(repo_id) uses huggingface_hub.try_to_load_from_cache(repo_id, "config.json") (lazy import, preserving the module's import-light contract; mirrors provisioning._snapshot_downloader). Any failure — including huggingface_hub being absent in the lightweight CI env — degrades to "assume present" (debug-logged), which only omits the indicator and never blocks alignment.
  • Only HF-backed models get the indicator; torch-native models (en/fr/de/es/it, model_name=None, shared ~/.cache/torch) are unaffected.
  • _load_align_model_watchdogged wraps both align sites (single-language and multilingual): flips to downloading_align_model when the HF-backed model isn't cached, runs the watchdogged load, and always resets to aligning before returning — so a stalled or failed fetch never sticks on the download stage.
  • UI labels added to the web SPA (transcript-viewer.js) and the macOS app (JobPresentation.swift).

Plan: docs/plans/145-align-model-download-indicator.md.

Verification

  • Python unit tests: tests/unit/test_align_models.py, tests/unit/test_transcriber.py, tests/unit/test_provisioning.py — all pass (79). New coverage: cache-probe outcomes (cached / not-cached / known-missing sentinel / hf_hub error / hf_hub absent) and the stage-flip behavior (HF-backed uncached surfaces downloading_align_model then aligning; cached and torch-native do not).
  • macOS unit suite: swift run MeetingTranscriberKitTests — 272 passed, incl. the new JobStagePresentation label assertion.
  • ruff check + ruff format --check clean.
  • Architect plan review and code review both passed (no Critical/Major).

@julien731 julien731 added the feature New feature or enhancement label Sep 4, 2026
@julien731 julien731 self-assigned this Sep 4, 2026
@julien731
julien731 merged commit f867f70 into main Sep 4, 2026
5 checks passed
@julien731
julien731 deleted the feature/145-align-model-download-indicator branch September 4, 2026 10:37
@julien731

Copy link
Copy Markdown
Member Author

QA Confidence Verdict — PR #149 (issue #145)

Read-only verification against the derived ACs/truths. Scope: commits e2693e1..ec38938 atop 3968e89. Python unit suite run locally: 64 passed (test_align_models.py + test_transcriber.py).

What Was Verified

Derived ACs

# AC Verdict Evidence
1 HF-backed, not cached → downloading_align_model then back to aligning PASS _load_align_model_watchdogged flips stage when bool(align_model_name) and not align_model_cached(...), resets to aligning after load. Test test_hf_backed_uncached_surfaces_download_then_aligning asserts exact sequence ["downloading_align_model", "aligning"].
2 Cached → no download stage (unchanged) PASS test_hf_backed_cached_does_not_flip_stage asserts stage absent.
3 Torch-native (model_name=None) never triggers PASS bool(None) short-circuits before the probe. test_torch_native_skips_probe_and_stage asserts stage absent and align_model_cached never called.
4 Both paths surface the stage PASS Single-language (_run_single_language_transcription, align_progress=50) and multilingual (_align_multilingual_segments, align_progress=82) both route loads through the shared helper; _align_multilingual_segments now threads job_id from _run_multilingual_transcription.
5 Human-readable label in web + macOS PASS transcript-viewer.js: downloading_align_model: 'Downloading alignment model...'. JobPresentation.swift: "downloading_align_model": "Downloading alignment model". macOS assertion added in UploadValidationTests.swift.
6 Watchdog remains; never stuck on download stage PASS Helper still calls _call_with_timeout(load_fn, ALIGNMENT_TIMEOUT_SEC, ...). _call_with_timeout never propagates (catches into return value), so the unconditional reset-to-aligning always runs on ok/timeout/error. Timeout in multilingual raises after reset.

Truths

  • Cache probe never blocks/fails alignment — PASS. align_model_cached wraps the lazy huggingface_hub import + probe in a bare except, degrading to True ("assume present"). Covered by test_probe_error_degrades_to_present and test_missing_huggingface_hub_degrades_to_present. _CACHED_NO_EXIST sentinel (non-str) correctly treated as absent (test_known_missing_sentinel_is_absent).
  • No transcript-text degradation — PASS. Load path unchanged (same whisperx.load_align_model under the same watchdog); only stage/progress reporting and label maps added.

All checks are programmatic (unit tests) + code inspection. None via Playwright — see Risk Areas.

What Needs Human Eyes

  • Live stage transition UX: the downloading_align_model → aligning flip and its label rendering were not exercised against a running app (requires a real uncached HF-backed language download + full ML stack). Worth one manual run with an auto-detected non-pre-provisioned HF language (e.g. ja/ko) to confirm the progress bar actually reads "Downloading alignment model…".
  • Copy: web uses trailing ellipsis ("Downloading alignment model..."), macOS omits it — consistent with each surface's existing convention, but confirm intended.
  • macOS suite: not run in this env (no Swift toolchain invoked here); PR claims 272 passed. Recommend confirming CI green.

Risk Areas

  • Behavior depends on huggingface_hub.try_to_load_from_cache cache-dir resolution matching the bundled service's HF_HOME (asserted by construction in the docstring, not tested end-to-end). Low risk — safe-degrade path only omits the indicator, never blocks.
  • The download stage itself is inherently untestable without network + models; coverage relies on stubbing align_model_cached and the load callable. Acceptable given the safe-degrade design.

Suggested QA Focus

  • Quick glance: web + macOS label rendering (static maps, low risk).
  • One thorough run: upload audio in an HF-backed, non-pre-provisioned language with a cold cache; confirm the bar shows the download label mid-align, then returns to "Aligning…", and that a killed/stalled network still ends (watchdog → segment-level timestamps) without sticking on downloading_align_model.

Overall confidence: HIGH — all 6 ACs and both truths verified against code + tests; residual gaps are live-UX/visual only. No truth fails; no spec drift (implements issue option 1 as documented in the plan).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Surface a model-download indicator when an alignment model is fetched mid-transcription

1 participant