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 <noreply@anthropic.com>
This commit is contained in:
parent
fe28cce921
commit
c95b4e185d
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user