From fb5cdee54b2951dea1d623c6d29f3d8ca5769860 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sun, 17 May 2026 18:34:37 +0800 Subject: [PATCH] auto-sync: 2026-05-17 18:34:37 --- src/api/blackboard_routes.py | 65 +++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/src/api/blackboard_routes.py b/src/api/blackboard_routes.py index 56a45e2..1eed571 100644 --- a/src/api/blackboard_routes.py +++ b/src/api/blackboard_routes.py @@ -140,10 +140,67 @@ async def get_outputs(project_id: str, task_id: str): @router.post("/tasks/{task_id}/outputs") async def write_output(project_id: str, task_id: str, body: Dict[str, Any]): bb = _bb(project_id) - oid = bb.write_output(task_id, body["agent"], body["type"], - body["title"], content_path=body.get("path"), - summary=body.get("summary"), - metadata=body.get("metadata")) + + # 字段校验 + Agent-friendly 错误 + agent = body.get("agent") + if not agent: + raise HTTPException(422, { + "error": "validation_failed", + "detail": "Missing required field: agent", + "hint": "Provide your agent ID, e.g. 'zhangfei-dev'", + }) + + # type 字段:接受 type 或 content_type(别名兼容) + output_type = body.get("type") or body.get("content_type") + valid_types = sorted(OUTPUT_TYPES) + if not output_type: + raise HTTPException(422, { + "error": "validation_failed", + "detail": "Missing required field: type", + "valid_values": {"type": valid_types}, + "hint": "Use 'type' field. Also accepts 'content_type' as alias.", + }) + if output_type not in OUTPUT_TYPES: + raise HTTPException(422, { + "error": "validation_failed", + "detail": f"Invalid type: '{output_type}'", + "valid_values": {"type": valid_types}, + }) + + title = body.get("title") + if not title: + raise HTTPException(422, { + "error": "validation_failed", + "detail": "Missing required field: title", + "hint": "Provide a brief title describing this output", + }) + + # 内容模式:content(直传)或 content_path(引用) + content = body.get("content") + content_path = body.get("content_path") or body.get("path") + + if content and not content_path: + # 内容直传模式:自动写文件 + import os + artifacts_dir = os.path.join( + os.path.dirname(bb.db_path), "artifacts", task_id + ) + os.makedirs(artifacts_dir, exist_ok=True) + # 安全文件名 + safe_name = "".join(c if c.isalnum() or c in "._-" else "_" for c in title) + if not safe_name: + safe_name = "output" + file_path = os.path.join(artifacts_dir, safe_name) + with open(file_path, "w", encoding="utf-8") as f: + f.write(content) + content_path = file_path + + oid = bb.write_output( + task_id, agent, output_type, title, + content_path=content_path, + summary=body.get("summary"), + metadata=body.get("metadata"), + ) return {"ok": True, "output_id": oid}