-
Notifications
You must be signed in to change notification settings - Fork 79
Consolidate model-cli into model-runner monorepo #190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b4649d8
771b747
89439ba
de1791b
50cdbb8
2d44164
138bd11
649f613
fbd8326
e9a51a3
6608b16
43d44f7
a153aba
afbfce1
cd0a1f4
c460fc7
90580b9
8267c5a
6c02d7f
0fa176c
493bbe9
1c5211e
b46d23c
645488c
670ef98
8a49b0a
d03c38f
a1eb68d
39729dd
430b784
7119eae
07ebe11
32289c3
9439c31
1107b13
d927555
f03cade
c94ac96
1926c51
537ca41
4040058
16bdda8
c7ce30b
3b4c8c8
a88568f
d5723e4
8a2f1e8
ed8cbd2
0f98703
31d580b
9b343e2
f1c3d1c
3f51f21
cbf0d0b
1e35209
102489b
b85ea46
092732a
0c822e6
10da2cb
3fd3abc
d7d9789
69d2009
cb54223
bfe1204
729ee30
b138cde
7ea6337
f577662
c19f7f0
50a3679
36641c6
b566aff
4145443
94e7f9e
e0ba13e
fac32dc
a2927b7
b82ff6d
27ccea9
29629ec
75be694
0b21e53
89356bf
f7f0131
19d70fb
3770ff2
c0e05c5
78363d5
f34a804
1853a48
077750d
4f43230
b6c4852
b5cb3af
ada3a33
38ae609
eacef42
9acb7ca
9f05dec
e84dba4
3d333f6
587200e
7afd493
21c7801
1a9ad49
c1e4512
1ef568d
2ac82c3
651163c
1dee910
f3cd2e4
9a84f47
d99b580
6b267da
1e1469f
3d8fa44
b0036ff
5d62cb8
bdbabf2
c987f89
d5e885f
e8f21cc
bdd5c51
633a30d
e46d480
50b2a6c
dd65cc4
f50e7e4
2fc0df7
a4a8978
55ddd3d
2b568f6
ef85c86
c5fea3a
929c032
44fe56d
c33c88d
dded24f
462ef1a
c46a752
19b82c2
1345d29
7f3479b
0a5cbb3
da0b8a6
8cc1646
47738ca
d38edc8
42cd726
97f44c1
0b21dda
7ea2d8d
4915a65
9302f08
f617edd
09ed234
edac824
cf6c379
0ab9371
73b89b6
c8c5214
5e46f5a
7a08e1a
3b14f66
9e70161
228cfc2
f440b22
d169b4c
a187fc4
df71e1f
b7fa673
fac4dfe
060a644
5593d33
481e29c
c505c45
3e0576d
f7ed9fb
e9b31b7
455cd0b
55e0524
a32a512
d644e5e
4087321
d7d3e72
6b6c9b5
48a3d87
a622fc9
64f4899
cdccd09
fb73e9b
7427cb9
7730f38
fb13633
8d1ffbd
d1d74fa
59018a5
5778df2
c0e7872
b77b8be
304b7c9
3526c6d
9a6b148
58d1261
98e3c6e
32594d1
c069e28
aa47e9b
cdbe1de
143c7ef
f5b0f6e
52ffaf0
6557e74
ccc376c
bb13d75
5781949
5db656e
85c86b5
6376fa2
ca956d5
82e5f3b
394ed36
44f2250
0ba9580
ff81297
757ccb1
c35a309
439344b
f59d083
b8ee609
5657a37
f679718
d304053
8036f0a
b61770e
aa3b37e
3e5ab4e
4f3cbd1
69965c7
858fe63
2cae2bb
f3c52a4
b1f0a10
369e604
a39f8ce
344ca1b
bbb7e9b
13d99c4
e23bc1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| name: Build model-cli | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ "main" ] | ||
| paths: | ||
| - 'cmd/cli/**' | ||
| - '.github/workflows/cli-build.yml' | ||
| pull_request: | ||
| branches: [ "main" ] | ||
| paths: | ||
| - 'cmd/cli/**' | ||
| - '.github/workflows/cli-build.yml' | ||
| workflow_dispatch: | ||
| inputs: | ||
| branch: | ||
| description: "Branch" | ||
| required: true | ||
| default: "main" | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: macos-latest | ||
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-go@v5 | ||
| with: | ||
| go-version-file: cmd/cli/go.mod | ||
| cache: true | ||
| cache-dependency-path: cmd/cli/go.sum | ||
| - name: Build model-cli | ||
| working-directory: cmd/cli | ||
| run: | | ||
| make release VERSION=${{ github.sha }} | ||
| - uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: dist | ||
| path: | | ||
| cmd/cli/dist/ | ||
| retention-days: 2 | ||
| if-no-files-found: error |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| name: Validate model-cli | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| branches: | ||
| - 'main' | ||
| - 'v[0-9]*' | ||
| tags: | ||
| - 'v*' | ||
| paths: | ||
| - 'cmd/cli/**' | ||
| - '.github/workflows/cli-validate.yml' | ||
| pull_request: | ||
| paths: | ||
| - 'cmd/cli/**' | ||
| - '.github/workflows/cli-validate.yml' | ||
|
|
||
| jobs: | ||
| prepare: | ||
| runs-on: ubuntu-24.04 | ||
| outputs: | ||
| targets: ${{ steps.generate.outputs.targets }} | ||
| steps: | ||
| - | ||
| name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - | ||
| name: List targets | ||
| id: generate | ||
| uses: docker/bake-action/subaction/list-targets@v6 | ||
|
||
| with: | ||
| files: ./cmd/cli/docker-bake.hcl | ||
| target: validate | ||
|
|
||
| validate: | ||
| runs-on: ubuntu-24.04 | ||
| needs: | ||
| - prepare | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| target: ${{ fromJson(needs.prepare.outputs.targets) }} | ||
| steps: | ||
| - | ||
| name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - | ||
| name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
Check warningCode scanning / CodeQL Unpinned tag for a non-immutable Action in workflow Medium
Unpinned 3rd party Action 'Validate model-cli' step
Uses Step Error loading related location Loading
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. security (yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha): An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload. Source: opengrep |
||
| with: | ||
| buildkitd-flags: --debug | ||
| - | ||
| name: Validate | ||
| uses: docker/bake-action@v6 | ||
|
||
| with: | ||
| files: ./cmd/cli/docker-bake.hcl | ||
| workdir: ./cmd/cli | ||
| targets: ${{ matrix.target }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| model-cli | ||
ilopezluna marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| .idea/ | ||
| dist/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| # syntax=docker/dockerfile:1 | ||
|
|
||
| ARG GO_VERSION=1.24 | ||
| ARG ALPINE_VERSION=3.21 | ||
|
|
||
| ARG DOCS_FORMATS="md,yaml" | ||
|
|
||
| FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base | ||
| RUN apk add --no-cache rsync git | ||
| ENV CGO_ENABLED=0 | ||
| WORKDIR /src | ||
|
|
||
| FROM base AS docs-gen | ||
| RUN --mount=target=. \ | ||
| --mount=target=/root/.cache,type=cache \ | ||
| go build -C cmd/cli -o /out/docsgen ./docs/generate.go | ||
|
|
||
| FROM base AS docs-build | ||
| COPY --from=docs-gen /out/docsgen /usr/bin | ||
| ENV DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND="model" | ||
| ARG DOCS_FORMATS | ||
| RUN --mount=target=/context \ | ||
| --mount=target=.,type=tmpfs <<EOT | ||
| set -e | ||
| rsync -a --exclude='models-store' /context/. . | ||
| docsgen --formats "$DOCS_FORMATS" --source "cmd/cli/docs/reference" | ||
| mkdir /out | ||
| cp -r cmd/cli/docs/reference/* /out/ | ||
| EOT | ||
|
|
||
| FROM scratch AS docs-update | ||
| COPY --from=docs-build /out / | ||
|
|
||
| FROM docs-build AS docs-validate | ||
| RUN --mount=target=/context \ | ||
| --mount=target=.,type=tmpfs <<EOT | ||
| set -e | ||
| rsync -a --exclude='models-store' /context/. . | ||
| git add -A | ||
| rm -rf cmd/cli/docs/reference/* | ||
| cp -rf /out/* ./cmd/cli/docs/reference/ | ||
| if [ -n "$(git status --porcelain -- docs/reference)" ]; then | ||
| echo >&2 'ERROR: Docs result differs. Please update with "make docs"' | ||
| git status --porcelain -- cmd/cli/docs/reference | ||
| exit 1 | ||
| fi | ||
| EOT | ||
|
|
||
| FROM base AS test | ||
| RUN apk add --no-cache make gcc musl-dev | ||
| WORKDIR /src | ||
| RUN --mount=target=. \ | ||
| --mount=target=/root/.cache,type=cache \ | ||
| CGO_ENABLED=1 make unit-tests |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| .PHONY: all build clean link mock unit-tests docs | ||
|
|
||
| BINARY_NAME=model-cli | ||
|
|
||
| PLUGIN_DIR=$(HOME)/.docker/cli-plugins | ||
| PLUGIN_NAME=docker-model | ||
|
|
||
| VERSION ?= | ||
|
|
||
| MACOS_MIN_VERSION := 14.0 | ||
| MACOS_MIN_VERSION_LDFLAG := -mmacosx-version-min=$(MACOS_MIN_VERSION) | ||
|
|
||
| all: build | ||
|
|
||
| build: | ||
| @echo "Building $(BINARY_NAME)..." | ||
| go build -ldflags="-s -w" -o $(BINARY_NAME) . | ||
|
|
||
| link: | ||
| @if [ ! -f $(BINARY_NAME) ]; then \ | ||
| echo "Binary not found, building first..."; \ | ||
| $(MAKE) build; \ | ||
| else \ | ||
| echo "Using existing binary $(BINARY_NAME)"; \ | ||
| fi | ||
| @echo "Linking $(BINARY_NAME) to Docker CLI plugins directory..." | ||
| @mkdir -p $(PLUGIN_DIR) | ||
| @ln -sf $(shell pwd)/$(BINARY_NAME) $(PLUGIN_DIR)/$(PLUGIN_NAME) | ||
| @echo "Link created: $(PLUGIN_DIR)/$(PLUGIN_NAME)" | ||
|
|
||
| install: build link | ||
|
|
||
| release: | ||
| @if [ -z "$(VERSION)" ]; then \ | ||
| echo "Error: VERSION parameter is required. Use: make release VERSION=x.y.z"; \ | ||
| exit 1; \ | ||
| fi | ||
| @echo "Building release version '$(VERSION)'..." | ||
| GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 CGO_CFLAGS="$(MACOS_MIN_VERSION_LDFLAG)" CGO_LDFLAGS="$(MACOS_MIN_VERSION_LDFLAG)" go build -trimpath -ldflags="-s -w -X github.com/docker/model-cli/desktop.Version=$(VERSION)" -o dist/darwin-arm64/$(PLUGIN_NAME) . | ||
| GOOS=windows GOARCH=amd64 go build -trimpath -ldflags="-s -w -X github.com/docker/model-cli/desktop.Version=$(VERSION)" -o dist/windows-amd64/$(PLUGIN_NAME).exe . | ||
| GOOS=windows GOARCH=arm64 go build -trimpath -ldflags="-s -w -X github.com/docker/model-cli/desktop.Version=$(VERSION)" -o dist/windows-arm64/$(PLUGIN_NAME).exe . | ||
| CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w -X github.com/docker/model-cli/desktop.Version=$(VERSION)" -o dist/linux-amd64/$(PLUGIN_NAME) . | ||
| CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags="-s -w -X github.com/docker/model-cli/desktop.Version=$(VERSION)" -o dist/linux-arm64/$(PLUGIN_NAME) . | ||
| @echo "Release build complete: $(PLUGIN_NAME) version '$(VERSION)'" | ||
|
|
||
| ce-release: | ||
| @if [ -z "$(VERSION)" ]; then \ | ||
| echo "Error: VERSION parameter is required. Use: make release VERSION=x.y.z"; \ | ||
| exit 1; \ | ||
| fi | ||
| @if [ "$(uname -s)" != "Linux" ]; then \ | ||
| echo "Warning: This release target is designed for Linux"; \ | ||
| fi | ||
| @echo "Building local release version '$(VERSION)'..." | ||
| CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w -X github.com/docker/model-cli/desktop.Version=$(VERSION)" -o dist/$(PLUGIN_NAME) . | ||
| @echo "Local release build complete: $(PLUGIN_NAME) version '$(VERSION)'" | ||
|
|
||
| mock: | ||
| @echo "Generating mocks..." | ||
| @mkdir -p mocks | ||
| @go generate ./... | ||
| @echo "Mocks generated!" | ||
|
|
||
| unit-tests: | ||
| @echo "Running unit tests..." | ||
| @go test -race -v ./... | ||
| @echo "Unit tests completed!" | ||
|
|
||
| clean: | ||
| @echo "Cleaning up..." | ||
| @rm -f $(BINARY_NAME) | ||
| @echo "Cleaned!" | ||
|
|
||
| docs: | ||
| $(eval $@_TMP_OUT := $(shell mktemp -d -t model-cli-output.XXXXXXXXXX)) | ||
| docker buildx bake --allow=fs.read=$(shell cd ../.. && pwd) --set "*.output=type=local,dest=$($@_TMP_OUT)" update-docs | ||
| rm -rf ./docs/reference/* | ||
| cp -R "$($@_TMP_OUT)"/* ./docs/reference/ | ||
| rm -rf "$($@_TMP_OUT)"/* |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # Docker Model CLI | ||
|
|
||
| A powerful command-line interface for managing, running, packaging, and deploying AI/ML models using Docker. This CLI lets you install and control the Docker Model Runner, interact with models, manage model artifacts, and integrate with OpenAI and other backends—all from your terminal. | ||
|
|
||
| ## Features | ||
| - **Install Model Runner**: Easily set up the Docker Model Runner for local or cloud environments with GPU support. | ||
| - **Run Models**: Execute models with prompts or in interactive chat mode, supporting multiline input and OpenAI-style backends. | ||
| - **List Models**: View all models available locally or via OpenAI, with options for JSON and quiet output. | ||
| - **Package Models**: Convert GGUF files into Docker model OCI artifacts and push them to registries, including license and context size options. | ||
| - **Configure Models**: Set runtime flags and context sizes for models. | ||
| - **Logs & Status**: Stream logs and check the status of the Model Runner and individual models. | ||
| - **Tag, Pull, Push, Remove, Unload**: Full lifecycle management for model artifacts. | ||
| - **Compose & Desktop Integration**: Advanced orchestration and desktop support for model backends. | ||
|
|
||
| ## Building | ||
| 1. **Clone the repo:** | ||
| ```bash | ||
| git clone https://github.com/docker/model-cli.git | ||
| cd model-cli | ||
| ``` | ||
| 2. **Build the CLI:** | ||
| ```bash | ||
| make build | ||
| ``` | ||
| 3. **Install Model Runner:** | ||
| ```bash | ||
| ./model install-runner | ||
| ``` | ||
| Use `--gpu cuda` for GPU support, or `--gpu auto` for automatic detection. | ||
|
|
||
| ## Usage | ||
| Run `./model --help` to see all commands and options. | ||
|
|
||
| ### Common Commands | ||
| - `model install-runner` — Install the Docker Model Runner | ||
| - `model run MODEL [PROMPT]` — Run a model with a prompt or enter chat mode | ||
| - `model list` — List available models | ||
| - `model package --gguf <path> --push <target>` — Package and push a model | ||
| - `model logs` — View logs | ||
| - `model status` — Check runner status | ||
| - `model configure MODEL [flags]` — Configure model runtime | ||
| - `model unload MODEL` — Unload a model | ||
| - `model tag SOURCE TARGET` — Tag a model | ||
| - `model pull MODEL` — Pull a model | ||
| - `model push MODEL` — Push a model | ||
| - `model rm MODEL` — Remove a model | ||
|
|
||
| ## Example: Interactive Chat | ||
| ```bash | ||
| ./model run llama.cpp "What is the capital of France?" | ||
| ``` | ||
| Or enter chat mode: | ||
| ```bash | ||
| ./model run llama.cpp | ||
| Interactive chat mode started. Type '/bye' to exit. | ||
| > """ | ||
| Tell me a joke. | ||
| """ | ||
| ``` | ||
|
|
||
| ## Advanced | ||
| - **Packaging:** | ||
| Add licenses and set context size when packaging models for distribution. | ||
|
|
||
| ## Development | ||
| - **Run unit tests:** | ||
| ```bash | ||
| make unit-tests | ||
| ``` | ||
| - **Generate docs:** | ||
| ```bash | ||
| make docs | ||
| ``` | ||
|
|
||
| ## License | ||
| [Apache 2.0](LICENSE) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| package commands | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "maps" | ||
| "os" | ||
| "slices" | ||
| "strings" | ||
| ) | ||
|
|
||
| // ValidBackends is a map of valid backends | ||
| var ValidBackends = map[string]bool{ | ||
| "llama.cpp": true, | ||
| "openai": true, | ||
| } | ||
|
|
||
| // validateBackend checks if the provided backend is valid | ||
| func validateBackend(backend string) error { | ||
| if !ValidBackends[backend] { | ||
| return fmt.Errorf("invalid backend '%s'. Valid backends are: %s", | ||
| backend, ValidBackendsKeys()) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // ensureAPIKey retrieves the API key if needed | ||
| func ensureAPIKey(backend string) (string, error) { | ||
| if backend == "openai" { | ||
| apiKey := os.Getenv("OPENAI_API_KEY") | ||
| if apiKey == "" { | ||
| return "", errors.New("OPENAI_API_KEY environment variable is required when using --backend=openai") | ||
| } | ||
| return apiKey, nil | ||
| } | ||
| return "", nil | ||
| } | ||
|
|
||
| func ValidBackendsKeys() string { | ||
| keys := slices.Collect(maps.Keys(ValidBackends)) | ||
| slices.Sort(keys) | ||
| return strings.Join(keys, ", ") | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.