重构 API 路由并新增工作流编排功能
后端: - 重构 agents, heartbeats, locks, meetings, resources, roles, workflows 路由 - 新增 orchestrator 和 providers 路由 - 新增 CLI 调用器和流程编排服务 - 添加日志配置和依赖项 前端: - 更新 AgentsPage、SettingsPage、WorkflowPage 页面 - 扩展 api.ts 新增 API 接口 其他: - 清理测试 agent 数据文件 - 新增示例工作流和项目审计报告 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+197
-132
@@ -1,195 +1,260 @@
|
||||
"""
|
||||
会议管理 API 路由
|
||||
接入 MeetingScheduler(栅栏同步)+ MeetingRecorder(会议记录)
|
||||
"""
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from dataclasses import asdict
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
from ..services.meeting_scheduler import get_meeting_scheduler
|
||||
from ..services.meeting_recorder import get_meeting_recorder
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
meetings_db = []
|
||||
|
||||
|
||||
class Meeting(BaseModel):
|
||||
meeting_id: str
|
||||
title: str
|
||||
status: str
|
||||
attendees: List[str]
|
||||
agenda: str
|
||||
progress_summary: str
|
||||
created_at: float
|
||||
|
||||
|
||||
class MeetingCreate(BaseModel):
|
||||
title: str
|
||||
agenda: str
|
||||
meeting_type: str = "design_review"
|
||||
agenda: Optional[str] = ""
|
||||
meeting_type: Optional[str] = "design_review"
|
||||
attendees: List[str] = []
|
||||
steps: Optional[List[str]] = None
|
||||
|
||||
|
||||
class MeetingWaitRequest(BaseModel):
|
||||
agent_id: str
|
||||
timeout: Optional[int] = 300
|
||||
|
||||
|
||||
class DiscussionRequest(BaseModel):
|
||||
agent_id: str
|
||||
agent_name: Optional[str] = ""
|
||||
content: str
|
||||
step: Optional[str] = ""
|
||||
|
||||
|
||||
class ProgressRequest(BaseModel):
|
||||
step: str
|
||||
|
||||
|
||||
class FinishRequest(BaseModel):
|
||||
consensus: Optional[str] = ""
|
||||
|
||||
|
||||
def _meeting_to_dict(meeting) -> dict:
|
||||
"""将 MeetingInfo 转为前端友好的 dict"""
|
||||
return {
|
||||
"meeting_id": meeting.meeting_id,
|
||||
"title": meeting.title,
|
||||
"date": meeting.date,
|
||||
"status": meeting.status,
|
||||
"attendees": meeting.attendees,
|
||||
"steps": [
|
||||
{"step_id": s.step_id, "label": s.label, "status": s.status}
|
||||
for s in meeting.steps
|
||||
],
|
||||
"discussions": [
|
||||
{
|
||||
"agent_id": d.agent_id,
|
||||
"agent_name": d.agent_name,
|
||||
"content": d.content,
|
||||
"timestamp": d.timestamp,
|
||||
"step": d.step
|
||||
}
|
||||
for d in meeting.discussions
|
||||
],
|
||||
"progress_summary": meeting.progress_summary,
|
||||
"consensus": meeting.consensus,
|
||||
"created_at": meeting.created_at,
|
||||
"ended_at": meeting.ended_at
|
||||
}
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("/")
|
||||
async def list_meetings():
|
||||
"""获取所有会议列表"""
|
||||
return {
|
||||
"meetings": [
|
||||
{
|
||||
"meeting_id": "meeting-001",
|
||||
"title": "架构设计评审",
|
||||
"status": "in_progress",
|
||||
"attendees": ["claude-001", "kimi-001"],
|
||||
"agenda": "讨论系统架构设计",
|
||||
"progress_summary": "50%",
|
||||
"created_at": time.time() - 7200
|
||||
},
|
||||
{
|
||||
"meeting_id": "meeting-002",
|
||||
"title": "代码审查会议",
|
||||
"status": "completed",
|
||||
"attendees": ["claude-001"],
|
||||
"agenda": "审查前端组件代码",
|
||||
"progress_summary": "100%",
|
||||
"created_at": time.time() - 86400
|
||||
}
|
||||
]
|
||||
}
|
||||
async def list_meetings(date: Optional[str] = None):
|
||||
"""获取会议列表(默认今天)"""
|
||||
recorder = get_meeting_recorder()
|
||||
meetings = await recorder.list_meetings(date)
|
||||
return {"meetings": [_meeting_to_dict(m) for m in meetings]}
|
||||
|
||||
|
||||
@router.get("/today")
|
||||
async def list_today_meetings():
|
||||
"""获取今日会议"""
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
return {
|
||||
"meetings": [
|
||||
{
|
||||
"meeting_id": "meeting-001",
|
||||
"title": "架构设计评审",
|
||||
"date": today,
|
||||
"status": "in_progress",
|
||||
"attendees": ["claude-001", "kimi-001"],
|
||||
"steps": [
|
||||
{"step_id": "step-1", "label": "收集想法", "status": "completed"},
|
||||
{"step_id": "step-2", "label": "讨论迭代", "status": "active"},
|
||||
{"step_id": "step-3", "label": "生成共识", "status": "pending"}
|
||||
],
|
||||
"discussions": [
|
||||
{
|
||||
"agent_id": "claude-001",
|
||||
"agent_name": "Claude Code",
|
||||
"content": "建议采用微服务架构",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"step": "讨论迭代"
|
||||
}
|
||||
],
|
||||
"progress_summary": "50%",
|
||||
"consensus": ""
|
||||
},
|
||||
{
|
||||
"meeting_id": "meeting-002",
|
||||
"title": "代码审查会议",
|
||||
"date": today,
|
||||
"status": "completed",
|
||||
"attendees": ["claude-001"],
|
||||
"steps": [
|
||||
{"step_id": "step-1", "label": "代码检查", "status": "completed"},
|
||||
{"step_id": "step-2", "label": "问题讨论", "status": "completed"}
|
||||
],
|
||||
"discussions": [],
|
||||
"progress_summary": "100%",
|
||||
"consensus": "代码质量良好,可以合并"
|
||||
}
|
||||
]
|
||||
}
|
||||
recorder = get_meeting_recorder()
|
||||
meetings = await recorder.list_meetings()
|
||||
return {"meetings": [_meeting_to_dict(m) for m in meetings]}
|
||||
|
||||
|
||||
@router.post("/")
|
||||
async def create_meeting(meeting: MeetingCreate):
|
||||
"""创建新会议"""
|
||||
meeting_id = f"meeting-{int(time.time())}"
|
||||
meeting_data = {
|
||||
"meeting_id": meeting_id,
|
||||
"title": meeting.title,
|
||||
"status": "waiting",
|
||||
"attendees": meeting.attendees,
|
||||
"agenda": meeting.agenda,
|
||||
"progress_summary": "0%",
|
||||
"created_at": time.time()
|
||||
}
|
||||
meetings_db.append(meeting_data)
|
||||
return meeting_data
|
||||
"""创建新会议(同时创建调度记录和会议记录)"""
|
||||
recorder = get_meeting_recorder()
|
||||
scheduler = get_meeting_scheduler()
|
||||
|
||||
meeting_id = f"meeting-{int(datetime.now().timestamp())}"
|
||||
|
||||
@router.get("/{meeting_id}")
|
||||
async def get_meeting(meeting_id: str):
|
||||
"""获取会议详情"""
|
||||
for meeting in meetings_db:
|
||||
if meeting["meeting_id"] == meeting_id:
|
||||
return meeting
|
||||
# 返回模拟数据
|
||||
return {
|
||||
"meeting_id": meeting_id,
|
||||
"title": "测试会议",
|
||||
"status": "in_progress",
|
||||
"attendees": ["claude-001"],
|
||||
"agenda": "测试议程",
|
||||
"progress_summary": "50%",
|
||||
"created_at": time.time()
|
||||
}
|
||||
# 在调度器中创建(用于栅栏同步)
|
||||
await scheduler.create_meeting(
|
||||
meeting_id=meeting_id,
|
||||
title=meeting.title,
|
||||
expected_attendees=meeting.attendees
|
||||
)
|
||||
|
||||
# 在记录器中创建(用于记录内容)
|
||||
meeting_info = await recorder.create_meeting(
|
||||
meeting_id=meeting_id,
|
||||
title=meeting.title,
|
||||
attendees=meeting.attendees,
|
||||
steps=meeting.steps
|
||||
)
|
||||
|
||||
return _meeting_to_dict(meeting_info)
|
||||
|
||||
|
||||
@router.post("/create")
|
||||
async def create_meeting_api(meeting: MeetingCreate):
|
||||
"""创建会议 API(前端使用的端点)"""
|
||||
async def create_meeting_alt(meeting: MeetingCreate):
|
||||
"""创建会议 API(前端使用的端点,与 POST / 相同)"""
|
||||
return await create_meeting(meeting)
|
||||
|
||||
|
||||
@router.get("/{meeting_id}")
|
||||
async def get_meeting(meeting_id: str, date: Optional[str] = None):
|
||||
"""获取会议详情"""
|
||||
recorder = get_meeting_recorder()
|
||||
meeting_info = await recorder.get_meeting(meeting_id, date)
|
||||
if not meeting_info:
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
return _meeting_to_dict(meeting_info)
|
||||
|
||||
|
||||
@router.get("/{meeting_id}/queue")
|
||||
async def get_meeting_queue(meeting_id: str):
|
||||
"""获取会议等待队列"""
|
||||
scheduler = get_meeting_scheduler()
|
||||
queue = await scheduler.get_queue(meeting_id)
|
||||
if not queue:
|
||||
raise HTTPException(status_code=404, detail="Meeting queue not found")
|
||||
return {
|
||||
"meeting_id": queue.meeting_id,
|
||||
"title": queue.title,
|
||||
"status": queue.status,
|
||||
"expected_attendees": queue.expected_attendees,
|
||||
"arrived_attendees": queue.arrived_attendees,
|
||||
"missing_attendees": queue.missing_attendees,
|
||||
"progress": queue.progress,
|
||||
"is_ready": queue.is_ready
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{meeting_id}/wait")
|
||||
async def wait_for_meeting(meeting_id: str, request: MeetingWaitRequest):
|
||||
"""栅栏同步等待(阻塞直到所有参会者到齐或超时)"""
|
||||
scheduler = get_meeting_scheduler()
|
||||
status = await scheduler.wait_for_meeting(
|
||||
agent_id=request.agent_id,
|
||||
meeting_id=meeting_id,
|
||||
timeout=request.timeout or 300
|
||||
)
|
||||
return {"meeting_id": meeting_id, "status": status}
|
||||
|
||||
|
||||
@router.post("/{meeting_id}/end")
|
||||
async def end_meeting(meeting_id: str):
|
||||
"""结束会议(调度层)"""
|
||||
scheduler = get_meeting_scheduler()
|
||||
success = await scheduler.end_meeting(meeting_id)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
return {"success": True, "meeting_id": meeting_id}
|
||||
|
||||
|
||||
@router.post("/{meeting_id}/join")
|
||||
async def join_meeting(meeting_id: str, data: dict):
|
||||
"""Agent 加入会议"""
|
||||
agent_id = data.get("agent_id", "")
|
||||
scheduler = get_meeting_scheduler()
|
||||
await scheduler.add_attendee(meeting_id, agent_id)
|
||||
return {"success": True, "meeting_id": meeting_id, "agent_id": agent_id}
|
||||
|
||||
|
||||
@router.post("/{meeting_id}/discuss")
|
||||
async def add_discussion(meeting_id: str, data: dict):
|
||||
async def add_discussion(meeting_id: str, data: DiscussionRequest):
|
||||
"""添加讨论内容"""
|
||||
recorder = get_meeting_recorder()
|
||||
await recorder.add_discussion(
|
||||
meeting_id=meeting_id,
|
||||
agent_id=data.agent_id,
|
||||
agent_name=data.agent_name or data.agent_id,
|
||||
content=data.content,
|
||||
step=data.step or ""
|
||||
)
|
||||
return {"success": True, "meeting_id": meeting_id}
|
||||
|
||||
|
||||
@router.post("/{meeting_id}/finish")
|
||||
async def finish_meeting(meeting_id: str, data: dict):
|
||||
"""完成会议"""
|
||||
async def finish_meeting(meeting_id: str, data: FinishRequest):
|
||||
"""完成会议(记录层 - 保存共识并标记完成)"""
|
||||
recorder = get_meeting_recorder()
|
||||
success = await recorder.end_meeting(
|
||||
meeting_id=meeting_id,
|
||||
consensus=data.consensus or ""
|
||||
)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="Meeting not found")
|
||||
|
||||
# 同时结束调度
|
||||
scheduler = get_meeting_scheduler()
|
||||
await scheduler.end_meeting(meeting_id)
|
||||
|
||||
return {"success": True, "meeting_id": meeting_id}
|
||||
|
||||
|
||||
@router.post("/{meeting_id}/progress")
|
||||
async def update_progress(meeting_id: str, data: dict):
|
||||
"""更新进度"""
|
||||
async def update_progress(meeting_id: str, data: ProgressRequest):
|
||||
"""更新会议进度"""
|
||||
recorder = get_meeting_recorder()
|
||||
await recorder.update_progress(
|
||||
meeting_id=meeting_id,
|
||||
step_label=data.step
|
||||
)
|
||||
return {"success": True, "meeting_id": meeting_id}
|
||||
|
||||
|
||||
@router.post("/record/create")
|
||||
async def create_meeting_record(data: dict):
|
||||
"""创建会议记录(前端使用的端点)"""
|
||||
meeting_id = f"meeting-{int(time.time())}"
|
||||
meeting_data = {
|
||||
"meeting_id": meeting_id,
|
||||
"title": data.get("title", "未命名会议"),
|
||||
"agenda": data.get("agenda", ""),
|
||||
"attendees": data.get("attendees", []),
|
||||
"status": "waiting",
|
||||
"progress_summary": "0%",
|
||||
"steps": data.get("steps", []),
|
||||
"discussions": [],
|
||||
"created_at": time.time()
|
||||
}
|
||||
meetings_db.append(meeting_data)
|
||||
return meeting_data
|
||||
recorder = get_meeting_recorder()
|
||||
meeting_id = data.get("meeting_id", f"meeting-{int(datetime.now().timestamp())}")
|
||||
meeting_info = await recorder.create_meeting(
|
||||
meeting_id=meeting_id,
|
||||
title=data.get("title", "未命名会议"),
|
||||
attendees=data.get("attendees", []),
|
||||
steps=data.get("steps", [])
|
||||
)
|
||||
|
||||
# 同时在调度器中注册
|
||||
scheduler = get_meeting_scheduler()
|
||||
await scheduler.create_meeting(
|
||||
meeting_id=meeting_id,
|
||||
title=data.get("title", "未命名会议"),
|
||||
expected_attendees=data.get("attendees", [])
|
||||
)
|
||||
|
||||
return _meeting_to_dict(meeting_info)
|
||||
|
||||
|
||||
@router.post("/record/{meeting_id}/discussion")
|
||||
async def add_meeting_discussion(meeting_id: str, data: dict):
|
||||
"""添加会议讨论(前端使用的端点)"""
|
||||
recorder = get_meeting_recorder()
|
||||
await recorder.add_discussion(
|
||||
meeting_id=meeting_id,
|
||||
agent_id=data.get("agent_id", ""),
|
||||
agent_name=data.get("agent_name", data.get("agent_id", "")),
|
||||
content=data.get("content", ""),
|
||||
step=data.get("step", "")
|
||||
)
|
||||
return {"success": True, "meeting_id": meeting_id, "discussion": data}
|
||||
|
||||
Reference in New Issue
Block a user