feat(sdk)!: pull OCI registry models via llmman - #1412
Open
Eric Curtin (ericcurtin) wants to merge 1 commit into
Open
feat(sdk)!: pull OCI registry models via llmman#1412Eric Curtin (ericcurtin) wants to merge 1 commit into
Eric Curtin (ericcurtin) wants to merge 1 commit into
Conversation
Replaces the hand-rolled Docker Registry HTTP API V2 client from qualcomm#1165 with a source that delegates acquisition to llmman, so GenieX stops maintaining registry protocol code it isn't in the business of owning. llmman already implements registry auth, both Docker `application/vnd.docker.ai.*` layouts, CNCF ModelPack, and content-addressed resume, and covers GHCR / quay / ECR / self-hosted registries that dockerhub.rs never did. GENIEX_HUB_DOCKER is renamed to GENIEX_HUB_LLMMAN, keeping value 5 so the ABI is unchanged; `--model-hub docker` becomes `--model-hub llmman`. BREAKING CHANGE: GENIEX_HUB_DOCKER is now GENIEX_HUB_LLMMAN, and `--model-hub docker|dockerhub` is now `--model-hub llmman|oci`. The enum value (5) and the FFI symbol table are unchanged, so only source compatibility breaks. Pulling an OCI reference now requires llmman installed with `llmman serve` running. Signed-off-by: Eric Curtin <eric.curtin@docker.com>
Contributor
|
Hi, Eric Curtin (@ericcurtin) thank you for your contribution. This idea is quite informative. GenieX indeed has no intention of maintaining duplicate OCI‑related protocol implementations. However, the current PR requires users to install |
Contributor
Author
|
Mengsheng Wu (@mengshengwu) we could create libllmman and you statically link that in. It's more maintenance on the GenieX end though. |
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
Replaces the Docker Hub source added in #1165 with one that delegates
OCI-registry acquisition to llmman,
talking to a running
llmman servedaemon.#1165 landed a 797-line hand-rolled Docker Registry HTTP API V2 client
(anonymous token exchange, content-negotiated manifest GET, config blob
GET, both
application/vnd.docker.ai.*layouts). That is registryprotocol code GenieX has no particular interest in owning, and it stopped
at Docker Hub — GHCR, quay, ECR, and self-hosted registries were all out
of scope, as was CNCF ModelPack (explicitly rejected with an error).
llmman already implements all of it, plus content-addressed resume. So:
sdk/model-manager/crates/core/src/source/llmman.rs(new, replacesdockerhub.rs): aModelSourcethat probesGET /api/version, streamsPOST /api/pull(NDJSON), then shells tollmman resolve --no-pullforthe resulting on-disk path and emits
FileSpec { BytesSource::LocalLink }.GENIEX_HUB_DOCKER→GENIEX_HUB_LLMMAN, keeping value 5. No symboladded or removed, so the CMake export list and the whole FFI table are
untouched.
mapping.rs:is_docker_hub_reference→is_llmman_reference,broadened to
ghcr.io/,quay.io/,gcr.io/,mcr.microsoft.com/,public.ecr.aws/, and anoci://scheme. Newllmman_store_name_from_referencederives theorg/repostore path,and
reference_has_tagkeeps a registry port from being read as a tag.HubDocker→HubLlmman, Python'docker'→'llmman'/'oci',Kotlin
DOCKER(5)→LLMMAN(5), per the FFI-update rule.Two design points worth review
1.
plan()now moves bytes for this one source. llmman does thedownloading, so by the time we can name the files they have already landed.
Rather than let a multi-gigabyte pull run with no progress bar,
ModelSourcegains a provided method
plan_with_progress(Option<&ProgressCallback>)that defaults to
plan(). OnlyLlmmanSourceoverrides it; no other sourceor test changed. llmman's aggregate
total/completedmap ontogeniex_FileProgress, and returningfalsestill cancels.2. Path resolution goes through the CLI, not HTTP.
llmman servedeliberately exposes no local path —
/api/showreturns only a digest andsize, and
/propshardcodes"model_path": "".llmman resolveis thedocumented interface for this (it's what llmman's own vLLM plugin uses), runs
in-process, and
--no-pullguarantees it only reports on bytes/api/pullalready fetched. So an llmman pull needs both the daemon reachable and the
binary on
PATH(orGENIEX_LLMMAN_BIN); each missing piece has its ownactionable error.
New
BytesSource::LocalLinkhard-links (falling back to copy) so a modelshared with llmman's store costs its bytes once rather than twice. It is
deliberately not used for
LocalFsSource, whose source is a user-owneddirectory they may edit in place.
Also fixed along the way: routing a bare name through
canonicalize_model_namewould have storedgeniex pull gemma3 --model-hub llmmanunderqualcomm/gemma3, colliding with the real AI Hub model. It nowuses
ai/, matching llmman's own bare-name expansion.geniex_model_queryreturns an error for this hub — answering it would meanperforming the entire download, and a registry tag names one artifact rather
than a set of GGUF quantizations.
Try it
llmman serve & geniex pull docker.io/ai/gemma3 geniex infer docker.io/ai/gemma3 geniex pull ghcr.io/org/model:v1 geniex pull ai/gemma3 --model-hub llmmanTest plan
cargo test --workspaceinsdk/model-manager— 186 unit tests(was 185) + every integration suite green.
crates/core/tests/llmman_pull.rs(7 tests): full pull against awiremock daemon + stub
llmmanbinary, asserting forwarded progress,the published
geniex.json, andst_ino/nlinkto prove the GGUF washard-linked rather than copied; blob-path naming; no daemon listening;
a non-llmman server on the port; in-band error at HTTP 200; a stream
that ends without
success; and callback cancellation.source::llmman(endpoint parsing, NDJSON linehandling, filename synthesis, safetensors rejection) and 5 in
mapping.cargo clippy --workspace --all-targets— 9 warnings, identical tomain; none in any touched file.cargo fmt --all --check— clean.gofmt -l cli/ bindings/go/— clean._Static_asserton all sevengeniex_HubSourcevalues and thegeniex_model_resolve_hubprototype.nmonlibgeniex_model.a— all 23geniex_model_*symbols stillexported, matching the CMake
/EXPORT:list unchanged.go build ./...forbindings/go+cli— not verified here (needsthe CMake-staged
sdk/pkg-geniextree). Changes are a constant renameplus comments, and
gofmtis clean.