2026-03-11 16:25:48 +08:00
|
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"net/http"
|
2026-03-13 21:26:27 +08:00
|
|
|
|
"strconv"
|
2026-03-11 16:25:48 +08:00
|
|
|
|
|
|
|
|
|
|
"x-agents/server/internal/service"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// AgentHandler Agent 处理器
|
|
|
|
|
|
type AgentHandler struct {
|
|
|
|
|
|
agentService *service.AgentService
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewAgentHandler 创建 Agent 处理器
|
|
|
|
|
|
func NewAgentHandler(agentService *service.AgentService) *AgentHandler {
|
|
|
|
|
|
return &AgentHandler{
|
|
|
|
|
|
agentService: agentService,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ChatRequest 对话请求
|
|
|
|
|
|
type ChatRequest struct {
|
2026-03-13 21:26:27 +08:00
|
|
|
|
AgentID string `json:"agent_id" binding:"required"` // 字符串类型
|
2026-03-11 16:25:48 +08:00
|
|
|
|
Message string `json:"message" binding:"required"`
|
|
|
|
|
|
SessionID string `json:"session_id"`
|
2026-03-11 17:22:40 +08:00
|
|
|
|
ModelID string `json:"model_id"`
|
2026-03-12 10:49:44 +08:00
|
|
|
|
UseXBot bool `json:"use_xbot"`
|
2026-03-11 16:25:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ChatResponse 对话响应
|
|
|
|
|
|
type ChatResponse struct {
|
2026-03-15 19:49:27 +08:00
|
|
|
|
AgentID string `json:"agent_id"` // 支持 UUID 字符串
|
2026-03-11 16:25:48 +08:00
|
|
|
|
Reply string `json:"reply"`
|
|
|
|
|
|
ToolsUsed []string `json:"tools_used"`
|
|
|
|
|
|
SessionID string `json:"session_id"`
|
|
|
|
|
|
TokensUsed int `json:"tokens_used"`
|
|
|
|
|
|
DurationMs int `json:"duration_ms"`
|
|
|
|
|
|
Metadata interface{} `json:"metadata"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 17:16:53 +08:00
|
|
|
|
|
|
|
|
|
|
// CreateAgentResponse 创建智能体响应
|
|
|
|
|
|
type CreateAgentResponse struct {
|
|
|
|
|
|
AgentID int `json:"agent_id"`
|
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ListAgentsResponse 获取智能体列表响应
|
|
|
|
|
|
type ListAgentsResponse struct {
|
|
|
|
|
|
Agents []interface{} `json:"agents"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 16:25:48 +08:00
|
|
|
|
// Chat 单智能体对话
|
2026-03-12 17:16:53 +08:00
|
|
|
|
// @Summary 单智能体对话
|
|
|
|
|
|
// @Tags 智能体管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param request body ChatRequest true "对话请求"
|
|
|
|
|
|
// @Success 200 {object} ChatResponse
|
|
|
|
|
|
// @Router /api/agent/chat [post]
|
2026-03-11 16:25:48 +08:00
|
|
|
|
func (h *AgentHandler) Chat(c *gin.Context) {
|
|
|
|
|
|
var req ChatRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户 ID(从认证中间件获取)
|
2026-03-13 21:26:27 +08:00
|
|
|
|
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
|
|
|
|
|
|
userID, _ := strconv.Atoi(userIDStr)
|
|
|
|
|
|
|
2026-03-15 19:49:27 +08:00
|
|
|
|
// 直接使用字符串类型的 agent_id,支持 UUID
|
2026-03-11 16:25:48 +08:00
|
|
|
|
pythonReq := service.AgentChatRequest{
|
2026-03-15 19:49:27 +08:00
|
|
|
|
AgentID: req.AgentID,
|
2026-03-11 16:25:48 +08:00
|
|
|
|
Message: req.Message,
|
|
|
|
|
|
UserID: userID,
|
|
|
|
|
|
SessionID: req.SessionID,
|
2026-03-11 17:22:40 +08:00
|
|
|
|
ModelID: req.ModelID,
|
2026-03-12 10:49:44 +08:00
|
|
|
|
UseXBot: req.UseXBot,
|
2026-03-11 16:25:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.agentService.Chat(pythonReq)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转换工具调用
|
|
|
|
|
|
toolsUsed := make([]string, 0)
|
|
|
|
|
|
for _, tool := range result.ToolCalls {
|
|
|
|
|
|
if toolMap, ok := tool.(map[string]interface{}); ok {
|
|
|
|
|
|
if skillID, ok := toolMap["skill_id"].(string); ok {
|
|
|
|
|
|
toolsUsed = append(toolsUsed, skillID)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, ChatResponse{
|
|
|
|
|
|
AgentID: result.AgentID,
|
|
|
|
|
|
Reply: result.Response,
|
|
|
|
|
|
ToolsUsed: toolsUsed,
|
|
|
|
|
|
SessionID: result.SessionID,
|
|
|
|
|
|
TokensUsed: result.TokensUsed,
|
|
|
|
|
|
DurationMs: result.DurationMs,
|
|
|
|
|
|
Metadata: nil,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 10:49:44 +08:00
|
|
|
|
// ChatStream 单智能体对话(流式输出)
|
2026-03-12 17:16:53 +08:00
|
|
|
|
// @Summary 单智能体对话(流式输出)
|
|
|
|
|
|
// @Tags 智能体管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce text/event-stream
|
|
|
|
|
|
// @Param request body ChatRequest true "对话请求"
|
|
|
|
|
|
// @Router /api/agent/chat/stream [post]
|
2026-03-12 10:49:44 +08:00
|
|
|
|
func (h *AgentHandler) ChatStream(c *gin.Context) {
|
|
|
|
|
|
var req ChatRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户 ID
|
2026-03-13 21:26:27 +08:00
|
|
|
|
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
|
|
|
|
|
|
userID, _ := strconv.Atoi(userIDStr)
|
|
|
|
|
|
|
2026-03-15 19:49:27 +08:00
|
|
|
|
// 直接使用字符串类型的 agent_id,支持 UUID
|
|
|
|
|
|
agentID := req.AgentID
|
2026-03-12 10:49:44 +08:00
|
|
|
|
|
|
|
|
|
|
// 构建 SSE 流
|
|
|
|
|
|
c.Header("Content-Type", "text/event-stream")
|
|
|
|
|
|
c.Header("Cache-Control", "no-cache")
|
|
|
|
|
|
c.Header("Connection", "keep-alive")
|
|
|
|
|
|
c.Header("Access-Control-Allow-Origin", "*")
|
|
|
|
|
|
|
|
|
|
|
|
// 调用 Python 服务的流式端点
|
2026-03-13 21:26:27 +08:00
|
|
|
|
err := h.agentService.ChatStream(c, agentID, req.Message, req.SessionID, req.ModelID, userID)
|
2026-03-12 10:49:44 +08:00
|
|
|
|
if err != nil && !c.IsAborted() {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 16:25:48 +08:00
|
|
|
|
// TeamChatRequest 多智能体群聊请求
|
|
|
|
|
|
type TeamChatRequest struct {
|
|
|
|
|
|
SupervisorAgentID int `json:"supervisor_agent_id" binding:"required"`
|
|
|
|
|
|
MemberAgentIDs []int `json:"member_agent_ids" binding:"required"`
|
|
|
|
|
|
Message string `json:"message" binding:"required"`
|
|
|
|
|
|
SessionID string `json:"session_id"`
|
|
|
|
|
|
Strategy string `json:"strategy"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TeamChatResponse 多智能体群聊响应
|
|
|
|
|
|
type TeamChatResponse struct {
|
|
|
|
|
|
SupervisorAgentID int `json:"supervisor_agent_id"`
|
|
|
|
|
|
Reply string `json:"reply"`
|
|
|
|
|
|
SubtaskResults interface{} `json:"subtask_results"`
|
|
|
|
|
|
Strategy string `json:"strategy"`
|
|
|
|
|
|
SessionID string `json:"session_id"`
|
|
|
|
|
|
DurationMs int `json:"duration_ms"`
|
|
|
|
|
|
Metadata interface{} `json:"metadata"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TeamChat 多智能体群聊
|
2026-03-12 17:16:53 +08:00
|
|
|
|
// @Summary 多智能体群聊
|
|
|
|
|
|
// @Tags 智能体管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param request body TeamChatRequest true "群聊请求"
|
|
|
|
|
|
// @Success 200 {object} TeamChatResponse
|
|
|
|
|
|
// @Router /api/agent/team/chat [post]
|
2026-03-11 16:25:48 +08:00
|
|
|
|
func (h *AgentHandler) TeamChat(c *gin.Context) {
|
|
|
|
|
|
var req TeamChatRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户 ID
|
2026-03-13 21:26:27 +08:00
|
|
|
|
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
|
|
|
|
|
|
userID, _ := strconv.Atoi(userIDStr)
|
2026-03-11 16:25:48 +08:00
|
|
|
|
|
|
|
|
|
|
pythonReq := service.TeamChatRequest{
|
|
|
|
|
|
SupervisorAgentID: req.SupervisorAgentID,
|
|
|
|
|
|
MemberAgentIDs: req.MemberAgentIDs,
|
|
|
|
|
|
Message: req.Message,
|
|
|
|
|
|
UserID: userID,
|
|
|
|
|
|
SessionID: req.SessionID,
|
|
|
|
|
|
Strategy: req.Strategy,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.agentService.TeamChat(pythonReq)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, TeamChatResponse{
|
|
|
|
|
|
SupervisorAgentID: result.SupervisorAgentID,
|
|
|
|
|
|
Reply: result.Response,
|
|
|
|
|
|
SubtaskResults: result.SubtaskResults,
|
|
|
|
|
|
Strategy: result.Strategy,
|
|
|
|
|
|
SessionID: result.SessionID,
|
|
|
|
|
|
DurationMs: result.DurationMs,
|
|
|
|
|
|
Metadata: nil,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2026-03-12 17:16:53 +08:00
|
|
|
|
|
|
|
|
|
|
// CreateAgent 创建智能体
|
|
|
|
|
|
// @Summary 创建智能体
|
|
|
|
|
|
// @Tags 智能体管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param request body CreateAgentRequest true "创建智能体请求"
|
|
|
|
|
|
// @Success 200 {object} CreateAgentResponse
|
|
|
|
|
|
// @Router /api/agent/create [post]
|
|
|
|
|
|
func (h *AgentHandler) CreateAgent(c *gin.Context) {
|
|
|
|
|
|
var req service.CreateAgentRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户 ID
|
|
|
|
|
|
userID := 1 // TODO: 从 c.Get("user_id") 获取
|
|
|
|
|
|
|
|
|
|
|
|
result, err := h.agentService.CreateAgent(req, userID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ListAgents 获取智能体列表
|
|
|
|
|
|
// @Summary 获取智能体列表
|
|
|
|
|
|
// @Tags 智能体管理
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Success 200 {object} ListAgentsResponse
|
|
|
|
|
|
// @Router /api/agent/list [get]
|
|
|
|
|
|
func (h *AgentHandler) ListAgents(c *gin.Context) {
|
|
|
|
|
|
result, err := h.agentService.ListAgents()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
|
|
|
|
}
|
2026-03-12 23:18:46 +08:00
|
|
|
|
|
|
|
|
|
|
// UpdateAgentStatusRequest 更新状态请求
|
|
|
|
|
|
type UpdateAgentStatusRequest struct {
|
|
|
|
|
|
IsActive bool `json:"is_active"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateAgentStatus 更新智能体状态
|
|
|
|
|
|
// @Summary 更新智能体状态
|
|
|
|
|
|
// @Tags 智能体管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param id path string true "Agent ID"
|
|
|
|
|
|
// @Param request body UpdateAgentStatusRequest true "状态请求"
|
|
|
|
|
|
// @Success 200 {object} gin.H
|
|
|
|
|
|
// @Router /api/agent/{id}/status [put]
|
|
|
|
|
|
func (h *AgentHandler) UpdateAgentStatus(c *gin.Context) {
|
|
|
|
|
|
agentID := c.Param("id")
|
|
|
|
|
|
if agentID == "" {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "agent id is required"})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var req UpdateAgentStatusRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err := h.agentService.UpdateAgentStatus(agentID, req.IsActive)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "status updated successfully"})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteAgent 删除智能体
|
|
|
|
|
|
// @Summary 删除智能体
|
|
|
|
|
|
// @Tags 智能体管理
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param id path string true "Agent ID"
|
|
|
|
|
|
// @Success 200 {object} gin.H
|
|
|
|
|
|
// @Router /api/agent/{id} [delete]
|
|
|
|
|
|
func (h *AgentHandler) DeleteAgent(c *gin.Context) {
|
|
|
|
|
|
agentID := c.Param("id")
|
|
|
|
|
|
if agentID == "" {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "agent id is required"})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err := h.agentService.DeleteAgent(agentID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "agent deleted successfully"})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateAgentRequest 更新智能体请求
|
|
|
|
|
|
type UpdateAgentRequest struct {
|
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
|
Description string `json:"description"`
|
2026-03-15 19:49:27 +08:00
|
|
|
|
Avatar string `json:"avatar"`
|
2026-03-12 23:18:46 +08:00
|
|
|
|
Skills []string `json:"skills"`
|
|
|
|
|
|
RoleDescription string `json:"role_description"`
|
|
|
|
|
|
ModelProvider string `json:"model_provider"`
|
|
|
|
|
|
ModelName string `json:"model_name"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateAgent 更新智能体
|
|
|
|
|
|
// @Summary 更新智能体
|
|
|
|
|
|
// @Tags 智能体管理
|
|
|
|
|
|
// @Accept json
|
|
|
|
|
|
// @Produce json
|
|
|
|
|
|
// @Param id path string true "Agent ID"
|
|
|
|
|
|
// @Param request body UpdateAgentRequest true "更新请求"
|
|
|
|
|
|
// @Success 200 {object} gin.H
|
|
|
|
|
|
// @Router /api/agent/{id} [put]
|
|
|
|
|
|
func (h *AgentHandler) UpdateAgent(c *gin.Context) {
|
|
|
|
|
|
agentID := c.Param("id")
|
|
|
|
|
|
if agentID == "" {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "agent id is required"})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var req UpdateAgentRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-15 19:49:27 +08:00
|
|
|
|
err := h.agentService.UpdateAgent(agentID, req.Name, req.Description, req.Avatar, req.Skills, req.RoleDescription, req.ModelProvider, req.ModelName)
|
2026-03-12 23:18:46 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "agent updated successfully"})
|
|
|
|
|
|
}
|