Skip to content

Commit 2bc68c1

Browse files
committed
feat: migrate variable splitting node
1 parent f453354 commit 2bc68c1

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author: 虎虎虎
5+
@file: __init__.py
6+
@desc:
7+
"""
8+
9+
from .variable_splitting_node import VariableSplittingNode
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author: 虎虎虎
5+
@file: variable_splitting_node.py
6+
@desc: 变量拆分节点
7+
"""
8+
9+
import json
10+
11+
from django.utils.translation import gettext_lazy as _
12+
from jsonpath_ng.ext import parse
13+
from rest_framework import serializers
14+
15+
from application.workflow.common import WorkflowType
16+
from application.workflow.i_node import INode
17+
from common.cache.mem_cache import MemCache
18+
19+
jsonpath_expr_cache = MemCache(
20+
"parse_path",
21+
{
22+
"TIMEOUT": 3600, # 缓存有效期为 1 小时
23+
"OPTIONS": {
24+
"MAX_ENTRIES": 1000, # 最多缓存 1000 个条目
25+
"CULL_FREQUENCY": 10, # 达到上限时,删除约 1/10 的缓存
26+
},
27+
},
28+
)
29+
30+
31+
class VariableSplittingNodeParamsSerializer(serializers.Serializer):
32+
input_variable = serializers.ListField(required=True, label=_("input variable"))
33+
variable_list = serializers.ListField(required=True, label=_("Split variables"))
34+
35+
36+
def parse_and_cache(path):
37+
jsonpath_expr = jsonpath_expr_cache.get(path)
38+
if not jsonpath_expr:
39+
jsonpath_expr = parse(path)
40+
jsonpath_expr_cache.set(path, jsonpath_expr)
41+
return jsonpath_expr
42+
43+
44+
def smart_jsonpath_search(data: dict, path: str):
45+
"""智能 JSON Path 搜索。
46+
47+
- 单个匹配: 直接返回值
48+
- 多个匹配: 返回值的列表
49+
- 无匹配: 返回 None
50+
"""
51+
jsonpath_expr = parse_and_cache(path)
52+
matches = jsonpath_expr.find(data)
53+
54+
if not matches:
55+
return None
56+
elif len(matches) == 1:
57+
return matches[0].value
58+
else:
59+
return [match.value for match in matches]
60+
61+
62+
class VariableSplittingNode(INode):
63+
serializer_class = VariableSplittingNodeParamsSerializer
64+
supported_workflow_type_list = [
65+
WorkflowType.APPLICATION,
66+
WorkflowType.KNOWLEDGE,
67+
WorkflowType.TOOL,
68+
]
69+
type = "variable-splitting-node"
70+
71+
def execute(self):
72+
node_params = self.get_parameters()
73+
74+
input_variable = self.workflow_manage.get_reference_field(
75+
node_params.get("input_variable")[0],
76+
node_params.get("input_variable")[1:],
77+
)
78+
variable_list = node_params.get("variable_list", [])
79+
80+
if isinstance(input_variable, str):
81+
try:
82+
input_variable = json.loads(input_variable)
83+
except Exception:
84+
pass
85+
86+
self.write_context("request", input_variable)
87+
response = {v["field"]: smart_jsonpath_search(input_variable, v["expression"]) for v in variable_list}
88+
self.write_context("result", response)
89+
for key, value in response.items():
90+
self.write_context(key, value)
91+
92+
def get_details(self, index: int = 0, position: dict = None, old_details: dict = None, **kwargs):
93+
details = super().get_details(index, position, old_details, **kwargs)
94+
details.update(
95+
{
96+
"request": self.get_context("request"),
97+
"result": self.get_context("result"),
98+
"status": self.status.value if self.status else None,
99+
}
100+
)
101+
return details

0 commit comments

Comments
 (0)