from __future__ import annotations from typing import Any, Literal, Optional from pydantic import BaseModel, ConfigDict, Field class ViewModel(BaseModel): model_config = ConfigDict(extra="forbid") class RunSummary(ViewModel): script_build_id: int status: str summary: str started_at: Optional[str] = None completed_at: Optional[str] = None class InputSummary(ViewModel): topic: str account_name: Optional[str] = None persona_point_count: int = 0 strategy_names: list[str] = Field(default_factory=list) snapshot_id: str digest: str class DataRef(ViewModel): label: str kind: str ref: Optional[str] = None detail: Optional[str] = None class ContractNarration(ViewModel): action: str reasoning: str goals: list[str] inputs: list[str] output: str criteria: list[str] class ProcessView(ViewModel): label: str actor: str tool_name: Optional[str] = None trace_id: Optional[str] = None details: dict[str, Any] = Field(default_factory=dict) class StepEvidence(ViewModel): contract: Optional[dict[str, Any]] = None tool_calls: list[dict[str, Any]] = Field(default_factory=list) artifact_refs: list[dict[str, Any]] = Field(default_factory=list) validation: Optional[dict[str, Any]] = None decision: Optional[dict[str, Any]] = None class JourneyStep(ViewModel): step_id: str sequence: int = 0 step_type: Literal["plan", "execute", "validate", "decide"] title: str status: str task_id: str attempt_id: Optional[str] = None validation_id: Optional[str] = None decision_id: Optional[str] = None reasoning: str reasoning_source: Literal["observable", "contract", "validation", "decision", "system"] process: ProcessView input_data: list[DataRef] = Field(default_factory=list) output_data: list[DataRef] = Field(default_factory=list) contract: Optional[ContractNarration] = None parallel_group_id: Optional[str] = None parallel_position: Optional[int] = None parallel_total: Optional[int] = None model: Optional[str] = None tokens: Optional[int] = None cost: Optional[float] = None duration_ms: Optional[int] = None created_at: str evidence: StepEvidence class JourneyEdge(ViewModel): edge_id: str source: str target: str relation: Literal["sequence", "creates", "validates", "decides", "loop"] label: str class JourneyView(ViewModel): schema_version: Literal["script-build-journey/v1"] generated_at: str script_build_id: int root_trace_id: str status: str objective: str input_summary: InputSummary steps: list[JourneyStep] edges: list[JourneyEdge] warnings: list[str]