mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-11-06 09:04:27 +00:00
Implement Docker command handling for Llama, MLX, and vLLM backends
This commit is contained in:
@@ -1,8 +1,12 @@
|
|||||||
package llamacpp
|
package llamacpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"llamactl/pkg/backends"
|
"llamactl/pkg/backends"
|
||||||
|
"llamactl/pkg/config"
|
||||||
|
"os/exec"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@@ -329,6 +333,56 @@ func (o *LlamaServerOptions) BuildCommandArgs() []string {
|
|||||||
return backends.BuildCommandArgs(o, multipleFlags)
|
return backends.BuildCommandArgs(o, multipleFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildCommandArgsWithDocker converts InstanceOptions to command line arguments,
|
||||||
|
// handling Docker transformations if needed
|
||||||
|
func (o *LlamaServerOptions) BuildCommandArgsWithDocker(dockerImage string) []string {
|
||||||
|
args := o.BuildCommandArgs()
|
||||||
|
|
||||||
|
// No special Docker transformations needed for llama-cpp
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildCommand creates the complete command for execution, handling Docker vs native execution
|
||||||
|
func (o *LlamaServerOptions) BuildCommand(ctx context.Context, backendConfig *config.BackendSettings) (*exec.Cmd, error) {
|
||||||
|
// Build instance-specific arguments using backend functions
|
||||||
|
var instanceArgs []string
|
||||||
|
if backendConfig.Docker != nil && backendConfig.Docker.Enabled {
|
||||||
|
// Use Docker-aware argument building
|
||||||
|
instanceArgs = o.BuildCommandArgsWithDocker(backendConfig.Docker.Image)
|
||||||
|
} else {
|
||||||
|
// Use regular argument building for native execution
|
||||||
|
instanceArgs = o.BuildCommandArgs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine backend args with instance args
|
||||||
|
finalArgs := append(backendConfig.Args, instanceArgs...)
|
||||||
|
|
||||||
|
// Choose Docker vs Native execution
|
||||||
|
if backendConfig.Docker != nil && backendConfig.Docker.Enabled {
|
||||||
|
return buildDockerCommand(ctx, backendConfig, finalArgs)
|
||||||
|
} else {
|
||||||
|
return exec.CommandContext(ctx, backendConfig.Command, finalArgs...), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildDockerCommand builds a Docker command with the specified configuration and arguments
|
||||||
|
func buildDockerCommand(ctx context.Context, backendConfig *config.BackendSettings, args []string) (*exec.Cmd, error) {
|
||||||
|
// Start with configured Docker arguments (should include "run", "--rm", etc.)
|
||||||
|
dockerArgs := make([]string, len(backendConfig.Docker.Args))
|
||||||
|
copy(dockerArgs, backendConfig.Docker.Args)
|
||||||
|
|
||||||
|
// Add environment variables
|
||||||
|
for key, value := range backendConfig.Docker.Environment {
|
||||||
|
dockerArgs = append(dockerArgs, "-e", fmt.Sprintf("%s=%s", key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add image and container arguments
|
||||||
|
dockerArgs = append(dockerArgs, backendConfig.Docker.Image)
|
||||||
|
dockerArgs = append(dockerArgs, args...)
|
||||||
|
|
||||||
|
return exec.CommandContext(ctx, "docker", dockerArgs...), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ParseLlamaCommand parses a llama-server command string into LlamaServerOptions
|
// ParseLlamaCommand parses a llama-server command string into LlamaServerOptions
|
||||||
// Supports multiple formats:
|
// Supports multiple formats:
|
||||||
// 1. Full command: "llama-server --model file.gguf"
|
// 1. Full command: "llama-server --model file.gguf"
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package mlx
|
package mlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"llamactl/pkg/backends"
|
"llamactl/pkg/backends"
|
||||||
|
"llamactl/pkg/config"
|
||||||
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MlxServerOptions struct {
|
type MlxServerOptions struct {
|
||||||
@@ -36,6 +40,56 @@ func (o *MlxServerOptions) BuildCommandArgs() []string {
|
|||||||
return backends.BuildCommandArgs(o, multipleFlags)
|
return backends.BuildCommandArgs(o, multipleFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildCommandArgsWithDocker converts to command line arguments,
|
||||||
|
// handling Docker transformations if needed
|
||||||
|
func (o *MlxServerOptions) BuildCommandArgsWithDocker(dockerImage string) []string {
|
||||||
|
args := o.BuildCommandArgs()
|
||||||
|
|
||||||
|
// No special Docker transformations needed for MLX
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildCommand creates the complete command for execution, handling Docker vs native execution
|
||||||
|
func (o *MlxServerOptions) BuildCommand(ctx context.Context, backendConfig *config.BackendSettings) (*exec.Cmd, error) {
|
||||||
|
// Build instance-specific arguments using backend functions
|
||||||
|
var instanceArgs []string
|
||||||
|
if backendConfig.Docker != nil && backendConfig.Docker.Enabled {
|
||||||
|
// Use Docker-aware argument building
|
||||||
|
instanceArgs = o.BuildCommandArgsWithDocker(backendConfig.Docker.Image)
|
||||||
|
} else {
|
||||||
|
// Use regular argument building for native execution
|
||||||
|
instanceArgs = o.BuildCommandArgs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine backend args with instance args
|
||||||
|
finalArgs := append(backendConfig.Args, instanceArgs...)
|
||||||
|
|
||||||
|
// Choose Docker vs Native execution
|
||||||
|
if backendConfig.Docker != nil && backendConfig.Docker.Enabled {
|
||||||
|
return buildDockerCommand(ctx, backendConfig, finalArgs)
|
||||||
|
} else {
|
||||||
|
return exec.CommandContext(ctx, backendConfig.Command, finalArgs...), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildDockerCommand builds a Docker command with the specified configuration and arguments
|
||||||
|
func buildDockerCommand(ctx context.Context, backendConfig *config.BackendSettings, args []string) (*exec.Cmd, error) {
|
||||||
|
// Start with configured Docker arguments (should include "run", "--rm", etc.)
|
||||||
|
dockerArgs := make([]string, len(backendConfig.Docker.Args))
|
||||||
|
copy(dockerArgs, backendConfig.Docker.Args)
|
||||||
|
|
||||||
|
// Add environment variables
|
||||||
|
for key, value := range backendConfig.Docker.Environment {
|
||||||
|
dockerArgs = append(dockerArgs, "-e", fmt.Sprintf("%s=%s", key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add image and container arguments
|
||||||
|
dockerArgs = append(dockerArgs, backendConfig.Docker.Image)
|
||||||
|
dockerArgs = append(dockerArgs, args...)
|
||||||
|
|
||||||
|
return exec.CommandContext(ctx, "docker", dockerArgs...), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ParseMlxCommand parses a mlx_lm.server command string into MlxServerOptions
|
// ParseMlxCommand parses a mlx_lm.server command string into MlxServerOptions
|
||||||
// Supports multiple formats:
|
// Supports multiple formats:
|
||||||
// 1. Full command: "mlx_lm.server --model model/path"
|
// 1. Full command: "mlx_lm.server --model model/path"
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
package vllm
|
package vllm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"llamactl/pkg/config"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"llamactl/pkg/backends"
|
"llamactl/pkg/backends"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -160,6 +166,75 @@ func (o *VllmServerOptions) BuildCommandArgs() []string {
|
|||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildCommandArgsWithDocker converts VllmServerOptions to command line arguments,
|
||||||
|
// handling Docker transformations if needed
|
||||||
|
func (o *VllmServerOptions) BuildCommandArgsWithDocker(dockerImage string) []string {
|
||||||
|
args := o.BuildCommandArgs()
|
||||||
|
|
||||||
|
// Handle vLLM Docker image quirk
|
||||||
|
if isVLLMDocker(dockerImage) {
|
||||||
|
args = transformVLLMArgs(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// isVLLMDocker checks if the Docker image is a vLLM image
|
||||||
|
func isVLLMDocker(image string) bool {
|
||||||
|
return strings.Contains(strings.ToLower(image), "vllm")
|
||||||
|
}
|
||||||
|
|
||||||
|
// transformVLLMArgs converts vLLM arguments for Docker execution
|
||||||
|
// Convert: ["serve", "microsoft/DialoGPT-medium", "--flag", "value"]
|
||||||
|
// To: ["--model", "microsoft/DialoGPT-medium", "--flag", "value"]
|
||||||
|
func transformVLLMArgs(args []string) []string {
|
||||||
|
if len(args) >= 2 && args[0] == "serve" {
|
||||||
|
return append([]string{"--model", args[1]}, args[2:]...)
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildCommand creates the complete command for execution, handling Docker vs native execution
|
||||||
|
func (o *VllmServerOptions) BuildCommand(ctx context.Context, backendConfig *config.BackendSettings) (*exec.Cmd, error) {
|
||||||
|
// Build instance-specific arguments using backend functions
|
||||||
|
var instanceArgs []string
|
||||||
|
if backendConfig.Docker != nil && backendConfig.Docker.Enabled {
|
||||||
|
// Use Docker-aware argument building
|
||||||
|
instanceArgs = o.BuildCommandArgsWithDocker(backendConfig.Docker.Image)
|
||||||
|
} else {
|
||||||
|
// Use regular argument building for native execution
|
||||||
|
instanceArgs = o.BuildCommandArgs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine backend args with instance args
|
||||||
|
finalArgs := append(backendConfig.Args, instanceArgs...)
|
||||||
|
|
||||||
|
// Choose Docker vs Native execution
|
||||||
|
if backendConfig.Docker != nil && backendConfig.Docker.Enabled {
|
||||||
|
return buildDockerCommand(ctx, backendConfig, finalArgs)
|
||||||
|
} else {
|
||||||
|
return exec.CommandContext(ctx, backendConfig.Command, finalArgs...), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildDockerCommand builds a Docker command with the specified configuration and arguments
|
||||||
|
func buildDockerCommand(ctx context.Context, backendConfig *config.BackendSettings, args []string) (*exec.Cmd, error) {
|
||||||
|
// Start with configured Docker arguments (should include "run", "--rm", etc.)
|
||||||
|
dockerArgs := make([]string, len(backendConfig.Docker.Args))
|
||||||
|
copy(dockerArgs, backendConfig.Docker.Args)
|
||||||
|
|
||||||
|
// Add environment variables
|
||||||
|
for key, value := range backendConfig.Docker.Environment {
|
||||||
|
dockerArgs = append(dockerArgs, "-e", fmt.Sprintf("%s=%s", key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add image and container arguments
|
||||||
|
dockerArgs = append(dockerArgs, backendConfig.Docker.Image)
|
||||||
|
dockerArgs = append(dockerArgs, args...)
|
||||||
|
|
||||||
|
return exec.CommandContext(ctx, "docker", dockerArgs...), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ParseVllmCommand parses a vLLM serve command string into VllmServerOptions
|
// ParseVllmCommand parses a vLLM serve command string into VllmServerOptions
|
||||||
// Supports multiple formats:
|
// Supports multiple formats:
|
||||||
// 1. Full command: "vllm serve --model MODEL_NAME --other-args"
|
// 1. Full command: "vllm serve --model MODEL_NAME --other-args"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"llamactl/pkg/backends"
|
"llamactl/pkg/backends"
|
||||||
|
"llamactl/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start starts the llama server instance and returns an error if it fails.
|
// Start starts the llama server instance and returns an error if it fails.
|
||||||
@@ -41,24 +42,14 @@ func (i *Process) Start() error {
|
|||||||
return fmt.Errorf("failed to create log files: %w", err)
|
return fmt.Errorf("failed to create log files: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := i.options.BuildCommandArgs()
|
// Build command using backend-specific methods
|
||||||
i.ctx, i.cancel = context.WithCancel(context.Background())
|
cmd, cmdErr := i.buildCommand()
|
||||||
|
if cmdErr != nil {
|
||||||
var executable string
|
return fmt.Errorf("failed to build command: %w", cmdErr)
|
||||||
|
|
||||||
// Get executable from global configuration
|
|
||||||
switch i.options.BackendType {
|
|
||||||
case backends.BackendTypeLlamaCpp:
|
|
||||||
executable = i.globalBackendSettings.LlamaExecutable
|
|
||||||
case backends.BackendTypeMlxLm:
|
|
||||||
executable = i.globalBackendSettings.MLXLMExecutable
|
|
||||||
case backends.BackendTypeVllm:
|
|
||||||
executable = i.globalBackendSettings.VllmExecutable
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported backend type: %s", i.options.BackendType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i.cmd = exec.CommandContext(i.ctx, executable, args...)
|
i.ctx, i.cancel = context.WithCancel(context.Background())
|
||||||
|
i.cmd = cmd
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
setProcAttrs(i.cmd)
|
setProcAttrs(i.cmd)
|
||||||
@@ -372,3 +363,35 @@ func (i *Process) validateRestartConditions() (shouldRestart bool, maxRestarts i
|
|||||||
|
|
||||||
return true, maxRestarts, restartDelay
|
return true, maxRestarts, restartDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildCommand builds the command to execute using backend-specific logic
|
||||||
|
func (i *Process) buildCommand() (*exec.Cmd, error) {
|
||||||
|
// Get backend configuration
|
||||||
|
backendConfig, err := i.getBackendConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate to the backend's BuildCommand method
|
||||||
|
return i.options.BuildCommand(i.ctx, backendConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBackendConfig resolves the backend configuration for the current instance
|
||||||
|
func (i *Process) getBackendConfig() (*config.BackendSettings, error) {
|
||||||
|
var backendTypeStr string
|
||||||
|
|
||||||
|
switch i.options.BackendType {
|
||||||
|
case backends.BackendTypeLlamaCpp:
|
||||||
|
backendTypeStr = "llama-cpp"
|
||||||
|
case backends.BackendTypeMlxLm:
|
||||||
|
backendTypeStr = "mlx"
|
||||||
|
case backends.BackendTypeVllm:
|
||||||
|
backendTypeStr = "vllm"
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported backend type: %s", i.options.BackendType)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings := i.globalBackendSettings.GetBackendSettings(backendTypeStr)
|
||||||
|
return &settings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user