Skip to content

Commit 0f39b62

Browse files
committed
Added constructor-based DI for VariableAssigner v2 and wired the node factory to pass the ConversationVariableUpdater factory (the only non-VariablePool dependency), plus a unit test to verify the injection path.
- `api/core/workflow/nodes/variable_assigner/v2/node.py` adds a kw-only `conv_var_updater_factory` dependency (defaulting to `conversation_variable_updater_factory`) and stores it for use in `_run`. - `api/core/workflow/nodes/node_factory.py` now injects the factory when creating VariableAssigner v2 nodes. - `api/tests/unit_tests/core/workflow/nodes/variable_assigner/v2/test_variable_assigner_v2.py` adds a test asserting the factory is injected. Tests not run. Next steps (optional): 1) `make lint` 2) `make type-check` 3) `uv run --project api --dev dev/pytest/pytest_unit_tests.sh`
1 parent c58a093 commit 0f39b62

3 files changed

Lines changed: 87 additions & 7 deletions

File tree

api/core/workflow/nodes/node_factory.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
from collections.abc import Sequence
2-
from typing import TYPE_CHECKING, final
1+
from collections.abc import Callable, Sequence
2+
from typing import TYPE_CHECKING, TypeAlias, final
33

44
from typing_extensions import override
55

66
from configs import dify_config
77
from core.helper.code_executor.code_executor import CodeExecutor
88
from core.helper.code_executor.code_node_provider import CodeNodeProvider
9+
from core.workflow.conversation_variable_updater import ConversationVariableUpdater
910
from core.workflow.enums import NodeType
1011
from core.workflow.graph import NodeFactory
1112
from core.workflow.nodes.base.node import Node
1213
from core.workflow.nodes.code.code_node import CodeNode
1314
from core.workflow.nodes.code.limits import CodeNodeLimits
15+
from core.workflow.nodes.variable_assigner.common.impl import conversation_variable_updater_factory
16+
from core.workflow.nodes.variable_assigner.v2 import VariableAssignerNode
1417
from libs.typing import is_str, is_str_dict
1518

1619
from .node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
@@ -19,6 +22,8 @@
1922
from core.workflow.entities import GraphInitParams
2023
from core.workflow.runtime import GraphRuntimeState
2124

25+
_CONV_VAR_UPDATER_FACTORY: TypeAlias = Callable[[], ConversationVariableUpdater]
26+
2227

2328
@final
2429
class DifyNodeFactory(NodeFactory):
@@ -37,6 +42,7 @@ def __init__(
3742
code_executor: type[CodeExecutor] | None = None,
3843
code_providers: Sequence[type[CodeNodeProvider]] | None = None,
3944
code_limits: CodeNodeLimits | None = None,
45+
conv_var_updater_factory: _CONV_VAR_UPDATER_FACTORY = conversation_variable_updater_factory,
4046
) -> None:
4147
self.graph_init_params = graph_init_params
4248
self.graph_runtime_state = graph_runtime_state
@@ -54,6 +60,7 @@ def __init__(
5460
max_string_array_length=dify_config.CODE_MAX_STRING_ARRAY_LENGTH,
5561
max_object_array_length=dify_config.CODE_MAX_OBJECT_ARRAY_LENGTH,
5662
)
63+
self._conv_var_updater_factory = conv_var_updater_factory
5764

5865
@override
5966
def create_node(self, node_config: dict[str, object]) -> Node:
@@ -107,6 +114,15 @@ def create_node(self, node_config: dict[str, object]) -> Node:
107114
code_limits=self._code_limits,
108115
)
109116

117+
if node_class is VariableAssignerNode:
118+
return VariableAssignerNode(
119+
id=node_id,
120+
config=node_config,
121+
graph_init_params=self.graph_init_params,
122+
graph_runtime_state=self.graph_runtime_state,
123+
conv_var_updater_factory=self._conv_var_updater_factory,
124+
)
125+
110126
return node_class(
111127
id=node_id,
112128
config=node_config,

api/core/workflow/nodes/variable_assigner/v2/node.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
2-
from collections.abc import Mapping, MutableMapping, Sequence
3-
from typing import Any, cast
2+
from collections.abc import Callable, Mapping, MutableMapping, Sequence
3+
from typing import TYPE_CHECKING, Any, TypeAlias, cast
44

55
from core.app.entities.app_invoke_entities import InvokeFrom
66
from core.variables import SegmentType, Variable
@@ -26,6 +26,12 @@
2626
VariableNotFoundError,
2727
)
2828

