Compare commits

...

6 Commits

Author SHA1 Message Date
-LAN-
a30a64d51b Scope tool configuration test patches 2026-03-14 19:29:05 +08:00
-LAN-
34ef10c818 Keep direct provider_id consumers unchanged 2026-03-14 19:14:30 +08:00
-LAN-
26fedca865 Keep trigger provider handling in the node 2026-03-14 19:14:30 +08:00
-LAN-
2fd4e9e259 Restore trigger provider metadata on start events 2026-03-14 19:14:30 +08:00
-LAN-
9e8a4c8a71 Keep dify_graph node base generic 2026-03-14 19:14:30 +08:00
-LAN-
238497b7ab Move trigger workflow nodes into core workflow 2026-03-14 19:14:29 +08:00
42 changed files with 214 additions and 82 deletions

View File

@@ -30,6 +30,7 @@ from core.app.entities.queue_entities import (
QueueWorkflowSucceededEvent,
)
from core.workflow.node_factory import DifyNodeFactory
from core.workflow.nodes.node_mapping import NODE_TYPE_CLASSES_MAPPING
from core.workflow.workflow_entry import WorkflowEntry
from dify_graph.entities import GraphInitParams
from dify_graph.entities.graph_config import NodeConfigDictAdapter
@@ -63,7 +64,6 @@ from dify_graph.graph_events import (
NodeRunSucceededEvent,
)
from dify_graph.graph_events.graph import GraphRunAbortedEvent
from dify_graph.nodes.node_mapping import NODE_TYPE_CLASSES_MAPPING
from dify_graph.runtime import GraphRuntimeState, VariablePool
from dify_graph.system_variable import SystemVariable
from dify_graph.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader, load_into_variable_pool

View File

@@ -316,7 +316,7 @@ class QueueNodeStartedEvent(AppQueueEvent):
start_at: datetime
agent_strategy: AgentNodeStrategyInit | None = None
# FIXME(-LAN-): only for ToolNode, need to refactor
# Legacy provider fields kept for existing start-event consumers.
provider_type: str # should be a core.tools.entities.tool_entities.ToolProviderType
provider_id: str

View File

@@ -19,10 +19,10 @@ from core.trigger.debug.events import (
build_plugin_pool_key,
build_webhook_pool_key,
)
from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData
from core.workflow.nodes.trigger_schedule.entities import ScheduleConfig
from dify_graph.entities.graph_config import NodeConfigDict
from dify_graph.enums import NodeType
from dify_graph.nodes.trigger_plugin.entities import TriggerEventNodeData
from dify_graph.nodes.trigger_schedule.entities import ScheduleConfig
from extensions.ext_redis import redis_client
from libs.datetime_utils import ensure_naive_utc, naive_utc_now
from libs.schedule_utils import calculate_next_run_at

View File

