mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-11-06 00:54:23 +00:00
Improve instance management
This commit is contained in:
@@ -1,12 +1,52 @@
|
|||||||
package llamactl
|
package llamactl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
ID uuid.UUID
|
ID uuid.UUID
|
||||||
Port int
|
Args []string
|
||||||
Status string
|
|
||||||
Options *InstanceOptions
|
Options *InstanceOptions
|
||||||
|
|
||||||
|
// Status
|
||||||
|
Running bool
|
||||||
|
|
||||||
|
// Output channels
|
||||||
|
StdOut chan string // Channel for sending output messages
|
||||||
|
StdErr chan string // Channel for sending error messages
|
||||||
|
|
||||||
|
// internal
|
||||||
|
cmd *exec.Cmd // Command to run the instance
|
||||||
|
ctx context.Context // Context for managing the instance lifecycle
|
||||||
|
cancel context.CancelFunc // Function to cancel the context
|
||||||
|
stdout io.ReadCloser // Standard output stream
|
||||||
|
stderr io.ReadCloser // Standard error stream
|
||||||
|
mu sync.Mutex // Mutex for synchronizing access to the instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInstance(id uuid.UUID, options *InstanceOptions) *Instance {
|
||||||
|
return &Instance{
|
||||||
|
ID: id,
|
||||||
|
Args: options.BuildCommandArgs(),
|
||||||
|
Options: options,
|
||||||
|
|
||||||
|
Running: false,
|
||||||
|
|
||||||
|
StdOut: make(chan string, 100),
|
||||||
|
StdErr: make(chan string, 100),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) Start() *exec.Cmd {
|
||||||
|
args := i.Options.BuildCommandArgs()
|
||||||
|
cmd := exec.Command("llama-server", args...)
|
||||||
|
|
||||||
|
cmd.Start()
|
||||||
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,15 +46,30 @@ func (im *instanceManager) ListInstances() ([]*Instance, error) {
|
|||||||
// CreateInstance creates a new instance with the given options and returns it.
|
// CreateInstance creates a new instance with the given options and returns it.
|
||||||
// The instance is initially in a "stopped" state.
|
// The instance is initially in a "stopped" state.
|
||||||
func (im *instanceManager) CreateInstance(options *InstanceOptions) (*Instance, error) {
|
func (im *instanceManager) CreateInstance(options *InstanceOptions) (*Instance, error) {
|
||||||
id := uuid.New()
|
if options == nil {
|
||||||
instance := &Instance{
|
return nil, fmt.Errorf("instance options cannot be nil")
|
||||||
ID: id,
|
|
||||||
Port: 8080, // Default port, can be changed later
|
|
||||||
Status: "stopped", // Initial status
|
|
||||||
Options: options,
|
|
||||||
}
|
}
|
||||||
im.instances[id] = instance
|
|
||||||
|
// Generate a unique ID for the new instance
|
||||||
|
id := uuid.New()
|
||||||
|
for im.instances[id] != nil {
|
||||||
|
id = uuid.New() // Ensure unique ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign a port if not specified
|
||||||
|
if options.Port == 0 {
|
||||||
|
port, err := im.getNextAvailablePort()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get next available port: %w", err)
|
||||||
|
}
|
||||||
|
options.Port = port
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := NewInstance(id, options)
|
||||||
|
im.instances[instance.ID] = instance
|
||||||
|
|
||||||
return instance, nil
|
return instance, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInstance retrieves an instance by its ID.
|
// GetInstance retrieves an instance by its ID.
|
||||||
@@ -83,8 +98,8 @@ func (im *instanceManager) DeleteInstance(id uuid.UUID) error {
|
|||||||
return fmt.Errorf("instance with ID %s not found", id)
|
return fmt.Errorf("instance with ID %s not found", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if im.instances[id].Status != "stopped" {
|
if im.instances[id].Running {
|
||||||
return fmt.Errorf("cannot delete a running instance, stop it first")
|
return fmt.Errorf("instance with ID %s is still running, stop it before deleting", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(im.instances, id)
|
delete(im.instances, id)
|
||||||
@@ -98,13 +113,11 @@ func (im *instanceManager) StartInstance(id uuid.UUID) (*Instance, error) {
|
|||||||
if !exists {
|
if !exists {
|
||||||
return nil, fmt.Errorf("instance with ID %s not found", id)
|
return nil, fmt.Errorf("instance with ID %s not found", id)
|
||||||
}
|
}
|
||||||
if instance.Status == "running" {
|
if instance.Running {
|
||||||
return instance, fmt.Errorf("instance with ID %s is already running", id)
|
return instance, fmt.Errorf("instance with ID %s is already running", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:
|
//TODO:
|
||||||
|
|
||||||
instance.Status = "running"
|
|
||||||
return instance, nil
|
return instance, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,12 +127,11 @@ func (im *instanceManager) StopInstance(id uuid.UUID) (*Instance, error) {
|
|||||||
if !exists {
|
if !exists {
|
||||||
return nil, fmt.Errorf("instance with ID %s not found", id)
|
return nil, fmt.Errorf("instance with ID %s not found", id)
|
||||||
}
|
}
|
||||||
if instance.Status == "stopped" {
|
if !instance.Running {
|
||||||
return instance, fmt.Errorf("instance with ID %s is already stopped", id)
|
return instance, fmt.Errorf("instance with ID %s is already stopped", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
instance.Status = "stopped"
|
|
||||||
return instance, nil
|
return instance, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,35 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type InstanceOptions struct {
|
type InstanceOptions struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"` // Display name
|
||||||
|
|
||||||
|
// Auto restart
|
||||||
|
AutoRestart bool `json:"auto_restart,omitempty"`
|
||||||
|
MaxRestarts int `json:"max_restarts,omitempty"`
|
||||||
|
RestartDelay int `json:"restart_delay,omitempty"` // in seconds
|
||||||
|
|
||||||
*LlamaServerOptions
|
*LlamaServerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements custom JSON unmarshaling with default values
|
||||||
|
func (o *InstanceOptions) UnmarshalJSON(data []byte) error {
|
||||||
|
// Set defaults first
|
||||||
|
o.AutoRestart = true
|
||||||
|
o.MaxRestarts = 3
|
||||||
|
o.RestartDelay = 5
|
||||||
|
|
||||||
|
// Create a temporary struct to avoid recursion
|
||||||
|
type tempInstanceOptions InstanceOptions
|
||||||
|
temp := (*tempInstanceOptions)(o)
|
||||||
|
|
||||||
|
// Unmarshal into the temporary struct
|
||||||
|
if err := json.Unmarshal(data, temp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type LlamaServerOptions struct {
|
type LlamaServerOptions struct {
|
||||||
// Common params
|
// Common params
|
||||||
VerbosePrompt bool `json:"verbose_prompt,omitempty"`
|
VerbosePrompt bool `json:"verbose_prompt,omitempty"`
|
||||||
|
|||||||
Reference in New Issue
Block a user