Skip to content

Commit 5d1df33

Browse files
committed
feat: Support internal functionlib
--story=1017939 --user=刘瑞斌 【函数库】- 支持 系统内置函数 https://www.tapd.cn/57709429/s/1665943
1 parent 6e19167 commit 5d1df33

File tree

25 files changed

+603
-55
lines changed

25 files changed

+603
-55
lines changed

apps/application/flow/step_node/function_lib_node/impl/base_function_lib_node.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,14 @@ def execute(self, function_lib_id, input_field_list, **kwargs) -> NodeResult:
107107
), **field}
108108
for field in
109109
function_lib.input_field_list]}
110+
init_field_list = function_lib.init_field_list
111+
init_params = {field.get('field'): field.get('value') if field.get('value',
112+
None) is not None else field.get(
113+
'default_value') for field in init_field_list}
110114
self.context['params'] = params
111-
result = function_executor.exec_code(function_lib.code, params)
115+
# 合并初始化参数
116+
all_params = init_params | params
117+
result = function_executor.exec_code(function_lib.code, all_params)
112118
return NodeResult({'result': result}, {}, _write_context=write_context)
113119

114120
def get_details(self, index: int, **kwargs):

apps/application/serializers/application_serializers.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from dataset.models import DataSet, Document, Image
4646
from dataset.serializers.common_serializers import list_paragraph, get_embedding_model_by_dataset_id_list
4747
from embedding.models import SearchMode
48-
from function_lib.models.function import FunctionLib, PermissionType
48+
from function_lib.models.function import FunctionLib, PermissionType, FunctionType
4949
from function_lib.serializers.function_lib_serializer import FunctionLibSerializer, FunctionLibModelSerializer
5050
from setting.models import AuthOperate, TeamMemberPermission
5151
from setting.models.model_management import Model
@@ -810,8 +810,10 @@ def list_function_lib(self, with_valid=True):
810810
if with_valid:
811811
self.is_valid(raise_exception=True)
812812
application = QuerySet(Application).filter(id=self.data.get("application_id")).first()
813-
return FunctionLibSerializer.Query(data={'user_id': application.user_id, 'is_active': True}).list(
814-
with_valid=True)
813+
return FunctionLibSerializer.Query(
814+
data={'user_id': application.user_id, 'is_active': True,
815+
'function_type': FunctionType.PUBLIC}
816+
).list(with_valid=True)
815817

