feat(workflow): support multi-variable assignment in assigner node

This commit is contained in:
mengyonghao
2025-12-23 17:45:37 +08:00
parent ac1afeffaf
commit d423e80ddb
2 changed files with 61 additions and 50 deletions

View File

@@ -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.base_config import BaseNodeConfig
from app.core.workflow.nodes.enums import AssignmentOperator from app.core.workflow.nodes.enums import AssignmentOperator
class AssignerNodeConfig(BaseNodeConfig): class AssignmentItem(BaseModel):
"""
Single assignment definition.
"""
variable_selector: str | list[str] = Field( variable_selector: str | list[str] = Field(
..., ...,
description="Variables to be assigned", description="Target variable name(s) to assign",
) )
operation: AssignmentOperator = Field( operation: AssignmentOperator = Field(
..., ...,
description="Operator to assign", description="Assignment operator",
) )
value: str | list[str] = Field( 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",
) )

View File

@@ -29,52 +29,52 @@ class AssignerNode(BaseNode):
""" """
# Initialize a variable pool for accessing conversation, node, and system variables # Initialize a variable pool for accessing conversation, node, and system variables
pool = VariablePool(state) 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") # Only conversation variables ('conv') are allowed
variable_selector = self.typed_config.variable_selector if variable_selector[0] != 'conv': # TODO: Loop node variable support (Feature)
if isinstance(variable_selector, str): raise ValueError("Only conversation variables can be assigned.")
# Support dot-separated string paths, e.g., "conv.test" -> ["conv", "test"]
variable_selector = variable_selector.split('.')
# Only conversation variables ('conv') are allowed # Get the value or expression to assign
if variable_selector[0] != 'conv': # TODO: Loop node variable support (Feature) value = assignment.value
raise ValueError("Only conversation variables can be assigned.") 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 # Select the appropriate assignment operator instance based on the target variable type
value = self.typed_config.value operator: AssignmentOperatorInstance = AssignmentOperator.get_operator(pool.get(variable_selector))(
if isinstance(value, list): pool, variable_selector, value
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 # Execute the configured assignment operation
operator: AssignmentOperatorInstance = AssignmentOperator.get_operator(pool.get(variable_selector))( match assignment.operation:
pool, variable_selector, value case AssignmentOperator.ASSIGN:
) operator.assign()
case AssignmentOperator.CLEAR:
# Execute the configured assignment operation operator.clear()
match self.typed_config.operation: case AssignmentOperator.ADD:
case AssignmentOperator.ASSIGN: operator.add()
operator.assign() case AssignmentOperator.SUBTRACT:
case AssignmentOperator.CLEAR: operator.subtract()
operator.clear() case AssignmentOperator.MULTIPLY:
case AssignmentOperator.ADD: operator.multiply()
operator.add() case AssignmentOperator.DIVIDE:
case AssignmentOperator.SUBTRACT: operator.divide()
operator.subtract() case AssignmentOperator.APPEND:
case AssignmentOperator.MULTIPLY: operator.append()
operator.multiply() case AssignmentOperator.REMOVE_FIRST:
case AssignmentOperator.DIVIDE: operator.remove_first()
operator.divide() case AssignmentOperator.REMOVE_LAST:
case AssignmentOperator.APPEND: operator.remove_last()
operator.append() case _:
case AssignmentOperator.REMOVE_FIRST: raise ValueError(f"Invalid Operator: {assignment.operation}")
operator.remove_first()
case AssignmentOperator.REMOVE_LAST:
operator.remove_last()
case _:
raise ValueError(f"Invalid Operator: {self.typed_config.operation}")