refactor(api): move workflow knowledge nodes and trigger nodes (#33445)

This commit is contained in:
-LAN-
2026-03-15 15:24:59 +08:00
committed by GitHub
parent 1b6e695520
commit fb41b215c8
232 changed files with 1575 additions and 1421 deletions

View File

@ -42,7 +42,7 @@ from core.app.entities.task_entities import (
PingStreamResponse,
)
from core.base.tts.app_generator_tts_publisher import AudioTrunk
from dify_graph.enums import NodeType
from dify_graph.enums import BuiltinNodeTypes
from dify_graph.runtime import GraphRuntimeState, VariablePool
from dify_graph.system_variable import SystemVariable
from models.enums import MessageStatus
@ -226,7 +226,7 @@ class TestAdvancedChatGenerateTaskPipeline:
pipeline._save_output_for_event = lambda event, node_execution_id: None
event = SimpleNamespace(
node_type=NodeType.ANSWER,
node_type=BuiltinNodeTypes.ANSWER,
outputs={"k": "v"},
node_execution_id="exec",
node_id="node",
@ -254,7 +254,7 @@ class TestAdvancedChatGenerateTaskPipeline:
iter_start = QueueIterationStartEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
start_at=datetime.utcnow(),
node_run_index=1,
@ -263,14 +263,14 @@ class TestAdvancedChatGenerateTaskPipeline:
index=1,
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
node_run_index=1,
)
iter_done = QueueIterationCompletedEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
start_at=datetime.utcnow(),
node_run_index=1,
@ -278,7 +278,7 @@ class TestAdvancedChatGenerateTaskPipeline:
loop_start = QueueLoopStartEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
start_at=datetime.utcnow(),
node_run_index=1,
@ -287,14 +287,14 @@ class TestAdvancedChatGenerateTaskPipeline:
index=1,
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
node_run_index=1,
)
loop_done = QueueLoopCompletedEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
start_at=datetime.utcnow(),
node_run_index=1,
@ -358,7 +358,7 @@ class TestAdvancedChatGenerateTaskPipeline:
failed_event = QueueNodeFailedEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=datetime.utcnow(),
inputs={},
outputs={},
@ -368,7 +368,7 @@ class TestAdvancedChatGenerateTaskPipeline:
exc_event = QueueNodeExceptionEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=datetime.utcnow(),
inputs={},
outputs={},
@ -462,7 +462,7 @@ class TestAdvancedChatGenerateTaskPipeline:
filled_event = QueueHumanInputFormFilledEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="title",
rendered_content="content",
action_id="action",
@ -470,7 +470,7 @@ class TestAdvancedChatGenerateTaskPipeline:
)
timeout_event = QueueHumanInputFormTimeoutEvent(
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="title",
expiration_time=datetime.utcnow(),
)
@ -589,7 +589,7 @@ class TestAdvancedChatGenerateTaskPipeline:
event = QueueNodeExceptionEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=datetime.utcnow(),
inputs={},
outputs={},

View File

