Skip to content

Commit af97134

Browse files
authored
Merge branch 'main' into feature/add-sglang-backend
2 parents 7f13540 + cf550f3 commit af97134

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1927
-1356
lines changed

.github/workflows/cli-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
working-directory: cmd/cli
3636
run: |
3737
make release VERSION=${{ github.sha }}
38-
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
38+
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
3939
with:
4040
name: dist
4141
path: |

.golangci.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ version: "2"
22

33
run:
44
timeout: 5m
5-
# Skip vendor and generated code
6-
skip-dirs:
7-
- pkg/go-containerregistry
85

96
formatters:
107
enable:
@@ -214,6 +211,10 @@ linters:
214211
- text: '^shadow: declaration of "(ctx|err|ok)" shadows declaration'
215212
linters:
216213
- govet
214+
- path: main_test\.go # Exclude main_test.go files because of a false positive: https://github.com/docker/model-runner/actions/runs/20296739902/job/58292102948
215+
linters:
216+
- staticcheck
217+
text: "SA5011"
217218

218219
issues:
219220
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.

cmd/cli/commands/bench.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func newBenchCmd() *cobra.Command {
5757
)
5858

5959
cmd := &cobra.Command{
60-
Use: "bench [MODEL]",
60+
Use: "bench MODEL",
6161
Short: "Benchmark a model's performance at different concurrency levels",
6262
Long: `Benchmark a model's performance showing tokens per second at different concurrency levels.
6363

cmd/cli/commands/compose.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func newComposeCmd() *cobra.Command {
3737
func newUpCommand() *cobra.Command {
3838
var models []string
3939
var ctxSize int64
40+
var rawRuntimeFlags string
4041
var backend string
4142
var draftModel string
4243
var numTokens int
@@ -69,6 +70,9 @@ func newUpCommand() *cobra.Command {
6970
if ctxSize > 0 {
7071
sendInfo(fmt.Sprintf("Setting context size to %d", ctxSize))
7172
}
73+
if rawRuntimeFlags != "" {
74+
sendInfo("Setting raw runtime flags to " + rawRuntimeFlags)
75+
}
7276

7377
// Build speculative config if any speculative flags are set
7478
var speculativeConfig *inference.SpeculativeDecodingConfig
@@ -89,10 +93,11 @@ func newUpCommand() *cobra.Command {
8993
ContextSize: &size,
9094
Speculative: speculativeConfig,
9195
},
96+
RawRuntimeFlags: rawRuntimeFlags,
9297
}); err != nil {
93-
configErrFmtString := "failed to configure backend for model %s with context-size %d"
94-
_ = sendErrorf(configErrFmtString+": %v", model, ctxSize, err)
95-
return fmt.Errorf(configErrFmtString+": %w", model, ctxSize, err)
98+
configErrFmtString := "failed to configure backend for model %s with context-size %d and runtime-flags %s"
99+
_ = sendErrorf(configErrFmtString+": %v", model, ctxSize, rawRuntimeFlags, err)
100+
return fmt.Errorf(configErrFmtString+": %w", model, ctxSize, rawRuntimeFlags, err)
96101
}
97102
sendInfo("Successfully configured backend for model " + model)
98103
}
@@ -114,6 +119,7 @@ func newUpCommand() *cobra.Command {
114119
}
115120
c.Flags().StringArrayVar(&models, "model", nil, "model to use")
116121
c.Flags().Int64Var(&ctxSize, "context-size", -1, "context size for the model")
122+
c.Flags().StringVar(&rawRuntimeFlags, "runtime-flags", "", "raw runtime flags to pass to the inference engine")
117123
c.Flags().StringVar(&backend, "backend", llamacpp.Name, "inference backend to use")
118124
c.Flags().StringVar(&draftModel, "speculative-draft-model", "", "draft model for speculative decoding")
119125
c.Flags().IntVar(&numTokens, "speculative-num-tokens", 0, "number of tokens to predict speculatively")
@@ -177,7 +183,7 @@ func downloadModelsOnlyIfNotFound(desktopClient *desktop.Client, models []string
177183
printer := desktop.NewSimplePrinter(func(s string) {
178184
_ = sendInfo(s)
179185
})
180-
_, _, err = desktopClient.Pull(model, false, printer)
186+
_, _, err = desktopClient.Pull(model, printer)
181187
if err != nil {
182188
_ = sendErrorf("Failed to pull model: %v", err)
183189
return fmt.Errorf("Failed to pull model: %w\n", err)

cmd/cli/commands/configure.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,28 @@ func newConfigureCmd() *cobra.Command {
1111
var flags ConfigureFlags
1212

1313
c := &cobra.Command{
14-
Use: "configure [--context-size=<n>] [--speculative-draft-model=<model>] [--hf_overrides=<json>] [--gpu-memory-utilization=<float>] [--mode=<mode>] [--think] MODEL",
15-
Short: "Configure runtime options for a model",
16-
Hidden: true,
14+
Use: "configure [--context-size=<n>] [--speculative-draft-model=<model>] [--hf_overrides=<json>] [--gpu-memory-utilization=<float>] [--mode=<mode>] [--think] MODEL [-- <runtime-flags...>]",
15+
Aliases: []string{"config"},
16+
Short: "Manage model runtime configurations",
17+
Hidden: true,
1718
Args: func(cmd *cobra.Command, args []string) error {
18-
if len(args) != 1 {
19-
return fmt.Errorf(
20-
"Exactly one model must be specified, got %d: %v\n\n"+
21-
"See 'docker model configure --help' for more information",
22-
len(args), args)
19+
argsBeforeDash := cmd.ArgsLenAtDash()
20+
if argsBeforeDash == -1 {
21+
// No "--" used, so we need exactly 1 total argument.
22+
if len(args) != 1 {
23+
return fmt.Errorf(
24+
"Exactly one model must be specified, got %d: %v\n\n"+
25+
"See 'docker model configure --help' for more information",
26+
len(args), args)
27+
}
28+
} else {
29+
// Has "--", so we need exactly 1 argument before it.
30+
if argsBeforeDash != 1 {
31+
return fmt.Errorf(
32+
"Exactly one model must be specified before --, got %d\n\n"+
33+
"See 'docker model configure --help' for more information",
34+
argsBeforeDash)
35+
}
2336
}
2437
return nil
2538
},
@@ -29,11 +42,13 @@ func newConfigureCmd() *cobra.Command {
2942
if err != nil {
3043
return err
3144
}
45+
opts.RuntimeFlags = args[1:]
3246
return desktopClient.ConfigureBackend(opts)
3347
},
3448
ValidArgsFunction: completion.ModelNames(getDesktopClient, -1),
3549
}
3650

3751
flags.RegisterFlags(c)
52+
c.AddCommand(newConfigureShowCmd())
3853
return c
3954
}

cmd/cli/commands/configure_show.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package commands
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/docker/model-runner/cmd/cli/commands/completion"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
func newConfigureShowCmd() *cobra.Command {
12+
c := &cobra.Command{
13+
Use: "show [MODEL]",
14+
Short: "Show model configurations",
15+
Args: cobra.MaximumNArgs(1),
16+
RunE: func(cmd *cobra.Command, args []string) error {
17+
var modelFilter string
18+
if len(args) > 0 {
19+
modelFilter = args[0]
20+
}
21+
configs, err := desktopClient.ShowConfigs(modelFilter)
22+
if err != nil {
23+
return err
24+
}
25+
jsonResult, err := json.MarshalIndent(configs, "", " ")
26+
if err != nil {
27+
return fmt.Errorf("failed to marshal configs to JSON: %w", err)
28+
}
29+
cmd.Println(string(jsonResult))
30+
return nil
31+
},
32+
ValidArgsFunction: completion.ModelNames(getDesktopClient, 1),
33+
}
34+
return c
35+
}

cmd/cli/commands/configure_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,90 @@ func TestThinkFlagBehavior(t *testing.T) {
305305
})
306306
}
307307
}
308+
309+
func TestRuntimeFlagsValidation(t *testing.T) {
310+
tests := []struct {
311+
name string
312+
runtimeFlags []string
313+
expectError bool
314+
errorContains string
315+
}{
316+
{
317+
name: "valid runtime flags without paths",
318+
runtimeFlags: []string{"--verbose", "--threads", "4"},
319+
expectError: false,
320+
},
321+
{
322+
name: "empty runtime flags",
323+
runtimeFlags: []string{},
324+
expectError: false,
325+
},
326+
{
327+
name: "reject absolute path in value",
328+
runtimeFlags: []string{"--log-file", "/var/log/model.log"},
329+
expectError: true,
330+
errorContains: "paths are not allowed",
331+
},
332+
{
333+
name: "reject absolute path in flag=value format",
334+
runtimeFlags: []string{"--output-file=/tmp/output.txt"},
335+
expectError: true,
336+
errorContains: "paths are not allowed",
337+
},
338+
{
339+
name: "reject relative path",
340+
runtimeFlags: []string{"--config", "../config.yaml"},
341+
expectError: true,
342+
errorContains: "paths are not allowed",
343+
},
344+
{
345+
name: "reject URL",
346+
runtimeFlags: []string{"--endpoint", "http://example.com/api"},
347+
expectError: true,
348+
errorContains: "paths are not allowed",
349+
},
350+
}
351+
352+
for _, tt := range tests {
353+
t.Run(tt.name, func(t *testing.T) {
354+
flags := ConfigureFlags{}
355+
req, err := flags.BuildConfigureRequest("test-model")
356+
if err != nil {
357+
t.Fatalf("BuildConfigureRequest failed: %v", err)
358+
}
359+
360+
// Set runtime flags after building request
361+
req.RuntimeFlags = tt.runtimeFlags
362+
363+
// Note: The actual validation happens in scheduler.ConfigureRunner,
364+
// but we're testing that the BuildConfigureRequest correctly
365+
// preserves the RuntimeFlags for validation downstream.
366+
// For a true integration test, we would need to mock the scheduler.
367+
368+
if tt.expectError {
369+
// In this unit test context, we verify the flags are preserved
370+
// The actual validation will happen in the scheduler
371+
if len(req.RuntimeFlags) == 0 && len(tt.runtimeFlags) > 0 {
372+
t.Error("RuntimeFlags should be preserved in the request")
373+
}
374+
} else {
375+
if !equalStringSlices(req.RuntimeFlags, tt.runtimeFlags) {
376+
t.Errorf("Expected RuntimeFlags %v, got %v", tt.runtimeFlags, req.RuntimeFlags)
377+
}
378+
}
379+
})
380+
}
381+
}
382+
383+
// equalStringSlices checks if two string slices are equal
384+
func equalStringSlices(a, b []string) bool {
385+
if len(a) != len(b) {
386+
return false
387+
}
388+
for i := range a {
389+
if a[i] != b[i] {
390+
return false
391+
}
392+
}
393+
return true
394+
}

cmd/cli/commands/install-runner.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func ensureStandaloneRunnerAvailable(ctx context.Context, printer standalone.Sta
138138
port = standalone.DefaultControllerPortCloud
139139
environment = "cloud"
140140
}
141-
if err := standalone.CreateControllerContainer(ctx, dockerClient, port, host, environment, false, gpu, "", modelStorageVolume, printer, engineKind, debug, false); err != nil {
141+
if err := standalone.CreateControllerContainer(ctx, dockerClient, port, host, environment, false, gpu, "", modelStorageVolume, printer, engineKind, debug, false, ""); err != nil {
142142
return nil, fmt.Errorf("unable to initialize standalone model runner container: %w", err)
143143
}
144144

@@ -172,6 +172,7 @@ type runnerOptions struct {
172172
doNotTrack bool
173173
pullImage bool
174174
pruneContainers bool
175+
proxyCert string
175176
}
176177

177178
// runInstallOrStart is shared logic for install-runner and start-runner commands
@@ -285,7 +286,7 @@ func runInstallOrStart(cmd *cobra.Command, opts runnerOptions, debug bool) error
285286
return fmt.Errorf("unable to initialize standalone model storage: %w", err)
286287
}
287288
// Create the model runner container.
288-
if err := standalone.CreateControllerContainer(cmd.Context(), dockerClient, port, opts.host, environment, opts.doNotTrack, gpu, opts.backend, modelStorageVolume, asPrinter(cmd), engineKind, debug, vllmOnWSL); err != nil {
289+
if err := standalone.CreateControllerContainer(cmd.Context(), dockerClient, port, opts.host, environment, opts.doNotTrack, gpu, opts.backend, modelStorageVolume, asPrinter(cmd), engineKind, debug, vllmOnWSL, opts.proxyCert); err != nil {
289290
return fmt.Errorf("unable to initialize standalone model runner container: %w", err)
290291
}
291292

@@ -300,6 +301,7 @@ func newInstallRunner() *cobra.Command {
300301
var backend string
301302
var doNotTrack bool
302303
var debug bool
304+
var proxyCert string
303305
c := &cobra.Command{
304306
Use: "install-runner",
305307
Short: "Install Docker Model Runner (Docker Engine only)",
@@ -312,6 +314,7 @@ func newInstallRunner() *cobra.Command {
312314
doNotTrack: doNotTrack,
313315
pullImage: true,
314316
pruneContainers: false,
317+
proxyCert: proxyCert,
315318
}, debug)
316319
},
317320
ValidArgsFunction: completion.NoComplete,
@@ -323,6 +326,7 @@ func newInstallRunner() *cobra.Command {
323326
Backend: &backend,
324327
DoNotTrack: &doNotTrack,
325328
Debug: &debug,
329+
ProxyCert: &proxyCert,
326330
})
327331
return c
328332
}

0 commit comments

Comments
 (0)