Skip to content

feat(models): add llmman as a models provider - #14452

Open
ericcurtin wants to merge 3 commits into
docker:mainfrom
ericcurtin:feat/llmman-models
Open

ericcurtin wants to merge 3 commits into
docker:mainfrom
ericcurtin:feat/llmman-models

Conversation

@ericcurtin

@ericcurtin ericcurtin commented Nov 4, 2026 •

Copy link
Copy Markdown

What this PR does

Adds llmman (https://github.com/llmmanorg/llmman) as an alternative backend for the top-level models element, selected explicitly with COMPOSE_MODEL_PROVIDER=llmman (shell or .env). It is also used automatically when COMPOSE_MODEL_PROVIDER is unset, the docker model plugin is not installed and llmman is on PATH (debug log only). Docker Model Runner always wins when its plugin is installed, and an explicit docker-model-runner never falls back. Any other value is an error.

Related issue

No linked issue yet: this is a proposal and I'm aware CONTRIBUTING.md asks for a status/approved issue for AI-assisted PRs. Happy to open an issue and discuss the design there if maintainers prefer.

Changes made

  • modelAPI becomes a small internal interface (Check, ListModels, HasModel, PullModel, ConfigureModel, SetModelVariables, Close). The existing code is dmrModelAPI, with the same commands and errors. The model/endpoint variable injection is shared by both providers.
  • New pkg/compose/model_llmman.go, driving the llmman CLI with the project environment:
    • llmman ps verifies the daemon first (never starts it; actionable error otherwise).
    • llmman list --format finds models already stored, and llmman itself resolves references, so only missing models are pulled with llmman pull.
    • context_size and runtime_flags have no per-model equivalent in llmman, so they are ignored with a warning event.
    • Services get <NAME>_MODEL and <NAME>_URL (or model_var / endpoint_var), where the URL is the OpenAI base URL built from LLMMAN_HOST (default http://host.docker.internal:17434/v1).
  • Dry-run only runs the read-only ps and list commands.
  • Usage notes are in the doc comment of model_llmman.go; there is no hand-written models doc to update.

Testing done

  • Unit tests pass: go test ./pkg/... (new fake-llmman tests plus the unchanged Docker Model Runner dry-run test)
  • E2E tests pass: not run
  • Linting passes on ./pkg/compose/... with golangci-lint (3 remaining nolintlint reports are in files this PR does not touch)
  • Code formatted: gofmt, gofumpt/gci through golangci-lint
  • Manually tested: real llmman 0.1.567 and a Docker engine on Linux with a throwaway daemon. Pulled docker.io/ai/smollm2:135M-Q2_K and hf.co/unsloth/SmolLM2-135M-Instruct-GGUF:Q2_K through docker compose create, re-ran with nothing to pull, checked the container env vars, --dry-run, daemon down, missing binary and a bad provider value. Not tested: a container calling the daemon, inference, Docker Desktop, API-key auth or TLS.

AI tool used (if applicable)

AI Tool: opencode with anthropic/claude-sonnet-5-5 (high variant), supervised by me. The code, tests and this description were drafted by the agent, and I have reviewed the change (AI_AGENT_DISCLOSURE.md removed from the commit).

Additional context

Behaviour worth knowing
  • llmman resolves ai/<name> as a Hugging Face repo, not Docker Hub, unlike Docker Model Runner. Use <name> or docker.io/ai/<name>; a failed pull of an ai/ reference says so.
  • llmman binds 127.0.0.1 by default. On a Linux engine, containers need the daemon bound on a reachable address (0.0.0.0 needs LLMMAN_API_KEYS or LLMMAN_AUTH=off) and extra_hosts: host.docker.internal:host-gateway. Documented, not automated.
  • ensureModels is touched (Check/HasModel hooks, a local err in the goroutine instead of the shared outer one); Docker Model Runner behaviour is the same. This may conflict with fix(models): check the model listing error and keep pull errors per goroutine #14284.

@ericcurtin
ericcurtin requested review from a team as code owners November 4, 2026 09:36
@ericcurtin
ericcurtin requested review from glours and ndeloof November 4, 2026 09:36

@docker-agent docker-agent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟡 NEEDS ATTENTION

Lower-confidence findings (not posted inline)

  • [low] pkg/compose/model_llmman.go:212 — scanner.Err() not checked after stdout-scanning loop in PullModel (confidence: weak 38/100)
  • [low] pkg/compose/model_llmman.go:161 — llmman list <model> --format flag after positional arg may be unsupported by some CLI frameworks (confidence: weak 34/100)
  • [low] pkg/compose/model.go:57 — ListModels error silently ignored; could mask backend failures (confidence: weak 38/100)

Comment thread pkg/compose/model_llmman.go Resolved
Add llmman (https://github.com/llmmanorg/llmman) as an alternative backend
for the top-level models element. It is selected explicitly with
COMPOSE_MODEL_PROVIDER=llmman (shell or .env), and is also used when
COMPOSE_MODEL_PROVIDER is unset, the docker model plugin is not installed
and llmman is found in PATH. Docker Model Runner remains the default and is
unchanged whenever its plugin is installed.

modelAPI becomes a small internal interface implemented by the existing
Docker Model Runner code and by a new llmman backend that drives the llmman
CLI: llmman ps checks the daemon first, llmman list finds stored models,
llmman pull fetches missing ones. context_size and runtime_flags have no
per-model equivalent in llmman and are ignored with a warning.

Signed-off-by: Eric Curtin <eric.curtin@docker.com>
@ericcurtin ericcurtin changed the title feat(models): add llmman as an opt-in models provider feat(models): add llmman as a models provider Nov 4, 2026
@codecov

codecov Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.41270% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/compose/model.go 96.72% 2 Missing ⚠️
pkg/compose/model_llmman.go 99.21% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@ndeloof

ndeloof commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

IMHO compose is not the right place to select a model runtime. Docker Model Runner has been design to abstract this away, so that model reference is the only thing user has to provide. Better request DMR to support this alternative runtime

A line past the scanner's limit stopped it reading, which could block the child.

Signed-off-by: Eric Curtin <eric.curtin@docker.com>
Signed-off-by: Eric Curtin <eric.curtin@docker.com>
@ericcurtin

ericcurtin commented Nov 6, 2026 •

Copy link
Copy Markdown
Author

IMHO compose is not the right place to select a model runtime. Docker Model Runner has been design to abstract this away, so that model reference is the only thing user has to provide. Better request DMR to support this alternative runtime

That's not how this works. llmman is a replacement for DMR, like a DMR 2.0, DMR is not an llmman wrapper.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants