-
+
{nodeData.label}
- {getNodeTypeDescription(nodeData.type, t, nodeData.nodeTypeLabel)}
+ {getNodeTypeDescription(
+ nodeData.type,
+ t,
+ nodeData.nodeTypeLabel,
+ )}
{/* Status indicator */}
{status !== 'idle' && (
-
-
+
)}
@@ -291,16 +353,20 @@ function WorkflowNodeComponent({ data, selected }: NodeProps) {
{/* Execution info */}
{(status === 'completed' || status === 'failed') && (
-
+
{status}
{formattedDuration && (
-
{formattedDuration}
+
+ {formattedDuration}
+
)}
)}
@@ -329,7 +395,10 @@ function WorkflowNodeComponent({ data, selected }: NodeProps) {
position={Position.Right}
id={output.name}
style={{
- top: outputs.length === 1 ? '50%' : `${((index + 1) / (outputs.length + 1)) * 100}%`,
+ top:
+ outputs.length === 1
+ ? '50%'
+ : `${((index + 1) / (outputs.length + 1)) * 100}%`,
background: colors.handleBg,
width: 12,
height: 12,
@@ -340,12 +409,20 @@ function WorkflowNodeComponent({ data, selected }: NodeProps) {
/>
- {getPortLabel(output.label, output.name, 'workflows.nodeOutputs', t)}
- {output.type && {output.type}
}
+
+ {getPortLabel(
+ output.label,
+ output.name,
+ 'workflows.nodeOutputs',
+ t,
+ )}
+
+ {output.type && (
+ {output.type}
+ )}
))}
-
);
diff --git a/web/src/app/home/workflows/components/workflow-editor/index.ts b/web/src/app/home/workflows/components/workflow-editor/index.ts
index 9847ca4e..a0fd7789 100644
--- a/web/src/app/home/workflows/components/workflow-editor/index.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/index.ts
@@ -1,5 +1,8 @@
export { default as WorkflowEditorComponent } from './WorkflowEditorComponent';
-export { default as WorkflowNodeComponent, type WorkflowNodeData } from './WorkflowNodeComponent';
+export {
+ default as WorkflowNodeComponent,
+ type WorkflowNodeData,
+} from './WorkflowNodeComponent';
export { default as NodePalette } from './NodePalette';
export { default as PropertyPanel } from './PropertyPanel';
diff --git a/web/src/app/home/workflows/components/workflow-editor/node-configs/ai-configs.ts b/web/src/app/home/workflows/components/workflow-editor/node-configs/ai-configs.ts
index 4078c952..e6ae4742 100644
--- a/web/src/app/home/workflows/components/workflow-editor/node-configs/ai-configs.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/node-configs/ai-configs.ts
@@ -1,6 +1,6 @@
/**
* AI Node Configurations
- *
+ *
* Defines configurations for all AI-related node types:
* - llm_call: Call a large language model
* - question_classifier: Classify user questions into categories
@@ -80,8 +80,10 @@ export const llmCallConfig: NodeConfigMeta = {
zh_Hans: '系统提示词',
},
description: {
- en_US: 'System prompt to set the model behavior (supports variable interpolation with {{variable}})',
- zh_Hans: '设置模型行为的系统提示词(支持使用 {{variable}} 进行变量插值)',
+ en_US:
+ 'System prompt to set the model behavior (supports variable interpolation with {{variable}})',
+ zh_Hans:
+ '设置模型行为的系统提示词(支持使用 {{variable}} 进行变量插值)',
},
required: false,
default: '',
@@ -95,8 +97,10 @@ export const llmCallConfig: NodeConfigMeta = {
zh_Hans: '用户提示词模板',
},
description: {
- en_US: 'User prompt template with variable placeholders (e.g., {{input}}, {{context.key}})',
- zh_Hans: '带有变量占位符的用户提示词模板(例如 {{input}}、{{context.key}})',
+ en_US:
+ 'User prompt template with variable placeholders (e.g., {{input}}, {{context.key}})',
+ zh_Hans:
+ '带有变量占位符的用户提示词模板(例如 {{input}}、{{context.key}})',
},
required: true,
default: '{{input}}',
@@ -110,7 +114,8 @@ export const llmCallConfig: NodeConfigMeta = {
zh_Hans: '温度',
},
description: {
- en_US: 'Controls randomness in responses (0.0 = deterministic, 2.0 = very random)',
+ en_US:
+ 'Controls randomness in responses (0.0 = deterministic, 2.0 = very random)',
zh_Hans: '控制响应的随机性(0.0 = 确定性,2.0 = 非常随机)',
},
required: false,
@@ -125,7 +130,8 @@ export const llmCallConfig: NodeConfigMeta = {
zh_Hans: '最大令牌数',
},
description: {
- en_US: 'Maximum number of tokens to generate (leave 0 for model default)',
+ en_US:
+ 'Maximum number of tokens to generate (leave 0 for model default)',
zh_Hans: '生成的最大令牌数(设为 0 使用模型默认值)',
},
required: false,
@@ -148,7 +154,10 @@ export const llmCallConfig: NodeConfigMeta = {
options: [
{ name: 'text', label: { en_US: 'Plain Text', zh_Hans: '纯文本' } },
{ name: 'json', label: { en_US: 'JSON', zh_Hans: 'JSON' } },
- { name: 'markdown', label: { en_US: 'Markdown', zh_Hans: 'Markdown 文本' } },
+ {
+ name: 'markdown',
+ label: { en_US: 'Markdown', zh_Hans: 'Markdown 文本' },
+ },
],
},
{
@@ -282,8 +291,10 @@ export const questionClassifierConfig: NodeConfigMeta = {
zh_Hans: '分类定义',
},
description: {
- en_US: 'Define categories in JSON format: [{"name": "category1", "description": "...", "examples": ["..."]}]',
- zh_Hans: '使用 JSON 格式定义分类: [{"name": "分类1", "description": "...", "examples": ["..."]}]',
+ en_US:
+ 'Define categories in JSON format: [{"name": "category1", "description": "...", "examples": ["..."]}]',
+ zh_Hans:
+ '使用 JSON 格式定义分类: [{"name": "分类1", "description": "...", "examples": ["..."]}]',
},
required: true,
default: '[]',
@@ -389,8 +400,10 @@ export const parameterExtractorConfig: NodeConfigMeta = {
zh_Hans: '参数架构',
},
description: {
- en_US: 'JSON array defining expected parameters: [{"name": "date", "type": "string", "description": "Meeting date", "required": true}]',
- zh_Hans: '定义期望参数的 JSON 数组: [{"name": "日期", "type": "string", "description": "会议日期", "required": true}]',
+ en_US:
+ 'JSON array defining expected parameters: [{"name": "date", "type": "string", "description": "Meeting date", "required": true}]',
+ zh_Hans:
+ '定义期望参数的 JSON 数组: [{"name": "日期", "type": "string", "description": "会议日期", "required": true}]',
},
required: true,
default: '[]',
@@ -445,7 +458,8 @@ export const knowledgeRetrievalConfig: NodeConfigMeta = {
zh_Hans: '知识检索',
},
description: {
- en_US: 'Retrieve relevant information from knowledge bases using semantic search',
+ en_US:
+ 'Retrieve relevant information from knowledge bases using semantic search',
zh_Hans: '使用语义搜索从知识库中检索相关信息',
},
icon: 'BookOpen',
@@ -532,9 +546,18 @@ export const knowledgeRetrievalConfig: NodeConfigMeta = {
required: false,
default: 'vector',
options: [
- { name: 'vector', label: { en_US: 'Vector Search', zh_Hans: '向量检索' } },
- { name: 'hybrid', label: { en_US: 'Hybrid Search', zh_Hans: '混合检索' } },
- { name: 'keyword', label: { en_US: 'Keyword Search', zh_Hans: '关键词检索' } },
+ {
+ name: 'vector',
+ label: { en_US: 'Vector Search', zh_Hans: '向量检索' },
+ },
+ {
+ name: 'hybrid',
+ label: { en_US: 'Hybrid Search', zh_Hans: '混合检索' },
+ },
+ {
+ name: 'keyword',
+ label: { en_US: 'Keyword Search', zh_Hans: '关键词检索' },
+ },
],
},
{
@@ -700,8 +723,10 @@ export const intentRecognitionConfig: NodeConfigMeta = {
zh_Hans: '意图定义',
},
description: {
- en_US: 'Define intents in JSON format: [{"name": "intent1", "description": "...", "examples": ["..."]}]',
- zh_Hans: '使用 JSON 格式定义意图: [{"name": "意图1", "description": "...", "examples": ["..."]}]',
+ en_US:
+ 'Define intents in JSON format: [{"name": "intent1", "description": "...", "examples": ["..."]}]',
+ zh_Hans:
+ '使用 JSON 格式定义意图: [{"name": "意图1", "description": "...", "examples": ["..."]}]',
},
required: true,
default: '[]',
diff --git a/web/src/app/home/workflows/components/workflow-editor/node-configs/control-configs.ts b/web/src/app/home/workflows/components/workflow-editor/node-configs/control-configs.ts
index 4fd6447f..b4a06fe4 100644
--- a/web/src/app/home/workflows/components/workflow-editor/node-configs/control-configs.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/node-configs/control-configs.ts
@@ -1,6 +1,6 @@
/**
* Control Node Configurations
- *
+ *
* Defines configurations for flow control node types:
* - condition: Conditional branching
* - switch_case: Multi-way branching
@@ -62,10 +62,16 @@ export const conditionConfig: NodeConfigMeta = {
required: true,
default: 'expression',
options: [
- { name: 'expression', label: { en_US: 'Expression', zh_Hans: '表达式' } },
+ {
+ name: 'expression',
+ label: { en_US: 'Expression', zh_Hans: '表达式' },
+ },
{ name: 'comparison', label: { en_US: 'Comparison', zh_Hans: '比较' } },
{ name: 'exists', label: { en_US: 'Value Exists', zh_Hans: '值存在' } },
- { name: 'type_check', label: { en_US: 'Type Check', zh_Hans: '类型检查' } },
+ {
+ name: 'type_check',
+ label: { en_US: 'Type Check', zh_Hans: '类型检查' },
+ },
],
},
{
@@ -124,15 +130,36 @@ export const conditionConfig: NodeConfigMeta = {
default: 'eq',
options: [
{ name: 'eq', label: { en_US: 'Equals (==)', zh_Hans: '等于 (==)' } },
- { name: 'neq', label: { en_US: 'Not Equals (!=)', zh_Hans: '不等于 (!=)' } },
- { name: 'gt', label: { en_US: 'Greater Than (>)', zh_Hans: '大于 (>)' } },
- { name: 'gte', label: { en_US: 'Greater or Equal (>=)', zh_Hans: '大于等于 (>=)' } },
+ {
+ name: 'neq',
+ label: { en_US: 'Not Equals (!=)', zh_Hans: '不等于 (!=)' },
+ },
+ {
+ name: 'gt',
+ label: { en_US: 'Greater Than (>)', zh_Hans: '大于 (>)' },
+ },
+ {
+ name: 'gte',
+ label: { en_US: 'Greater or Equal (>=)', zh_Hans: '大于等于 (>=)' },
+ },
{ name: 'lt', label: { en_US: 'Less Than (<)', zh_Hans: '小于 (<)' } },
- { name: 'lte', label: { en_US: 'Less or Equal (<=)', zh_Hans: '小于等于 (<=)' } },
+ {
+ name: 'lte',
+ label: { en_US: 'Less or Equal (<=)', zh_Hans: '小于等于 (<=)' },
+ },
{ name: 'contains', label: { en_US: 'Contains', zh_Hans: '包含' } },
- { name: 'starts_with', label: { en_US: 'Starts With', zh_Hans: '以...开头' } },
- { name: 'ends_with', label: { en_US: 'Ends With', zh_Hans: '以...结尾' } },
- { name: 'matches', label: { en_US: 'Matches Regex', zh_Hans: '匹配正则' } },
+ {
+ name: 'starts_with',
+ label: { en_US: 'Starts With', zh_Hans: '以...开头' },
+ },
+ {
+ name: 'ends_with',
+ label: { en_US: 'Ends With', zh_Hans: '以...结尾' },
+ },
+ {
+ name: 'matches',
+ label: { en_US: 'Matches Regex', zh_Hans: '匹配正则' },
+ },
],
show_if: {
field: 'condition_type',
@@ -261,11 +288,14 @@ export const switchCaseConfig: NodeConfigMeta = {
zh_Hans: '情况',
},
description: {
- en_US: 'Define cases as JSON array: [{"name": "case_1", "value": "value1"}, {"name": "case_2", "values": ["v1", "v2"]}]',
- zh_Hans: '使用 JSON 数组定义情况: [{"name": "case_1", "value": "value1"}, {"name": "case_2", "values": ["v1", "v2"]}]',
+ en_US:
+ 'Define cases as JSON array: [{"name": "case_1", "value": "value1"}, {"name": "case_2", "values": ["v1", "v2"]}]',
+ zh_Hans:
+ '使用 JSON 数组定义情况: [{"name": "case_1", "value": "value1"}, {"name": "case_2", "values": ["v1", "v2"]}]',
},
required: true,
- default: '[{"name": "case_1", "value": ""}, {"name": "case_2", "value": ""}]',
+ default:
+ '[{"name": "case_1", "value": ""}, {"name": "case_2", "value": ""}]',
},
{
id: 'case_sensitive',
@@ -502,8 +532,10 @@ export const parallelConfig: NodeConfigMeta = {
zh_Hans: '分支',
},
description: {
- en_US: 'Define branches as JSON array: [{"name": "branch_1"}, {"name": "branch_2"}]',
- zh_Hans: '使用 JSON 数组定义分支: [{"name": "branch_1"}, {"name": "branch_2"}]',
+ en_US:
+ 'Define branches as JSON array: [{"name": "branch_1"}, {"name": "branch_2"}]',
+ zh_Hans:
+ '使用 JSON 数组定义分支: [{"name": "branch_1"}, {"name": "branch_2"}]',
},
required: true,
default: '[{"name": "branch_1"}, {"name": "branch_2"}]',
@@ -763,7 +795,10 @@ export const iteratorConfig: NodeConfigMeta = {
name: 'parallel',
type: DynamicFormItemType.BOOLEAN,
label: { en_US: 'Parallel Processing', zh_Hans: '并行处理' },
- description: { en_US: 'Process items in parallel', zh_Hans: '并行处理项目' },
+ description: {
+ en_US: 'Process items in parallel',
+ zh_Hans: '并行处理项目',
+ },
required: false,
default: false,
},
@@ -772,7 +807,10 @@ export const iteratorConfig: NodeConfigMeta = {
name: 'max_concurrency',
type: DynamicFormItemType.INT,
label: { en_US: 'Max Concurrency', zh_Hans: '最大并发数' },
- description: { en_US: 'Maximum number of concurrent iterations', zh_Hans: '最大并发迭代数' },
+ description: {
+ en_US: 'Maximum number of concurrent iterations',
+ zh_Hans: '最大并发迭代数',
+ },
required: false,
default: 5,
show_if: { field: 'parallel', operator: 'eq', value: true },
@@ -782,7 +820,10 @@ export const iteratorConfig: NodeConfigMeta = {
name: 'max_iterations',
type: DynamicFormItemType.INT,
label: { en_US: 'Max Iterations', zh_Hans: '最大迭代次数' },
- description: { en_US: 'Safety limit on iterations', zh_Hans: '迭代次数安全限制' },
+ description: {
+ en_US: 'Safety limit on iterations',
+ zh_Hans: '迭代次数安全限制',
+ },
required: false,
default: 1000,
},
@@ -831,14 +872,29 @@ export const mergeConfig: NodeConfigMeta = {
name: 'merge_strategy',
type: DynamicFormItemType.SELECT,
label: { en_US: 'Merge Strategy', zh_Hans: '合并策略' },
- description: { en_US: 'How to merge inputs from branches', zh_Hans: '如何合并分支输入' },
+ description: {
+ en_US: 'How to merge inputs from branches',
+ zh_Hans: '如何合并分支输入',
+ },
required: true,
default: 'wait_all',
options: [
- { name: 'wait_all', label: { en_US: 'Wait for All', zh_Hans: '等待全部' } },
- { name: 'first_completed', label: { en_US: 'First Completed', zh_Hans: '第一个完成' } },
- { name: 'combine', label: { en_US: 'Combine to Object', zh_Hans: '合并为对象' } },
- { name: 'array', label: { en_US: 'Collect to Array', zh_Hans: '收集为数组' } },
+ {
+ name: 'wait_all',
+ label: { en_US: 'Wait for All', zh_Hans: '等待全部' },
+ },
+ {
+ name: 'first_completed',
+ label: { en_US: 'First Completed', zh_Hans: '第一个完成' },
+ },
+ {
+ name: 'combine',
+ label: { en_US: 'Combine to Object', zh_Hans: '合并为对象' },
+ },
+ {
+ name: 'array',
+ label: { en_US: 'Collect to Array', zh_Hans: '收集为数组' },
+ },
],
},
],
@@ -881,7 +937,11 @@ export const variableAggregatorConfig: NodeConfigMeta = {
name: 'variable_mappings',
type: DynamicFormItemType.TEXT,
label: { en_US: 'Variable Mappings', zh_Hans: '变量映射' },
- description: { en_US: 'JSON mapping of output variables: {"out_key": "{{nodes.xxx.value}}"}', zh_Hans: 'JSON 格式的输出变量映射: {"out_key": "{{nodes.xxx.value}}"}' },
+ description: {
+ en_US:
+ 'JSON mapping of output variables: {"out_key": "{{nodes.xxx.value}}"}',
+ zh_Hans: 'JSON 格式的输出变量映射: {"out_key": "{{nodes.xxx.value}}"}',
+ },
required: true,
default: '{}',
},
@@ -890,13 +950,25 @@ export const variableAggregatorConfig: NodeConfigMeta = {
name: 'aggregation_mode',
type: DynamicFormItemType.SELECT,
label: { en_US: 'Aggregation Mode', zh_Hans: '聚合模式' },
- description: { en_US: 'How to aggregate the variables', zh_Hans: '如何聚合变量' },
+ description: {
+ en_US: 'How to aggregate the variables',
+ zh_Hans: '如何聚合变量',
+ },
required: true,
default: 'merge',
options: [
- { name: 'merge', label: { en_US: 'Merge Objects', zh_Hans: '合并对象' } },
- { name: 'array', label: { en_US: 'Collect to Array', zh_Hans: '收集为数组' } },
- { name: 'first', label: { en_US: 'First Non-null', zh_Hans: '第一个非空' } },
+ {
+ name: 'merge',
+ label: { en_US: 'Merge Objects', zh_Hans: '合并对象' },
+ },
+ {
+ name: 'array',
+ label: { en_US: 'Collect to Array', zh_Hans: '收集为数组' },
+ },
+ {
+ name: 'first',
+ label: { en_US: 'First Non-null', zh_Hans: '第一个非空' },
+ },
],
},
],
diff --git a/web/src/app/home/workflows/components/workflow-editor/node-configs/index.ts b/web/src/app/home/workflows/components/workflow-editor/node-configs/index.ts
index d34c2873..0bc9071c 100644
--- a/web/src/app/home/workflows/components/workflow-editor/node-configs/index.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/node-configs/index.ts
@@ -1,6 +1,6 @@
/**
* Node Configurations Index
- *
+ *
* This module exports all node configuration metadata and provides
* utility functions for accessing node configurations.
*/
@@ -9,7 +9,7 @@
export * from './types';
// Trigger Nodes
-export {
+export {
triggerConfigs,
getTriggerConfig,
messageTriggerConfig,
@@ -147,7 +147,9 @@ export function getNodeConfig(nodeType: string): NodeConfigMeta | undefined {
/**
* Get all node configurations for a category
*/
-export function getNodeConfigsByCategory(category: NodeCategory): NodeConfigMeta[] {
+export function getNodeConfigsByCategory(
+ category: NodeCategory,
+): NodeConfigMeta[] {
return allNodeConfigs.filter((config) => config.category === category);
}
@@ -171,18 +173,18 @@ export function isValidNodeType(nodeType: string): boolean {
export function getDefaultConfig(nodeType: string): Record
{
const config = getNodeConfig(nodeType);
if (!config) return {};
-
+
// Build default config from schema defaults
const defaults: Record = {};
for (const field of config.configSchema) {
defaults[field.name] = field.default;
}
-
+
// Override with explicit defaultConfig if provided
if (config.defaultConfig) {
Object.assign(defaults, config.defaultConfig);
}
-
+
return defaults;
}
@@ -191,32 +193,35 @@ export function getDefaultConfig(nodeType: string): Record {
*/
export function validateNodeConfig(
nodeType: string,
- config: Record
+ config: Record,
): { valid: boolean; errors: string[] } {
const nodeConfig = getNodeConfig(nodeType);
if (!nodeConfig) {
return { valid: false, errors: [`Unknown node type: ${nodeType}`] };
}
-
+
const errors: string[] = [];
-
+
for (const field of nodeConfig.configSchema) {
const value = config[field.name];
-
+
// Check required fields
- if (field.required && (value === undefined || value === null || value === '')) {
+ if (
+ field.required &&
+ (value === undefined || value === null || value === '')
+ ) {
errors.push(`Field "${field.name}" is required`);
continue;
}
-
+
// Skip validation for optional empty fields
if (!field.required && (value === undefined || value === null)) {
continue;
}
-
+
// Type-specific validation could be added here
}
-
+
return { valid: errors.length === 0, errors };
}
diff --git a/web/src/app/home/workflows/components/workflow-editor/node-configs/integration-configs.ts b/web/src/app/home/workflows/components/workflow-editor/node-configs/integration-configs.ts
index e0037bad..d516574f 100644
--- a/web/src/app/home/workflows/components/workflow-editor/node-configs/integration-configs.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/node-configs/integration-configs.ts
@@ -1,6 +1,6 @@
/**
* Integration Node Configurations
- *
+ *
* Defines configurations for integration node types:
* - database_query: Query databases
* - redis_operation: Redis operations
@@ -64,7 +64,10 @@ export const databaseQueryConfig: NodeConfigMeta = {
required: true,
default: 'postgresql',
options: [
- { name: 'postgresql', label: { en_US: 'PostgreSQL', zh_Hans: 'PostgreSQL' } },
+ {
+ name: 'postgresql',
+ label: { en_US: 'PostgreSQL', zh_Hans: 'PostgreSQL' },
+ },
{ name: 'mysql', label: { en_US: 'MySQL', zh_Hans: 'MySQL' } },
{ name: 'sqlite', label: { en_US: 'SQLite', zh_Hans: 'SQLite' } },
],
@@ -372,7 +375,8 @@ export const mcpToolConfig: NodeConfigMeta = {
zh_Hans: '参数模板',
},
description: {
- en_US: 'Tool arguments as JSON (supports variable interpolation). Leave empty to use input.',
+ en_US:
+ 'Tool arguments as JSON (supports variable interpolation). Leave empty to use input.',
zh_Hans: '工具参数(JSON 格式,支持变量插值)。留空则使用输入。',
},
required: false,
@@ -537,27 +541,78 @@ export const memoryStoreConfig: NodeConfigMeta = {
export const difyWorkflowConfig: NodeConfigMeta = {
nodeType: 'dify_workflow',
label: { en_US: 'Dify Workflow', zh_Hans: 'Dify 工作流' },
- description: { en_US: 'Call a Dify platform workflow', zh_Hans: '调用 Dify 平台工作流' },
+ description: {
+ en_US: 'Call a Dify platform workflow',
+ zh_Hans: '调用 Dify 平台工作流',
+ },
icon: 'Bot',
category: 'integration',
color: '#ec4899',
inputs: [
- createInput('input', 'any', { description: 'Input data', label: { en_US: 'Input', zh_Hans: '输入' }, required: false }),
+ createInput('input', 'any', {
+ description: 'Input data',
+ label: { en_US: 'Input', zh_Hans: '输入' },
+ required: false,
+ }),
],
outputs: [
- createOutput('result', 'any', { description: 'Workflow result', label: { en_US: 'Result', zh_Hans: '结果' } }),
- createOutput('success', 'boolean', { description: 'Whether call was successful', label: { en_US: 'Success', zh_Hans: '成功' } }),
+ createOutput('result', 'any', {
+ description: 'Workflow result',
+ label: { en_US: 'Result', zh_Hans: '结果' },
+ }),
+ createOutput('success', 'boolean', {
+ description: 'Whether call was successful',
+ label: { en_US: 'Success', zh_Hans: '成功' },
+ }),
],
configSchema: [
- { id: 'base-url', name: 'base-url', type: DynamicFormItemType.STRING, label: { en_US: 'Base URL', zh_Hans: 'Base URL' }, description: { en_US: 'Dify API base URL', zh_Hans: 'Dify API 基础 URL' }, required: true, default: '' },
- { id: 'api-key', name: 'api-key', type: DynamicFormItemType.STRING, label: { en_US: 'API Key', zh_Hans: 'API Key' }, description: { en_US: 'Dify API key', zh_Hans: 'Dify API 密钥' }, required: true, default: '' },
- { id: 'app-type', name: 'app-type', type: DynamicFormItemType.SELECT, label: { en_US: 'App Type', zh_Hans: '应用类型' }, description: { en_US: 'Dify application type', zh_Hans: 'Dify 应用类型' }, required: true, default: 'workflow', options: [
- { name: 'workflow', label: { en_US: 'Workflow', zh_Hans: '工作流' } },
- { name: 'chatbot', label: { en_US: 'Chatbot', zh_Hans: '聊天机器人' } },
- ] },
- { id: 'timeout', name: 'timeout', type: DynamicFormItemType.INT, label: { en_US: 'Timeout (seconds)', zh_Hans: '超时时间(秒)' }, description: { en_US: 'Request timeout', zh_Hans: '请求超时时间' }, required: false, default: 60 },
+ {
+ id: 'base-url',
+ name: 'base-url',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'Base URL', zh_Hans: 'Base URL' },
+ description: { en_US: 'Dify API base URL', zh_Hans: 'Dify API 基础 URL' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'api-key',
+ name: 'api-key',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'API Key', zh_Hans: 'API Key' },
+ description: { en_US: 'Dify API key', zh_Hans: 'Dify API 密钥' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'app-type',
+ name: 'app-type',
+ type: DynamicFormItemType.SELECT,
+ label: { en_US: 'App Type', zh_Hans: '应用类型' },
+ description: { en_US: 'Dify application type', zh_Hans: 'Dify 应用类型' },
+ required: true,
+ default: 'workflow',
+ options: [
+ { name: 'workflow', label: { en_US: 'Workflow', zh_Hans: '工作流' } },
+ { name: 'chatbot', label: { en_US: 'Chatbot', zh_Hans: '聊天机器人' } },
+ ],
+ },
+ {
+ id: 'timeout',
+ name: 'timeout',
+ type: DynamicFormItemType.INT,
+ label: { en_US: 'Timeout (seconds)', zh_Hans: '超时时间(秒)' },
+ description: { en_US: 'Request timeout', zh_Hans: '请求超时时间' },
+ required: false,
+ default: 60,
+ },
],
- defaultConfig: { 'base-url': '', 'api-key': '', 'app-type': 'workflow', timeout: 60 },
+ defaultConfig: {
+ 'base-url': '',
+ 'api-key': '',
+ 'app-type': 'workflow',
+ timeout: 60,
+ },
};
/**
@@ -566,22 +621,69 @@ export const difyWorkflowConfig: NodeConfigMeta = {
export const difyKnowledgeQueryConfig: NodeConfigMeta = {
nodeType: 'dify_knowledge_query',
label: { en_US: 'Dify Knowledge Query', zh_Hans: 'Dify 知识库查询' },
- description: { en_US: 'Query Dify knowledge base', zh_Hans: '查询 Dify 知识库' },
+ description: {
+ en_US: 'Query Dify knowledge base',
+ zh_Hans: '查询 Dify 知识库',
+ },
icon: 'Search',
category: 'integration',
color: '#ec4899',
inputs: [
- createInput('query', 'string', { description: 'Search query', label: { en_US: 'Query', zh_Hans: '查询' } }),
+ createInput('query', 'string', {
+ description: 'Search query',
+ label: { en_US: 'Query', zh_Hans: '查询' },
+ }),
],
outputs: [
- createOutput('results', 'array', { description: 'Search results', label: { en_US: 'Results', zh_Hans: '结果' } }),
- createOutput('success', 'boolean', { description: 'Whether query was successful', label: { en_US: 'Success', zh_Hans: '成功' } }),
+ createOutput('results', 'array', {
+ description: 'Search results',
+ label: { en_US: 'Results', zh_Hans: '结果' },
+ }),
+ createOutput('success', 'boolean', {
+ description: 'Whether query was successful',
+ label: { en_US: 'Success', zh_Hans: '成功' },
+ }),
],
configSchema: [
- { id: 'base-url', name: 'base-url', type: DynamicFormItemType.STRING, label: { en_US: 'Base URL', zh_Hans: 'Base URL' }, description: { en_US: 'Dify API base URL', zh_Hans: 'Dify API 基础 URL' }, required: true, default: '' },
- { id: 'api-key', name: 'api-key', type: DynamicFormItemType.STRING, label: { en_US: 'API Key', zh_Hans: 'API Key' }, description: { en_US: 'Dify API key', zh_Hans: 'Dify API 密钥' }, required: true, default: '' },
- { id: 'dataset_id', name: 'dataset_id', type: DynamicFormItemType.STRING, label: { en_US: 'Dataset ID', zh_Hans: '数据集 ID' }, description: { en_US: 'Dify dataset ID', zh_Hans: 'Dify 数据集 ID' }, required: true, default: '' },
- { id: 'top_k', name: 'top_k', type: DynamicFormItemType.INT, label: { en_US: 'Top K', zh_Hans: 'Top K' }, description: { en_US: 'Number of results to return', zh_Hans: '返回结果数量' }, required: false, default: 5 },
+ {
+ id: 'base-url',
+ name: 'base-url',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'Base URL', zh_Hans: 'Base URL' },
+ description: { en_US: 'Dify API base URL', zh_Hans: 'Dify API 基础 URL' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'api-key',
+ name: 'api-key',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'API Key', zh_Hans: 'API Key' },
+ description: { en_US: 'Dify API key', zh_Hans: 'Dify API 密钥' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'dataset_id',
+ name: 'dataset_id',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'Dataset ID', zh_Hans: '数据集 ID' },
+ description: { en_US: 'Dify dataset ID', zh_Hans: 'Dify 数据集 ID' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'top_k',
+ name: 'top_k',
+ type: DynamicFormItemType.INT,
+ label: { en_US: 'Top K', zh_Hans: 'Top K' },
+ description: {
+ en_US: 'Number of results to return',
+ zh_Hans: '返回结果数量',
+ },
+ required: false,
+ default: 5,
+ },
],
defaultConfig: { 'base-url': '', 'api-key': '', dataset_id: '', top_k: 5 },
};
@@ -592,20 +694,49 @@ export const difyKnowledgeQueryConfig: NodeConfigMeta = {
export const n8nWorkflowConfig: NodeConfigMeta = {
nodeType: 'n8n_workflow',
label: { en_US: 'N8n Workflow', zh_Hans: 'n8n 工作流' },
- description: { en_US: 'Call an n8n workflow via webhook', zh_Hans: '通过 webhook 调用 n8n 工作流' },
+ description: {
+ en_US: 'Call an n8n workflow via webhook',
+ zh_Hans: '通过 webhook 调用 n8n 工作流',
+ },
icon: 'Settings',
category: 'integration',
color: '#ec4899',
inputs: [
- createInput('input', 'any', { description: 'Input data', label: { en_US: 'Input', zh_Hans: '输入' }, required: false }),
+ createInput('input', 'any', {
+ description: 'Input data',
+ label: { en_US: 'Input', zh_Hans: '输入' },
+ required: false,
+ }),
],
outputs: [
- createOutput('result', 'any', { description: 'Workflow result', label: { en_US: 'Result', zh_Hans: '结果' } }),
- createOutput('success', 'boolean', { description: 'Whether call was successful', label: { en_US: 'Success', zh_Hans: '成功' } }),
+ createOutput('result', 'any', {
+ description: 'Workflow result',
+ label: { en_US: 'Result', zh_Hans: '结果' },
+ }),
+ createOutput('success', 'boolean', {
+ description: 'Whether call was successful',
+ label: { en_US: 'Success', zh_Hans: '成功' },
+ }),
],
configSchema: [
- { id: 'webhook-url', name: 'webhook-url', type: DynamicFormItemType.STRING, label: { en_US: 'Webhook URL', zh_Hans: 'Webhook URL' }, description: { en_US: 'N8n webhook URL', zh_Hans: 'n8n Webhook URL' }, required: true, default: '' },
- { id: 'timeout', name: 'timeout', type: DynamicFormItemType.INT, label: { en_US: 'Timeout (seconds)', zh_Hans: '超时时间(秒)' }, description: { en_US: 'Request timeout', zh_Hans: '请求超时时间' }, required: false, default: 60 },
+ {
+ id: 'webhook-url',
+ name: 'webhook-url',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'Webhook URL', zh_Hans: 'Webhook URL' },
+ description: { en_US: 'N8n webhook URL', zh_Hans: 'n8n Webhook URL' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'timeout',
+ name: 'timeout',
+ type: DynamicFormItemType.INT,
+ label: { en_US: 'Timeout (seconds)', zh_Hans: '超时时间(秒)' },
+ description: { en_US: 'Request timeout', zh_Hans: '请求超时时间' },
+ required: false,
+ default: 60,
+ },
],
defaultConfig: { 'webhook-url': '', timeout: 60 },
};
@@ -621,17 +752,65 @@ export const langflowFlowConfig: NodeConfigMeta = {
category: 'integration',
color: '#ec4899',
inputs: [
- createInput('input', 'any', { description: 'Input data', label: { en_US: 'Input', zh_Hans: '输入' }, required: false }),
+ createInput('input', 'any', {
+ description: 'Input data',
+ label: { en_US: 'Input', zh_Hans: '输入' },
+ required: false,
+ }),
],
outputs: [
- createOutput('result', 'any', { description: 'Flow result', label: { en_US: 'Result', zh_Hans: '结果' } }),
- createOutput('success', 'boolean', { description: 'Whether call was successful', label: { en_US: 'Success', zh_Hans: '成功' } }),
+ createOutput('result', 'any', {
+ description: 'Flow result',
+ label: { en_US: 'Result', zh_Hans: '结果' },
+ }),
+ createOutput('success', 'boolean', {
+ description: 'Whether call was successful',
+ label: { en_US: 'Success', zh_Hans: '成功' },
+ }),
],
configSchema: [
- { id: 'base-url', name: 'base-url', type: DynamicFormItemType.STRING, label: { en_US: 'Base URL', zh_Hans: 'Base URL' }, description: { en_US: 'Langflow API base URL', zh_Hans: 'Langflow API 基础 URL' }, required: true, default: '' },
- { id: 'flow-id', name: 'flow-id', type: DynamicFormItemType.STRING, label: { en_US: 'Flow ID', zh_Hans: '流程 ID' }, description: { en_US: 'Langflow flow ID', zh_Hans: 'Langflow 流程 ID' }, required: true, default: '' },
- { id: 'api-key', name: 'api-key', type: DynamicFormItemType.STRING, label: { en_US: 'API Key', zh_Hans: 'API Key' }, description: { en_US: 'Langflow API key (optional)', zh_Hans: 'Langflow API 密钥(可选)' }, required: false, default: '' },
- { id: 'timeout', name: 'timeout', type: DynamicFormItemType.INT, label: { en_US: 'Timeout (seconds)', zh_Hans: '超时时间(秒)' }, description: { en_US: 'Request timeout', zh_Hans: '请求超时时间' }, required: false, default: 60 },
+ {
+ id: 'base-url',
+ name: 'base-url',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'Base URL', zh_Hans: 'Base URL' },
+ description: {
+ en_US: 'Langflow API base URL',
+ zh_Hans: 'Langflow API 基础 URL',
+ },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'flow-id',
+ name: 'flow-id',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'Flow ID', zh_Hans: '流程 ID' },
+ description: { en_US: 'Langflow flow ID', zh_Hans: 'Langflow 流程 ID' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'api-key',
+ name: 'api-key',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'API Key', zh_Hans: 'API Key' },
+ description: {
+ en_US: 'Langflow API key (optional)',
+ zh_Hans: 'Langflow API 密钥(可选)',
+ },
+ required: false,
+ default: '',
+ },
+ {
+ id: 'timeout',
+ name: 'timeout',
+ type: DynamicFormItemType.INT,
+ label: { en_US: 'Timeout (seconds)', zh_Hans: '超时时间(秒)' },
+ description: { en_US: 'Request timeout', zh_Hans: '请求超时时间' },
+ required: false,
+ default: 60,
+ },
],
defaultConfig: { 'base-url': '', 'flow-id': '', 'api-key': '', timeout: 60 },
};
@@ -647,19 +826,65 @@ export const cozeBotConfig: NodeConfigMeta = {
category: 'integration',
color: '#ec4899',
inputs: [
- createInput('message', 'string', { description: 'Message to send', label: { en_US: 'Message', zh_Hans: '消息' } }),
+ createInput('message', 'string', {
+ description: 'Message to send',
+ label: { en_US: 'Message', zh_Hans: '消息' },
+ }),
],
outputs: [
- createOutput('result', 'any', { description: 'Bot response', label: { en_US: 'Result', zh_Hans: '结果' } }),
- createOutput('success', 'boolean', { description: 'Whether call was successful', label: { en_US: 'Success', zh_Hans: '成功' } }),
+ createOutput('result', 'any', {
+ description: 'Bot response',
+ label: { en_US: 'Result', zh_Hans: '结果' },
+ }),
+ createOutput('success', 'boolean', {
+ description: 'Whether call was successful',
+ label: { en_US: 'Success', zh_Hans: '成功' },
+ }),
],
configSchema: [
- { id: 'api-base', name: 'api-base', type: DynamicFormItemType.STRING, label: { en_US: 'API Base URL', zh_Hans: 'API 基础 URL' }, description: { en_US: 'Coze API base URL', zh_Hans: 'Coze API 基础 URL' }, required: true, default: 'https://api.coze.com' },
- { id: 'bot-id', name: 'bot-id', type: DynamicFormItemType.STRING, label: { en_US: 'Bot ID', zh_Hans: 'Bot ID' }, description: { en_US: 'Coze Bot ID', zh_Hans: 'Coze Bot ID' }, required: true, default: '' },
- { id: 'api-key', name: 'api-key', type: DynamicFormItemType.STRING, label: { en_US: 'API Key', zh_Hans: 'API Key' }, description: { en_US: 'Coze API key', zh_Hans: 'Coze API 密钥' }, required: true, default: '' },
- { id: 'timeout', name: 'timeout', type: DynamicFormItemType.INT, label: { en_US: 'Timeout (seconds)', zh_Hans: '超时时间(秒)' }, description: { en_US: 'Request timeout', zh_Hans: '请求超时时间' }, required: false, default: 60 },
+ {
+ id: 'api-base',
+ name: 'api-base',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'API Base URL', zh_Hans: 'API 基础 URL' },
+ description: { en_US: 'Coze API base URL', zh_Hans: 'Coze API 基础 URL' },
+ required: true,
+ default: 'https://api.coze.com',
+ },
+ {
+ id: 'bot-id',
+ name: 'bot-id',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'Bot ID', zh_Hans: 'Bot ID' },
+ description: { en_US: 'Coze Bot ID', zh_Hans: 'Coze Bot ID' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'api-key',
+ name: 'api-key',
+ type: DynamicFormItemType.STRING,
+ label: { en_US: 'API Key', zh_Hans: 'API Key' },
+ description: { en_US: 'Coze API key', zh_Hans: 'Coze API 密钥' },
+ required: true,
+ default: '',
+ },
+ {
+ id: 'timeout',
+ name: 'timeout',
+ type: DynamicFormItemType.INT,
+ label: { en_US: 'Timeout (seconds)', zh_Hans: '超时时间(秒)' },
+ description: { en_US: 'Request timeout', zh_Hans: '请求超时时间' },
+ required: false,
+ default: 60,
+ },
],
- defaultConfig: { 'api-base': 'https://api.coze.com', 'bot-id': '', 'api-key': '', timeout: 60 },
+ defaultConfig: {
+ 'api-base': 'https://api.coze.com',
+ 'bot-id': '',
+ 'api-key': '',
+ timeout: 60,
+ },
};
/**
@@ -680,6 +905,8 @@ export const integrationConfigs: NodeConfigMeta[] = [
/**
* Get integration config by type
*/
-export function getIntegrationConfig(nodeType: string): NodeConfigMeta | undefined {
+export function getIntegrationConfig(
+ nodeType: string,
+): NodeConfigMeta | undefined {
return integrationConfigs.find((config) => config.nodeType === nodeType);
}
diff --git a/web/src/app/home/workflows/components/workflow-editor/node-configs/process-configs.ts b/web/src/app/home/workflows/components/workflow-editor/node-configs/process-configs.ts
index 7721c836..4382552e 100644
--- a/web/src/app/home/workflows/components/workflow-editor/node-configs/process-configs.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/node-configs/process-configs.ts
@@ -1,6 +1,6 @@
/**
* Process Node Configurations
- *
+ *
* Defines configurations for general processing node types:
* - text_template: Generate text using templates
* - json_transform: Transform JSON data
@@ -52,7 +52,8 @@ export const textTemplateConfig: NodeConfigMeta = {
zh_Hans: '模板',
},
description: {
- en_US: 'Text template with variable placeholders (e.g., {{variable_name}})',
+ en_US:
+ 'Text template with variable placeholders (e.g., {{variable_name}})',
zh_Hans: '带有变量占位符的文本模板(例如 {{variable_name}})',
},
required: true,
@@ -141,9 +142,18 @@ export const jsonTransformConfig: NodeConfigMeta = {
required: true,
default: 'jmespath',
options: [
- { name: 'jmespath', label: { en_US: 'JMESPath Expression', zh_Hans: 'JMESPath 表达式' } },
- { name: 'jsonpath', label: { en_US: 'JSONPath Expression', zh_Hans: 'JSONPath 表达式' } },
- { name: 'mapping', label: { en_US: 'Field Mapping', zh_Hans: '字段映射' } },
+ {
+ name: 'jmespath',
+ label: { en_US: 'JMESPath Expression', zh_Hans: 'JMESPath 表达式' },
+ },
+ {
+ name: 'jsonpath',
+ label: { en_US: 'JSONPath Expression', zh_Hans: 'JSONPath 表达式' },
+ },
+ {
+ name: 'mapping',
+ label: { en_US: 'Field Mapping', zh_Hans: '字段映射' },
+ },
],
},
{
@@ -175,8 +185,10 @@ export const jsonTransformConfig: NodeConfigMeta = {
zh_Hans: '字段映射',
},
description: {
- en_US: 'JSON object defining field mappings: {"output_field": "input.path.to.field"}',
- zh_Hans: '定义字段映射的 JSON 对象: {"output_field": "input.path.to.field"}',
+ en_US:
+ 'JSON object defining field mappings: {"output_field": "input.path.to.field"}',
+ zh_Hans:
+ '定义字段映射的 JSON 对象: {"output_field": "input.path.to.field"}',
},
required: true,
default: '{}',
@@ -243,7 +255,10 @@ export const codeExecutorConfig: NodeConfigMeta = {
required: true,
default: 'javascript',
options: [
- { name: 'javascript', label: { en_US: 'JavaScript', zh_Hans: 'JavaScript' } },
+ {
+ name: 'javascript',
+ label: { en_US: 'JavaScript', zh_Hans: 'JavaScript' },
+ },
{ name: 'python', label: { en_US: 'Python', zh_Hans: 'Python' } },
],
},
@@ -256,11 +271,13 @@ export const codeExecutorConfig: NodeConfigMeta = {
zh_Hans: '代码',
},
description: {
- en_US: 'Code to execute. Use `input` to access input data and return the result.',
+ en_US:
+ 'Code to execute. Use `input` to access input data and return the result.',
zh_Hans: '要执行的代码。使用 `input` 访问输入数据,并返回结果。',
},
required: true,
- default: '// Access input with: input\n// Return result with: return result;\n\nreturn input;',
+ default:
+ '// Access input with: input\n// Return result with: return result;\n\nreturn input;',
},
{
id: 'timeout',
@@ -334,13 +351,25 @@ export const dataAggregatorConfig: NodeConfigMeta = {
required: true,
default: 'array',
options: [
- { name: 'array', label: { en_US: 'Collect to Array', zh_Hans: '收集为数组' } },
- { name: 'concat', label: { en_US: 'Concatenate Strings', zh_Hans: '连接字符串' } },
+ {
+ name: 'array',
+ label: { en_US: 'Collect to Array', zh_Hans: '收集为数组' },
+ },
+ {
+ name: 'concat',
+ label: { en_US: 'Concatenate Strings', zh_Hans: '连接字符串' },
+ },
{ name: 'sum', label: { en_US: 'Sum Numbers', zh_Hans: '求和' } },
- { name: 'average', label: { en_US: 'Average Numbers', zh_Hans: '求平均' } },
+ {
+ name: 'average',
+ label: { en_US: 'Average Numbers', zh_Hans: '求平均' },
+ },
{ name: 'min', label: { en_US: 'Minimum', zh_Hans: '最小值' } },
{ name: 'max', label: { en_US: 'Maximum', zh_Hans: '最大值' } },
- { name: 'merge', label: { en_US: 'Merge Objects', zh_Hans: '合并对象' } },
+ {
+ name: 'merge',
+ label: { en_US: 'Merge Objects', zh_Hans: '合并对象' },
+ },
{ name: 'first', label: { en_US: 'First Item', zh_Hans: '第一项' } },
{ name: 'last', label: { en_US: 'Last Item', zh_Hans: '最后一项' } },
],
@@ -437,11 +466,23 @@ export const textSplitterConfig: NodeConfigMeta = {
required: true,
default: 'separator',
options: [
- { name: 'separator', label: { en_US: 'By Separator', zh_Hans: '按分隔符' } },
+ {
+ name: 'separator',
+ label: { en_US: 'By Separator', zh_Hans: '按分隔符' },
+ },
{ name: 'length', label: { en_US: 'By Length', zh_Hans: '按长度' } },
- { name: 'sentences', label: { en_US: 'By Sentences', zh_Hans: '按句子' } },
- { name: 'paragraphs', label: { en_US: 'By Paragraphs', zh_Hans: '按段落' } },
- { name: 'regex', label: { en_US: 'By Regex', zh_Hans: '按正则表达式' } },
+ {
+ name: 'sentences',
+ label: { en_US: 'By Sentences', zh_Hans: '按句子' },
+ },
+ {
+ name: 'paragraphs',
+ label: { en_US: 'By Paragraphs', zh_Hans: '按段落' },
+ },
+ {
+ name: 'regex',
+ label: { en_US: 'By Regex', zh_Hans: '按正则表达式' },
+ },
],
},
{
@@ -613,7 +654,10 @@ export const variableAssignmentConfig: NodeConfigMeta = {
options: [
{ name: 'input', label: { en_US: 'From Input', zh_Hans: '来自输入' } },
{ name: 'static', label: { en_US: 'Static Value', zh_Hans: '静态值' } },
- { name: 'expression', label: { en_US: 'Expression', zh_Hans: '表达式' } },
+ {
+ name: 'expression',
+ label: { en_US: 'Expression', zh_Hans: '表达式' },
+ },
],
},
{
@@ -714,7 +758,10 @@ export const dataTransformConfig: NodeConfigMeta = {
{ name: 'template', label: { en_US: 'Template', zh_Hans: '模板' } },
{ name: 'jsonpath', label: { en_US: 'JSONPath', zh_Hans: 'JSONPath' } },
{ name: 'jmespath', label: { en_US: 'JMESPath', zh_Hans: 'JMESPath' } },
- { name: 'expression', label: { en_US: 'Expression', zh_Hans: '表达式' } },
+ {
+ name: 'expression',
+ label: { en_US: 'Expression', zh_Hans: '表达式' },
+ },
],
},
{
diff --git a/web/src/app/home/workflows/components/workflow-editor/node-configs/trigger-configs.ts b/web/src/app/home/workflows/components/workflow-editor/node-configs/trigger-configs.ts
index 240de244..9e96a04e 100644
--- a/web/src/app/home/workflows/components/workflow-editor/node-configs/trigger-configs.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/node-configs/trigger-configs.ts
@@ -1,6 +1,6 @@
/**
* Trigger Node Configurations
- *
+ *
* Defines configurations for all trigger node types:
* - message_trigger: Triggered by incoming messages
* - cron_trigger: Triggered by scheduled time
@@ -62,9 +62,15 @@ export const messageTriggerConfig: NodeConfigMeta = {
default: 'all',
options: [
{ name: 'all', label: { en_US: 'All Messages', zh_Hans: '所有消息' } },
- { name: 'prefix', label: { en_US: 'Prefix Match', zh_Hans: '前缀匹配' } },
+ {
+ name: 'prefix',
+ label: { en_US: 'Prefix Match', zh_Hans: '前缀匹配' },
+ },
{ name: 'regex', label: { en_US: 'Regex Match', zh_Hans: '正则匹配' } },
- { name: 'contains', label: { en_US: 'Contains Keyword', zh_Hans: '包含关键词' } },
+ {
+ name: 'contains',
+ label: { en_US: 'Contains Keyword', zh_Hans: '包含关键词' },
+ },
{ name: 'exact', label: { en_US: 'Exact Match', zh_Hans: '精确匹配' } },
],
},
@@ -77,7 +83,8 @@ export const messageTriggerConfig: NodeConfigMeta = {
zh_Hans: '匹配模式',
},
description: {
- en_US: 'The pattern to match against the message (prefix, regex, keyword, or exact text)',
+ en_US:
+ 'The pattern to match against the message (prefix, regex, keyword, or exact text)',
zh_Hans: '用于匹配消息的模式(前缀、正则表达式、关键词或精确文本)',
},
required: false,
@@ -172,12 +179,39 @@ export const cronTriggerConfig: NodeConfigMeta = {
default: 'Asia/Shanghai',
options: [
{ name: 'UTC', label: { en_US: 'UTC', zh_Hans: 'UTC' } },
- { name: 'Asia/Shanghai', label: { en_US: 'Asia/Shanghai (UTC+8)', zh_Hans: '亚洲/上海 (UTC+8)' } },
- { name: 'Asia/Tokyo', label: { en_US: 'Asia/Tokyo (UTC+9)', zh_Hans: '亚洲/东京 (UTC+9)' } },
- { name: 'America/New_York', label: { en_US: 'America/New_York (EST)', zh_Hans: '美国/纽约 (EST)' } },
- { name: 'America/Los_Angeles', label: { en_US: 'America/Los_Angeles (PST)', zh_Hans: '美国/洛杉矶 (PST)' } },
- { name: 'Europe/London', label: { en_US: 'Europe/London (GMT)', zh_Hans: '欧洲/伦敦 (GMT)' } },
- { name: 'Europe/Berlin', label: { en_US: 'Europe/Berlin (CET)', zh_Hans: '欧洲/柏林 (CET)' } },
+ {
+ name: 'Asia/Shanghai',
+ label: {
+ en_US: 'Asia/Shanghai (UTC+8)',
+ zh_Hans: '亚洲/上海 (UTC+8)',
+ },
+ },
+ {
+ name: 'Asia/Tokyo',
+ label: { en_US: 'Asia/Tokyo (UTC+9)', zh_Hans: '亚洲/东京 (UTC+9)' },
+ },
+ {
+ name: 'America/New_York',
+ label: {
+ en_US: 'America/New_York (EST)',
+ zh_Hans: '美国/纽约 (EST)',
+ },
+ },
+ {
+ name: 'America/Los_Angeles',
+ label: {
+ en_US: 'America/Los_Angeles (PST)',
+ zh_Hans: '美国/洛杉矶 (PST)',
+ },
+ },
+ {
+ name: 'Europe/London',
+ label: { en_US: 'Europe/London (GMT)', zh_Hans: '欧洲/伦敦 (GMT)' },
+ },
+ {
+ name: 'Europe/Berlin',
+ label: { en_US: 'Europe/Berlin (CET)', zh_Hans: '欧洲/柏林 (CET)' },
+ },
],
},
{
@@ -230,7 +264,8 @@ export const webhookTriggerConfig: NodeConfigMeta = {
zh_Hans: 'Webhook 触发',
},
description: {
- en_US: 'Trigger workflow when an HTTP request is received at the webhook URL',
+ en_US:
+ 'Trigger workflow when an HTTP request is received at the webhook URL',
zh_Hans: '当在 Webhook URL 收到 HTTP 请求时触发工作流',
},
icon: 'Webhook',
@@ -288,8 +323,14 @@ export const webhookTriggerConfig: NodeConfigMeta = {
default: 'none',
options: [
{ name: 'none', label: { en_US: 'None', zh_Hans: '无' } },
- { name: 'token', label: { en_US: 'Bearer Token', zh_Hans: 'Bearer 令牌' } },
- { name: 'signature', label: { en_US: 'Signature', zh_Hans: '签名验证' } },
+ {
+ name: 'token',
+ label: { en_US: 'Bearer Token', zh_Hans: 'Bearer 令牌' },
+ },
+ {
+ name: 'signature',
+ label: { en_US: 'Signature', zh_Hans: '签名验证' },
+ },
{ name: 'basic', label: { en_US: 'Basic Auth', zh_Hans: '基本认证' } },
],
},
@@ -328,10 +369,25 @@ export const webhookTriggerConfig: NodeConfigMeta = {
required: false,
default: 'application/json',
options: [
- { name: 'application/json', label: { en_US: 'application/json', zh_Hans: 'JSON' } },
- { name: 'application/x-www-form-urlencoded', label: { en_US: 'application/x-www-form-urlencoded', zh_Hans: '表单编码' } },
- { name: 'multipart/form-data', label: { en_US: 'multipart/form-data', zh_Hans: '表单数据' } },
- { name: 'text/plain', label: { en_US: 'text/plain', zh_Hans: '纯文本' } },
+ {
+ name: 'application/json',
+ label: { en_US: 'application/json', zh_Hans: 'JSON' },
+ },
+ {
+ name: 'application/x-www-form-urlencoded',
+ label: {
+ en_US: 'application/x-www-form-urlencoded',
+ zh_Hans: '表单编码',
+ },
+ },
+ {
+ name: 'multipart/form-data',
+ label: { en_US: 'multipart/form-data', zh_Hans: '表单数据' },
+ },
+ {
+ name: 'text/plain',
+ label: { en_US: 'text/plain', zh_Hans: '纯文本' },
+ },
],
},
{
@@ -424,15 +480,42 @@ export const eventTriggerConfig: NodeConfigMeta = {
required: true,
default: 'member_join',
options: [
- { name: 'member_join', label: { en_US: 'Member Join', zh_Hans: '成员加入' } },
- { name: 'member_leave', label: { en_US: 'Member Leave', zh_Hans: '成员离开' } },
- { name: 'message_recall', label: { en_US: 'Message Recall', zh_Hans: '消息撤回' } },
- { name: 'group_created', label: { en_US: 'Group Created', zh_Hans: '群组创建' } },
- { name: 'group_disbanded', label: { en_US: 'Group Disbanded', zh_Hans: '群组解散' } },
- { name: 'bot_added', label: { en_US: 'Bot Added to Group', zh_Hans: '机器人被添加到群' } },
- { name: 'bot_removed', label: { en_US: 'Bot Removed from Group', zh_Hans: '机器人被移出群' } },
- { name: 'friend_request', label: { en_US: 'Friend Request', zh_Hans: '好友请求' } },
- { name: 'group_request', label: { en_US: 'Group Join Request', zh_Hans: '入群请求' } },
+ {
+ name: 'member_join',
+ label: { en_US: 'Member Join', zh_Hans: '成员加入' },
+ },
+ {
+ name: 'member_leave',
+ label: { en_US: 'Member Leave', zh_Hans: '成员离开' },
+ },
+ {
+ name: 'message_recall',
+ label: { en_US: 'Message Recall', zh_Hans: '消息撤回' },
+ },
+ {
+ name: 'group_created',
+ label: { en_US: 'Group Created', zh_Hans: '群组创建' },
+ },
+ {
+ name: 'group_disbanded',
+ label: { en_US: 'Group Disbanded', zh_Hans: '群组解散' },
+ },
+ {
+ name: 'bot_added',
+ label: { en_US: 'Bot Added to Group', zh_Hans: '机器人被添加到群' },
+ },
+ {
+ name: 'bot_removed',
+ label: { en_US: 'Bot Removed from Group', zh_Hans: '机器人被移出群' },
+ },
+ {
+ name: 'friend_request',
+ label: { en_US: 'Friend Request', zh_Hans: '好友请求' },
+ },
+ {
+ name: 'group_request',
+ label: { en_US: 'Group Join Request', zh_Hans: '入群请求' },
+ },
],
},
],
diff --git a/web/src/app/home/workflows/components/workflow-editor/node-configs/types.ts b/web/src/app/home/workflows/components/workflow-editor/node-configs/types.ts
index e5a1a2c6..516b5be4 100644
--- a/web/src/app/home/workflows/components/workflow-editor/node-configs/types.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/node-configs/types.ts
@@ -1,6 +1,6 @@
/**
* Workflow Node Configuration Types
- *
+ *
* This module defines the types used for node configuration metadata.
* It extends the existing dynamic form system to support workflow-specific features.
*/
@@ -23,40 +23,40 @@ export interface ExtendedPortDefinition extends PortDefinition {
export interface NodeConfigMeta {
/** Unique node type identifier */
nodeType: string;
-
+
/** Display name for the node */
label: I18nObject;
-
+
/** Description of what the node does */
description: I18nObject;
-
+
/** Icon name (from lucide-react) */
icon: string;
-
+
/** Node category for organization */
category: NodeCategory;
-
+
/** Color for the node header */
color?: string;
-
+
/** Input port definitions */
inputs: ExtendedPortDefinition[];
-
+
/** Output port definitions */
outputs: ExtendedPortDefinition[];
-
+
/** Configuration schema using the dynamic form system */
configSchema: IDynamicFormItemSchema[];
-
+
/** Default configuration values */
defaultConfig?: Record;
-
+
/** Whether this node can be the starting point of a workflow */
isEntryPoint?: boolean;
-
+
/** Maximum number of this node type allowed in a workflow (undefined = unlimited) */
maxInstances?: number;
-
+
/** Documentation URL */
docsUrl?: string;
}
@@ -76,7 +76,7 @@ export function createPort(
description?: string;
required?: boolean;
label?: I18nObject;
- }
+ },
): ExtendedPortDefinition {
return {
name,
@@ -97,9 +97,12 @@ export function createInput(
description?: string;
required?: boolean;
label?: I18nObject;
- }
+ },
): ExtendedPortDefinition {
- return createPort(name, type, { ...options, required: options?.required ?? true });
+ return createPort(name, type, {
+ ...options,
+ required: options?.required ?? true,
+ });
}
/**
@@ -111,7 +114,7 @@ export function createOutput(
options?: {
description?: string;
label?: I18nObject;
- }
+ },
): ExtendedPortDefinition {
return createPort(name, type, { ...options, required: false });
}
diff --git a/web/src/app/home/workflows/components/workflow-editor/workflow-constants.ts b/web/src/app/home/workflows/components/workflow-editor/workflow-constants.ts
index 01ad999b..24c11b00 100644
--- a/web/src/app/home/workflows/components/workflow-editor/workflow-constants.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/workflow-constants.ts
@@ -48,53 +48,176 @@ import { resolveI18nLabel, maybeTranslateKey } from './workflow-i18n';
// Single source of truth. Used by WorkflowNodeComponent,
// NodePalette, and useWorkflowStore.
-export const NODE_TYPE_I18N_KEYS: Record = {
+export const NODE_TYPE_I18N_KEYS: Record<
+ string,
+ { labelKey: string; descriptionKey: string }
+> = {
// Trigger
- 'trigger.message_trigger': { labelKey: 'workflows.nodes.messageTrigger', descriptionKey: 'workflows.nodes.messageTriggerDescription' },
- 'trigger.cron_trigger': { labelKey: 'workflows.nodes.cronTrigger', descriptionKey: 'workflows.nodes.cronTriggerDescription' },
- 'trigger.webhook_trigger': { labelKey: 'workflows.nodes.webhookTrigger', descriptionKey: 'workflows.nodes.webhookTriggerDescription' },
- 'trigger.event_trigger': { labelKey: 'workflows.nodes.eventTrigger', descriptionKey: 'workflows.nodes.eventTriggerDescription' },
+ 'trigger.message_trigger': {
+ labelKey: 'workflows.nodes.messageTrigger',
+ descriptionKey: 'workflows.nodes.messageTriggerDescription',
+ },
+ 'trigger.cron_trigger': {
+ labelKey: 'workflows.nodes.cronTrigger',
+ descriptionKey: 'workflows.nodes.cronTriggerDescription',
+ },
+ 'trigger.webhook_trigger': {
+ labelKey: 'workflows.nodes.webhookTrigger',
+ descriptionKey: 'workflows.nodes.webhookTriggerDescription',
+ },
+ 'trigger.event_trigger': {
+ labelKey: 'workflows.nodes.eventTrigger',
+ descriptionKey: 'workflows.nodes.eventTriggerDescription',
+ },
// Process / AI
- 'process.llm_call': { labelKey: 'workflows.nodes.llmCall', descriptionKey: 'workflows.nodes.llmCallDescription' },
- 'process.question_classifier': { labelKey: 'workflows.nodes.questionClassifier', descriptionKey: 'workflows.nodes.questionClassifierDescription' },
- 'process.parameter_extractor': { labelKey: 'workflows.nodes.parameterExtractor', descriptionKey: 'workflows.nodes.parameterExtractorDescription' },
- 'process.knowledge_retrieval': { labelKey: 'workflows.nodes.knowledgeRetrieval', descriptionKey: 'workflows.nodes.knowledgeRetrievalDescription' },
- 'process.code_executor': { labelKey: 'workflows.nodes.codeExecutor', descriptionKey: 'workflows.nodes.codeExecutorDescription' },
- 'process.http_request': { labelKey: 'workflows.nodes.httpRequest', descriptionKey: 'workflows.nodes.httpRequestDescription' },
- 'process.data_transform': { labelKey: 'workflows.nodes.dataTransform', descriptionKey: 'workflows.nodes.dataTransformDescription' },
- 'process.text_template': { labelKey: 'workflows.nodes.textTemplate', descriptionKey: 'workflows.nodes.textTemplateDescription' },
- 'process.json_transform': { labelKey: 'workflows.nodes.jsonTransform', descriptionKey: 'workflows.nodes.jsonTransformDescription' },
- 'process.data_aggregator': { labelKey: 'workflows.nodes.dataAggregator', descriptionKey: 'workflows.nodes.dataAggregatorDescription' },
- 'process.text_splitter': { labelKey: 'workflows.nodes.textSplitter', descriptionKey: 'workflows.nodes.textSplitterDescription' },
- 'process.variable_assignment': { labelKey: 'workflows.nodes.variableAssignment', descriptionKey: 'workflows.nodes.variableAssignmentDescription' },
+ 'process.llm_call': {
+ labelKey: 'workflows.nodes.llmCall',
+ descriptionKey: 'workflows.nodes.llmCallDescription',
+ },
+ 'process.question_classifier': {
+ labelKey: 'workflows.nodes.questionClassifier',
+ descriptionKey: 'workflows.nodes.questionClassifierDescription',
+ },
+ 'process.parameter_extractor': {
+ labelKey: 'workflows.nodes.parameterExtractor',
+ descriptionKey: 'workflows.nodes.parameterExtractorDescription',
+ },
+ 'process.knowledge_retrieval': {
+ labelKey: 'workflows.nodes.knowledgeRetrieval',
+ descriptionKey: 'workflows.nodes.knowledgeRetrievalDescription',
+ },
+ 'process.code_executor': {
+ labelKey: 'workflows.nodes.codeExecutor',
+ descriptionKey: 'workflows.nodes.codeExecutorDescription',
+ },
+ 'process.http_request': {
+ labelKey: 'workflows.nodes.httpRequest',
+ descriptionKey: 'workflows.nodes.httpRequestDescription',
+ },
+ 'process.data_transform': {
+ labelKey: 'workflows.nodes.dataTransform',
+ descriptionKey: 'workflows.nodes.dataTransformDescription',
+ },
+ 'process.text_template': {
+ labelKey: 'workflows.nodes.textTemplate',
+ descriptionKey: 'workflows.nodes.textTemplateDescription',
+ },
+ 'process.json_transform': {
+ labelKey: 'workflows.nodes.jsonTransform',
+ descriptionKey: 'workflows.nodes.jsonTransformDescription',
+ },
+ 'process.data_aggregator': {
+ labelKey: 'workflows.nodes.dataAggregator',
+ descriptionKey: 'workflows.nodes.dataAggregatorDescription',
+ },
+ 'process.text_splitter': {
+ labelKey: 'workflows.nodes.textSplitter',
+ descriptionKey: 'workflows.nodes.textSplitterDescription',
+ },
+ 'process.variable_assignment': {
+ labelKey: 'workflows.nodes.variableAssignment',
+ descriptionKey: 'workflows.nodes.variableAssignmentDescription',
+ },
// Control
- 'control.condition': { labelKey: 'workflows.nodes.condition', descriptionKey: 'workflows.nodes.conditionDescription' },
- 'control.switch': { labelKey: 'workflows.nodes.switch', descriptionKey: 'workflows.nodes.switchDescription' },
- 'control.loop': { labelKey: 'workflows.nodes.loop', descriptionKey: 'workflows.nodes.loopDescription' },
- 'control.iterator': { labelKey: 'workflows.nodes.iterator', descriptionKey: 'workflows.nodes.iteratorDescription' },
- 'control.parallel': { labelKey: 'workflows.nodes.parallel', descriptionKey: 'workflows.nodes.parallelDescription' },
- 'control.wait': { labelKey: 'workflows.nodes.wait', descriptionKey: 'workflows.nodes.waitDescription' },
- 'control.merge': { labelKey: 'workflows.nodes.merge', descriptionKey: 'workflows.nodes.mergeDescription' },
- 'control.variable_aggregator': { labelKey: 'workflows.nodes.variableAggregator', descriptionKey: 'workflows.nodes.variableAggregatorDescription' },
+ 'control.condition': {
+ labelKey: 'workflows.nodes.condition',
+ descriptionKey: 'workflows.nodes.conditionDescription',
+ },
+ 'control.switch': {
+ labelKey: 'workflows.nodes.switch',
+ descriptionKey: 'workflows.nodes.switchDescription',
+ },
+ 'control.loop': {
+ labelKey: 'workflows.nodes.loop',
+ descriptionKey: 'workflows.nodes.loopDescription',
+ },
+ 'control.iterator': {
+ labelKey: 'workflows.nodes.iterator',
+ descriptionKey: 'workflows.nodes.iteratorDescription',
+ },
+ 'control.parallel': {
+ labelKey: 'workflows.nodes.parallel',
+ descriptionKey: 'workflows.nodes.parallelDescription',
+ },
+ 'control.wait': {
+ labelKey: 'workflows.nodes.wait',
+ descriptionKey: 'workflows.nodes.waitDescription',
+ },
+ 'control.merge': {
+ labelKey: 'workflows.nodes.merge',
+ descriptionKey: 'workflows.nodes.mergeDescription',
+ },
+ 'control.variable_aggregator': {
+ labelKey: 'workflows.nodes.variableAggregator',
+ descriptionKey: 'workflows.nodes.variableAggregatorDescription',
+ },
// Action
- 'action.send_message': { labelKey: 'workflows.nodes.sendMessage', descriptionKey: 'workflows.nodes.sendMessageDescription' },
- 'action.reply_message': { labelKey: 'workflows.nodes.replyMessage', descriptionKey: 'workflows.nodes.replyMessageDescription' },
- 'action.store_data': { labelKey: 'workflows.nodes.storeData', descriptionKey: 'workflows.nodes.storeDataDescription' },
- 'action.call_pipeline': { labelKey: 'workflows.nodes.callPipeline', descriptionKey: 'workflows.nodes.callPipelineDescription' },
- 'action.set_variable': { labelKey: 'workflows.nodes.setVariable', descriptionKey: 'workflows.nodes.setVariableDescription' },
- 'action.opening_statement': { labelKey: 'workflows.nodes.openingStatement', descriptionKey: 'workflows.nodes.openingStatementDescription' },
- 'action.end': { labelKey: 'workflows.nodes.end', descriptionKey: 'workflows.nodes.endDescription' },
+ 'action.send_message': {
+ labelKey: 'workflows.nodes.sendMessage',
+ descriptionKey: 'workflows.nodes.sendMessageDescription',
+ },
+ 'action.reply_message': {
+ labelKey: 'workflows.nodes.replyMessage',
+ descriptionKey: 'workflows.nodes.replyMessageDescription',
+ },
+ 'action.store_data': {
+ labelKey: 'workflows.nodes.storeData',
+ descriptionKey: 'workflows.nodes.storeDataDescription',
+ },
+ 'action.call_pipeline': {
+ labelKey: 'workflows.nodes.callPipeline',
+ descriptionKey: 'workflows.nodes.callPipelineDescription',
+ },
+ 'action.set_variable': {
+ labelKey: 'workflows.nodes.setVariable',
+ descriptionKey: 'workflows.nodes.setVariableDescription',
+ },
+ 'action.opening_statement': {
+ labelKey: 'workflows.nodes.openingStatement',
+ descriptionKey: 'workflows.nodes.openingStatementDescription',
+ },
+ 'action.end': {
+ labelKey: 'workflows.nodes.end',
+ descriptionKey: 'workflows.nodes.endDescription',
+ },
// Integration – external services
- 'integration.dify_workflow': { labelKey: 'workflows.nodes.difyWorkflow', descriptionKey: 'workflows.nodes.difyWorkflowDescription' },
- 'integration.dify_knowledge_query': { labelKey: 'workflows.nodes.difyKnowledgeQuery', descriptionKey: 'workflows.nodes.difyKnowledgeQueryDescription' },
- 'integration.n8n_workflow': { labelKey: 'workflows.nodes.n8nWorkflow', descriptionKey: 'workflows.nodes.n8nWorkflowDescription' },
- 'integration.langflow_flow': { labelKey: 'workflows.nodes.langflowFlow', descriptionKey: 'workflows.nodes.langflowFlowDescription' },
- 'integration.coze_bot': { labelKey: 'workflows.nodes.cozeBot', descriptionKey: 'workflows.nodes.cozeBotDescription' },
+ 'integration.dify_workflow': {
+ labelKey: 'workflows.nodes.difyWorkflow',
+ descriptionKey: 'workflows.nodes.difyWorkflowDescription',
+ },
+ 'integration.dify_knowledge_query': {
+ labelKey: 'workflows.nodes.difyKnowledgeQuery',
+ descriptionKey: 'workflows.nodes.difyKnowledgeQueryDescription',
+ },
+ 'integration.n8n_workflow': {
+ labelKey: 'workflows.nodes.n8nWorkflow',
+ descriptionKey: 'workflows.nodes.n8nWorkflowDescription',
+ },
+ 'integration.langflow_flow': {
+ labelKey: 'workflows.nodes.langflowFlow',
+ descriptionKey: 'workflows.nodes.langflowFlowDescription',
+ },
+ 'integration.coze_bot': {
+ labelKey: 'workflows.nodes.cozeBot',
+ descriptionKey: 'workflows.nodes.cozeBotDescription',
+ },
// Integration – data & tools
- 'integration.database_query': { labelKey: 'workflows.nodes.databaseQuery', descriptionKey: 'workflows.nodes.databaseQueryDescription' },
- 'integration.redis_operation': { labelKey: 'workflows.nodes.redisOperation', descriptionKey: 'workflows.nodes.redisOperationDescription' },
- 'integration.mcp_tool': { labelKey: 'workflows.nodes.mcpTool', descriptionKey: 'workflows.nodes.mcpToolDescription' },
- 'integration.memory_store': { labelKey: 'workflows.nodes.memoryStore', descriptionKey: 'workflows.nodes.memoryStoreDescription' },
+ 'integration.database_query': {
+ labelKey: 'workflows.nodes.databaseQuery',
+ descriptionKey: 'workflows.nodes.databaseQueryDescription',
+ },
+ 'integration.redis_operation': {
+ labelKey: 'workflows.nodes.redisOperation',
+ descriptionKey: 'workflows.nodes.redisOperationDescription',
+ },
+ 'integration.mcp_tool': {
+ labelKey: 'workflows.nodes.mcpTool',
+ descriptionKey: 'workflows.nodes.mcpToolDescription',
+ },
+ 'integration.memory_store': {
+ labelKey: 'workflows.nodes.memoryStore',
+ descriptionKey: 'workflows.nodes.memoryStoreDescription',
+ },
};
// Flat version: type → label key only (convenience for store / node component)
@@ -104,10 +227,10 @@ export const NODE_TYPE_LABEL_KEYS: Record = Object.fromEntries(
// Category i18n
export const CATEGORY_I18N_KEYS: Record = {
- trigger: { labelKey: 'workflows.nodes.trigger' },
- process: { labelKey: 'workflows.nodes.process' },
- control: { labelKey: 'workflows.nodes.control' },
- action: { labelKey: 'workflows.nodes.action' },
+ trigger: { labelKey: 'workflows.nodes.trigger' },
+ process: { labelKey: 'workflows.nodes.process' },
+ control: { labelKey: 'workflows.nodes.control' },
+ action: { labelKey: 'workflows.nodes.action' },
integration: { labelKey: 'workflows.nodes.integration' },
};
@@ -195,11 +318,16 @@ export const PALETTE_CATEGORY_COLORS: Record = {
};
export const PALETTE_CATEGORY_BG: Record = {
- trigger: 'bg-amber-100 dark:bg-amber-900/30 hover:bg-amber-200 dark:hover:bg-amber-900/50',
- process: 'bg-blue-100 dark:bg-blue-900/30 hover:bg-blue-200 dark:hover:bg-blue-900/50',
- control: 'bg-purple-100 dark:bg-purple-900/30 hover:bg-purple-200 dark:hover:bg-purple-900/50',
- action: 'bg-green-100 dark:bg-green-900/30 hover:bg-green-200 dark:hover:bg-green-900/50',
- integration: 'bg-pink-100 dark:bg-pink-900/30 hover:bg-pink-200 dark:hover:bg-pink-900/50',
+ trigger:
+ 'bg-amber-100 dark:bg-amber-900/30 hover:bg-amber-200 dark:hover:bg-amber-900/50',
+ process:
+ 'bg-blue-100 dark:bg-blue-900/30 hover:bg-blue-200 dark:hover:bg-blue-900/50',
+ control:
+ 'bg-purple-100 dark:bg-purple-900/30 hover:bg-purple-200 dark:hover:bg-purple-900/50',
+ action:
+ 'bg-green-100 dark:bg-green-900/30 hover:bg-green-200 dark:hover:bg-green-900/50',
+ integration:
+ 'bg-pink-100 dark:bg-pink-900/30 hover:bg-pink-200 dark:hover:bg-pink-900/50',
};
export const PALETTE_CATEGORY_BORDER: Record = {
@@ -224,7 +352,9 @@ export const CATEGORY_ICONS: Record = {
* Find the i18n keys for a node type, with fuzzy matching so both
* "trigger.message_trigger" and "message_trigger" work.
*/
-export function findNodeI18nKeys(type: string): { labelKey: string; descriptionKey: string } | undefined {
+export function findNodeI18nKeys(
+ type: string,
+): { labelKey: string; descriptionKey: string } | undefined {
let keys = NODE_TYPE_I18N_KEYS[type];
if (keys) return keys;
@@ -234,7 +364,13 @@ export function findNodeI18nKeys(type: string): { labelKey: string; descriptionK
if (parts.length > 1) {
keys = NODE_TYPE_I18N_KEYS[type]; // already tried
} else {
- for (const cat of ['trigger', 'process', 'control', 'action', 'integration']) {
+ for (const cat of [
+ 'trigger',
+ 'process',
+ 'control',
+ 'action',
+ 'integration',
+ ]) {
keys = NODE_TYPE_I18N_KEYS[`${cat}.${typeName}`];
if (keys) return keys;
}
diff --git a/web/src/app/home/workflows/components/workflow-editor/workflow-node-metadata.ts b/web/src/app/home/workflows/components/workflow-editor/workflow-node-metadata.ts
index 8c0387ae..82dcaf6a 100644
--- a/web/src/app/home/workflows/components/workflow-editor/workflow-node-metadata.ts
+++ b/web/src/app/home/workflows/components/workflow-editor/workflow-node-metadata.ts
@@ -3,10 +3,7 @@ import type {
WorkflowPortDefinition,
} from '@/app/infra/entities/api';
import type { I18nObject } from '@/app/infra/entities/common';
-import {
- getNodeConfig,
- type NodeConfigMeta,
-} from './node-configs';
+import { getNodeConfig, type NodeConfigMeta } from './node-configs';
export const WORKFLOW_NODE_CATEGORIES = [
'trigger',
@@ -59,7 +56,9 @@ function normalizePort(
};
}
-function toBackendI18nObject(value?: I18nObject): Record | undefined {
+function toBackendI18nObject(
+ value?: I18nObject,
+): Record | undefined {
if (!value) return undefined;
return {
@@ -103,7 +102,9 @@ function getLocalConfigVariants(type: string): string[] {
return [...variants];
}
-export function getLocalNodeTypeMeta(type: string): WorkflowNodeTypeMetadata | null {
+export function getLocalNodeTypeMeta(
+ type: string,
+): WorkflowNodeTypeMetadata | null {
let localConfig: NodeConfigMeta | undefined;
for (const variant of getLocalConfigVariants(type)) {
@@ -138,30 +139,27 @@ export function normalizeWorkflowNodeTypeMeta(
const category =
nodeType?.category || localMeta?.category || resolveNodeTypeCategory(type);
- const inputs =
- nodeType?.inputs?.length
- ? nodeType.inputs.map((input) =>
- normalizePort('workflows.nodeInputs', input),
- )
- : localMeta?.inputs?.length
- ? localMeta.inputs
- : [DEFAULT_INPUT_PORT];
+ const inputs = nodeType?.inputs?.length
+ ? nodeType.inputs.map((input) =>
+ normalizePort('workflows.nodeInputs', input),
+ )
+ : localMeta?.inputs?.length
+ ? localMeta.inputs
+ : [DEFAULT_INPUT_PORT];
- const outputs =
- nodeType?.outputs?.length
- ? nodeType.outputs.map((output) =>
- normalizePort('workflows.nodeOutputs', output),
- )
- : localMeta?.outputs?.length
- ? localMeta.outputs
- : [DEFAULT_OUTPUT_PORT];
+ const outputs = nodeType?.outputs?.length
+ ? nodeType.outputs.map((output) =>
+ normalizePort('workflows.nodeOutputs', output),
+ )
+ : localMeta?.outputs?.length
+ ? localMeta.outputs
+ : [DEFAULT_OUTPUT_PORT];
- const configSchema =
- nodeType?.config_schema?.length
- ? nodeType.config_schema
- : localMeta?.config_schema?.length
- ? localMeta.config_schema
- : [];
+ const configSchema = nodeType?.config_schema?.length
+ ? nodeType.config_schema
+ : localMeta?.config_schema?.length
+ ? localMeta.config_schema
+ : [];
return {
type,
diff --git a/web/src/app/home/workflows/components/workflow-form/WorkflowFormComponent.tsx b/web/src/app/home/workflows/components/workflow-form/WorkflowFormComponent.tsx
index 2f40598c..9fc4e84f 100644
--- a/web/src/app/home/workflows/components/workflow-form/WorkflowFormComponent.tsx
+++ b/web/src/app/home/workflows/components/workflow-form/WorkflowFormComponent.tsx
@@ -6,7 +6,13 @@ import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Switch } from '@/components/ui/switch';
import { Workflow } from '@/app/infra/entities/api';
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from '@/components/ui/card';
import { Trash2, AlertTriangle } from 'lucide-react';
import {
AlertDialog,
@@ -101,7 +107,9 @@ export default function WorkflowFormComponent({
{/* Description */}
-
+
-
+
@@ -135,15 +140,21 @@ export default function WorkflowFormComponent({