From d423e80ddb1cd379b372c0e1fa167ba4d4e0c992 Mon Sep 17 00:00:00 2001 From: mengyonghao <1533512157@qq.com> Date: Tue, 23 Dec 2025 17:45:37 +0800 Subject: [PATCH] feat(workflow): support multi-variable assignment in assigner node --- .../core/workflow/nodes/assigner/config.py | 21 +++-- api/app/core/workflow/nodes/assigner/node.py | 90 +++++++++---------- 2 files changed, 61 insertions(+), 50 deletions(-) diff --git a/api/app/core/workflow/nodes/assigner/config.py b/api/app/core/workflow/nodes/assigner/config.py index 1cb0def3..03302af4 100644 --- a/api/app/core/workflow/nodes/assigner/config.py +++ b/api/app/core/workflow/nodes/assigner/config.py @@ -1,21 +1,32 @@ -from pydantic import Field +from pydantic import Field, BaseModel from app.core.workflow.nodes.base_config import BaseNodeConfig from app.core.workflow.nodes.enums import AssignmentOperator -class AssignerNodeConfig(BaseNodeConfig): +class AssignmentItem(BaseModel): + """ + Single assignment definition. + """ + variable_selector: str | list[str] = Field( ..., - description="Variables to be assigned", + description="Target variable name(s) to assign", ) operation: AssignmentOperator = Field( ..., - description="Operator to assign", + description="Assignment operator", ) value: str | list[str] = Field( ..., - description="Values to assign", + description="Value(s) to assign to the variable(s)", + ) + + +class AssignerNodeConfig(BaseNodeConfig): + assignments: list[AssignmentItem] = Field( + ..., + description="List of variable assignment definitions", ) diff --git a/api/app/core/workflow/nodes/assigner/node.py b/api/app/core/workflow/nodes/assigner/node.py index eb32bf8b..b8b7c1f4 100644 --- a/api/app/core/workflow/nodes/assigner/node.py +++ b/api/app/core/workflow/nodes/assigner/node.py @@ -29,52 +29,52 @@ class AssignerNode(BaseNode): """ # Initialize a variable pool for accessing conversation, node, and system variables pool = VariablePool(state) + for assignment in self.typed_config.assignments: + # Get the target variable selector (e.g., "conv.test") + variable_selector = assignment.variable_selector + if isinstance(variable_selector, str): + # Support dot-separated string paths, e.g., "conv.test" -> ["conv", "test"] + variable_selector = variable_selector.split('.') - # Get the target variable selector (e.g., "conv.test") - variable_selector = self.typed_config.variable_selector - if isinstance(variable_selector, str): - # Support dot-separated string paths, e.g., "conv.test" -> ["conv", "test"] - variable_selector = variable_selector.split('.') + # Only conversation variables ('conv') are allowed + if variable_selector[0] != 'conv': # TODO: Loop node variable support (Feature) + raise ValueError("Only conversation variables can be assigned.") - # Only conversation variables ('conv') are allowed - if variable_selector[0] != 'conv': # TODO: Loop node variable support (Feature) - raise ValueError("Only conversation variables can be assigned.") + # Get the value or expression to assign + value = assignment.value + if isinstance(value, list): + value = '.'.join(value) + value = ExpressionEvaluator.evaluate( + expression=value, + variables=pool.get_all_conversation_vars(), + node_outputs=pool.get_all_node_outputs(), + system_vars=pool.get_all_system_vars(), + ) - # Get the value or expression to assign - value = self.typed_config.value - if isinstance(value, list): - value = '.'.join(value) - value = ExpressionEvaluator.evaluate( - expression=value, - variables=pool.get_all_conversation_vars(), - node_outputs=pool.get_all_node_outputs(), - system_vars=pool.get_all_system_vars(), - ) + # Select the appropriate assignment operator instance based on the target variable type + operator: AssignmentOperatorInstance = AssignmentOperator.get_operator(pool.get(variable_selector))( + pool, variable_selector, value + ) - # Select the appropriate assignment operator instance based on the target variable type - operator: AssignmentOperatorInstance = AssignmentOperator.get_operator(pool.get(variable_selector))( - pool, variable_selector, value - ) - - # Execute the configured assignment operation - match self.typed_config.operation: - case AssignmentOperator.ASSIGN: - operator.assign() - case AssignmentOperator.CLEAR: - operator.clear() - case AssignmentOperator.ADD: - operator.add() - case AssignmentOperator.SUBTRACT: - operator.subtract() - case AssignmentOperator.MULTIPLY: - operator.multiply() - case AssignmentOperator.DIVIDE: - operator.divide() - case AssignmentOperator.APPEND: - operator.append() - case AssignmentOperator.REMOVE_FIRST: - operator.remove_first() - case AssignmentOperator.REMOVE_LAST: - operator.remove_last() - case _: - raise ValueError(f"Invalid Operator: {self.typed_config.operation}") + # Execute the configured assignment operation + match assignment.operation: + case AssignmentOperator.ASSIGN: + operator.assign() + case AssignmentOperator.CLEAR: + operator.clear() + case AssignmentOperator.ADD: + operator.add() + case AssignmentOperator.SUBTRACT: + operator.subtract() + case AssignmentOperator.MULTIPLY: + operator.multiply() + case AssignmentOperator.DIVIDE: + operator.divide() + case AssignmentOperator.APPEND: + operator.append() + case AssignmentOperator.REMOVE_FIRST: + operator.remove_first() + case AssignmentOperator.REMOVE_LAST: + operator.remove_last() + case _: + raise ValueError(f"Invalid Operator: {assignment.operation}")