816818
def get_function_lib(self, function_lib_id, with_valid=True):
817819
if with_valid:
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# Generated by Django 4.2.15 on 2025-03-10 08:57
2+
3+
from django.db import migrations, models
4+
5+
function_template = """
6+
INSERT INTO function_lib (create_time, update_time, id, name, "desc", code, input_field_list,
7+
user_id, is_active, permission_type, function_type, icon,
8+
init_field_list, template_id)
9+
VALUES ('2025-02-25 07:44:40.141515 +00:00', '2025-03-11 06:33:53.248495 +00:00',
10+
'5e912f00-f34c-11ef-8a9c-5618c4394482', 'LangSearch API', e'A Web Search API supporting natural language search
11+
', e'def search(query, apikey):
12+
import requests
13+
import json
14+
url = "https://api.langsearch.com/v1/web-search"
15+
payload = json.dumps({
16+
"query": query,
17+
"summary": True,
18+
"freshness": "noLimit",
19+
"summary": True,
20+
"livecrawl": True,
21+
"count": 20
22+
})
23+
headers = {
24+
\'Authorization\': apikey,
25+
\'Content-Type\': \'application/json\'
26+
}
27+
# key从官网申请 https://langsearch.com/
28+
response = requests.request("POST", url, headers=headers, data=payload)
29+
if response.status_code == 200:
30+
return str(response.json())
31+
else:
32+
raise Exception(f"API请求失败: {response.status_code}, 错误信息: {response.text}")
33+
return(response.text)',
34+
'{"{\"name\": \"query\", \"type\": \"string\", \"source\": \"reference\", \"is_required\": true}"}',
35+
'f0dd8f71-e4ee-11ee-8c84-a8a1595801ab', TRUE, 'PUBLIC', 'INTERNAL',
36+
'/src/assets/langsearch.png', '[
37+
{
38+
"attrs": {
39+
"type": "password",
40+
"maxlength": 200,
41+
"minlength": 1,
42+
"show-password": true,
43+
"show-word-limit": true
44+
},
45+
"field": "apikey",
46+
"label": "apikey",
47+
"required": true,
48+
"input_type": "PasswordInput",
49+
"props_info": {
50+
"rules": [
51+
{
52+
"message": "apikey 为必填属性",
53+
"required": true
54+
},
55+
{
56+
"max": 200,
57+
"min": 1,
58+
"message": "apikey长度在 1 到 200 个字符",
59+
"trigger": "blur"
60+
}
61+
]
62+
},
63+
"default_value": "<your_apikey>",
64+
"show_default_value": false
65+
}
66+
]', NULL);
67+
68+
INSERT INTO function_lib (create_time, update_time, id, name, "desc", code, input_field_list,
69+
user_id, is_active, permission_type, function_type, icon,
70+
init_field_list, template_id)
71+
VALUES ('2025-03-10 06:20:35.945414 +00:00', '2025-03-10 09:19:23.608026 +00:00',
72+
'c75cb48e-fd77-11ef-84d2-5618c4394482', '博查AI', '从博查搜索任何信息和网页URL', e'def bocha_search(query, apikey):
73+
import requests
74+
import json
75+
url = "https://api.bochaai.com/v1/web-search"
76+
payload = json.dumps({
77+
"query": query,
78+
"Boolean": \'true\',
79+
"count": 8
80+
})
81+
82+
headers = {
83+
\'Authorization\': \'Bearer \' + apikey, #鉴权参数,示例:Bearer xxxxxx,API KEY请先前往博查AI开放平台(https://open.bochaai.com)> API KEY 管理中获取。
84+
\'Content-Type\': \'application/json\'
85+
}
86+
87+
response = requests.request("POST", url, headers=headers, data=payload)
88+
return response.json()',
89+
'{"{\"name\": \"query\", \"type\": \"string\", \"source\": \"reference\", \"is_required\": true}"}',
90+
'f0dd8f71-e4ee-11ee-8c84-a8a1595801ab', TRUE, 'PUBLIC', 'INTERNAL',
91+
'/src/assets/bochaai.png', '[
92+
{
93+
"attrs": {
94+
"type": "password",
95+
"maxlength": 200,
96+
"minlength": 1,
97+
"show-password": true,
98+
"show-word-limit": true
99+
},
100+
"field": "apikey",
101+
"label": "apikey",
102+
"required": true,
103+
"input_type": "PasswordInput",
104+
"props_info": {
105+
"rules": [
106+
{
107+
"message": "apikey 为必填属性",
108+
"required": true
109+
},
110+
{
111+
"max": 200,
112+
"min": 1,
113+
"message": "apikey长度在 1 到 200 个字符",
114+
"trigger": "blur"
115+
}
116+
]
117+
},
118+
"default_value": "<your_apikey>",
119+
"show_default_value": false
120+
}
121+
]', NULL);
122+
123+
INSERT INTO function_lib (create_time, update_time, id, name, "desc", code, input_field_list,
124+
user_id, is_active, permission_type, function_type, icon,
125+
init_field_list, template_id)
126+
VALUES ('2025-02-26 03:36:48.187286 +00:00', '2025-03-11 07:23:46.123972 +00:00',
127+
'e89ad2ae-f3f2-11ef-ad09-0242ac110002', 'Google Search', 'Google Web Search', e'def google_search(query, apikey, cx):
128+
import requests
129+
import json
130+
url = "https://customsearch.googleapis.com/customsearch/v1"
131+
params = {
132+
"q": query,
133+
"key": apikey,
134+
"cx": cx,
135+
"num": 10, # 每次最多返回10条
136+
}
137+
138+
response = requests.get(url, params=params)
139+
if response.status_code == 200:
140+
contexts = response.json()[\'items\']
141+
max_context = min(len(contexts), 10)
142+
format_contexts = []
143+
for i in range(max_context):
144+
formatted_context = f"[[引用:{i + 1}]]\\n网页标题:{contexts[i][\'title\']}\\n网页链接:{contexts[i][\'link\']}\\n网页内容:{contexts[i][\'snippet\']}\\n网站名称:{contexts[i][\'displayLink\']}"
145+
format_contexts.append(formatted_context)
146+
147+
context = "\\n\\n".join(format_contexts)
148+
return context
149+
else:
150+
print(f"Error: {response.status_code}")
151+
return None',
152+
'{"{\"name\": \"query\", \"type\": \"string\", \"source\": \"reference\", \"is_required\": true}"}',
153+
'f0dd8f71-e4ee-11ee-8c84-a8a1595801ab', TRUE, 'PUBLIC', 'INTERNAL',
154+
'/src/assets/google_search.png', '[
155+
{
156+
"attrs": {
157+
"type": "password",
158+
"maxlength": 200,
159+
"minlength": 1,
160+
"show-password": true,
161+
"show-word-limit": true
162+
},
163+
"field": "apikey",
164+
"label": "apikey",
165+
"required": true,
166+
"input_type": "PasswordInput",
167+
"props_info": {
168+
"rules": [
169+
{
170+
"message": "apikey 为必填属性",
171+
"required": true
172+
},
173+
{
174+
"max": 200,
175+
"min": 1,
176+
"message": "apikey长度在 1 到 200 个字符",
177+
"trigger": "blur"
178+
}
179+
]
180+
},
181+
"default_value": "<your_apikey>",
182+
"show_default_value": false
183+
},
184+
{
185+
"attrs": {
186+
"maxlength": 50,
187+
"minlength": 1,
188+
"show-word-limit": true
189+
},
190+
"field": "cx",
191+
"label": "cx",
192+
"required": true,
193+
"input_type": "TextInput",
194+
"props_info": {
195+
"rules": [
196+
{
197+
"message": "cx 为必填属性",
198+
"required": true
199+
},
200+
{
201+
"max": 50,
202+
"min": 1,
203+
"message": "cx长度在 1 到 50 个字符",
204+
"trigger": "blur"
205+
}
206+
]
207+
},
208+
"default_value": "<your_cx>",
209+
"show_default_value": false
210+
}
211+
]', NULL);
212+
213+
"""
214+
215+
216+
class Migration(migrations.Migration):
217+
218+
dependencies = [
219+
('function_lib', '0002_functionlib_is_active_functionlib_permission_type'),
220+
]
221+
222+
operations = [
223+
migrations.AddField(
224+
model_name='functionlib',
225+
name='function_type',
226+
field=models.CharField(choices=[('INTERNAL', '内置'), ('PUBLIC', '公开')], default='PUBLIC', max_length=20, verbose_name='函数类型'),
227+
),
228+
migrations.AddField(
229+
model_name='functionlib',
230+
name='icon',
231+
field=models.CharField(default='/ui/favicon.ico', max_length=256, verbose_name='函数库icon'),
232+
),
233+
migrations.AddField(
234+
model_name='functionlib',
235+
name='init_field_list',
236+
field=models.JSONField(default=list, verbose_name='启动字段列表'),
237+
),
238+
migrations.AddField(
239+
model_name='functionlib',
240+
name='template_id',
241+
field=models.UUIDField(default=None, null=True, verbose_name='模版id'),
242+
),
243+
migrations.RunSQL(function_template)
244+
]

