feat: Add comprehensive debug information for workflow nodes
All checks were successful
Deploy Development / deploy (push) Successful in 55s
Build Test / pytest-backend (push) Successful in 8s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s

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.
This commit is contained in:
Lars 2026-04-13 12:38:55 +02:00
parent 3664f53c51
commit 12d4d7c63b
3 changed files with 40 additions and 7 deletions

View File

@ -314,11 +314,11 @@ async def execute_node(
# Logic Nodes (Phase 3) # Logic Nodes (Phase 3)
if node.type == "logic": if node.type == "logic":
return execute_logic_node(node, context, graph) return execute_logic_node(node, context, graph, enable_debug)
# Join Nodes (Phase 4) # Join Nodes (Phase 4)
if node.type == "join": if node.type == "join":
return execute_join_node(node, context, graph) return execute_join_node(node, context, graph, enable_debug)
# Analysis Nodes # Analysis Nodes
if node.type == "analysis": if node.type == "analysis":
@ -398,6 +398,11 @@ async def execute_node(
decision_signals=parsed["decision_signals"], decision_signals=parsed["decision_signals"],
normalized_signals=normalized_signals, normalized_signals=normalized_signals,
reasoning_anchors=parsed.get("reasoning_anchors"), 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, started_at=started_at,
completed_at=datetime.utcnow().isoformat() completed_at=datetime.utcnow().isoformat()
) )
@ -419,7 +424,8 @@ async def execute_node(
def execute_logic_node( def execute_logic_node(
node, node,
context: Dict[str, Any], context: Dict[str, Any],
graph: WorkflowGraph graph: WorkflowGraph,
enable_debug: bool = False
) -> NodeExecutionState: ) -> NodeExecutionState:
""" """
Führt Logic Node aus (Phase 3). Führt Logic Node aus (Phase 3).
@ -519,7 +525,15 @@ def execute_logic_node(
"evaluation_result": result if not error else None, "evaluation_result": result if not error else None,
"error": error, "error": error,
"activated_edges": activated_edges "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: except Exception as e:
@ -536,7 +550,8 @@ def execute_logic_node(
def execute_join_node( def execute_join_node(
node, node,
context: Dict[str, Any], context: Dict[str, Any],
graph: WorkflowGraph graph: WorkflowGraph,
enable_debug: bool = False
) -> NodeExecutionState: ) -> NodeExecutionState:
""" """
Führt Join Node aus (Phase 4). Führt Join Node aus (Phase 4).
@ -608,7 +623,16 @@ def execute_join_node(
normalized_signals=consolidated_signals_list, normalized_signals=consolidated_signals_list,
metadata=join_result.metadata, metadata=join_result.metadata,
started_at=started_at, 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: except Exception as e:

View File

@ -349,6 +349,15 @@ class NodeExecutionState(BaseModel):
normalized_signals: List[NormalizedSignal] = Field(default_factory=list, description="Normalisierte Signale (Phase 2)") 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") 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 & Timing
error: Optional[str] = Field(None, description="Fehlermeldung bei failed") error: Optional[str] = Field(None, description="Fehlermeldung bei failed")
started_at: Optional[str] = Field(None, description="Start-Timestamp (ISO)") started_at: Optional[str] = Field(None, description="Start-Timestamp (ISO)")

View File

@ -382,7 +382,7 @@ export default function Analysis() {
setLoading(slug); setError(null); setNewResult(null); setProgress(null) setLoading(slug); setError(null); setNewResult(null); setProgress(null)
try { try {
// Use SSE-based executor for long-running workflows // 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 // Progress callback: update UI in real-time
if (event.type === 'execution_started') { if (event.type === 'execution_started') {
setProgress({ total_nodes: 0, completed_nodes: 0, current_node_label: 'Starte...' }) setProgress({ total_nodes: 0, completed_nodes: 0, current_node_label: 'Starte...' })