From 471cd4aefe8562387d13f28476465c4e92dc4323 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com>
Date: Wed, 3 Jun 2026 17:30:38 +0800
Subject: [PATCH] feat: Supports `type_is` and `type_not` comparisons
---
apps/application/flow/compare/__init__.py | 8 +++++--
.../flow/compare/type_is_compare.py | 23 +++++++++++++++++++
.../flow/compare/type_not_compare.py | 23 +++++++++++++++++++
ui/src/locales/lang/en-US/workflow.ts | 9 ++++++--
ui/src/locales/lang/zh-CN/workflow.ts | 9 ++++++--
ui/src/locales/lang/zh-Hant/workflow.ts | 9 ++++++--
ui/src/workflow/common/data.ts | 12 ++++++----
.../workflow/nodes/condition-node/index.vue | 20 +++++++++++++---
.../workflow/nodes/loop-break-node/index.vue | 16 +++++++++++++
.../nodes/loop-continue-node/index.vue | 16 +++++++++++++
10 files changed, 130 insertions(+), 15 deletions(-)
create mode 100644 apps/application/flow/compare/type_is_compare.py
create mode 100644 apps/application/flow/compare/type_not_compare.py
diff --git a/apps/application/flow/compare/__init__.py b/apps/application/flow/compare/__init__.py
index ce0c430e1ad..e681859019b 100644
--- a/apps/application/flow/compare/__init__.py
+++ b/apps/application/flow/compare/__init__.py
@@ -28,6 +28,8 @@
from .not_equal_compare import NotEqualCompare
from .regex_compare import RegexCompare
from .start_with import StartWithCompare
+from .type_is_compare import TypeIsCompare
+from .type_not_compare import TypeNotCompare
from .wildcard_compare import WildcardCompare
_compare_handler_dict = {
@@ -35,6 +37,8 @@
'is_not_null': IsNotNullCompare(),
'contain': ContainCompare(),
'not_contain': NotContainCompare(),
+ 'regex': RegexCompare(),
+ 'wildcard': WildcardCompare(),
'eq': EqualCompare(),
'not_eq': NotEqualCompare(),
'ge': GECompare(),
@@ -48,10 +52,10 @@
'len_lt': LenLTCompare(),
'is_true': IsTrueCompare(),
'is_not_true': IsNotTrueCompare(),
+ 'type_is': TypeIsCompare(),
+ 'type_not': TypeNotCompare(),
'start_with': StartWithCompare(),
'end_with': EndWithCompare(),
- 'regex': RegexCompare(),
- 'wildcard': WildcardCompare(),
}
diff --git a/apps/application/flow/compare/type_is_compare.py b/apps/application/flow/compare/type_is_compare.py
new file mode 100644
index 00000000000..199f477f70c
--- /dev/null
+++ b/apps/application/flow/compare/type_is_compare.py
@@ -0,0 +1,23 @@
+# coding=utf-8
+"""
+@project: MaxKB
+@Author:wangliang181230
+@file:type_is_compare.py
+@date:2026/5/25 10:01
+@desc: “数据类型是” 比较器
+"""
+from .compare import Compare
+
+
+class TypeIsCompare(Compare):
+
+ def compare(self, source_value, compare, target_value):
+ try:
+ if target_value == "json":
+ return isinstance(source_value, (list, dict))
+ elif target_value == "num":
+ return isinstance(source_value, (int, float))
+ else:
+ return type(source_value).__name__ == target_value
+ except Exception:
+ return False
diff --git a/apps/application/flow/compare/type_not_compare.py b/apps/application/flow/compare/type_not_compare.py
new file mode 100644
index 00000000000..634cf057b96
--- /dev/null
+++ b/apps/application/flow/compare/type_not_compare.py
@@ -0,0 +1,23 @@
+# coding=utf-8
+"""
+@project: MaxKB
+@Author:wangliang181230
+@file:type_not_compare.py
+@date:2026/5/25 10:01
+@desc: “数据类型不是” 比较器
+"""
+from .compare import Compare
+
+
+class TypeNotCompare(Compare):
+
+ def compare(self, source_value, compare, target_value):
+ try:
+ if target_value == "json":
+ return not isinstance(source_value, (list, dict))
+ elif target_value == "num":
+ return not isinstance(source_value, (int, float))
+ else:
+ return type(source_value).__name__ != target_value
+ except Exception:
+ return False
diff --git a/ui/src/locales/lang/en-US/workflow.ts b/ui/src/locales/lang/en-US/workflow.ts
index f322667c8a3..d60c799fa7f 100644
--- a/ui/src/locales/lang/en-US/workflow.ts
+++ b/ui/src/locales/lang/en-US/workflow.ts
@@ -235,6 +235,9 @@ You are a master of problem optimization, adept at accurately inferring user int
requiredMessage: 'Please select conditions',
},
valueMessage: 'Please enter a value',
+ verify_type_compare: {
+ requiredMessage: 'Please select a type',
+ },
addCondition: 'Add Condition',
addBranch: 'Add Branch',
},
@@ -537,6 +540,8 @@ You are a master of problem optimization, adept at accurately inferring user int
is_not_null: 'Is not null',
contain: 'Contains',
not_contain: 'Does not contain',
+ regex: 'Regex matching',
+ wildcard: 'Wildcard matching',
eq: 'Equal to',
not_eq: 'Not equal to',
ge: 'Greater than or equal to',
@@ -550,8 +555,8 @@ You are a master of problem optimization, adept at accurately inferring user int
len_lt: 'Length less than',
is_true: 'Is true',
is_not_true: 'Is not true',
- regex: 'Regex matching',
- wildcard: 'Wildcard matching',
+ type_is: 'Type is',
+ type_not: 'Type not',
},
SystemPromptPlaceholder: 'System Prompt, can reference variables in the system, such as',
UserPromptPlaceholder: 'User Prompt, can reference variables in the system, such as',
diff --git a/ui/src/locales/lang/zh-CN/workflow.ts b/ui/src/locales/lang/zh-CN/workflow.ts
index 42cdf5a89f7..1aa3cb222e2 100644
--- a/ui/src/locales/lang/zh-CN/workflow.ts
+++ b/ui/src/locales/lang/zh-CN/workflow.ts
@@ -235,6 +235,9 @@ export default {
requiredMessage: '请选择条件',
},
valueMessage: '请输入值',
+ verify_type_compare: {
+ requiredMessage: '请选择类型',
+ },
addCondition: '添加条件',
addBranch: '添加分支',
},
@@ -528,6 +531,8 @@ export default {
is_not_null: '不为空',
contain: '包含',
not_contain: '不包含',
+ regex: '正则匹配',
+ wildcard: '通配符匹配',
eq: '等于',
not_eq: '不等于',
ge: '大于等于',
@@ -541,8 +546,8 @@ export default {
len_lt: '长度小于',
is_true: '为真',
is_not_true: '不为真',
- regex: '正则匹配',
- wildcard: '通配符匹配',
+ type_is: '类型是',
+ type_not: '类型不是',
},
SystemPromptPlaceholder: '系统提示词,可以引用系统中的变量:如',
UserPromptPlaceholder: '用户提示词,可以引用系统中的变量:如',
diff --git a/ui/src/locales/lang/zh-Hant/workflow.ts b/ui/src/locales/lang/zh-Hant/workflow.ts
index 6678d4536a7..0c19626e228 100644
--- a/ui/src/locales/lang/zh-Hant/workflow.ts
+++ b/ui/src/locales/lang/zh-Hant/workflow.ts
@@ -235,6 +235,9 @@ export default {
requiredMessage: '請選擇條件',
},
valueMessage: '請輸入值',
+ verify_type_compare: {
+ requiredMessage: '請選擇類型',
+ },
addCondition: '添加條件',
addBranch: '添加分支',
},
@@ -522,6 +525,8 @@ export default {
is_not_null: '不為空',
contain: '包含',
not_contain: '不包含',
+ regex: '正則匹配',
+ wildcard: '通配符匹配',
eq: '等於',
not_eq: '不等於',
ge: '大於等於',
@@ -535,8 +540,8 @@ export default {
len_lt: '長度小於',
is_true: '為真',
is_not_true: '不為真',
- regex: '正則匹配',
- wildcard: '通配符匹配',
+ type_is: '類型是',
+ type_not: '類型不是',
},
SystemPromptPlaceholder: '系統提示詞,可以引用系統中的變量:如',
UserPromptPlaceholder: '用戶提示詞,可以引用系統中的變量:如',
diff --git a/ui/src/workflow/common/data.ts b/ui/src/workflow/common/data.ts
index 23fe80e60e2..cb56d833151 100644
--- a/ui/src/workflow/common/data.ts
+++ b/ui/src/workflow/common/data.ts
@@ -1125,6 +1125,8 @@ export const compareList = [
{ value: 'is_not_null', label: t('workflow.compare.is_not_null') },
{ value: 'contain', label: t('workflow.compare.contain') },
{ value: 'not_contain', label: t('workflow.compare.not_contain') },
+ { value: 'regex', label: t('workflow.compare.regex') },
+ { value: 'wildcard', label: t('workflow.compare.wildcard') },
{ value: 'eq', label: t('workflow.compare.eq') },
{ value: 'not_eq', label: t('workflow.compare.not_eq') },
{ value: 'ge', label: t('workflow.compare.ge') },
@@ -1138,10 +1140,10 @@ export const compareList = [
{ value: 'len_lt', label: t('workflow.compare.len_lt') },
{ value: 'is_true', label: t('workflow.compare.is_true') },
{ value: 'is_not_true', label: t('workflow.compare.is_not_true') },
+ { value: 'type_is', label: t('workflow.compare.type_is') },
+ { value: 'type_not', label: t('workflow.compare.type_not') },
{ value: 'start_with', label: 'startWith' },
{ value: 'end_with', label: 'endWith' },
- { value: 'regex', label: t('workflow.compare.regex') },
- { value: 'wildcard', label: t('workflow.compare.wildcard') },
]
export const nodeDict: any = {
[WorkflowType.AiChat]: aiChatNode,
@@ -1533,6 +1535,8 @@ ${t('workflow.nodes.formNode.form_content_format2')}`,
'workflow.compare.is_not_null',
'workflow.compare.contain',
'workflow.compare.not_contain',
+ 'workflow.compare.regex',
+ 'workflow.compare.wildcard',
'workflow.compare.eq',
'workflow.compare.not_eq',
'workflow.compare.ge',
@@ -1546,9 +1550,9 @@ ${t('workflow.nodes.formNode.form_content_format2')}`,
'workflow.compare.len_lt',
'workflow.compare.is_true',
'workflow.compare.is_not_true',
+ 'workflow.compare.type_is',
+ 'workflow.compare.type_not',
].forEach((key, index) => defineLocaleGetter(compareList[index], 'label', key))
-defineLocaleGetter(compareList[19], 'label', 'workflow.compare.regex')
-defineLocaleGetter(compareList[20], 'label', 'workflow.compare.wildcard')
export function isWorkFlow(type: string | undefined) {
return type === 'WORK_FLOW'
diff --git a/ui/src/workflow/nodes/condition-node/index.vue b/ui/src/workflow/nodes/condition-node/index.vue
index d80c2c28cad..922aeb7692f 100644
--- a/ui/src/workflow/nodes/condition-node/index.vue
+++ b/ui/src/workflow/nodes/condition-node/index.vue
@@ -115,11 +115,25 @@
trigger: 'blur',
}"
>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/workflow/nodes/loop-break-node/index.vue b/ui/src/workflow/nodes/loop-break-node/index.vue
index d718dca4bc5..66b182374f1 100644
--- a/ui/src/workflow/nodes/loop-break-node/index.vue
+++ b/ui/src/workflow/nodes/loop-break-node/index.vue
@@ -83,7 +83,23 @@
trigger: 'blur',
}"
>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/workflow/nodes/loop-continue-node/index.vue b/ui/src/workflow/nodes/loop-continue-node/index.vue
index d718dca4bc5..66b182374f1 100644
--- a/ui/src/workflow/nodes/loop-continue-node/index.vue
+++ b/ui/src/workflow/nodes/loop-continue-node/index.vue
@@ -83,7 +83,23 @@
trigger: 'blur',
}"
>
+
+
+
+
+
+
+
+
+
+
+