apps/function_lib/migrations/0003_functionlib_icon_functionlib_init_field_list.py

Lines changed: 0 additions & 23 deletions
This file was deleted.

apps/function_lib/models/function.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ class PermissionType(models.TextChoices):
1919
PUBLIC = "PUBLIC", '公开'
2020
PRIVATE = "PRIVATE", "私有"
2121

22+
class FunctionType(models.TextChoices):
23+
INTERNAL = "INTERNAL", '内置'
24+
PUBLIC = "PUBLIC", "公开"
25+
2226

2327
class FunctionLib(AppModelMixin):
2428
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid1, editable=False, verbose_name="主键id")
@@ -34,6 +38,9 @@ class FunctionLib(AppModelMixin):
3438
is_active = models.BooleanField(default=True)
3539
permission_type = models.CharField(max_length=20, verbose_name='权限类型', choices=PermissionType.choices,
3640
default=PermissionType.PRIVATE)
41+
function_type = models.CharField(max_length=20, verbose_name='函数类型', choices=FunctionType.choices,
42+
default=FunctionType.PUBLIC)
43+
template_id = models.UUIDField(max_length=128, verbose_name="模版id", null=True, default=None)
3744

3845
class Meta:
3946
db_table = "function_lib"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎
5+
@file: __init__.py.py
6+
@date:2024/8/2 14:55
7+
@desc:
8+
"""

0 commit comments

Comments
 (0)