fix: Tools cannot be called in the workflow (#3516)

This commit is contained in:
shaohuzhang1 2025-07-08 20:14:33 +08:00 committed by GitHub
parent dc51cc1f2a
commit 6866ff03b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 68 additions and 35 deletions

View File

@ -144,6 +144,8 @@ class FlowParamsSerializer(serializers.Serializer):
workspace_id = serializers.CharField(required=True, label="工作空间id")
application_id = serializers.CharField(required=True, label="应用id")
re_chat = serializers.BooleanField(required=True, label="换个答案")
debug = serializers.BooleanField(required=True, label="是否debug")

View File

@ -23,14 +23,14 @@ class InputField(serializers.Serializer):
class FunctionLibNodeParamsSerializer(serializers.Serializer):
tool_id = serializers.UUIDField(required=True, label=_('Library ID'))
tool_lib_id = serializers.UUIDField(required=True, label=_('Library ID'))
input_field_list = InputField(required=True, many=True)
is_result = serializers.BooleanField(required=False,
label=_('Whether to return content'))
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
f_lib = QuerySet(Tool).filter(id=self.data.get('tool_id')).first()
f_lib = QuerySet(Tool).filter(id=self.data.get('tool_lib_id')).first()
if f_lib is None:
raise Exception(_('The function has been deleted'))
@ -44,5 +44,5 @@ class IToolLibNode(INode):
def _run(self):
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
def execute(self, function_lib_id, input_field_list, **kwargs) -> NodeResult:
def execute(self, tool_lib_id, input_field_list, **kwargs) -> NodeResult:
pass

View File

@ -15,10 +15,13 @@ from django.utils.translation import gettext as _
from application.flow.i_step_node import NodeResult
from application.flow.step_node.tool_lib_node.i_tool_lib_node import IToolLibNode
from common.database_model_manage.database_model_manage import DatabaseModelManage
from common.exception.app_exception import AppApiException
from common.utils.function_code import FunctionExecutor
from common.utils.rsa_util import rsa_long_decrypt
from maxkb.const import CONFIG
from system_manage.models import AuthTargetType
from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer
from tools.models import Tool
function_executor = FunctionExecutor(CONFIG.get('SANDBOX'))
@ -101,13 +104,14 @@ def convert_value(name: str, value, _type, is_required, source, node):
value=value))
def valid_function(function_lib, user_id):
if function_lib is None:
raise Exception(_('Function does not exist'))
if function_lib.permission_type == 'PRIVATE' and str(function_lib.user_id) != str(user_id):
raise Exception(_('No permission to use this function {name}').format(name=function_lib.name))
if not function_lib.is_active:
raise Exception(_('Function {name} is unavailable').format(name=function_lib.name))
def valid_function(tool_lib, workspace_id):
if tool_lib is None:
raise Exception(_('Tool does not exist'))
get_authorized_tool = DatabaseModelManage.get_model("get_authorized_tool")
if tool_lib and tool_lib.workspace_id != workspace_id and get_authorized_tool is not None:
tool_lib = get_authorized_tool(QuerySet(Tool).filter(id=tool_lib.id), workspace_id).first()
if tool_lib is None:
raise Exception(_("Tool does not exist"))
class BaseToolLibNodeNode(IToolLibNode):
@ -116,9 +120,10 @@ class BaseToolLibNodeNode(IToolLibNode):
if self.node_params.get('is_result'):
self.answer_text = str(details.get('result'))
def execute(self, function_lib_id, input_field_list, **kwargs) -> NodeResult:
function_lib = QuerySet(Tool).filter(id=function_lib_id).first()
valid_function(function_lib, self.flow_params_serializer.data.get('user_id'))
def execute(self, tool_lib_id, input_field_list, **kwargs) -> NodeResult:
workspace_id = self.workflow_manage.get_body().get('workspace_id')
tool_lib = QuerySet(Tool).filter(id=tool_lib_id).first()
valid_function(tool_lib, workspace_id)
params = {field.get('name'): convert_value(field.get('name'), field.get('value'), field.get('type'),
field.get('is_required'),
field.get('source'), self)
@ -126,15 +131,15 @@ class BaseToolLibNodeNode(IToolLibNode):
[{'value': get_field_value(input_field_list, field.get('name'), field.get('is_required'),
), **field}
for field in
function_lib.input_field_list]}
tool_lib.input_field_list]}
self.context['params'] = params
# 合并初始化参数
if function_lib.init_params is not None:
all_params = json.loads(rsa_long_decrypt(function_lib.init_params)) | params
if tool_lib.init_params is not None:
all_params = json.loads(rsa_long_decrypt(tool_lib.init_params)) | params
else:
all_params = params
result = function_executor.exec_code(function_lib.code, all_params)
result = function_executor.exec_code(tool_lib.code, all_params)
return NodeResult({'result': result}, {}, _write_context=write_context)
def get_details(self, index: int, **kwargs):

