Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/application/flow/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from models_provider.tools import get_model_credential
from tools.models.tool import Tool

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

Expand Down
3 changes: 2 additions & 1 deletion apps/application/flow/step_node/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .data_source_web_node.impl.base_data_source_web_node import BaseDataSourceWebNode
from .direct_reply_node import *
from .document_extract_node import *
from .empty_node import BaseEmptyNode
from .form_node import *
from .image_generate_step_node import *
from .image_to_video_step_node import BaseImageToVideoNode
Expand Down Expand Up @@ -44,7 +45,7 @@
from .tool_start_node import BaseToolStartStepNode

node_list = [BaseStartStepNode, BaseChatNode, BaseSearchKnowledgeNode, BaseSearchDocumentNode, BaseQuestionNode,
BaseConditionNode, BaseReplyNode,
BaseConditionNode, BaseReplyNode, BaseEmptyNode,
BaseToolNodeNode, BaseToolLibNodeNode, BaseRerankerNode, BaseApplicationNode,
BaseDocumentExtractNode,
BaseImageUnderstandNode, BaseFormNode, BaseSpeechToTextNode, BaseTextToSpeechNode,
Expand Down
1 change: 1 addition & 0 deletions apps/application/flow/step_node/empty_node/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .impl import BaseEmptyNode
39 changes: 39 additions & 0 deletions apps/application/flow/step_node/empty_node/i_empty_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# coding=utf-8
"""
@project: MaxKB
@Author:AI Assistant
@file: i_empty_node.py
@date:2026/06/10
@desc: 空节点接口定义 - 用于流程判断的ELSE分支占位
"""
from typing import Type

from rest_framework import serializers

from application.flow.common import WorkflowMode
from application.flow.i_step_node import INode, NodeResult


class EmptyNodeParamsSerializer(serializers.Serializer):
"""空节点参数序列化器 - 无需任何参数"""

def is_valid(self, *, raise_exception=False):
# 空节点不需要验证任何参数,直接返回 True
return True


class IEmptyNode(INode):
"""空节点接口"""
type = 'empty-node'
support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP,
WorkflowMode.KNOWLEDGE, WorkflowMode.TOOL, WorkflowMode.TOOL_LOOP]

def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
return EmptyNodeParamsSerializer

def _run(self):
return self.execute()

def execute(self, **kwargs) -> NodeResult:
"""执行空节点 - 不产生任何输出"""
return NodeResult({}, {})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .base_empty_node import BaseEmptyNode
37 changes: 37 additions & 0 deletions apps/application/flow/step_node/empty_node/impl/base_empty_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# coding=utf-8
"""
@project: MaxKB
@Author:AI Assistant
@file: base_empty_node.py
@date:2026/06/10
@desc: 空节点实现 - 用于流程判断的ELSE分支占位
"""
from application.flow.i_step_node import NodeResult
from application.flow.step_node.empty_node.i_empty_node import IEmptyNode


class BaseEmptyNode(IEmptyNode):
"""空节点实现类"""

def save_context(self, details, workflow_manage):
"""保存上下文 - 空节点无需保存任何内容"""
self.context['exception_message'] = details.get('err_message')

def execute(self, **kwargs) -> NodeResult:
"""
执行空节点
空节点不产生任何输出,仅作为流程占位符
"""
return NodeResult({}, {})

def get_details(self, index: int, **kwargs):
"""获取节点执行详情"""
return {
'name': self.node.properties.get('stepName'),
"index": index,
'run_time': self.context.get('run_time'),
'type': self.node.type,
'status': self.status,
'err_message': self.err_message,
'enableException': self.node.properties.get('enableException'),
}
4 changes: 4 additions & 0 deletions ui/src/assets/workflow/icon_empty.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions ui/src/components/execution-detail-card/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,18 @@
</div>
</template>

<!-- 空节点 -->
<template v-if="data.type === WorkflowType.EmptyNode">
<div class="card-never border-r-6">
<div class="p-8-12 lighter">
<div class="empty-node-info">
<p class="mb-8">{{ $t('workflow.nodes.emptyNode.description') }}</p>
<p class="lighter">{{ $t('workflow.nodes.emptyNode.text') }}</p>
</div>
</div>
</div>
</template>

