From c95b4e185d931ce972b6976572ffdc3182632198 Mon Sep 17 00:00:00 2001 From: Lars Date: Sun, 5 Apr 2026 07:22:32 +0200 Subject: [PATCH] fix: Edge format normalization and nullable workflow_id Fixes: 1. Edge Format Mismatch: - graph_data uses React Flow format (source/target) - WorkflowEdge expects backend format (from/to) - Added normalization in parse_workflow_graph() 2. UUID Validation Error: - workflow_id can be None when using graph_data (Phase 5) - save_execution_state now accepts Optional[str] - ExecutionResult uses "N/A" placeholder when None Changes: - workflow_engine.py: normalize edges before Pydantic validation - workflow_executor.py: Optional[str] for workflow_id parameter Co-Authored-By: Claude Opus 4.6 --- backend/workflow_engine.py | 16 ++++++++++++++++ backend/workflow_executor.py | 8 ++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/backend/workflow_engine.py b/backend/workflow_engine.py index 59bf7bf..cd5b850 100644 --- a/backend/workflow_engine.py +++ b/backend/workflow_engine.py @@ -354,6 +354,9 @@ def parse_workflow_graph(graph_jsonb: Dict) -> WorkflowGraph: Args: graph_jsonb: JSONB dict aus workflow_definitions.graph + Unterstützt beide Edge-Formate: + - Backend: {"from": "...", "to": "..."} + - Frontend: {"source": "...", "target": "..."} Returns: Validiertes WorkflowGraph-Objekt @@ -361,6 +364,19 @@ def parse_workflow_graph(graph_jsonb: Dict) -> WorkflowGraph: Raises: ValidationError: Bei ungültigem Graph-Format """ + # Normalize edges: convert React Flow format (source/target) to backend format (from/to) + if "edges" in graph_jsonb: + normalized_edges = [] + for edge in graph_jsonb["edges"]: + normalized_edge = edge.copy() + # Convert source → from, target → to (if present) + if "source" in normalized_edge and "from" not in normalized_edge: + normalized_edge["from"] = normalized_edge.pop("source") + if "target" in normalized_edge and "to" not in normalized_edge: + normalized_edge["to"] = normalized_edge.pop("target") + normalized_edges.append(normalized_edge) + graph_jsonb = {**graph_jsonb, "edges": normalized_edges} + return WorkflowGraph(**graph_jsonb) diff --git a/backend/workflow_executor.py b/backend/workflow_executor.py index 323c4f9..6ad0c94 100644 --- a/backend/workflow_executor.py +++ b/backend/workflow_executor.py @@ -201,7 +201,7 @@ async def execute_workflow( completed_at = datetime.utcnow().isoformat() save_execution_state( execution_id=execution_id, - workflow_id=workflow_id or "", # Empty string if None (when graph_data is used) + workflow_id=workflow_id, # Can be None when graph_data is used profile_id=profile_id, node_states=node_states if 'node_states' in locals() else [], status="failed", @@ -212,7 +212,7 @@ async def execute_workflow( return ExecutionResult( execution_id=execution_id, - workflow_id=workflow_id or "", # Empty string if None (when graph_data is used) + workflow_id=workflow_id or "N/A", # ExecutionResult requires string, use placeholder status="failed", node_states=node_states if 'node_states' in locals() else [], aggregated_result={}, @@ -840,7 +840,7 @@ def aggregate_results(node_states: List[NodeExecutionState]) -> Dict[str, Any]: def save_execution_state( execution_id: str, - workflow_id: str, + workflow_id: Optional[str], # None when using graph_data directly (Phase 5) profile_id: str, node_states: List[NodeExecutionState], status: str, @@ -853,7 +853,7 @@ def save_execution_state( Args: execution_id: UUID der Execution - workflow_id: UUID des Workflows + workflow_id: UUID des Workflows (None wenn graph_data direkt verwendet wird) profile_id: UUID des Profils node_states: Liste aller NodeExecutionState status: 'completed' | 'failed' | 'partial'