Skip to content

Commit 6cf15b5

Browse files
committed
Add an optional CommandModifier for backend runner processes
Thread an optional func(*exec.Cmd) from BackendsConfig down through each backend's RunnerConfig to RunBackend, where it runs on the runner command after the internal setup (cancellation, stdio, env) and just before the process starts. Lets an embedder customize the spawned process, for example its credentials, environment or working directory, without a global or a change to sandbox.Create. No behavior change when unset.
1 parent 71f7c82 commit 6cf15b5

9 files changed

Lines changed: 60 additions & 15 deletions

File tree

pkg/inference/backends/diffusers/diffusers.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ type diffusers struct {
5656
installDir string
5757
// registryMirrors is the list of registry mirrors to try before registry-1.docker.io.
5858
registryMirrors []string
59+
// commandModifier, if non-nil, is applied to the server process before it starts.
60+
commandModifier func(*exec.Cmd)
5961
}
6062

6163
// New creates a new diffusers-based backend for image generation.
6264
// customPythonPath is an optional path to a custom python3 binary; if empty, the default installation is used.
63-
func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, conf *Config, customPythonPath string, registryMirrors []string) (inference.Backend, error) {
65+
func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, conf *Config, customPythonPath string, registryMirrors []string, commandModifier func(*exec.Cmd)) (inference.Backend, error) {
6466
// If no config is provided, use the default configuration
6567
if conf == nil {
6668
conf = NewDefaultConfig()
@@ -81,6 +83,7 @@ func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Log
8183
customPythonPath: customPythonPath,
8284
installDir: installDir,
8385
registryMirrors: registryMirrors,
86+
commandModifier: commandModifier,
8487
}, nil
8588
}
8689

@@ -265,6 +268,7 @@ func (d *diffusers) Run(ctx context.Context, socket, model string, modelRef stri
265268
Logger: d.log,
266269
ServerLogWriter: logging.NewWriter(d.serverLog),
267270
ErrorTransformer: ExtractPythonError,
271+
CommandModifier: d.commandModifier,
268272
})
269273
}
270274

pkg/inference/backends/llamacpp/llamacpp.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ type llamaCpp struct {
5353
gpuSupported bool
5454
// registryMirrors is the list of registry mirrors to try before registry-1.docker.io.
5555
registryMirrors []string
56+
// commandModifier, if non-nil, is applied to the server process before it starts.
57+
commandModifier func(*exec.Cmd)
5658
}
5759

5860
// New creates a new llama.cpp-based backend. installDir is the directory that
@@ -65,6 +67,7 @@ func New(
6567
installDir string,
6668
conf config.BackendConfig,
6769
registryMirrors []string,
70+
commandModifier func(*exec.Cmd),
6871
) (inference.Backend, error) {
6972
// If no config is provided, use the default configuration
7073
if conf == nil {
@@ -87,6 +90,7 @@ func New(
8790
status: inference.FormatNotInstalled(""),
8891
config: conf,
8992
registryMirrors: registryMirrors,
93+
commandModifier: commandModifier,
9094
}, nil
9195
}
9296

@@ -228,6 +232,7 @@ func (l *llamaCpp) Run(ctx context.Context, socket, model string, _ string, mode
228232
Logger: l.log,
229233
ServerLogWriter: logging.NewWriter(l.serverLog),
230234
ErrorTransformer: ExtractLlamaCppError,
235+
CommandModifier: l.commandModifier,
231236
})
232237
}
233238

pkg/inference/backends/mlx/mlx.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ type mlx struct {
3939
pythonPath string
4040
// customPythonPath is an optional custom path to the python3 binary.
4141
customPythonPath string
42+
// commandModifier, if non-nil, is applied to the server process before it starts.
43+
commandModifier func(*exec.Cmd)
4244
}
4345

