560 lines
18 KiB
Go
560 lines
18 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/kandev/kandev/internal/agent/agents"
|
|
"github.com/kandev/kandev/internal/agent/mcpconfig"
|
|
"github.com/kandev/kandev/internal/agent/settings/controller"
|
|
"github.com/kandev/kandev/internal/agent/settings/dto"
|
|
"github.com/kandev/kandev/internal/common/logger"
|
|
ws "github.com/kandev/kandev/pkg/websocket"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
var availableAgentsBroadcastTimeout = 10 * time.Second
|
|
|
|
type Handlers struct {
|
|
controller *controller.Controller
|
|
hub Broadcaster
|
|
logger *logger.Logger
|
|
}
|
|
|
|
type Broadcaster interface {
|
|
Broadcast(msg *ws.Message)
|
|
}
|
|
|
|
func NewHandlers(ctrl *controller.Controller, hub Broadcaster, log *logger.Logger) *Handlers {
|
|
return &Handlers{
|
|
controller: ctrl,
|
|
hub: hub,
|
|
logger: log.WithFields(zap.String("component", "agent-settings-handlers")),
|
|
}
|
|
}
|
|
|
|
func RegisterRoutes(router *gin.Engine, ctrl *controller.Controller, hub Broadcaster, log *logger.Logger) {
|
|
// Wire the install job store with the same broadcaster used by other
|
|
// agent-settings notifications so streaming install events reach the UI.
|
|
ctrl.SetJobBroadcaster(hub)
|
|
handlers := NewHandlers(ctrl, hub, log)
|
|
handlers.registerHTTP(router)
|
|
}
|
|
|
|
func (h *Handlers) registerHTTP(router *gin.Engine) {
|
|
api := router.Group("/api/v1")
|
|
api.GET("/agents/discovery", h.httpDiscoverAgents)
|
|
api.GET("/agents/available", h.httpListAvailableAgents)
|
|
api.GET("/agents", h.httpListAgents)
|
|
api.POST("/agents", h.httpCreateAgent)
|
|
api.POST("/agents/tui", h.httpCreateCustomTUIAgent)
|
|
api.GET("/agents/:id", h.httpGetAgent)
|
|
api.PATCH("/agents/:id", h.httpUpdateAgent)
|
|
api.DELETE("/agents/:id", h.httpDeleteAgent)
|
|
api.POST("/agents/:id/profiles", h.httpCreateProfile)
|
|
api.GET("/agents/:id/logo", h.httpGetAgentLogo)
|
|
api.GET("/agent-models/:agentName", h.httpGetAgentModels)
|
|
api.POST("/agent-command-preview/:agentName", h.httpPreviewAgentCommand)
|
|
api.POST("/agent-install/:agentName", h.httpInstallAgent)
|
|
api.GET("/agent-install/jobs", h.httpListInstallJobs)
|
|
api.GET("/agent-install/jobs/:id", h.httpGetInstallJob)
|
|
api.PATCH("/agent-profiles/:id", h.httpUpdateProfile)
|
|
api.DELETE("/agent-profiles/:id", h.httpDeleteProfile)
|
|
api.GET("/agent-profiles/:id/mcp-config", h.httpGetProfileMcpConfig)
|
|
api.POST("/agent-profiles/:id/mcp-config", h.httpUpdateProfileMcpConfig)
|
|
}
|
|
|
|
func (h *Handlers) httpDiscoverAgents(c *gin.Context) {
|
|
h.controller.InvalidateDiscoveryCache()
|
|
resp, err := h.controller.ListDiscovery(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("failed to discover agents", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to discover agents"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
h.broadcastAvailableAgentsAsync()
|
|
}
|
|
|
|
func (h *Handlers) httpListAvailableAgents(c *gin.Context) {
|
|
resp, err := h.controller.ListAvailableAgents(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("failed to list available agents", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list available agents"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
// httpInstallAgent enqueues an async install and returns the job snapshot.
|
|
// The install runs in a goroutine; clients track progress via WS
|
|
// (agent.install.started/output/finished) or by polling /agent-install/jobs/:id.
|
|
//
|
|
// Idempotent: a second POST for the same agent while a job is running returns
|
|
// the existing job_id rather than starting a duplicate.
|
|
func (h *Handlers) httpInstallAgent(c *gin.Context) {
|
|
name := strings.TrimSpace(c.Param("agentName"))
|
|
if name == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "agent name is required"})
|
|
return
|
|
}
|
|
job, err := h.controller.EnqueueInstall(name)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, controller.ErrAgentNotFound):
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent not found"})
|
|
case errors.Is(err, controller.ErrInstallScriptEmpty):
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "agent has no install script"})
|
|
case errors.Is(err, controller.ErrJobStoreUnavailable):
|
|
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "install service not ready"})
|
|
default:
|
|
h.logger.Error("failed to enqueue install", zap.String("agent", name), zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to enqueue install"})
|
|
}
|
|
return
|
|
}
|
|
c.JSON(http.StatusAccepted, job)
|
|
}
|
|
|
|
func (h *Handlers) httpListInstallJobs(c *gin.Context) {
|
|
c.JSON(http.StatusOK, dto.ListInstallJobsResponse{Jobs: h.controller.ListInstallJobs()})
|
|
}
|
|
|
|
func (h *Handlers) httpGetInstallJob(c *gin.Context) {
|
|
id := strings.TrimSpace(c.Param("id"))
|
|
if id == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "job id is required"})
|
|
return
|
|
}
|
|
job, ok := h.controller.GetInstallJob(id)
|
|
if !ok {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "job not found"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, job)
|
|
}
|
|
|
|
func (h *Handlers) broadcastAvailableAgentsAsync() {
|
|
if h.hub == nil {
|
|
return
|
|
}
|
|
go func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), availableAgentsBroadcastTimeout)
|
|
defer cancel()
|
|
|
|
availableResp, err := h.controller.ListAvailableAgents(ctx)
|
|
if err != nil {
|
|
h.logger.Error("failed to list available agents for broadcast", zap.Error(err))
|
|
return
|
|
}
|
|
notification, _ := ws.NewNotification(ws.ActionAgentAvailableUpdated, gin.H{
|
|
"agents": availableResp.Agents,
|
|
})
|
|
h.hub.Broadcast(notification)
|
|
}()
|
|
}
|
|
|
|
func (h *Handlers) httpListAgents(c *gin.Context) {
|
|
resp, err := h.controller.ListAgents(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("failed to list agents", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list agents"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
type createAgentRequest struct {
|
|
Name string `json:"name"`
|
|
WorkspaceID *string `json:"workspace_id,omitempty"`
|
|
Profiles []createAgentProfileRequest `json:"profiles,omitempty"`
|
|
}
|
|
|
|
type createAgentProfileRequest struct {
|
|
Name string `json:"name"`
|
|
Model string `json:"model"`
|
|
Mode string `json:"mode,omitempty"`
|
|
CLIFlags []dto.CLIFlagDTO `json:"cli_flags,omitempty"`
|
|
}
|
|
|
|
func (h *Handlers) httpCreateAgent(c *gin.Context) {
|
|
var body createAgentRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
if body.Name == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
|
|
return
|
|
}
|
|
profiles := make([]controller.CreateAgentProfileRequest, 0, len(body.Profiles))
|
|
for _, profile := range body.Profiles {
|
|
if strings.TrimSpace(profile.Name) == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "profile name is required"})
|
|
return
|
|
}
|
|
profiles = append(profiles, controller.CreateAgentProfileRequest{
|
|
Name: profile.Name,
|
|
Model: profile.Model,
|
|
Mode: profile.Mode,
|
|
CLIFlags: profile.CLIFlags,
|
|
})
|
|
}
|
|
resp, err := h.controller.CreateAgent(c.Request.Context(), controller.CreateAgentRequest{
|
|
Name: body.Name,
|
|
WorkspaceID: body.WorkspaceID,
|
|
Profiles: profiles,
|
|
})
|
|
if err != nil {
|
|
h.logger.Error("failed to create agent", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
func (h *Handlers) httpGetAgent(c *gin.Context) {
|
|
resp, err := h.controller.GetAgent(c.Request.Context(), c.Param("id"))
|
|
if err != nil {
|
|
if err == controller.ErrAgentNotFound {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent not found"})
|
|
return
|
|
}
|
|
h.logger.Error("failed to get agent", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get agent"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
type updateAgentRequest struct {
|
|
WorkspaceID *string `json:"workspace_id,omitempty"`
|
|
SupportsMCP *bool `json:"supports_mcp,omitempty"`
|
|
MCPConfigPath *string `json:"mcp_config_path,omitempty"`
|
|
}
|
|
|
|
func (h *Handlers) httpUpdateAgent(c *gin.Context) {
|
|
var body updateAgentRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
resp, err := h.controller.UpdateAgent(c.Request.Context(), controller.UpdateAgentRequest{
|
|
ID: c.Param("id"),
|
|
WorkspaceID: body.WorkspaceID,
|
|
SupportsMCP: body.SupportsMCP,
|
|
MCPConfigPath: body.MCPConfigPath,
|
|
})
|
|
if err != nil {
|
|
if err == controller.ErrAgentNotFound {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent not found"})
|
|
return
|
|
}
|
|
h.logger.Error("failed to update agent", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update agent"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
func (h *Handlers) httpDeleteAgent(c *gin.Context) {
|
|
if err := h.controller.DeleteAgent(c.Request.Context(), c.Param("id")); err != nil {
|
|
if err == controller.ErrAgentNotFound {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent not found"})
|
|
return
|
|
}
|
|
h.logger.Error("failed to delete agent", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete agent"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
h.broadcastAvailableAgentsAsync()
|
|
}
|
|
|
|
type updateProfileMcpConfigRequest struct {
|
|
Enabled *bool `json:"enabled"`
|
|
Servers map[string]mcpconfig.ServerDef `json:"servers"`
|
|
MCPServers map[string]mcpconfig.ServerDef `json:"mcpServers"`
|
|
Meta map[string]any `json:"meta,omitempty"`
|
|
}
|
|
|
|
func (h *Handlers) httpGetProfileMcpConfig(c *gin.Context) {
|
|
profileID := c.Param("id")
|
|
if profileID == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "profile id is required"})
|
|
return
|
|
}
|
|
|
|
resp, err := h.controller.GetAgentProfileMcpConfig(c.Request.Context(), profileID)
|
|
if err != nil {
|
|
if err == controller.ErrAgentProfileNotFound {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent profile not found"})
|
|
return
|
|
}
|
|
if err == controller.ErrAgentMcpUnsupported {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "mcp not supported by agent"})
|
|
return
|
|
}
|
|
h.logger.Error("failed to get mcp config", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get mcp config"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
func (h *Handlers) httpUpdateProfileMcpConfig(c *gin.Context) {
|
|
profileID := c.Param("id")
|
|
if profileID == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "profile id is required"})
|
|
return
|
|
}
|
|
|
|
var body updateProfileMcpConfigRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
if body.Enabled == nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "enabled is required"})
|
|
return
|
|
}
|
|
servers := body.Servers
|
|
if len(servers) == 0 && len(body.MCPServers) > 0 {
|
|
servers = body.MCPServers
|
|
}
|
|
if servers == nil {
|
|
servers = map[string]mcpconfig.ServerDef{}
|
|
}
|
|
|
|
resp, err := h.controller.UpdateAgentProfileMcpConfig(c.Request.Context(), profileID, controller.UpdateAgentProfileMcpConfigRequest{
|
|
Enabled: *body.Enabled,
|
|
Servers: servers,
|
|
Meta: body.Meta,
|
|
})
|
|
if err != nil {
|
|
if err == controller.ErrAgentProfileNotFound {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent profile not found"})
|
|
return
|
|
}
|
|
if err == controller.ErrAgentMcpUnsupported {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "mcp not supported by agent"})
|
|
return
|
|
}
|
|
h.logger.Error("failed to update mcp config", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update mcp config"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
type createProfileRequest struct {
|
|
Name string `json:"name"`
|
|
Model string `json:"model"`
|
|
Mode string `json:"mode,omitempty"`
|
|
AllowIndexing bool `json:"allow_indexing"`
|
|
CLIPassthrough bool `json:"cli_passthrough"`
|
|
CLIFlags []dto.CLIFlagDTO `json:"cli_flags,omitempty"`
|
|
}
|
|
|
|
func (h *Handlers) httpCreateProfile(c *gin.Context) {
|
|
var body createProfileRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
if strings.TrimSpace(body.Name) == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "profile name is required"})
|
|
return
|
|
}
|
|
resp, err := h.controller.CreateProfile(c.Request.Context(), controller.CreateProfileRequest{
|
|
AgentID: c.Param("id"),
|
|
Name: body.Name,
|
|
Model: body.Model,
|
|
Mode: body.Mode,
|
|
AllowIndexing: body.AllowIndexing,
|
|
CLIPassthrough: body.CLIPassthrough,
|
|
CLIFlags: body.CLIFlags,
|
|
})
|
|
if err != nil {
|
|
h.logger.Error("failed to create profile", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create profile"})
|
|
return
|
|
}
|
|
if h.hub != nil {
|
|
notification, _ := ws.NewNotification(ws.ActionAgentProfileCreated, gin.H{
|
|
"profile": resp,
|
|
})
|
|
h.hub.Broadcast(notification)
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
type updateProfileRequest struct {
|
|
Name *string `json:"name,omitempty"`
|
|
Model *string `json:"model,omitempty"`
|
|
Mode *string `json:"mode,omitempty"`
|
|
AllowIndexing *bool `json:"allow_indexing,omitempty"`
|
|
CLIPassthrough *bool `json:"cli_passthrough,omitempty"`
|
|
CLIFlags *[]dto.CLIFlagDTO `json:"cli_flags,omitempty"`
|
|
}
|
|
|
|
func (h *Handlers) httpUpdateProfile(c *gin.Context) {
|
|
var body updateProfileRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
if body.Name != nil && strings.TrimSpace(*body.Name) == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "profile name is required"})
|
|
return
|
|
}
|
|
resp, err := h.controller.UpdateProfile(c.Request.Context(), controller.UpdateProfileRequest{
|
|
ID: c.Param("id"),
|
|
Name: body.Name,
|
|
Model: body.Model,
|
|
Mode: body.Mode,
|
|
AllowIndexing: body.AllowIndexing,
|
|
CLIPassthrough: body.CLIPassthrough,
|
|
CLIFlags: body.CLIFlags,
|
|
})
|
|
if err != nil {
|
|
if err == controller.ErrAgentProfileNotFound {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent profile not found"})
|
|
return
|
|
}
|
|
h.logger.Error("failed to update profile", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update profile"})
|
|
return
|
|
}
|
|
if h.hub != nil {
|
|
notification, _ := ws.NewNotification(ws.ActionAgentProfileUpdated, gin.H{
|
|
"profile": resp,
|
|
})
|
|
h.hub.Broadcast(notification)
|
|
}
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
func (h *Handlers) httpDeleteProfile(c *gin.Context) {
|
|
force := c.Query("force") == "true"
|
|
profile, err := h.controller.DeleteProfile(c.Request.Context(), c.Param("id"), force)
|
|
if err != nil {
|
|
if err == controller.ErrAgentProfileNotFound {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent profile not found"})
|
|
return
|
|
}
|
|
var inUseErr *controller.ErrProfileInUseDetail
|
|
if errors.As(err, &inUseErr) {
|
|
c.JSON(http.StatusConflict, gin.H{
|
|
"error": "agent profile is used by active session(s)",
|
|
"active_sessions": inUseErr.ActiveSessions,
|
|
})
|
|
return
|
|
}
|
|
h.logger.Error("failed to delete profile", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete profile"})
|
|
return
|
|
}
|
|
if h.hub != nil {
|
|
notification, _ := ws.NewNotification(ws.ActionAgentProfileDeleted, gin.H{
|
|
"profile": profile,
|
|
})
|
|
h.hub.Broadcast(notification)
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
}
|
|
|
|
type commandPreviewRequest struct {
|
|
Model string `json:"model"`
|
|
PermissionSettings map[string]bool `json:"permission_settings"`
|
|
CLIPassthrough bool `json:"cli_passthrough"`
|
|
CLIFlags []dto.CLIFlagDTO `json:"cli_flags"`
|
|
}
|
|
|
|
func (h *Handlers) httpPreviewAgentCommand(c *gin.Context) {
|
|
agentName := c.Param("agentName")
|
|
if agentName == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "agent name is required"})
|
|
return
|
|
}
|
|
|
|
var body commandPreviewRequest
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
|
|
return
|
|
}
|
|
|
|
resp, err := h.controller.PreviewAgentCommand(c.Request.Context(), agentName, controller.CommandPreviewRequest{
|
|
Model: body.Model,
|
|
PermissionSettings: body.PermissionSettings,
|
|
CLIPassthrough: body.CLIPassthrough,
|
|
CLIFlags: body.CLIFlags,
|
|
})
|
|
if err != nil {
|
|
h.logger.Error("failed to preview agent command", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
// httpGetAgentLogo serves agent SVG logos.
|
|
// Note: :id here is the agent name (e.g. "claude-code"), not the UUID used by other /agents/:id routes.
|
|
func (h *Handlers) httpGetAgentLogo(c *gin.Context) {
|
|
agentName := c.Param("id")
|
|
if agentName == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "agent name is required"})
|
|
return
|
|
}
|
|
|
|
variant := agents.LogoLight
|
|
if c.Query("variant") == "dark" {
|
|
variant = agents.LogoDark
|
|
}
|
|
|
|
data, err := h.controller.GetAgentLogo(c.Request.Context(), agentName, variant)
|
|
if err != nil {
|
|
if err == controller.ErrAgentNotFound || err == controller.ErrLogoNotAvailable {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
h.logger.Error("failed to get agent logo", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get agent logo"})
|
|
return
|
|
}
|
|
|
|
c.Header("Cache-Control", "public, max-age=86400")
|
|
c.Data(http.StatusOK, "image/svg+xml", data)
|
|
}
|
|
|
|
func (h *Handlers) httpGetAgentModels(c *gin.Context) {
|
|
agentName := c.Param("agentName")
|
|
if agentName == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "agent name is required"})
|
|
return
|
|
}
|
|
|
|
// Check for refresh query parameter
|
|
refresh := c.Query("refresh") == "true"
|
|
|
|
resp, err := h.controller.FetchDynamicModels(c.Request.Context(), agentName, refresh)
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), "not found") {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "agent not found"})
|
|
return
|
|
}
|
|
h.logger.Error("failed to fetch agent models", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|