Skip to content
This repository was archived by the owner on Oct 6, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions commands/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/docker/model-cli/pkg/types"
"github.com/spf13/pflag"
"slices"
"strings"
Expand Down Expand Up @@ -48,7 +49,7 @@ func newUpCommand() *cobra.Command {
if err != nil {
_ = sendErrorf("Failed to initialize standalone model runner: %v", err)
return fmt.Errorf("Failed to initialize standalone model runner: %w", err)
} else if ((kind == desktop.ModelRunnerEngineKindMoby || kind == desktop.ModelRunnerEngineKindCloud) &&
} else if ((kind == types.ModelRunnerEngineKindMoby || kind == types.ModelRunnerEngineKindCloud) &&
standalone == nil) ||
(standalone != nil && (standalone.gatewayIP == "" || standalone.gatewayPort == 0)) {
return errors.New("unable to determine standalone runner endpoint")
Expand Down Expand Up @@ -79,13 +80,13 @@ func newUpCommand() *cobra.Command {
}

switch kind {
case desktop.ModelRunnerEngineKindDesktop:
case types.ModelRunnerEngineKindDesktop:
_ = setenv("URL", "http://model-runner.docker.internal/engines/v1/")
case desktop.ModelRunnerEngineKindMobyManual:
case types.ModelRunnerEngineKindMobyManual:
_ = setenv("URL", modelRunner.URL("/engines/v1/"))
case desktop.ModelRunnerEngineKindCloud:
case types.ModelRunnerEngineKindCloud:
fallthrough
case desktop.ModelRunnerEngineKindMoby:
case types.ModelRunnerEngineKindMoby:
_ = setenv("URL", fmt.Sprintf("http://%s:%d/engines/v1", standalone.gatewayIP, standalone.gatewayPort))
default:
return fmt.Errorf("unhandled engine kind: %v", kind)
Expand Down
19 changes: 10 additions & 9 deletions commands/install-runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/docker/model-cli/pkg/types"
"os"
"time"

Expand Down Expand Up @@ -77,8 +78,8 @@ func inspectStandaloneRunner(container container.Summary) *standaloneRunner {
func ensureStandaloneRunnerAvailable(ctx context.Context, printer standalone.StatusPrinter) (*standaloneRunner, error) {
// If we're not in a supported model runner context, then don't do anything.
engineKind := modelRunner.EngineKind()
standaloneSupported := engineKind == desktop.ModelRunnerEngineKindMoby ||
engineKind == desktop.ModelRunnerEngineKindCloud
standaloneSupported := engineKind == types.ModelRunnerEngineKindMoby ||
engineKind == types.ModelRunnerEngineKindCloud
if !standaloneSupported {
return nil, nil
}
Expand Down Expand Up @@ -127,11 +128,11 @@ func ensureStandaloneRunnerAvailable(ctx context.Context, printer standalone.Sta
// Create the model runner container.
port := uint16(standalone.DefaultControllerPortMoby)
environment := "moby"
if engineKind == desktop.ModelRunnerEngineKindCloud {
if engineKind == types.ModelRunnerEngineKindCloud {
port = standalone.DefaultControllerPortCloud
environment = "cloud"
}
if err := standalone.CreateControllerContainer(ctx, dockerClient, port, environment, false, gpu, modelStorageVolume, printer); err != nil {
if err := standalone.CreateControllerContainer(ctx, dockerClient, port, environment, false, gpu, modelStorageVolume, printer, engineKind); err != nil {
return nil, fmt.Errorf("unable to initialize standalone model runner container: %w", err)
}

Expand Down Expand Up @@ -166,14 +167,14 @@ func newInstallRunner() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
// Ensure that we're running in a supported model runner context.
engineKind := modelRunner.EngineKind()
if engineKind == desktop.ModelRunnerEngineKindDesktop {
if engineKind == types.ModelRunnerEngineKindDesktop {
// TODO: We may eventually want to auto-forward this to
// docker desktop enable model-runner, but we should first make
// sure the CLI flags match.
cmd.Println("Standalone installation not supported with Docker Desktop")
cmd.Println("Use `docker desktop enable model-runner` instead")
return nil
} else if engineKind == desktop.ModelRunnerEngineKindMobyManual {
} else if engineKind == types.ModelRunnerEngineKindMobyManual {
cmd.Println("Standalone installation not supported with MODEL_RUNNER_HOST set")
return nil
}
Expand All @@ -185,14 +186,14 @@ func newInstallRunner() *cobra.Command {
// when context detection happens. So assume that a default value
// indicates that we want the Cloud default port. This is less
// problematic in Cloud since the UX there is mostly invisible.
if engineKind == desktop.ModelRunnerEngineKindCloud &&
if engineKind == types.ModelRunnerEngineKindCloud &&
port == standalone.DefaultControllerPortMoby {
port = standalone.DefaultControllerPortCloud
}

// Set the appropriate environment.
environment := "moby"
if engineKind == desktop.ModelRunnerEngineKindCloud {
if engineKind == types.ModelRunnerEngineKindCloud {
environment = "cloud"
}

Expand Down Expand Up @@ -238,7 +239,7 @@ func newInstallRunner() *cobra.Command {
return fmt.Errorf("unable to initialize standalone model storage: %w", err)
}
// Create the model runner container.
if err := standalone.CreateControllerContainer(cmd.Context(), dockerClient, port, environment, doNotTrack, gpu, modelStorageVolume, cmd); err != nil {
if err := standalone.CreateControllerContainer(cmd.Context(), dockerClient, port, environment, doNotTrack, gpu, modelStorageVolume, cmd, engineKind); err != nil {
return fmt.Errorf("unable to initialize standalone model runner container: %w", err)
}

Expand Down
5 changes: 3 additions & 2 deletions commands/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"errors"
"fmt"
"github.com/docker/model-cli/pkg/types"
"io"
"os"
"os/signal"
Expand Down Expand Up @@ -37,8 +38,8 @@ func newLogsCmd() *cobra.Command {
// If we're running in standalone mode, then print the container
// logs.
engineKind := modelRunner.EngineKind()
useStandaloneLogs := engineKind == desktop.ModelRunnerEngineKindMoby ||
engineKind == desktop.ModelRunnerEngineKindCloud
useStandaloneLogs := engineKind == types.ModelRunnerEngineKindMoby ||
engineKind == types.ModelRunnerEngineKindCloud
if useStandaloneLogs {
dockerClient, err := desktop.DockerClientForContext(dockerCLI, dockerCLI.CurrentContext())
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions commands/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package commands
import (
"encoding/json"
"fmt"
"github.com/docker/model-cli/pkg/types"
"os"

"github.com/docker/cli/cli-plugins/hooks"
Expand Down Expand Up @@ -74,13 +75,13 @@ func jsonStatus(standalone *standaloneRunner, status desktop.Status, backendStat
var endpoint string
kind := modelRunner.EngineKind()
switch kind {
case desktop.ModelRunnerEngineKindDesktop:
case types.ModelRunnerEngineKindDesktop:
endpoint = "http://model-runner.docker.internal/engines/v1/"
case desktop.ModelRunnerEngineKindMobyManual:
case types.ModelRunnerEngineKindMobyManual:
endpoint = modelRunner.URL("/engines/v1/")
case desktop.ModelRunnerEngineKindCloud:
case types.ModelRunnerEngineKindCloud:
fallthrough
case desktop.ModelRunnerEngineKindMoby:
case types.ModelRunnerEngineKindMoby:
endpoint = fmt.Sprintf("http://%s:%d/engines/v1", standalone.gatewayIP, standalone.gatewayPort)
default:
return fmt.Errorf("unhandled engine kind: %v", kind)
Expand Down
5 changes: 3 additions & 2 deletions commands/uninstall-runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package commands

import (
"fmt"
"github.com/docker/model-cli/pkg/types"

"github.com/docker/model-cli/commands/completion"
"github.com/docker/model-cli/desktop"
Expand All @@ -16,14 +17,14 @@ func newUninstallRunner() *cobra.Command {
Short: "Uninstall Docker Model Runner",
RunE: func(cmd *cobra.Command, args []string) error {
// Ensure that we're running in a supported model runner context.
if kind := modelRunner.EngineKind(); kind == desktop.ModelRunnerEngineKindDesktop {
if kind := modelRunner.EngineKind(); kind == types.ModelRunnerEngineKindDesktop {
// TODO: We may eventually want to auto-forward this to
// docker desktop disable model-runner, but we should first
// make install-runner forward in the same way.
cmd.Println("Standalone uninstallation not supported with Docker Desktop")
cmd.Println("Use `docker desktop disable model-runner` instead")
return nil
} else if kind == desktop.ModelRunnerEngineKindMobyManual {
} else if kind == types.ModelRunnerEngineKindMobyManual {
cmd.Println("Standalone uninstallation not supported with MODEL_RUNNER_HOST set")
return nil
}
Expand Down
61 changes: 13 additions & 48 deletions desktop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package desktop
import (
"context"
"fmt"
"github.com/docker/model-cli/pkg/types"
"net/http"
"net/url"
"os"
Expand Down Expand Up @@ -79,47 +80,11 @@ func DockerClientForContext(cli *command.DockerCli, name string) (*clientpkg.Cli
)
}

// ModelRunnerEngineKind encodes the kind of Docker engine associated with the
// model runner context.
type ModelRunnerEngineKind uint8

const (
// ModelRunnerEngineKindMoby represents a non-Desktop/Cloud engine on which
// the Model CLI command is responsible for managing a Model Runner.
ModelRunnerEngineKindMoby ModelRunnerEngineKind = iota
// ModelRunnerEngineKindMobyManual represents a non-Desktop/Cloud engine
// that's explicitly targeted by a MODEL_RUNNER_HOST environment variable on
// which the user is responsible for managing a Model Runner.
ModelRunnerEngineKindMobyManual
// ModelRunnerEngineKindDesktop represents a Docker Desktop engine. It only
// refers to a Docker Desktop Linux engine, i.e. not a Windows container
// engine in the case of Docker Desktop for Windows.
ModelRunnerEngineKindDesktop
// ModelRunnerEngineKindCloud represents a Docker Cloud engine.
ModelRunnerEngineKindCloud
)

// String returns a human-readable engine kind description.
func (k ModelRunnerEngineKind) String() string {
switch k {
case ModelRunnerEngineKindMoby:
return "Docker Engine"
case ModelRunnerEngineKindMobyManual:
return "Docker Engine (Manual Install)"
case ModelRunnerEngineKindDesktop:
return "Docker Desktop"
case ModelRunnerEngineKindCloud:
return "Docker Cloud"
default:
return "Unknown"
}
}

// ModelRunnerContext encodes the operational context of a Model CLI command and
// provides facilities for inspecting and interacting with the Model Runner.
type ModelRunnerContext struct {
// kind stores the associated engine kind.
kind ModelRunnerEngineKind
kind types.ModelRunnerEngineKind
// urlPrefix is the prefix URL for all requests.
urlPrefix *url.URL
// client is the model runner client.
Expand All @@ -134,7 +99,7 @@ func NewContextForMock(client DockerHttpClient) *ModelRunnerContext {
panic("error occurred while parsing known-good URL")
}
return &ModelRunnerContext{
kind: ModelRunnerEngineKindDesktop,
kind: types.ModelRunnerEngineKindDesktop,
urlPrefix: urlPrefix,
client: client,
}
Expand All @@ -150,25 +115,25 @@ func DetectContext(ctx context.Context, cli *command.DockerCli) (*ModelRunnerCon
treatDesktopAsMoby := os.Getenv("_MODEL_RUNNER_TREAT_DESKTOP_AS_MOBY") == "1"

// Detect the associated engine type.
kind := ModelRunnerEngineKindMoby
kind := types.ModelRunnerEngineKindMoby
if modelRunnerHost != "" {
kind = ModelRunnerEngineKindMobyManual
kind = types.ModelRunnerEngineKindMobyManual
} else if isDesktopContext(ctx, cli) {
kind = ModelRunnerEngineKindDesktop
kind = types.ModelRunnerEngineKindDesktop
if treatDesktopAsMoby {
kind = ModelRunnerEngineKindMoby
kind = types.ModelRunnerEngineKindMoby
}
} else if isCloudContext(cli) {
kind = ModelRunnerEngineKindCloud
kind = types.ModelRunnerEngineKindCloud
}

// Compute the URL prefix based on the associated engine kind.
var rawURLPrefix string
if kind == ModelRunnerEngineKindMoby {
if kind == types.ModelRunnerEngineKindMoby {
rawURLPrefix = "http://localhost:" + strconv.Itoa(standalone.DefaultControllerPortMoby)
} else if kind == ModelRunnerEngineKindCloud {
} else if kind == types.ModelRunnerEngineKindCloud {
rawURLPrefix = "http://localhost:" + strconv.Itoa(standalone.DefaultControllerPortCloud)
} else if kind == ModelRunnerEngineKindMobyManual {
} else if kind == types.ModelRunnerEngineKindMobyManual {
rawURLPrefix = modelRunnerHost
} else { // ModelRunnerEngineKindDesktop
rawURLPrefix = "http://localhost" + inference.ExperimentalEndpointsPrefix
Expand All @@ -180,7 +145,7 @@ func DetectContext(ctx context.Context, cli *command.DockerCli) (*ModelRunnerCon

// Construct the HTTP client.
var client DockerHttpClient
if kind == ModelRunnerEngineKindDesktop {
if kind == types.ModelRunnerEngineKindDesktop {
dockerClient, err := DockerClientForContext(cli, cli.CurrentContext())
if err != nil {
return nil, fmt.Errorf("unable to create model runner client: %w", err)
Expand All @@ -199,7 +164,7 @@ func DetectContext(ctx context.Context, cli *command.DockerCli) (*ModelRunnerCon
}

// EngineKind returns the Docker engine kind associated with the model runner.
func (c *ModelRunnerContext) EngineKind() ModelRunnerEngineKind {
func (c *ModelRunnerContext) EngineKind() types.ModelRunnerEngineKind {
return c.kind
}

Expand Down
13 changes: 10 additions & 3 deletions pkg/standalone/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
gpupkg "github.com/docker/model-cli/pkg/gpu"
"github.com/docker/model-cli/pkg/types"
)

// controllerContainerName is the name to use for the controller container.
Expand All @@ -31,7 +32,13 @@ var concurrentInstallMatcher = regexp.MustCompile(`is already in use by containe

// copyDockerConfigToContainer copies the Docker config file from the host to the container
// and sets up proper ownership and permissions for the modelrunner user.
func copyDockerConfigToContainer(ctx context.Context, dockerClient *client.Client, containerID string) error {
// It does nothing for Desktop and Cloud engine kinds.
func copyDockerConfigToContainer(ctx context.Context, dockerClient *client.Client, containerID string, engineKind types.ModelRunnerEngineKind) error {
// Do nothing for Desktop and Cloud engine kinds
if engineKind == types.ModelRunnerEngineKindDesktop || engineKind == types.ModelRunnerEngineKindCloud {
return nil
}
Comment on lines +38 to +40
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll probably also need to rework this to skip copying from a DD client to a Docker CE engine remotely, but we don't support that right now so I'll just make a note of it.


dockerConfigPath := os.ExpandEnv("$HOME/.docker/config.json")
if s, err := os.Stat(dockerConfigPath); err != nil || s.Mode()&os.ModeType != 0 {
return nil
Expand Down Expand Up @@ -215,7 +222,7 @@ func waitForContainerToStart(ctx context.Context, dockerClient client.ContainerA
}

// CreateControllerContainer creates and starts a controller container.
func CreateControllerContainer(ctx context.Context, dockerClient *client.Client, port uint16, environment string, doNotTrack bool, gpu gpupkg.GPUSupport, modelStorageVolume string, printer StatusPrinter) error {
func CreateControllerContainer(ctx context.Context, dockerClient *client.Client, port uint16, environment string, doNotTrack bool, gpu gpupkg.GPUSupport, modelStorageVolume string, printer StatusPrinter, engineKind types.ModelRunnerEngineKind) error {
// Determine the target image.
var imageName string
switch gpu {
Expand Down Expand Up @@ -294,7 +301,7 @@ func CreateControllerContainer(ctx context.Context, dockerClient *client.Client,
}

// Copy Docker config file if it exists
if err := copyDockerConfigToContainer(ctx, dockerClient, resp.ID); err != nil {
if err := copyDockerConfigToContainer(ctx, dockerClient, resp.ID, engineKind); err != nil {
// Log warning but continue - don't fail container creation
printer.Printf("Warning: failed to copy Docker config: %v\n", err)
}
Expand Down
37 changes: 37 additions & 0 deletions pkg/types/engine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package types

// ModelRunnerEngineKind encodes the kind of Docker engine associated with the
// model runner context.
type ModelRunnerEngineKind uint8

const (
// ModelRunnerEngineKindMoby represents a non-Desktop/Cloud engine on which
// the Model CLI command is responsible for managing a Model Runner.
ModelRunnerEngineKindMoby ModelRunnerEngineKind = iota
// ModelRunnerEngineKindMobyManual represents a non-Desktop/Cloud engine
// that's explicitly targeted by a MODEL_RUNNER_HOST environment variable on
// which the user is responsible for managing a Model Runner.
ModelRunnerEngineKindMobyManual
// ModelRunnerEngineKindDesktop represents a Docker Desktop engine. It only
// refers to a Docker Desktop Linux engine, i.e. not a Windows container
// engine in the case of Docker Desktop for Windows.
ModelRunnerEngineKindDesktop
// ModelRunnerEngineKindCloud represents a Docker Cloud engine.
ModelRunnerEngineKindCloud
)

// String returns a human-readable engine kind description.
func (k ModelRunnerEngineKind) String() string {
switch k {
case ModelRunnerEngineKindMoby:
return "Docker Engine"
case ModelRunnerEngineKindMobyManual:
return "Docker Engine (Manual Install)"
case ModelRunnerEngineKindDesktop:
return "Docker Desktop"
case ModelRunnerEngineKindCloud:
return "Docker Cloud"
default:
return "Unknown"
}
}