244 lines
6.5 KiB
Go
244 lines
6.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"chat-switchboard/models"
|
|
"chat-switchboard/providers"
|
|
"chat-switchboard/roles"
|
|
"chat-switchboard/store"
|
|
)
|
|
|
|
// RolesHandler manages model role configuration.
|
|
type RolesHandler struct {
|
|
stores store.Stores
|
|
resolver *roles.Resolver
|
|
}
|
|
|
|
// NewRolesHandler creates a roles handler.
|
|
func NewRolesHandler(s store.Stores, resolver *roles.Resolver) *RolesHandler {
|
|
return &RolesHandler{stores: s, resolver: resolver}
|
|
}
|
|
|
|
// ── List All Role Configs ──────────────────
|
|
// GET /admin/roles
|
|
|
|
func (h *RolesHandler) ListRoles(c *gin.Context) {
|
|
allRoles, err := h.stores.GlobalConfig.Get(c.Request.Context(), "model_roles")
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load roles"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, allRoles)
|
|
}
|
|
|
|
// ── Get Single Role Config ─────────────────
|
|
// GET /admin/roles/:role
|
|
|
|
func (h *RolesHandler) GetRole(c *gin.Context) {
|
|
role := c.Param("role")
|
|
if !roles.IsValidRole(role) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "unknown role: " + role})
|
|
return
|
|
}
|
|
|
|
cfg, err := h.resolver.GetConfig(c.Request.Context(), role, "", nil)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, cfg)
|
|
}
|
|
|
|
// ── Update Role Config ─────────────────────
|
|
// PUT /admin/roles/:role
|
|
|
|
func (h *RolesHandler) UpdateRole(c *gin.Context) {
|
|
role := c.Param("role")
|
|
if !roles.IsValidRole(role) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "unknown role: " + role})
|
|
return
|
|
}
|
|
|
|
var req roles.RoleConfig
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Load current global model_roles
|
|
allRoles, err := h.stores.GlobalConfig.Get(c.Request.Context(), "model_roles")
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load roles"})
|
|
return
|
|
}
|
|
if allRoles == nil {
|
|
allRoles = models.JSONMap{}
|
|
}
|
|
|
|
// Update the specific role
|
|
allRoles[role] = req
|
|
|
|
// Persist
|
|
userID := getUserID(c)
|
|
if err := h.stores.GlobalConfig.Set(c.Request.Context(), "model_roles", allRoles, userID); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save role"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, req)
|
|
}
|
|
|
|
// ── Test Role ──────────────────────────────
|
|
// POST /admin/roles/:role/test
|
|
|
|
func (h *RolesHandler) TestRole(c *gin.Context) {
|
|
role := c.Param("role")
|
|
if !roles.IsValidRole(role) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "unknown role: " + role})
|
|
return
|
|
}
|
|
|
|
if role == roles.RoleEmbedding {
|
|
// Test embedding
|
|
result, err := h.resolver.Embed(c.Request.Context(), role, "", nil, []string{"test embedding"})
|
|
if err != nil {
|
|
c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"status": "ok",
|
|
"model": result.Model,
|
|
"provider": result.ProviderID,
|
|
"dimensions": len(result.Embeddings[0]),
|
|
"used_fallback": result.UsedFallback,
|
|
})
|
|
return
|
|
}
|
|
|
|
// Test completion with a minimal prompt
|
|
result, err := h.resolver.Complete(c.Request.Context(), role, "", nil, []providers.Message{
|
|
{Role: "user", Content: "Say 'ok' and nothing else."},
|
|
})
|
|
if err != nil {
|
|
c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"status": "ok",
|
|
"model": result.Model,
|
|
"provider": result.ProviderID,
|
|
"content": result.Content,
|
|
"input_tokens": result.InputTokens,
|
|
"output_tokens": result.OutputTokens,
|
|
"used_fallback": result.UsedFallback,
|
|
})
|
|
}
|
|
|
|
// ── Team Role Overrides ────────────────────
|
|
|
|
// ListTeamRoles returns role overrides for a specific team.
|
|
// GET /teams/:teamId/roles
|
|
func (h *RolesHandler) ListTeamRoles(c *gin.Context) {
|
|
teamID := c.Param("teamId")
|
|
|
|
team, err := h.stores.Teams.GetByID(c.Request.Context(), teamID)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "team not found"})
|
|
return
|
|
}
|
|
|
|
roleOverrides := make(map[string]interface{})
|
|
if team.Settings != nil {
|
|
if raw, ok := team.Settings["model_roles"]; ok {
|
|
if m, ok := raw.(map[string]interface{}); ok {
|
|
roleOverrides = m
|
|
}
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": roleOverrides})
|
|
}
|
|
|
|
// UpdateTeamRole sets a team role override.
|
|
// PUT /teams/:teamId/roles/:role
|
|
func (h *RolesHandler) UpdateTeamRole(c *gin.Context) {
|
|
teamID := c.Param("teamId")
|
|
role := c.Param("role")
|
|
if !roles.IsValidRole(role) {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "unknown role: " + role})
|
|
return
|
|
}
|
|
|
|
var req roles.RoleConfig
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
team, err := h.stores.Teams.GetByID(c.Request.Context(), teamID)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "team not found"})
|
|
return
|
|
}
|
|
|
|
settings := team.Settings
|
|
if settings == nil {
|
|
settings = models.JSONMap{}
|
|
}
|
|
|
|
roleOverrides, _ := settings["model_roles"].(map[string]interface{})
|
|
if roleOverrides == nil {
|
|
roleOverrides = make(map[string]interface{})
|
|
}
|
|
roleOverrides[role] = req
|
|
settings["model_roles"] = roleOverrides
|
|
|
|
if err := h.stores.Teams.Update(c.Request.Context(), teamID, map[string]interface{}{
|
|
"settings": settings,
|
|
}); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save team role"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, req)
|
|
}
|
|
|
|
// DeleteTeamRole removes a team role override (falls back to global).
|
|
// DELETE /teams/:teamId/roles/:role
|
|
func (h *RolesHandler) DeleteTeamRole(c *gin.Context) {
|
|
teamID := c.Param("teamId")
|
|
role := c.Param("role")
|
|
|
|
team, err := h.stores.Teams.GetByID(c.Request.Context(), teamID)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "team not found"})
|
|
return
|
|
}
|
|
|
|
settings := team.Settings
|
|
if settings == nil {
|
|
c.JSON(http.StatusOK, gin.H{"message": "no override to remove"})
|
|
return
|
|
}
|
|
|
|
roleOverrides, _ := settings["model_roles"].(map[string]interface{})
|
|
if roleOverrides != nil {
|
|
delete(roleOverrides, role)
|
|
settings["model_roles"] = roleOverrides
|
|
}
|
|
|
|
if err := h.stores.Teams.Update(c.Request.Context(), teamID, map[string]interface{}{
|
|
"settings": settings,
|
|
}); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to remove team role"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "override removed"})
|
|
}
|