Skip to content

Commit dc90be8

Browse files
committed
feat: add generateCode function to streamline Python code generation
1 parent d714724 commit dc90be8

File tree

9 files changed

+768
-10
lines changed

9 files changed

+768
-10
lines changed

apps/tools/api/tool.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,4 @@ def get_parameters():
322322
required=True,
323323
),
324324
]
325+

apps/tools/serializers/tool.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@
99
import tempfile
1010
import zipfile
1111
from typing import Dict
12-
from django.core.cache import cache
12+
1313
import requests
1414
import uuid_utils.compat as uuid
1515
from django.core import validators
16+
from django.core.cache import cache
1617
from django.db import transaction
1718
from django.db.models import QuerySet, Q, Subquery, OuterRef, CharField, Value, When, Case
1819
from django.http import HttpResponse
1920
from django.utils import timezone
2021
from django.utils.translation import gettext_lazy as _
22+
from langchain_core.messages import HumanMessage, AIMessage
2123
from langchain_mcp_adapters.client import MultiServerMCPClient
2224
from pylint.lint import Run
2325
from pylint.reporters import JSON2Reporter
@@ -36,6 +38,7 @@
3638
from common.utils.tool_code import ToolExecutor
3739
from knowledge.models import File, FileSourceType, Knowledge
3840
from maxkb.const import PROJECT_DIR
41+
from models_provider.models import Model
3942
from system_manage.models import AuthTargetType, WorkspaceUserResourcePermission
4043
from system_manage.models.resource_mapping import ResourceMapping
4144
from system_manage.serializers.resource_mapping_serializers import ResourceMappingSerializer
@@ -1108,6 +1111,68 @@ def upload(self):
11081111
file.save(self.data.get('file').read())
11091112
return file_id
11101113

1114+
class GenerateCodeSerializer(serializers.Serializer):
1115+
workspace_id = serializers.CharField(required=True, label=_('Workspace ID'))
1116+
model_id = serializers.UUIDField(required=True, label=_('Model ID'))
1117+
prompt = serializers.CharField(required=True, label=_('Prompt'))
1118+
messages = serializers.ListField(required=True, label=_('Messages'))
1119+
model_params_setting = serializers.DictField(required=False, default=dict, label=_('Model Params Setting'))
1120+
init_field_list = serializers.ListField(required=False, default=list, label=_('Init Field List'))
1121+
input_field_list = serializers.ListField(required=False, default=list, label=_('Input Field List'))
1122+
1123+
def generate_code(self):
1124+
from models_provider.tools import get_model_instance_by_model_workspace_id
1125+
from application.flow.tools import to_stream_response_simple
1126+
1127+
self.is_valid(raise_exception=True)
1128+
1129+
workspace_id = self.data.get('workspace_id')
1130+
model_id = self.data.get('model_id')
1131+
prompt = self.data.get('prompt')
1132+
messages = self.data.get('messages')
1133+
model_params_setting = self.data.get('model_params_setting')
1134+
init_field_list = self.data.get('init_field_list')
1135+
input_field_list = self.data.get('input_field_list')
1136+
1137+
message = messages[-1]['content']
1138+
q = prompt.replace(
1139+
"{userInput}", message
1140+
).replace(
1141+
"{initFieldList}", json.dumps(init_field_list)
1142+
).replace(
1143+
"{inputFieldList}", json.dumps(input_field_list)
1144+
)
1145+
1146+
messages[-1]['content'] = q
1147+
SUPPORTED_MODEL_TYPES = ["LLM"]
1148+
model_exist = QuerySet(Model).filter(
1149+
id=model_id,
1150+
model_type__in=SUPPORTED_MODEL_TYPES
1151+
).exists()
1152+
if not model_exist:
1153+
raise Exception(_("Model does not exists or is not an LLM model"))
1154+
1155+
def process():
1156+
model = get_model_instance_by_model_workspace_id(
1157+
model_id=model_id, workspace_id=workspace_id, **model_params_setting
1158+
)
1159+
try:
1160+
for r in model.stream([
1161+
# SystemMessage(content=SYSTEM_ROLE),
1162+
*[
1163+
HumanMessage(
1164+
content=m.get('content')
1165+
) if m.get('role') == 'user' else AIMessage(
1166+
content=m.get('content')
1167+
) for m in messages
1168+
]
1169+
]):
1170+
yield 'data: ' + json.dumps({'content': r.content}) + '\n\n'
1171+
except Exception as e:
1172+
yield 'data: ' + json.dumps({'error': str(e)}) + '\n\n'
1173+
1174+
return to_stream_response_simple(process())
1175+
11111176