4446
// New creates a new MLX-based backend.
4547
// customPythonPath is an optional path to a custom python3 binary; if empty, the default path is used.
46-
func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, conf *Config, customPythonPath string) (inference.Backend, error) {
48+
func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, conf *Config, customPythonPath string, commandModifier func(*exec.Cmd)) (inference.Backend, error) {
4749
// If no config is provided, use the default configuration
4850
if conf == nil {
4951
conf = NewDefaultMLXConfig()
@@ -56,6 +58,7 @@ func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Log
5658
config: conf,
5759
status: inference.FormatNotInstalled(""),
5860
customPythonPath: customPythonPath,
61+
commandModifier: commandModifier,
5962
}, nil
6063
}
6164

@@ -145,6 +148,7 @@ func (m *mlx) Run(ctx context.Context, socket, model string, modelRef string, mo
145148
Args: args,
146149
Logger: m.log,
147150
ServerLogWriter: logging.NewWriter(m.serverLog),
151+
CommandModifier: m.commandModifier,
148152
})
149153
}
150154

pkg/inference/backends/runner.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ type RunnerConfig struct {
4747
// process environment. If nil or empty, the backend inherits the parent
4848
// env as-is (an empty non-nil slice is treated the same as nil).
4949
Env []string
50+
// CommandModifier, if non-nil, is invoked on the backend command immediately
51+
// before it is started, after the internal setup (cancellation, stdio, env).
52+
// Embedders use it to customize process attributes such as credentials
53+
// (SysProcAttr), environment, or working directory. It composes with, and
54+
// runs after, the internal modifier.
55+
CommandModifier func(*exec.Cmd)
5056
}
5157

5258
// Logger interface for backend logging
@@ -113,6 +119,9 @@ func RunBackend(ctx context.Context, config RunnerConfig) error {
113119
if len(config.Env) > 0 {
114120
command.Env = append(os.Environ(), config.Env...)
115121
}
122+
if config.CommandModifier != nil {
123+
config.CommandModifier(command)
124+
}
116125
},
117126
config.SandboxPath,
118127
config.BinaryPath,

pkg/inference/backends/sglang/sglang.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,13 @@ type sglang struct {
4747
pythonPath string
4848
// customPythonPath is an optional custom path to the python3 binary.
4949
customPythonPath string
50+
// commandModifier, if non-nil, is applied to the server process before it starts.
51+
commandModifier func(*exec.Cmd)
5052
}
5153

5254
// New creates a new SGLang-based backend.
5355
// customPythonPath is an optional path to a custom python3 binary; if empty, the default path is used.
54-
func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, conf *Config, customPythonPath string) (inference.Backend, error) {
56+
func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, conf *Config, customPythonPath string, commandModifier func(*exec.Cmd)) (inference.Backend, error) {
5557
// If no config is provided, use the default configuration
5658
if conf == nil {
5759
conf = NewDefaultSGLangConfig()
@@ -64,6 +66,7 @@ func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Log
6466
config: conf,
6567
status: inference.FormatNotInstalled(""),
6668
customPythonPath: customPythonPath,
69+
commandModifier: commandModifier,
6770
}, nil
6871
}
6972

@@ -174,6 +177,7 @@ func (s *sglang) Run(ctx context.Context, socket, model string, modelRef string,
174177
Args: args,
175178
Logger: s.log,
176179
ServerLogWriter: logging.NewWriter(s.serverLog),
180+
CommandModifier: s.commandModifier,
177181
})
178182
}
179183

pkg/inference/backends/vllm/vllm.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io/fs"
99
"net/http"
1010
"os"
11+
"os/exec"
1112
"path/filepath"
1213
"strings"
1314

@@ -45,14 +46,17 @@ type vLLM struct {
4546
customBinaryPath string
4647
// registryMirrors is the list of registry mirrors to try before registry-1.docker.io.
4748
registryMirrors []string
49+
// commandModifier, if non-nil, is applied to the server process before it starts.
50+
commandModifier func(*exec.Cmd)
4851
}
4952

5053
// Options holds the configuration for the unified vLLM backend constructor.
5154
type Options struct {
52-
Config *Config // Linux-only: extra vllm args (nil = defaults)
53-
LinuxBinaryPath string // Linux: custom vllm binary path
54-
MetalPythonPath string // macOS ARM64: custom python path
55-
RegistryMirrors []string // registry mirrors tried before registry-1.docker.io
55+
Config *Config // Linux-only: extra vllm args (nil = defaults)
56+
LinuxBinaryPath string // Linux: custom vllm binary path
57+
MetalPythonPath string // macOS ARM64: custom python path
58+
RegistryMirrors []string // registry mirrors tried before registry-1.docker.io
59+
CommandModifier func(*exec.Cmd) // applied to the server process before it starts
5660
}
5761

