From 891f6a5b5a3b41400840fa67592800e3045dcd5d Mon Sep 17 00:00:00 2001 From: Benson Wong Date: Tue, 17 Dec 2024 14:37:44 -0800 Subject: [PATCH] Add /upstream endpoint (#30) * remove catch-all route to upstream proxy (it was broken anyways) * add /upstream/:model_id to swap and route to upstream path * add /upstream HTML endpoint and unlisted option * add /upstream endpoint to show a list of available models * add `unlisted` configuration option to omit a model from /v1/models and /upstream lists * add favicon.ico --- README.md | 11 ++++- config.example.yaml | 3 ++ misc/assets/favicon-raw.png | Bin 0 -> 52428 bytes proxy/config.go | 1 + proxy/html/favicon.ico | Bin 0 -> 15406 bytes proxy/proxymanager.go | 78 +++++++++++++++++++++++++----- proxy/proxymanager_loghandlers.go | 7 --- 7 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 misc/assets/favicon-raw.png create mode 100644 proxy/html/favicon.ico diff --git a/README.md b/README.md index e997993..dfbfbba 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ llama-swap is an OpenAI API compatible server that gives you complete control ov Features: - ✅ Easy to deploy: single binary with no dependencies -- ✅ Single yaml configuration file +- ✅ Easy to config: single yaml file - ✅ On-demand model switching - ✅ Full control over server settings per model - ✅ OpenAI API support (`v1/completions` and `v1/chat/completions`) @@ -16,7 +16,8 @@ Features: - ✅ Run multiple models at once with `profiles` - ✅ Remote log monitoring at `/log` - ✅ Automatic unloading of models from GPUs after timeout -- ✅ Use any local server that provides an OpenAI compatible API (llama.cpp, vllm, tabblyAPI, etc) +- ✅ Use any local OpenAI compatible server (llama.cpp, vllm, tabblyAPI, etc) +- ✅ Direct access to proxied upstream HTTP server via `/upstream/:model_id` ## Releases @@ -73,6 +74,12 @@ models: --model path/to/Qwen2.5-1.5B-Instruct-Q4_K_M.gguf proxy: http://127.0.0.1:8999 + # unlisted models do not show up in /v1/models or /upstream lists + # but they can still be requested as normal + "qwen-unlisted": + cmd: llama-server --port 9999 -m Llama-3.2-1B-Instruct-Q4_K_M.gguf -ngl 0 + unlisted: true + # profiles make it easy to managing multi model (and gpu) configurations. # # Tips: diff --git a/config.example.yaml b/config.example.yaml index 583b4b2..093ff9e 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -33,6 +33,7 @@ models: - env1=hello cmd: build/simple-responder --port 8999 proxy: http://127.0.0.1:8999 + unlisted: true # use "none" to skip check. Caution this may cause some requests to fail # until the upstream server is ready for traffic @@ -42,9 +43,11 @@ models: "broken": cmd: models/llama-server-osx --port 8999 -m models/doesnotexist.gguf proxy: http://127.0.0.1:8999 + unlisted: true "broken_timeout": cmd: models/llama-server-osx --port 8999 -m models/qwen2.5-0.5b-instruct-q8_0.gguf proxy: http://127.0.0.1:9000 + unlisted: true # creating a coding profile with models for code generation and general questions profiles: diff --git a/misc/assets/favicon-raw.png b/misc/assets/favicon-raw.png new file mode 100644 index 0000000000000000000000000000000000000000..191c37cdce94d02f8194f5d0e6653ceb9f6bfd70 GIT binary patch literal 52428 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 KMNUMnLSTX)1z)oO literal 0 HcmV?d00001 diff --git a/proxy/config.go b/proxy/config.go index f17e019..f3bef77 100644 --- a/proxy/config.go +++ b/proxy/config.go @@ -16,6 +16,7 @@ type ModelConfig struct { Env []string `yaml:"env"` CheckEndpoint string `yaml:"checkEndpoint"` UnloadAfter int `yaml:"ttl"` + Unlisted bool `yaml:"unlisted"` } func (m *ModelConfig) SanitizedCommand() ([]string, error) { diff --git a/proxy/html/favicon.ico b/proxy/html/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..388ef73f0877dc29f441a9b4d2d4e3e946dbe777 GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/proxy/proxymanager.go b/proxy/proxymanager.go index 0c26990..b418243 100644 --- a/proxy/proxymanager.go +++ b/proxy/proxymanager.go @@ -2,10 +2,12 @@ package proxy import ( "bytes" + "embed" "encoding/json" "fmt" "io" "net/http" + "sort" "strconv" "strings" "sync" @@ -18,6 +20,15 @@ const ( PROFILE_SPLIT_CHAR = ":" ) +//go:embed html/favicon.ico +var faviconData []byte + +//go:embed html/logs.html +var logsHTML []byte + +// make sure embed is kept there by the IDE auto-package importer +var _ = embed.FS{} + type ProxyManager struct { sync.Mutex @@ -48,7 +59,12 @@ func New(config *Config) *ProxyManager { pm.ginEngine.GET("/logs/stream", pm.streamLogsHandler) pm.ginEngine.GET("/logs/streamSSE", pm.streamLogsHandlerSSE) - pm.ginEngine.NoRoute(pm.proxyNoRouteHandler) + pm.ginEngine.GET("/upstream", pm.upstreamIndex) + pm.ginEngine.Any("/upstream/:model_id/*upstreamPath", pm.proxyToUpstream) + + pm.ginEngine.GET("/favicon.ico", func(c *gin.Context) { + c.Data(http.StatusOK, "image/x-icon", faviconData) + }) // Disable console color for testing gin.DisableConsoleColor() @@ -86,7 +102,11 @@ func (pm *ProxyManager) stopProcesses() { func (pm *ProxyManager) listModelsHandler(c *gin.Context) { data := []interface{}{} - for id := range pm.config.Models { + for id, modelConfig := range pm.config.Models { + if modelConfig.Unlisted { + continue + } + data = append(data, map[string]interface{}{ "id": id, "object": "model", @@ -113,7 +133,7 @@ func (pm *ProxyManager) swapModel(requestedModel string) (*Process, error) { pm.Lock() defer pm.Unlock() - // Check if requestedModel contains a / + // Check if requestedModel contains a PROFILE_SPLIT_CHAR profileName, modelName := "", requestedModel if idx := strings.Index(requestedModel, PROFILE_SPLIT_CHAR); idx != -1 { profileName = requestedModel[:idx] @@ -170,6 +190,48 @@ func (pm *ProxyManager) swapModel(requestedModel string) (*Process, error) { return pm.currentProcesses[requestedProcessKey], nil } +func (pm *ProxyManager) proxyToUpstream(c *gin.Context) { + requestedModel := c.Param("model_id") + + if requestedModel == "" { + c.AbortWithError(http.StatusBadRequest, fmt.Errorf("model id required in path")) + return + } + + if process, err := pm.swapModel(requestedModel); err != nil { + c.AbortWithError(http.StatusNotFound, fmt.Errorf("unable to swap to model, %s", err.Error())) + } else { + // rewrite the path + c.Request.URL.Path = c.Param("upstreamPath") + process.ProxyRequest(c.Writer, c.Request) + } +} + +func (pm *ProxyManager) upstreamIndex(c *gin.Context) { + var html strings.Builder + + html.WriteString("\n

Available Models

    ") + + // Extract keys and sort them + var modelIDs []string + for modelID, modelConfig := range pm.config.Models { + if modelConfig.Unlisted { + continue + } + + modelIDs = append(modelIDs, modelID) + } + sort.Strings(modelIDs) + + // Iterate over sorted keys + for _, modelID := range modelIDs { + html.WriteString(fmt.Sprintf("
  • %s
  • ", modelID, modelID)) + } + html.WriteString("
") + c.Header("Content-Type", "text/html") + c.String(http.StatusOK, html.String()) +} + func (pm *ProxyManager) proxyChatRequestHandler(c *gin.Context) { bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { @@ -201,16 +263,6 @@ func (pm *ProxyManager) proxyChatRequestHandler(c *gin.Context) { } } -func (pm *ProxyManager) proxyNoRouteHandler(c *gin.Context) { - // since maps are unordered, just use the first available process if one exists - for _, process := range pm.currentProcesses { - process.ProxyRequest(c.Writer, c.Request) - return - } - - c.AbortWithError(http.StatusBadRequest, fmt.Errorf("no strategy to handle request")) -} - func ProcessKeyName(groupName, modelName string) string { return groupName + PROFILE_SPLIT_CHAR + modelName } diff --git a/proxy/proxymanager_loghandlers.go b/proxy/proxymanager_loghandlers.go index fc2ac4d..36cea1c 100644 --- a/proxy/proxymanager_loghandlers.go +++ b/proxy/proxymanager_loghandlers.go @@ -1,7 +1,6 @@ package proxy import ( - "embed" "fmt" "net/http" "strings" @@ -9,12 +8,6 @@ import ( "github.com/gin-gonic/gin" ) -//go:embed html/logs.html -var logsHTML []byte - -// make sure embed is kept there by the IDE auto-package importer -var _ = embed.FS{} - func (pm *ProxyManager) sendLogsHandlers(c *gin.Context) { accept := c.GetHeader("Accept")