auto-sync: 2026-05-19 13:54:58
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"""Checkpoint API — M3 人工确认点
|
||||
|
||||
Agent 创建 checkpoint → 用户 approve/reject → 后端自动推进 task 状态
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
from src.blackboard.operations import Blackboard
|
||||
from src.blackboard.registry import ProjectRegistry
|
||||
|
||||
router = APIRouter(prefix="/api/projects/{project_id}/tasks/{task_id}/checkpoints", tags=["checkpoints"])
|
||||
|
||||
|
||||
# ── 请求模型 ──
|
||||
|
||||
class CreateCheckpointRequest(BaseModel):
|
||||
type: str # verify | decision | action
|
||||
title: str
|
||||
payload: dict
|
||||
description: Optional[str] = None
|
||||
id: Optional[str] = None # 可选自定义 ID
|
||||
|
||||
|
||||
class ResolveCheckpointRequest(BaseModel):
|
||||
resolved_by: str = "user"
|
||||
note: Optional[str] = None
|
||||
|
||||
|
||||
# ── 工具 ──
|
||||
|
||||
def _bb(project_id: str) -> Blackboard:
|
||||
registry = ProjectRegistry()
|
||||
db_path = registry.get_db_path(project_id)
|
||||
if not db_path:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
return Blackboard(db_path)
|
||||
|
||||
|
||||
# ── API ──
|
||||
|
||||
@router.get("")
|
||||
def list_checkpoints(project_id: str, task_id: str):
|
||||
"""列出 task 的所有 checkpoint"""
|
||||
bb = _bb(project_id)
|
||||
cps = bb.list_checkpoints(task_id)
|
||||
return {"checkpoints": cps}
|
||||
|
||||
|
||||
@router.post("")
|
||||
def create_checkpoint(project_id: str, task_id: str, req: CreateCheckpointRequest):
|
||||
"""Agent 创建 checkpoint"""
|
||||
if req.type not in ("verify", "decision", "action"):
|
||||
raise HTTPException(status_code=400, detail=f"Invalid checkpoint type: {req.type}")
|
||||
|
||||
bb = _bb(project_id)
|
||||
# 验证 task 存在
|
||||
task = bb.get_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
result = bb.create_checkpoint(
|
||||
task_id=task_id,
|
||||
cp_type=req.type,
|
||||
title=req.title,
|
||||
payload=req.payload,
|
||||
description=req.description,
|
||||
checkpoint_id=req.id,
|
||||
)
|
||||
return {"ok": True, **result}
|
||||
|
||||
|
||||
@router.post("/{checkpoint_id}/approve")
|
||||
def approve_checkpoint(project_id: str, task_id: str, checkpoint_id: str, req: ResolveCheckpointRequest):
|
||||
"""用户通过 checkpoint → 自动推进 task 状态"""
|
||||
bb = _bb(project_id)
|
||||
result = bb.resolve_checkpoint(checkpoint_id, "approve", req.resolved_by, req.note)
|
||||
if result is None:
|
||||
raise HTTPException(status_code=404, detail="Checkpoint not found")
|
||||
if "error" in result:
|
||||
raise HTTPException(status_code=400, detail=result["error"])
|
||||
return {"ok": True, **result}
|
||||
|
||||
|
||||
@router.post("/{checkpoint_id}/reject")
|
||||
def reject_checkpoint(project_id: str, task_id: str, checkpoint_id: str, req: ResolveCheckpointRequest):
|
||||
"""用户驳回 checkpoint → task 回到 working"""
|
||||
bb = _bb(project_id)
|
||||
result = bb.resolve_checkpoint(checkpoint_id, "reject", req.resolved_by, req.note)
|
||||
if result is None:
|
||||
raise HTTPException(status_code=404, detail="Checkpoint not found")
|
||||
if "error" in result:
|
||||
raise HTTPException(status_code=400, detail=result["error"])
|
||||
return {"ok": True, **result}
|
||||
Reference in New Issue
Block a user