@ -24,7 +24,7 @@ from core.app.entities.queue_entities import (
QueueNodeSucceededEvent,
)
from dify_graph.entities.workflow_start_reason import WorkflowStartReason
from dify_graph.enums import NodeType
from dify_graph.enums import BuiltinNodeTypes
from dify_graph.system_variable import SystemVariable
from libs.datetime_utils import naive_utc_now
from models import Account
@ -66,7 +66,7 @@ class TestWorkflowResponseConverter:
node_execution_id=node_execution_id or str(uuid.uuid4()),
node_id="test-node-id",
node_title="Test Node",
node_type=NodeType.CODE,
node_type=BuiltinNodeTypes.CODE,
start_at=naive_utc_now(),
in_iteration_id=None,
in_loop_id=None,
@ -83,7 +83,7 @@ class TestWorkflowResponseConverter:
"""Create a QueueNodeSucceededEvent for testing."""
return QueueNodeSucceededEvent(
node_id="test-node-id",
node_type=NodeType.CODE,
node_type=BuiltinNodeTypes.CODE,
node_execution_id=node_execution_id,
start_at=naive_utc_now(),
in_iteration_id=None,
@ -108,7 +108,7 @@ class TestWorkflowResponseConverter:
error="oops",
retry_index=1,
node_id="test-node-id",
node_type=NodeType.CODE,
node_type=BuiltinNodeTypes.CODE,
node_title="test code",
provider_type="built-in",
provider_id="code",
@ -319,7 +319,7 @@ class TestWorkflowResponseConverter:
iteration_event = QueueNodeSucceededEvent(
node_id="iteration-node",
node_type=NodeType.ITERATION,
node_type=BuiltinNodeTypes.ITERATION,
node_execution_id=str(uuid.uuid4()),
start_at=naive_utc_now(),
in_iteration_id=None,
@ -336,7 +336,7 @@ class TestWorkflowResponseConverter:
)
assert response is None
loop_event = iteration_event.model_copy(update={"node_type": NodeType.LOOP})
loop_event = iteration_event.model_copy(update={"node_type": BuiltinNodeTypes.LOOP})
response = converter.workflow_node_finish_to_stream_response(
event=loop_event,
task_id="test-task-id",
@ -478,7 +478,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
event = QueueNodeSucceededEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=naive_utc_now(),
inputs=large_value,
process_data=large_value,
@ -523,7 +523,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
event = QueueNodeSucceededEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=naive_utc_now(),
inputs=large_value,
process_data=large_value,
@ -562,7 +562,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
event = QueueNodeSucceededEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=naive_utc_now(),
inputs=large_value,
process_data=large_value,
@ -600,7 +600,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
return QueueNodeSucceededEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=naive_utc_now(),
inputs=inputs,
process_data=process_data,
@ -614,7 +614,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
return QueueNodeFailedEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=naive_utc_now(),
inputs=inputs,
process_data=process_data,
@ -628,7 +628,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
return QueueNodeExceptionEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=naive_utc_now(),
inputs=inputs,
process_data=process_data,
@ -690,7 +690,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
start_event = QueueNodeStartedEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="Test Node",
node_run_index=1,
start_at=naive_utc_now(),
@ -706,7 +706,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
event = QueueNodeRetryEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="Test Node",
node_run_index=1,
start_at=naive_utc_now(),
@ -748,7 +748,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
start_event = QueueIterationStartEvent(
node_execution_id="test_iter_exec_id",
node_id="test_iteration",
node_type=NodeType.ITERATION,
node_type=BuiltinNodeTypes.ITERATION,
node_title="Test Iteration",
node_run_index=0,
start_at=naive_utc_now(),
@ -776,7 +776,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
start_event = QueueLoopStartEvent(
node_execution_id="test_loop_exec_id",
node_id="test_loop",
node_type=NodeType.LOOP,
node_type=BuiltinNodeTypes.LOOP,
node_title="Test Loop",
start_at=naive_utc_now(),
inputs=large_inputs,
@ -806,7 +806,7 @@ class TestWorkflowResponseConverterServiceApiTruncation:
event = QueueNodeSucceededEvent(
node_execution_id="test_node_exec_id",
node_id="test_node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
start_at=naive_utc_now(),
inputs=large_inputs,
process_data=large_process_data,

View File

@ -479,7 +479,7 @@ class TestBaseAppGeneratorExtras:
def test_get_draft_var_saver_factory_debugger(self):
from core.app.entities.app_invoke_entities import InvokeFrom
from dify_graph.enums import NodeType
from dify_graph.enums import BuiltinNodeTypes
from models import Account
base_app_generator = BaseAppGenerator()
@ -492,7 +492,7 @@ class TestBaseAppGeneratorExtras:
session=MagicMock(),
app_id="app-id",
node_id="node-id",
node_type=NodeType.START,
node_type=BuiltinNodeTypes.START,
node_execution_id="node-exec-id",
)

View File

@ -12,7 +12,7 @@ from dify_graph.entities.base_node_data import BaseNodeData, RetryConfig
from dify_graph.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter
from dify_graph.entities.pause_reason import SchedulingPause
from dify_graph.entities.workflow_start_reason import WorkflowStartReason
from dify_graph.enums import NodeType, WorkflowNodeExecutionStatus
from dify_graph.enums import BuiltinNodeTypes, NodeType, WorkflowNodeExecutionStatus
from dify_graph.graph import Graph
from dify_graph.graph_engine import GraphEngine
from dify_graph.graph_engine.command_channels.in_memory_channel import InMemoryChannel
@ -44,12 +44,12 @@ if "core.ops.ops_trace_manager" not in sys.modules:
class _StubToolNodeData(BaseNodeData):
type: NodeType = NodeType.TOOL
type: NodeType = BuiltinNodeTypes.TOOL
pause_on: bool = False
class _StubToolNode(Node[_StubToolNodeData]):
node_type = NodeType.TOOL
node_type = BuiltinNodeTypes.TOOL
@classmethod
def version(cls) -> str:
@ -94,7 +94,7 @@ def _patch_tool_node(mocker):
def _patched_create_node(self, node_config: dict[str, object] | NodeConfigDict) -> Node:
typed_node_config = NodeConfigDictAdapter.validate_python(node_config)
node_data = typed_node_config["data"]
if node_data.type == NodeType.TOOL:
if node_data.type == BuiltinNodeTypes.TOOL:
return _StubToolNode(
id=str(typed_node_config["id"]),
config=typed_node_config,
@ -108,7 +108,7 @@ def _patch_tool_node(mocker):
def _node_data(node_type: NodeType, data: BaseNodeData) -> dict[str, object]:
node_data = data.model_dump()
node_data["type"] = node_type.value
node_data["type"] = str(node_type)
return node_data
@ -124,11 +124,11 @@ def _build_graph_config(*, pause_on: str | None) -> dict[str, object]:
)
nodes = [
{"id": "start", "data": _node_data(NodeType.START, start_data)},
{"id": "tool_a", "data": _node_data(NodeType.TOOL, tool_data_a)},
{"id": "tool_b", "data": _node_data(NodeType.TOOL, tool_data_b)},
{"id": "tool_c", "data": _node_data(NodeType.TOOL, tool_data_c)},
{"id": "end", "data": _node_data(NodeType.END, end_data)},
{"id": "start", "data": _node_data(BuiltinNodeTypes.START, start_data)},
{"id": "tool_a", "data": _node_data(BuiltinNodeTypes.TOOL, tool_data_a)},
{"id": "tool_b", "data": _node_data(BuiltinNodeTypes.TOOL, tool_data_b)},
{"id": "tool_c", "data": _node_data(BuiltinNodeTypes.TOOL, tool_data_c)},
{"id": "end", "data": _node_data(BuiltinNodeTypes.END, end_data)},
]
edges = [
{"source": "start", "target": "tool_a"},
@ -157,7 +157,7 @@ def _build_graph(runtime_state: GraphRuntimeState, *, pause_on: str | None) -> G
graph_runtime_state=runtime_state,
)
return Graph.init(graph_config=graph_config, node_factory=node_factory)
return Graph.init(graph_config=graph_config, node_factory=node_factory, root_node_id="start")
def _build_runtime_state(run_id: str) -> GraphRuntimeState:

View File

@ -17,7 +17,7 @@ from core.app.entities.queue_entities import (
QueueWorkflowSucceededEvent,
)
from dify_graph.entities.pause_reason import HumanInputRequired
from dify_graph.enums import NodeType
from dify_graph.enums import BuiltinNodeTypes
from dify_graph.graph_events import (
GraphRunPausedEvent,
GraphRunStartedEvent,
@ -193,7 +193,7 @@ class TestWorkflowBasedAppRunner:
NodeRunStartedEvent(
id="exec",
node_id="node",
node_type=NodeType.START,
node_type=BuiltinNodeTypes.START,
node_title="Start",
start_at=datetime.utcnow(),
),
@ -203,7 +203,7 @@ class TestWorkflowBasedAppRunner:
NodeRunStreamChunkEvent(
id="exec",
node_id="node",
node_type=NodeType.START,
node_type=BuiltinNodeTypes.START,
selector=["node", "text"],
chunk="hi",
is_final=False,
@ -214,7 +214,7 @@ class TestWorkflowBasedAppRunner:
NodeRunAgentLogEvent(
id="exec",
node_id="node",
node_type=NodeType.START,
node_type=BuiltinNodeTypes.START,
message_id="msg",
label="label",
node_execution_id="exec",
@ -230,7 +230,7 @@ class TestWorkflowBasedAppRunner:
NodeRunIterationSucceededEvent(
id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="Iter",
start_at=datetime.utcnow(),
inputs={},
@ -244,7 +244,7 @@ class TestWorkflowBasedAppRunner:
NodeRunLoopFailedEvent(
id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="Loop",
start_at=datetime.utcnow(),
inputs={},

View File

@ -44,7 +44,7 @@ from core.app.entities.task_entities import (
WorkflowStartStreamResponse,
)
from core.base.tts.app_generator_tts_publisher import AudioTrunk
from dify_graph.enums import NodeType, WorkflowExecutionStatus
from dify_graph.enums import BuiltinNodeTypes, WorkflowExecutionStatus
from dify_graph.runtime import GraphRuntimeState, VariablePool
from dify_graph.system_variable import SystemVariable
from models.enums import CreatorUserRole
@ -190,7 +190,7 @@ class TestWorkflowGenerateTaskPipeline:
event = QueueNodeSucceededEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.START,
node_type=BuiltinNodeTypes.START,
start_at=datetime.utcnow(),
inputs={},
outputs={},
@ -243,7 +243,7 @@ class TestWorkflowGenerateTaskPipeline:
event = QueueNodeFailedEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.START,
node_type=BuiltinNodeTypes.START,
start_at=datetime.utcnow(),
inputs={},
outputs={},
@ -300,7 +300,7 @@ class TestWorkflowGenerateTaskPipeline:
iter_start = QueueIterationStartEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
start_at=datetime.utcnow(),
node_run_index=1,
@ -309,14 +309,14 @@ class TestWorkflowGenerateTaskPipeline:
index=1,
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
node_run_index=1,
)
iter_done = QueueIterationCompletedEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
start_at=datetime.utcnow(),
node_run_index=1,
@ -324,7 +324,7 @@ class TestWorkflowGenerateTaskPipeline:
loop_start = QueueLoopStartEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
start_at=datetime.utcnow(),
node_run_index=1,
@ -333,14 +333,14 @@ class TestWorkflowGenerateTaskPipeline:
index=1,
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
node_run_index=1,
)
loop_done = QueueLoopCompletedEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="LLM",
start_at=datetime.utcnow(),
node_run_index=1,
@ -348,7 +348,7 @@ class TestWorkflowGenerateTaskPipeline:
filled_event = QueueHumanInputFormFilledEvent(
node_execution_id="exec",
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="title",
rendered_content="content",
action_id="action",
@ -356,7 +356,7 @@ class TestWorkflowGenerateTaskPipeline:
)
timeout_event = QueueHumanInputFormTimeoutEvent(
node_id="node",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_title="title",
expiration_time=datetime.utcnow(),
)
@ -645,7 +645,7 @@ class TestWorkflowGenerateTaskPipeline:
node_execution_id="exec",
node_id="node",
node_title="title",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_run_index=1,
start_at=datetime.utcnow(),
provider_type="provider",
@ -657,7 +657,7 @@ class TestWorkflowGenerateTaskPipeline:
node_execution_id="exec",
node_id="node",
node_title="title",
node_type=NodeType.LLM,
node_type=BuiltinNodeTypes.LLM,
node_run_index=1,
start_at=datetime.utcnow(),
provider_type="provider",
@ -683,7 +683,7 @@ class TestWorkflowGenerateTaskPipeline:
event = QueueNodeExceptionEvent(
node_execution_id="exec-id",
node_id="node",
node_type=NodeType.START,
node_type=BuiltinNodeTypes.START,
start_at=datetime.utcnow(),
inputs={},
outputs={},
@ -855,7 +855,7 @@ class TestWorkflowGenerateTaskPipeline:
event = QueueNodeSucceededEvent(
node_execution_id="exec-id",
node_id="node-id",
node_type=NodeType.START,
node_type=BuiltinNodeTypes.START,
in_loop_id="loop-id",
start_at=datetime.utcnow(),
process_data={"k": "v"},

View File

@ -4,7 +4,7 @@ from unittest.mock import Mock
from core.app.layers.conversation_variable_persist_layer import ConversationVariablePersistenceLayer
from dify_graph.constants import CONVERSATION_VARIABLE_NODE_ID
from dify_graph.enums import NodeType, WorkflowNodeExecutionStatus
from dify_graph.enums import BuiltinNodeTypes, NodeType, WorkflowNodeExecutionStatus
from dify_graph.graph_engine.protocols.command_channel import CommandChannel
from dify_graph.graph_events.node import NodeRunSucceededEvent
from dify_graph.node_events import NodeRunResult
@ -78,7 +78,7 @@ def test_persists_conversation_variables_from_assigner_output():
layer = ConversationVariablePersistenceLayer(updater)
layer.initialize(_build_graph_runtime_state(variable_pool, conversation_id), Mock(spec=CommandChannel))
event = _build_node_run_succeeded_event(node_type=NodeType.VARIABLE_ASSIGNER, process_data=process_data)
event = _build_node_run_succeeded_event(node_type=BuiltinNodeTypes.VARIABLE_ASSIGNER, process_data=process_data)
layer.on_event(event)
updater.update.assert_called_once_with(conversation_id=conversation_id, variable=variable)
@ -100,7 +100,7 @@ def test_skips_when_outputs_missing():
layer = ConversationVariablePersistenceLayer(updater)
layer.initialize(_build_graph_runtime_state(variable_pool, conversation_id), Mock(spec=CommandChannel))
event = _build_node_run_succeeded_event(node_type=NodeType.VARIABLE_ASSIGNER)
event = _build_node_run_succeeded_event(node_type=BuiltinNodeTypes.VARIABLE_ASSIGNER)
layer.on_event(event)
updater.update.assert_not_called()
@ -112,7 +112,7 @@ def test_skips_non_assigner_nodes():
layer = ConversationVariablePersistenceLayer(updater)
layer.initialize(_build_graph_runtime_state(MockReadOnlyVariablePool()), Mock(spec=CommandChannel))
event = _build_node_run_succeeded_event(node_type=NodeType.LLM)
event = _build_node_run_succeeded_event(node_type=BuiltinNodeTypes.LLM)
layer.on_event(event)
updater.update.assert_not_called()
@ -137,7 +137,7 @@ def test_skips_non_conversation_variables():
layer = ConversationVariablePersistenceLayer(updater)
layer.initialize(_build_graph_runtime_state(variable_pool, conversation_id), Mock(spec=CommandChannel))
event = _build_node_run_succeeded_event(node_type=NodeType.VARIABLE_ASSIGNER, process_data=process_data)
event = _build_node_run_succeeded_event(node_type=BuiltinNodeTypes.VARIABLE_ASSIGNER, process_data=process_data)
layer.on_event(event)
updater.update.assert_not_called()