fix(api): fix the issue that variable assigner does not save user input into variable pool

pull/20699/head
QuantumGhost 11 months ago
parent 719986f763
commit b41d0e1782

@ -61,14 +61,17 @@ class VariableAssignerNode(BaseNode[VariableAssignerData]):
node_id: str, node_id: str,
node_data: VariableAssignerData, node_data: VariableAssignerData,
) -> Mapping[str, Sequence[str]]: ) -> Mapping[str, Sequence[str]]:
mapping = {}
assigned_variable_node_id = node_data.assigned_variable_selector[0] assigned_variable_node_id = node_data.assigned_variable_selector[0]
if assigned_variable_node_id != CONVERSATION_VARIABLE_NODE_ID: if assigned_variable_node_id == CONVERSATION_VARIABLE_NODE_ID:
return {}
selector_key = ".".join(node_data.assigned_variable_selector) selector_key = ".".join(node_data.assigned_variable_selector)
key = f"{node_id}.#{selector_key}#" key = f"{node_id}.#{selector_key}#"
return { mapping[key] = node_data.assigned_variable_selector
key: node_data.assigned_variable_selector,
} selector_key = ".".join(node_data.input_variable_selector)
key = f"{node_id}.#{selector_key}#"
mapping[key] = node_data.input_variable_selector
return mapping
def _run(self) -> NodeRunResult: def _run(self) -> NodeRunResult:
assigned_variable_selector = self.node_data.assigned_variable_selector assigned_variable_selector = self.node_data.assigned_variable_selector

@ -12,6 +12,12 @@ class VariableOperationItem(BaseModel):
variable_selector: Sequence[str] variable_selector: Sequence[str]
input_type: InputType input_type: InputType
operation: Operation operation: Operation
# NOTE(QuantumGhost): The `value` field serves multiple purposes depending on context:
#
# 1. For CONSTANT input_type: Contains the literal value to be used in the operation.
# 2. For VARIABLE input_type: Initially contains the selector of the source variable.
# 3. During the variable updating procedure: The `value` field is reassigned to hold
# the resolved actual value that will be applied to the target variable.
value: Any | None = None value: Any | None = None

@ -29,3 +29,8 @@ class InvalidInputValueError(VariableOperatorNodeError):
class ConversationIDNotFoundError(VariableOperatorNodeError): class ConversationIDNotFoundError(VariableOperatorNodeError):
def __init__(self): def __init__(self):
super().__init__("conversation_id not found") super().__init__("conversation_id not found")
class InvalidDataError(VariableOperatorNodeError):
def __init__(self, message: str) -> None:
super().__init__(message)

