Skip to content

Commit 1b6f2ce

Browse files
committed
refactor(cli): centralize standalone runner initialization
Add withStandaloneRunner decorator to wrap commands that require a running model runner, ensuring consistent auto-installation behavior across all CLI commands. Signed-off-by: Dorin Geman <[email protected]>
1 parent ca12aae commit 1b6f2ce

File tree

12 files changed

+77
-54
lines changed

12 files changed

+77
-54
lines changed

cmd/cli/commands/bench.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,6 @@ measuring the tokens per second (TPS) that the model can generate.`,
8383
return handleClientError(err, "Failed to inspect model")
8484
}
8585

86-
// Ensure model runner is available
87-
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false); err != nil {
88-
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
89-
}
90-
9186
if !jsonOutput {
9287
fmt.Printf("Prompt: %s\n", prompt)
9388
fmt.Printf("Duration: %v per concurrency level\n", duration)

cmd/cli/commands/compose.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ func newUpCommand() *cobra.Command {
5353

5454
sendInfo("Initializing model runner...")
5555
kind := modelRunner.EngineKind()
56-
standalone, err := ensureStandaloneRunnerAvailable(cmd.Context(), nil, false)
56+
runner, err := getStandaloneRunner(cmd.Context())
5757
if err != nil {
58-
_ = sendErrorf("Failed to initialize standalone model runner: %v", err)
59-
return fmt.Errorf("Failed to initialize standalone model runner: %w", err)
58+
_ = sendErrorf("Failed to get standalone model runner info: %v", err)
59+
return fmt.Errorf("Failed to get standalone model runner info: %w", err)
6060
} else if ((kind == types.ModelRunnerEngineKindMoby || kind == types.ModelRunnerEngineKindCloud) &&
61-
standalone == nil) ||
62-
(standalone != nil && (standalone.gatewayIP == "" || standalone.gatewayPort == 0)) {
61+
runner == nil) ||
62+
(runner != nil && (runner.gatewayIP == "" || runner.gatewayPort == 0)) {
6363
return errors.New("unable to determine standalone runner endpoint")
6464
}
6565

@@ -110,7 +110,7 @@ func newUpCommand() *cobra.Command {
110110
case types.ModelRunnerEngineKindCloud:
111111
fallthrough
112112
case types.ModelRunnerEngineKindMoby:
113-
_ = setenv("URL", "http://"+net.JoinHostPort(standalone.gatewayIP, strconv.Itoa(int(standalone.gatewayPort)))+"/engines/v1/")
113+
_ = setenv("URL", "http://"+net.JoinHostPort(runner.gatewayIP, strconv.Itoa(int(runner.gatewayPort)))+"/engines/v1/")
114114
default:
115115
return fmt.Errorf("unhandled engine kind: %v", kind)
116116
}

cmd/cli/commands/inspect.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ func newInspectCmd() *cobra.Command {
1818
Short: "Display detailed information on one model",
1919
Args: requireExactArgs(1, "inspect", "MODEL"),
2020
RunE: func(cmd *cobra.Command, args []string) error {
21-
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false); err != nil {
22-
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
23-
}
2421
if openai && remote {
2522
return fmt.Errorf("--remote flag cannot be used with --openai flag")
2623
}

cmd/cli/commands/install-runner.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,53 @@ func ensureStandaloneRunnerAvailable(ctx context.Context, printer standalone.Sta
163163
return inspectStandaloneRunner(container), nil
164164
}
165165

166+
// withStandaloneRunner wraps a command's RunE to ensure the standalone runner
167+
// is available before executing the command. This is a no-op in unsupported
168+
// contexts (e.g., Docker Desktop) or if automatic installations have been disabled.
169+
func withStandaloneRunner(cmd *cobra.Command) *cobra.Command {
170+
if cmd.RunE == nil {
171+
return cmd
172+
}
173+
originalRunE := cmd.RunE
174+
cmd.RunE = func(cmd *cobra.Command, args []string) error {
175+
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false); err != nil {
176+
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
177+
}
178+
return originalRunE(cmd, args)
179+
}
180+
return cmd
181+
}
182+
183+
// getStandaloneRunner returns the standalone runner info by finding the controller container.
184+
// This is useful for commands that need runner details after withStandaloneRunner has run.
185+
// Returns nil for non-standalone contexts (e.g., Docker Desktop).
186+
func getStandaloneRunner(ctx context.Context) (*standaloneRunner, error) {
187+
// Only standalone contexts have a runner container to inspect.
188+
engineKind := modelRunner.EngineKind()
189+
standaloneSupported := engineKind == types.ModelRunnerEngineKindMoby ||
190+
engineKind == types.ModelRunnerEngineKindCloud
191+
if !standaloneSupported {
192+
return nil, nil
193+
}
194+
195+
if dockerCLI == nil {
196+
return nil, nil
197+
}
198+
199+
dockerClient, err := desktop.DockerClientForContext(dockerCLI, dockerCLI.CurrentContext())
200+
if err != nil {
201+
return nil, fmt.Errorf("failed to create Docker client: %w", err)
202+
}
203+
containerID, _, ctr, err := standalone.FindControllerContainer(ctx, dockerClient)
204+
if err != nil {
205+
return nil, fmt.Errorf("unable to find standalone model runner: %w", err)
206+
}
207+
if containerID == "" {
208+
return nil, nil
209+
}
210+
return inspectStandaloneRunner(ctr), nil
211+
}
212+
166213
// runnerOptions holds common configuration for install/start/reinstall commands
167214
type runnerOptions struct {
168215
port uint16

cmd/cli/commands/pull.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package commands
22

33
import (
4-
"fmt"
5-
64
"github.com/docker/model-runner/cmd/cli/commands/completion"
75
"github.com/docker/model-runner/cmd/cli/desktop"
8-
96
"github.com/spf13/cobra"
107
)
118

@@ -15,9 +12,6 @@ func newPullCmd() *cobra.Command {
1512
Short: "Pull a model from Docker Hub or HuggingFace to your local environment",
1613
Args: requireExactArgs(1, "pull", "MODEL"),
1714
RunE: func(cmd *cobra.Command, args []string) error {
18-
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false); err != nil {
19-
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
20-
}
2115
return pullModel(cmd, desktopClient, args[0])
2216
},
2317
ValidArgsFunction: completion.NoComplete,

cmd/cli/commands/push.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package commands
22

33
import (
4-
"fmt"
5-
64
"github.com/docker/model-runner/cmd/cli/commands/completion"
75
"github.com/docker/model-runner/cmd/cli/desktop"
8-
96
"github.com/spf13/cobra"
107
)
118

@@ -15,9 +12,6 @@ func newPushCmd() *cobra.Command {
1512
Short: "Push a model to Docker Hub",
1613
Args: requireExactArgs(1, "push", "MODEL"),
1714
RunE: func(cmd *cobra.Command, args []string) error {
18-
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false); err != nil {
19-
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
20-
}
2115
return pushModel(cmd, desktopClient, args[0])
2216
},
2317
ValidArgsFunction: completion.NoComplete,

cmd/cli/commands/requests.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ func newRequestsCmd() *cobra.Command {
2525
return nil
2626
},
2727
RunE: func(cmd *cobra.Command, args []string) error {
28-
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false); err != nil {
29-
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
30-
}
31-
3228
responseBody, cancel, err := desktopClient.Requests(model, follow, includeExisting)
3329
if err != nil {
3430
errMsg := "Failed to get requests"

cmd/cli/commands/rm.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package commands
22

33
import (
4-
"fmt"
5-
64
"github.com/docker/model-runner/cmd/cli/commands/completion"
7-
85
"github.com/spf13/cobra"
96
)
107

@@ -16,9 +13,6 @@ func newRemoveCmd() *cobra.Command {
1613
Short: "Remove local models downloaded from Docker Hub",
1714
Args: requireMinArgs(1, "rm", "[MODEL...]"),
1815
RunE: func(cmd *cobra.Command, args []string) error {
19-
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false); err != nil {
20-
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
21-
}
2216
response, err := desktopClient.Remove(args, force)
2317
if response != "" {
2418
cmd.Print(response)

cmd/cli/commands/root.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,33 +84,42 @@ func NewRootCmd(cli *command.DockerCli) *cobra.Command {
8484
globalOptions.InstallFlags(rootCmd.Flags())
8585
}
8686

87-
// Add subcommands.
87+
// Runner management commands - these manage the runner itself and don't need automatic runner initialization.
8888
rootCmd.AddCommand(
8989
newVersionCmd(),
90+
newInstallRunner(),
91+
newUninstallRunner(),
92+
newStartRunner(),
93+
newStopRunner(),
94+
newRestartRunner(),
95+
newReinstallRunner(),
96+
)
97+
98+
// Commands that require a running model runner. These are wrapped to ensure the standalone runner is available.
99+
for _, cmd := range []*cobra.Command{
90100
newStatusCmd(),
91101
newPullCmd(),
92102
newPushCmd(),
93103
newPackagedCmd(),
94104
newListCmd(),
95105
newLogsCmd(),
96-
newRunCmd(),
97106
newRemoveCmd(),
98107
newInspectCmd(),
99108
newComposeCmd(),
100109
newTagCmd(),
101-
newInstallRunner(),
102-
newUninstallRunner(),
103-
newStartRunner(),
104-
newStopRunner(),
105-
newRestartRunner(),
106-
newReinstallRunner(),
107110
newConfigureCmd(),
108111
newPSCmd(),
109112
newDFCmd(),
110113
newUnloadCmd(),
111114
newRequestsCmd(),
112115
newPurgeCmd(),
113116
newBenchCmd(),
114-
)
117+
} {
118+
rootCmd.AddCommand(withStandaloneRunner(cmd))
119+
}
120+
121+
// run command handles standalone runner initialization itself (needs debug flag)
122+
rootCmd.AddCommand(newRunCmd())
123+
115124
return rootCmd
116125
}

cmd/cli/commands/run.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,10 @@ func newRunCmd() *cobra.Command {
586586
}
587587
},
588588
RunE: func(cmd *cobra.Command, args []string) error {
589+
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), debug); err != nil {
590+
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
591+
}
592+
589593
model := args[0]
590594
prompt := ""
591595
argsLen := len(args)
@@ -675,10 +679,6 @@ func newRunCmd() *cobra.Command {
675679
return nil
676680
}
677681

678-
if _, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), debug); err != nil {
679-
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
680-
}
681-
682682
_, err := desktopClient.Inspect(model, false)
683683
if err != nil {
684684
if !errors.Is(err, desktop.ErrNotFound) {

0 commit comments

Comments
 (0)