Async Workflow #82

Merged
Lars merged 14 commits from develop into main 2026-04-13 11:58:01 +02:00
2 changed files with 20 additions and 26 deletions
Showing only changes of commit f5ce1ec941 - Show all commits

View File

@ -409,37 +409,28 @@ def execute_logic_node(
completed_at=datetime.utcnow().isoformat() completed_at=datetime.utcnow().isoformat()
) )
# Handle both serialization formats: # Handle both formats (thanks to Union[LogicExpression, Condition] type):
# UI format: condition = {operands: [...], operator: "and"} (dict or LogicExpression) # 1. Direct LogicExpression (UI format): node.condition is LogicExpression
# Legacy format: condition = {expression: {operands: [...], operator: "and"}} (Condition object) # 2. Wrapped in Condition (legacy): node.condition is Condition with .expression
from workflow_models import LogicExpression, Condition
expression = None expression = None
# Convert to dict if it's a Pydantic model if isinstance(node.condition, LogicExpression):
condition_dict = node.condition
if hasattr(node.condition, 'model_dump'):
condition_dict = node.condition.model_dump()
elif hasattr(node.condition, 'dict'):
condition_dict = node.condition.dict()
# Check if it's a dict
if isinstance(condition_dict, dict):
# UI format: direct LogicExpression # UI format: direct LogicExpression
if 'operator' in condition_dict and 'operands' in condition_dict: expression = node.condition
from workflow_models import LogicExpression elif isinstance(node.condition, Condition):
expression = LogicExpression(**condition_dict)
# Legacy format: wrapped in Condition # Legacy format: wrapped in Condition
elif 'expression' in condition_dict and condition_dict['expression'] is not None: expression = node.condition.expression
from workflow_models import LogicExpression
expression = LogicExpression(**condition_dict['expression'])
# Pydantic object
else: else:
# Fallback: try to detect format manually
if hasattr(node.condition, 'operator') and hasattr(node.condition, 'operands'): if hasattr(node.condition, 'operator') and hasattr(node.condition, 'operands'):
expression = node.condition expression = node.condition # Looks like LogicExpression
elif hasattr(node.condition, 'expression') and node.condition.expression is not None: elif hasattr(node.condition, 'expression'):
expression = node.condition.expression expression = node.condition.expression # Looks like Condition
if expression is None: if expression is None:
error_msg = f"Logic node {node.id} has invalid or empty condition (operator/operands/expression is None or missing)" error_msg = f"Logic node {node.id} has no valid condition/expression defined"
logger.error(error_msg) logger.error(error_msg)
return NodeExecutionState( return NodeExecutionState(
node_id=node.id, node_id=node.id,

View File

@ -6,7 +6,7 @@ Data validation schemas for Workflow-Graph, Knoten, Kanten, Bedingungen.
Konzept-Basis: konzept_workflow_engine_konsolidated.md Konzept-Basis: konzept_workflow_engine_konsolidated.md
Anforderungsanalyse: anforderungsanalyse_umsetzungsplan.md Anforderungsanalyse: anforderungsanalyse_umsetzungsplan.md
""" """
from typing import Optional, List, Dict, Any from typing import Optional, List, Dict, Any, Union
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from enum import Enum from enum import Enum
@ -148,11 +148,14 @@ class LogicExpression(BaseModel):
} }
""" """
operator: LogicOperator = Field(..., description="Logischer Operator (and, or, not) oder Vergleichsoperator") operator: LogicOperator = Field(..., description="Logischer Operator (and, or, not) oder Vergleichsoperator")
operands: Optional[List[Any]] = Field(None, description="Liste von Operanden (LogicOperand oder verschachtelte LogicExpression)") operands: Optional[List['LogicExpression']] = Field(None, description="Liste von Operanden (LogicOperand oder verschachtelte LogicExpression)")
# Bei einfachem Vergleich: # Bei einfachem Vergleich:
ref: Optional[str] = Field(None, description="Signal-Referenz (nur bei Vergleichsoperatoren)") ref: Optional[str] = Field(None, description="Signal-Referenz (nur bei Vergleichsoperatoren)")
value: Optional[Any] = Field(None, description="Vergleichswert (nur bei Vergleichsoperatoren)") value: Optional[Any] = Field(None, description="Vergleichswert (nur bei Vergleichsoperatoren)")
# Enable forward reference resolution for recursive model
LogicExpression.model_rebuild()
class Condition(BaseModel): class Condition(BaseModel):
""" """
@ -196,7 +199,7 @@ class WorkflowNode(BaseModel):
# LOGIC-Knoten # LOGIC-Knoten
# Support both formats: direct LogicExpression (UI) or wrapped in Condition (legacy) # Support both formats: direct LogicExpression (UI) or wrapped in Condition (legacy)
condition: Optional[Any] = Field(None, description="Bedingung für Pfad-Routing (LogicExpression or Condition)") condition: Optional[Union[LogicExpression, Condition]] = Field(None, description="Bedingung für Pfad-Routing")
fallback: Optional[FallbackConfig] = Field(None, description="Fallback-Konfiguration") fallback: Optional[FallbackConfig] = Field(None, description="Fallback-Konfiguration")
# JOIN-Knoten (Phase 4) # JOIN-Knoten (Phase 4)