deerflow-code/offline-backend-20260512/backend/app/report_collaboration/execution/board.py
2026-09-07 18:24:55 +08:00

83 lines
3.2 KiB
Python

"""Artifact board: pending drafts become downstream inputs only after validation."""
from __future__ import annotations
from typing import Any
from uuid import uuid4
from deerflow.persistence.report_collaboration.base import ReportCollaborationStore
from deerflow.persistence.report_collaboration.codec import iso_now
class ArtifactBoard:
"""Persistence wrapper. QualityGate (RC-BE-010) will sit in front of ``validate``."""
def __init__(self, store: ReportCollaborationStore) -> None:
self._store = store
async def submit(
self,
*,
session_id: str,
run_id: str,
node_id: str,
attempt: int,
producer_node_run_id: str,
artifact_type: str,
content: dict[str, Any],
requirement_revision: int,
summary: str | None = None,
validation_status: str = "pending",
) -> dict[str, Any]:
return await self._store.save_artifact(
{
"id": f"rca_{uuid4().hex[:16]}",
"session_id": session_id,
"run_id": run_id,
"node_id": node_id,
"attempt": attempt,
"producer_node_run_id": producer_node_run_id,
"artifact_type": artifact_type,
"schema_version": int(content.get("schema_version") or 1),
"requirement_revision": requirement_revision,
"content": content,
"summary": summary,
"validation_status": validation_status,
"superseded_by": None,
"created_at": iso_now(),
}
)
async def mark(self, artifact: dict[str, Any], *, validation_status: str, superseded_by: str | None = None) -> dict[str, Any]:
payload = dict(artifact)
payload["validation_status"] = validation_status
if superseded_by is not None:
payload["superseded_by"] = superseded_by
return await self._store.save_artifact(payload)
async def supersede_previous(self, *, run_id: str, node_id: str, new_artifact_id: str, attempt: int | None = None) -> list[dict[str, Any]]:
"""Hide older attempts of the same node. Same-attempt siblings stay visible."""
updated: list[dict[str, Any]] = []
for item in await self._store.list_artifacts_full(run_id):
if item.get("node_id") != node_id or item.get("id") == new_artifact_id:
continue
if item.get("superseded_by"):
continue
if attempt is not None and int(item.get("attempt") or 0) >= attempt:
continue
updated.append(await self.mark(item, validation_status="superseded", superseded_by=new_artifact_id))
return updated
async def validated_for(self, run_id: str, artifact_ids: list[str] | None = None) -> list[dict[str, Any]]:
rows = await self._store.list_artifacts_full(run_id)
out = [
item
for item in rows
if item.get("validation_status") == "validated" and not item.get("superseded_by")
]
if artifact_ids is None:
return out
allowed = set(artifact_ids)
return [item for item in out if item["id"] in allowed]