From 094c9fd802292d86c3b3e8275f8e3629f639a1ed Mon Sep 17 00:00:00 2001 From: Harry Date: Wed, 7 Jan 2026 15:22:12 +0800 Subject: [PATCH] fix: command node single debug run - Added FIXME comments to indicate the need for unifying runtime config checking in AdvancedChatAppGenerator and WorkflowAppGenerator. - Introduced sandbox management in WorkflowService with proper error handling for sandbox release. - Enhanced runtime feature handling in the workflow execution process. --- .../app/apps/advanced_chat/app_generator.py | 1 + api/core/app/apps/workflow/app_generator.py | 1 + api/services/workflow_service.py | 52 +++++++++++++------ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/api/core/app/apps/advanced_chat/app_generator.py b/api/core/app/apps/advanced_chat/app_generator.py index 3a348532e4..12a18861e0 100644 --- a/api/core/app/apps/advanced_chat/app_generator.py +++ b/api/core/app/apps/advanced_chat/app_generator.py @@ -513,6 +513,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator): if workflow is None: raise ValueError("Workflow not found") + # FIXME: Consolidate runtime config checking into a unified location. runtime = workflow.features_dict.get("runtime") graph_engine_layers = () if isinstance(runtime, dict) and runtime.get("enabled"): diff --git a/api/core/app/apps/workflow/app_generator.py b/api/core/app/apps/workflow/app_generator.py index 197d988a7b..a8aa26fe55 100644 --- a/api/core/app/apps/workflow/app_generator.py +++ b/api/core/app/apps/workflow/app_generator.py @@ -488,6 +488,7 @@ class WorkflowAppGenerator(BaseAppGenerator): if workflow is None: raise ValueError("Workflow not found") + # FIXME: Consolidate runtime config checking into a unified location. runtime = workflow.features_dict.get("runtime") if isinstance(runtime, dict) and runtime.get("enabled"): graph_engine_layers = (*graph_engine_layers, SandboxLayer()) diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py index b45a167b73..c6db6403fd 100644 --- a/api/services/workflow_service.py +++ b/api/services/workflow_service.py @@ -1,4 +1,5 @@ import json +import logging import time import uuid from collections.abc import Callable, Generator, Mapping, Sequence @@ -15,6 +16,7 @@ from core.file import File from core.repositories import DifyCoreRepositoryFactory from core.variables import Variable from core.variables.variables import VariableUnion +from core.virtual_environment.factory import SandboxFactory, SandboxType from core.workflow.entities import WorkflowNodeExecution from core.workflow.enums import ErrorStrategy, WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from core.workflow.errors import WorkflowNodeRunFailedError @@ -46,6 +48,8 @@ from services.workflow.workflow_converter import WorkflowConverter from .errors.workflow_service import DraftWorkflowDeletionError, WorkflowInUseError from .workflow_draft_variable_service import DraftVariableSaver, DraftVarLoader, WorkflowDraftVariableService +logger = logging.getLogger(__name__) + class WorkflowService: """ @@ -693,22 +697,40 @@ class WorkflowService: else: enclosing_node_id = None - run = WorkflowEntry.single_step_run( - workflow=draft_workflow, - node_id=node_id, - user_inputs=user_inputs, - user_id=account.id, - variable_pool=variable_pool, - variable_loader=variable_loader, - ) + # FIXME: Consolidate runtime config checking into a unified location. + runtime = draft_workflow.features_dict.get("runtime") + sandbox = None + if isinstance(runtime, dict) and runtime.get("enabled"): + sandbox = SandboxFactory.create(sandbox_type=SandboxType.DOCKER) - # run draft workflow node - start_at = time.perf_counter() - node_execution = self._handle_single_step_result( - invoke_node_fn=lambda: run, - start_at=start_at, - node_id=node_id, - ) + try: + node, generator = WorkflowEntry.single_step_run( + workflow=draft_workflow, + node_id=node_id, + user_inputs=user_inputs, + user_id=account.id, + variable_pool=variable_pool, + variable_loader=variable_loader, + ) + + # FIXME: Use a proper dependency injection pattern for sandbox. + if sandbox: + node.sandbox = sandbox # type: ignore[attr-defined] + + # Run draft workflow node + start_at = time.perf_counter() + node_execution = self._handle_single_step_result( + invoke_node_fn=lambda: (node, generator), + start_at=start_at, + node_id=node_id, + ) + finally: + # Release sandbox after node execution + if sandbox: + try: + sandbox.release_environment() + except Exception: + logger.exception("Failed to release sandbox") # Set workflow_id on the NodeExecution node_execution.workflow_id = draft_workflow.id