View File

@ -237,7 +237,8 @@ class ChatSerializers(serializers.Serializer):
'chat_user_type': chat_user_type,
'workspace_id': workspace_id,
'debug': debug,
'chat_user': chat_info.get_chat_user()},
'chat_user': chat_info.get_chat_user(),
'application_id': chat_info.application_id},
WorkFlowPostHandler(chat_info),
base_to_response, form_data, image_list, document_list, audio_list,
other_list,

View File

@ -103,6 +103,33 @@ class UserResourcePermissionSerializer(serializers.Serializer):
auth_target_type=self.data.get('auth_target_type'))
}
def is_auth(self, resource_id: str):
self.is_valid(raise_exception=True)
auth_target_type = self.data.get('auth_target_type')
workspace_id = self.data.get('workspace_id')
user_id = self.data.get('user_id')
workspace_manage = is_workspace_manage(user_id, workspace_id)
if workspace_manage:
return True
wurp = QuerySet(WorkspaceUserResourcePermission).filter(auth_target_type=auth_target_type,
workspace_id=workspace_id, user=user_id,
target=resource_id).first()
if wurp is None:
return False
workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
role_permission_mapping_model = DatabaseModelManage.get_model("role_permission_mapping_model")
if wurp.auth_type == ResourceAuthType.ROLE.value:
if workspace_user_role_mapping_model and role_permission_mapping_model:
inner = QuerySet(workspace_user_role_mapping_model).filter(workspace_id=workspace_id, user_id=user_id)
return QuerySet(role_permission_mapping_model).filter(role_id__in=inner,
permission_id=(
auth_target_type + ':READ')).exists()
else:
return False
else:
return wurp.permission_list.__contains__(ResourcePermission.VIEW.value)
def auth_resource(self, resource_id: str):
self.is_valid(raise_exception=True)
auth_target_type = self.data.get('auth_target_type')

View File

@ -2,8 +2,7 @@ import { Result } from '@/request/Result'
import { get, post, del, put, exportFile } from '@/request/index'
import { type Ref } from 'vue'
import type { pageRequest } from '@/api/type/common'
import type {AddInternalToolParam, toolData} from '@/api/type/tool'
import type { AddInternalToolParam, toolData } from '@/api/type/tool'
import useStore from '@/stores'
const prefix: any = { _value: '/workspace/' }
@ -18,10 +17,10 @@ Object.defineProperty(prefix, 'value', {
*
* @params {folder_id: string}
*/
const getToolList: (data?: any, loading?: Ref<boolean>) => Promise<Result<{tools: any[], folders: any[]}>> = (
data,
loading,
) => {
const getToolList: (
data?: any,
loading?: Ref<boolean>,
) => Promise<Result<{ tools: any[]; folders: any[] }>> = (data, loading) => {
return get(`${prefix.value}`, data, loading)
}
@ -83,18 +82,18 @@ const getToolById: (tool_id: string, loading?: Ref<boolean>) => Promise<Result<a
*
* @param tool_id
*/
const delTool: (
tool_id: string,
loading?: Ref<boolean>
) => Promise<Result<boolean>> = (tool_id, loading) => {
const delTool: (tool_id: string, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
tool_id,
loading,
) => {
return del(`${prefix.value}/${tool_id}`, undefined, {}, loading)
}
const putToolIcon: (
id: string,
data: any,
loading?: Ref<boolean>
) => Promise<Result<any>> = (id, data, loading) => {
const putToolIcon: (id: string, data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
id,
data,
loading,
) => {
return put(`${prefix.value}/${id}/edit_icon`, data, undefined, loading)
}
@ -139,7 +138,6 @@ const addInternalTool: (
return post(`${prefix.value}/${tool_id}/add_internal_tool`, param, undefined, loading)
}
export default {
getToolList,
getToolListPage,
@ -152,5 +150,5 @@ export default {
exportTool,
putToolIcon,
delTool,
addInternalTool
addInternalTool,
}