refactor: rename new runtime as sandbox feature

This commit is contained in:
Harry
2026-01-12 01:53:26 +08:00
parent 3d2840edb6
commit 9dd0361d0e
13 changed files with 47 additions and 18 deletions

View File

@ -41,6 +41,7 @@ from factories import file_factory
from libs.flask_utils import preserve_flask_contexts
from models import Account, App, Conversation, EndUser, Message, Workflow, WorkflowNodeExecutionTriggeredFrom
from models.enums import WorkflowRunTriggeredFrom
from models.workflow_features import WorkflowFeatures
from services.conversation_service import ConversationService
from services.workflow_draft_variable_service import (
DraftVarLoader,
@ -513,10 +514,8 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
if workflow is None:
raise ValueError("Workflow not found")
# FIXME:(sandbox) Consolidate runtime config checking into a unified location.
runtime = workflow.features_dict.get("runtime")
graph_engine_layers: tuple = ()
if isinstance(runtime, dict) and runtime.get("enabled"):
if workflow.get_feature(WorkflowFeatures.SANDBOX).enabled:
graph_engine_layers = (SandboxLayer(tenant_id=application_generate_entity.app_config.tenant_id),)
# Determine system_user_id based on invocation source

View File

@ -38,6 +38,7 @@ from factories import file_factory
from libs.flask_utils import preserve_flask_contexts
from models import Account, App, EndUser, Workflow, WorkflowNodeExecutionTriggeredFrom
from models.enums import WorkflowRunTriggeredFrom
from models.workflow_features import WorkflowFeatures
from services.workflow_draft_variable_service import DraftVarLoader, WorkflowDraftVariableService
SKIP_PREPARE_USER_INPUTS_KEY = "_skip_prepare_user_inputs"
@ -488,9 +489,7 @@ class WorkflowAppGenerator(BaseAppGenerator):
if workflow is None:
raise ValueError("Workflow not found")
# FIXME:(sandbox) Consolidate runtime config checking into a unified location.
runtime = workflow.features_dict.get("runtime")
if isinstance(runtime, dict) and runtime.get("enabled"):
if workflow.get_feature(WorkflowFeatures.SANDBOX).enabled:
graph_engine_layers = (
*graph_engine_layers,
SandboxLayer(tenant_id=application_generate_entity.app_config.tenant_id),

View File

@ -109,6 +109,7 @@ from .workflow import (
WorkflowRun,
WorkflowType,
)
from .workflow_features import WorkflowFeature, WorkflowFeatures
__all__ = [
"APIBasedExtension",
@ -202,6 +203,8 @@ __all__ = [
"Workflow",
"WorkflowAppLog",
"WorkflowAppLogCreatedFrom",
"WorkflowFeature",
"WorkflowFeatures",
"WorkflowNodeExecutionModel",
"WorkflowNodeExecutionOffload",
"WorkflowNodeExecutionTriggeredFrom",

View File

@ -37,13 +37,13 @@ from extensions.ext_storage import Storage
from factories.variable_factory import TypeMismatchError, build_segment_with_type
from libs.datetime_utils import naive_utc_now
from libs.uuid_utils import uuidv7
from models.workflow_features import WorkflowFeature, WorkflowFeatures
from ._workflow_exc import NodeNotFoundError, WorkflowDataError
if TYPE_CHECKING:
from .model import AppMode, UploadFile
from constants import DEFAULT_FILE_NUMBER_LIMITS, HIDDEN_VALUE
from core.helper import encrypter
from core.variables import SecretVariable, Segment, SegmentType, Variable
@ -345,6 +345,9 @@ class Workflow(Base): # bug
def features_dict(self) -> dict[str, Any]:
return json.loads(self.features) if self.features else {}
def get_feature(self, key: WorkflowFeatures) -> WorkflowFeature:
return WorkflowFeature.from_dict(self.features_dict.get(key.value))
def walk_nodes(
self, specific_node_type: NodeType | None = None
) -> Generator[tuple[str, Mapping[str, Any]], None, None]:

View File

@ -0,0 +1,26 @@
from collections.abc import Mapping
from dataclasses import dataclass
from enum import StrEnum
from typing import Any
class WorkflowFeatures(StrEnum):
SANDBOX = "sandbox"
SPEECH_TO_TEXT = "speech_to_text"
TEXT_TO_SPEECH = "text_to_speech"
RETRIEVER_RESOURCE = "retriever_resource"
SENSITIVE_WORD_AVOIDANCE = "sensitive_word_avoidance"
FILE_UPLOAD = "file_upload"
SUGGESTED_QUESTIONS_AFTER_ANSWER = "suggested_questions_after_answer"
@dataclass(frozen=True)
class WorkflowFeature:
enabled: bool
config: Mapping[str, Any]
@classmethod
def from_dict(cls, data: Mapping[str, Any] | None) -> "WorkflowFeature":
if data is None or not isinstance(data, dict):
return cls(enabled=False, config={})
return cls(enabled=bool(data.get("enabled", False)), config=data)

View File

@ -38,6 +38,7 @@ from models import Account
from models.model import App, AppMode
from models.tools import WorkflowToolProvider
from models.workflow import Workflow, WorkflowNodeExecutionModel, WorkflowNodeExecutionTriggeredFrom, WorkflowType
from models.workflow_features import WorkflowFeatures
from repositories.factory import DifyAPIRepositoryFactory
from services.billing_service import BillingService
from services.enterprise.plugin_manager_service import PluginCredentialType
@ -697,11 +698,9 @@ class WorkflowService:
else:
enclosing_node_id = None
# TODO: Consolidate runtime config checking into a unified location.
runtime = draft_workflow.features_dict.get("runtime")
sandbox = None
single_step_execution_id: str | None = None
if isinstance(runtime, dict) and runtime.get("enabled"):
if draft_workflow.get_feature(WorkflowFeatures.SANDBOX).enabled:
sandbox = SandboxProviderService.create_sandbox(tenant_id=draft_workflow.tenant_id)
single_step_execution_id = f"single-step-{uuid.uuid4()}"
from core.virtual_environment.sandbox_manager import SandboxManager