pull: add Hugging Face org/repo[:quant] downloads and serve --hf - #623
Open
timtoole02 wants to merge 1 commit into
Open
pull: add Hugging Face org/repo[:quant] downloads and serve --hf#623timtoole02 wants to merge 1 commit into
timtoole02 wants to merge 1 commit into
Conversation
Camelid could only pull the ~30 curated catalog rows from the CLI; arbitrary Hugging Face GGUFs were reachable only through the Models page's experimental browse lane. This adds the CLI twin of that lane: camelid pull prism-ml/Ternary-Bonsai-27B-gguf:Q2_0 camelid serve --hf prism-ml/Ternary-Bonsai-27B-gguf:Q2_0 Any pull query containing '/' is a Hugging Face spec (curated ids never contain one); serve/chat --hf download-if-missing then load through the ordinary explicit-model path. Prerequisite for a Hugging Face Local Apps entry, whose "Use this model" snippet needs a one-line spec-addressable command. Support contract unchanged: this lane is experimental — unverified, no parity claim. A download path is not a support claim, and runnability is still decided at load time by the inspect-first typed-blocker flow, fail-closed. See D21. Reuse over reinvention: discovery goes through hf_browse's LFS-aware tree fetch (extracted as list_gguf_files_blocking); the download adopts the web installer's semantics (.part + rename promotion, resume, retries, stall detection, download ceiling). Stricter than the web install path in two places: repo ids are gated by fit_dims::is_safe_hf_component (the /catalog/fit contract, which install skips) and Windows reserved device stems are rejected. Selection fails closed rather than guessing: a multi-GGUF repo requires an explicit :quant; recognized labels must match exactly (:Q4_K never resolves to Q4_K_M) and unrecognized ones match on filename token boundaries (:F16 never picks BF16); mmproj companions and multi-part shards are unselectable; a same-named file with different bytes is an error, never an overwrite; and a stale oversized .part is discarded rather than resumed forever. guess_quant now matches on token boundaries and knows the _L/_XL/Q4_0_x_y variants — as a substring matcher it labeled Q6_K_L as "Q6_K", which made plain tags ambiguous in the standard bartowski layout and could silently substitute a different quantization. --hf is CLI-only (no env alias) and not a clap conflict with --model, so an exported CAMELID_MODEL cannot make it unusable and no inherited variable can start a download on desktop app open; a typed --hf wins at dispatch. serve --hf honors --max-download-bytes and downloads into the same directory the server scans (api::resolve_models_dir), so pulled files always appear in the Models page. Validation: cargo fmt --check, clippy --all-targets -D warnings, and cargo test --all-targets (2041 passed, 0 failed) all green on macOS; live end-to-end pull of unsloth/SmolLM2-135M-Instruct-GGUF:Q2_K exercised first download, size-verified skip, resume from a truncated partial, and stale-partial recovery. Docs updated in the same change: README quick start and pull-catalog sections, docs/CONFIGURATION.md ceiling table, DECISIONS.md D21, DOCS.md index. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 tasks
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
camelid pull org/repo[:quant]downloads any public Hugging Face repo that ships single-file, top-level GGUFs — the CLI twin of the Models page's "Experimental (Hugging Face)" browse lane. Any pull query containing/is a spec; curated ids never contain one, so existing pull behavior is untouched.serve --hfandchat --hfdownload-if-missing, then load through the ordinary explicit-model path.camelid pull --dry-runresolves and prints the target without moving bytes.:quant,mmprojcompanions and multi-part shards are unselectable, and a same-named file with different bytes is an error rather than an overwrite.src/hf_pull.rs; discovery and download reuse the existing audited paths rather than adding a second downloader.Why this change exists
Camelid could only pull the ~30 curated catalog rows from the terminal. Anything else required the web UI's Models page, so there was no one-line command to fetch and run an arbitrary GGUF.
This is also the prerequisite for listing Camelid in the Hugging Face Hub's "Use this model" dropdown (their Local Apps registry): that snippet has to be a single spec-addressable command, the way llama.cpp's
llama serve -hf org/repo:Q4_K_Mis. A separate PR tohuggingface/huggingface.jswill follow.No support-contract change. This lane is experimental — unverified, no parity claim. A download path is not a support claim, and runnability is still decided at load time by the inspect-first typed-blocker flow, fail-closed. Recorded as
DECISIONS.mdD21.Design notes
Reuse over reinvention. Discovery goes through
hf_browse's LFS-aware tree fetch (extracted fromrepo_gguf_filesaslist_gguf_files_blocking). The download adopts the web installer's semantics:.part+ rename promotion so a loadable GGUF never exists half-written, resume viacurl -C -, retries, stall detection, and the download ceiling enforced before and during transfer.Stricter than the web install path in two places: repo ids are gated by
fit_dims::is_safe_hf_component(the/catalog/fitcontract, which the install handler skips), and Windows reserved device stems are rejected. The latter is a local check for now —model_default::valid_local_model_filenameis shared with the web lane and hardening it there is filed as separate follow-up work.Quant labeling fix with blast radius beyond this feature.
hf_browse::guess_quantwas a plain substring matcher, so…-Q6_K_L.ggufwas labeledQ6_Kand…-Q4_0_8_8.ggufwas labeledQ4_0. In the standard bartowski layout that made plain tags ambiguous, and in a repo shipping only a superstring variant it could have silently substituted a different quantization. It now matches on token boundaries and knows the_L/_XL/Q4_0_x_yvariants. This also improves the advisory quant shown in the web UI's browse rows.Env-var safety.
--hfis CLI-only (no env alias) and deliberately not a clapconflicts_with = "model": because--modelcarriesenv = "CAMELID_MODEL", a hard conflict would make--hfunusable for anyone with that variable exported. A typed--hfsimply wins at dispatch. No inherited variable can start a multi-gigabyte download on desktop app open.Directory correctness.
serve --hfdownloads into the same directory the server scans (api::resolve_models_dir, nowpub), not a CWD-relative./models, so a pulled file always appears in the Models page in the shipped exe-dir layout.serve --hfalso honors--max-download-bytes, not just the env var.Validation
git diff --checkcargo fmt --all -- --checkcargo test --all-targets— 2041 passed, 0 failed (macOS;--all-featuresis omitted because it enables thecudafeature, which has no cudarc dep on this target — pre-existing, unrelated to this change)cargo clippy --all-targets -- -D warningscd frontend && npm run build— not run; this change touches no frontend sourcePublic-repo privacy check
bash scripts/check-public-scrub.shnode scripts/audit-evidence-bundle-privacy.mjs --strictSupport-contract check
COMPATIBILITY.md,STATUS.md, and/api/capabilitiesare untouchedThe user-facing wording for this lane is the web UI's, verbatim: "Experimental — unverified, no parity claim", followed by "A download path is not a support claim; whether this file loads is decided at load time, fail-closed." The post-download hint is hedged the same way ("loads only if the file's architecture is implemented; fails closed otherwise") rather than promising a working chat.
Evidence / artifacts
Live end-to-end run against
unsloth/SmolLM2-135M-Instruct-GGUF:Q2_K(88,201,792 bytes) covering all four download paths:.partpromoted by rename, exact byte count, no leftover partial..part, completed to exactly 88,201,792 bytes..partis discarded and re-downloaded fresh rather than failing "re-run to resume" forever.Resolution behavior against
prism-ml/Ternary-Bonsai-27B-gguf(6 selectable GGUFs plus 2 mmproj companions): a bare spec lists the files with selectable tags and refuses to guess;:Q2_0selectsQ2_0and notPQ2_0;:pq2_0andhf.co/…URL prefixes both resolve; an unknown repo produces a clean error naming the anonymous-download limitation.22 unit tests cover spec parsing, selection, the destination trichotomy, ceiling parsing, and the download pre-gates. One invariant test asserts that every tag the CLI prints in its listing actually selects that exact file — the listing is the tool's own suggestion surface, so a printed tag that errors would be a dead end.
The implementation was reviewed by a multi-agent adversarial pass across correctness, security, product-policy, and test-coverage lenses; 20 confirmed findings were fixed before this commit, including the two
.partbugs, the quant-labeling collision, theCAMELID_MODELconflict, the models-dir divergence, and a parser panic on a multi-byte character straddling a URL-prefix boundary.Docs impact
README.md— quick start gains theserve --hfone-liner with the experimental-lane caveat; thecamelid pullcatalog section notes that a/query resolves as a Hugging Face spec.docs/CONFIGURATION.md— download-ceiling section documents which lanes honor the flag versus the env var, and the anonymous/gated-repo limitation.DECISIONS.md— new D21 recording the lane, its fail-closed selection rules, and what it explicitly does not decide (no sha256 pinning for arbitrary files, no multi-part or vision-companion support).DOCS.md— decision-log index updated to D21.COMPATIBILITY.md,STATUS.md,SUPPORT_MATRIX_v0.1.md,ROADMAP.md— unchanged, deliberately.🤖 Generated with Claude Code