ソースを参照

feat(可视化模型): 重建真实执行观察器的数据结构

以 RunSummary、TaskNode、PlannerTurn、LifecycleEvent、AttemptDetail、DataUsage 和 ExecutionView 取代旧固定图模型。允许从真实 Ledger 和 Trace 投影动态任务树、Planner 焦点及 Worker 生命周期。
SamLee 15 時間 前
コミット
496cbb5a9e
1 ファイル変更111 行追加82 行削除
  1. 111 82
      visualization/backend/app/models.py

+ 111 - 82
visualization/backend/app/models.py

@@ -1,106 +1,135 @@
 from __future__ import annotations
 
-from typing import Any, Literal
+from typing import Any, Literal, Optional
 
 from pydantic import BaseModel, ConfigDict
 
 
-class StrictModel(BaseModel):
+class ViewModel(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
 
-class Metric(StrictModel):
-    label: str
-    value: str
-
-
-class RecordEnvelope(StrictModel):
-    model_name: str
-    schema_version: str | None = None
-    payload: dict[str, Any]
-
-
-class FlowNode(StrictModel):
-    id: str
-    node_type: Literal[
-        "input",
-        "contract",
-        "operation",
-        "worker",
-        "attempt",
-        "artifact",
-        "validator",
-        "validation",
-        "decision",
-        "checkpoint",
-        "publication",
-        "transaction",
-        "readback",
-    ]
-    phase: Literal["phase-1", "phase-2", "phase-3", "delivery"]
-    lane: str
+class RunSummary(ViewModel):
+    script_build_id: int
     title: str
-    subtitle: str
     status: str
-    agent_role: str | None = None
-    task_kind: str | None = None
-    sequence: int
-    x: int
-    y: int
-    width: int = 268
-    tags: list[str]
-    metrics: list[Metric]
-    record: RecordEnvelope
+    checkpoint: str
+    root_trace_id: str
+    revision: int
+    task_count: int
+    attempt_count: int
+    planner_turn_count: int
+    current_focus_task_id: Optional[str]
+    current_gap: str
+    created_at: str
+    updated_at: str
 
 
-class FlowEdge(StrictModel):
-    id: str
-    source: str
-    target: str
-    label: str | None = None
-    kind: Literal["control", "artifact", "validation", "publication"] = "control"
-    animated: bool = False
+class TaskNode(ViewModel):
+    task_id: str
+    display_path: str
+    parent_task_id: Optional[str]
+    child_task_ids: list[str]
+    task_kind: str
+    phase: str
+    objective: str
+    status: str
+    blocked_reason: Optional[str]
+    attempt_count: int
+    validation_count: int
+    decision_count: int
+    current_spec_version: int
+    acceptance_criteria: list[dict[str, Any]]
+    context_refs: list[str]
+    created_at: str
+    updated_at: str
 
 
-class PhaseFrame(StrictModel):
-    id: str
-    title: str
-    subtitle: str
-    x: int
-    y: int
-    width: int
-    height: int
-    tone: Literal["coral", "blue", "purple", "green"]
+class PlannerTurn(ViewModel):
+    turn_id: str
+    order: int
+    message_sequence: int
+    phase: str
+    action: str
+    action_label: str
+    summary: str
+    observable_reasoning: str
+    result_summary: str
+    status: Literal["succeeded", "failed", "unknown"]
+    focus_task_id: Optional[str]
+    affected_task_ids: list[str]
+    visible_task_ids: list[str]
+    tool_name: str
+    tool_arguments: dict[str, Any]
+    tokens: int
+    cost: float
+    created_at: str
 
 
-class RunSummary(StrictModel):
-    script_build_id: int
+class LifecycleEvent(ViewModel):
+    event_id: str
+    kind: Literal["task", "attempt", "validation", "decision"]
     title: str
     status: str
-    checkpoint: str
-    root_trace_id: str
-    publication_state: str
-    node_count: int
+    summary: str
     created_at: str
-    updated_at: str
+    attempt_id: Optional[str] = None
 
 
-class RunGraph(StrictModel):
-    schema_version: Literal["script-build-visualization/v1"]
-    data_mode: Literal["fake"]
+class AgentStep(ViewModel):
+    sequence: int
+    role: str
+    step_kind: Literal["instruction", "explanation", "tool_call", "tool_result"]
+    title: str
+    text: str
+    tool_name: Optional[str]
+    tool_arguments: Optional[dict[str, Any]]
+    tokens: int
+    cost: float
+    duration_ms: Optional[float]
+    created_at: str
+
+
+class DataUsage(ViewModel):
+    inherited_inputs: list[dict[str, Any]]
+    confirmed_reads: list[dict[str, Any]]
+    produced_outputs: list[dict[str, Any]]
+    adoption: list[dict[str, Any]]
+
+
+class AttemptDetail(ViewModel):
+    attempt_id: str
+    task_id: str
+    status: str
+    worker_preset: Optional[str]
+    worker_trace_id: Optional[str]
+    execution_mode: Optional[str]
+    started_at: Optional[str]
+    completed_at: Optional[str]
+    total_tokens: int
+    total_cost: float
+    error: Optional[Any]
+    submission: Optional[dict[str, Any]]
+    validation: Optional[dict[str, Any]]
+    decision: Optional[dict[str, Any]]
+    prompt: str
+    steps: list[AgentStep]
+
+
+class TaskDetail(ViewModel):
+    task: TaskNode
+    contract: Optional[dict[str, Any]]
+    lifecycle: list[LifecycleEvent]
+    attempts: list[AttemptDetail]
+    data_usage: DataUsage
+
+
+class ExecutionView(ViewModel):
+    schema_version: Literal["script-build-execution-view/v1"]
+    data_mode: Literal["persisted-real-run"]
     generated_at: str
     run: RunSummary
-    frames: list[PhaseFrame]
-    nodes: list[FlowNode]
-    edges: list[FlowEdge]
-    schema_catalog: dict[str, list[str]]
-    enum_catalog: dict[str, list[str]]
-    declared_gaps: list[str]
-
-
-class NodeDetail(StrictModel):
-    data_mode: Literal["fake"]
-    run_id: int
-    node: FlowNode
-    incoming: list[FlowEdge]
-    outgoing: list[FlowEdge]
+    planner_turns: list[PlannerTurn]
+    tasks: list[TaskNode]
+    snapshot_basis: str
+    warnings: list[str]