From 12d4d7c63badbfce907edf7baf96e8f5379622db Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 13 Apr 2026 12:38:55 +0200 Subject: [PATCH] feat: Add comprehensive debug information for workflow nodes Backend changes: - workflow_models.py: Add debug_prompt, debug_raw_response, debug_node_type, debug_prompt_slug, metadata fields to NodeExecutionState - workflow_executor.py: Capture and store debug info for analysis, logic, and join nodes when enable_debug=True - Analysis nodes: store full prompt + raw AI response - Logic nodes: store expression + evaluation result - Join nodes: store strategy + path statistics Frontend changes: - Analysis.jsx: Enable debug mode by default (debug=true) for all workflow executions This allows developers to see exactly what prompt was sent to the AI, what response was received, and how each node was processed - essential for debugging workflow issues. --- backend/workflow_executor.py | 36 +++++++++++++++++++++++++++------ backend/workflow_models.py | 9 +++++++++ frontend/src/pages/Analysis.jsx | 2 +- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/backend/workflow_executor.py b/backend/workflow_executor.py index 6e36ad4..4672926 100644 --- a/backend/workflow_executor.py +++ b/backend/workflow_executor.py @@ -314,11 +314,11 @@ async def execute_node( # Logic Nodes (Phase 3) if node.type == "logic": - return execute_logic_node(node, context, graph) + return execute_logic_node(node, context, graph, enable_debug) # Join Nodes (Phase 4) if node.type == "join": - return execute_join_node(node, context, graph) + return execute_join_node(node, context, graph, enable_debug) # Analysis Nodes if node.type == "analysis": @@ -398,6 +398,11 @@ async def execute_node( decision_signals=parsed["decision_signals"], normalized_signals=normalized_signals, reasoning_anchors=parsed.get("reasoning_anchors"), + # Debug information (nur wenn enable_debug=True) + debug_prompt=augmented_prompt if enable_debug else None, + debug_raw_response=llm_response if enable_debug else None, + debug_node_type="analysis", + debug_prompt_slug=node.prompt_slug if enable_debug else None, started_at=started_at, completed_at=datetime.utcnow().isoformat() ) @@ -419,7 +424,8 @@ async def execute_node( def execute_logic_node( node, context: Dict[str, Any], - graph: WorkflowGraph + graph: WorkflowGraph, + enable_debug: bool = False ) -> NodeExecutionState: """ Führt Logic Node aus (Phase 3). @@ -519,7 +525,15 @@ def execute_logic_node( "evaluation_result": result if not error else None, "error": error, "activated_edges": activated_edges - }) + }), + # Debug information + debug_prompt=json.dumps(expression.model_dump() if hasattr(expression, 'model_dump') else str(expression), ensure_ascii=False) if enable_debug and expression else None, + debug_raw_response=json.dumps({ + "result": result, + "error": error, + "activated_edges": activated_edges + }, ensure_ascii=False) if enable_debug else None, + debug_node_type="logic" ) except Exception as e: @@ -536,7 +550,8 @@ def execute_logic_node( def execute_join_node( node, context: Dict[str, Any], - graph: WorkflowGraph + graph: WorkflowGraph, + enable_debug: bool = False ) -> NodeExecutionState: """ Führt Join Node aus (Phase 4). @@ -608,7 +623,16 @@ def execute_join_node( normalized_signals=consolidated_signals_list, metadata=join_result.metadata, started_at=started_at, - completed_at=datetime.utcnow().isoformat() + completed_at=datetime.utcnow().isoformat(), + # Debug information + debug_prompt=f"Join Strategy: {node.join_strategy.value if node.join_strategy else 'wait_all'}\nPaths: {total_count}\nMinimum Required: {node.min_paths if node.min_paths else 'all'}" if enable_debug else None, + debug_raw_response=json.dumps({ + "executed_paths": executed_count, + "total_paths": total_count, + "strategy": node.join_strategy.value if node.join_strategy else 'wait_all', + "consolidated_nodes": list(join_result.consolidated_analysis_core.keys()) + }, ensure_ascii=False) if enable_debug else None, + debug_node_type="join" ) except Exception as e: diff --git a/backend/workflow_models.py b/backend/workflow_models.py index 0467285..1d67142 100644 --- a/backend/workflow_models.py +++ b/backend/workflow_models.py @@ -349,6 +349,15 @@ class NodeExecutionState(BaseModel): normalized_signals: List[NormalizedSignal] = Field(default_factory=list, description="Normalisierte Signale (Phase 2)") reasoning_anchors: Optional[str] = Field(None, description="Begründungsanker aus ## Begründung") + # Debug Information (nur wenn enable_debug=True) + debug_prompt: Optional[str] = Field(None, description="Vollständiger Prompt der an die KI gesendet wurde") + debug_raw_response: Optional[str] = Field(None, description="Rohe KI-Antwort (ungeparst)") + debug_node_type: Optional[str] = Field(None, description="Node-Typ (analysis, logic, join, etc.)") + debug_prompt_slug: Optional[str] = Field(None, description="Verwendeter Prompt-Slug (bei ANALYSIS nodes)") + + # Metadata (für Join Nodes und andere Zusatzinfos) + metadata: Optional[Dict[str, Any]] = Field(None, description="Zusätzliche Metadaten (z.B. Join-Statistiken)") + # Error & Timing error: Optional[str] = Field(None, description="Fehlermeldung bei failed") started_at: Optional[str] = Field(None, description="Start-Timestamp (ISO)") diff --git a/frontend/src/pages/Analysis.jsx b/frontend/src/pages/Analysis.jsx index 8f03c81..5ddcb56 100644 --- a/frontend/src/pages/Analysis.jsx +++ b/frontend/src/pages/Analysis.jsx @@ -382,7 +382,7 @@ export default function Analysis() { setLoading(slug); setError(null); setNewResult(null); setProgress(null) try { // Use SSE-based executor for long-running workflows - const result = await api.executeUnifiedPromptStream(slug, null, null, false, true, (event) => { + const result = await api.executeUnifiedPromptStream(slug, null, null, true, true, (event) => { // Progress callback: update UI in real-time if (event.type === 'execution_started') { setProgress({ total_nodes: 0, completed_nodes: 0, current_node_label: 'Starte...' })