feat(settings): ask before downloading a newly selected model - #152
Merged
Conversation
Saving a transcription model change queued a download as a side effect. That download runs on the GPU lane, the same lane that serves live transcription, so on a single-card host it could sit in front of a meeting; nothing in the UI said it had started, and the setup wizard's promise to "check progress later in Settings" was never honoured. Preparation is now explicit. Selecting a model that is not on the server prompts for a choice: fetch it now so it is ready for the next recording, or leave it to the lazy fetch on first use, which delays live transcription and Meeting Edge until the download completes. Selecting a model already on disk saves silently, so the prompt only appears when there is something to download. A new admin-only POST /system/models/prepare carries the request, with targets for the active selection, the core batch, and each ONNX ASR model, so a single missing row in Model dependencies can be repaired on its own. It refuses a second request with 409 while one is in flight, and resolves the model from the admin's own settings rather than the install config, since the transcription keys are user-scoped and reading config alone would prepare the install default instead. Model dependencies gains a Download action on every missing row plus a live progress strip, so declining the prompt is recoverable and a running preparation is visible however it was started. Saving settings through the API no longer prepares models for any caller. Lazy fetch on first use still covers that path. Refs: docs/DEPLOYMENT.md, docs/ADMIN.md, docs/USAGE.md
Canary was reported as missing however many times it was downloaded. The status check matched the Nojoin model id against Hugging Face cache directory names, but onnx-asr caches nemo-canary-1b-v2 under the repo istupakov/canary-1b-v2-onnx, so the id never appeared in the directory name. Parakeet escaped the bug only because its id happens to be a substring of its repo name. Match a fragment of the repo name instead, held in one named map with the divergence written down so it is not "corrected" back to the model id later. Resolving the id through onnx-asr would be exact, but that would pull the ASR stack into the API process, which the torch boundary test exists to prevent. This also unblocks deletion and the admin health card, both of which read the same status: a model reported as missing has no resolvable path to delete, and the transcription component showed Canary as uncached. Refs: backend/preload_models.py
Deleting a model failed on every Docker install with EROFS. The API mounts the shared model volume read-only, by design, so the delete could never have worked from there: the failure was structural rather than a bad path. Dispatch the delete to the io lane, which mounts that volume read-write along with the other worker lanes. The task resolves the path itself rather than accepting one from the API, because the same volume is mounted at a different path in each container, and a delete sink that trusts a caller-supplied path is worth not building. It returns a status dict instead of raising, since the JSON serialiser would not carry the difference between "not found" and "refused" across the boundary. The request still waits for the result, so the UI can keep refreshing model status the moment it returns, with a 60 second ceiling that fails the request rather than holding a thread when no worker answers. Deleting a model now needs a running worker; that is documented. Refs: docs/DEPLOYMENT.md, docs/ADMIN.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Description
Selecting a transcription model queued a download as a side effect of saving the setting. That download runs on the GPU lane, the same lane that serves live transcription, so on a single-card host it could sit in front of a meeting. Nothing in the UI said it had started, and the setup wizard's promise to "check progress later in Settings" was never honoured by anything.
Preparation is now explicit. Selecting a model that is not on the server prompts for a choice: fetch it now so it is ready for the next recording, or leave it to the lazy fetch on first use, which delays live transcription and Meeting Edge until the download completes. Selecting a model already on disk saves silently, so the prompt only appears when there is something to download.
A new admin-only
POST /system/models/preparecarries the request, with targets for the active selection, the core batch, and each ONNX ASR model, so a single missing row in Model dependencies can be repaired on its own. It refuses a second request with 409 while one is in flight, and resolves the model from the admin's own settings rather than the install config, since the transcription keys are user-scoped and reading config alone would prepare the install default instead. Model dependencies gains a Download action on every missing row plus a live progress strip, so declining the prompt is recoverable and a running preparation is visible however it was started.Two pre-existing bugs surfaced while validating this against a real install and are fixed here, since the new Download button is unusable without them.
Canary was reported as missing however many times it was downloaded. The status check matched the Nojoin model id against Hugging Face cache directory names, but onnx-asr caches
nemo-canary-1b-v2under the repoistupakov/canary-1b-v2-onnx, so the id never appeared in the directory name. Parakeet escaped the bug only because its id happens to be a substring of its repo name. Matching a fragment of the repo name instead also unblocks deletion and the admin health card, both of which read the same status. Resolving the id through onnx-asr would be exact, but that would pull the ASR stack into the API process, which the torch boundary test exists to prevent.Deleting a model failed on every Docker install with EROFS. The API mounts the shared model volume read-only, by design, so the delete could never have worked from there. The delete now goes to the io lane, which mounts that volume read-write. The task resolves the path itself rather than accepting one from the API, because the same volume is mounted at a different path in each container, and a delete sink that trusts a caller-supplied path is worth not building. It returns a status dict rather than raising, since the JSON serialiser would not carry the difference between "not found" and "refused" across the boundary. The request still waits for the result so the UI keeps refreshing status the moment it returns, with a 60 second ceiling that fails the request rather than holding a thread when no worker answers.
No new dependencies.
Behaviour changes worth noting
Type of change
Checks run
source .venv/bin/activate && pytestpython scripts/check.py(Ruff lint, format check, mypy, doc and Alembic validators)cd frontend && npm run lintcd frontend && npm run testcd frontend && npm run buildpython3 scripts/validate_docs.pypython3 scripts/validate_alembic.pyscripts/check.pyreported OK across lint, format, whitespace, filesize, heldpins, typecheck, docs, alembic and tests, with 1122 backend tests passing. Frontend: 296 tests across 52 files, lint clean, build clean.Migration impact
Documentation impact
docs/DEPLOYMENT.md(worker startup: preparation is opt-in, deletion needs a worker, the read-only mount),docs/ADMIN.md(the new admin actions),docs/USAGE.md(the prompt in Settings).Security impact
Both new endpoints are admin-only. The model deletion task takes a model identity from a fixed allowlist rather than a path, so nothing caller-supplied reaches the filesystem sink, and the API keeps its read-only mount on the model volume.
Manual verification
Verified against a live install on the deployment host:
models--istupakov--canary-1b-v2-onnxwhile the UI reported it Missing, which is what led to the detection fix. Confirmed the corrected matcher reports both ONNX models present using those exact directory names.Still pending, because the running containers predate these commits and both fixes need the api and worker images rebuilt:
No capture, recording context-menu, or migration changes in this PR.