mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-11-06 00:54:23 +00:00
Implement common ParseCommand interface
This commit is contained in:
@@ -23,6 +23,7 @@ type backend interface {
|
|||||||
SetPort(int)
|
SetPort(int)
|
||||||
GetHost() string
|
GetHost() string
|
||||||
Validate() error
|
Validate() error
|
||||||
|
ParseCommand(string) (any, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var backendConstructors = map[BackendType]func() backend{
|
var backendConstructors = map[BackendType]func() backend{
|
||||||
|
|||||||
@@ -378,19 +378,19 @@ func (o *LlamaServerOptions) BuildDockerArgs() []string {
|
|||||||
return o.BuildCommandArgs()
|
return o.BuildCommandArgs()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseLlamaCommand parses a llama-server command string into LlamaServerOptions
|
// ParseCommand 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"
|
||||||
// 2. Full path: "/usr/local/bin/llama-server --model file.gguf"
|
// 2. Full path: "/usr/local/bin/llama-server --model file.gguf"
|
||||||
// 3. Args only: "--model file.gguf --gpu-layers 32"
|
// 3. Args only: "--model file.gguf --gpu-layers 32"
|
||||||
// 4. Multiline commands with backslashes
|
// 4. Multiline commands with backslashes
|
||||||
func ParseLlamaCommand(command string) (*LlamaServerOptions, error) {
|
func (o *LlamaServerOptions) ParseCommand(command string) (any, error) {
|
||||||
executableNames := []string{"llama-server"}
|
executableNames := []string{"llama-server"}
|
||||||
var subcommandNames []string // Llama has no subcommands
|
var subcommandNames []string // Llama has no subcommands
|
||||||
// Use package-level llamaMultiValuedFlags variable
|
// Use package-level llamaMultiValuedFlags variable
|
||||||
|
|
||||||
var llamaOptions LlamaServerOptions
|
var llamaOptions LlamaServerOptions
|
||||||
if err := ParseCommand(command, executableNames, subcommandNames, llamaMultiValuedFlags, &llamaOptions); err != nil {
|
if err := parseCommand(command, executableNames, subcommandNames, llamaMultiValuedFlags, &llamaOptions); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -385,7 +385,9 @@ func TestParseLlamaCommand(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result, err := backends.ParseLlamaCommand(tt.command)
|
var opts backends.LlamaServerOptions
|
||||||
|
resultAny, err := opts.ParseCommand(tt.command)
|
||||||
|
result, _ := resultAny.(*backends.LlamaServerOptions)
|
||||||
|
|
||||||
if tt.expectErr {
|
if tt.expectErr {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -413,7 +415,9 @@ func TestParseLlamaCommand(t *testing.T) {
|
|||||||
|
|
||||||
func TestParseLlamaCommandArrays(t *testing.T) {
|
func TestParseLlamaCommandArrays(t *testing.T) {
|
||||||
command := "llama-server --model test.gguf --lora adapter1.bin --lora=adapter2.bin"
|
command := "llama-server --model test.gguf --lora adapter1.bin --lora=adapter2.bin"
|
||||||
result, err := backends.ParseLlamaCommand(command)
|
var opts backends.LlamaServerOptions
|
||||||
|
resultAny, err := opts.ParseCommand(command)
|
||||||
|
result, _ := resultAny.(*backends.LlamaServerOptions)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
@@ -429,4 +433,4 @@ func TestParseLlamaCommandArrays(t *testing.T) {
|
|||||||
t.Errorf("expected lora[%d]=%s got %s", i, v, result.Lora[i])
|
t.Errorf("expected lora[%d]=%s got %s", i, v, result.Lora[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,19 +70,19 @@ func (o *MlxServerOptions) BuildDockerArgs() []string {
|
|||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseMlxCommand parses a mlx_lm.server command string into MlxServerOptions
|
// ParseCommand 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"
|
||||||
// 2. Full path: "/usr/local/bin/mlx_lm.server --model model/path"
|
// 2. Full path: "/usr/local/bin/mlx_lm.server --model model/path"
|
||||||
// 3. Args only: "--model model/path --host 0.0.0.0"
|
// 3. Args only: "--model model/path --host 0.0.0.0"
|
||||||
// 4. Multiline commands with backslashes
|
// 4. Multiline commands with backslashes
|
||||||
func ParseMlxCommand(command string) (*MlxServerOptions, error) {
|
func (o *MlxServerOptions) ParseCommand(command string) (any, error) {
|
||||||
executableNames := []string{"mlx_lm.server"}
|
executableNames := []string{"mlx_lm.server"}
|
||||||
var subcommandNames []string // MLX has no subcommands
|
var subcommandNames []string // MLX has no subcommands
|
||||||
multiValuedFlags := map[string]bool{} // MLX has no multi-valued flags
|
multiValuedFlags := map[string]bool{} // MLX has no multi-valued flags
|
||||||
|
|
||||||
var mlxOptions MlxServerOptions
|
var mlxOptions MlxServerOptions
|
||||||
if err := ParseCommand(command, executableNames, subcommandNames, multiValuedFlags, &mlxOptions); err != nil {
|
if err := parseCommand(command, executableNames, subcommandNames, multiValuedFlags, &mlxOptions); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ func TestParseMlxCommand(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result, err := backends.ParseMlxCommand(tt.command)
|
var opts backends.MlxServerOptions
|
||||||
|
resultAny, err := opts.ParseCommand(tt.command)
|
||||||
|
result, _ := resultAny.(*backends.MlxServerOptions)
|
||||||
|
|
||||||
if tt.expectErr {
|
if tt.expectErr {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -174,11 +176,11 @@ func TestMlxBuildCommandArgs_BooleanFields(t *testing.T) {
|
|||||||
|
|
||||||
func TestMlxBuildCommandArgs_ZeroValues(t *testing.T) {
|
func TestMlxBuildCommandArgs_ZeroValues(t *testing.T) {
|
||||||
options := backends.MlxServerOptions{
|
options := backends.MlxServerOptions{
|
||||||
Port: 0, // Should be excluded
|
Port: 0, // Should be excluded
|
||||||
TopK: 0, // Should be excluded
|
TopK: 0, // Should be excluded
|
||||||
Temp: 0, // Should be excluded
|
Temp: 0, // Should be excluded
|
||||||
Model: "", // Should be excluded
|
Model: "", // Should be excluded
|
||||||
LogLevel: "", // Should be excluded
|
LogLevel: "", // Should be excluded
|
||||||
TrustRemoteCode: false, // Should be excluded
|
TrustRemoteCode: false, // Should be excluded
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,4 +201,4 @@ func TestMlxBuildCommandArgs_ZeroValues(t *testing.T) {
|
|||||||
t.Errorf("Zero value argument %q should not be present in %v", excludedArg, args)
|
t.Errorf("Zero value argument %q should not be present in %v", excludedArg, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseCommand parses a command string into a target struct
|
// parseCommand parses a command string into a target struct
|
||||||
func ParseCommand(command string, executableNames []string, subcommandNames []string, multiValuedFlags map[string]bool, target any) error {
|
func parseCommand(command string, executableNames []string, subcommandNames []string, multiValuedFlags map[string]bool, target any) error {
|
||||||
// Normalize multiline commands
|
// Normalize multiline commands
|
||||||
command = normalizeCommand(command)
|
command = normalizeCommand(command)
|
||||||
if command == "" {
|
if command == "" {
|
||||||
|
|||||||
@@ -202,14 +202,14 @@ func (o *VllmServerOptions) BuildDockerArgs() []string {
|
|||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseVllmCommand parses a vLLM serve command string into VllmServerOptions
|
// ParseCommand 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"
|
||||||
// 2. Full path: "/usr/local/bin/vllm serve --model MODEL_NAME"
|
// 2. Full path: "/usr/local/bin/vllm serve --model MODEL_NAME"
|
||||||
// 3. Serve only: "serve --model MODEL_NAME --other-args"
|
// 3. Serve only: "serve --model MODEL_NAME --other-args"
|
||||||
// 4. Args only: "--model MODEL_NAME --other-args"
|
// 4. Args only: "--model MODEL_NAME --other-args"
|
||||||
// 5. Multiline commands with backslashes
|
// 5. Multiline commands with backslashes
|
||||||
func ParseVllmCommand(command string) (*VllmServerOptions, error) {
|
func (o *VllmServerOptions) ParseCommand(command string) (any, error) {
|
||||||
executableNames := []string{"vllm"}
|
executableNames := []string{"vllm"}
|
||||||
subcommandNames := []string{"serve"}
|
subcommandNames := []string{"serve"}
|
||||||
multiValuedFlags := map[string]bool{
|
multiValuedFlags := map[string]bool{
|
||||||
@@ -223,7 +223,7 @@ func ParseVllmCommand(command string) (*VllmServerOptions, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var vllmOptions VllmServerOptions
|
var vllmOptions VllmServerOptions
|
||||||
if err := ParseCommand(command, executableNames, subcommandNames, multiValuedFlags, &vllmOptions); err != nil {
|
if err := parseCommand(command, executableNames, subcommandNames, multiValuedFlags, &vllmOptions); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ func TestParseVllmCommand(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result, err := backends.ParseVllmCommand(tt.command)
|
var opts backends.VllmServerOptions
|
||||||
|
resultAny, err := opts.ParseCommand(tt.command)
|
||||||
|
result, _ := resultAny.(*backends.VllmServerOptions)
|
||||||
|
|
||||||
if tt.expectErr {
|
if tt.expectErr {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -173,11 +175,11 @@ func TestVllmBuildCommandArgs_BooleanFields(t *testing.T) {
|
|||||||
|
|
||||||
func TestVllmBuildCommandArgs_ZeroValues(t *testing.T) {
|
func TestVllmBuildCommandArgs_ZeroValues(t *testing.T) {
|
||||||
options := backends.VllmServerOptions{
|
options := backends.VllmServerOptions{
|
||||||
Port: 0, // Should be excluded
|
Port: 0, // Should be excluded
|
||||||
TensorParallelSize: 0, // Should be excluded
|
TensorParallelSize: 0, // Should be excluded
|
||||||
GPUMemoryUtilization: 0, // Should be excluded
|
GPUMemoryUtilization: 0, // Should be excluded
|
||||||
Model: "", // Should be excluded (positional arg)
|
Model: "", // Should be excluded (positional arg)
|
||||||
Host: "", // Should be excluded
|
Host: "", // Should be excluded
|
||||||
EnableLogOutputs: false, // Should be excluded
|
EnableLogOutputs: false, // Should be excluded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user