Files

261 lines
8.0 KiB
Python
Raw Permalink Normal View History

"""
会议管理 API 路由
接入 MeetingScheduler栅栏同步+ MeetingRecorder会议记录
"""
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List, Optional
from dataclasses import asdict
from datetime import datetime
from ..services.meeting_scheduler import get_meeting_scheduler
from ..services.meeting_recorder import get_meeting_recorder
router = APIRouter()
class MeetingCreate(BaseModel):
title: str
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(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():
"""获取今日会议"""
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):
"""创建新会议(同时创建调度记录和会议记录)"""
recorder = get_meeting_recorder()
scheduler = get_meeting_scheduler()
meeting_id = f"meeting-{int(datetime.now().timestamp())}"
# 在调度器中创建(用于栅栏同步)
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_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: 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: 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: 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):
"""创建会议记录(前端使用的端点)"""
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}