From 96bdf43cb3e05d695019a49083cbb9ca6962834b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Mon, 30 Mar 2026 12:14:09 +0800 Subject: [PATCH 1/9] feat: support `regex` and `wildcard` comparison --- apps/application/flow/compare/__init__.py | 4 +- .../application/flow/compare/regex_compare.py | 39 +++++++++++++++++++ .../flow/compare/wildcard_compare.py | 22 +++++++++++ ui/src/locales/lang/en-US/workflow.ts | 2 + ui/src/locales/lang/zh-CN/workflow.ts | 2 + ui/src/locales/lang/zh-Hant/workflow.ts | 2 + ui/src/workflow/common/data.ts | 3 +- 7 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 apps/application/flow/compare/regex_compare.py create mode 100644 apps/application/flow/compare/wildcard_compare.py diff --git a/apps/application/flow/compare/__init__.py b/apps/application/flow/compare/__init__.py index f0dd01523f5..0de69b87b14 100644 --- a/apps/application/flow/compare/__init__.py +++ b/apps/application/flow/compare/__init__.py @@ -26,9 +26,11 @@ from .not_contain_compare import * from .not_equal_compare import * from .start_with import StartWithCompare +from .regex_compare import * +from .wildcard_compare import * compare_handle_list = [GECompare(), GTCompare(), ContainCompare(), EqualCompare(), LTCompare(), LECompare(), LenLECompare(), LenGECompare(), LenEqualCompare(), LenGTCompare(), LenLTCompare(), IsNullCompare(), IsNotNullCompare(), NotContainCompare(), NotEqualCompare(), IsTrueCompare(), IsNotTrueCompare(), StartWithCompare(), - EndWithCompare()] + EndWithCompare(), RegexCompare(), WildcardCompare()] diff --git a/apps/application/flow/compare/regex_compare.py b/apps/application/flow/compare/regex_compare.py new file mode 100644 index 00000000000..956f56636ec --- /dev/null +++ b/apps/application/flow/compare/regex_compare.py @@ -0,0 +1,39 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:wangliang181230 + @file: regex_compare.py + @date:2026/3/30 12:11 + @desc: +""" +import re +from typing import List + +from application.flow.compare import Compare +from common.cache.mem_cache import MemCache + +regex_cache = MemCache('regex', { + 'TIMEOUT': 3600, # 缓存有效期为 1 小时 + 'OPTIONS': { + 'MAX_ENTRIES': 500, # 最多缓存 500 个条目 + 'CULL_FREQUENCY': 10, # 达到上限时,删除约 1/10 的缓存 + }, +}) + + +def compile_and_cache(regex_str): + regex = regex_cache.get(regex_str) + if not regex: + regex = re.compile(regex_str) + regex_cache.set(regex_str, regex) + return regex + +class RegularExpressionCompare(Compare): + + def support(self, node_id, fields: List[str], source_value, compare, target_value): + if compare == 'regex': + return True + + def compare(self, source_value, compare, target_value): + regex = compile_and_cache(target_value) + return bool(regex.match(str(source_value))) diff --git a/apps/application/flow/compare/wildcard_compare.py b/apps/application/flow/compare/wildcard_compare.py new file mode 100644 index 00000000000..a4d144187d3 --- /dev/null +++ b/apps/application/flow/compare/wildcard_compare.py @@ -0,0 +1,22 @@ +# coding=utf-8 +""" + @project: maxkb + @Author:wangliang181230 + @file: wildcard_compare.py + @date:2026/3/30 12:11 + @desc: +""" +from typing import List + +from application.flow.compare import Compare + +import fnmatch + +class WildcardCompare(Compare): + + def support(self, node_id, fields: List[str], source_value, compare, target_value): + if compare == 'wildcard': + return True + + def compare(self, source_value, compare, target_value): + return fnmatch.fnmatch(str(source_value), str(target_value)) diff --git a/ui/src/locales/lang/en-US/workflow.ts b/ui/src/locales/lang/en-US/workflow.ts index d4268a6f242..f87956b3f7a 100644 --- a/ui/src/locales/lang/en-US/workflow.ts +++ b/ui/src/locales/lang/en-US/workflow.ts @@ -538,6 +538,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', }, 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 cdb243d9d6a..3e9a5d8a7d8 100644 --- a/ui/src/locales/lang/zh-CN/workflow.ts +++ b/ui/src/locales/lang/zh-CN/workflow.ts @@ -529,6 +529,8 @@ export default { len_lt: '长度小于', is_true: '为真', is_not_true: '不为真', + regex: '正则匹配', + wildcard: '通配符匹配', }, SystemPromptPlaceholder: '系统提示词,可以引用系统中的变量:如', UserPromptPlaceholder: '用户提示词,可以引用系统中的变量:如', diff --git a/ui/src/locales/lang/zh-Hant/workflow.ts b/ui/src/locales/lang/zh-Hant/workflow.ts index 51ceadff86a..8d482d1f2b6 100644 --- a/ui/src/locales/lang/zh-Hant/workflow.ts +++ b/ui/src/locales/lang/zh-Hant/workflow.ts @@ -523,6 +523,8 @@ export default { len_lt: '長度小於', is_true: '為真', is_not_true: '不為真', + regex: '正則匹配', + wildcard: '通配符匹配', }, SystemPromptPlaceholder: '系統提示詞,可以引用系統中的變量:如', UserPromptPlaceholder: '用戶提示詞,可以引用系統中的變量:如', diff --git a/ui/src/workflow/common/data.ts b/ui/src/workflow/common/data.ts index e44a7cb24a0..cc992d9548b 100644 --- a/ui/src/workflow/common/data.ts +++ b/ui/src/workflow/common/data.ts @@ -1092,7 +1092,8 @@ export const compareList = [ { value: 'is_true', label: t('workflow.compare.is_true') }, { value: 'is_not_true', label: t('workflow.compare.is_not_true') }, { 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, From f1fb8f6cc0d1b7461516b6c66a6b137c454a52ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Mon, 30 Mar 2026 12:38:51 +0800 Subject: [PATCH 2/9] fix --- apps/application/flow/compare/regex_compare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/application/flow/compare/regex_compare.py b/apps/application/flow/compare/regex_compare.py index 956f56636ec..3d6867e67cf 100644 --- a/apps/application/flow/compare/regex_compare.py +++ b/apps/application/flow/compare/regex_compare.py @@ -28,7 +28,7 @@ def compile_and_cache(regex_str): regex_cache.set(regex_str, regex) return regex -class RegularExpressionCompare(Compare): +class RegexCompare(Compare): def support(self, node_id, fields: List[str], source_value, compare, target_value): if compare == 'regex': From 20ee3b0326ad6361ebb7e06cd699457200e4f0f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Mon, 30 Mar 2026 13:32:07 +0800 Subject: [PATCH 3/9] fix --- ui/src/workflow/common/data.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/workflow/common/data.ts b/ui/src/workflow/common/data.ts index cc992d9548b..b304b809554 100644 --- a/ui/src/workflow/common/data.ts +++ b/ui/src/workflow/common/data.ts @@ -1092,6 +1092,7 @@ export const compareList = [ { value: 'is_true', label: t('workflow.compare.is_true') }, { value: 'is_not_true', label: t('workflow.compare.is_not_true') }, { value: 'start_with', label: 'startWith' }, + { value: 'end_with', label: 'endWith' }, { value: 'regex', label: t('workflow.compare.regex') }, { value: 'wildcard', label: t('workflow.compare.wildcard') }, ] From 03ca8dc88ecfc2430fb9f409d0ce2eb3882dc86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Mon, 30 Mar 2026 14:00:27 +0800 Subject: [PATCH 4/9] optimize --- apps/application/flow/compare/regex_compare.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/application/flow/compare/regex_compare.py b/apps/application/flow/compare/regex_compare.py index 3d6867e67cf..17c2314b15e 100644 --- a/apps/application/flow/compare/regex_compare.py +++ b/apps/application/flow/compare/regex_compare.py @@ -12,7 +12,7 @@ from application.flow.compare import Compare from common.cache.mem_cache import MemCache -regex_cache = MemCache('regex', { +pattern_cache = MemCache('regex', { 'TIMEOUT': 3600, # 缓存有效期为 1 小时 'OPTIONS': { 'MAX_ENTRIES': 500, # 最多缓存 500 个条目 @@ -21,12 +21,12 @@ }) -def compile_and_cache(regex_str): - regex = regex_cache.get(regex_str) - if not regex: - regex = re.compile(regex_str) - regex_cache.set(regex_str, regex) - return regex +def compile_and_cache(regex): + pattern = pattern_cache.get(regex) + if not pattern: + pattern = re.compile(regex) + pattern_cache.set(regex, pattern) + return pattern class RegexCompare(Compare): @@ -35,5 +35,5 @@ def support(self, node_id, fields: List[str], source_value, compare, target_valu return True def compare(self, source_value, compare, target_value): - regex = compile_and_cache(target_value) - return bool(regex.match(str(source_value))) + pattern = compile_and_cache(str(target_value)) + return bool(pattern.match(str(source_value))) From 1c4fe18b583af8d982a29c36b07503d87b0c966e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Mon, 30 Mar 2026 14:39:23 +0800 Subject: [PATCH 5/9] =?UTF-8?q?optimize:=20=E9=80=9A=E9=85=8D=E7=AC=A6?= =?UTF-8?q?=E8=BD=AC=E6=AD=A3=E5=88=99=E6=89=A7=E8=A1=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flow/compare/wildcard_compare.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/application/flow/compare/wildcard_compare.py b/apps/application/flow/compare/wildcard_compare.py index a4d144187d3..70cb7458079 100644 --- a/apps/application/flow/compare/wildcard_compare.py +++ b/apps/application/flow/compare/wildcard_compare.py @@ -6,11 +6,29 @@ @date:2026/3/30 12:11 @desc: """ +import fnmatch +import re from typing import List from application.flow.compare import Compare +from common.cache.mem_cache import MemCache -import fnmatch +pattern_cache = MemCache('wildcard_to_regex', { + 'TIMEOUT': 3600, # 缓存有效期为 1 小时 + 'OPTIONS': { + 'MAX_ENTRIES': 500, # 最多缓存 500 个条目 + 'CULL_FREQUENCY': 10, # 达到上限时,删除约 1/10 的缓存 + }, +}) + +# 转成正则执行,性能更高 +def translate_and_compile_and_cache(wildcard): + pattern = pattern_cache.get(wildcard) + if not pattern: + regex = fnmatch.translate(wildcard) + pattern = re.compile(regex) + pattern_cache.set(wildcard, pattern) + return pattern class WildcardCompare(Compare): @@ -19,4 +37,6 @@ def support(self, node_id, fields: List[str], source_value, compare, target_valu return True def compare(self, source_value, compare, target_value): - return fnmatch.fnmatch(str(source_value), str(target_value)) + # 转成正则执行,性能更高 + pattern = translate_and_compile_and_cache(str(target_value)) + return bool(pattern.match(str(source_value))) From 11a007db32cc3a4e46f44d19155cd0192e2036d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Mon, 30 Mar 2026 14:52:05 +0800 Subject: [PATCH 6/9] =?UTF-8?q?optimize:=20=E7=BC=93=E5=AD=98=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E6=9B=B4=E5=B0=91=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/application/flow/compare/regex_compare.py | 16 ++++++++-------- .../application/flow/compare/wildcard_compare.py | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/application/flow/compare/regex_compare.py b/apps/application/flow/compare/regex_compare.py index 17c2314b15e..60fdeec00c9 100644 --- a/apps/application/flow/compare/regex_compare.py +++ b/apps/application/flow/compare/regex_compare.py @@ -12,7 +12,7 @@ from application.flow.compare import Compare from common.cache.mem_cache import MemCache -pattern_cache = MemCache('regex', { +match_cache = MemCache('regex', { 'TIMEOUT': 3600, # 缓存有效期为 1 小时 'OPTIONS': { 'MAX_ENTRIES': 500, # 最多缓存 500 个条目 @@ -22,11 +22,11 @@ def compile_and_cache(regex): - pattern = pattern_cache.get(regex) - if not pattern: - pattern = re.compile(regex) - pattern_cache.set(regex, pattern) - return pattern + match = match_cache.get(regex) + if not match: + match = re.compile(regex).match + match_cache.set(regex, match) + return match class RegexCompare(Compare): @@ -35,5 +35,5 @@ def support(self, node_id, fields: List[str], source_value, compare, target_valu return True def compare(self, source_value, compare, target_value): - pattern = compile_and_cache(str(target_value)) - return bool(pattern.match(str(source_value))) + match = compile_and_cache(str(target_value)) + return bool(match(str(source_value))) diff --git a/apps/application/flow/compare/wildcard_compare.py b/apps/application/flow/compare/wildcard_compare.py index 70cb7458079..b3210156820 100644 --- a/apps/application/flow/compare/wildcard_compare.py +++ b/apps/application/flow/compare/wildcard_compare.py @@ -13,7 +13,7 @@ from application.flow.compare import Compare from common.cache.mem_cache import MemCache -pattern_cache = MemCache('wildcard_to_regex', { +match_cache = MemCache('wildcard_to_regex', { 'TIMEOUT': 3600, # 缓存有效期为 1 小时 'OPTIONS': { 'MAX_ENTRIES': 500, # 最多缓存 500 个条目 @@ -23,12 +23,12 @@ # 转成正则执行,性能更高 def translate_and_compile_and_cache(wildcard): - pattern = pattern_cache.get(wildcard) - if not pattern: + match = match_cache.get(wildcard) + if not match: regex = fnmatch.translate(wildcard) - pattern = re.compile(regex) - pattern_cache.set(wildcard, pattern) - return pattern + match = re.compile(regex).match + match_cache.set(wildcard, match) + return match class WildcardCompare(Compare): @@ -38,5 +38,5 @@ def support(self, node_id, fields: List[str], source_value, compare, target_valu def compare(self, source_value, compare, target_value): # 转成正则执行,性能更高 - pattern = translate_and_compile_and_cache(str(target_value)) - return bool(pattern.match(str(source_value))) + match = translate_and_compile_and_cache(str(target_value)) + return bool(match(str(source_value))) From 75cc35335278fe635f69dd088fd5c8c5d8e55d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Tue, 31 Mar 2026 13:44:52 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=E5=B0=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/application/flow/compare/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/application/flow/compare/__init__.py b/apps/application/flow/compare/__init__.py index 0de69b87b14..3841a339b3d 100644 --- a/apps/application/flow/compare/__init__.py +++ b/apps/application/flow/compare/__init__.py @@ -26,8 +26,8 @@ from .not_contain_compare import * from .not_equal_compare import * from .start_with import StartWithCompare -from .regex_compare import * -from .wildcard_compare import * +from .regex_compare import RegexCompare +from .wildcard_compare import WildcardCompare compare_handle_list = [GECompare(), GTCompare(), ContainCompare(), EqualCompare(), LTCompare(), LECompare(), LenLECompare(), LenGECompare(), LenEqualCompare(), LenGTCompare(), LenLTCompare(), From 070180608c03e74f768dc70a5ef0da1e651d4f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Tue, 31 Mar 2026 13:47:12 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=E5=B0=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/application/flow/compare/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/application/flow/compare/__init__.py b/apps/application/flow/compare/__init__.py index 3841a339b3d..4e99bfa7270 100644 --- a/apps/application/flow/compare/__init__.py +++ b/apps/application/flow/compare/__init__.py @@ -25,8 +25,8 @@ from .lt_compare import * from .not_contain_compare import * from .not_equal_compare import * -from .start_with import StartWithCompare from .regex_compare import RegexCompare +from .start_with import StartWithCompare from .wildcard_compare import WildcardCompare compare_handle_list = [GECompare(), GTCompare(), ContainCompare(), EqualCompare(), LTCompare(), LECompare(), From 9dbdfe2df0de67cf8bdfb7ac7ffc9ba0e04e42f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Tue, 31 Mar 2026 13:55:54 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=E5=B0=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/application/flow/compare/regex_compare.py | 1 + apps/application/flow/compare/wildcard_compare.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/application/flow/compare/regex_compare.py b/apps/application/flow/compare/regex_compare.py index 60fdeec00c9..8fd98f3eca8 100644 --- a/apps/application/flow/compare/regex_compare.py +++ b/apps/application/flow/compare/regex_compare.py @@ -12,6 +12,7 @@ from application.flow.compare import Compare from common.cache.mem_cache import MemCache + match_cache = MemCache('regex', { 'TIMEOUT': 3600, # 缓存有效期为 1 小时 'OPTIONS': { diff --git a/apps/application/flow/compare/wildcard_compare.py b/apps/application/flow/compare/wildcard_compare.py index b3210156820..65ea2c74a5a 100644 --- a/apps/application/flow/compare/wildcard_compare.py +++ b/apps/application/flow/compare/wildcard_compare.py @@ -13,6 +13,7 @@ from application.flow.compare import Compare from common.cache.mem_cache import MemCache + match_cache = MemCache('wildcard_to_regex', { 'TIMEOUT': 3600, # 缓存有效期为 1 小时 'OPTIONS': { @@ -21,7 +22,7 @@ }, }) -# 转成正则执行,性能更高 + def translate_and_compile_and_cache(wildcard): match = match_cache.get(wildcard) if not match: @@ -37,6 +38,6 @@ def support(self, node_id, fields: List[str], source_value, compare, target_valu return True def compare(self, source_value, compare, target_value): - # 转成正则执行,性能更高 + # 转成正则,性能更高 match = translate_and_compile_and_cache(str(target_value)) return bool(match(str(source_value)))