diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index f88aa3c..7366a03 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -188,14 +188,6 @@ func (i *Instance) GetProxy() (*httputil.ReverseProxy, error) { return nil, fmt.Errorf("instance %s has no proxy component", i.Name) } - // Remote instances should not use local proxy - they are handled by RemoteInstanceProxy - opts := i.GetOptions() - if opts != nil && len(opts.Nodes) > 0 { - if _, isLocal := opts.Nodes[i.localNodeName]; !isLocal { - return nil, fmt.Errorf("instance %s is a remote instance and should not use local proxy", i.Name) - } - } - return i.proxy.get() } diff --git a/pkg/instance/instance_test.go b/pkg/instance/instance_test.go index 3fb5795..a843845 100644 --- a/pkg/instance/instance_test.go +++ b/pkg/instance/instance_test.go @@ -577,7 +577,9 @@ func TestRemoteInstanceOperations(t *testing.T) { LlamaCpp: config.BackendSettings{Command: "llama-server"}, }, Instances: config.InstancesConfig{LogsDir: "/tmp/test"}, - Nodes: map[string]config.NodeConfig{}, + Nodes: map[string]config.NodeConfig{ + "remote-node": {Address: "http://remote-node:8080"}, + }, LocalNode: "main", } options := &instance.Options{ @@ -612,8 +614,8 @@ func TestRemoteInstanceOperations(t *testing.T) { } // GetProxy should fail for remote instance - if _, err := inst.GetProxy(); err == nil { - t.Error("Expected error when getting proxy for remote instance") + if _, err := inst.GetProxy(); err != nil { + t.Error("Expected no error when getting proxy for remote instance") } // GetLogs should fail for remote instance diff --git a/pkg/server/handlers.go b/pkg/server/handlers.go index 9e31df9..4ddbfea 100644 --- a/pkg/server/handlers.go +++ b/pkg/server/handlers.go @@ -4,8 +4,6 @@ import ( "llamactl/pkg/config" "llamactl/pkg/manager" "net/http" - "net/http/httputil" - "sync" "time" ) @@ -13,8 +11,6 @@ type Handler struct { InstanceManager manager.InstanceManager cfg config.AppConfig httpClient *http.Client - remoteProxies map[string]*httputil.ReverseProxy // Cache of remote proxies by instance name - remoteProxiesMu sync.RWMutex } func NewHandler(im manager.InstanceManager, cfg config.AppConfig) *Handler { @@ -24,6 +20,5 @@ func NewHandler(im manager.InstanceManager, cfg config.AppConfig) *Handler { httpClient: &http.Client{ Timeout: 30 * time.Second, }, - remoteProxies: make(map[string]*httputil.ReverseProxy), } } diff --git a/pkg/server/handlers_backends.go b/pkg/server/handlers_backends.go index 43d01ad..74cd0bd 100644 --- a/pkg/server/handlers_backends.go +++ b/pkg/server/handlers_backends.go @@ -49,7 +49,7 @@ func (h *Handler) LlamaCppProxy(onDemandStart bool) http.HandlerFunc { return } - if !inst.IsRunning() { + if !inst.IsRemote() && !inst.IsRunning() { if !(onDemandStart && options.OnDemandStart != nil && *options.OnDemandStart) { http.Error(w, "Instance is not running", http.StatusServiceUnavailable) diff --git a/pkg/server/handlers_instances.go b/pkg/server/handlers_instances.go index dfe9971..10b88d7 100644 --- a/pkg/server/handlers_instances.go +++ b/pkg/server/handlers_instances.go @@ -7,8 +7,6 @@ import ( "llamactl/pkg/manager" "llamactl/pkg/validation" "net/http" - "net/http/httputil" - "net/url" "strconv" "strings" @@ -375,12 +373,6 @@ func (h *Handler) ProxyToInstance() http.HandlerFunc { return } - // Check if this is a remote instance - if inst.IsRemote() { - h.RemoteInstanceProxy(w, r, validatedName, inst) - return - } - if !inst.IsRunning() { http.Error(w, "Instance is not running", http.StatusServiceUnavailable) return @@ -393,9 +385,12 @@ func (h *Handler) ProxyToInstance() http.HandlerFunc { return } - // Strip the "/api/v1/instances//proxy" prefix from the request URL - prefix := fmt.Sprintf("/api/v1/instances/%s/proxy", validatedName) - r.URL.Path = strings.TrimPrefix(r.URL.Path, prefix) + // Check if this is a remote instance + if !inst.IsRemote() { + // Strip the "/api/v1/instances//proxy" prefix from the request URL + prefix := fmt.Sprintf("/api/v1/instances/%s/proxy", validatedName) + r.URL.Path = strings.TrimPrefix(r.URL.Path, prefix) + } // Update the last request time for the instance inst.UpdateLastRequestTime() @@ -408,66 +403,3 @@ func (h *Handler) ProxyToInstance() http.HandlerFunc { proxy.ServeHTTP(w, r) } } - -// RemoteInstanceProxy proxies requests to a remote instance -func (h *Handler) RemoteInstanceProxy(w http.ResponseWriter, r *http.Request, name string, inst *instance.Instance) { - // Get the node name from instance options - options := inst.GetOptions() - if options == nil { - http.Error(w, "Instance has no options configured", http.StatusInternalServerError) - return - } - - // Get the first node from the set - var nodeName string - for node := range options.Nodes { - nodeName = node - break - } - if nodeName == "" { - http.Error(w, "Instance has no node configured", http.StatusInternalServerError) - return - } - - // Check if we have a cached proxy for this node - h.remoteProxiesMu.RLock() - proxy, exists := h.remoteProxies[nodeName] - h.remoteProxiesMu.RUnlock() - - if !exists { - // Find node configuration - nodeConfig, exists := h.cfg.Nodes[nodeName] - if !exists { - http.Error(w, fmt.Sprintf("Node %s not found", nodeName), http.StatusInternalServerError) - return - } - - // Create reverse proxy to remote node - targetURL, err := url.Parse(nodeConfig.Address) - if err != nil { - http.Error(w, "Failed to parse node address: "+err.Error(), http.StatusInternalServerError) - return - } - - proxy = httputil.NewSingleHostReverseProxy(targetURL) - - // Modify request before forwarding - originalDirector := proxy.Director - apiKey := nodeConfig.APIKey // Capture for closure - proxy.Director = func(req *http.Request) { - originalDirector(req) - // Add API key if configured - if apiKey != "" { - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey)) - } - } - - // Cache the proxy by node name - h.remoteProxiesMu.Lock() - h.remoteProxies[nodeName] = proxy - h.remoteProxiesMu.Unlock() - } - - // Forward the request using the cached proxy - proxy.ServeHTTP(w, r) -} diff --git a/pkg/server/handlers_openai.go b/pkg/server/handlers_openai.go index bddabf3..fd9b818 100644 --- a/pkg/server/handlers_openai.go +++ b/pkg/server/handlers_openai.go @@ -3,13 +3,9 @@ package server import ( "bytes" "encoding/json" - "fmt" "io" - "llamactl/pkg/instance" "llamactl/pkg/validation" "net/http" - "net/http/httputil" - "net/url" ) // OpenAIListInstances godoc @@ -100,15 +96,7 @@ func (h *Handler) OpenAIProxy() http.HandlerFunc { return } - // Check if this is a remote instance - if inst.IsRemote() { - // Restore the body for the remote proxy - r.Body = io.NopCloser(bytes.NewReader(bodyBytes)) - h.RemoteOpenAIProxy(w, r, validatedName, inst) - return - } - - if !inst.IsRunning() { + if !inst.IsRemote() && !inst.IsRunning() { options := inst.GetOptions() allowOnDemand := options != nil && options.OnDemandStart != nil && *options.OnDemandStart if !allowOnDemand { @@ -158,66 +146,3 @@ func (h *Handler) OpenAIProxy() http.HandlerFunc { proxy.ServeHTTP(w, r) } } - -// RemoteOpenAIProxy proxies OpenAI-compatible requests to a remote instance -func (h *Handler) RemoteOpenAIProxy(w http.ResponseWriter, r *http.Request, modelName string, inst *instance.Instance) { - // Get the node name from instance options - options := inst.GetOptions() - if options == nil { - http.Error(w, "Instance has no options configured", http.StatusInternalServerError) - return - } - - // Get the first node from the set - var nodeName string - for node := range options.Nodes { - nodeName = node - break - } - if nodeName == "" { - http.Error(w, "Instance has no node configured", http.StatusInternalServerError) - return - } - - // Check if we have a cached proxy for this node - h.remoteProxiesMu.RLock() - proxy, exists := h.remoteProxies[nodeName] - h.remoteProxiesMu.RUnlock() - - if !exists { - // Find node configuration - nodeConfig, exists := h.cfg.Nodes[nodeName] - if !exists { - http.Error(w, fmt.Sprintf("Node %s not found", nodeName), http.StatusInternalServerError) - return - } - - // Create reverse proxy to remote node - targetURL, err := url.Parse(nodeConfig.Address) - if err != nil { - http.Error(w, "Failed to parse node address: "+err.Error(), http.StatusInternalServerError) - return - } - - proxy = httputil.NewSingleHostReverseProxy(targetURL) - - // Modify request before forwarding - originalDirector := proxy.Director - apiKey := nodeConfig.APIKey // Capture for closure - proxy.Director = func(req *http.Request) { - originalDirector(req) - // Add API key if configured - if apiKey != "" { - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey)) - } - } - - // Cache the proxy - h.remoteProxiesMu.Lock() - h.remoteProxies[nodeName] = proxy - h.remoteProxiesMu.Unlock() - } - - // Forward the request using the cached proxy - proxy.ServeHTTP(w, r) -}