refactor(api): inject code executor from node factory (#32618)

This commit is contained in:
-LAN- 2026-02-27 13:29:00 +08:00 committed by GitHub
parent 5b45b62994
commit 700a4029c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 62 additions and 12 deletions

View File

@ -110,7 +110,6 @@ ignore_imports =
core.workflow.nodes.agent.agent_node -> core.model_manager core.workflow.nodes.agent.agent_node -> core.model_manager
core.workflow.nodes.agent.agent_node -> core.provider_manager core.workflow.nodes.agent.agent_node -> core.provider_manager
core.workflow.nodes.agent.agent_node -> core.tools.tool_manager core.workflow.nodes.agent.agent_node -> core.tools.tool_manager
core.workflow.nodes.code.code_node -> core.helper.code_executor.code_executor
core.workflow.nodes.datasource.datasource_node -> models.model core.workflow.nodes.datasource.datasource_node -> models.model
core.workflow.nodes.datasource.datasource_node -> models.tools core.workflow.nodes.datasource.datasource_node -> models.tools
core.workflow.nodes.datasource.datasource_node -> services.datasource_provider_service core.workflow.nodes.datasource.datasource_node -> services.datasource_provider_service

View File

@ -1,9 +1,10 @@
from typing import TYPE_CHECKING, final from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, final
from typing_extensions import override from typing_extensions import override
from configs import dify_config from configs import dify_config
from core.helper.code_executor.code_executor import CodeExecutor from core.helper.code_executor.code_executor import CodeExecutionError, CodeExecutor
from core.helper.code_executor.code_node_provider import CodeNodeProvider from core.helper.code_executor.code_node_provider import CodeNodeProvider
from core.helper.ssrf_proxy import ssrf_proxy from core.helper.ssrf_proxy import ssrf_proxy
from core.rag.retrieval.dataset_retrieval import DatasetRetrieval from core.rag.retrieval.dataset_retrieval import DatasetRetrieval
@ -13,7 +14,8 @@ from core.workflow.enums import NodeType
from core.workflow.file.file_manager import file_manager from core.workflow.file.file_manager import file_manager
from core.workflow.graph.graph import NodeFactory from core.workflow.graph.graph import NodeFactory
from core.workflow.nodes.base.node import Node from core.workflow.nodes.base.node import Node
from core.workflow.nodes.code.code_node import CodeNode from core.workflow.nodes.code.code_node import CodeNode, WorkflowCodeExecutor
from core.workflow.nodes.code.entities import CodeLanguage
from core.workflow.nodes.code.limits import CodeNodeLimits from core.workflow.nodes.code.limits import CodeNodeLimits
from core.workflow.nodes.document_extractor import DocumentExtractorNode, UnstructuredApiConfig from core.workflow.nodes.document_extractor import DocumentExtractorNode, UnstructuredApiConfig
from core.workflow.nodes.http_request import HttpRequestNode, build_http_request_config from core.workflow.nodes.http_request import HttpRequestNode, build_http_request_config
@ -27,6 +29,24 @@ if TYPE_CHECKING:
from core.workflow.runtime import GraphRuntimeState from core.workflow.runtime import GraphRuntimeState
class DefaultWorkflowCodeExecutor:
def execute(
self,
*,
language: CodeLanguage,
code: str,
inputs: Mapping[str, Any],
) -> Mapping[str, Any]:
return CodeExecutor.execute_workflow_code_template(
language=language,
code=code,
inputs=inputs,
)
def is_execution_error(self, error: Exception) -> bool:
return isinstance(error, CodeExecutionError)
@final @final
class DifyNodeFactory(NodeFactory): class DifyNodeFactory(NodeFactory):
""" """
@ -43,7 +63,7 @@ class DifyNodeFactory(NodeFactory):
) -> None: ) -> None:
self.graph_init_params = graph_init_params self.graph_init_params = graph_init_params
self.graph_runtime_state = graph_runtime_state self.graph_runtime_state = graph_runtime_state
self._code_executor: type[CodeExecutor] = CodeExecutor self._code_executor: WorkflowCodeExecutor = DefaultWorkflowCodeExecutor()
self._code_providers: tuple[type[CodeNodeProvider], ...] = CodeNode.default_code_providers() self._code_providers: tuple[type[CodeNodeProvider], ...] = CodeNode.default_code_providers()
self._code_limits = CodeNodeLimits( self._code_limits = CodeNodeLimits(
max_string_length=dify_config.CODE_MAX_STRING_LENGTH, max_string_length=dify_config.CODE_MAX_STRING_LENGTH,

View File

@ -1,8 +1,7 @@
from collections.abc import Mapping, Sequence from collections.abc import Mapping, Sequence
from decimal import Decimal from decimal import Decimal
from typing import TYPE_CHECKING, Any, ClassVar, cast from typing import TYPE_CHECKING, Any, ClassVar, Protocol, cast
from core.helper.code_executor.code_executor import CodeExecutionError, CodeExecutor, CodeLanguage
from core.helper.code_executor.code_node_provider import CodeNodeProvider from core.helper.code_executor.code_node_provider import CodeNodeProvider
from core.helper.code_executor.javascript.javascript_code_provider import JavascriptCodeProvider from core.helper.code_executor.javascript.javascript_code_provider import JavascriptCodeProvider
from core.helper.code_executor.python3.python3_code_provider import Python3CodeProvider from core.helper.code_executor.python3.python3_code_provider import Python3CodeProvider
@ -11,7 +10,7 @@ from core.variables.types import SegmentType
from core.workflow.enums import NodeType, WorkflowNodeExecutionStatus from core.workflow.enums import NodeType, WorkflowNodeExecutionStatus
from core.workflow.node_events import NodeRunResult from core.workflow.node_events import NodeRunResult
from core.workflow.nodes.base.node import Node from core.workflow.nodes.base.node import Node
from core.workflow.nodes.code.entities import CodeNodeData from core.workflow.nodes.code.entities import CodeLanguage, CodeNodeData
from core.workflow.nodes.code.limits import CodeNodeLimits from core.workflow.nodes.code.limits import CodeNodeLimits
from .exc import ( from .exc import (
@ -25,6 +24,18 @@ if TYPE_CHECKING:
from core.workflow.runtime import GraphRuntimeState from core.workflow.runtime import GraphRuntimeState
class WorkflowCodeExecutor(Protocol):
def execute(
self,
*,
language: CodeLanguage,
code: str,
inputs: Mapping[str, Any],
) -> Mapping[str, Any]: ...
def is_execution_error(self, error: Exception) -> bool: ...
class CodeNode(Node[CodeNodeData]): class CodeNode(Node[CodeNodeData]):
node_type = NodeType.CODE node_type = NodeType.CODE
_DEFAULT_CODE_PROVIDERS: ClassVar[tuple[type[CodeNodeProvider], ...]] = ( _DEFAULT_CODE_PROVIDERS: ClassVar[tuple[type[CodeNodeProvider], ...]] = (
@ -40,7 +51,7 @@ class CodeNode(Node[CodeNodeData]):
graph_init_params: "GraphInitParams", graph_init_params: "GraphInitParams",
graph_runtime_state: "GraphRuntimeState", graph_runtime_state: "GraphRuntimeState",
*, *,
code_executor: type[CodeExecutor] | None = None, code_executor: WorkflowCodeExecutor,
code_providers: Sequence[type[CodeNodeProvider]] | None = None, code_providers: Sequence[type[CodeNodeProvider]] | None = None,
code_limits: CodeNodeLimits, code_limits: CodeNodeLimits,
) -> None: ) -> None:
@ -50,7 +61,7 @@ class CodeNode(Node[CodeNodeData]):
graph_init_params=graph_init_params, graph_init_params=graph_init_params,
graph_runtime_state=graph_runtime_state, graph_runtime_state=graph_runtime_state,
) )
self._code_executor: type[CodeExecutor] = code_executor or CodeExecutor self._code_executor: WorkflowCodeExecutor = code_executor
self._code_providers: tuple[type[CodeNodeProvider], ...] = ( self._code_providers: tuple[type[CodeNodeProvider], ...] = (
tuple(code_providers) if code_providers else self._DEFAULT_CODE_PROVIDERS tuple(code_providers) if code_providers else self._DEFAULT_CODE_PROVIDERS
) )
@ -98,7 +109,7 @@ class CodeNode(Node[CodeNodeData]):
# Run code # Run code
try: try:
_ = self._select_code_provider(code_language) _ = self._select_code_provider(code_language)
result = self._code_executor.execute_workflow_code_template( result = self._code_executor.execute(
language=code_language, language=code_language,
code=code, code=code,
inputs=variables, inputs=variables,
@ -106,7 +117,13 @@ class CodeNode(Node[CodeNodeData]):
# Transform result # Transform result
result = self._transform_result(result=result, output_schema=self.node_data.outputs) result = self._transform_result(result=result, output_schema=self.node_data.outputs)
except (CodeExecutionError, CodeNodeError) as e: except CodeNodeError as e:
return NodeRunResult(
status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, error=str(e), error_type=type(e).__name__
)
except Exception as e:
if not self._code_executor.is_execution_error(e):
raise
return NodeRunResult( return NodeRunResult(
status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, error=str(e), error_type=type(e).__name__ status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, error=str(e), error_type=type(e).__name__
) )

View File

@ -68,6 +68,7 @@ def init_code_node(code_config: dict):
config=code_config, config=code_config,
graph_init_params=init_params, graph_init_params=init_params,
graph_runtime_state=graph_runtime_state, graph_runtime_state=graph_runtime_state,
code_executor=node_factory._code_executor,
code_limits=CodeNodeLimits( code_limits=CodeNodeLimits(
max_string_length=dify_config.CODE_MAX_STRING_LENGTH, max_string_length=dify_config.CODE_MAX_STRING_LENGTH,
max_number=dify_config.CODE_MAX_NUMBER, max_number=dify_config.CODE_MAX_NUMBER,

View File

@ -24,6 +24,16 @@ DEFAULT_CODE_LIMITS = CodeNodeLimits(
) )
class _NoopCodeExecutor:
def execute(self, *, language: object, code: str, inputs: dict[str, object]) -> dict[str, object]:
_ = (language, code, inputs)
return {}
def is_execution_error(self, error: Exception) -> bool:
_ = error
return False
class TestMockTemplateTransformNode: class TestMockTemplateTransformNode:
"""Test cases for MockTemplateTransformNode.""" """Test cases for MockTemplateTransformNode."""
@ -319,6 +329,7 @@ class TestMockCodeNode:
graph_init_params=graph_init_params, graph_init_params=graph_init_params,
graph_runtime_state=graph_runtime_state, graph_runtime_state=graph_runtime_state,
mock_config=mock_config, mock_config=mock_config,
code_executor=_NoopCodeExecutor(),
code_limits=DEFAULT_CODE_LIMITS, code_limits=DEFAULT_CODE_LIMITS,
) )
@ -384,6 +395,7 @@ class TestMockCodeNode:
graph_init_params=graph_init_params, graph_init_params=graph_init_params,
graph_runtime_state=graph_runtime_state, graph_runtime_state=graph_runtime_state,
mock_config=mock_config, mock_config=mock_config,
code_executor=_NoopCodeExecutor(),
code_limits=DEFAULT_CODE_LIMITS, code_limits=DEFAULT_CODE_LIMITS,
) )
@ -453,6 +465,7 @@ class TestMockCodeNode:
graph_init_params=graph_init_params, graph_init_params=graph_init_params,
graph_runtime_state=graph_runtime_state, graph_runtime_state=graph_runtime_state,
mock_config=mock_config, mock_config=mock_config,
code_executor=_NoopCodeExecutor(),
code_limits=DEFAULT_CODE_LIMITS, code_limits=DEFAULT_CODE_LIMITS,
) )