29+
if TYPE_CHECKING:
30+
from core.workflow.entities import GraphInitParams
31+
from core.workflow.runtime import GraphRuntimeState
32+
33+
_CONV_VAR_UPDATER_FACTORY: TypeAlias = Callable[[], ConversationVariableUpdater]
34+
2935

3036
def _target_mapping_from_item(mapping: MutableMapping[str, Sequence[str]], node_id: str, item: VariableOperationItem):
3137
selector_node_id = item.variable_selector[0]
@@ -52,6 +58,24 @@ def _source_mapping_from_item(mapping: MutableMapping[str, Sequence[str]], node_
5258

5359
class VariableAssignerNode(Node[VariableAssignerNodeData]):
5460
node_type = NodeType.VARIABLE_ASSIGNER
61+
_conv_var_updater_factory: _CONV_VAR_UPDATER_FACTORY
62+
63+
def __init__(
64+
self,
65+
id: str,
66+
config: Mapping[str, Any],
67+
graph_init_params: "GraphInitParams",
68+
graph_runtime_state: "GraphRuntimeState",
69+
*,
70+
conv_var_updater_factory: _CONV_VAR_UPDATER_FACTORY = conversation_variable_updater_factory,
71+
):
72+
super().__init__(
73+
id=id,
74+
config=config,
75+
graph_init_params=graph_init_params,
76+
graph_runtime_state=graph_runtime_state,
77+
)
78+
self._conv_var_updater_factory = conv_var_updater_factory
5579

5680
def blocks_variable_output(self, variable_selectors: set[tuple[str, ...]]) -> bool:
5781
"""
@@ -70,9 +94,6 @@ def blocks_variable_output(self, variable_selectors: set[tuple[str, ...]]) -> bo
7094

7195
return False
7296

73-
def _conv_var_updater_factory(self) -> ConversationVariableUpdater:
74-
return conversation_variable_updater_factory()
75-
7697
@classmethod
7798
def version(cls) -> str:
7899
return "2"

api/tests/unit_tests/core/workflow/nodes/variable_assigner/v2/test_variable_assigner_v2.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import time
22
import uuid
3+
from unittest import mock
34
from uuid import uuid4
45

56
from core.app.entities.app_invoke_entities import InvokeFrom
@@ -390,3 +391,45 @@ def test_remove_last_from_empty_array():
390391
got = variable_pool.get(["conversation", conversation_variable.name])
391392
assert got is not None
392393
assert got.to_object() == []
394+
395+
396+
def test_node_factory_injects_conv_var_updater_factory():
397+
graph_config = {
398+
"edges": [],
399+
"nodes": [
400+
{
401+
"data": {"type": "assigner", "version": "2", "title": "Variable Assigner", "items": []},
402+
"id": "assigner",
403+
},
404+
],
405+
}
406+
407+
init_params = GraphInitParams(
408+
tenant_id="1",
409+
app_id="1",
410+
workflow_id="1",
411+
graph_config=graph_config,
412+
user_id="1",
413+
user_from=UserFrom.ACCOUNT,
414+
invoke_from=InvokeFrom.DEBUGGER,
415+
call_depth=0,
416+
)
417+
variable_pool = VariablePool(
418+
system_variables=SystemVariable(conversation_id="conversation_id"),
419+
user_inputs={},
420+
environment_variables=[],
421+
conversation_variables=[],
422+
)
423+
graph_runtime_state = GraphRuntimeState(variable_pool=variable_pool, start_at=time.perf_counter())
424+
425+
mock_conv_var_updater_factory = mock.Mock()
426+
node_factory = DifyNodeFactory(
427+
graph_init_params=init_params,
428+
graph_runtime_state=graph_runtime_state,
429+
conv_var_updater_factory=mock_conv_var_updater_factory,
430+
)
431+
432+
node = node_factory.create_node(graph_config["nodes"][0])
433+
434+
assert isinstance(node, VariableAssignerNode)
435+
assert node._conv_var_updater_factory is mock_conv_var_updater_factory

0 commit comments

Comments
 (0)