mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 08:57:57 +00:00
- Lua adapters per upstream (transform_request/response/stream_chunk, build_headers signing hooks) - AUTO priority routing with per-model kind (chat/image), explicit source/model routing - Per-source concurrency caps with queueing, exponential backoff, AUTO failover - OpenAI-compatible API: chat completions, SSE streaming, image generations, models - Gateway key auth, web UI for adapter/source management, runtime persistence - e2e test running the real binary against mocked upstreams
116 lines
3.7 KiB
Go
116 lines
3.7 KiB
Go
package gateway
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"llmsproxy/internal/config"
|
|
)
|
|
|
|
type adapterPayload struct {
|
|
Name string `json:"name"`
|
|
Code string `json:"code"`
|
|
}
|
|
|
|
func (g *Gateway) handleAdaptersAPI(w http.ResponseWriter, r *http.Request) {
|
|
path := strings.TrimPrefix(r.URL.Path, "/api/adapters")
|
|
path = strings.Trim(path, "/")
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
if path == "" {
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{"adapters": g.core.ListAdapters()})
|
|
return
|
|
}
|
|
writeError(w, http.StatusNotFound, "not_found", "adapter code not exposed; edit in UI")
|
|
case http.MethodPost:
|
|
var p adapterPayload
|
|
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid_request", "invalid json: "+err.Error())
|
|
return
|
|
}
|
|
if err := g.core.UploadAdapter(p.Name, p.Code); err != nil {
|
|
writeError(w, http.StatusBadRequest, "adapter_error", err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true, "name": p.Name})
|
|
case http.MethodDelete:
|
|
if path == "" {
|
|
writeError(w, http.StatusBadRequest, "invalid_request", "adapter name required")
|
|
return
|
|
}
|
|
if err := g.core.RemoveAdapter(path); err != nil {
|
|
writeError(w, http.StatusBadRequest, "adapter_error", err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
|
|
default:
|
|
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "")
|
|
}
|
|
}
|
|
|
|
// sourcePayload mirrors config.Source for JSON web UI editing.
|
|
type sourcePayload struct {
|
|
Name string `json:"name"`
|
|
BaseURL string `json:"base_url"`
|
|
APIKey string `json:"api_key"`
|
|
Adapter string `json:"adapter"`
|
|
Endpoint string `json:"endpoint"`
|
|
ImageEndpoint string `json:"image_endpoint"`
|
|
Models []config.Model `json:"models"`
|
|
Headers map[string]string `json:"headers"`
|
|
Meta map[string]interface{} `json:"meta"`
|
|
Temperature float64 `json:"temperature"`
|
|
MaxTokens int `json:"max_tokens"`
|
|
MaxConcurrent int `json:"max_concurrent"`
|
|
}
|
|
|
|
func (g *Gateway) handleSourcesAPI(w http.ResponseWriter, r *http.Request) {
|
|
path := strings.TrimPrefix(r.URL.Path, "/api/sources")
|
|
path = strings.Trim(path, "/")
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{"sources": g.core.Sources()})
|
|
case http.MethodPost:
|
|
var p sourcePayload
|
|
body, _ := io.ReadAll(r.Body)
|
|
if err := json.Unmarshal(body, &p); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid_request", "invalid json: "+err.Error())
|
|
return
|
|
}
|
|
src := config.Source{
|
|
Name: p.Name,
|
|
BaseURL: p.BaseURL,
|
|
APIKey: p.APIKey,
|
|
Adapter: p.Adapter,
|
|
Endpoint: p.Endpoint,
|
|
ImageEndpoint: p.ImageEndpoint,
|
|
Models: p.Models,
|
|
Headers: p.Headers,
|
|
Meta: p.Meta,
|
|
Temperature: p.Temperature,
|
|
MaxTokens: p.MaxTokens,
|
|
MaxConcurrent: p.MaxConcurrent,
|
|
}
|
|
if err := g.core.AddSource(src); err != nil {
|
|
writeError(w, http.StatusBadRequest, "source_error", err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
|
|
case http.MethodDelete:
|
|
if path == "" {
|
|
writeError(w, http.StatusBadRequest, "invalid_request", "source name required")
|
|
return
|
|
}
|
|
if err := g.core.RemoveSource(path); err != nil {
|
|
writeError(w, http.StatusBadRequest, "source_error", err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{"ok": true})
|
|
default:
|
|
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "")
|
|
}
|
|
} |