feat: support oci:// model URIs via llmman serve - #2572
Open
ericcurtin wants to merge 1 commit into
Open
Conversation
Lets a model's uri point at a model published as a CNCF ModelPack OCI
artifact:
{"name": "my-model", "parameters": {"uri": "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. The daemon does the pull (POST
/api/pull, streamed as NDJSON so a multi-gigabyte fetch is not silent,
and an error arriving in-band at HTTP 200 is caught) but deliberately
exposes no local path, so `llmman resolve --no-pull` reports where the
bytes landed. The client is stdlib-only, so no new dependency.
get_model_uri is the single dispatch point. Resolution runs before the
urlparse, so the well-known-filename search and to_absolute_path apply
to the extracted directory unchanged; llmman returns an absolute path,
which os.path.join already passes through. The blocking pull runs in an
executor so the event loop is not stalled.
Every other uri shape reaches exactly the branch it did before: s3://,
gs:// and other rclone remotes are still returned untouched for the
runtime to handle, and file:// and bare paths are unaffected.
An explicit oci:// scheme is required rather than sniffing a bare
registry/name:tag, which is indistinguishable from other reference
shapes.
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
Adds an
oci://scheme toparameters.uri, so a model published as a CNCF ModelPack artifact can be served directly:{ "name": "my-model", "implementation": "mlserver_sklearn.SKLearnModel", "parameters": {"uri": "oci://ghcr.io/org/model:tag"} }Model distribution is increasingly moving to OCI registries -- the same registries, credentials, mirroring and air-gap tooling a deployment already uses for container images.
How
get_model_uriinmlserver/utils.pyis the single dispatch point. Resolution runs before theurlparse, so the well-known-filename search andto_absolute_pathapply to the extracted directory unchanged (llmman returns an absolute path, whichos.path.joinalready passes through). The blocking pull runs in an executor so the event loop is not stalled.Every other uri shape reaches exactly the branch it did before:
s3://,gs://and other rclone remotes are still returned untouched for the runtime to handle;file://and bare relative/absolute paths are unaffected.tests/test_utils.pypasses unchanged, which pins that.Acquisition is delegated to a running
llmman serverather than hand-rolled: llmman already implements the ModelPack media types, registry auth, resumable blob download and a content-addressed store.New
mlserver/llmman.pyis the daemon client, stdlib-only (urllib), no new dependency: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 NDJSON so a multi-gigabyte fetch is not silent. An error arrives in-band at HTTP 200, and a stream that ends withoutsuccessis also a failure -- both are errors, not a completed pull.llmman resolve --no-pullreports where the bytes landed;--no-pullkeeps the daemon the only thing that touches the network.LLMMAN_HOSTis honoured with llmman's own parsing, including rewriting a wildcard bind (0.0.0.0,[::]) to loopback.A pull needs both the daemon reachable and the binary on
PATH(orMLSERVER_LLMMAN_BIN); each missing piece has its own actionable error, and neither is required unless anoci://uri is used.Testing
New
tests/test_llmman.py, running against a real HTTP server on a loopback port rather than mocks, so the NDJSON contract is genuinely exercised.All executed here. 31 new:
/api/versionaccepted, a non-llmman server rejected, nothing-listening reported actionably; pull success with forwarded byte progress and the exact request body asserted; in-band error at HTTP 200; a stream ending withoutsuccess; non-OK status; a non-JSON diagnostic tolerated; scheme detection incl. case-insensitivity; thats3://,gs://,file://, a bare filename and an absolute path are not claimed; the resolve contract plus seven malformed-output cases; everyLLMMAN_HOSTform incl. wildcard-to-loopback; and twoget_model_uriintegration tests -- anoci://uri resolved to a local path, and ans3://uri never reaching the resolver.tests/test_utils.py(20 tests, incl. the fullget_model_uriparametrisation) passes unchanged, so no existing uri behaviour regressed.blackclean on all three filesNot verified here, flagged rather than implied: no end-to-end model load against a live
llmman servebacked by a real registry.