Files
aiagent/backend/app/api/approval.py
renjianbo f3cb35c460 feat: Phase 2 - Orchestrator workflow node + tool-level human approval
2.1 Orchestrator in workflow:
- New run_orchestrator_node() in workflow_integration.py loads agents from DB,
  supports route/sequential/debate/pipeline modes
- New 'orchestrator' node type in workflow_engine.py execute_node dispatch

2.2 Tool-level human approval:
- AgentToolConfig extended with require_approval, approval_timeout_ms,
  approval_default fields
- New ApprovalManager (approval_manager.py) with asyncio.Event-based
  create/wait_for_decision/resolve pattern
- AgentRuntime run() and run_stream() intercept tool execution,
  wait for approval decision before executing
- New POST /api/v1/approval/{id}/resolve REST endpoint
- Frontend: approval_required SSE event handling, approval dialog UI
  with approve/deny/skip buttons

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-04 23:17:59 +08:00

47 lines
1.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""工具审批 REST API — 前端提交审批决定"""
from __future__ import annotations
import logging
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
from app.services.approval_manager import approval_manager
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v1/approval", tags=["approval"])
class ApprovalDecisionRequest(BaseModel):
decision: str = Field(..., description="审批决定: approved | denied | skip")
@router.post("/{approval_id}/resolve")
async def resolve_approval(approval_id: str, req: ApprovalDecisionRequest):
"""提交工具审批决定。
- **approved**: 批准执行
- **denied**: 拒绝执行Agent 会收到拒绝提示)
- **skip**: 跳过该工具Agent 会跳过继续)
"""
ok = approval_manager.resolve(approval_id, req.decision)
if not ok:
raise HTTPException(status_code=404, detail=f"审批请求不存在或已完成: {approval_id}")
return {"success": True, "approval_id": approval_id, "decision": req.decision}
@router.get("/{approval_id}")
async def get_approval(approval_id: str):
"""查询待审批请求详情(用于前端展示工具名和参数)。"""
req = approval_manager.get_pending(approval_id)
if not req:
raise HTTPException(status_code=404, detail=f"审批请求不存在或已完成: {approval_id}")
return {
"approval_id": req.approval_id,
"tool_name": req.tool_name,
"args": req.args,
"decision": req.decision,
}