diff --git a/apps/tools/api/tool.py b/apps/tools/api/tool.py index 70e567702..4448e2cf8 100644 --- a/apps/tools/api/tool.py +++ b/apps/tools/api/tool.py @@ -5,7 +5,7 @@ from drf_spectacular.utils import OpenApiParameter from common.mixins.api_mixin import APIMixin from common.result import ResultSerializer, DefaultResultSerializer from tools.serializers.tool import ToolModelSerializer, ToolCreateRequest, ToolDebugRequest, ToolEditRequest, \ - PylintInstance + PylintInstance, AddInternalToolRequest class ToolCreateResponse(ResultSerializer): @@ -274,4 +274,51 @@ class EditIconAPI(APIMixin): @staticmethod def get_response(): - return DefaultResultSerializer \ No newline at end of file + return DefaultResultSerializer + + +class GetInternalToolAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="name", + description="工具名称", + type=OpenApiTypes.STR, + location='query', + required=False, + ) + ] + + @staticmethod + def get_response(): + return DefaultResultSerializer() + + +class AddInternalToolAPI(APIMixin): + @staticmethod + def get_request(): + return AddInternalToolRequest + + @staticmethod + def get_response(): + return DefaultResultSerializer + + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True, + ), + OpenApiParameter( + name="tool_id", + description="工具id", + type=OpenApiTypes.STR, + location='path', + required=True, + ), + ] diff --git a/apps/tools/serializers/tool.py b/apps/tools/serializers/tool.py index 92491bb3a..20fef78a3 100644 --- a/apps/tools/serializers/tool.py +++ b/apps/tools/serializers/tool.py @@ -24,7 +24,7 @@ from common.utils.rsa_util import rsa_long_decrypt, rsa_long_encrypt from common.utils.tool_code import ToolExecutor from knowledge.models import File, FileSourceType from maxkb.const import CONFIG, PROJECT_DIR -from tools.models import Tool, ToolScope, ToolFolder +from tools.models import Tool, ToolScope, ToolFolder, ToolType from tools.serializers.tool_folder import ToolFolderFlatSerializer tool_executor = ToolExecutor(CONFIG.get('SANDBOX')) @@ -157,14 +157,19 @@ class ToolCreateRequest(serializers.Serializer): class ToolEditRequest(serializers.Serializer): name = serializers.CharField(required=False, label=_('tool name'), allow_null=True) desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('tool description')) - code = serializers.CharField(required=False, label=_('tool content'), allow_null=True,) + code = serializers.CharField(required=False, label=_('tool content'), allow_null=True, ) input_field_list = serializers.ListField(required=False, default=list, allow_null=True, label=_('input field list')) init_field_list = serializers.ListField(required=False, default=list, allow_null=True, label=_('init field list')) init_params = serializers.DictField(required=False, default=dict, allow_null=True, label=_('init params')) - is_active = serializers.BooleanField(required=False, label=_('Is active'), allow_null=True,) + is_active = serializers.BooleanField(required=False, label=_('Is active'), allow_null=True, ) folder_id = serializers.CharField(required=False, allow_null=True) +class AddInternalToolRequest(serializers.Serializer): + name = serializers.CharField(required=False, label=_("tool name"), allow_null=True, allow_blank=True) + folder_id = serializers.CharField(required=False, allow_null=True, label=_("folder id")) + + class DebugField(serializers.Serializer): name = serializers.CharField(required=True, label=_('variable name')) value = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_('variable value')) @@ -420,6 +425,59 @@ class ToolSerializer(serializers.Serializer): return tool.icon + class InternalTool(serializers.Serializer): + user_id = serializers.UUIDField(required=True, label=_("User ID")) + name = serializers.CharField(required=False, label=_("tool name"), allow_null=True, allow_blank=True) + + def get_internal_tools(self): + self.is_valid(raise_exception=True) + query_set = QuerySet(Tool) + + if self.data.get('name', '') != '': + query_set = query_set.filter( + Q(name__icontains=self.data.get('name')) | + Q(desc__icontains=self.data.get('name')) + ) + + query_set = query_set.filter( + Q(scope=ToolScope.INTERNAL) & + Q(is_active=True) + ) + + return ToolModelSerializer(query_set, many=True).data + + class AddInternalTool(serializers.Serializer): + user_id = serializers.UUIDField(required=True, label=_("User ID")) + workspace_id = serializers.CharField(required=True, label=_("workspace id")) + tool_id = serializers.UUIDField(required=True, label=_("tool id")) + + def add(self, instance, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + AddInternalToolRequest(data=instance).is_valid(raise_exception=True) + + internal_tool = QuerySet(Tool).filter(id=self.data.get('tool_id')).first() + if internal_tool is None: + raise AppApiException(500, _('Tool does not exist')) + + tool = Tool( + id=uuid.uuid7(), + name=instance.get('name', internal_tool.name), + desc=internal_tool.desc, + code=internal_tool.code, + user_id=self.data.get('user_id'), + workspace_id=self.data.get('workspace_id'), + input_field_list=internal_tool.input_field_list, + init_field_list=internal_tool.init_field_list, + scope=ToolScope.WORKSPACE, + tool_type=ToolType.CUSTOM, + folder_id=instance.get('folder_id', self.data.get('workspace_id')), + is_active=False + ) + tool.save() + + return ToolModelSerializer(tool).data + class ToolTreeSerializer(serializers.Serializer): workspace_id = serializers.CharField(required=True, label=_('workspace id')) diff --git a/apps/tools/urls.py b/apps/tools/urls.py index 33a1f6ccb..6a7815503 100644 --- a/apps/tools/urls.py +++ b/apps/tools/urls.py @@ -4,6 +4,7 @@ from . import views app_name = "tool" urlpatterns = [ + path('workspace/internal/tool', views.ToolView.InternalTool.as_view()), path('workspace//tool', views.ToolView.as_view()), path('workspace//tool/import', views.ToolView.Import.as_view()), path('workspace//tool/pylint', views.ToolView.Pylint.as_view()), @@ -11,5 +12,6 @@ urlpatterns = [ path('workspace//tool/', views.ToolView.Operate.as_view()), path('workspace//tool//edit_icon', views.ToolView.EditIcon.as_view()), path('workspace//tool//export', views.ToolView.Export.as_view()), + path('workspace//tool//add_internal_tool', views.ToolView.AddInternalTool.as_view()), path('workspace//tool//', views.ToolView.Page.as_view()), ] diff --git a/apps/tools/views/tool.py b/apps/tools/views/tool.py index 9d94f31e6..c0f7355e1 100644 --- a/apps/tools/views/tool.py +++ b/apps/tools/views/tool.py @@ -11,7 +11,7 @@ from common.constants.permission_constants import PermissionConstants, RoleConst from common.log.log import log from common.result import result from tools.api.tool import ToolCreateAPI, ToolEditAPI, ToolReadAPI, ToolDeleteAPI, ToolTreeReadAPI, ToolDebugApi, \ - ToolExportAPI, ToolImportAPI, ToolPageAPI, PylintAPI, EditIconAPI + ToolExportAPI, ToolImportAPI, ToolPageAPI, PylintAPI, EditIconAPI, GetInternalToolAPI, AddInternalToolAPI from tools.models import ToolScope, Tool from tools.serializers.tool import ToolSerializer, ToolTreeSerializer @@ -44,7 +44,7 @@ class ToolView(APIView): ) @log( menu="Tool", operate="Create tool", - get_operation_object=lambda r, k: r.data.get('name'), + get_operation_object=lambda r, k: r.data.get('name'), ) def post(self, request: Request, workspace_id: str): return result.success(ToolSerializer.Create( @@ -110,7 +110,7 @@ class ToolView(APIView): @log( menu='Tool', operate='Update tool', get_operation_object=lambda r, k: get_tool_operation_object(k.get('tool_id')), - + ) def put(self, request: Request, workspace_id: str, tool_id: str): return result.success(ToolSerializer.Operate( @@ -152,7 +152,7 @@ class ToolView(APIView): @log( menu='Tool', operate="Delete tool", get_operation_object=lambda r, k: get_tool_operation_object(k.get('tool_id')), - + ) def delete(self, request: Request, workspace_id: str, tool_id: str): return result.success(ToolSerializer.Operate( @@ -227,9 +227,8 @@ class ToolView(APIView): RoleConstants.WORKSPACE_MANAGE.get_workspace_role() ) @log( - menu='Tool', operate="Export function", - get_operation_object=lambda r, k: get_tool_operation_object(k.get('id')), - + menu='Tool', operate="Export tool", + get_operation_object=lambda r, k: get_tool_operation_object(k.get('tool_id')), ) def get(self, request: Request, tool_id: str, workspace_id: str): return ToolSerializer.Operate( @@ -284,3 +283,49 @@ class ToolView(APIView): 'user_id': request.user.id, 'image': request.FILES.get('file') }).edit(request.data)) + + class InternalTool(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_("Get internal tool"), + summary=_("Get internal tool"), + operation_id=_("Get internal tool"), # type: ignore + parameters=GetInternalToolAPI.get_parameters(), + responses=GetInternalToolAPI.get_response(), + tags=[_("Tool")] # type: ignore + ) + def get(self, request: Request): + return result.success(ToolSerializer.InternalTool(data={ + 'user_id': request.user.id, + 'name': request.query_params.get('name', ''), + }).get_internal_tools()) + + class AddInternalTool(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['POST'], + description=_("Add internal tool"), + summary=_("Add internal tool"), + operation_id=_("Add internal tool"), # type: ignore + parameters=AddInternalToolAPI.get_parameters(), + request=AddInternalToolAPI.get_request(), + responses=AddInternalToolAPI.get_response(), + tags=[_("Tool")] # type: ignore + ) + @has_permissions( + PermissionConstants.TOOL_CREATE.get_workspace_permission(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role() + ) + @log( + menu='Tool', operate="Add internal tool", + get_operation_object=lambda r, k: get_tool_operation_object(k.get('tool_id')), + ) + def post(self, request: Request, tool_id: str, workspace_id: str): + return result.success(ToolSerializer.AddInternalTool(data={ + 'tool_id': tool_id, + 'user_id': request.user.id, + 'workspace_id': workspace_id + }).add(request.data))