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>
This commit is contained in:
46
backend/app/api/approval.py
Normal file
46
backend/app/api/approval.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""工具审批 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,
|
||||
}
|
||||
Reference in New Issue
Block a user