from datetime import datetime from typing import Any, Literal from pydantic import BaseModel, ConfigDict, Field, field_validator class Position(BaseModel): x: float y: float class CredentialReference(BaseModel): id: str | None = None name: str type: str class WorkflowNodeData(BaseModel): model_config = ConfigDict(extra="allow") label: str = Field(min_length=1, max_length=128) type: str = Field(min_length=1, max_length=256) typeVersion: float = Field(default=1, ge=0) category: Literal[ "trigger", "core", "ai", "database", "communication", "cloud", "developer" ] subtitle: str | None = Field(default=None, max_length=256) parameters: dict[str, Any] = Field(default_factory=dict) credentials: dict[str, CredentialReference] | None = None disabled: bool = False issues: int = 0 class WorkflowNode(BaseModel): id: str = Field(min_length=1, max_length=128) type: str = "workflow" position: Position data: WorkflowNodeData selected: bool | None = None class WorkflowEdge(BaseModel): id: str = Field(min_length=1, max_length=256) source: str target: str type: str = "smoothstep" animated: bool = False sourceHandle: str | None = None targetHandle: str | None = None class WorkflowMeta(BaseModel): model_config = ConfigDict(extra="allow") description: str | None = None generatedBy: str | None = None version: int | None = None tags: list[str] = Field(default_factory=list) class WorkflowDocument(BaseModel): id: str | None = None name: str = Field(min_length=1, max_length=160) active: bool = False nodes: list[WorkflowNode] = Field(default_factory=list, max_length=1000) edges: list[WorkflowEdge] = Field(default_factory=list, max_length=5000) settings: dict[str, Any] = Field(default_factory=dict) meta: WorkflowMeta = Field(default_factory=WorkflowMeta) pinData: dict[str, Any] = Field(default_factory=dict) @field_validator("nodes") @classmethod def unique_node_ids(cls, nodes: list[WorkflowNode]) -> list[WorkflowNode]: ids = [node.id for node in nodes] if len(ids) != len(set(ids)): raise ValueError("Node IDs must be unique") return nodes @field_validator("edges") @classmethod def unique_edge_ids(cls, edges: list[WorkflowEdge]) -> list[WorkflowEdge]: ids = [edge.id for edge in edges] if len(ids) != len(set(ids)): raise ValueError("Edge IDs must be unique") return edges class GenerateWorkflowRequest(BaseModel): prompt: str = Field(min_length=10, max_length=20_000) provider: Literal["openai", "gemini", "openrouter", "deterministic"] | None = None model: str | None = Field( default=None, min_length=1, max_length=200, pattern=r"^[A-Za-z0-9][A-Za-z0-9._:/-]*$", ) class GenerateWorkflowResponse(BaseModel): workflow: WorkflowDocument explanation: str warnings: list[str] = Field(default_factory=list) class WorkflowRequest(BaseModel): workflow: WorkflowDocument class ValidationIssue(BaseModel): code: str severity: Literal["error", "warning", "info"] message: str nodeId: str | None = None suggestion: str | None = None class ValidationResult(BaseModel): valid: bool score: int = Field(ge=0, le=100) issues: list[ValidationIssue] class OptimizationSuggestion(BaseModel): title: str description: str impact: Literal["low", "medium", "high"] nodeIds: list[str] = Field(default_factory=list) class OptimizationResponse(BaseModel): workflow: WorkflowDocument suggestions: list[OptimizationSuggestion] class ChatRequest(BaseModel): message: str = Field(min_length=1, max_length=20_000) workflow: WorkflowDocument conversation_id: str | None = None class ChatResponse(BaseModel): message: str workflow: WorkflowDocument | None = None actions: list[str] = Field(default_factory=list) class ExpressionRequest(BaseModel): description: str = Field(min_length=2, max_length=4000) context: dict[str, Any] = Field(default_factory=dict) class ExpressionResponse(BaseModel): expression: str explanation: str alternatives: list[str] = Field(default_factory=list) class ImportRequest(BaseModel): content: str = Field(min_length=2, max_length=5_000_000) source: Literal["json", "clipboard", "url", "github"] = "json" class ExportRequest(BaseModel): workflow: WorkflowDocument format: Literal["n8n", "internal"] = "n8n" class SaveRequest(BaseModel): workflow: WorkflowDocument project_id: str | None = None change_summary: str = Field(default="Manual save", max_length=500) class SaveResponse(BaseModel): id: str version: int saved_at: str class SimulationRequest(BaseModel): workflow: WorkflowDocument input_data: dict[str, Any] = Field(default_factory=dict) class NodeRunResult(BaseModel): node_id: str node_name: str status: Literal["success", "skipped", "error"] duration_ms: int = Field(ge=0) input_data: dict[str, Any] = Field(default_factory=dict) output_data: dict[str, Any] = Field(default_factory=dict) error: str | None = None class SimulationResponse(BaseModel): status: Literal["success", "error"] duration_ms: int = Field(ge=0) trace: list[NodeRunResult] output_data: dict[str, Any] = Field(default_factory=dict) warnings: list[str] = Field(default_factory=list) class TestAssertion(BaseModel): path: str = Field(min_length=1, max_length=500) operator: Literal["equals", "not_equals", "exists", "contains"] = "equals" expected: Any = None class WorkflowTestCase(BaseModel): name: str = Field(min_length=1, max_length=160) input_data: dict[str, Any] = Field(default_factory=dict) assertions: list[TestAssertion] = Field(default_factory=list, max_length=100) class TestWorkflowRequest(BaseModel): workflow: WorkflowDocument cases: list[WorkflowTestCase] = Field(min_length=1, max_length=100) class TestCaseResult(BaseModel): name: str passed: bool failures: list[str] = Field(default_factory=list) duration_ms: int = Field(ge=0) class TestWorkflowResponse(BaseModel): passed: int failed: int results: list[TestCaseResult] class CostEstimate(BaseModel): executions_per_month: int estimated_api_calls: int estimated_ai_tokens: int estimated_monthly_usd: float assumptions: list[str] rate_limit_warnings: list[str] = Field(default_factory=list) class CostEstimateRequest(BaseModel): workflow: WorkflowDocument executions_per_month: int = Field(default=1000, ge=1, le=100_000_000) class WorkflowDiffRequest(BaseModel): before: WorkflowDocument after: WorkflowDocument class WorkflowDiff(BaseModel): added_nodes: list[str] = Field(default_factory=list) removed_nodes: list[str] = Field(default_factory=list) modified_nodes: list[str] = Field(default_factory=list) moved_nodes: list[str] = Field(default_factory=list) added_edges: int = 0 removed_edges: int = 0 class ShareRequest(BaseModel): workflow_id: str permission: Literal["view", "copy"] = "view" expires_in_days: int | None = Field(default=30, ge=1, le=365) class ShareResponse(BaseModel): id: str url: str permission: Literal["view", "copy"] expires_at: datetime | None = None class SharedWorkflowResponse(BaseModel): workflow: WorkflowDocument permission: Literal["view", "copy"] expires_at: datetime | None = None class VersionSummary(BaseModel): id: str version: int change_summary: str | None = None created_at: str created_by: str class RestoreVersionRequest(BaseModel): version: int = Field(ge=1) class CommentRequest(BaseModel): workflow_id: str body: str = Field(min_length=1, max_length=10_000) node_id: str | None = Field(default=None, max_length=128) class WorkflowComment(BaseModel): id: str workflow_id: str user_id: str node_id: str | None = None body: str resolved_at: str | None = None created_at: str class DeploymentRequest(BaseModel): workflow: WorkflowDocument activate: bool = False class DeploymentResponse(BaseModel): status: Literal["deployed", "preview"] remote_workflow_id: str | None = None message: str class LineageField(BaseModel): field: str source_nodes: list[str] = Field(default_factory=list) consumer_nodes: list[str] = Field(default_factory=list) classification: Literal["public", "internal", "personal", "financial", "secret"] class LineageResponse(BaseModel): fields: list[LineageField] node_dependencies: dict[str, list[str]] sensitive_paths: list[str] = Field(default_factory=list) class ContractRequest(BaseModel): workflow: WorkflowDocument sample_data: dict[str, Any] = Field(default_factory=dict) expected_schema: dict[str, Literal["string", "number", "boolean", "object", "array", "null"]] class ContractResponse(BaseModel): valid: bool inferred_schema: dict[str, str] violations: list[str] = Field(default_factory=list) class QualityResponse(BaseModel): overall: int = Field(ge=0, le=100) scores: dict[str, int] findings: list[ValidationIssue] class IntentDriftRequest(BaseModel): workflow: WorkflowDocument requirement: str = Field(min_length=10, max_length=20_000) class IntentDriftResponse(BaseModel): alignment_score: int = Field(ge=0, le=100) covered_terms: list[str] missing_terms: list[str] class ReplayRequest(BaseModel): workflow: WorkflowDocument node_id: str = Field(min_length=1, max_length=128) input_data: dict[str, Any] = Field(default_factory=dict) class EnvironmentPromotionRequest(BaseModel): workflow: WorkflowDocument environment: Literal["development", "staging", "production"] values: dict[str, str | int | float | bool] = Field(default_factory=dict) class EnvironmentPromotionResponse(BaseModel): workflow: WorkflowDocument environment: str replacements: int unresolved: list[str] = Field(default_factory=list) class ReleasePlanRequest(BaseModel): workflow: WorkflowDocument strategy: Literal["shadow", "canary", "synthetic"] traffic_percentage: int = Field(default=10, ge=0, le=100) success_threshold: float = Field(default=0.99, ge=0, le=1) max_error_rate: float = Field(default=0.02, ge=0, le=1) class ReleasePlanResponse(BaseModel): strategy: str status: Literal["draft", "blocked"] requires_approval: bool = True steps: list[str] rollback_conditions: list[str] warnings: list[str] = Field(default_factory=list) class WorkflowPackageRequest(BaseModel): workflow: WorkflowDocument tests: list[WorkflowTestCase] = Field(default_factory=list) contracts: dict[str, Any] = Field(default_factory=dict) environments: dict[str, dict[str, Any]] = Field(default_factory=dict) class WorkflowPackageResponse(BaseModel): manifest: dict[str, Any] workflow: WorkflowDocument tests: list[WorkflowTestCase] contracts: dict[str, Any] environments: dict[str, dict[str, Any]] class DocumentationResponse(BaseModel): markdown: str class RoiRequest(BaseModel): workflow: WorkflowDocument executions_per_month: int = Field(default=1000, ge=1, le=100_000_000) minutes_saved_per_execution: float = Field(default=5, ge=0, le=100_000) hourly_rate_usd: float = Field(default=30, ge=0, le=100_000) sla_minutes: float = Field(default=60, gt=0, le=1_000_000) class RoiResponse(BaseModel): hours_saved: float labor_value_usd: float estimated_operating_cost_usd: float net_value_usd: float estimated_duration_ms: int sla_headroom_percent: float class WebhookInspectRequest(BaseModel): payload: dict[str, Any] redact: bool = True class WebhookInspectResponse(BaseModel): payload: dict[str, Any] schema_map: dict[str, str] redacted_fields: list[str] class DependencyImpactRequest(BaseModel): workflow: WorkflowDocument dependency: str = Field(min_length=1, max_length=500) class DependencyImpactResponse(BaseModel): affected_nodes: list[str] downstream_nodes: list[str] severity: Literal["none", "low", "medium", "high"] class SelfHealRequest(BaseModel): workflow: WorkflowDocument errors: list[str] = Field(default_factory=list, max_length=100) class SelfHealResponse(BaseModel): proposed_workflow: WorkflowDocument changes: list[str] quality_before: int quality_after: int requires_approval: bool = True