auto-sync: 2026-05-19 13:54:58

This commit is contained in:
cfdaily
2026-05-19 13:54:58 +08:00
parent d4d7a03bc1
commit cf4529720b
+97
View File
@@ -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}