from __future__ import annotations import math from datetime import datetime from typing import Any, Literal from pydantic import BaseModel, Field, field_validator WorkflowDefinitionStatus = Literal["draft", "active", "archived"] DefinitionScopeType = Literal["system", "tenant", "group", "user"] DefinitionKind = Literal["flow", "template"] class WorkflowPosition(BaseModel): x: float = 0 y: float = 0 @field_validator("x", "y") @classmethod def finite_coordinate(cls, value: float) -> float: if not math.isfinite(value): raise ValueError("Graph coordinates must be finite.") return value class WorkflowNode(BaseModel): id: str = Field(min_length=1, max_length=120) type: str = Field(min_length=1, max_length=120) label: str = Field(default="", max_length=300) position: WorkflowPosition = Field(default_factory=WorkflowPosition) config: dict[str, Any] = Field(default_factory=dict) class WorkflowEdge(BaseModel): id: str = Field(min_length=1, max_length=120) source: str = Field(min_length=1, max_length=120) target: str = Field(min_length=1, max_length=120) source_port: str = Field(default="output", min_length=1, max_length=120) target_port: str = Field(default="input", min_length=1, max_length=120) class WorkflowGraph(BaseModel): schema_version: Literal[1] = 1 nodes: list[WorkflowNode] = Field(default_factory=list, max_length=150) edges: list[WorkflowEdge] = Field(default_factory=list, max_length=300) class WorkflowGraphValidationRequest(BaseModel): graph: WorkflowGraph class WorkflowDiagnosticResponse(BaseModel): severity: Literal["error", "warning"] code: str message: str node_id: str | None = None field: str | None = None class WorkflowGraphValidationResponse(BaseModel): valid: bool diagnostics: list[WorkflowDiagnosticResponse] class WorkflowPortResponse(BaseModel): id: str label: str required: bool multiple: bool minimum_connections: int class WorkflowConfigFieldResponse(BaseModel): id: str label: str kind: str required: bool description: str | None options: list[tuple[str, str]] class WorkflowNodeTypeResponse(BaseModel): type: str category: str category_label: str label: str description: str icon: str input_ports: list[WorkflowPortResponse] output_ports: list[WorkflowPortResponse] config_fields: list[WorkflowConfigFieldResponse] default_config: dict[str, Any] class WorkflowNodeLibraryResponse(BaseModel): id: str version: str allows_cycles: bool nodes: list[WorkflowNodeTypeResponse] class WorkflowDefinitionRevisionResponse(BaseModel): id: str revision: int schema_version: int graph: WorkflowGraph content_hash: str library_id: str library_version: str created_by: str | None created_at: datetime class WorkflowActionDecisionResponse(BaseModel): allowed: bool reason: str | None = None source_path: list[dict[str, Any]] = Field(default_factory=list) requirements: list[str] = Field(default_factory=list) details: dict[str, Any] = Field(default_factory=dict) class WorkflowGovernanceResponse(BaseModel): scope_type: DefinitionScopeType scope_id: str | None definition_kind: DefinitionKind inherit_to_lower_scopes: bool allow_start: bool allow_reuse: bool allow_automation: bool derived_from_definition_id: str | None derived_from_revision: int | None derived_from_hash: str | None derivation_provenance: dict[str, Any] = Field(default_factory=dict) actions: dict[str, WorkflowActionDecisionResponse] automation_runtime_available: bool = False automation_runtime_reason: str | None = None class WorkflowDefinitionResponse(BaseModel): id: str tenant_id: str | None key: str name: str description: str | None status: WorkflowDefinitionStatus current_revision: int active_revision: int | None metadata: dict[str, Any] created_by: str | None updated_by: str | None created_at: datetime updated_at: datetime revision: WorkflowDefinitionRevisionResponse governance: WorkflowGovernanceResponse class WorkflowDefinitionListResponse(BaseModel): definitions: list[WorkflowDefinitionResponse] class WorkflowDefinitionRevisionListResponse(BaseModel): revisions: list[WorkflowDefinitionRevisionResponse] class WorkflowDefinitionCreateRequest(BaseModel): key: str | None = Field( default=None, min_length=1, max_length=120, pattern=r"^[A-Za-z0-9][A-Za-z0-9_-]*$", ) name: str = Field(min_length=1, max_length=300) description: str | None = Field(default=None, max_length=4_000) graph: WorkflowGraph metadata: dict[str, Any] = Field(default_factory=dict) scope_type: DefinitionScopeType = "tenant" scope_id: str | None = Field(default=None, max_length=36) definition_kind: DefinitionKind = "flow" inherit_to_lower_scopes: bool = False allow_start: bool = True allow_reuse: bool = False allow_automation: bool = False class WorkflowDefinitionUpdateRequest(BaseModel): name: str = Field(min_length=1, max_length=300) description: str | None = Field(default=None, max_length=4_000) graph: WorkflowGraph metadata: dict[str, Any] = Field(default_factory=dict) expected_revision: int = Field(ge=1) scope_type: DefinitionScopeType = "tenant" scope_id: str | None = Field(default=None, max_length=36) definition_kind: DefinitionKind = "flow" inherit_to_lower_scopes: bool = False allow_start: bool = True allow_reuse: bool = False allow_automation: bool = False class WorkflowDefinitionDeriveRequest(BaseModel): key: str | None = Field( default=None, min_length=1, max_length=120, pattern=r"^[A-Za-z0-9][A-Za-z0-9_-]*$", ) name: str = Field(min_length=1, max_length=300) description: str | None = Field(default=None, max_length=4_000) source_revision: int | None = Field(default=None, ge=1) metadata: dict[str, Any] = Field(default_factory=dict) scope_type: DefinitionScopeType = "tenant" scope_id: str | None = Field(default=None, max_length=36) definition_kind: DefinitionKind = "flow" inherit_to_lower_scopes: bool = False allow_start: bool = True allow_reuse: bool = False allow_automation: bool = False class WorkflowDefinitionActivateRequest(BaseModel): revision: int | None = Field(default=None, ge=1) class WorkflowDefinitionDeleteResponse(BaseModel): deleted: bool definition_id: str