diff --git a/api/core/agent/agent_app_runner.py b/api/core/agent/agent_app_runner.py index b5a2cd1ef5..1473707ec3 100644 --- a/api/core/agent/agent_app_runner.py +++ b/api/core/agent/agent_app_runner.py @@ -272,7 +272,7 @@ class AgentAppRunner(BaseAgentRunner): self.queue_manager.publish( QueueMessageEndEvent( llm_result=LLMResult( - model=self.model_instance.model, + model=self.model_instance.model_name, prompt_messages=prompt_messages, message=AssistantPromptMessage(content=final_answer), usage=usage, diff --git a/api/core/agent/patterns/base.py b/api/core/agent/patterns/base.py index f3d31fdbc0..61245775ae 100644 --- a/api/core/agent/patterns/base.py +++ b/api/core/agent/patterns/base.py @@ -320,7 +320,7 @@ class AgentPattern(ABC): def _create_text_chunk(self, text: str, prompt_messages: list[PromptMessage]) -> LLMResultChunk: """Create a text chunk for streaming.""" return LLMResultChunk( - model=self.model_instance.model, + model=self.model_instance.model_name, prompt_messages=prompt_messages, delta=LLMResultChunkDelta( index=0, diff --git a/api/core/agent/patterns/function_call.py b/api/core/agent/patterns/function_call.py index ba5fd7902f..f520cd5841 100644 --- a/api/core/agent/patterns/function_call.py +++ b/api/core/agent/patterns/function_call.py @@ -71,7 +71,7 @@ class FunctionCallStrategy(AgentPattern): # On last iteration, remove tools to force final answer current_tools: list[PromptMessageTool] = [] if iteration_step == max_iterations else prompt_tools model_log = self._create_log( - label=f"{self.model_instance.model} Thought", + label=f"{self.model_instance.model_name} Thought", log_type=AgentLog.LogType.THOUGHT, status=AgentLog.LogStatus.START, data={}, diff --git a/api/core/agent/patterns/react.py b/api/core/agent/patterns/react.py index a7032dfa20..6834afef3d 100644 --- a/api/core/agent/patterns/react.py +++ b/api/core/agent/patterns/react.py @@ -91,7 +91,7 @@ class ReActStrategy(AgentPattern): ) model_log = self._create_log( - label=f"{self.model_instance.model} Thought", + label=f"{self.model_instance.model_name} Thought", log_type=AgentLog.LogType.THOUGHT, status=AgentLog.LogStatus.START, data={}, diff --git a/api/dify_graph/nodes/agent/agent_node.py b/api/dify_graph/nodes/agent/agent_node.py index bf5d780006..7000215f32 100644 --- a/api/dify_graph/nodes/agent/agent_node.py +++ b/api/dify_graph/nodes/agent/agent_node.py @@ -36,6 +36,7 @@ from core.workflow.nodes.agent.exceptions import ( ToolFileNotFoundError, ) from dify_graph.enums import ( + BuiltinNodeTypes, NodeType, SystemVariableKey, WorkflowNodeExecutionMetadataKey, @@ -79,7 +80,7 @@ class AgentNode(Node[AgentNodeData]): Agent Node """ - node_type = NodeType.AGENT + node_type = BuiltinNodeTypes.AGENT @classmethod def version(cls) -> str: @@ -740,7 +741,7 @@ class AgentNode(Node[AgentNodeData]): ) elif message.type == ToolInvokeMessage.MessageType.JSON: assert isinstance(message.message, ToolInvokeMessage.JsonMessage) - if node_type == NodeType.AGENT: + if node_type == BuiltinNodeTypes.AGENT: if isinstance(message.message.json_object, dict): msg_metadata: dict[str, Any] = message.message.json_object.pop("execution_metadata", {}) llm_usage = LLMUsage.from_metadata(cast(LLMUsageMetadata, msg_metadata)) diff --git a/api/dify_graph/nodes/llm/llm_utils.py b/api/dify_graph/nodes/llm/llm_utils.py index 07b0997b46..fbb80b9480 100644 --- a/api/dify_graph/nodes/llm/llm_utils.py +++ b/api/dify_graph/nodes/llm/llm_utils.py @@ -47,6 +47,20 @@ from .exc import ( from .protocols import TemplateRenderer +def fetch_model_config( + *, tenant_id: str, node_data_model: ModelConfig +) -> tuple[ModelInstance, Any]: + from core.app.llm.model_access import build_dify_model_access + from core.app.llm.model_access import fetch_model_config as _fetch + + credentials_provider, model_factory = build_dify_model_access(tenant_id) + return _fetch( + node_data_model=node_data_model, + credentials_provider=credentials_provider, + model_factory=model_factory, + ) + + def fetch_model_schema(*, model_instance: ModelInstance) -> AIModelEntity: model_schema = cast(LargeLanguageModel, model_instance.model_type_instance).get_model_schema( model_instance.model_name, diff --git a/api/dify_graph/nodes/llm/node.py b/api/dify_graph/nodes/llm/node.py index 89622981f9..39cb9caeae 100644 --- a/api/dify_graph/nodes/llm/node.py +++ b/api/dify_graph/nodes/llm/node.py @@ -1985,7 +1985,7 @@ class LLMNode(Node[LLMNodeData]): try: model_type_instance = model_instance.model_type_instance model_schema = model_type_instance.get_model_schema( - model_instance.model, + model_instance.model_name, model_instance.credentials, ) return model_schema.features if model_schema and model_schema.features else [] diff --git a/api/services/skill_service.py b/api/services/skill_service.py index 3876916d7e..9065f83893 100644 --- a/api/services/skill_service.py +++ b/api/services/skill_service.py @@ -26,7 +26,7 @@ from core.skill.entities.skill_document import SkillDocument from core.skill.entities.skill_metadata import SkillMetadata from core.skill.entities.tool_dependencies import ToolDependencies, ToolDependency from core.skill.skill_manager import SkillManager -from dify_graph.enums import NodeType +from dify_graph.enums import BuiltinNodeTypes from models.model import App from services.app_asset_service import AppAssetService @@ -55,7 +55,7 @@ class SkillService: Returns an empty list when the node has no skill prompts or when no draft assets exist. """ - if node_data.get("type", "") != NodeType.LLM.value: + if node_data.get("type", "") != BuiltinNodeTypes.LLM: return [] if not SkillService._has_skill(node_data): diff --git a/api/services/workflow/nested_node_graph_service.py b/api/services/workflow/nested_node_graph_service.py index 3e9eeb9e5b..5ff1d57410 100644 --- a/api/services/workflow/nested_node_graph_service.py +++ b/api/services/workflow/nested_node_graph_service.py @@ -9,7 +9,7 @@ from typing import Any from sqlalchemy.orm import Session -from dify_graph.enums import NodeType +from dify_graph.enums import BuiltinNodeTypes from dify_graph.model_runtime.entities import LLMMode from services.model_provider_service import ModelProviderService from services.workflow.entities import NestedNodeGraphRequest, NestedNodeGraphResponse, NestedNodeParameterSchema @@ -124,7 +124,7 @@ class NestedNodeGraphService: "id": node_id, "position": {"x": 0, "y": 0}, "data": { - "type": NodeType.LLM.value, + "type": BuiltinNodeTypes.LLM, # BaseNodeData fields "title": f"NestedNode: {parameter_schema.name}", "desc": f"Extract {parameter_schema.name} from conversation context", diff --git a/api/tests/unit_tests/core/agent/patterns/test_base.py b/api/tests/unit_tests/core/agent/patterns/test_base.py index c5a65bd6d2..a31913e537 100644 --- a/api/tests/unit_tests/core/agent/patterns/test_base.py +++ b/api/tests/unit_tests/core/agent/patterns/test_base.py @@ -22,7 +22,7 @@ class ConcreteAgentPattern(AgentPattern): def mock_model_instance(): """Create a mock model instance.""" model_instance = MagicMock() - model_instance.model = "test-model" + model_instance.model_name = "test-model" model_instance.provider = "test-provider" return model_instance diff --git a/api/tests/unit_tests/core/agent/patterns/test_function_call.py b/api/tests/unit_tests/core/agent/patterns/test_function_call.py index a2764a8a99..0d2c584550 100644 --- a/api/tests/unit_tests/core/agent/patterns/test_function_call.py +++ b/api/tests/unit_tests/core/agent/patterns/test_function_call.py @@ -18,7 +18,7 @@ from dify_graph.model_runtime.entities.message_entities import ( def mock_model_instance(): """Create a mock model instance.""" model_instance = MagicMock() - model_instance.model = "test-model" + model_instance.model_name = "test-model" model_instance.provider = "test-provider" return model_instance diff --git a/api/tests/unit_tests/core/agent/patterns/test_react.py b/api/tests/unit_tests/core/agent/patterns/test_react.py index 012a7ec077..500aba8fcb 100644 --- a/api/tests/unit_tests/core/agent/patterns/test_react.py +++ b/api/tests/unit_tests/core/agent/patterns/test_react.py @@ -13,7 +13,7 @@ from dify_graph.model_runtime.entities import SystemPromptMessage, UserPromptMes def mock_model_instance(): """Create a mock model instance.""" model_instance = MagicMock() - model_instance.model = "test-model" + model_instance.model_name = "test-model" model_instance.provider = "test-provider" return model_instance diff --git a/api/tests/unit_tests/core/agent/patterns/test_strategy_factory.py b/api/tests/unit_tests/core/agent/patterns/test_strategy_factory.py index ec35cc56b6..cdd5aff022 100644 --- a/api/tests/unit_tests/core/agent/patterns/test_strategy_factory.py +++ b/api/tests/unit_tests/core/agent/patterns/test_strategy_factory.py @@ -15,7 +15,7 @@ from dify_graph.model_runtime.entities.model_entities import ModelFeature def mock_model_instance(): """Create a mock model instance.""" model_instance = MagicMock() - model_instance.model = "test-model" + model_instance.model_name = "test-model" model_instance.provider = "test-provider" return model_instance diff --git a/api/tests/unit_tests/core/rag/embedding/test_cached_embedding.py b/api/tests/unit_tests/core/rag/embedding/test_cached_embedding.py index a0db25174d..c774042315 100644 --- a/api/tests/unit_tests/core/rag/embedding/test_cached_embedding.py +++ b/api/tests/unit_tests/core/rag/embedding/test_cached_embedding.py @@ -27,7 +27,7 @@ class TestCacheEmbeddingMultimodalDocuments: def mock_model_instance(self): """Create a mock ModelInstance for testing.""" model_instance = Mock() - model_instance.model = "vision-embedding-model" + model_instance.model_name = "vision-embedding-model" model_instance.provider = "openai" model_instance.credentials = {"api_key": "test-key"} @@ -315,7 +315,7 @@ class TestCacheEmbeddingMultimodalQuery: def mock_model_instance(self): """Create a mock ModelInstance for testing.""" model_instance = Mock() - model_instance.model = "vision-embedding-model" + model_instance.model_name = "vision-embedding-model" model_instance.provider = "openai" model_instance.credentials = {"api_key": "test-key"} return model_instance @@ -466,7 +466,7 @@ class TestCacheEmbeddingQueryErrors: def mock_model_instance(self): """Create a mock ModelInstance for testing.""" model_instance = Mock() - model_instance.model = "text-embedding-ada-002" + model_instance.model_name = "text-embedding-ada-002" model_instance.provider = "openai" model_instance.credentials = {"api_key": "test-key"} return model_instance @@ -535,7 +535,7 @@ class TestCacheEmbeddingInitialization: def test_initialization_with_user(self): """Test CacheEmbedding initialization with user parameter.""" model_instance = Mock() - model_instance.model = "test-model" + model_instance.model_name = "test-model" model_instance.provider = "test-provider" cache_embedding = CacheEmbedding(model_instance, user="test-user") @@ -546,7 +546,7 @@ class TestCacheEmbeddingInitialization: def test_initialization_without_user(self): """Test CacheEmbedding initialization without user parameter.""" model_instance = Mock() - model_instance.model = "test-model" + model_instance.model_name = "test-model" model_instance.provider = "test-provider" cache_embedding = CacheEmbedding(model_instance) diff --git a/web/app/components/header/account-setting/constants.ts b/web/app/components/header/account-setting/constants.ts index f9cff47aad..7f0bf2c440 100644 --- a/web/app/components/header/account-setting/constants.ts +++ b/web/app/components/header/account-setting/constants.ts @@ -3,6 +3,7 @@ export const ACCOUNT_SETTING_MODAL_ACTION = 'showSettings' export const ACCOUNT_SETTING_TAB = { SANDBOX_PROVIDER: 'sandbox-provider', MODEL_PROVIDER: 'model-provider', + PROVIDER: 'model-provider', MEMBERS: 'members', BILLING: 'billing', DATA_SOURCE: 'data-source', diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx index 6b4018e2aa..ac245a4154 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -32,6 +32,7 @@ import Trigger from './trigger' export type ModelParameterModalProps = { popupClassName?: string + portalToFollowElemContentClassName?: string isAdvancedMode: boolean modelId: string provider: string diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx index 289a08e89e..55daf1935b 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx @@ -19,6 +19,7 @@ import { export type TriggerProps = { open?: boolean + disabled?: boolean currentProvider?: ModelProvider | Model currentModel?: ModelItem providerName?: string diff --git a/web/app/components/header/account-setting/sandbox-provider-page/config-modal.tsx b/web/app/components/header/account-setting/sandbox-provider-page/config-modal.tsx index be885058f4..f2d6bce29c 100644 --- a/web/app/components/header/account-setting/sandbox-provider-page/config-modal.tsx +++ b/web/app/components/header/account-setting/sandbox-provider-page/config-modal.tsx @@ -7,9 +7,9 @@ import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { BaseForm } from '@/app/components/base/form/components/base' import { FormTypeEnum } from '@/app/components/base/form/types' -import Modal from '@/app/components/base/modal' import RadioUI from '@/app/components/base/radio/ui' -import { useToastContext } from '@/app/components/base/toast/context' +import { Dialog, DialogCloseButton, DialogContent } from '@/app/components/base/ui/dialog' +import { toast } from '@/app/components/base/ui/toast' import { useActivateSandboxProvider, useDeleteSandboxProviderConfig, @@ -21,7 +21,6 @@ import ProviderIcon from './provider-icon' type ConfigMode = 'managed' | 'byok' -// Providers that support mode selection (must have system config available) const PROVIDERS_WITH_MODE_SELECTION: readonly string[] = ['e2b'] type ModeOptionProps = { @@ -64,20 +63,16 @@ type ConfigModalProps = { function ConfigModal({ provider, onClose }: ConfigModalProps) { const { t } = useTranslation() - const { notify } = useToastContext() const formRef = useRef(null) const { mutateAsync: saveConfig, isPending: isSaving } = useSaveSandboxProviderConfig() const { mutateAsync: deleteConfig, isPending: isDeleting } = useDeleteSandboxProviderConfig() const { mutateAsync: activateProvider, isPending: isActivating } = useActivateSandboxProvider() - // Determine if mode selection should be shown (for providers that support it) const shouldShowModeSelection = PROVIDERS_WITH_MODE_SELECTION.includes(provider.provider_type) - // Managed mode is only available when system has configured this provider const isManagedModeAvailable = provider.is_system_configured - // Determine default mode based on configuration state const defaultMode: ConfigMode = provider.is_tenant_configured ? 'byok' : provider.is_system_configured @@ -105,11 +100,10 @@ function ConfigModal({ provider, onClose }: ConfigModalProps) { }, [provider.config_schema, provider.config, t]) const handleSave = useCallback(async () => { - // For managed mode, activate system config (preserves user config for future use) if (shouldShowModeSelection && configMode === 'managed') { try { await activateProvider({ providerType: provider.provider_type, type: 'system' }) - notify({ type: 'success', message: t('api.saved', { ns: 'common' }) }) + toast.success(t('api.saved', { ns: 'common' })) onClose() } catch { @@ -118,7 +112,6 @@ function ConfigModal({ provider, onClose }: ConfigModalProps) { return } - // For BYOK mode, validate and save user-provided config const formValues = formRef.current?.getFormValues({ needTransformWhenSecretFieldIsPristine: true, }) @@ -132,124 +125,126 @@ function ConfigModal({ provider, onClose }: ConfigModalProps) { config: formValues.values, activate: true, }) - notify({ type: 'success', message: t('api.saved', { ns: 'common' }) }) + toast.success(t('api.saved', { ns: 'common' })) onClose() } catch { // Error toast is handled by fetch layer } - }, [shouldShowModeSelection, configMode, saveConfig, activateProvider, provider.provider_type, notify, t, onClose]) + }, [shouldShowModeSelection, configMode, saveConfig, activateProvider, provider.provider_type, t, onClose]) const handleRevoke = useCallback(async () => { try { await deleteConfig(provider.provider_type) - notify({ type: 'success', message: t('api.remove', { ns: 'common' }) }) + toast.success(t('api.remove', { ns: 'common' })) onClose() } catch { // Error toast is handled by fetch layer } - }, [deleteConfig, provider.provider_type, notify, t, onClose]) + }, [deleteConfig, provider.provider_type, t, onClose]) const docLink = PROVIDER_DOC_LINKS[provider.provider_type] const providerLabel = PROVIDER_STATIC_LABELS[provider.provider_type as keyof typeof PROVIDER_STATIC_LABELS] ?? provider.provider_type - // Only show revoke button when in BYOK mode, tenant has custom config, and provider is not active - // (active provider cannot be revoked to prevent "no sandbox provider" error) const showRevokeButton = provider.is_tenant_configured && !provider.is_active && (!shouldShowModeSelection || configMode === 'byok') const isActionDisabled = isSaving || isDeleting || isActivating const showByokForm = !shouldShowModeSelection || configMode === 'byok' return ( - - {/* Header */} -
-

- {t('sandboxProvider.configModal.title', { ns: 'common' })} -

-
- - {providerLabel} -
-
+ !open && onClose()}> + + - {/* Mode Selection */} - {shouldShowModeSelection && ( -
- -
- setConfigMode('managed')} - /> - setConfigMode('byok')} - /> + {/* Header */} +
+

+ {t('sandboxProvider.configModal.title', { ns: 'common' })} +

+
+ + {providerLabel}
- )} - {/* Form fields (hidden when managed mode is selected) */} - {showByokForm && ( - - )} + {/* Mode Selection */} + {shouldShowModeSelection && ( +
+ +
+ setConfigMode('managed')} + /> + setConfigMode('byok')} + /> +
+
+ )} - {/* Footer Actions */} -
- -
- {showRevokeButton && ( - + )} + - )} - - + +
-
- {/* Security tip */} -
- -

