Files
aiagent/backend/app/api/agents.py

334 lines
10 KiB
Python
Raw Normal View History

2026-01-19 00:09:36 +08:00
"""
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