@@ -22,6 +22,7 @@ from core.rag.retrieval.dataset_retrieval import DatasetRetrieval
from core.rag.summary_index.summary_index import SummaryIndex
from core.repositories.human_input_repository import HumanInputFormRepositoryImpl
from core.tools.tool_file_manager import ToolFileManager
from core.workflow.nodes.node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
from dify_graph.entities.base_node_data import BaseNodeData
from dify_graph.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter
from dify_graph.entities.graph_init_params import DIFY_RUN_CONTEXT_KEY
@@ -39,7 +40,6 @@ from dify_graph.nodes.document_extractor import UnstructuredApiConfig
from dify_graph.nodes.http_request import build_http_request_config
from dify_graph.nodes.llm.entities import LLMNodeData
from dify_graph.nodes.llm.exc import LLMModeRequiredError, ModelNotExistError
from dify_graph.nodes.node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
from dify_graph.nodes.parameter_extractor.entities import ParameterExtractorNodeData
from dify_graph.nodes.question_classifier.entities import QuestionClassifierNodeData
from dify_graph.nodes.template_transform.template_renderer import (

View File

@@ -0,0 +1 @@
"""Core-owned workflow node packages."""

View File

@@ -0,0 +1,30 @@
"""Node mapping for workflow execution.
`core.workflow` owns the trigger node implementations, while the remaining node
implementations still live under `dify_graph`. This module imports the
core-owned node packages first, then asks the shared `Node` registry to load the
rest of the workflow nodes from `dify_graph`.
"""
import importlib
import pkgutil
from collections.abc import Mapping
from dify_graph.enums import NodeType
from dify_graph.nodes.base.node import Node
LATEST_VERSION = "latest"
def _register_core_workflow_nodes() -> None:
import core.workflow.nodes as workflow_nodes_pkg
for _, modname, _ in pkgutil.walk_packages(workflow_nodes_pkg.__path__, workflow_nodes_pkg.__name__ + "."):
if modname == "core.workflow.nodes.node_mapping":
continue
importlib.import_module(modname)
_register_core_workflow_nodes()
NODE_TYPE_CLASSES_MAPPING: Mapping[NodeType, Mapping[str, type[Node]]] = Node.get_node_type_classes_mapping()

View File

@@ -6,7 +6,8 @@ from pydantic import BaseModel, Field, ValidationInfo, field_validator
from core.trigger.entities.entities import EventParameter
from dify_graph.entities.base_node_data import BaseNodeData
from dify_graph.enums import NodeType
from dify_graph.nodes.trigger_plugin.exc import TriggerEventParameterError
from .exc import TriggerEventParameterError
class TriggerEventNodeData(BaseNodeData):

View File

@@ -3,6 +3,7 @@ from collections.abc import Mapping
from dify_graph.constants import SYSTEM_VARIABLE_NODE_ID
from dify_graph.entities.workflow_node_execution import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus
from dify_graph.enums import NodeExecutionType, NodeType
from dify_graph.graph_events import NodeRunStartedEvent
from dify_graph.node_events import NodeRunResult
from dify_graph.nodes.base.node import Node
@@ -32,6 +33,11 @@ class TriggerEventNode(Node[TriggerEventNodeData]):
def version(cls) -> str:
return "1"
def customize_start_event(self, event: NodeRunStartedEvent) -> None:
provider_id = self.node_data.provider_id
event.provider_id = provider_id
event.extras["provider_id"] = provider_id
def _run(self) -> NodeRunResult:
"""
Run the plugin trigger node.

View File

@@ -0,0 +1,3 @@
from .trigger_schedule_node import TriggerScheduleNode
__all__ = ["TriggerScheduleNode"]

View File

@@ -5,7 +5,8 @@ from dify_graph.entities.workflow_node_execution import WorkflowNodeExecutionSta
from dify_graph.enums import NodeExecutionType, NodeType
from dify_graph.node_events import NodeRunResult
from dify_graph.nodes.base.node import Node
from dify_graph.nodes.trigger_schedule.entities import TriggerScheduleNodeData
from .entities import TriggerScheduleNodeData
class TriggerScheduleNode(Node[TriggerScheduleNodeData]):

View File

@@ -9,6 +9,7 @@ from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom, build_di
from core.app.workflow.layers.llm_quota import LLMQuotaLayer
from core.app.workflow.layers.observability import ObservabilityLayer
from core.workflow.node_factory import DifyNodeFactory
from core.workflow.nodes.node_mapping import NODE_TYPE_CLASSES_MAPPING
from dify_graph.constants import ENVIRONMENT_VARIABLE_NODE_ID
from dify_graph.entities import GraphInitParams
from dify_graph.entities.graph_config import NodeConfigDictAdapter
@@ -23,7 +24,6 @@ from dify_graph.graph_engine.protocols.command_channel import CommandChannel
from dify_graph.graph_events import GraphEngineEvent, GraphNodeEventBase, GraphRunFailedEvent
from dify_graph.nodes import NodeType
from dify_graph.nodes.base.node import Node
from dify_graph.nodes.node_mapping import NODE_TYPE_CLASSES_MAPPING
from dify_graph.runtime import ChildGraphNotFoundError, GraphRuntimeState, VariablePool
from dify_graph.system_variable import SystemVariable
from dify_graph.variable_loader import DUMMY_VARIABLE_LOADER, VariableLoader, load_into_variable_pool

View File

@@ -15,8 +15,9 @@ class NodeRunStartedEvent(GraphNodeEventBase):
predecessor_node_id: str | None = None
agent_strategy: AgentNodeStrategyInit | None = None
start_at: datetime = Field(..., description="node start time")
extras: dict[str, object] = Field(default_factory=dict)
# FIXME(-LAN-): only for ToolNode
# Legacy provider fields kept for existing start-event consumers.
provider_type: str = ""
provider_id: str = ""

View File

@@ -179,7 +179,7 @@ class Node(Generic[NodeDataT]):
# Skip base class itself
if cls is Node:
return
# Only register production node implementations defined under dify_graph.nodes.*
# Only register production node implementations defined under dify_graph.nodes.*.
# This prevents test helper subclasses from polluting the global registry and
# accidentally overriding real node types (e.g., a test Answer node).
module_name = getattr(cls, "__module__", "")
@@ -273,6 +273,10 @@ class Node(Generic[NodeDataT]):
"""Optional hook for subclasses requiring extra initialization."""
return
def customize_start_event(self, event: NodeRunStartedEvent) -> None:
"""Optional hook for subclasses to attach start-event metadata or extras."""
return
@property
def graph_init_params(self) -> GraphInitParams:
return self._graph_init_params
@@ -379,12 +383,6 @@ class Node(Generic[NodeDataT]):
start_event.provider_id = f"{plugin_id}/{provider_name}"
start_event.provider_type = getattr(self.node_data, "provider_type", "")
from dify_graph.nodes.trigger_plugin.trigger_event_node import TriggerEventNode
if isinstance(self, TriggerEventNode):
start_event.provider_id = getattr(self.node_data, "provider_id", "")
start_event.provider_type = getattr(self.node_data, "provider_type", "")
from dify_graph.nodes.agent.agent_node import AgentNode
from dify_graph.nodes.agent.entities import AgentNodeData
@@ -394,6 +392,8 @@ class Node(Generic[NodeDataT]):
icon=self.agent_strategy_icon,
)
self.customize_start_event(start_event)
# ===
yield start_event
@@ -524,6 +524,7 @@ class Node(Generic[NodeDataT]):
"""Return mapping of NodeType -> {version -> Node subclass} using __init_subclass__ registry.
Import all modules under dify_graph.nodes so subclasses register themselves on import.
Higher-level packages may register additional nodes before calling this helper.
Then we return a readonly view of the registry to avoid accidental mutation.
"""
# Import all node modules to ensure they are loaded (thus registered)

View File

@@ -1,3 +0,0 @@
from dify_graph.nodes.trigger_schedule.trigger_schedule_node import TriggerScheduleNode
__all__ = ["TriggerScheduleNode"]

View File

@@ -4,7 +4,7 @@ from typing import cast
from sqlalchemy import select
from sqlalchemy.orm import Session
from dify_graph.nodes.trigger_schedule.entities import SchedulePlanUpdate
from core.workflow.nodes.trigger_schedule.entities import SchedulePlanUpdate
from events.app_event import app_published_workflow_was_updated
from extensions.ext_database import db
from models import AppMode, Workflow, WorkflowSchedulePlan

View File

@@ -20,6 +20,7 @@ from sqlalchemy.orm import Session
from configs import dify_config
from core.helper import ssrf_proxy
from core.plugin.entities.plugin import PluginDependency
from core.workflow.nodes.trigger_schedule.trigger_schedule_node import TriggerScheduleNode
from dify_graph.enums import NodeType
from dify_graph.model_runtime.utils.encoders import jsonable_encoder
from dify_graph.nodes.knowledge_retrieval.entities import KnowledgeRetrievalNodeData
@@ -27,7 +28,6 @@ from dify_graph.nodes.llm.entities import LLMNodeData
from dify_graph.nodes.parameter_extractor.entities import ParameterExtractorNodeData
from dify_graph.nodes.question_classifier.entities import QuestionClassifierNodeData
from dify_graph.nodes.tool.entities import ToolNodeData
from dify_graph.nodes.trigger_schedule.trigger_schedule_node import TriggerScheduleNode
from events.app_event import app_model_config_was_updated, app_was_created
from extensions.ext_redis import redis_client
from factories import variable_factory

View File

@@ -36,6 +36,7 @@ from core.rag.entities.event import (
)
from core.repositories.factory import DifyCoreRepositoryFactory
from core.repositories.sqlalchemy_workflow_node_execution_repository import SQLAlchemyWorkflowNodeExecutionRepository
from core.workflow.nodes.node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
from core.workflow.workflow_entry import WorkflowEntry
from dify_graph.entities.workflow_node_execution import (
WorkflowNodeExecution,
@@ -48,7 +49,6 @@ from dify_graph.graph_events.base import GraphNodeEventBase
from dify_graph.node_events.base import NodeRunResult
from dify_graph.nodes.base.node import Node
from dify_graph.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, build_http_request_config
from dify_graph.nodes.node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
from dify_graph.repositories.workflow_node_execution_repository import OrderConfig
from dify_graph.runtime import VariablePool
from dify_graph.system_variable import SystemVariable

View File

@@ -5,15 +5,15 @@ from datetime import datetime
from sqlalchemy import select
from sqlalchemy.orm import Session
from dify_graph.entities.graph_config import NodeConfigDict
from dify_graph.nodes import NodeType
from dify_graph.nodes.trigger_schedule.entities import (
from core.workflow.nodes.trigger_schedule.entities import (
ScheduleConfig,
SchedulePlanUpdate,
TriggerScheduleNodeData,
VisualConfig,
)
from dify_graph.nodes.trigger_schedule.exc import ScheduleConfigError, ScheduleNotFoundError
from core.workflow.nodes.trigger_schedule.exc import ScheduleConfigError, ScheduleNotFoundError
from dify_graph.entities.graph_config import NodeConfigDict
from dify_graph.nodes import NodeType
from libs.schedule_utils import calculate_next_run_at, convert_12h_to_24h
from models.account import Account, TenantAccountJoin
from models.trigger import WorkflowSchedulePlan

View File

@@ -16,9 +16,9 @@ from core.trigger.debug.events import PluginTriggerDebugEvent
from core.trigger.provider import PluginTriggerProviderController
from core.trigger.trigger_manager import TriggerManager
from core.trigger.utils.encryption import create_trigger_provider_encrypter_for_subscription
from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData
from dify_graph.entities.graph_config import NodeConfigDict
from dify_graph.enums import NodeType
from dify_graph.nodes.trigger_plugin.entities import TriggerEventNodeData
from extensions.ext_database import db
from extensions.ext_redis import redis_client
from models.model import App

View File

@@ -16,15 +16,15 @@ from werkzeug.exceptions import RequestEntityTooLarge
from configs import dify_config
from core.app.entities.app_invoke_entities import InvokeFrom
from core.tools.tool_file_manager import ToolFileManager
from dify_graph.entities.graph_config import NodeConfigDict
from dify_graph.enums import NodeType
from dify_graph.file.models import FileTransferMethod
from dify_graph.nodes.trigger_webhook.entities import (
from core.workflow.nodes.trigger_webhook.entities import (
ContentType,
WebhookBodyParameter,
WebhookData,
WebhookParameter,
)
from dify_graph.entities.graph_config import NodeConfigDict
from dify_graph.enums import NodeType
from dify_graph.file.models import FileTransferMethod
from dify_graph.variables.types import ArrayValidation, SegmentType
from enums.quota_type import QuotaType
from extensions.ext_database import db

View File

@@ -14,6 +14,7 @@ from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager
from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom, build_dify_run_context
from core.repositories import DifyCoreRepositoryFactory
from core.repositories.human_input_repository import HumanInputFormRepositoryImpl
from core.workflow.nodes.node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
from core.workflow.workflow_entry import WorkflowEntry
from dify_graph.entities import GraphInitParams, WorkflowNodeExecution
from dify_graph.entities.graph_config import NodeConfigDict
@@ -34,7 +35,6 @@ from dify_graph.nodes.human_input.entities import (
)
from dify_graph.nodes.human_input.enums import HumanInputFormKind
from dify_graph.nodes.human_input.human_input_node import HumanInputNode
from dify_graph.nodes.node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
from dify_graph.nodes.start.entities import StartNodeData
from dify_graph.repositories.human_input_form_repository import FormCreateParams
from dify_graph.runtime import GraphRuntimeState, VariablePool

View File

@@ -25,8 +25,8 @@ from core.trigger.debug.events import PluginTriggerDebugEvent, build_plugin_pool
from core.trigger.entities.entities import TriggerProviderEntity
from core.trigger.provider import PluginTriggerProviderController
from core.trigger.trigger_manager import TriggerManager
from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData
from dify_graph.enums import NodeType, WorkflowExecutionStatus
from dify_graph.nodes.trigger_plugin.entities import TriggerEventNodeData
from enums.quota_type import QuotaType, unlimited
from models.enums import (
AppTriggerType,

View File

@@ -3,7 +3,7 @@ import logging
from celery import shared_task
from core.db.session_factory import session_factory
from dify_graph.nodes.trigger_schedule.exc import (
from core.workflow.nodes.trigger_schedule.exc import (
ScheduleExecutionError,
ScheduleNotFoundError,
TenantOwnerNotFoundError,

View File

@@ -1,6 +1,6 @@
import time
import uuid
from unittest.mock import MagicMock
from unittest.mock import MagicMock, patch
from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom
from core.tools.utils.configuration import ToolParameterConfigurationManager
@@ -87,17 +87,20 @@ def test_tool_variable_invoke():
}
)
ToolParameterConfigurationManager.decrypt_tool_parameters = MagicMock(return_value={"format": "%Y-%m-%d %H:%M:%S"})
node.graph_runtime_state.variable_pool.add(["1", "args1"], "1+1")
# execute node
result = node._run()
for item in result:
if isinstance(item, StreamCompletedEvent):
assert item.node_run_result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert item.node_run_result.outputs is not None
assert item.node_run_result.outputs.get("text") is not None
with patch.object(
ToolParameterConfigurationManager,
"decrypt_tool_parameters",
return_value={"format": "%Y-%m-%d %H:%M:%S"},
):
# execute node
result = node._run()
for item in result:
if isinstance(item, StreamCompletedEvent):
assert item.node_run_result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert item.node_run_result.outputs is not None
assert item.node_run_result.outputs.get("text") is not None
def test_tool_mixed_invoke():
@@ -121,12 +124,15 @@ def test_tool_mixed_invoke():
}
)
ToolParameterConfigurationManager.decrypt_tool_parameters = MagicMock(return_value={"format": "%Y-%m-%d %H:%M:%S"})
# execute node
result = node._run()
for item in result:
if isinstance(item, StreamCompletedEvent):
assert item.node_run_result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert item.node_run_result.outputs is not None
assert item.node_run_result.outputs.get("text") is not None
with patch.object(
ToolParameterConfigurationManager,
"decrypt_tool_parameters",
return_value={"format": "%Y-%m-%d %H:%M:%S"},
):
# execute node
result = node._run()
for item in result:
if isinstance(item, StreamCompletedEvent):
assert item.node_run_result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert item.node_run_result.outputs is not None
assert item.node_run_result.outputs.get("text") is not None

View File

@@ -6,7 +6,7 @@ import uuid
from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any
from unittest.mock import Mock
from unittest.mock import Mock, patch
import pytest
@@ -738,6 +738,30 @@ class TestWorkflowResponseConverterServiceApiTruncation:
assert not response.data.process_data_truncated
assert not response.data.outputs_truncated
def test_trigger_plugin_start_event_uses_provider_id_for_icon(self):
converter = self.create_test_converter(InvokeFrom.WEB_APP)
event = QueueNodeStartedEvent(
node_execution_id=str(uuid.uuid4()),
node_id="trigger-node",
node_title="Trigger Node",
node_type=NodeType.TRIGGER_PLUGIN,
start_at=naive_utc_now(),
in_iteration_id=None,
in_loop_id=None,
provider_type="",
provider_id="provider-1",
)
with patch(
"core.app.apps.common.workflow_response_converter.TriggerManager.get_trigger_plugin_icon",
return_value="https://example.com/icon.png",
) as get_trigger_plugin_icon:
response = converter.workflow_node_start_to_stream_response(event=event, task_id="task-1")
assert response is not None
assert response.data.extras["icon"] == "https://example.com/icon.png"
get_trigger_plugin_icon.assert_called_once_with("test_tenant", "provider-1")
def test_service_api_iteration_events_no_truncation(self):
"""Test that Service API doesn't truncate iteration events."""
converter = self.create_test_converter(InvokeFrom.SERVICE_API)

View File

@@ -6,7 +6,7 @@ import pytest
import pytz
from core.trigger.debug import event_selectors
from dify_graph.nodes.trigger_schedule.entities import ScheduleConfig
from core.workflow.nodes.trigger_schedule.entities import ScheduleConfig
class _DummyRedis:

View File

@@ -1,12 +1,11 @@
import pytest
# Ensures that all node classes are imported.
from core.workflow.nodes.node_mapping import NODE_TYPE_CLASSES_MAPPING
from dify_graph.entities.base_node_data import BaseNodeData
from dify_graph.enums import NodeType
from dify_graph.nodes.base.node import Node
# Ensures that all node classes are imported.
from dify_graph.nodes.node_mapping import NODE_TYPE_CLASSES_MAPPING
# Ensure `NODE_TYPE_CLASSES_MAPPING` is used and not automatically removed.
_ = NODE_TYPE_CLASSES_MAPPING

View File

@@ -0,0 +1,62 @@
from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom
from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData
from core.workflow.nodes.trigger_plugin.trigger_event_node import TriggerEventNode
from dify_graph.entities.graph_init_params import DIFY_RUN_CONTEXT_KEY, GraphInitParams
from dify_graph.graph_events import NodeRunStartedEvent
from dify_graph.runtime.graph_runtime_state import GraphRuntimeState
from dify_graph.runtime.variable_pool import VariablePool
from dify_graph.system_variable import SystemVariable
def create_trigger_event_node(node_data: TriggerEventNodeData) -> TriggerEventNode:
node_config = {
"id": "trigger-node",
"data": node_data.model_dump(),
}
graph_init_params = GraphInitParams(
workflow_id="workflow-1",
graph_config={},
run_context={
DIFY_RUN_CONTEXT_KEY: {
"tenant_id": "tenant-1",
"app_id": "app-1",
"user_id": "user-1",
"user_from": UserFrom.ACCOUNT,
"invoke_from": InvokeFrom.SERVICE_API,
}
},
call_depth=0,
)
runtime_state = GraphRuntimeState(
variable_pool=VariablePool(
system_variables=SystemVariable.default(),
user_inputs={},
),
start_at=0,
)
return TriggerEventNode(
id="trigger-node",
config=node_config,
graph_init_params=graph_init_params,
graph_runtime_state=runtime_state,
)
def test_trigger_event_start_event_carries_provider_metadata() -> None:
node = create_trigger_event_node(
TriggerEventNodeData(
title="Plugin Trigger",
provider_id="provider-1",
plugin_id="plugin-1",
event_name="event.created",
subscription_id="subscription-1",
plugin_unique_identifier="plugin/provider",
event_parameters={},
)
)
start_event = next(node.run())
assert isinstance(start_event, NodeRunStartedEvent)
assert start_event.provider_id == "provider-1"
assert start_event.extras == {"provider_id": "provider-1"}

View File

@@ -1,7 +1,7 @@
import pytest
from pydantic import ValidationError
from dify_graph.nodes.trigger_webhook.entities import (
from core.workflow.nodes.trigger_webhook.entities import (
ContentType,
Method,
WebhookBodyParameter,

View File

@@ -1,12 +1,12 @@
import pytest
from dify_graph.entities.exc import BaseNodeError
from dify_graph.nodes.trigger_webhook.exc import (
from core.workflow.nodes.trigger_webhook.exc import (
WebhookConfigError,
WebhookNodeError,
WebhookNotFoundError,
WebhookTimeoutError,
)
from dify_graph.entities.exc import BaseNodeError
def test_webhook_node_error_inheritance():
@@ -149,7 +149,7 @@ def test_webhook_error_attributes():
assert WebhookConfigError.__name__ == "WebhookConfigError"
# Test that all error classes have proper __module__
expected_module = "dify_graph.nodes.trigger_webhook.exc"
expected_module = "core.workflow.nodes.trigger_webhook.exc"
assert WebhookNodeError.__module__ == expected_module
assert WebhookTimeoutError.__module__ == expected_module
assert WebhookNotFoundError.__module__ == expected_module

View File

@@ -9,15 +9,15 @@ when passing files to downstream LLM nodes.
from unittest.mock import Mock, patch
from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom
from dify_graph.entities.graph_init_params import DIFY_RUN_CONTEXT_KEY, GraphInitParams
from dify_graph.entities.workflow_node_execution import WorkflowNodeExecutionStatus
from dify_graph.nodes.trigger_webhook.entities import (
from core.workflow.nodes.trigger_webhook.entities import (
ContentType,
Method,
WebhookBodyParameter,
WebhookData,
)
from dify_graph.nodes.trigger_webhook.node import TriggerWebhookNode
from core.workflow.nodes.trigger_webhook.node import TriggerWebhookNode
from dify_graph.entities.graph_init_params import DIFY_RUN_CONTEXT_KEY, GraphInitParams
from dify_graph.entities.workflow_node_execution import WorkflowNodeExecutionStatus
from dify_graph.runtime.graph_runtime_state import GraphRuntimeState
from dify_graph.runtime.variable_pool import VariablePool
from dify_graph.system_variable import SystemVariable
@@ -130,8 +130,8 @@ def test_webhook_node_file_conversion_to_file_variable():
# Mock the file factory and variable factory
with (
patch("factories.file_factory.build_from_mapping") as mock_file_factory,
patch("dify_graph.nodes.trigger_webhook.node.build_segment_with_type") as mock_segment_factory,
patch("dify_graph.nodes.trigger_webhook.node.FileVariable") as mock_file_variable,
patch("core.workflow.nodes.trigger_webhook.node.build_segment_with_type") as mock_segment_factory,
patch("core.workflow.nodes.trigger_webhook.node.FileVariable") as mock_file_variable,
):
# Setup mocks
mock_file_obj = Mock()
@@ -322,8 +322,8 @@ def test_webhook_node_file_conversion_mixed_parameters():
with (
patch("factories.file_factory.build_from_mapping") as mock_file_factory,
patch("dify_graph.nodes.trigger_webhook.node.build_segment_with_type") as mock_segment_factory,
patch("dify_graph.nodes.trigger_webhook.node.FileVariable") as mock_file_variable,
patch("core.workflow.nodes.trigger_webhook.node.build_segment_with_type") as mock_segment_factory,
patch("core.workflow.nodes.trigger_webhook.node.FileVariable") as mock_file_variable,
):
# Setup mocks for file
mock_file_obj = Mock()
@@ -390,8 +390,8 @@ def test_webhook_node_different_file_types():
with (
patch("factories.file_factory.build_from_mapping") as mock_file_factory,
patch("dify_graph.nodes.trigger_webhook.node.build_segment_with_type") as mock_segment_factory,
patch("dify_graph.nodes.trigger_webhook.node.FileVariable") as mock_file_variable,
patch("core.workflow.nodes.trigger_webhook.node.build_segment_with_type") as mock_segment_factory,
patch("core.workflow.nodes.trigger_webhook.node.FileVariable") as mock_file_variable,
):
# Setup mocks for all files
mock_file_objs = [Mock() for _ in range(3)]

View File

@@ -3,17 +3,17 @@ from unittest.mock import patch
import pytest
from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom
from dify_graph.entities.graph_init_params import DIFY_RUN_CONTEXT_KEY, GraphInitParams
from dify_graph.entities.workflow_node_execution import WorkflowNodeExecutionStatus
from dify_graph.file import File, FileTransferMethod, FileType
from dify_graph.nodes.trigger_webhook.entities import (
from core.workflow.nodes.trigger_webhook.entities import (
ContentType,
Method,
WebhookBodyParameter,
WebhookData,
WebhookParameter,
)
from dify_graph.nodes.trigger_webhook.node import TriggerWebhookNode
from core.workflow.nodes.trigger_webhook.node import TriggerWebhookNode
from dify_graph.entities.graph_init_params import DIFY_RUN_CONTEXT_KEY, GraphInitParams
from dify_graph.entities.workflow_node_execution import WorkflowNodeExecutionStatus
from dify_graph.file import File, FileTransferMethod, FileType
from dify_graph.runtime.graph_runtime_state import GraphRuntimeState
from dify_graph.runtime.variable_pool import VariablePool
from dify_graph.system_variable import SystemVariable

View File

@@ -294,7 +294,7 @@ class TestFrontendBackendIntegration(unittest.TestCase):
def test_schedule_service_integration(self):
"""Test integration with ScheduleService patterns."""
from dify_graph.nodes.trigger_schedule.entities import VisualConfig
from core.workflow.nodes.trigger_schedule.entities import VisualConfig
from services.trigger.schedule_service import ScheduleService
# Test enhanced syntax through visual config conversion

View File

@@ -5,8 +5,8 @@ from unittest.mock import MagicMock, Mock, patch
import pytest
from sqlalchemy.orm import Session
from dify_graph.nodes.trigger_schedule.entities import ScheduleConfig, SchedulePlanUpdate, VisualConfig
from dify_graph.nodes.trigger_schedule.exc import ScheduleConfigError
from core.workflow.nodes.trigger_schedule.entities import ScheduleConfig, SchedulePlanUpdate, VisualConfig
from core.workflow.nodes.trigger_schedule.exc import ScheduleConfigError
from events.event_handlers.sync_workflow_schedule_when_app_published import (
sync_schedule_from_workflow,
)
@@ -136,7 +136,7 @@ class TestScheduleService(unittest.TestCase):
def test_update_schedule_not_found(self):
"""Test updating a non-existent schedule raises exception."""
from dify_graph.nodes.trigger_schedule.exc import ScheduleNotFoundError
from core.workflow.nodes.trigger_schedule.exc import ScheduleNotFoundError
mock_session = MagicMock(spec=Session)
mock_session.get.return_value = None
@@ -172,7 +172,7 @@ class TestScheduleService(unittest.TestCase):
def test_delete_schedule_not_found(self):
"""Test deleting a non-existent schedule raises exception."""
from dify_graph.nodes.trigger_schedule.exc import ScheduleNotFoundError
from core.workflow.nodes.trigger_schedule.exc import ScheduleNotFoundError
mock_session = MagicMock(spec=Session)
mock_session.get.return_value = None