feat: snippets has no envirment variables. Snippet diable start, human_input, knowledge node.

This commit is contained in:
FFXN 2026-03-30 16:57:55 +08:00
parent 48d4c54762
commit 3865483d95
4 changed files with 49 additions and 26 deletions

View File

@ -71,7 +71,6 @@ class SnippetDraftSyncPayload(BaseModel):
graph: dict[str, Any]
hash: str | None = None
environment_variables: list[dict[str, Any]] | None = None
conversation_variables: list[dict[str, Any]] | None = None
input_fields: list[dict[str, Any]] | None = None

View File

@ -135,10 +135,6 @@ class SnippetDraftWorkflowApi(Resource):
payload = SnippetDraftSyncPayload.model_validate(console_ns.payload or {})
try:
environment_variables_list = payload.environment_variables or []
environment_variables = [
variable_factory.build_environment_variable_from_mapping(obj) for obj in environment_variables_list
]
conversation_variables_list = payload.conversation_variables or []
conversation_variables = [
variable_factory.build_conversation_variable_from_mapping(obj) for obj in conversation_variables_list
@ -149,12 +145,13 @@ class SnippetDraftWorkflowApi(Resource):
graph=payload.graph,
unique_hash=payload.hash,
account=current_user,
environment_variables=environment_variables,
conversation_variables=conversation_variables,
input_fields=payload.input_fields,
)
except WorkflowHashNotEqualError:
raise DraftWorkflowNotSync()
except ValueError as e:
return {"message": str(e)}, 400
return {
"result": "success",

View File

@ -22,7 +22,7 @@ from models import Account
from models.snippet import CustomizedSnippet, SnippetType
from models.workflow import Workflow
from services.plugin.dependencies_analysis import DependenciesAnalysisService
from services.snippet_service import SnippetService
from services.snippet_service import SNIPPET_FORBIDDEN_NODE_TYPES, SnippetService
logger = logging.getLogger(__name__)
@ -32,13 +32,6 @@ IMPORT_INFO_REDIS_EXPIRY = 10 * 60 # 10 minutes
DSL_MAX_SIZE = 10 * 1024 * 1024 # 10MB
CURRENT_DSL_VERSION = "0.1.0"
# List of node types that are not allowed in snippets
FORBIDDEN_NODE_TYPES = [
BuiltinNodeTypes.START,
BuiltinNodeTypes.HUMAN_INPUT,
]
class ImportMode(StrEnum):
YAML_CONTENT = "yaml-content"
YAML_URL = "yaml-url"
@ -230,7 +223,7 @@ class SnippetDslService:
if not node_data:
continue
node_type = node_data.get("type", "")
if node_type in FORBIDDEN_NODE_TYPES:
if node_type in SNIPPET_FORBIDDEN_NODE_TYPES:
forbidden_nodes_found.append(node_type)
if forbidden_nodes_found:
@ -427,12 +420,8 @@ class SnippetDslService:
# Create or update draft workflow
if workflow_data:
graph = workflow_data.get("graph", {})
environment_variables_list = workflow_data.get("environment_variables", [])
conversation_variables_list = workflow_data.get("conversation_variables", [])
environment_variables = [
variable_factory.build_environment_variable_from_mapping(obj) for obj in environment_variables_list
]
conversation_variables = [
variable_factory.build_conversation_variable_from_mapping(obj) for obj in conversation_variables_list
]
@ -447,7 +436,6 @@ class SnippetDslService:
graph=graph,
unique_hash=unique_hash,
account=account,
environment_variables=environment_variables,
conversation_variables=conversation_variables,
input_fields=input_fields,
)
@ -494,6 +482,8 @@ class SnippetDslService:
"""
workflow_dict = workflow.to_dict(include_secret=include_secret)
# Filter workspace related data from nodes
workflow_dict["environment_variables"] = []
for node in workflow_dict.get("graph", {}).get("nodes", []):
node_data = node.get("data", {})
if not node_data:

View File

@ -9,7 +9,7 @@ from sqlalchemy.orm import Session, sessionmaker
from core.workflow.node_factory import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING
from extensions.ext_database import db
from graphon.enums import NodeType
from graphon.enums import BuiltinNodeTypes, NodeType
from graphon.variables.variables import VariableBase
from libs.infinite_scroll_pagination import InfiniteScrollPagination
from models import Account
@ -26,6 +26,15 @@ from services.errors.app import WorkflowHashNotEqualError
logger = logging.getLogger(__name__)
# Node types not allowed in snippet workflows (sync, publish, DSL import).
SNIPPET_FORBIDDEN_NODE_TYPES: frozenset[str] = frozenset(
{
BuiltinNodeTypes.START,
BuiltinNodeTypes.HUMAN_INPUT,
BuiltinNodeTypes.KNOWLEDGE_RETRIEVAL,
}
)
class SnippetService:
"""Service for managing customized snippets."""
@ -39,6 +48,29 @@ class SnippetService:
)
self._workflow_run_repo = DifyAPIRepositoryFactory.create_api_workflow_run_repository(session_maker)
@staticmethod
def validate_snippet_graph_forbidden_nodes(graph: Mapping[str, Any]) -> None:
"""Reject graphs that contain node types not allowed in snippets."""
nodes = graph.get("nodes") or []
disallowed: list[tuple[str, str]] = []
for node in nodes:
if not isinstance(node, dict):
continue
node_data = node.get("data") or {}
node_type = node_data.get("type")
if not isinstance(node_type, str):
continue
if node_type in SNIPPET_FORBIDDEN_NODE_TYPES:
node_id = node.get("id")
disallowed.append((str(node_id) if node_id is not None else "?", node_type))
if not disallowed:
return
detail = ", ".join(f"{nid}:{t}" for nid, t in disallowed)
raise ValueError(
"Snippet workflow cannot contain start, human-input, or knowledge-retrieval nodes. "
f"Found: {detail}"
)
# --- CRUD Operations ---
@staticmethod
@ -276,23 +308,25 @@ class SnippetService:
graph: dict,
unique_hash: str | None,
account: Account,
environment_variables: Sequence[VariableBase],
conversation_variables: Sequence[VariableBase],
input_fields: list[dict] | None = None,
) -> Workflow:
"""
Sync draft workflow for snippet.
Snippet workflows do not persist environment variables (always empty).
:param snippet: CustomizedSnippet instance
:param graph: Workflow graph configuration
:param unique_hash: Hash for conflict detection
:param account: Account making the change
:param environment_variables: Environment variables
:param conversation_variables: Conversation variables
:param input_fields: Input fields for snippet
:return: Synced Workflow
:raises WorkflowHashNotEqualError: If hash mismatch
"""
SnippetService.validate_snippet_graph_forbidden_nodes(graph)
workflow = self.get_draft_workflow(snippet=snippet)
if workflow and workflow.unique_hash != unique_hash:
@ -308,7 +342,7 @@ class SnippetService:
version="draft",
graph=json.dumps(graph),
created_by=account.id,
environment_variables=environment_variables,
environment_variables=[],
conversation_variables=conversation_variables,
)
db.session.add(workflow)
@ -318,7 +352,7 @@ class SnippetService:
workflow.graph = json.dumps(graph)
workflow.updated_by = account.id
workflow.updated_at = datetime.now(UTC).replace(tzinfo=None)
workflow.environment_variables = environment_variables
workflow.environment_variables = []
workflow.conversation_variables = conversation_variables
# Update snippet's input_fields if provided
@ -356,6 +390,8 @@ class SnippetService:
if not draft_workflow:
raise ValueError("No valid workflow found.")
SnippetService.validate_snippet_graph_forbidden_nodes(draft_workflow.graph_dict)
# Create new published workflow
workflow = Workflow.new(
tenant_id=snippet.tenant_id,
@ -365,8 +401,9 @@ class SnippetService:
graph=draft_workflow.graph,
features=draft_workflow.features,
created_by=account.id,
environment_variables=draft_workflow.environment_variables,
environment_variables=[],
conversation_variables=draft_workflow.conversation_variables,
rag_pipeline_variables=draft_workflow.rag_pipeline_variables,
marked_name="",
marked_comment="",
)