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()
)
# Handle both serialization formats:
# UI format: condition = {operands: [...], operator: "and"} (dict or LogicExpression)
# Legacy format: condition = {expression: {operands: [...], operator: "and"}} (Condition object)
# Handle both formats (thanks to Union[LogicExpression, Condition] type):
# 1. Direct LogicExpression (UI format): node.condition is LogicExpression
# 2. Wrapped in Condition (legacy): node.condition is Condition with .expression
from workflow_models import LogicExpression, Condition
expression = None
# Convert to dict if it's a Pydantic model
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):
if isinstance(node.condition, LogicExpression):
# UI format: direct LogicExpression
if 'operator' in condition_dict and 'operands' in condition_dict:
from workflow_models import LogicExpression
expression = LogicExpression(**condition_dict)
expression = node.condition
elif isinstance(node.condition, Condition):
# Legacy format: wrapped in Condition
elif 'expression' in condition_dict and condition_dict['expression'] is not None:
from workflow_models import LogicExpression
expression = LogicExpression(**condition_dict['expression'])
# Pydantic object
expression = node.condition.expression
else:
# Fallback: try to detect format manually
if hasattr(node.condition, 'operator') and hasattr(node.condition, 'operands'):
expression = node.condition
elif hasattr(node.condition, 'expression') and node.condition.expression is not None:
expression = node.condition.expression
expression = node.condition # Looks like LogicExpression
elif hasattr(node.condition, 'expression'):
expression = node.condition.expression # Looks like Condition
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)
return NodeExecutionState(
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
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 enum import Enum
@ -148,11 +148,14 @@ class LogicExpression(BaseModel):
}
"""
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:
ref: Optional[str] = Field(None, description="Signal-Referenz (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):
"""
@ -196,7 +199,7 @@ class WorkflowNode(BaseModel):
# LOGIC-Knoten
# 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")
# JOIN-Knoten (Phase 4)