Repository navigation
fix(models): download the AI models family by family - #473
Merged
Merged
Conversation
"Download AI models" (POST /v1/models/assets/downloads) put the assets of every missing model family into one download, and the first file whose HEAD request failed ended it. So one unreachable release (a family whose tag is not published yet, a failed workflow run, a blocked host) meant nothing was downloaded at all, not even the search model for a new user. The combined download now goes family by family. A family whose files cannot be resolved or fetched is skipped, and the others still download. The job still ends in `error` when any family failed: `error_messages` holds one "<family name>: <reason>" per failed family, and `error` names each of them and says what to do. The plugin shows that under "Not all AI models could be downloaded". A `Family` enum is now the one place for each family's id, name, size estimate and readiness, which `GET /v1/models/assets` reads too (same JSON). The single-family routes (CLIP, BioCLIP, AuraFace) share the split-out HEAD pass and transfer, and keep their all-or-nothing behaviour and messages. Sizes now come from the Content-Length header. reqwest's content_length() is the length of the body it received, which is 0 for a HEAD response (seanmonstar/reqwest#1814), so every download here ran with a progress total of 0 until it had finished. Tested against a local fake release server: a missing family, a family whose transfer breaks part-way, every family landing, and the single-model path, plus contract tests for the /v1/models/assets shape. Docs-Reviewed: Credits.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HaMMvtwbAwMWB9jcpdCc3T
From review:
- The summary's closing advice only named the network, but a family can
also fail on the disk ("failed to create model directory", "failed
writing"). It now names both: reaching github.com, and the backend
being able to write to its model folder.
- Troubleshooting step 1 still said an installation with the search model
downloads only the species one, which stopped being true when the
quality and face models became part of the download.
- Step 2 claimed every model has an indicator in the Plug-in Manager. The
quality model has none, so it now says that every model the dialog does
not name is ready, and that "All AI models are ready" turns green once
the last one is in.
Also puts the test module's imports in rustfmt's order: they were moved
with the fake release server and not re-sorted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HaMMvtwbAwMWB9jcpdCc3T
…wnload #471 added the `face_state` family ("Eye and expression analysis", `face-state-assets-v1`) as one more `need_*` flag in the combined download, which this branch replaced with per-family `Family` entries. It becomes `Family::FaceState` here, with #471's size estimate, release tag and assets, so a family that is not published yet (the case that started this branch) is skipped and named while the others still download. - routes/assets.rs: `Family::FaceState` with id `face_state`, name "Eye and expression analysis", `FACE_STATE_APPROX_BYTES`, `state.face_state.is_cached()` and `face_state_assets`. - tests/contract.rs: the /v1/models/assets id list includes `face_state`. - Getting-Started.md: main's text (about 4.2 GB, with the eye and expression model). - Troubleshooting.md: the §3 intro names the fifth model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HaMMvtwbAwMWB9jcpdCc3T
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.
Summary
Download AI models (
POST /v1/models/assets/downloads) put the files of every missing model family into one list and handed it todownload_url_assets. That function sent a HEAD request per file and stopped the whole job at the first failure. So one unreachable release meant nothing downloaded, not even the search model for a new user. A release can be unreachable when its tag is not published yet, when amodel-assets-*.ymlrun failed, or when a host is blocked.Now each family downloads on its own:
errorwhen any family failed:error_messagesholds one"<family name>: <reason>"per failed family.errornames each failed family and says what to do. The status staysdownloadinguntil every family has been tried.totalcovers only what will be fetched. A family that fails part-way takes the bytes it will not fetch out oftotal, so the bar still ends full.resolve_sizes) and transfer (fetch_assets). They stay all-or-nothing, with the same messages. A test pins that.The progress total was 0
The HEAD pass used
resp.content_length(). reqwest computes it from the received body, and a HEAD response has none, so it is always 0 (seanmonstar/reqwest#1814). Every download on this machinery therefore hadtotal = 0untilset_done()filled it in at the end. Sizes now come from theContent-Lengthheader. Switching back tocontent_length()makes 3 of the new tests fail (left: 0, right: 57344).Changes
routes/clip.rsdownload_url_assetsintoresolve_sizes,fetch_assetsandrelease_urls.ModelDownloadStatusgetserror_messages, omitted from the JSON when empty so the other download routes answer exactly as before, plusset_errors.routes/assets.rsFamilyenum is the one place for each family's id, human name, size estimate and readiness.GET /v1/models/assetsuses it and returns the same JSON, now pinned by a contract test.download_familiesis the per-family loop, andfailure_summarybuilds the user-facing text.mainmerged in, feat(cull): eye focus, eye state, smile and gaze per face #471'sface_stateisFamily::FaceState: "Eye and expression analysis",face-state-assets-v1.src/fake_release.rs(#[cfg(test)]): a local axum stand-in for GitHub release downloads with three behaviours: files that answer, a 404, and HEAD-OK/GET-502. It sits outsideroutes/becausecheck-docs.pyreads every.route(there as an endpoint.APISearchIndex.luastartAssetDownload): all three callers end in this loop: the Plug-in Manager button, the setup wizard and the run preflight. The loop already showed the backend'serror. Whenerror_messagesis non-empty, the headline now reads Not all AI models could be downloaded. The Plug-in Manager's 5-second/v1/models/assetspoll picks up what landed.Dev-Backend-API.md: the assets download section.Troubleshooting.md: §3 resolution steps, and the intro now lists all five models.server-rs/README.md, and theDev-Server-README.mdgenerated from it: the BioCLIP 404 note.Getting-Started.md:main's text, about 4.2 GB.Credits.mdis still accurate (Docs-Reviewedtrailer).Testing
3292f52, the merged head:cargo-test(cargo test --workspacewithLRG_REQUIRE_GOLDENS=face),format-lint,format-lint-rust,docs-checkandbusted.totalshrinkscompletedGET /v1/models/assetsshape (five families, now includingface_state), and for noerror_messageswithout failures.cargo fmt --checkandcargo clippy --workspace --all-targets -- -D warningsare clean.stylua --check,luacheck(0/0) andbusted(614) pass.scripts/check-docs.pyexits 0./v1/models/assetslisted five families withmissing_approx_bytes: 977000000.bioclip, face, face_stateand skipped each one with its own reason.error, as the plugin shows it:Notes
routes/llm.rs(the GGUF download) has the samecontent_length()-on-HEAD bug in its own loop, so its progress total is 0 as well.HTTP 401/403 … the release may not exist yet,resolve_sizescould fall back to a one-byte ranged GET, which pre-signed GET URLs accept. The download counts on the asset releases suggest HEAD works outside the sandbox, so I left it out.🤖 Generated with Claude Code
https://claude.ai/code/session_01HaMMvtwbAwMWB9jcpdCc3T