fix: Support UI-format LogicExpression in Logic-Node condition field
Root cause: UI saves LogicExpression directly as condition:
{operands: [...], operator: "and"}
But Pydantic model expected Condition with wrapped expression:
{expression: {operands: [...], operator: "and"}}
Result: Pydantic deserialized it as Condition with expression=None
→ Logic-Nodes failed with "'NoneType' object has no attribute 'operator'"
Fix:
1. Changed WorkflowNode.condition type from Condition to Any
2. Executor now handles both dict and Pydantic model formats
3. Detects UI format (operator+operands) vs legacy format (expression wrapper)
4. Converts dict to LogicExpression before evaluation
Fixes: Logic-Node execution failures in Training-Tiefenanalyse workflow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0eac40abf6
commit
2deb6510f8
|
|
@ -410,16 +410,32 @@ def execute_logic_node(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle both serialization formats:
|
# Handle both serialization formats:
|
||||||
# UI format: condition = {operands: [...], operator: "and"}
|
# UI format: condition = {operands: [...], operator: "and"} (dict or LogicExpression)
|
||||||
# Expected format: condition = {expression: {operands: [...], operator: "and"}}
|
# Legacy format: condition = {expression: {operands: [...], operator: "and"}} (Condition object)
|
||||||
expression = None
|
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):
|
||||||
|
# UI format: direct LogicExpression
|
||||||
|
if 'operator' in condition_dict and 'operands' in condition_dict:
|
||||||
|
from workflow_models import LogicExpression
|
||||||
|
expression = LogicExpression(**condition_dict)
|
||||||
|
# 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
|
||||||
|
else:
|
||||||
if hasattr(node.condition, 'operator') and hasattr(node.condition, 'operands'):
|
if hasattr(node.condition, 'operator') and hasattr(node.condition, 'operands'):
|
||||||
# UI format (direct) - check that operator is not None
|
|
||||||
if node.condition.operator is not None:
|
|
||||||
expression = node.condition
|
expression = node.condition
|
||||||
elif hasattr(node.condition, 'expression') and node.condition.expression is not None:
|
elif hasattr(node.condition, 'expression') and node.condition.expression is not None:
|
||||||
# Expected format (wrapped) - check that expression is not None
|
|
||||||
expression = node.condition.expression
|
expression = node.condition.expression
|
||||||
|
|
||||||
if expression is None:
|
if expression is None:
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,8 @@ class WorkflowNode(BaseModel):
|
||||||
question_augmentations: Optional[List[QuestionAugmentation]] = Field(None, description="Fragenergänzungen (knotengebunden, überschreiben Prompt-Defaults)")
|
question_augmentations: Optional[List[QuestionAugmentation]] = Field(None, description="Fragenergänzungen (knotengebunden, überschreiben Prompt-Defaults)")
|
||||||
|
|
||||||
# LOGIC-Knoten
|
# LOGIC-Knoten
|
||||||
condition: Optional[Condition] = Field(None, description="Bedingung für Pfad-Routing")
|
# 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)")
|
||||||
fallback: Optional[FallbackConfig] = Field(None, description="Fallback-Konfiguration")
|
fallback: Optional[FallbackConfig] = Field(None, description="Fallback-Konfiguration")
|
||||||
|
|
||||||
# JOIN-Knoten (Phase 4)
|
# JOIN-Knoten (Phase 4)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user