Files
X-Agents/server/internal/handler/agent_handler.go

441 lines
13 KiB
Go
Raw Normal View History

package handler
import (
"log"
"net/http"
"strconv"
"strings"
"x-agents/server/internal/model"
"x-agents/server/internal/repository"
"x-agents/server/internal/service"
"x-agents/server/internal/utils"
"github.com/gin-gonic/gin"
)
// AgentHandler Agent 处理器
type AgentHandler struct {
agentService *service.AgentService
agentRepo *repository.AgentRepository
}
// NewAgentHandler 创建 Agent 处理器
func NewAgentHandler(agentService *service.AgentService, agentRepo *repository.AgentRepository) *AgentHandler {
return &AgentHandler{
agentService: agentService,
agentRepo: agentRepo,
}
}
// ChatRequest 对话请求
type ChatRequest struct {
AgentID string `json:"agent_id"` // 字符串类型,支持 UUID可为空当使用 mentioned_agent_ids 时)
Message string `json:"message" binding:"required"`
SessionID string `json:"session_id"`
ModelID string `json:"model_id"`
UseXBot bool `json:"use_xbot"`
MentionedAgentIDs []string `json:"mentioned_agent_ids"` // @ 提及的智能体 ID 列表
}
// ChatResponse 对话响应
type ChatResponse struct {
AgentID string `json:"agent_id"` // 支持 UUID 字符串
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"`
}
// 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"`
}
// Chat 单智能体对话
// @Summary 单智能体对话
// @Tags 智能体管理
// @Accept json
// @Produce json
// @Param request body ChatRequest true "对话请求"
// @Success 200 {object} ChatResponse
// @Router /api/agent/chat [post]
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从认证中间件获取
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
userID, _ := strconv.Atoi(userIDStr)
// 直接使用字符串类型的 agent_id支持 UUID
pythonReq := service.AgentChatRequest{
AgentID: req.AgentID,
Message: req.Message,
UserID: userID,
SessionID: req.SessionID,
ModelID: req.ModelID,
UseXBot: req.UseXBot,
}
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,
})
}
// ChatStream 单智能体对话(流式输出)
// @Summary 单智能体对话(流式输出)
// @Tags 智能体管理
// @Accept json
// @Produce text/event-stream
// @Param request body ChatRequest true "对话请求"
// @Router /api/agent/chat/stream [post]
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
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
userID, _ := strconv.Atoi(userIDStr)
// 直接使用字符串类型的 agent_id支持 UUID
agentID := req.AgentID
// 优先使用前端传递的 mentioned_agent_ids
if len(req.MentionedAgentIDs) > 0 {
// 如果有多个 @ 提及,使用第一个
mentionedAgentID := req.MentionedAgentIDs[0]
log.Printf("[ChatStream] Using mentioned_agent_ids: %v", req.MentionedAgentIDs)
agentID = mentionedAgentID
// 清理消息,移除 @ 提及
mentionParser := utils.NewMentionParser()
req.Message = mentionParser.RemoveMentions(req.Message)
} else if agentID == "" {
// 兼容:解析消息中的 @ 提及(备用方案)
mentionParser := utils.NewMentionParser()
mentions := mentionParser.ParseMentions(req.Message)
if len(mentions) > 0 {
mentionedAgent := h.findAgentByName(mentions[0])
if mentionedAgent != nil {
log.Printf("[ChatStream] Detected @mention: %s, routing to agent: %s", mentions[0], mentionedAgent.ID)
agentID = mentionedAgent.ID
}
req.Message = mentionParser.RemoveMentions(req.Message)
}
}
// 构建 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 服务的流式端点
err := h.agentService.ChatStream(c, agentID, req.Message, req.SessionID, req.ModelID, userID)
if err != nil && !c.IsAborted() {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
}
// findAgentByName 根据用户名查找智能体
func (h *AgentHandler) findAgentByName(name string) *model.Agent {
log.Printf("[findAgentByName] Searching for agent: %s, agentRepo: %v", name, h.agentRepo)
if h.agentRepo == nil {
log.Printf("[findAgentByName] ERROR: agentRepo is nil!")
return nil
}
// 先尝试精确匹配
agents, err := h.agentRepo.FindAll()
if err != nil {
return nil
}
for _, agent := range agents {
if agent.Name == name {
return &agent
}
}
// 再尝试模糊匹配(忽略大小写)
for _, agent := range agents {
if strings.Contains(strings.ToLower(agent.Name), strings.ToLower(name)) {
return &agent
}
}
return nil
}
// 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 多智能体群聊
// @Summary 多智能体群聊
// @Tags 智能体管理
// @Accept json
// @Produce json
// @Param request body TeamChatRequest true "群聊请求"
// @Success 200 {object} TeamChatResponse
// @Router /api/agent/team/chat [post]
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
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
userID, _ := strconv.Atoi(userIDStr)
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,
})
}
// 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)
}
// InitTeamMembersResponse 初始化团队成员响应
type InitTeamMembersResponse struct {
Message string `json:"message"`
Count int `json:"count"`
}
// InitTeamMembers 初始化团队成员智能体
// @Summary 初始化团队成员智能体
// @Tags 智能体管理
// @Produce json
// @Success 200 {object} InitTeamMembersResponse
// @Router /api/agent/init-team [post]
func (h *AgentHandler) InitTeamMembers(c *gin.Context) {
if err := h.agentService.InitTeamMembers(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, InitTeamMembersResponse{
Message: "Team members initialized successfully",
Count: 1, // 小荣
})
}
// 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)
}
// 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"`
Avatar string `json:"avatar"`
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
}
err := h.agentService.UpdateAgent(agentID, req.Name, req.Description, req.Avatar, req.Skills, req.RoleDescription, req.ModelProvider, req.ModelName)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "agent updated successfully"})
}