feat: resolve oci:// model ids via llmman serve - #920
Open
ericcurtin wants to merge 1 commit into
Open
Conversation
Lets --model-id point at a model published as a CNCF ModelPack OCI
artifact:
text-embeddings-router --model-id oci://ghcr.io/org/model:tag
Model distribution is increasingly moving to OCI registries, which lets
a deployment reuse the registry, credentials, mirroring and air-gap
tooling it already has for container images.
Acquisition is delegated to a running `llmman serve`, which already
implements the ModelPack media types, registry auth, resumable blob
download and a content-addressed store. Two pieces are needed because
the daemon deliberately exposes no local path: POST /api/pull streams
the download as NDJSON (so a multi-gigabyte fetch is not silent, and an
error arriving in-band at HTTP 200 is caught), then
`llmman resolve --no-pull` reports where the bytes landed.
The branch sits alongside the existing local-directory check in run(),
so the extracted directory is loaded exactly like any other local model.
api_repo is None for it, as for a local model, since there is no Hub repo
behind an OCI artifact.
An explicit oci:// scheme is required rather than sniffing a bare
registry/name:tag: that shape is indistinguishable from a HuggingFace
repo id, so guessing would silently hijack existing deployments.
reqwest gains the json/stream features and tokio the process feature;
both crates were already dependencies.
Signed-off-by: Eric Curtin <eric.curtin@docker.com>
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.
What does this PR do?
Adds an
oci://scheme so a model published as a CNCF ModelPack artifact can be used anywhere a Hugging Face repo id can:Model distribution is increasingly moving to OCI registries -- the same registries, credentials, mirroring and air-gap tooling a deployment already uses for container images. This is usually easier to run air-gapped than reaching the Hub.
Implementation
One branch in
run()inrouter/src/lib.rs, alongside the existing local-directory check:The extracted directory is then loaded exactly like any other local model --
config.json,get_backend_model_type, tokenizer and dense-module discovery all operate on it unchanged.api_repoisNone, as it already is for a local model, since there is no Hub repo behind an OCI artifact; the1_Dense/ dense-path lookups fall back to what is on disk.New
router/src/oci.rsdelegates acquisition to a runningllmman serverather than reimplementing registry handling: llmman already implements the ModelPack media types, registry auth, resumable blob download and a content-addressed store. Two pieces are needed, because the daemon deliberately exposes no local path:GET /api/versionprobes reachability and identity -- a server answering without aversionfield is reported as "not an llmman daemon", worth distinguishing from nothing listening.POST /api/pullstreams newline-delimited JSON so a multi-gigabyte fetch is not silent. An error arrives in-band at HTTP 200, and a stream that simply ends withoutsuccessis also a failure -- both are errors, not a completed pull.llmman resolve --no-pullthen reports where the bytes landed.--no-pullguarantees it only reports on what/api/pullalready fetched, keeping the daemon the only thing that touches the network.LLMMAN_HOSTis honoured with the same parsing llmman's own clients use, including rewriting a wildcard bind (0.0.0.0,[::]) to loopback by value rather than spelling, so an expanded IPv6 form is caught too. A pull needs both the daemon reachable and the binary onPATH(orTEI_LLMMAN_BIN); each missing piece has its own actionable error, and neither is required unless anoci://id is used.Dependency changes
No new crates.
reqwest(already a router dependency) gainsjson+stream;tokiogainsprocess;tempfileis added as a dev-dependency for the tests.Design notes
registry/name:tagis indistinguishable from a Hugging Face repo id (BAAI/bge-large-en-v1.5); guessing would silently hijack existing--model-id org/modeldeployments. Every other id shape reaches exactly the branch it did before.resolvestdout is used; unknown JSON fields are ignored so the contract can grow.Testing
14 unit tests in
router::oci, all executed and passing:OCI://) and the empty-scheme edge cases3:///hf://are not claimed -- the regression that matters moststrip_schemeround-tripsLLMMAN_HOSTform (bare host,host:port, scheme prefix, trailing path, quoted), and wildcard-to-loopback rewriting incl.[0:0:0:0:0:0:0:0]success, byte progress, tolerance of a non-JSON diagnostic and blank linescargo check -p text-embeddings-routercleancargo clippy -p text-embeddings-router --lib --tests-- no warningsrustfmt --check router/src/oci.rsclean. Notecargo fmtcannot run repo-wide here: it fails onrouter/src/grpc/pb.rs(generated at build time, absent in a fresh checkout) onmaintoo, unrelated to this change.Not verified here, flagged rather than implied: no end-to-end serve against a live
llmman servebacked by a real registry. The HTTP client is covered at the line-protocol level rather than against a stub server.Before submitting
docs/if wanted.