@ -1,9 +1,10 @@
import json import json
from collections.abc import Callable, Mapping, Sequence from collections.abc import Callable, Mapping, MutableMapping, Sequence
from typing import Any, ClassVar, TypeAlias, cast from typing import Any, ClassVar, TypeAlias, cast
from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.app_invoke_entities import InvokeFrom
from core.variables import SegmentType, Variable from core.variables import SegmentType, Variable
from core.variables.consts import MIN_SELECTORS_LENGTH
from core.workflow.constants import CONVERSATION_VARIABLE_NODE_ID from core.workflow.constants import CONVERSATION_VARIABLE_NODE_ID
from core.workflow.conversation_variable_updater import ConversationVariableUpdater from core.workflow.conversation_variable_updater import ConversationVariableUpdater
from core.workflow.entities.node_entities import NodeRunResult from core.workflow.entities.node_entities import NodeRunResult
@ -16,11 +17,12 @@ from core.workflow.nodes.variable_assigner.common.impl import conversation_varia
from . import helpers from . import helpers
from .constants import EMPTY_VALUE_MAPPING from .constants import EMPTY_VALUE_MAPPING
from .entities import VariableAssignerNodeData from .entities import VariableAssignerNodeData, VariableOperationItem
from .enums import InputType, Operation from .enums import InputType, Operation
from .exc import ( from .exc import (
ConversationIDNotFoundError, ConversationIDNotFoundError,
InputTypeNotSupportedError, InputTypeNotSupportedError,
InvalidDataError,
InvalidInputValueError, InvalidInputValueError,
OperationNotSupportedError, OperationNotSupportedError,
VariableNotFoundError, VariableNotFoundError,
@ -29,6 +31,29 @@ from .exc import (
_CONV_VAR_UPDATER_FACTORY: TypeAlias = Callable[[], ConversationVariableUpdater] _CONV_VAR_UPDATER_FACTORY: TypeAlias = Callable[[], ConversationVariableUpdater]
def _target_mapping_from_item(mapping: MutableMapping[str, Sequence[str]], node_id: str, item: VariableOperationItem):
selector_node_id = item.variable_selector[0]
if selector_node_id != CONVERSATION_VARIABLE_NODE_ID:
return
selector_str = ".".join(item.variable_selector)
key = f"{node_id}.#{selector_str}#"
mapping[key] = item.variable_selector
def _source_mapping_from_item(mapping: MutableMapping[str, Sequence[str]], node_id: str, item: VariableOperationItem):
# Keep this in sync with the logic in _run methods...
if item.input_type != InputType.VARIABLE:
return
selector = item.value
if not isinstance(selector, list):
raise InvalidDataError(f"selector is not a list, {node_id=}, {item=}")
if len(selector) < MIN_SELECTORS_LENGTH:
raise InvalidDataError(f"selector too short, {node_id=}, {item=}")
selector_str = ".".join(selector)
key = f"{node_id}.#{selector_str}#"
mapping[key] = selector
class VariableAssignerNode(BaseNode[VariableAssignerNodeData]): class VariableAssignerNode(BaseNode[VariableAssignerNodeData]):
_node_data_cls = VariableAssignerNodeData _node_data_cls = VariableAssignerNodeData
_node_type = NodeType.VARIABLE_ASSIGNER _node_type = NodeType.VARIABLE_ASSIGNER
@ -49,12 +74,8 @@ class VariableAssignerNode(BaseNode[VariableAssignerNodeData]):
) -> Mapping[str, Sequence[str]]: ) -> Mapping[str, Sequence[str]]:
var_mapping: dict[str, Sequence[str]] = {} var_mapping: dict[str, Sequence[str]] = {}
for item in node_data.items: for item in node_data.items:
selector_node_id = item.variable_selector[0] _target_mapping_from_item(var_mapping, node_id, item)
if selector_node_id != CONVERSATION_VARIABLE_NODE_ID: _source_mapping_from_item(var_mapping, node_id, item)
continue
selector_str = ".".join(item.variable_selector)
key = f"{node_id}.#{selector_str}#"
var_mapping[key] = item.variable_selector
return var_mapping return var_mapping
def _run(self) -> NodeRunResult: def _run(self) -> NodeRunResult:

@ -703,11 +703,11 @@ class DraftVariableSaver:
Raw variable value if found, None otherwise Raw variable value if found, None otherwise
""" """
outputs_dict = node_exec.outputs_dict or {} outputs_dict = node_exec.outputs_dict or {}
process_data_dict = node_exec.process_data_dict or {}
# Note: Based on the implementation in `_build_from_variable_assigner_mapping`, # Note: Based on the implementation in `_build_from_variable_assigner_mapping`,
# VariableAssignerNode (both v1 and v2) can only create conversation draft variables. # VariableAssignerNode (both v1 and v2) can only create conversation draft variables.
# For consistency, we should simply return when processing VARIABLE_ASSIGNER nodes. # For consistency, we should simply return when processing VARIABLE_ASSIGNER nodes.
#
# This implementation must remain synchronized with the `_build_from_variable_assigner_mapping` # This implementation must remain synchronized with the `_build_from_variable_assigner_mapping`
# and `save` methods. # and `save` methods.

Loading…
Cancel
Save