""" Agent管理API """ from fastapi import APIRouter, Depends, HTTPException, status, Query from sqlalchemy.orm import Session from pydantic import BaseModel from typing import List, Optional, Dict, Any from datetime import datetime import logging from app.core.database import get_db from app.models.agent import Agent from app.api.auth import get_current_user from app.models.user import User from app.core.exceptions import NotFoundError, ValidationError, ConflictError from app.services.permission_service import check_agent_permission from app.services.workflow_validator import validate_workflow import uuid logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/v1/agents", tags=["agents"], responses={ 401: {"description": "未授权"}, 404: {"description": "资源不存在"}, 400: {"description": "请求参数错误"}, 500: {"description": "服务器内部错误"} } ) class AgentCreate(BaseModel): """Agent创建模型""" name: str description: Optional[str] = None workflow_config: Dict[str, Any] # 包含nodes和edges class AgentUpdate(BaseModel): """Agent更新模型""" name: Optional[str] = None description: Optional[str] = None workflow_config: Optional[Dict[str, Any]] = None status: Optional[str] = None class AgentResponse(BaseModel): """Agent响应模型""" id: str name: str description: Optional[str] workflow_config: Dict[str, Any] version: int status: str user_id: str created_at: datetime updated_at: datetime class Config: from_attributes = True @router.get("", response_model=List[AgentResponse]) async def get_agents( skip: int = Query(0, ge=0, description="跳过记录数"), limit: int = Query(100, ge=1, le=100, description="每页记录数"), search: Optional[str] = Query(None, description="搜索关键词(按名称或描述)"), status: Optional[str] = Query(None, description="状态筛选"), db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 获取Agent列表 支持分页、搜索、状态筛选 """ # 管理员可以看到所有Agent,普通用户只能看到自己拥有的或有read权限的 if current_user.role == "admin": query = db.query(Agent) else: # 获取用户拥有或有read权限的Agent from sqlalchemy import or_ from app.models.permission import AgentPermission # 用户拥有的Agent owned_agents = db.query(Agent.id).filter(Agent.user_id == current_user.id).subquery() # 用户有read权限的Agent(通过用户ID或角色) user_permissions = db.query(AgentPermission.agent_id).filter( AgentPermission.permission_type == "read", or_( AgentPermission.user_id == current_user.id, AgentPermission.role_id.in_([r.id for r in current_user.roles]) ) ).subquery() query = db.query(Agent).filter( or_( Agent.id.in_(db.query(owned_agents.c.id)), Agent.id.in_(db.query(user_permissions.c.agent_id)) ) ) # 搜索:按名称或描述搜索 if search: search_pattern = f"%{search}%" query = query.filter( (Agent.name.like(search_pattern)) | (Agent.description.like(search_pattern)) ) # 筛选:按状态筛选 if status: query = query.filter(Agent.status == status) # 排序和分页 agents = query.order_by(Agent.created_at.desc()).offset(skip).limit(limit).all() return agents @router.post("", response_model=AgentResponse, status_code=status.HTTP_201_CREATED) async def create_agent( agent_data: AgentCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 创建Agent 创建时会验证工作流配置的有效性 """ # 验证工作流配置 if "nodes" not in agent_data.workflow_config or "edges" not in agent_data.workflow_config: raise ValidationError("工作流配置必须包含nodes和edges") nodes = agent_data.workflow_config.get("nodes", []) edges = agent_data.workflow_config.get("edges", []) # 验证工作流 validation_result = validate_workflow(nodes, edges) if not validation_result["valid"]: raise ValidationError(f"工作流配置验证失败: {', '.join(validation_result['errors'])}") # 检查名称是否重复 existing_agent = db.query(Agent).filter( Agent.name == agent_data.name, Agent.user_id == current_user.id ).first() if existing_agent: raise ConflictError(f"Agent名称 '{agent_data.name}' 已存在") # 创建Agent agent = Agent( name=agent_data.name, description=agent_data.description, workflow_config=agent_data.workflow_config, user_id=current_user.id, status="draft" ) db.add(agent) db.commit() db.refresh(agent) logger.info(f"用户 {current_user.username} 创建了Agent: {agent.name} ({agent.id})") return agent @router.get("/{agent_id}", response_model=AgentResponse) async def get_agent( agent_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 获取Agent详情 """ agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise NotFoundError(f"Agent不存在: {agent_id}") # 检查权限:read权限 if not check_agent_permission(db, current_user, agent, "read"): raise HTTPException(status_code=403, detail="无权访问此Agent") return agent @router.put("/{agent_id}", response_model=AgentResponse) async def update_agent( agent_id: str, agent_data: AgentUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 更新Agent 如果更新了workflow_config,会验证工作流配置的有效性 """ agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise NotFoundError(f"Agent不存在: {agent_id}") # 检查权限:write权限 if not check_agent_permission(db, current_user, agent, "write"): raise HTTPException(status_code=403, detail="无权修改此Agent") # 更新字段 if agent_data.name is not None: # 检查名称是否重复(排除当前Agent) existing_agent = db.query(Agent).filter( Agent.name == agent_data.name, Agent.user_id == current_user.id, Agent.id != agent_id ).first() if existing_agent: raise ConflictError(f"Agent名称 '{agent_data.name}' 已存在") agent.name = agent_data.name if agent_data.description is not None: agent.description = agent_data.description if agent_data.workflow_config is not None: # 验证工作流配置 if "nodes" not in agent_data.workflow_config or "edges" not in agent_data.workflow_config: raise ValidationError("工作流配置必须包含nodes和edges") nodes = agent_data.workflow_config.get("nodes", []) edges = agent_data.workflow_config.get("edges", []) validation_result = validate_workflow(nodes, edges) if not validation_result["valid"]: raise ValidationError(f"工作流配置验证失败: {', '.join(validation_result['errors'])}") agent.workflow_config = agent_data.workflow_config agent.version += 1 # 版本号自增 if agent_data.status is not None: valid_statuses = ["draft", "published", "running", "stopped"] if agent_data.status not in valid_statuses: raise ValidationError(f"无效的状态: {agent_data.status}") agent.status = agent_data.status db.commit() db.refresh(agent) logger.info(f"用户 {current_user.username} 更新了Agent: {agent.name} ({agent.id})") return agent @router.delete("/{agent_id}", status_code=status.HTTP_200_OK) async def delete_agent( agent_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 删除Agent(只有所有者可以删除) """ agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise NotFoundError(f"Agent不存在: {agent_id}") # 只有Agent所有者可以删除 if agent.user_id != current_user.id and current_user.role != "admin": raise HTTPException(status_code=403, detail="无权删除此Agent") agent_name = agent.name db.delete(agent) db.commit() logger.info(f"用户 {current_user.username} 删除了Agent: {agent_name} ({agent_id})") return {"message": "Agent已删除"} @router.post("/{agent_id}/deploy", response_model=AgentResponse, status_code=status.HTTP_200_OK) async def deploy_agent( agent_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 部署Agent 将Agent状态设置为published """ agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise NotFoundError(f"Agent不存在: {agent_id}") # 检查权限:deploy权限 if not check_agent_permission(db, current_user, agent, "deploy"): raise HTTPException(status_code=403, detail="无权部署此Agent") agent.status = "published" db.commit() db.refresh(agent) logger.info(f"用户 {current_user.username} 部署了Agent: {agent.name} ({agent_id})") return agent @router.post("/{agent_id}/stop", response_model=AgentResponse, status_code=status.HTTP_200_OK) async def stop_agent( agent_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 停止Agent 将Agent状态设置为stopped """ agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise NotFoundError(f"Agent不存在: {agent_id}") # 检查权限:deploy权限(停止也需要deploy权限) if not check_agent_permission(db, current_user, agent, "deploy"): raise HTTPException(status_code=403, detail="无权停止此Agent") agent.status = "stopped" db.commit() db.refresh(agent) logger.info(f"用户 {current_user.username} 停止了Agent: {agent.name} ({agent_id})") return agent