11121177
class ToolTreeSerializer(serializers.Serializer):
11131178
class Query(serializers.Serializer):

apps/tools/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
path('workspace/<str:workspace_id>/tool/tool_list', views.ToolView.Query.as_view()),
1616
path('workspace/<str:workspace_id>/tool/test_connection', views.ToolView.TestConnection.as_view()),
1717
path('workspace/<str:workspace_id>/tool/upload_skill_file', views.ToolView.UploadSkillFile.as_view()),
18+
path('workspace/<str:workspace_id>/tool/generate_code', views.ToolView.GenerateCode.as_view()),
1819
path('workspace/<str:workspace_id>/tool/<str:tool_id>', views.ToolView.Operate.as_view()),
1920
path('workspace/<str:workspace_id>/tool/<str:tool_id>/publish', views.ToolWorkflowView.Publish.as_view()),
2021
path('workspace/<str:workspace_id>/tool/<str:tool_id>/debug', views.ToolWorkflowDebugView.as_view()),

apps/tools/views/tool.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,3 +596,20 @@ def put(self, request: Request, workspace_id: str):
596596
'user_id': request.user.id,
597597
'file': request.FILES.get('file'),
598598
}).upload())
599+
600+
class GenerateCode(APIView):
601+
authentication_classes = [TokenAuth]
602+
603+
@has_permissions(
604+
PermissionConstants.TOOL_CREATE.get_workspace_permission(),
605+
PermissionConstants.TOOL_CREATE.get_workspace_permission_workspace_manage_role(),
606+
PermissionConstants.TOOL_EDIT.get_workspace_permission(),
607+
PermissionConstants.TOOL_EDIT.get_workspace_permission_workspace_manage_role(),
608+
RoleConstants.WORKSPACE_MANAGE.get_workspace_role(),
609+
RoleConstants.USER.get_workspace_role()
610+
)
611+
def post(self, request: Request, workspace_id: str):
612+
return ToolSerializer.GenerateCodeSerializer(data={
613+
'workspace_id': workspace_id,
614+
**request.data
615+
}).generate_code()

ui/src/api/system-resource-management/tool.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Result } from '@/request/Result'
2-
import { get, post, del, put, exportFile } from '@/request/index'
2+
import { get, post, del, put, exportFile, postStream } from '@/request/index'
33
import { type Ref } from 'vue'
44
import type { pageRequest } from '@/api/type/common'
55
import type { toolData } from '@/api/type/tool'
@@ -156,6 +156,15 @@ const uploadSkillFile: (data: toolData, loading?: Ref<boolean>) => Promise<Resul
156156
return put(`${prefix}/upload_skill_file`, data, undefined, loading)
157157
}
158158

159+
const generateCode: (data:any) => Promise<Result<any>> = (
160+
data: any,
161+
) => {
162+
const p = (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/admin') + '/api'
163+
return postStream(
164+
`${p}${prefix}/generate_code`,
165+
data,
166+
)
167+
}
159168

160169

161170
export default {
@@ -173,4 +182,5 @@ export default {
173182
pageToolRecord,
174183
getToolRecordDetail,
175184
uploadSkillFile,
185+
generateCode,
176186
}

ui/src/api/system-shared/tool.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Result } from '@/request/Result'
2-
import { get, post, del, put, exportFile } from '@/request/index'
2+
import { get, post, del, put, exportFile, postStream } from '@/request/index'
33
import { type Ref } from 'vue'
44
import type { pageRequest } from '@/api/type/common'
55
import type { toolData, AddInternalToolParam } from '@/api/type/tool'
@@ -200,6 +200,15 @@ const uploadSkillFile: (data: toolData, loading?: Ref<boolean>) => Promise<Resul
200200
return put(`${prefix}/upload_skill_file`, data, undefined, loading)
201201
}
202202

