"""Node contracts and live node state. The public contract endpoint never returns execution bodies.""" from __future__ import annotations from pydantic import BaseModel, ConfigDict, Field from app.report_collaboration.contracts.enums import CollaborationNodeStatus class NodeAwaitingInput(BaseModel): model_config = ConfigDict(extra="forbid") reason: str question: str impact: list[str] = Field(default_factory=list) class NodeAttemptState(BaseModel): model_config = ConfigDict(extra="forbid") attempt: int status: CollaborationNodeStatus started_at: str | None = None ended_at: str | None = None error: str | None = None class CollaborationNodeState(BaseModel): model_config = ConfigDict(extra="forbid") node_id: str label: str role_display_name: str | None = None angle: str | None = None status: CollaborationNodeStatus attempt: int progress: str | None = None awaiting_input: NodeAwaitingInput | None = None error: str | None = None attempts: list[NodeAttemptState] = Field(default_factory=list) class CollaborationNodeContract(BaseModel): """Internal full contract stored on a node-run row.""" model_config = ConfigDict(extra="forbid") node_id: str mission: str input_artifact_types: list[str] = Field(default_factory=list) output_artifact_type: str allowed_tools: list[str] = Field(default_factory=list) source_policy: str | None = None acceptance_criteria: list[str] = Field(default_factory=list) max_attempts: int = 3 time_budget_seconds: int | None = None token_budget: int | None = None agent_profile_snapshot: dict[str, object] = Field(default_factory=dict) dependencies: list[str] = Field(default_factory=list) forbidden: list[str] = Field(default_factory=list) output_structure: str | None = None class NodeContractDetail(BaseModel): """``GET /runs/{run_id}/nodes/{node_id}/contract`` — duties only, never result text.""" model_config = ConfigDict(extra="forbid") node_id: str label: str role: str | None = None angle: str | None = None status: CollaborationNodeStatus attempt: int mission: str input_artifact_types: list[str] = Field(default_factory=list) source_policy: str | None = None output_artifact_type: str output_structure: str | None = None acceptance_criteria: list[str] = Field(default_factory=list) allowed_tools: list[str] = Field(default_factory=list) forbidden: list[str] = Field(default_factory=list) time_budget_seconds: int | None = None token_budget: int | None = None max_attempts: int upstream: list[str] = Field(default_factory=list) downstream: list[str] = Field(default_factory=list)