第一次提交
This commit is contained in:
333
backend/app/api/agents.py
Normal file
333
backend/app/api/agents.py
Normal file
@@ -0,0 +1,333 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user