package admin import ( "encoding/json" "net/http" "github.com/gin-gonic/gin" "github.com/rose_cat707/luminary/internal/model" "github.com/rose_cat707/luminary/internal/provider" "github.com/rose_cat707/luminary/internal/store/cache" ) func (h *Handler) ListEndpointMeta(c *gin.Context) { category := model.ProviderCategory(c.Query("category")) if category == "" { category = model.CategoryLLM } providerType := model.ProviderType(c.Query("type")) if providerType == "" { providerType = provider.DefaultProviderType(category) } endpoints := provider.EndpointsForCategory(category) providerTypes := provider.TypesForCategory(category) type item struct { provider.EndpointMeta DefaultPath string `json:"default_path"` } out := make([]item, 0, len(endpoints)) for _, e := range endpoints { out = append(out, item{ EndpointMeta: e, DefaultPath: provider.DefaultPath(providerType, e.Key), }) } c.JSON(http.StatusOK, gin.H{ "categories": []gin.H{ {"key": model.CategoryLLM, "label": provider.CategoryLabel(model.CategoryLLM), "supported": true}, {"key": model.CategoryEmbedding, "label": provider.CategoryLabel(model.CategoryEmbedding), "supported": true}, {"key": model.CategoryRerank, "label": provider.CategoryLabel(model.CategoryRerank), "supported": true}, {"key": model.CategorySpeech, "label": provider.CategoryLabel(model.CategorySpeech), "supported": false}, {"key": model.CategoryImage, "label": provider.CategoryLabel(model.CategoryImage), "supported": true}, {"key": model.CategoryAudio, "label": provider.CategoryLabel(model.CategoryAudio), "supported": false}, }, "provider_types": providerTypes, "all_provider_types": provider.AllProviderTypes(), "endpoints": out, }) } func (h *Handler) ListProviders(c *gin.Context) { category := c.Query("category") providers := h.cache.ListProviders() if category != "" { filtered := providers[:0] for _, p := range providers { if string(p.Category) == category { filtered = append(filtered, p) } } providers = filtered } out := make([]model.Provider, 0, len(providers)) for _, p := range providers { p.Models = h.cache.GetModels(p.ID) out = append(out, p) } c.JSON(http.StatusOK, gin.H{"data": out}) } func (h *Handler) CreateProvider(c *gin.Context) { var req struct { Name string `json:"name"` Type model.ProviderType `json:"type"` Category model.ProviderCategory `json:"category"` BaseURL string `json:"base_url"` AllowedEndpoints []string `json:"allowed_endpoints"` EndpointPaths map[string]string `json:"endpoint_paths"` } if err := c.ShouldBindJSON(&req); err != nil || req.Name == "" || req.Type == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) return } if req.Category == "" { req.Category = model.CategoryLLM } if !model.IsCategorySupported(req.Category) { c.JSON(http.StatusBadRequest, gin.H{"error": "category not supported"}) return } if !provider.IsTypeValidForCategory(req.Category, req.Type) { c.JSON(http.StatusBadRequest, gin.H{"error": "protocol type not supported for this category"}) return } if h.providerNameTaken(req.Name, 0) { c.JSON(http.StatusBadRequest, gin.H{"error": "provider name already exists"}) return } allowed := req.AllowedEndpoints if len(allowed) == 0 { allowed = provider.DefaultEndpointsForCategory(req.Category) } if err := provider.ValidateCategoryEndpoints(req.Category, allowed); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } pathsJSON, _ := json.Marshal(req.EndpointPaths) if req.EndpointPaths == nil { pathsJSON = []byte("{}") } p := model.Provider{ Name: req.Name, Type: req.Type, Category: req.Category, BaseURL: req.BaseURL, AllowedEndpointsJSON: cache.ModelsToJSON(allowed), EndpointPathsJSON: string(pathsJSON), Status: model.ProviderUnknown, } if err := h.db.DB().Create(&p).Error; err != nil { if h.providerNameTaken(req.Name, 0) { c.JSON(http.StatusBadRequest, gin.H{"error": "provider name already exists"}) return } c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } h.cache.SetProvider(p) c.JSON(http.StatusCreated, p) } func (h *Handler) GetProvider(c *gin.Context) { id := parseUint(c.Param("id")) p, ok := h.cache.GetProvider(id) if !ok { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } p.Keys = h.cache.ListProviderKeys(id) p.Models = h.cache.GetModels(id) c.JSON(http.StatusOK, p) } func (h *Handler) UpdateProvider(c *gin.Context) { id := parseUint(c.Param("id")) p, ok := h.cache.GetProvider(id) if !ok { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } var req struct { Name string `json:"name"` BaseURL string `json:"base_url"` Category model.ProviderCategory `json:"category"` AllowedEndpoints []string `json:"allowed_endpoints"` EndpointPaths map[string]string `json:"endpoint_paths"` Enabled *bool `json:"enabled"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) return } if req.Name != "" && req.Name != p.Name { if h.providerNameTaken(req.Name, p.ID) { c.JSON(http.StatusBadRequest, gin.H{"error": "provider name already exists"}) return } p.Name = req.Name } if req.BaseURL != "" { p.BaseURL = req.BaseURL } if req.Category != "" { if !model.IsCategorySupported(req.Category) { c.JSON(http.StatusBadRequest, gin.H{"error": "category not supported yet"}) return } p.Category = req.Category } if req.AllowedEndpoints != nil { if err := provider.ValidateCategoryEndpoints(p.Category, req.AllowedEndpoints); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } p.AllowedEndpointsJSON = cache.ModelsToJSON(req.AllowedEndpoints) } if req.EndpointPaths != nil { b, _ := json.Marshal(req.EndpointPaths) p.EndpointPathsJSON = string(b) } if req.Enabled != nil { p.Enabled = *req.Enabled } h.db.DB().Save(&p) h.cache.SetProvider(p) c.JSON(http.StatusOK, p) }