- {t('sandboxProvider.configModal.securityTip', { ns: 'common' })} - {' '} - PKCS1_OAEP - {' '} - {t('sandboxProvider.configModal.securityTipTechnology', { ns: 'common' })} -

-
- + {/* Security tip */} +
+ +

+ {t('sandboxProvider.configModal.securityTip', { ns: 'common' })} + {' '} + PKCS1_OAEP + {' '} + {t('sandboxProvider.configModal.securityTipTechnology', { ns: 'common' })} +

+
+ +
) } diff --git a/web/app/components/header/account-setting/sandbox-provider-page/switch-modal.tsx b/web/app/components/header/account-setting/sandbox-provider-page/switch-modal.tsx index 20dc5774ee..2117ce27db 100644 --- a/web/app/components/header/account-setting/sandbox-provider-page/switch-modal.tsx +++ b/web/app/components/header/account-setting/sandbox-provider-page/switch-modal.tsx @@ -4,8 +4,8 @@ import type { SandboxProvider } from '@/types/sandbox-provider' import { memo, useCallback } from 'react' import { Trans, useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' -import Modal from '@/app/components/base/modal' -import { useToastContext } from '@/app/components/base/toast/context' +import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@/app/components/base/ui/dialog' +import { toast } from '@/app/components/base/ui/toast' import { useActivateSandboxProvider } from '@/service/use-sandbox-provider' import { PROVIDER_STATIC_LABELS } from './constants' @@ -19,77 +19,75 @@ const SwitchModal = ({ onClose, }: SwitchModalProps) => { const { t } = useTranslation() - const { notify } = useToastContext() const { mutateAsync: activateProvider, isPending } = useActivateSandboxProvider() const providerLabel = PROVIDER_STATIC_LABELS[provider.provider_type as keyof typeof PROVIDER_STATIC_LABELS] ?? provider.provider_type - // Determine the type based on provider configuration - // If tenant has custom config, activate as 'user', otherwise as 'system' const activationType: 'system' | 'user' = provider.is_tenant_configured ? 'user' : 'system' const handleConfirm = useCallback(async () => { try { await activateProvider({ providerType: provider.provider_type, type: activationType }) - notify({ type: 'success', message: t('api.success', { ns: 'common' }) }) + toast.success(t('api.success', { ns: 'common' })) onClose() } catch { // Error toast is handled by fetch layer } - }, [activateProvider, provider.provider_type, activationType, notify, t, onClose]) + }, [activateProvider, provider.provider_type, activationType, t, onClose]) return ( - -
- {/* Warning Section */} -
-
- {t('sandboxProvider.switchModal.warning', { ns: 'common' })} + !open && onClose()}> + + + + {t('sandboxProvider.switchModal.title', { ns: 'common' })} + + +
+ {/* Warning Section */} +
+
+ {t('sandboxProvider.switchModal.warning', { ns: 'common' })} +
+
+ {t('sandboxProvider.switchModal.warningDesc', { ns: 'common' })} +
-
- {t('sandboxProvider.switchModal.warningDesc', { ns: 'common' })} + + {/* Confirm Text */} +
+ }} + /> +
+ + {/* Footer Actions */} +
+ +
- - {/* Confirm Text */} -
- }} - /> -
- - {/* Footer Actions */} -
- - -
-
- +
+
) } diff --git a/web/app/components/workflow/hooks/use-edges-interactions.ts b/web/app/components/workflow/hooks/use-edges-interactions.ts index f83bfc021f..bc86d05a7f 100644 --- a/web/app/components/workflow/hooks/use-edges-interactions.ts +++ b/web/app/components/workflow/hooks/use-edges-interactions.ts @@ -8,6 +8,8 @@ import type { } from '../types' import { produce } from 'immer' import { useCallback } from 'react' +import { useStoreApi } from 'reactflow' +import { useWorkflowStore } from '../store' import { getNodesConnectedSourceOrTargetHandleIdsMap } from '../utils' import { useCollaborativeWorkflow } from './use-collaborative-workflow' import { useNodesSyncDraft } from './use-nodes-sync-draft' diff --git a/web/app/components/workflow/panel/version-history-panel/index.spec.tsx b/web/app/components/workflow/panel/version-history-panel/index.spec.tsx index 62f45adc61..38836aa410 100644 --- a/web/app/components/workflow/panel/version-history-panel/index.spec.tsx +++ b/web/app/components/workflow/panel/version-history-panel/index.spec.tsx @@ -130,6 +130,7 @@ describe('VersionHistoryPanel', () => { render( `/apps/app-1/workflows/${versionId}/restore`} />, ) @@ -144,6 +145,7 @@ describe('VersionHistoryPanel', () => { render( `/apps/app-1/workflows/${versionId}/restore`} />, ) diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json index e39d21d6ae..66a4359a88 100644 --- a/web/i18n/en-US/workflow.json +++ b/web/i18n/en-US/workflow.json @@ -3,10 +3,12 @@ "blocks.answer": "Answer", "blocks.assigner": "Variable Assigner", "blocks.code": "Code", + "blocks.command": "Command", "blocks.datasource": "Data Source", "blocks.datasource-empty": "Empty Data Source", "blocks.document-extractor": "Doc Extractor", "blocks.end": "Output", + "blocks.file-upload": "File Upload", "blocks.http-request": "HTTP Request", "blocks.human-input": "Human Input", "blocks.if-else": "IF/ELSE", @@ -34,10 +36,12 @@ "blocksAbout.answer": "Define the reply content of a chat conversation", "blocksAbout.assigner": "The variable assignment node is used for assigning values to writable variables(like conversation variables).", "blocksAbout.code": "Execute a piece of Python or NodeJS code to implement custom logic", + "blocksAbout.command": "Execute shell commands in sandbox environment", "blocksAbout.datasource": "Data Source About", "blocksAbout.datasource-empty": "Empty Data Source placeholder", "blocksAbout.document-extractor": "Used to parse uploaded documents into text content that is easily understandable by LLM.", "blocksAbout.end": "Define the output and result type of a workflow", + "blocksAbout.file-upload": "Upload files to sandbox environment for use in workflows", "blocksAbout.http-request": "Allow server requests to be sent over the HTTP protocol", "blocksAbout.human-input": "Ask for human to confirm before generating the next step", "blocksAbout.if-else": "Allows you to split the workflow into two branches based on if/else conditions", @@ -107,6 +111,28 @@ "chatVariable.panelTitle": "Conversation Variables", "chatVariable.storedContent": "Stored content", "chatVariable.updatedAt": "Updated at ", + "collaboration.historyAction.generic": "Action", + "comments.actions.addComment": "Add Comment", + "comments.actions.deleteReply": "Delete Reply", + "comments.actions.editReply": "Edit Reply", + "comments.aria.closeComment": "Close comment", + "comments.aria.deleteComment": "Delete comment", + "comments.aria.nextComment": "Next comment", + "comments.aria.previousComment": "Previous comment", + "comments.aria.replyActions": "Reply actions", + "comments.aria.resolveComment": "Resolve comment", + "comments.confirm.deleteReplyDesc": "Are you sure you want to delete this reply? This action cannot be undone.", + "comments.confirm.deleteReplyTitle": "Delete Reply", + "comments.confirm.deleteThreadDesc": "Are you sure you want to delete this comment thread? This action cannot be undone.", + "comments.confirm.deleteThreadTitle": "Delete Comment Thread", + "comments.fallback.user": "Anonymous User", + "comments.loading": "Loading comments...", + "comments.noComments": "No comments yet", + "comments.panelTitle": "Comments", + "comments.placeholder.add": "Add a comment...", + "comments.placeholder.editReply": "Edit your reply...", + "comments.placeholder.reply": "Reply...", + "comments.reply": "Reply", "common.ImageUploadLegacyTip": "You can now create file type variables in the start form. We will no longer support the image upload feature in the future. ", "common.accessAPIReference": "Access API Reference", "common.addBlock": "Add Node", @@ -118,8 +144,10 @@ "common.backupCurrentDraft": "Backup Current Draft", "common.batchRunApp": "Batch Run App", "common.branch": "BRANCH", + "common.chatflowPublishSuccess": "Chatflow published successfully", "common.chooseDSL": "Choose DSL file", "common.chooseStartNodeToRun": "Choose the start node to run", + "common.commentMode": "Comment Mode", "common.configure": "Configure", "common.configureRequired": "Configure Required", "common.conversationLog": "Conversation Log", @@ -128,6 +156,7 @@ "common.currentDraftUnpublished": "Current Draft Unpublished", "common.currentView": "Current View", "common.currentWorkflow": "Current Workflow", + "common.data": "Data", "common.debugAndPreview": "Preview", "common.disconnect": "Disconnect", "common.duplicate": "Duplicate", @@ -176,6 +205,7 @@ "common.needConnectTip": "This step is not connected to anything", "common.needOutputNode": "The Output node must be added", "common.needStartNode": "At least one start node must be added", + "common.noAgentNodes": "No agent nodes", "common.noHistory": "No History", "common.noVar": "No variable", "common.notRunning": "Not running yet", @@ -196,9 +226,13 @@ "common.previewPlaceholder": "Enter content in the box below to start debugging the Chatbot", "common.processData": "Process Data", "common.publish": "Publish", + "common.publishToMarketplace": "Publish to Marketplace", + "common.publishToMarketplaceFailed": "Failed to publish to marketplace", "common.publishUpdate": "Publish Update", "common.published": "Published", "common.publishedAt": "Published", + "common.publishing": "Publishing...", + "common.publishingToMarketplace": "Publishing to marketplace...", "common.redo": "Redo", "common.restart": "Restart", "common.restore": "Restore", @@ -207,6 +241,7 @@ "common.runApp": "Run App", "common.runHistory": "Run History", "common.running": "Running", + "common.searchAgent": "Search agent...", "common.searchVar": "Search variable", "common.setVarValuePlaceholder": "Set variable", "common.showRunHistory": "Show Run History", @@ -218,12 +253,14 @@ "common.variableNamePlaceholder": "Variable name", "common.versionHistory": "Version History", "common.viewDetailInTracingPanel": "View details", + "common.viewInternals": "View Internals", "common.viewOnly": "View Only", "common.viewRunHistory": "View run history", "common.workflowAsTool": "Workflow as Tool", "common.workflowAsToolDisabledHint": "Publish the latest workflow and ensure a connected User Input node before configuring it as a tool.", "common.workflowAsToolTip": "Tool reconfiguration is required after the workflow update.", "common.workflowProcess": "Workflow Process", + "common.workflowPublishSuccess": "Workflow published successfully", "customWebhook": "Custom Webhook", "debug.copyLastRun": "Copy Last Run", "debug.copyLastRunError": "Failed to copy last run inputs", @@ -266,6 +303,13 @@ "debug.variableInspect.reset": "Reset to last run value", "debug.variableInspect.resetConversationVar": "Reset conversation variable to default value", "debug.variableInspect.systemNode": "System", + "debug.variableInspect.tab.artifacts": "Artifacts", + "debug.variableInspect.tab.variables": "Variables", + "debug.variableInspect.tabArtifacts.emptyLink": "Learn more", + "debug.variableInspect.tabArtifacts.emptyTip": "No artifacts generated yet. Run a node to generate artifacts.", + "debug.variableInspect.tabArtifacts.emptyTitle": "No Artifacts", + "debug.variableInspect.tabArtifacts.previewNotAvailable": "Preview not available", + "debug.variableInspect.tabArtifacts.selectFile": "Select a file to preview", "debug.variableInspect.title": "Variable Inspect", "debug.variableInspect.trigger.cached": "View cached variables", "debug.variableInspect.trigger.clear": "Clear", @@ -412,6 +456,14 @@ "nodes.code.outputVars": "Output Variables", "nodes.code.searchDependencies": "Search Dependencies", "nodes.code.syncFunctionSignature": "Sync function signature to code", + "nodes.command.command": "Command", + "nodes.command.commandPlaceholder": "Enter command to execute...", + "nodes.command.outputVars.exitCode": "Exit Code", + "nodes.command.outputVars.pid": "Process ID", + "nodes.command.outputVars.stderr": "Standard Error", + "nodes.command.outputVars.stdout": "Standard Output", + "nodes.command.workingDirectory": "Working Directory", + "nodes.command.workingDirectoryPlaceholder": "Enter working directory path...", "nodes.common.errorHandle.defaultValue.desc": "When an error occurs, specify a static output content.", "nodes.common.errorHandle.defaultValue.inLog": "Node exception, outputting according to default values.", "nodes.common.errorHandle.defaultValue.output": "Output Default Value", @@ -468,6 +520,9 @@ "nodes.end.type.none": "None", "nodes.end.type.plain-text": "Plain Text", "nodes.end.type.structured": "Structured", + "nodes.fileUpload.inputVar": "Input Variable", + "nodes.fileUpload.outputVars.fileName": "File Name", + "nodes.fileUpload.outputVars.sandboxPath": "Sandbox Path", "nodes.http.api": "API", "nodes.http.apiPlaceholder": "Enter URL, type ‘/’ insert variable", "nodes.http.authorization.api-key": "API-Key", @@ -731,9 +786,22 @@ "nodes.listFilter.outputVars.last_record": "Last record", "nodes.listFilter.outputVars.result": "Filter result", "nodes.listFilter.selectVariableKeyPlaceholder": "Select sub variable key", + "nodes.llm.addContext": "Add Context", "nodes.llm.addMessage": "Add Message", + "nodes.llm.advancedSettings": "Advanced Settings", + "nodes.llm.chatHistorry": "Chat History", + "nodes.llm.computerUse.disabledByStructuredOutput": "Computer use is disabled when structured output is enabled", + "nodes.llm.computerUse.dismiss": "Dismiss", + "nodes.llm.computerUse.enable": "Enable Computer Use", + "nodes.llm.computerUse.enableForPromptTools": "Enable for Prompt Tools", + "nodes.llm.computerUse.referenceTools": "Reference Tools", + "nodes.llm.computerUse.referenceToolsEmpty": "No reference tools available", + "nodes.llm.computerUse.title": "Computer Use", + "nodes.llm.computerUse.tooltip": "Enable the model to interact with a computer desktop environment", "nodes.llm.context": "context", + "nodes.llm.contextMissing": "Context is missing", "nodes.llm.contextTooltip": "You can import Knowledge as context", + "nodes.llm.contextUnknownNode": "Unknown node", "nodes.llm.files": "Files", "nodes.llm.jsonSchema.addChildField": "Add Child Field", "nodes.llm.jsonSchema.addField": "Add Field", @@ -761,6 +829,8 @@ "nodes.llm.jsonSchema.warningTips.saveSchema": "Please finish editing the current field before saving the schema", "nodes.llm.model": "model", "nodes.llm.notSetContextInPromptTip": "To enable the context feature, please fill in the context variable in PROMPT.", + "nodes.llm.outputVars.files": "Generated Files", + "nodes.llm.outputVars.generation": "Generated Content", "nodes.llm.outputVars.output": "Generate content", "nodes.llm.outputVars.reasoning_content": "Reasoning Content", "nodes.llm.outputVars.usage": "Model Usage Information", @@ -769,6 +839,7 @@ "nodes.llm.reasoningFormat.tagged": "Keep think tags", "nodes.llm.reasoningFormat.title": "Enable reasoning tag separation", "nodes.llm.reasoningFormat.tooltip": "Extract content from think tags and store it in the reasoning_content field.", + "nodes.llm.removeContext": "Remove Context", "nodes.llm.resolution.high": "High", "nodes.llm.resolution.low": "Low", "nodes.llm.resolution.name": "Resolution", @@ -777,6 +848,9 @@ "nodes.llm.roleDescription.user": "Provide instructions, queries, or any text-based input to the model", "nodes.llm.singleRun.variable": "Variable", "nodes.llm.sysQueryInUser": "sys.query in user message is required", + "nodes.llm.tools.disabledByStructuredOutput": "Tools are disabled when structured output is enabled", + "nodes.llm.tools.title": "Tools", + "nodes.llm.tools.tooltip": "Add tools for the LLM to use during generation", "nodes.llm.variables": "variables", "nodes.llm.vision": "vision", "nodes.loop.ErrorMethod.continueOnError": "Continue on Error", @@ -869,11 +943,29 @@ "nodes.templateTransform.codeSupportTip": "Only supports Jinja2", "nodes.templateTransform.inputVars": "Input Variables", "nodes.templateTransform.outputVars.output": "Transformed content", + "nodes.tool.agentPlaceholder": "Select an agent...", + "nodes.tool.agentPopupHeader": "Select Agent", + "nodes.tool.assembleVariables": "Assemble Variables", "nodes.tool.authorizationRequired": "Authorization required", "nodes.tool.authorize": "Authorize", + "nodes.tool.contextGenerate.apply": "Apply", + "nodes.tool.contextGenerate.defaultAssistantMessage": "I'll help you generate the context code.", + "nodes.tool.contextGenerate.generatedCode": "Generated Code", + "nodes.tool.contextGenerate.generating": "Generating...", + "nodes.tool.contextGenerate.initPlaceholder": "Describe what you want to generate...", + "nodes.tool.contextGenerate.inputPlaceholder": "Type your message...", + "nodes.tool.contextGenerate.output": "Output", + "nodes.tool.contextGenerate.resizeHandle": "Resize", + "nodes.tool.contextGenerate.rightSidePlaceholder": "Generated content will appear here", + "nodes.tool.contextGenerate.run": "Run", + "nodes.tool.contextGenerate.running": "Running...", + "nodes.tool.contextGenerate.subtitle": "Use AI to generate context code", + "nodes.tool.contextGenerate.suggestedQuestionsTitle": "Suggested Questions", + "nodes.tool.contextGenerate.title": "Context Generate", "nodes.tool.inputVars": "Input Variables", "nodes.tool.insertPlaceholder1": "Type or press", "nodes.tool.insertPlaceholder2": "insert variable", + "nodes.tool.insertPlaceholder3": "to insert variable", "nodes.tool.outputVars.files.title": "tool generated files", "nodes.tool.outputVars.files.transfer_method": "Transfer method.Value is remote_url or local_file", "nodes.tool.outputVars.files.type": "Support type. Now only support image", @@ -1049,6 +1141,9 @@ "operator.distributeVertical": "Space Vertically", "operator.horizontal": "Horizontal", "operator.selectionAlignment": "Selection Alignment", + "operator.showMiniMap": "Show Mini Map", + "operator.showUserComments": "Show User Comments", + "operator.showUserCursors": "Show User Cursors", "operator.vertical": "Vertical", "operator.zoomIn": "Zoom In", "operator.zoomOut": "Zoom Out", @@ -1082,6 +1177,13 @@ "publishLimit.startNodeDesc": "You’ve reached the limit of 2 triggers per workflow for this plan. Upgrade to publish this workflow.", "publishLimit.startNodeTitlePrefix": "Upgrade to", "publishLimit.startNodeTitleSuffix": "unlock unlimited triggers per workflow", + "sandboxMigrationModal.bannerHint": "Your workflow runtime has been upgraded. Some features may require attention.", + "sandboxMigrationModal.description": "Your workflow has been migrated to a new sandbox environment.", + "sandboxMigrationModal.dismiss": "Dismiss", + "sandboxMigrationModal.title": "Sandbox Migration", + "sandboxMigrationModal.upgrade": "Upgrade", + "sandboxMigrationModal.upgradedFrom": "Upgraded from {{version}}", + "sandboxMigrationModal.viewOriginal": "View Original", "sidebar.exportWarning": "Export Current Saved Version", "sidebar.exportWarningDesc": "This will export the current saved version of your workflow. If you have unsaved changes in the editor, please save them first by using the export option in the workflow canvas.", "singleRun.back": "Back", @@ -1091,9 +1193,129 @@ "singleRun.reRun": "Re-run", "singleRun.running": "Running", "singleRun.startRun": "Start Run", + "singleRun.subgraph.nullOutputError": "Subgraph returned null output", "singleRun.testRun": "Test Run", "singleRun.testRunIteration": "Test Run Iteration", "singleRun.testRunLoop": "Test Run Loop", + "skill.startTab.createBlankSkill": "Create Blank Skill", + "skill.startTab.createBlankSkillDesc": "Start from scratch with an empty skill", + "skill.startTab.createError": "Failed to create skill", + "skill.startTab.createModal.nameDuplicate": "A skill with this name already exists", + "skill.startTab.createModal.nameLabel": "Skill Name", + "skill.startTab.createModal.namePlaceholder": "Enter skill name...", + "skill.startTab.createModal.title": "Create New Skill", + "skill.startTab.createSuccess": "Skill created successfully", + "skill.startTab.filesIncluded": "{{count}} files included", + "skill.startTab.importModal.browseFiles": "Browse Files", + "skill.startTab.importModal.changeFile": "Change File", + "skill.startTab.importModal.dropHint": "Drop your file here", + "skill.startTab.importModal.errorEmptyZip": "The zip file is empty", + "skill.startTab.importModal.errorExtractedTooLarge": "Extracted content is too large", + "skill.startTab.importModal.errorInvalidZip": "Invalid zip file", + "skill.startTab.importModal.errorNoRootFolder": "No root folder found in zip", + "skill.startTab.importModal.errorPathTraversal": "Path traversal detected in zip file", + "skill.startTab.importModal.errorTooManyFiles": "Too many files in archive", + "skill.startTab.importModal.fileTooLarge": "File is too large", + "skill.startTab.importModal.importButton": "Import", + "skill.startTab.importModal.importSuccess": "Skill imported successfully", + "skill.startTab.importModal.invalidFileType": "Invalid file type. Please upload a .zip file.", + "skill.startTab.importModal.nameDuplicate": "A skill with this name already exists", + "skill.startTab.importModal.title": "Import Skill", + "skill.startTab.importSkill": "Import Skill", + "skill.startTab.importSkillDesc": "Import a skill from a zip file", + "skill.startTab.noTemplatesFound": "No templates found", + "skill.startTab.searchPlaceholder": "Search templates...", + "skill.startTab.skillAdded": "Skill Added", + "skill.startTab.templatesDesc": "Choose a template to get started quickly", + "skill.startTab.templatesTitle": "Skill Templates", + "skill.startTab.useThisSkill": "Use This Skill", + "skillEditor.addFiles": "Add Files", + "skillEditor.authorizationBadge": "Authorization Required", + "skillEditor.authorizationRequired": "Authorization required to use this tool", + "skillEditor.fileNotFound": "File not found", + "skillEditor.folderNotFound": "Folder not found", + "skillEditor.openInSkillEditor": "Open in Skill Editor", + "skillEditor.previewUnavailable": "Preview unavailable", + "skillEditor.referenceFiles": "Reference Files", + "skillEditor.toolMissing": "Tool Missing", + "skillEditor.toolMissingDesc": "The referenced tool is missing or has been removed", + "skillEditor.unsupportedPreview": "Unsupported file type for preview", + "skillEditor.uploadFiles": "Upload Files", + "skillEditor.uploadIn": "Upload in", + "skillSidebar.artifacts.emptyState": "No artifacts yet", + "skillSidebar.artifacts.openArtifacts": "Open Artifacts", + "skillSidebar.artifacts.title": "Artifacts", + "skillSidebar.dragAction.moveTo": "Move to {{target}}", + "skillSidebar.dragAction.uploadTo": "Upload to {{target}}", + "skillSidebar.dropTip": "Drop files here to upload", + "skillSidebar.empty": "No files yet", + "skillSidebar.fileNamePlaceholder": "Enter file name...", + "skillSidebar.folderNamePlaceholder": "Enter folder name...", + "skillSidebar.loadError": "Failed to load files", + "skillSidebar.menu.cannotMoveToSelf": "Cannot move item to itself", + "skillSidebar.menu.createError": "Failed to create", + "skillSidebar.menu.cut": "Cut", + "skillSidebar.menu.delete": "Delete", + "skillSidebar.menu.deleteConfirmContent": "Are you sure you want to delete this folder and all its contents?", + "skillSidebar.menu.deleteConfirmTitle": "Delete Folder", + "skillSidebar.menu.deleteError": "Failed to delete", + "skillSidebar.menu.deleted": "Deleted successfully", + "skillSidebar.menu.download": "Download", + "skillSidebar.menu.downloadError": "Failed to download", + "skillSidebar.menu.fileCreated": "File created", + "skillSidebar.menu.fileDeleteConfirmContent": "Are you sure you want to delete this file?", + "skillSidebar.menu.fileDeleteConfirmTitle": "Delete File", + "skillSidebar.menu.fileDeleteError": "Failed to delete file", + "skillSidebar.menu.fileDeleted": "File deleted", + "skillSidebar.menu.filesUploaded": "Files uploaded successfully", + "skillSidebar.menu.folderCreated": "Folder created", + "skillSidebar.menu.folderDropNotSupported": "Folder drop is not supported", + "skillSidebar.menu.importSkills": "Import Skills", + "skillSidebar.menu.moreActions": "More Actions", + "skillSidebar.menu.moveError": "Failed to move", + "skillSidebar.menu.moved": "Moved successfully", + "skillSidebar.menu.newFile": "New File", + "skillSidebar.menu.newFolder": "New Folder", + "skillSidebar.menu.paste": "Paste", + "skillSidebar.menu.rename": "Rename", + "skillSidebar.menu.renameError": "Failed to rename", + "skillSidebar.menu.renamed": "Renamed successfully", + "skillSidebar.menu.uploadError": "Failed to upload", + "skillSidebar.menu.uploadFile": "Upload File", + "skillSidebar.menu.uploadFolder": "Upload Folder", + "skillSidebar.renameFileInput": "Enter new file name", + "skillSidebar.renameFolderInput": "Enter new folder name", + "skillSidebar.resetFilter": "Reset Filter", + "skillSidebar.rootFolder": "Root", + "skillSidebar.searchNoResults": "No results found", + "skillSidebar.searchPlaceholder": "Search files...", + "skillSidebar.sqlitePreview.blobValue": "BLOB ({{size}} bytes)", + "skillSidebar.sqlitePreview.emptyRows": "No rows found", + "skillSidebar.sqlitePreview.emptyTables": "No tables found", + "skillSidebar.sqlitePreview.loadError": "Failed to load SQLite data", + "skillSidebar.sqlitePreview.nullValue": "NULL", + "skillSidebar.sqlitePreview.rowsTruncated": "Showing first {{limit}} rows", + "skillSidebar.sqlitePreview.selectTable": "Select a table", + "skillSidebar.startTab": "Start", + "skillSidebar.toggleFolder": "Toggle folder", + "skillSidebar.unsavedChanges.confirmClose": "Close without saving", + "skillSidebar.unsavedChanges.content": "You have unsaved changes. Are you sure you want to close?", + "skillSidebar.unsavedChanges.title": "Unsaved Changes", + "skillSidebar.uploadPartialError": "Some files failed to upload", + "skillSidebar.uploadPartialErrorDetail": "{{failed}} of {{total}} files failed to upload", + "skillSidebar.uploadSuccess": "Upload successful", + "skillSidebar.uploadSuccessDetail": "{{count}} files uploaded", + "skillSidebar.uploadingItems": "Uploading {{count}} items...", + "subGraphModal.internalStructure": "Internal Structure", + "subGraphModal.internalStructureDesc": "View the internal workflow structure of this sub-graph", + "subGraphModal.noRunHistory": "No run history", + "subGraphModal.outputVariables": "Output Variables", + "subGraphModal.title": "Sub-Graph Details", + "subGraphModal.whenOutputIsNone": "When Output is None", + "subGraphModal.whenOutputNone.default": "Use Default", + "subGraphModal.whenOutputNone.defaultDesc": "Use a default value when sub-graph output is null", + "subGraphModal.whenOutputNone.error": "Throw Error", + "subGraphModal.whenOutputNone.errorDesc": "Throw an error when sub-graph output is null", "tabs.-": "Default", "tabs.addAll": "Add all", "tabs.agent": "Agent Strategy", @@ -1128,6 +1350,8 @@ "tabs.usePlugin": "Select tool", "tabs.utilities": "Utilities", "tabs.workflowTool": "Workflow", + "toolGroup.actionsEnabled": "{{count}} actions enabled", + "toolGroup.byAuthor": "By {{author}}", "tracing.stopBy": "Stop by {{user}}", "triggerStatus.disabled": "TRIGGER • DISABLED", "triggerStatus.enabled": "TRIGGER", @@ -1140,6 +1364,7 @@ "versionHistory.action.deleteFailure": "Failed to delete version", "versionHistory.action.deleteSuccess": "Version deleted", "versionHistory.action.restoreFailure": "Failed to restore version", + "versionHistory.action.restoreInProgress": "Restoring...", "versionHistory.action.restoreSuccess": "Version restored", "versionHistory.action.updateFailure": "Failed to update version", "versionHistory.action.updateSuccess": "Version updated", @@ -1154,8 +1379,6 @@ "versionHistory.editVersionInfo": "Edit version info", "versionHistory.filter.all": "All", "versionHistory.filter.empty": "No matching version history found", - "viewPicker.graph": "Workflow", - "viewPicker.skill": "Skill", "versionHistory.filter.onlyShowNamedVersions": "Only show named versions", "versionHistory.filter.onlyYours": "Only yours", "versionHistory.filter.reset": "Reset Filter", @@ -1163,5 +1386,7 @@ "versionHistory.nameThisVersion": "Name this version", "versionHistory.releaseNotesPlaceholder": "Describe what changed", "versionHistory.restorationTip": "After version restoration, the current draft will be overwritten.", - "versionHistory.title": "Versions" + "versionHistory.title": "Versions", + "viewPicker.graph": "Workflow", + "viewPicker.skill": "Skill" } diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json index 4d3b42c9fe..7e74f11f8b 100644 --- a/web/i18n/zh-Hans/workflow.json +++ b/web/i18n/zh-Hans/workflow.json @@ -3,10 +3,12 @@ "blocks.answer": "直接回复", "blocks.assigner": "变量赋值", "blocks.code": "代码执行", + "blocks.command": "命令", "blocks.datasource": "数据源", "blocks.datasource-empty": "空数据源", "blocks.document-extractor": "文档提取器", "blocks.end": "输出", + "blocks.file-upload": "文件上传", "blocks.http-request": "HTTP 请求", "blocks.human-input": "人工介入", "blocks.if-else": "条件分支", @@ -34,10 +36,12 @@ "blocksAbout.answer": "定义一个聊天对话的回复内容", "blocksAbout.assigner": "变量赋值节点用于向可写入变量(例如会话变量)进行变量赋值。", "blocksAbout.code": "执行一段 Python 或 NodeJS 代码实现自定义逻辑", + "blocksAbout.command": "在沙盒环境中执行 Shell 命令", "blocksAbout.datasource": "数据源节点", "blocksAbout.datasource-empty": "空数据源占位符", "blocksAbout.document-extractor": "用于将用户上传的文档解析为 LLM 便于理解的文本内容。", "blocksAbout.end": "定义一个 workflow 流程的输出和结果类型", + "blocksAbout.file-upload": "上传文件到沙盒环境以在工作流中使用", "blocksAbout.http-request": "允许通过 HTTP 协议发送服务器请求", "blocksAbout.human-input": "人工输入,确认后生成下一步", "blocksAbout.if-else": "允许你根据 if/else 条件将 workflow 拆分成两个分支", @@ -107,6 +111,28 @@ "chatVariable.panelTitle": "会话变量", "chatVariable.storedContent": "存储内容", "chatVariable.updatedAt": "更新时间 ", + "collaboration.historyAction.generic": "操作", + "comments.actions.addComment": "添加评论", + "comments.actions.deleteReply": "删除回复", + "comments.actions.editReply": "编辑回复", + "comments.aria.closeComment": "关闭评论", + "comments.aria.deleteComment": "删除评论", + "comments.aria.nextComment": "下一条评论", + "comments.aria.previousComment": "上一条评论", + "comments.aria.replyActions": "回复操作", + "comments.aria.resolveComment": "解决评论", + "comments.confirm.deleteReplyDesc": "确定要删除此回复吗?此操作无法撤销。", + "comments.confirm.deleteReplyTitle": "删除回复", + "comments.confirm.deleteThreadDesc": "确定要删除此评论主题吗?此操作无法撤销。", + "comments.confirm.deleteThreadTitle": "删除评论主题", + "comments.fallback.user": "匿名用户", + "comments.loading": "加载评论中...", + "comments.noComments": "暂无评论", + "comments.panelTitle": "评论", + "comments.placeholder.add": "添加评论...", + "comments.placeholder.editReply": "编辑回复...", + "comments.placeholder.reply": "回复...", + "comments.reply": "回复", "common.ImageUploadLegacyTip": "现在可以在 start 表单中创建文件类型变量。未来我们将不继续支持图片上传功能。", "common.accessAPIReference": "访问 API", "common.addBlock": "添加节点", @@ -118,8 +144,10 @@ "common.backupCurrentDraft": "备份当前草稿", "common.batchRunApp": "批量运行", "common.branch": "分支", + "common.chatflowPublishSuccess": "Chatflow 发布成功", "common.chooseDSL": "选择 DSL(yml) 文件", "common.chooseStartNodeToRun": "选择启动节点进行运行", + "common.commentMode": "评论模式", "common.configure": "配置", "common.configureRequired": "需要进行配置", "common.conversationLog": "对话记录", @@ -128,6 +156,7 @@ "common.currentDraftUnpublished": "当前草稿未发布", "common.currentView": "当前视图", "common.currentWorkflow": "整个工作流", + "common.data": "数据", "common.debugAndPreview": "预览", "common.disconnect": "断开连接", "common.duplicate": "复制", @@ -176,6 +205,7 @@ "common.needConnectTip": "此节点尚未连接到其他节点", "common.needOutputNode": "必须添加输出节点", "common.needStartNode": "必须添加至少一个开始节点", + "common.noAgentNodes": "无 Agent 节点", "common.noHistory": "没有历史版本", "common.noVar": "没有变量", "common.notRunning": "尚未运行", @@ -196,9 +226,13 @@ "common.previewPlaceholder": "在下面的框中输入内容开始调试聊天机器人", "common.processData": "数据处理", "common.publish": "发布", + "common.publishToMarketplace": "发布到市场", + "common.publishToMarketplaceFailed": "发布到市场失败", "common.publishUpdate": "发布更新", "common.published": "已发布", "common.publishedAt": "发布于", + "common.publishing": "发布中...", + "common.publishingToMarketplace": "发布到市场中...", "common.redo": "重做", "common.restart": "重新开始", "common.restore": "恢复", @@ -207,6 +241,7 @@ "common.runApp": "运行", "common.runHistory": "运行历史", "common.running": "运行中", + "common.searchAgent": "搜索 Agent...", "common.searchVar": "搜索变量", "common.setVarValuePlaceholder": "设置变量值", "common.showRunHistory": "显示运行历史", @@ -218,12 +253,14 @@ "common.variableNamePlaceholder": "变量名", "common.versionHistory": "版本历史", "common.viewDetailInTracingPanel": "查看详细信息", + "common.viewInternals": "查看内部结构", "common.viewOnly": "只读", "common.viewRunHistory": "查看运行历史", "common.workflowAsTool": "发布为工具", "common.workflowAsToolDisabledHint": "请先发布最新的工作流,并确保已连接的 User Input 节点后再配置为工具。", "common.workflowAsToolTip": "工作流更新后需要重新配置工具参数", "common.workflowProcess": "工作流", + "common.workflowPublishSuccess": "工作流发布成功", "customWebhook": "自定义 Webhook", "debug.copyLastRun": "复制上次运行值", "debug.copyLastRunError": "复制上次运行输入失败", @@ -266,6 +303,13 @@ "debug.variableInspect.reset": "还原至上一次运行", "debug.variableInspect.resetConversationVar": "重置会话变量为默认值", "debug.variableInspect.systemNode": "系统变量", + "debug.variableInspect.tab.artifacts": "产物", + "debug.variableInspect.tab.variables": "变量", + "debug.variableInspect.tabArtifacts.emptyLink": "了解更多", + "debug.variableInspect.tabArtifacts.emptyTip": "尚未生成产物。运行节点以生成产物。", + "debug.variableInspect.tabArtifacts.emptyTitle": "暂无产物", + "debug.variableInspect.tabArtifacts.previewNotAvailable": "预览不可用", + "debug.variableInspect.tabArtifacts.selectFile": "选择文件以预览", "debug.variableInspect.title": "变量检查", "debug.variableInspect.trigger.cached": "查看缓存", "debug.variableInspect.trigger.clear": "清除", @@ -412,6 +456,14 @@ "nodes.code.outputVars": "输出变量", "nodes.code.searchDependencies": "搜索依赖", "nodes.code.syncFunctionSignature": "同步函数签名至代码", + "nodes.command.command": "命令", + "nodes.command.commandPlaceholder": "输入要执行的命令...", + "nodes.command.outputVars.exitCode": "退出码", + "nodes.command.outputVars.pid": "进程 ID", + "nodes.command.outputVars.stderr": "标准错误", + "nodes.command.outputVars.stdout": "标准输出", + "nodes.command.workingDirectory": "工作目录", + "nodes.command.workingDirectoryPlaceholder": "输入工作目录路径...", "nodes.common.errorHandle.defaultValue.desc": "当发生异常时,指定默认输出内容。", "nodes.common.errorHandle.defaultValue.inLog": "节点异常,根据默认值输出。", "nodes.common.errorHandle.defaultValue.output": "输出默认值", @@ -468,6 +520,9 @@ "nodes.end.type.none": "无", "nodes.end.type.plain-text": "纯文本", "nodes.end.type.structured": "结构化", + "nodes.fileUpload.inputVar": "输入变量", + "nodes.fileUpload.outputVars.fileName": "文件名", + "nodes.fileUpload.outputVars.sandboxPath": "沙盒路径", "nodes.http.api": "API", "nodes.http.apiPlaceholder": "输入 URL,输入变量时请键入‘/’", "nodes.http.authorization.api-key": "API-Key", @@ -731,9 +786,22 @@ "nodes.listFilter.outputVars.last_record": "最后一条记录", "nodes.listFilter.outputVars.result": "过滤结果", "nodes.listFilter.selectVariableKeyPlaceholder": "选择子变量的 Key", + "nodes.llm.addContext": "添加上下文", "nodes.llm.addMessage": "添加消息", + "nodes.llm.advancedSettings": "高级设置", + "nodes.llm.chatHistorry": "对话历史", + "nodes.llm.computerUse.disabledByStructuredOutput": "启用结构化输出时不可使用计算机操作", + "nodes.llm.computerUse.dismiss": "关闭", + "nodes.llm.computerUse.enable": "启用计算机操作", + "nodes.llm.computerUse.enableForPromptTools": "为 Prompt 工具启用", + "nodes.llm.computerUse.referenceTools": "引用工具", + "nodes.llm.computerUse.referenceToolsEmpty": "无可用引用工具", + "nodes.llm.computerUse.title": "计算机操作", + "nodes.llm.computerUse.tooltip": "允许模型与计算机桌面环境交互", "nodes.llm.context": "上下文", + "nodes.llm.contextMissing": "缺少上下文", "nodes.llm.contextTooltip": "您可以导入知识库作为上下文", + "nodes.llm.contextUnknownNode": "未知节点", "nodes.llm.files": "文件", "nodes.llm.jsonSchema.addChildField": "添加子字段", "nodes.llm.jsonSchema.addField": "添加字段", @@ -761,6 +829,8 @@ "nodes.llm.jsonSchema.warningTips.saveSchema": "请先完成当前字段的编辑", "nodes.llm.model": "模型", "nodes.llm.notSetContextInPromptTip": "要启用上下文功能,请在提示中填写上下文变量。", + "nodes.llm.outputVars.files": "生成的文件", + "nodes.llm.outputVars.generation": "生成内容", "nodes.llm.outputVars.output": "生成内容", "nodes.llm.outputVars.reasoning_content": "推理内容", "nodes.llm.outputVars.usage": "模型用量信息", @@ -769,6 +839,7 @@ "nodes.llm.reasoningFormat.tagged": "保持思考标签", "nodes.llm.reasoningFormat.title": "启用推理标签分离", "nodes.llm.reasoningFormat.tooltip": "从think标签中提取内容,并将其存储在reasoning_content字段中。", + "nodes.llm.removeContext": "移除上下文", "nodes.llm.resolution.high": "高", "nodes.llm.resolution.low": "低", "nodes.llm.resolution.name": "分辨率", @@ -777,6 +848,9 @@ "nodes.llm.roleDescription.user": "向模型提供指令、查询或任何基于文本的输入", "nodes.llm.singleRun.variable": "变量", "nodes.llm.sysQueryInUser": "user message 中必须包含 sys.query", + "nodes.llm.tools.disabledByStructuredOutput": "启用结构化输出时不可使用工具", + "nodes.llm.tools.title": "工具", + "nodes.llm.tools.tooltip": "添加 LLM 在生成过程中使用的工具", "nodes.llm.variables": "变量", "nodes.llm.vision": "视觉", "nodes.loop.ErrorMethod.continueOnError": "忽略错误并继续", @@ -869,11 +943,29 @@ "nodes.templateTransform.codeSupportTip": "只支持 Jinja2", "nodes.templateTransform.inputVars": "输入变量", "nodes.templateTransform.outputVars.output": "转换后内容", + "nodes.tool.agentPlaceholder": "选择 Agent...", + "nodes.tool.agentPopupHeader": "选择 Agent", + "nodes.tool.assembleVariables": "组装变量", "nodes.tool.authorizationRequired": "需要授权", "nodes.tool.authorize": "授权", + "nodes.tool.contextGenerate.apply": "应用", + "nodes.tool.contextGenerate.defaultAssistantMessage": "我将帮你生成上下文代码。", + "nodes.tool.contextGenerate.generatedCode": "生成的代码", + "nodes.tool.contextGenerate.generating": "生成中...", + "nodes.tool.contextGenerate.initPlaceholder": "描述你想要生成的内容...", + "nodes.tool.contextGenerate.inputPlaceholder": "输入消息...", + "nodes.tool.contextGenerate.output": "输出", + "nodes.tool.contextGenerate.resizeHandle": "调整大小", + "nodes.tool.contextGenerate.rightSidePlaceholder": "生成的内容将显示在这里", + "nodes.tool.contextGenerate.run": "运行", + "nodes.tool.contextGenerate.running": "运行中...", + "nodes.tool.contextGenerate.subtitle": "使用 AI 生成上下文代码", + "nodes.tool.contextGenerate.suggestedQuestionsTitle": "推荐问题", + "nodes.tool.contextGenerate.title": "上下文生成", "nodes.tool.inputVars": "输入变量", "nodes.tool.insertPlaceholder1": "键入", "nodes.tool.insertPlaceholder2": "插入变量", + "nodes.tool.insertPlaceholder3": "插入变量", "nodes.tool.outputVars.files.title": "工具生成的文件", "nodes.tool.outputVars.files.transfer_method": "传输方式。值为 remote_url 或 local_file", "nodes.tool.outputVars.files.type": "支持类型。现在只支持图片", @@ -1049,6 +1141,9 @@ "operator.distributeVertical": "垂直等间距", "operator.horizontal": "水平方向", "operator.selectionAlignment": "选择对齐", + "operator.showMiniMap": "显示小地图", + "operator.showUserComments": "显示用户评论", + "operator.showUserCursors": "显示用户光标", "operator.vertical": "垂直方向", "operator.zoomIn": "放大", "operator.zoomOut": "缩小", @@ -1082,6 +1177,13 @@ "publishLimit.startNodeDesc": "您已达到此计划上每个工作流最多 2 个触发器的限制。请升级后再发布此工作流。", "publishLimit.startNodeTitlePrefix": "升级以", "publishLimit.startNodeTitleSuffix": "解锁每个工作流无限制的触发器", + "sandboxMigrationModal.bannerHint": "您的工作流运行时已升级。部分功能可能需要关注。", + "sandboxMigrationModal.description": "您的工作流已迁移至新的沙盒环境。", + "sandboxMigrationModal.dismiss": "关闭", + "sandboxMigrationModal.title": "沙盒迁移", + "sandboxMigrationModal.upgrade": "升级", + "sandboxMigrationModal.upgradedFrom": "已从 {{version}} 升级", + "sandboxMigrationModal.viewOriginal": "查看原始版本", "sidebar.exportWarning": "导出当前已保存版本", "sidebar.exportWarningDesc": "这将导出您工作流的当前已保存版本。如果您在编辑器中有未保存的更改,请先使用工作流画布中的导出选项保存它们。", "singleRun.back": "返回", @@ -1091,9 +1193,129 @@ "singleRun.reRun": "重新运行", "singleRun.running": "运行中", "singleRun.startRun": "开始运行", + "singleRun.subgraph.nullOutputError": "子图返回了空输出", "singleRun.testRun": "测试运行", "singleRun.testRunIteration": "测试运行迭代", "singleRun.testRunLoop": "测试运行循环", + "skill.startTab.createBlankSkill": "创建空白技能", + "skill.startTab.createBlankSkillDesc": "从空白技能开始", + "skill.startTab.createError": "创建技能失败", + "skill.startTab.createModal.nameDuplicate": "同名技能已存在", + "skill.startTab.createModal.nameLabel": "技能名称", + "skill.startTab.createModal.namePlaceholder": "输入技能名称...", + "skill.startTab.createModal.title": "创建新技能", + "skill.startTab.createSuccess": "技能创建成功", + "skill.startTab.filesIncluded": "包含 {{count}} 个文件", + "skill.startTab.importModal.browseFiles": "浏览文件", + "skill.startTab.importModal.changeFile": "更换文件", + "skill.startTab.importModal.dropHint": "将文件拖放到此处", + "skill.startTab.importModal.errorEmptyZip": "压缩包为空", + "skill.startTab.importModal.errorExtractedTooLarge": "解压内容过大", + "skill.startTab.importModal.errorInvalidZip": "无效的压缩包", + "skill.startTab.importModal.errorNoRootFolder": "压缩包中未找到根目录", + "skill.startTab.importModal.errorPathTraversal": "压缩包中检测到路径遍历", + "skill.startTab.importModal.errorTooManyFiles": "压缩包中文件过多", + "skill.startTab.importModal.fileTooLarge": "文件过大", + "skill.startTab.importModal.importButton": "导入", + "skill.startTab.importModal.importSuccess": "技能导入成功", + "skill.startTab.importModal.invalidFileType": "无效的文件类型。请上传 .zip 文件。", + "skill.startTab.importModal.nameDuplicate": "同名技能已存在", + "skill.startTab.importModal.title": "导入技能", + "skill.startTab.importSkill": "导入技能", + "skill.startTab.importSkillDesc": "从压缩包导入技能", + "skill.startTab.noTemplatesFound": "未找到模板", + "skill.startTab.searchPlaceholder": "搜索模板...", + "skill.startTab.skillAdded": "已添加技能", + "skill.startTab.templatesDesc": "选择模板快速开始", + "skill.startTab.templatesTitle": "技能模板", + "skill.startTab.useThisSkill": "使用该技能", + "skillEditor.addFiles": "添加文件", + "skillEditor.authorizationBadge": "需要授权", + "skillEditor.authorizationRequired": "使用此工具需要授权", + "skillEditor.fileNotFound": "文件未找到", + "skillEditor.folderNotFound": "文件夹未找到", + "skillEditor.openInSkillEditor": "在技能编辑器中打开", + "skillEditor.previewUnavailable": "预览不可用", + "skillEditor.referenceFiles": "引用文件", + "skillEditor.toolMissing": "工具缺失", + "skillEditor.toolMissingDesc": "引用的工具缺失或已被移除", + "skillEditor.unsupportedPreview": "不支持预览的文件类型", + "skillEditor.uploadFiles": "上传文件", + "skillEditor.uploadIn": "上传至", + "skillSidebar.artifacts.emptyState": "暂无产物", + "skillSidebar.artifacts.openArtifacts": "打开产物", + "skillSidebar.artifacts.title": "产物", + "skillSidebar.dragAction.moveTo": "移动到 {{target}}", + "skillSidebar.dragAction.uploadTo": "上传到 {{target}}", + "skillSidebar.dropTip": "将文件拖放到此处以上传", + "skillSidebar.empty": "暂无文件", + "skillSidebar.fileNamePlaceholder": "输入文件名...", + "skillSidebar.folderNamePlaceholder": "输入文件夹名...", + "skillSidebar.loadError": "加载文件失败", + "skillSidebar.menu.cannotMoveToSelf": "不能将项目移动到自身", + "skillSidebar.menu.createError": "创建失败", + "skillSidebar.menu.cut": "剪切", + "skillSidebar.menu.delete": "删除", + "skillSidebar.menu.deleteConfirmContent": "确定要删除此文件夹及其所有内容吗?", + "skillSidebar.menu.deleteConfirmTitle": "删除文件夹", + "skillSidebar.menu.deleteError": "删除失败", + "skillSidebar.menu.deleted": "已成功删除", + "skillSidebar.menu.download": "下载", + "skillSidebar.menu.downloadError": "下载失败", + "skillSidebar.menu.fileCreated": "文件已创建", + "skillSidebar.menu.fileDeleteConfirmContent": "确定要删除此文件吗?", + "skillSidebar.menu.fileDeleteConfirmTitle": "删除文件", + "skillSidebar.menu.fileDeleteError": "删除文件失败", + "skillSidebar.menu.fileDeleted": "文件已删除", + "skillSidebar.menu.filesUploaded": "文件上传成功", + "skillSidebar.menu.folderCreated": "文件夹已创建", + "skillSidebar.menu.folderDropNotSupported": "不支持拖放文件夹", + "skillSidebar.menu.importSkills": "导入技能", + "skillSidebar.menu.moreActions": "更多操作", + "skillSidebar.menu.moveError": "移动失败", + "skillSidebar.menu.moved": "已成功移动", + "skillSidebar.menu.newFile": "新建文件", + "skillSidebar.menu.newFolder": "新建文件夹", + "skillSidebar.menu.paste": "粘贴", + "skillSidebar.menu.rename": "重命名", + "skillSidebar.menu.renameError": "重命名失败", + "skillSidebar.menu.renamed": "已成功重命名", + "skillSidebar.menu.uploadError": "上传失败", + "skillSidebar.menu.uploadFile": "上传文件", + "skillSidebar.menu.uploadFolder": "上传文件夹", + "skillSidebar.renameFileInput": "输入新文件名", + "skillSidebar.renameFolderInput": "输入新文件夹名", + "skillSidebar.resetFilter": "重置筛选", + "skillSidebar.rootFolder": "根目录", + "skillSidebar.searchNoResults": "未找到结果", + "skillSidebar.searchPlaceholder": "搜索文件...", + "skillSidebar.sqlitePreview.blobValue": "BLOB ({{size}} 字节)", + "skillSidebar.sqlitePreview.emptyRows": "无数据行", + "skillSidebar.sqlitePreview.emptyTables": "未找到表", + "skillSidebar.sqlitePreview.loadError": "加载 SQLite 数据失败", + "skillSidebar.sqlitePreview.nullValue": "NULL", + "skillSidebar.sqlitePreview.rowsTruncated": "显示前 {{limit}} 行", + "skillSidebar.sqlitePreview.selectTable": "选择表", + "skillSidebar.startTab": "开始", + "skillSidebar.toggleFolder": "展开/折叠文件夹", + "skillSidebar.unsavedChanges.confirmClose": "不保存关闭", + "skillSidebar.unsavedChanges.content": "您有未保存的更改。确定要关闭吗?", + "skillSidebar.unsavedChanges.title": "未保存的更改", + "skillSidebar.uploadPartialError": "部分文件上传失败", + "skillSidebar.uploadPartialErrorDetail": "{{total}} 个文件中有 {{failed}} 个上传失败", + "skillSidebar.uploadSuccess": "上传成功", + "skillSidebar.uploadSuccessDetail": "已上传 {{count}} 个文件", + "skillSidebar.uploadingItems": "正在上传 {{count}} 个项目...", + "subGraphModal.internalStructure": "内部结构", + "subGraphModal.internalStructureDesc": "查看此子图的内部工作流结构", + "subGraphModal.noRunHistory": "暂无运行历史", + "subGraphModal.outputVariables": "输出变量", + "subGraphModal.title": "子图详情", + "subGraphModal.whenOutputIsNone": "当输出为空时", + "subGraphModal.whenOutputNone.default": "使用默认值", + "subGraphModal.whenOutputNone.defaultDesc": "当子图输出为空时使用默认值", + "subGraphModal.whenOutputNone.error": "抛出错误", + "subGraphModal.whenOutputNone.errorDesc": "当子图输出为空时抛出错误", "tabs.-": "默认", "tabs.addAll": "添加全部", "tabs.agent": "Agent 策略", @@ -1128,6 +1350,8 @@ "tabs.usePlugin": "选择工具", "tabs.utilities": "工具", "tabs.workflowTool": "工作流", + "toolGroup.actionsEnabled": "已启用 {{count}} 个操作", + "toolGroup.byAuthor": "作者 {{author}}", "tracing.stopBy": "由{{user}}终止", "triggerStatus.disabled": "触发器 • 已禁用", "triggerStatus.enabled": "触发器", @@ -1140,6 +1364,7 @@ "versionHistory.action.deleteFailure": "删除失败", "versionHistory.action.deleteSuccess": "版本已删除", "versionHistory.action.restoreFailure": "回滚失败", + "versionHistory.action.restoreInProgress": "恢复中...", "versionHistory.action.restoreSuccess": "回滚成功", "versionHistory.action.updateFailure": "更新失败", "versionHistory.action.updateSuccess": "版本信息已更新", @@ -1154,8 +1379,6 @@ "versionHistory.editVersionInfo": "编辑信息", "versionHistory.filter.all": "全部", "versionHistory.filter.empty": "没有匹配的版本", - "viewPicker.graph": "工作流", - "viewPicker.skill": "技能", "versionHistory.filter.onlyShowNamedVersions": "只显示已命名版本", "versionHistory.filter.onlyYours": "仅你的", "versionHistory.filter.reset": "重置", @@ -1163,5 +1386,7 @@ "versionHistory.nameThisVersion": "命名", "versionHistory.releaseNotesPlaceholder": "请描述变更", "versionHistory.restorationTip": "版本回滚后,当前草稿将被覆盖。", - "versionHistory.title": "版本" + "versionHistory.title": "版本", + "viewPicker.graph": "工作流", + "viewPicker.skill": "技能" }