203+
const generateCode: (data:any) => Promise<Result<any>> = (
204+
data: any,
205+
) => {
206+
const p = (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/admin') + '/api'
207+
return postStream(
208+
`${p}${prefix}/generate_code`,
209+
data,
210+
)
211+
}
203212

204213

205214
export default {
@@ -222,4 +231,5 @@ export default {
222231
pageToolRecord,
223232
getToolRecordDetail,
224233
uploadSkillFile,
234+
generateCode,
225235
}

ui/src/api/tool/tool.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,18 @@ const debugToolWorkflow: (tool_id: string, data: any) => Promise<any> = (tool_id
299299
const p = (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/admin') + '/api'
300300
return postStream(`${p}${prefix.value}/${tool_id}/debug`, data)
301301
}
302+
303+
const generateCode: (data:any) => Promise<Result<any>> = (
304+
data: any,
305+
) => {
306+
const p = (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/admin') + '/api'
307+
return postStream(
308+
`${p}${prefix.value}/generate_code`,
309+
data,
310+
)
311+
}
312+
313+
302314
export default {
303315
getToolList,
304316
getAllToolList,
@@ -326,4 +338,5 @@ export default {
326338
publish,
327339
exportToolWorkflow,
328340
debugToolWorkflow,
341+
generateCode,
329342
}

ui/src/views/tool/ToolFormDrawer.vue

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,23 @@
170170
</el-table-column>
171171
</el-table>
172172

173-
<h4 class="title-decoration-1 mb-16">
174-
{{ $t('views.tool.form.param.code') }}
175-
<span class="color-danger" style="margin-left: -10px">*</span>
176-
<el-text type="info" class="color-secondary">
177-
{{ $t('views.tool.form.param.paramInfo2') }}
178-
</el-text>
179-
</h4>
173+
<div class="flex-between">
174+
<h4 class="title-decoration-1 mb-16">
175+
{{ $t('views.tool.form.param.code') }}
176+
<span class="color-danger" style="margin-left: -10px">*</span>
177+
<el-text type="info" class="color-secondary">
178+
{{ $t('views.tool.form.param.paramInfo2') }}
179+
</el-text>
180+
</h4>
181+
<el-button
182+
type="primary"
183+
@click="openGenerateCodeDialog"
184+
link
185+
>
186+
<AppIcon iconName="app-generate-star" class="mr-4"></AppIcon>
187+
{{ $t('views.application.generateDialog.label') }}
188+
</el-button>
189+
</div>
180190

181191
<div class="mb-8" v-if="showEditor">
182192
<CodemirrorEditor
@@ -217,6 +227,7 @@
217227
<FieldFormDialog ref="FieldFormDialogRef" @refresh="refreshFieldList" />
218228
<UserFieldFormDialog ref="UserFieldFormDialogRef" @refresh="refreshInitFieldList" />
219229
<EditAvatarDialog ref="EditAvatarDialogRef" @refresh="refreshTool" />
230+
<GenerateCodeDialog ref="GenerateCodeDialogRef" :toolData="form" @replace="replaceCode"/>
220231
</el-drawer>
221232
</template>
222233

@@ -237,6 +248,7 @@ import { useRoute } from 'vue-router'
237248
import useStore from '@/stores'
238249
import permissionMap from '@/permission'
239250
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
251+
import GenerateCodeDialog from "@/views/tool/component/GenerateCodeDialog.vue";
240252
const route = useRoute()
241253
242254
const props = defineProps({
@@ -265,6 +277,7 @@ const UserFieldFormDialogRef = ref()
265277
const EditAvatarDialogRef = ref()
266278
const initFieldTableRef = ref()
267279
const inputFieldTableRef = ref()
280+
const GenerateCodeDialogRef = ref()
268281
269282
const FormRef = ref()
270283
@@ -392,6 +405,15 @@ function openEditAvatar() {
392405
EditAvatarDialogRef.value.open(form.value)
393406
}
394407
408+
function openGenerateCodeDialog() {
409+
GenerateCodeDialogRef.value?.open(form.value.init_field_list, form.value.input_field_list)
410+
}
411+
412+
function replaceCode(code: string) {
413+
const match = code.replace('```python', '').replace('```', '')
414+
form.value.code = match
415+
}
416+
395417
const submit = async (formEl: FormInstance | undefined) => {
396418
if (!formEl) return
397419
await formEl.validate((valid: any) => {

0 commit comments

Comments
 (0)