From 6866ff03b352e755171b2df3e54c1f28b62dfe4a Mon Sep 17 00:00:00 2001 From: shaohuzhang1 <80892890+shaohuzhang1@users.noreply.github.com> Date: Tue, 8 Jul 2025 20:14:33 +0800 Subject: [PATCH] fix: Tools cannot be called in the workflow (#3516) --- apps/application/flow/i_step_node.py | 2 ++ .../tool_lib_node/i_tool_lib_node.py | 6 ++-- .../tool_lib_node/impl/base_tool_lib_node.py | 33 +++++++++++-------- apps/chat/serializers/chat.py | 3 +- .../serializers/user_resource_permission.py | 27 +++++++++++++++ ui/src/api/tool/tool.ts | 32 +++++++++--------- 6 files changed, 68 insertions(+), 35 deletions(-) diff --git a/apps/application/flow/i_step_node.py b/apps/application/flow/i_step_node.py index 1bbd29d9b..2fc0d8850 100644 --- a/apps/application/flow/i_step_node.py +++ b/apps/application/flow/i_step_node.py @@ -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") diff --git a/apps/application/flow/step_node/tool_lib_node/i_tool_lib_node.py b/apps/application/flow/step_node/tool_lib_node/i_tool_lib_node.py index 3dee9c7d4..185cbe8c2 100644 --- a/apps/application/flow/step_node/tool_lib_node/i_tool_lib_node.py +++ b/apps/application/flow/step_node/tool_lib_node/i_tool_lib_node.py @@ -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 diff --git a/apps/application/flow/step_node/tool_lib_node/impl/base_tool_lib_node.py b/apps/application/flow/step_node/tool_lib_node/impl/base_tool_lib_node.py index c5afaa66b..04ea0d0b6 100644 --- a/apps/application/flow/step_node/tool_lib_node/impl/base_tool_lib_node.py +++ b/apps/application/flow/step_node/tool_lib_node/impl/base_tool_lib_node.py @@ -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): diff --git a/apps/chat/serializers/chat.py b/apps/chat/serializers/chat.py index 5bea26462..4a48e58f6 100644 --- a/apps/chat/serializers/chat.py +++ b/apps/chat/serializers/chat.py @@ -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, diff --git a/apps/system_manage/serializers/user_resource_permission.py b/apps/system_manage/serializers/user_resource_permission.py index d5d383dd8..5987d533b 100644 --- a/apps/system_manage/serializers/user_resource_permission.py +++ b/apps/system_manage/serializers/user_resource_permission.py @@ -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') diff --git a/ui/src/api/tool/tool.ts b/ui/src/api/tool/tool.ts index be92dffb3..fb6b9da45 100644 --- a/ui/src/api/tool/tool.ts +++ b/ui/src/api/tool/tool.ts @@ -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) => Promise> = ( - data, - loading, -) => { +const getToolList: ( + data?: any, + loading?: Ref, +) => Promise> = (data, loading) => { return get(`${prefix.value}`, data, loading) } @@ -83,18 +82,18 @@ const getToolById: (tool_id: string, loading?: Ref) => Promise -) => Promise> = (tool_id, loading) => { +const delTool: (tool_id: string, loading?: Ref) => Promise> = ( + tool_id, + loading, +) => { return del(`${prefix.value}/${tool_id}`, undefined, {}, loading) } -const putToolIcon: ( - id: string, - data: any, - loading?: Ref -) => Promise> = (id, data, loading) => { +const putToolIcon: (id: string, data: any, loading?: Ref) => Promise> = ( + 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, }