Skip to content

Commit a3dfd40

Browse files
feat: Add a new node empty-node
1 parent 8b2d109 commit a3dfd40

18 files changed

Lines changed: 204 additions & 13 deletions

File tree

apps/application/flow/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from models_provider.tools import get_model_credential
2020
from tools.models.tool import Tool
2121

22-
end_nodes = ['ai-chat-node', 'reply-node', 'function-node', 'function-lib-node', 'application-node',
22+
end_nodes = ['ai-chat-node', 'reply-node', 'empty-node', 'function-node', 'function-lib-node', 'application-node',
2323
'image-understand-node', 'speech-to-text-node', 'text-to-speech-node', 'image-generate-node',
2424
'variable-assign-node']
2525

apps/application/flow/step_node/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .data_source_web_node.impl.base_data_source_web_node import BaseDataSourceWebNode
1414
from .direct_reply_node import *
1515
from .document_extract_node import *
16+
from .empty_node import BaseEmptyNode
1617
from .form_node import *
1718
from .image_generate_step_node import *
1819
from .image_to_video_step_node import BaseImageToVideoNode
@@ -44,7 +45,7 @@
4445
from .tool_start_node import BaseToolStartStepNode
4546

4647
node_list = [BaseStartStepNode, BaseChatNode, BaseSearchKnowledgeNode, BaseSearchDocumentNode, BaseQuestionNode,
47-
BaseConditionNode, BaseReplyNode,
48+
BaseConditionNode, BaseReplyNode, BaseEmptyNode,
4849
BaseToolNodeNode, BaseToolLibNodeNode, BaseRerankerNode, BaseApplicationNode,
4950
BaseDocumentExtractNode,
5051
BaseImageUnderstandNode, BaseFormNode, BaseSpeechToTextNode, BaseTextToSpeechNode,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .impl import BaseEmptyNode
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:AI Assistant
5+
@file: i_empty_node.py
6+
@date:2026/06/10
7+
@desc: 空节点接口定义 - 用于流程判断的ELSE分支占位
8+
"""
9+
from typing import Type
10+
11+
from rest_framework import serializers
12+
13+
from application.flow.common import WorkflowMode
14+
from application.flow.i_step_node import INode, NodeResult
15+
16+
17+
class EmptyNodeParamsSerializer(serializers.Serializer):
18+
"""空节点参数序列化器 - 无需任何参数"""
19+
20+
def is_valid(self, *, raise_exception=False):
21+
# 空节点不需要验证任何参数,直接返回 True
22+
return True
23+
24+
25+
class IEmptyNode(INode):
26+
"""空节点接口"""
27+
type = 'empty-node'
28+
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP,
29+
WorkflowMode.KNOWLEDGE, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]
30+
31+
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
32+
return EmptyNodeParamsSerializer
33+
34+
def _run(self):
35+
return self.execute()
36+
37+
def execute(self, **kwargs) -> NodeResult:
38+
"""执行空节点 - 不产生任何输出"""
39+
return NodeResult({}, {})
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .base_empty_node import BaseEmptyNode
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:AI Assistant
5+
@file: base_empty_node.py
6+
@date:2026/06/10
7+
@desc: 空节点实现 - 用于流程判断的ELSE分支占位
8+
"""
9+
from application.flow.i_step_node import NodeResult
10+
from application.flow.step_node.empty_node.i_empty_node import IEmptyNode
11+
12+
13+
class BaseEmptyNode(IEmptyNode):
14+
"""空节点实现类"""
15+
16+
def save_context(self, details, workflow_manage):
17+
"""保存上下文 - 空节点无需保存任何内容"""
18+
self.context['exception_message'] = details.get('err_message')
19+
20+
def execute(self, **kwargs) -> NodeResult:
21+
"""
22+
执行空节点
23+
空节点不产生任何输出,仅作为流程占位符
24+
"""
25+
return NodeResult({}, {})
26+
27+
def get_details(self, index: int, **kwargs):
28+
"""获取节点执行详情"""
29+
return {
30+
'name': self.node.properties.get('stepName'),
31+
"index": index,
32+
'run_time': self.context.get('run_time'),
33+
'type': self.node.type,
34+
'status': self.status,
35+
'err_message': self.err_message,
36+
'enableException': self.node.properties.get('enableException'),
37+
}
Lines changed: 4 additions & 0 deletions
Loading

ui/src/enums/application.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export enum WorkflowType {
1414
Question = 'question-node',
1515
Condition = 'condition-node',
1616
Reply = 'reply-node',
17+
EmptyNode = 'empty-node',
1718
ToolLib = 'tool-lib-node',
1819
ToolWorkflowLib = 'tool-workflow-lib-node',
1920
ToolLibCustom = 'tool-node',

ui/src/locales/lang/en-US/workflow.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ You are a master of problem optimization, adept at accurately inferring user int
243243
text: 'Specify reply content, referenced variables will be converted to strings for output',
244244
replyContent: 'Reply Content',
245245
},
246+
emptyNode: {
247+
label: 'Empty Node',
248+
text: 'No operation, used as a placeholder node in the workflow',
249+
description: 'This is an empty node',
250+
hint: 'Used when no action is needed',
251+
},
246252
rerankerNode: {
247253
label: 'Multi-path Recall',
248254
text: 'Use a re-ranking model to refine retrieval results from multiple knowledge sources',

ui/src/locales/lang/zh-CN/workflow.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ export default {
243243
text: '指定回复内容,引用变量会转换为字符串进行输出',
244244
replyContent: '回复内容',
245245
},
246+
emptyNode: {
247+
label: '空节点',
248+
text: '不执行任何操作,仅作为流程占位节点使用',
249+
description: '这是一个空节点',
250+
hint: '当不需要执行任何操作时使用',
251+
},
246252
rerankerNode: {
247253
label: '多路召回',
248254
text: '使用重排模型对多个知识库的检索结果进行二次召回',

0 commit comments

Comments
 (0)