From c4cc70b0fc76601927c92c20305f847d88b9554b Mon Sep 17 00:00:00 2001 From: cfdaily Date: Fri, 29 May 2026 01:11:42 +0800 Subject: [PATCH] auto-sync: 2026-05-29 01:11:42 --- src/blackboard/operations.py | 103 +++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/blackboard/operations.py b/src/blackboard/operations.py index 086e64d..de4ce20 100644 --- a/src/blackboard/operations.py +++ b/src/blackboard/operations.py @@ -832,3 +832,106 @@ class Blackboard: } finally: conn.close() + + # =================================================================== + # 四相循环 (v2.9 #01) + # =================================================================== + + def get_subtasks_summary(self, parent_id: str) -> Optional[Dict[str, Any]]: + """获取 parent task 下所有 sub task 的状态摘要 + + Returns: + {"total": N, "done": N, "failed": N, "cancelled": N, + "other": N, "all_terminal": bool} + 如果 parent 不存在或没有 sub task,返回 None + """ + conn = self._conn() + try: + # 确认 parent 存在 + parent = conn.execute( + "SELECT id, status, round_count FROM tasks WHERE id=?", + (parent_id,) + ).fetchone() + if not parent: + return None + + rows = conn.execute( + "SELECT status, COUNT(*) as cnt FROM tasks WHERE parent_task=? GROUP BY status", + (parent_id,) + ).fetchall() + + if not rows: + return None + + summary = {"parent_id": parent_id, "parent_status": parent["status"], + "round_count": parent["round_count"], + "total": 0, "done": 0, "failed": 0, "cancelled": 0, "other": 0} + for row in rows: + cnt = row["cnt"] + summary["total"] += cnt + if row["status"] in ("done", "failed", "cancelled"): + summary[row["status"]] += cnt + else: + summary["other"] += cnt + + summary["all_terminal"] = summary["other"] == 0 and summary["total"] > 0 + return summary + finally: + conn.close() + + def get_aggregate_outputs(self, parent_id: str) -> List[Dict[str, Any]]: + """聚合 parent task 下所有 sub task 的 outputs""" + conn = self._conn() + try: + sub_ids = conn.execute( + "SELECT id FROM tasks WHERE parent_task=?", + (parent_id,) + ).fetchall() + if not sub_ids: + return [] + + ids = [r["id"] for r in sub_ids] + placeholders = ",".join("?" * len(ids)) + rows = conn.execute( + f"SELECT o.*, t.title as task_title FROM outputs o " + f"JOIN tasks t ON o.task_id = t.id " + f"WHERE o.task_id IN ({placeholders}) " + f"ORDER BY o.created_at ASC", + ids + ).fetchall() + return [dict(r) for r in rows] + finally: + conn.close() + + def get_round_comments(self, parent_id: str) -> List[Dict[str, Any]]: + """获取 parent task 及其 sub task 的所有 comments""" + conn = self._conn() + try: + # parent 自身的 comments + 所有 sub 的 comments + rows = conn.execute( + """SELECT c.*, t.title as task_title FROM comments c + JOIN tasks t ON c.task_id = t.id + WHERE c.task_id = ? OR c.task_id IN (SELECT id FROM tasks WHERE parent_task = ?) + ORDER BY c.created_at ASC""", + (parent_id, parent_id) + ).fetchall() + return [dict(r) for r in rows] + finally: + conn.close() + + def increment_round_count(self, parent_id: str) -> int: + """递增 parent task 的 round_count,返回新值""" + conn = self._conn() + try: + conn.execute("BEGIN IMMEDIATE") + conn.execute( + "UPDATE tasks SET round_count = round_count + 1, updated_at=datetime('now') WHERE id=?", + (parent_id,) + ) + row = conn.execute( + "SELECT round_count FROM tasks WHERE id=?", (parent_id,) + ).fetchone() + conn.commit() + return row["round_count"] if row else 0 + finally: + conn.close()