feat: CEO plan preview & confirm before company execution

- Add POST /companies/{id}/plan endpoint for stateless CEO plan preview (no DB write)
- execute()/execute_stream() accept optional confirmed ceo_plan to skip re-planning
- Frontend: plan preview dialog in CompanyBuilder to review/tweak analysis, dept goals & deliverables before running; previewCompanyPlan API with extended timeout
- Grant file_write tool to CEO/CTO presets; add echarts dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 00:40:22 +08:00
parent f2e65a8fbb
commit eef48ce831
8 changed files with 875 additions and 88 deletions

View File

@@ -60,6 +60,9 @@ class DepartmentAdd(BaseModel):
class ExecuteRequest(BaseModel):
project_description: str = Field(..., min_length=1, description="公司级项目目标")
ceo_plan: Optional[Dict[str, Any]] = Field(
None, description="已确认的 CEO 计划;提供时跳过二次规划,直接据此执行"
)
# ─── Endpoints ───
@@ -365,6 +368,24 @@ def remove_department(
# ─── 项目执行 ───
@router.post("/{company_id}/plan")
async def plan_company(
company_id: str,
body: ExecuteRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""仅生成 CEO 计划供预览/确认,不派发部门、不落库。"""
company = db.query(Company).filter(Company.id == company_id).first()
if not company:
raise HTTPException(status_code=404, detail="公司不存在")
orchestrator = CompanyOrchestrator(db, company_id, current_user.id)
try:
return await orchestrator.preview_plan(body.project_description)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.post("/{company_id}/execute")
async def execute_company(
company_id: str,
@@ -378,7 +399,7 @@ async def execute_company(
raise HTTPException(status_code=404, detail="公司不存在")
orchestrator = CompanyOrchestrator(db, company_id, current_user.id)
try:
result = await orchestrator.execute(body.project_description)
result = await orchestrator.execute(body.project_description, ceo_plan=body.ceo_plan)
return result
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@@ -400,7 +421,7 @@ async def execute_company_stream(
async def event_stream():
try:
async for event in orchestrator.execute_stream(body.project_description):
async for event in orchestrator.execute_stream(body.project_description, ceo_plan=body.ceo_plan):
yield f"data: {json.dumps(event, ensure_ascii=False)}\n\n"
except Exception as e:
yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n"