<!-- 文档内容提取 -->
<template v-if="data.type === WorkflowType.DocumentExtractNode">
<div class="card-never border-r-6">
Expand Down
1 change: 1 addition & 0 deletions ui/src/enums/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum WorkflowType {
Question = 'question-node',
Condition = 'condition-node',
Reply = 'reply-node',
EmptyNode = 'empty-node',
ToolLib = 'tool-lib-node',
ToolWorkflowLib = 'tool-workflow-lib-node',
ToolLibCustom = 'tool-node',
Expand Down
6 changes: 6 additions & 0 deletions ui/src/locales/lang/en-US/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,12 @@ You are a master of problem optimization, adept at accurately inferring user int
text: 'Specify reply content, referenced variables will be converted to strings for output',
replyContent: 'Reply Content',
},
emptyNode: {
label: 'Empty Node',
text: 'No operation, used as a placeholder node in the workflow',
description: 'This is an empty node',
hint: 'Used when no action is needed',
},
rerankerNode: {
label: 'Multi-path Recall',
text: 'Use a re-ranking model to refine retrieval results from multiple knowledge sources',
Expand Down
6 changes: 6 additions & 0 deletions ui/src/locales/lang/zh-CN/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,12 @@ export default {
text: '指定回复内容,引用变量会转换为字符串进行输出',
replyContent: '回复内容',
},
emptyNode: {
label: '空节点',
text: '不执行任何操作,仅作为流程占位节点使用',
description: '这是一个空节点',
hint: '当不需要执行任何操作时使用',
},
rerankerNode: {
label: '多路召回',
text: '使用重排模型对多个知识库的检索结果进行二次召回',
Expand Down
2 changes: 1 addition & 1 deletion ui/src/locales/lang/zh-Hant/views/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export default {
},
reasoningContent: {
label: '輸出思考',
tooltip: '請根據模型返回的思考標簽設置,標簽中間的內容將會認定爲思考過程',
tooltip: '請根據模型返回的思考標籤設置,標籤中間的內容將會認定爲思考過程',
start: '開始',
end: '結束',
},
Expand Down
8 changes: 7 additions & 1 deletion ui/src/locales/lang/zh-Hant/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export default {
custom: '手動',
customTooltip: '手動設置標籤過濾條件',
auto: '自動',
autoTooltip: '根據檢索問題自動匹配文檔標簽',
autoTooltip: '根據檢索問題自動匹配文檔標籤',
documentList: '文檔列表',
knowledgeList: '知識庫列表',
result: '檢索結果',
Expand Down Expand Up @@ -243,6 +243,12 @@ export default {
text: '指定回覆內容,引用變量會轉換為字符串進行輸出',
replyContent: '回覆內容',
},
emptyNode: {
label: '空節點',
text: '不執行任何操作,僅作爲流程佔位節點使用',
description: '這是一個空節點',
hint: '當不需要執行任何操作時使用',
},
rerankerNode: {
label: '多路召回',
text: '使用重排模型對多個知識庫的檢索結果進行二次召回',
Expand Down
9 changes: 7 additions & 2 deletions ui/src/workflow/common/NodeContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
@drag.prevent
@dragover.prevent
@dragend.prevent
style="width: 69%"
>
<component
:is="iconComponent(`${nodeModel.type}-icon`)"
Expand All @@ -28,7 +27,13 @@
></h4>
</div>

<div @mousemove.stop @mousedown.stop @keydown.stop @click.stop>
<div
@mousemove.stop
@mousedown.stop
@keydown.stop
@click.stop
style="min-width: 85px; text-align: right"
>

@wangliang181230 wangliang181230 Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

顺便优化:移除节点左侧标题的宽度 width: 69%,固定右侧按钮的宽度(注:使用min-width更保险)使左侧标题能够显示出更多字来。

<el-button text @click="showNode = !showNode">
<el-icon class="arrow-icon color-secondary" :class="showNode ? 'rotate-180' : ''"
><ArrowDownBold />
Expand Down
27 changes: 20 additions & 7 deletions ui/src/workflow/common/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,18 @@ export const replyNode = {
},
},
}
export const emptyNode = {
type: WorkflowType.EmptyNode,
text: t('workflow.nodes.emptyNode.text'),
label: t('workflow.nodes.emptyNode.label'),
height: 150,
properties: {
stepName: t('workflow.nodes.emptyNode.label'),
config: {
fields: [],
},
},
}
export const rerankerNode = {
type: WorkflowType.RerankerNode,
text: t('workflow.nodes.rerankerNode.text'),
Expand Down Expand Up @@ -784,7 +796,6 @@ export const knowledgeMenuNodes = [
questionNode,
],
},

{
label: t('workflow.nodes.classify.businessLogic'),
list: [conditionNode, replyNode, loopNode],
Expand All @@ -800,7 +811,7 @@ export const knowledgeMenuNodes = [
},
{
label: t('common.other'),
list: [mcpNode, toolNode],
list: [mcpNode, toolNode, emptyNode],
},
]

Expand Down Expand Up @@ -839,7 +850,7 @@ export const menuNodes = [
},
{
label: t('common.other'),
list: [mcpNode, toolNode],
list: [mcpNode, toolNode, emptyNode],
},
]
export const applicationLoopMenuNodes = [
Expand Down Expand Up @@ -877,7 +888,7 @@ export const applicationLoopMenuNodes = [
},
{
label: t('common.other'),
list: [mcpNode, toolNode],
list: [mcpNode, toolNode, emptyNode],
},
]
export const knowledgeLoopMenuNodes = [
Expand Down Expand Up @@ -919,7 +930,7 @@ export const knowledgeLoopMenuNodes = [
},
{
label: t('common.other'),
list: [mcpNode, toolNode],
list: [mcpNode, toolNode, emptyNode],
},
]
export const toolLoopMenuNodes = [
Expand Down Expand Up @@ -961,7 +972,7 @@ export const toolLoopMenuNodes = [
},
{
label: t('common.other'),
list: [mcpNode, toolNode],
list: [mcpNode, toolNode, emptyNode],
},
]
const toolMenuNodes = [
Expand Down Expand Up @@ -1005,7 +1016,7 @@ const toolMenuNodes = [
},
{
label: t('common.other'),
list: [mcpNode, toolNode],
list: [mcpNode, toolNode, emptyNode],
},
]
export const getMenuNodes = (workflowMode: WorkflowMode) => {
Expand Down Expand Up @@ -1152,6 +1163,7 @@ export const nodeDict: any = {
[WorkflowType.Base]: baseNode,
[WorkflowType.Start]: startNode,
[WorkflowType.Reply]: replyNode,
[WorkflowType.EmptyNode]: emptyNode,
[WorkflowType.ToolLib]: toolNode,
[WorkflowType.ToolWorkflowLib]: toolWorkflowLibNode,
[WorkflowType.ToolLibCustom]: toolNode,
Expand Down Expand Up @@ -1283,6 +1295,7 @@ const nodeLocaleBindings: Array<[any, string, string]> = [
],
[conditionNode, 'workflow.nodes.conditionNode.text', 'workflow.nodes.conditionNode.label'],
[replyNode, 'workflow.nodes.replyNode.text', 'workflow.nodes.replyNode.label'],
[emptyNode, 'workflow.nodes.emptyNode.text', 'workflow.nodes.emptyNode.label'],
[rerankerNode, 'workflow.nodes.rerankerNode.text', 'workflow.nodes.rerankerNode.label'],
[formNode, 'workflow.nodes.formNode.text', 'workflow.nodes.formNode.label'],
[
Expand Down
2 changes: 2 additions & 0 deletions ui/src/workflow/common/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { t } from '@/locales'
const end_nodes: Array<string> = [
WorkflowType.AiChat,
WorkflowType.Reply,
WorkflowType.EmptyNode,
WorkflowType.ToolLib,
WorkflowType.ToolLibCustom,
WorkflowType.ImageUnderstandNode,
Expand All @@ -27,6 +28,7 @@ const end_nodes: Array<string> = [
const loop_end_nodes: Array<string> = [
WorkflowType.AiChat,
WorkflowType.Reply,
WorkflowType.EmptyNode,
WorkflowType.ToolLib,
WorkflowType.ToolLibCustom,
WorkflowType.ImageUnderstandNode,
Expand Down
6 changes: 6 additions & 0 deletions ui/src/workflow/icons/empty-node-icon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<el-avatar shape="square" style="background: #9CA3AF">
<img src="@/assets/workflow/icon_empty.svg" style="width: 75%" alt="" />
</el-avatar>
</template>
<script setup lang="ts"></script>
14 changes: 14 additions & 0 deletions ui/src/workflow/nodes/empty-node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import EmptyNodeVue from './index.vue'
import { AppNode, AppNodeModel } from '@/workflow/common/app-node'

class EmptyNode extends AppNode {
constructor(props: any) {
super(props, EmptyNodeVue)
}
}

export default {
type: 'empty-node',
model: AppNodeModel,
view: EmptyNode
}
Loading
Loading