5862
// New creates the appropriate vLLM backend for the current platform.
@@ -61,9 +65,9 @@ type Options struct {
6165
// methods return errors.
6266
func New(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, opts Options) (inference.Backend, error) {
6367
if platform.SupportsVLLMMetal() {
64-
return newMetal(log, modelManager, serverLog, opts.MetalPythonPath, opts.RegistryMirrors)
68+
return newMetal(log, modelManager, serverLog, opts.MetalPythonPath, opts.RegistryMirrors, opts.CommandModifier)
6569
}
66-
return newLinux(log, modelManager, serverLog, opts.Config, opts.LinuxBinaryPath, opts.RegistryMirrors)
70+
return newLinux(log, modelManager, serverLog, opts.Config, opts.LinuxBinaryPath, opts.RegistryMirrors, opts.CommandModifier)
6771
}
6872

6973
// NeedsDeferredInstall reports whether vllm on the current platform
@@ -74,7 +78,7 @@ func NeedsDeferredInstall() bool {
7478

7579
// newLinux creates a new Linux vLLM-based backend.
7680
// customBinaryPath is an optional path to a custom vllm binary; if empty, the default path is used.
77-
func newLinux(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, conf *Config, customBinaryPath string, registryMirrors []string) (inference.Backend, error) {
81+
func newLinux(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, conf *Config, customBinaryPath string, registryMirrors []string, commandModifier func(*exec.Cmd)) (inference.Backend, error) {
7882
// If no config is provided, use the default configuration
7983
if conf == nil {
8084
conf = NewDefaultVLLMConfig()
@@ -88,6 +92,7 @@ func newLinux(log logging.Logger, modelManager *models.Manager, serverLog loggin
8892
status: inference.FormatNotInstalled(""),
8993
customBinaryPath: customBinaryPath,
9094
registryMirrors: registryMirrors,
95+
commandModifier: commandModifier,
9196
}, nil
9297
}
9398

@@ -189,6 +194,7 @@ func (v *vLLM) Run(ctx context.Context, socket, model string, modelRef string, m
189194
Args: args,
190195
Logger: v.log,
191196
ServerLogWriter: logging.NewWriter(v.serverLog),
197+
CommandModifier: v.commandModifier,
192198
})
193199
}
194200

pkg/inference/backends/vllm/vllm_metal.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ type vllmMetal struct {
5353
status string
5454
// registryMirrors is the list of registry mirrors to try before registry-1.docker.io.
5555
registryMirrors []string
56+
// commandModifier, if non-nil, is applied to the server process before it starts.
57+
commandModifier func(*exec.Cmd)
5658
}
5759

5860
// newMetal creates a new vllm-metal backend.
5961
// customPythonPath is an optional path to a custom python3 binary; if empty, the default installation is used.
60-
func newMetal(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, customPythonPath string, registryMirrors []string) (inference.Backend, error) {
62+
func newMetal(log logging.Logger, modelManager *models.Manager, serverLog logging.Logger, customPythonPath string, registryMirrors []string, commandModifier func(*exec.Cmd)) (inference.Backend, error) {
6163
homeDir, err := os.UserHomeDir()
6264
if err != nil {
6365
return nil, fmt.Errorf("failed to get user home directory: %w", err)
@@ -72,6 +74,7 @@ func newMetal(log logging.Logger, modelManager *models.Manager, serverLog loggin
7274
installDir: installDir,
7375
status: inference.FormatNotInstalled(""),
7476
registryMirrors: registryMirrors,
77+
commandModifier: commandModifier,
7578
}, nil
7679
}
7780

@@ -250,6 +253,7 @@ func (v *vllmMetal) Run(ctx context.Context, socket, model string, modelRef stri
250253
Logger: v.log,
251254
ServerLogWriter: logging.NewWriter(v.serverLog),
252255
Env: []string{"VLLM_HOST_IP=127.0.0.1"},
256+
CommandModifier: v.commandModifier,
253257
})
254258
}
255259

pkg/routing/backends.go

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

33
import (
4+
"os/exec"
5+
46
"github.com/docker/model-runner/pkg/inference"
57
"github.com/docker/model-runner/pkg/inference/backends/diffusers"
68
"github.com/docker/model-runner/pkg/inference/backends/llamacpp"
@@ -39,6 +41,12 @@ type BackendsConfig struct {
3941
// when pulling backend images. Populated from MODEL_RUNNER_REGISTRY_MIRRORS or
4042
// injected by Docker Desktop from daemon.json registry-mirrors.
4143
RegistryMirrors []string
44+
45+
// CommandModifier, if non-nil, is applied to every backend runner process
46+
// immediately before it starts (see backends.RunnerConfig.CommandModifier).
47+
// Embedders use it to customize process attributes such as credentials or
48+
// environment; nil leaves the process unchanged.
49+
CommandModifier func(*exec.Cmd)
4250
}
4351

4452
// DefaultBackendDefs returns BackendDef entries for the configured backends.
@@ -54,13 +62,13 @@ func DefaultBackendDefs(cfg BackendsConfig) []BackendDef {
5462

5563
defs := []BackendDef{
5664
{Name: llamacpp.Name, Deferred: llamacpp.NeedsDeferredInstall(), Init: func(mm *models.Manager) (inference.Backend, error) {
57-
return llamacpp.New(cfg.Log, mm, sl(llamacpp.Name), cfg.LlamaCppPath, cfg.LlamaCppConfig, cfg.RegistryMirrors)
65+
return llamacpp.New(cfg.Log, mm, sl(llamacpp.Name), cfg.LlamaCppPath, cfg.LlamaCppConfig, cfg.RegistryMirrors, cfg.CommandModifier)
5866
}},
5967
}
6068

6169
if cfg.IncludeMLX {
6270
defs = append(defs, BackendDef{Name: mlx.Name, Init: func(mm *models.Manager) (inference.Backend, error) {
63-
return mlx.New(cfg.Log, mm, sl(mlx.Name), nil, cfg.MLXPath)
71+
return mlx.New(cfg.Log, mm, sl(mlx.Name), nil, cfg.MLXPath, cfg.CommandModifier)
6472
}})
6573
}
6674

@@ -73,6 +81,7 @@ func DefaultBackendDefs(cfg BackendsConfig) []BackendDef {
7381
LinuxBinaryPath: cfg.VLLMPath,
7482
MetalPythonPath: cfg.VLLMMetalPath,
7583
RegistryMirrors: cfg.RegistryMirrors,
84+
CommandModifier: cfg.CommandModifier,
7685
})
7786
},
7887
})
@@ -83,7 +92,7 @@ func DefaultBackendDefs(cfg BackendsConfig) []BackendDef {
8392
Name: diffusers.Name,
8493
Deferred: true,
8594
Init: func(mm *models.Manager) (inference.Backend, error) {
86-
return diffusers.New(cfg.Log, mm, sl(diffusers.Name), nil, cfg.DiffusersPath, cfg.RegistryMirrors)
95+
return diffusers.New(cfg.Log, mm, sl(diffusers.Name), nil, cfg.DiffusersPath, cfg.RegistryMirrors, cfg.CommandModifier)
8796
},
8897
})
8998
}

pkg/server/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func Run(ctx context.Context, cfg Config) error {
179179
RegistryMirrors: envconfig.RegistryMirrors(),
180180
}),
181181
routing.BackendDef{Name: sglang.Name, Init: func(mm *models.Manager) (inference.Backend, error) {
182-
return sglang.New(log, mm, log.With("component", sglang.Name), nil, sglangServerPath)
182+
return sglang.New(log, mm, log.With("component", sglang.Name), nil, sglangServerPath, nil)
183183
}},
184184
),
185185
OnBackendError: func(name string, err error) {

0 commit comments

Comments
 (0)