feat: add ToolModule model and API for managing tool modules with workspace support
This commit is contained in:
parent
dcb77bbe16
commit
d49fd74305
@ -21,6 +21,8 @@ class Group(Enum):
|
|||||||
|
|
||||||
MODEL = "MODEL"
|
MODEL = "MODEL"
|
||||||
|
|
||||||
|
TOOL = "TOOL"
|
||||||
|
|
||||||
|
|
||||||
class Operate(Enum):
|
class Operate(Enum):
|
||||||
"""
|
"""
|
||||||
@ -111,8 +113,7 @@ class PermissionConstants(Enum):
|
|||||||
RoleConstants.USER])
|
RoleConstants.USER])
|
||||||
USER_EDIT = Permission(group=Group.USER, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN])
|
USER_EDIT = Permission(group=Group.USER, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN])
|
||||||
USER_DELETE = Permission(group=Group.USER, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN])
|
USER_DELETE = Permission(group=Group.USER, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN])
|
||||||
TOOL_CREATE = Permission(group=Group.USER, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN,
|
|
||||||
RoleConstants.USER])
|
|
||||||
MODEL_CREATE = Permission(group=Group.MODEL, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN,
|
MODEL_CREATE = Permission(group=Group.MODEL, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN,
|
||||||
RoleConstants.USER])
|
RoleConstants.USER])
|
||||||
MODEL_READ = Permission(group=Group.MODEL, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
|
MODEL_READ = Permission(group=Group.MODEL, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
|
||||||
@ -121,6 +122,15 @@ class PermissionConstants(Enum):
|
|||||||
role_list=[RoleConstants.ADMIN, RoleConstants.USER])
|
role_list=[RoleConstants.ADMIN, RoleConstants.USER])
|
||||||
MODEL_DELETE = Permission(group=Group.MODEL, operate=Operate.DELETE,
|
MODEL_DELETE = Permission(group=Group.MODEL, operate=Operate.DELETE,
|
||||||
role_list=[RoleConstants.ADMIN, RoleConstants.USER])
|
role_list=[RoleConstants.ADMIN, RoleConstants.USER])
|
||||||
|
TOOL_MODULE_CREATE = Permission(group=Group.TOOL, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN,
|
||||||
|
RoleConstants.USER])
|
||||||
|
TOOL_MODULE_READ = Permission(group=Group.TOOL, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
|
||||||
|
RoleConstants.USER])
|
||||||
|
TOOL_MODULE_EDIT = Permission(group=Group.TOOL, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN,
|
||||||
|
RoleConstants.USER])
|
||||||
|
|
||||||
|
TOOL_CREATE = Permission(group=Group.TOOL, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN,
|
||||||
|
RoleConstants.USER])
|
||||||
|
|
||||||
def get_workspace_application_permission(self):
|
def get_workspace_application_permission(self):
|
||||||
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
|
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
|
||||||
|
|||||||
@ -24,6 +24,7 @@ urlpatterns = [
|
|||||||
path("api/", include("users.urls")),
|
path("api/", include("users.urls")),
|
||||||
path("api/", include("tools.urls")),
|
path("api/", include("tools.urls")),
|
||||||
path("api/", include("models_provider.urls")),
|
path("api/", include("models_provider.urls")),
|
||||||
|
path("api/", include("modules.urls"))
|
||||||
]
|
]
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
|
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
|
||||||
|
|||||||
0
apps/modules/__init__.py
Normal file
0
apps/modules/__init__.py
Normal file
1
apps/modules/api/__init__.py
Normal file
1
apps/modules/api/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# coding=utf-8
|
||||||
105
apps/modules/api/module.py
Normal file
105
apps/modules/api/module.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
from drf_spectacular.types import OpenApiTypes
|
||||||
|
from drf_spectacular.utils import OpenApiParameter
|
||||||
|
|
||||||
|
from common.mixins.api_mixin import APIMixin
|
||||||
|
from common.result import ResultSerializer
|
||||||
|
from modules.models.module import ModuleCreateRequest, ModuleEditRequest
|
||||||
|
from modules.serializers.module import ModuleSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleCreateResponse(ResultSerializer):
|
||||||
|
def get_data(self):
|
||||||
|
return ModuleSerializer()
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleCreateAPI(APIMixin):
|
||||||
|
@staticmethod
|
||||||
|
def get_parameters():
|
||||||
|
return [
|
||||||
|
OpenApiParameter(
|
||||||
|
name="workspace_id",
|
||||||
|
description="工作空间id",
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location='path',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
OpenApiParameter(
|
||||||
|
name="source",
|
||||||
|
description="菜单",
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
enum=["APPLICATION", "KNOWLEDGE", "TOOL"],
|
||||||
|
location='path',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_request():
|
||||||
|
return ModuleCreateRequest
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_response():
|
||||||
|
return ModuleCreateResponse
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleReadAPI(APIMixin):
|
||||||
|
@staticmethod
|
||||||
|
def get_parameters():
|
||||||
|
return [
|
||||||
|
OpenApiParameter(
|
||||||
|
name="workspace_id",
|
||||||
|
description="工作空间id",
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location='path',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
OpenApiParameter(
|
||||||
|
name="source",
|
||||||
|
description="菜单",
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
enum=["APPLICATION", "KNOWLEDGE", "TOOL"],
|
||||||
|
location='path',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
OpenApiParameter(
|
||||||
|
name="module_id",
|
||||||
|
description="模块id",
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location='path',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_response():
|
||||||
|
return ModuleCreateResponse
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleEditAPI(ModuleReadAPI):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_request():
|
||||||
|
return ModuleEditRequest
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleTreeReadAPI(APIMixin):
|
||||||
|
@staticmethod
|
||||||
|
def get_parameters():
|
||||||
|
return [
|
||||||
|
OpenApiParameter(
|
||||||
|
name="workspace_id",
|
||||||
|
description="工作空间id",
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
location='path',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
OpenApiParameter(
|
||||||
|
name="source",
|
||||||
|
description="菜单",
|
||||||
|
type=OpenApiTypes.STR,
|
||||||
|
enum=["APPLICATION", "KNOWLEDGE", "TOOL"],
|
||||||
|
location='path',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
0
apps/modules/models/__init__.py
Normal file
0
apps/modules/models/__init__.py
Normal file
13
apps/modules/models/module.py
Normal file
13
apps/modules/models/module.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleCreateRequest(serializers.Serializer):
|
||||||
|
name = serializers.CharField(required=True, label=_('module name'))
|
||||||
|
|
||||||
|
parent_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, default='root',
|
||||||
|
label=_('parent id'))
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleEditRequest(serializers.Serializer):
|
||||||
|
name = serializers.CharField(required=True, label=_('module name'))
|
||||||
1
apps/modules/serializers/__init__.py
Normal file
1
apps/modules/serializers/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# coding=utf-8
|
||||||
96
apps/modules/serializers/module.py
Normal file
96
apps/modules/serializers/module.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import uuid_utils.compat as uuid
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from common.constants.permission_constants import Group
|
||||||
|
from modules.api.module import ModuleCreateRequest
|
||||||
|
from tools.models import ToolModule
|
||||||
|
from tools.serializers.tool_module import ToolModuleTreeSerializer
|
||||||
|
|
||||||
|
|
||||||
|
def get_module_type(source):
|
||||||
|
if source == Group.TOOL.name:
|
||||||
|
return ToolModule
|
||||||
|
elif source == Group.APPLICATION.name:
|
||||||
|
# todo app module
|
||||||
|
return None
|
||||||
|
elif source == Group.KNOWLEDGE.name:
|
||||||
|
# todo knowledge module
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleSerializer(serializers.Serializer):
|
||||||
|
id = serializers.CharField(required=True, label=_('module id'))
|
||||||
|
name = serializers.CharField(required=True, label=_('module name'))
|
||||||
|
user_id = serializers.CharField(required=True, label=_('module user id'))
|
||||||
|
workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('workspace id'))
|
||||||
|
parent_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('parent id'))
|
||||||
|
|
||||||
|
class Create(serializers.Serializer):
|
||||||
|
user_id = serializers.UUIDField(required=True, label=_('user id'))
|
||||||
|
source = serializers.CharField(required=True, label=_('source'))
|
||||||
|
|
||||||
|
def insert(self, instance, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
ModuleCreateRequest(data=instance).is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
workspace_id = self.data.get('workspace_id', 'default')
|
||||||
|
parent_id = instance.get('parent_id', 'root')
|
||||||
|
name = instance.get('name')
|
||||||
|
|
||||||
|
Module = get_module_type(self.data.get('source'))
|
||||||
|
if QuerySet(Module).filter(name=name, workspace_id=workspace_id, parent_id=parent_id).exists():
|
||||||
|
raise serializers.ValidationError(_('Module name already exists'))
|
||||||
|
|
||||||
|
module = Module(
|
||||||
|
id=uuid.uuid7(),
|
||||||
|
name=instance.get('name'),
|
||||||
|
user_id=self.data.get('user_id'),
|
||||||
|
workspace_id=workspace_id,
|
||||||
|
parent_id=parent_id
|
||||||
|
)
|
||||||
|
module.save()
|
||||||
|
return ModuleSerializer(module).data
|
||||||
|
|
||||||
|
class Operate(serializers.Serializer):
|
||||||
|
id = serializers.CharField(required=True, label=_('module id'))
|
||||||
|
workspace_id = serializers.CharField(required=True, allow_null=True, allow_blank=True, label=_('workspace id'))
|
||||||
|
source = serializers.CharField(required=True, label=_('source'))
|
||||||
|
|
||||||
|
def edit(self, instance):
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
Module = get_module_type(self.data.get('source'))
|
||||||
|
if not QuerySet(Module).filter(id=self.data.get('id')).exists():
|
||||||
|
raise serializers.ValidationError(_('Module does not exist'))
|
||||||
|
|
||||||
|
edit_field_list = ['name']
|
||||||
|
edit_dict = {field: instance.get(field) for field in edit_field_list if (
|
||||||
|
field in instance and instance.get(field) is not None)}
|
||||||
|
|
||||||
|
QuerySet(Module).filter(id=self.data.get('id')).update(**edit_dict)
|
||||||
|
|
||||||
|
return self.one()
|
||||||
|
|
||||||
|
def one(self):
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
Module = get_module_type(self.data.get('source'))
|
||||||
|
module = QuerySet(Module).filter(id=self.data.get('id')).first()
|
||||||
|
return ModuleSerializer(module).data
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleTreeSerializer(serializers.Serializer):
|
||||||
|
workspace_id = serializers.CharField(required=True, allow_null=True, allow_blank=True, label=_('workspace id'))
|
||||||
|
source = serializers.CharField(required=True, label=_('source'))
|
||||||
|
|
||||||
|
def get_module_tree(self):
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
Module = get_module_type(self.data.get('source'))
|
||||||
|
nodes = Module.objects.filter(workspace_id=self.data.get('workspace_id')).get_cached_trees()
|
||||||
|
serializer = ToolModuleTreeSerializer(nodes, many=True)
|
||||||
|
return serializer.data # 这是可序列化的字典
|
||||||
10
apps/modules/urls.py
Normal file
10
apps/modules/urls.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = "module"
|
||||||
|
urlpatterns = [
|
||||||
|
path('workspace/<str:workspace_id>/<str:source>/module', views.ModuleView.Create.as_view()),
|
||||||
|
path('workspace/<str:workspace_id>/<str:source>/module/tree', views.ModuleTreeView.as_view()),
|
||||||
|
path('workspace/<str:workspace_id>/<str:source>/module/<str:module_id>', views.ModuleView.Operate.as_view()),
|
||||||
|
]
|
||||||
1
apps/modules/views/__init__.py
Normal file
1
apps/modules/views/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .module import *
|
||||||
79
apps/modules/views/module.py
Normal file
79
apps/modules/views/module.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from drf_spectacular.utils import extend_schema
|
||||||
|
from rest_framework.request import Request
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from common.auth import TokenAuth
|
||||||
|
from common.auth.authentication import has_permissions
|
||||||
|
from common.constants.permission_constants import Permission, Group, Operate
|
||||||
|
from common.result import result
|
||||||
|
from modules.api.module import ModuleCreateAPI, ModuleEditAPI, ModuleReadAPI, ModuleTreeReadAPI
|
||||||
|
from modules.serializers.module import ModuleSerializer, ModuleTreeSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleView(APIView):
|
||||||
|
class Create(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@extend_schema(methods=['POST'],
|
||||||
|
description=_('Create module'),
|
||||||
|
operation_id=_('Create module'),
|
||||||
|
parameters=ModuleCreateAPI.get_parameters(),
|
||||||
|
request=ModuleCreateAPI.get_request(),
|
||||||
|
responses=ModuleCreateAPI.get_response(),
|
||||||
|
tags=[_('Module')])
|
||||||
|
@has_permissions(lambda r, kwargs: Permission(group=Group(kwargs.get('source')), operate=Operate.CREATE,
|
||||||
|
resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}"))
|
||||||
|
def post(self, request: Request, workspace_id: str, source: str):
|
||||||
|
return result.success(ModuleSerializer.Create(
|
||||||
|
data={'user_id': request.user.id,
|
||||||
|
'source': source,
|
||||||
|
'workspace_id': workspace_id}
|
||||||
|
).insert(request.data))
|
||||||
|
|
||||||
|
class Operate(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@extend_schema(methods=['PUT'],
|
||||||
|
description=_('Update module'),
|
||||||
|
operation_id=_('Update module'),
|
||||||
|
parameters=ModuleEditAPI.get_parameters(),
|
||||||
|
request=ModuleEditAPI.get_request(),
|
||||||
|
responses=ModuleEditAPI.get_response(),
|
||||||
|
tags=[_('Module')])
|
||||||
|
@has_permissions(lambda r, kwargs: Permission(group=Group(kwargs.get('source')), operate=Operate.EDIT,
|
||||||
|
resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}"))
|
||||||
|
def put(self, request: Request, workspace_id: str, source: str, module_id: str):
|
||||||
|
return result.success(ModuleSerializer.Operate(
|
||||||
|
data={'id': module_id, 'workspace_id': workspace_id, 'source': source}
|
||||||
|
).edit(request.data))
|
||||||
|
|
||||||
|
@extend_schema(methods=['GET'],
|
||||||
|
description=_('Get module'),
|
||||||
|
operation_id=_('Get module'),
|
||||||
|
parameters=ModuleReadAPI.get_parameters(),
|
||||||
|
responses=ModuleReadAPI.get_response(),
|
||||||
|
tags=[_('Module')])
|
||||||
|
@has_permissions(lambda r, kwargs: Permission(group=Group(kwargs.get('source')), operate=Operate.READ,
|
||||||
|
resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}"))
|
||||||
|
def get(self, request: Request, workspace_id: str, source: str, module_id: str):
|
||||||
|
return result.success(ModuleSerializer.Operate(
|
||||||
|
data={'id': module_id, 'workspace_id': workspace_id, 'source': source}
|
||||||
|
).one())
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleTreeView(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@extend_schema(methods=['GET'],
|
||||||
|
description=_('Get module tree'),
|
||||||
|
operation_id=_('Get module tree'),
|
||||||
|
parameters=ModuleTreeReadAPI.get_parameters(),
|
||||||
|
responses=ModuleTreeReadAPI.get_response(),
|
||||||
|
tags=[_('Module')])
|
||||||
|
@has_permissions(lambda r, kwargs: Permission(group=Group(kwargs.get('source')), operate=Operate.READ,
|
||||||
|
resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}"))
|
||||||
|
def get(self, request: Request, workspace_id: str, source: str):
|
||||||
|
return result.success(ModuleTreeSerializer(
|
||||||
|
data={'workspace_id': workspace_id, 'source': source}
|
||||||
|
).get_module_tree())
|
||||||
@ -1,12 +1,19 @@
|
|||||||
# Generated by Django 5.2 on 2025-04-17 06:03
|
# Generated by Django 5.2 on 2025-04-18 04:07
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
import mptt.fields
|
||||||
import uuid_utils.compat
|
import uuid_utils.compat
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
from tools.models import ToolModule
|
||||||
|
|
||||||
|
|
||||||
|
def insert_default_data(apps, schema_editor):
|
||||||
|
# 创建一个根模块(没有父节点)
|
||||||
|
ToolModule.objects.create(id='root', name='根目录', user_id='f0dd8f71-e4ee-11ee-8c84-a8a1595801ab')
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@ -14,10 +21,37 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ToolModule',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(editable=False, max_length=64, primary_key=True, serialize=False,
|
||||||
|
verbose_name='主键id')),
|
||||||
|
('name', models.CharField(max_length=64, verbose_name='文件夹名称')),
|
||||||
|
('workspace_id',
|
||||||
|
models.CharField(db_index=True, default='default', max_length=64, verbose_name='工作空间id')),
|
||||||
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
||||||
|
('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
||||||
|
('lft', models.PositiveIntegerField(editable=False)),
|
||||||
|
('rght', models.PositiveIntegerField(editable=False)),
|
||||||
|
('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
|
||||||
|
('level', models.PositiveIntegerField(editable=False)),
|
||||||
|
('parent',
|
||||||
|
mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='children', to='tools.toolmodule')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user',
|
||||||
|
verbose_name='用户id')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'tool_module',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RunPython(insert_default_data),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Tool',
|
name='Tool',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False, verbose_name='主键id')),
|
('id',
|
||||||
|
models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False,
|
||||||
|
verbose_name='主键id')),
|
||||||
('name', models.CharField(max_length=64, verbose_name='函数名称')),
|
('name', models.CharField(max_length=64, verbose_name='函数名称')),
|
||||||
('desc', models.CharField(max_length=128, verbose_name='描述')),
|
('desc', models.CharField(max_length=128, verbose_name='描述')),
|
||||||
('code', models.CharField(max_length=102400, verbose_name='python代码')),
|
('code', models.CharField(max_length=102400, verbose_name='python代码')),
|
||||||
@ -25,14 +59,22 @@ class Migration(migrations.Migration):
|
|||||||
('init_field_list', models.JSONField(default=list, verbose_name='启动字段列表')),
|
('init_field_list', models.JSONField(default=list, verbose_name='启动字段列表')),
|
||||||
('icon', models.CharField(default='/ui/favicon.ico', max_length=256, verbose_name='函数库icon')),
|
('icon', models.CharField(default='/ui/favicon.ico', max_length=256, verbose_name='函数库icon')),
|
||||||
('is_active', models.BooleanField(default=True)),
|
('is_active', models.BooleanField(default=True)),
|
||||||
('scope', models.CharField(choices=[('SHARED', '共享'), ('WORKSPACE', '工作空间可用')], default='WORKSPACE', max_length=20, verbose_name='可用范围')),
|
('scope',
|
||||||
('tool_type', models.CharField(choices=[('INTERNAL', '内置'), ('PUBLIC', '公开')], default='PUBLIC', max_length=20, verbose_name='函数类型')),
|
models.CharField(choices=[('SHARED', '共享'), ('WORKSPACE', '工作空间可用')], default='WORKSPACE',
|
||||||
|
max_length=20, verbose_name='可用范围')),
|
||||||
|
('tool_type',
|
||||||
|
models.CharField(choices=[('INTERNAL', '内置'), ('PUBLIC', '公开')], default='PUBLIC', max_length=20,
|
||||||
|
verbose_name='函数类型')),
|
||||||
('template_id', models.UUIDField(default=None, null=True, verbose_name='模版id')),
|
('template_id', models.UUIDField(default=None, null=True, verbose_name='模版id')),
|
||||||
('module_id', models.CharField(default='root', max_length=64, null=True, verbose_name='模块id')),
|
('workspace_id', models.CharField(default='default', max_length=64, verbose_name='工作空间id')),
|
||||||
('init_params', models.CharField(max_length=102400, null=True, verbose_name='初始化参数')),
|
('init_params', models.CharField(max_length=102400, null=True, verbose_name='初始化参数')),
|
||||||
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
||||||
('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user', verbose_name='用户id')),
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user',
|
||||||
|
verbose_name='用户id')),
|
||||||
|
('module_id',
|
||||||
|
models.ForeignKey(default='root', on_delete=django.db.models.deletion.CASCADE, to='tools.toolmodule',
|
||||||
|
verbose_name='模块id')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'db_table': 'tool',
|
'db_table': 'tool',
|
||||||
|
|||||||
@ -1 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from .tool import *
|
from .tool import *
|
||||||
|
from .tool_module import *
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import uuid_utils.compat as uuid
|
import uuid_utils.compat as uuid
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
from .tool_module import ToolModule
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
@ -29,7 +30,8 @@ class Tool(models.Model):
|
|||||||
tool_type = models.CharField(max_length=20, verbose_name='函数类型', choices=ToolType.choices,
|
tool_type = models.CharField(max_length=20, verbose_name='函数类型', choices=ToolType.choices,
|
||||||
default=ToolType.PUBLIC)
|
default=ToolType.PUBLIC)
|
||||||
template_id = models.UUIDField(max_length=128, verbose_name="模版id", null=True, default=None)
|
template_id = models.UUIDField(max_length=128, verbose_name="模版id", null=True, default=None)
|
||||||
module_id = models.CharField(max_length=64, verbose_name="模块id", null=True, default='root')
|
module_id = models.ForeignKey(ToolModule, on_delete=models.CASCADE, verbose_name="模块id", default='root')
|
||||||
|
workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default")
|
||||||
init_params = models.CharField(max_length=102400, verbose_name="初始化参数", null=True)
|
init_params = models.CharField(max_length=102400, verbose_name="初始化参数", null=True)
|
||||||
create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True, null=True)
|
create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True, null=True)
|
||||||
update_time = models.DateTimeField(verbose_name="修改时间", auto_now=True, null=True)
|
update_time = models.DateTimeField(verbose_name="修改时间", auto_now=True, null=True)
|
||||||
|
|||||||
21
apps/tools/models/tool_module.py
Normal file
21
apps/tools/models/tool_module.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from django.db import models
|
||||||
|
from mptt.fields import TreeForeignKey
|
||||||
|
from mptt.models import MPTTModel
|
||||||
|
|
||||||
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
|
class ToolModule(MPTTModel):
|
||||||
|
id = models.CharField(primary_key=True, max_length=64, editable=False, verbose_name="主键id")
|
||||||
|
name = models.CharField(max_length=64, verbose_name="文件夹名称")
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户id")
|
||||||
|
workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True)
|
||||||
|
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
|
||||||
|
create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True, null=True)
|
||||||
|
update_time = models.DateTimeField(verbose_name="修改时间", auto_now=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "tool_module"
|
||||||
|
|
||||||
|
class MPTTMeta:
|
||||||
|
order_insertion_by = ['name']
|
||||||
@ -13,7 +13,7 @@ class ToolModelSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Tool
|
model = Tool
|
||||||
fields = ['id', 'name', 'icon', 'desc', 'code', 'input_field_list', 'init_field_list', 'init_params',
|
fields = ['id', 'name', 'icon', 'desc', 'code', 'input_field_list', 'init_field_list', 'init_params',
|
||||||
'scope', 'is_active', 'user_id', 'template_id',
|
'scope', 'is_active', 'user_id', 'template_id', 'workspace_id', 'module_id',
|
||||||
'create_time', 'update_time']
|
'create_time', 'update_time']
|
||||||
|
|
||||||
|
|
||||||
@ -38,12 +38,14 @@ class ToolCreateRequest(serializers.Serializer):
|
|||||||
|
|
||||||
code = serializers.CharField(required=True, label=_('tool content'))
|
code = serializers.CharField(required=True, label=_('tool content'))
|
||||||
|
|
||||||
input_field_list = serializers.ListField(child=ToolInputField(), required=True, label=_('input field list'))
|
input_field_list = serializers.ListField(child=ToolInputField(), required=False, label=_('input field list'))
|
||||||
|
|
||||||
init_field_list = serializers.ListField(required=False, default=list, label=_('init field list'))
|
init_field_list = serializers.ListField(required=False, default=list, label=_('init field list'))
|
||||||
|
|
||||||
is_active = serializers.BooleanField(required=False, label=_('Is active'))
|
is_active = serializers.BooleanField(required=False, label=_('Is active'))
|
||||||
|
|
||||||
|
module_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, default='root')
|
||||||
|
|
||||||
|
|
||||||
class ToolSerializer(serializers.Serializer):
|
class ToolSerializer(serializers.Serializer):
|
||||||
class Create(serializers.Serializer):
|
class Create(serializers.Serializer):
|
||||||
@ -58,8 +60,8 @@ class ToolSerializer(serializers.Serializer):
|
|||||||
desc=instance.get('desc'),
|
desc=instance.get('desc'),
|
||||||
code=instance.get('code'),
|
code=instance.get('code'),
|
||||||
user_id=self.data.get('user_id'),
|
user_id=self.data.get('user_id'),
|
||||||
input_field_list=instance.get('input_field_list'),
|
input_field_list=instance.get('input_field_list', []),
|
||||||
init_field_list=instance.get('init_field_list'),
|
init_field_list=instance.get('init_field_list', []),
|
||||||
scope=ToolScope.WORKSPACE,
|
scope=ToolScope.WORKSPACE,
|
||||||
is_active=False)
|
is_active=False)
|
||||||
tool.save()
|
tool.save()
|
||||||
|
|||||||
16
apps/tools/serializers/tool_module.py
Normal file
16
apps/tools/serializers/tool_module.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from tools.models import ToolModule
|
||||||
|
|
||||||
|
|
||||||
|
class ToolModuleTreeSerializer(serializers.ModelSerializer):
|
||||||
|
children = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ToolModule
|
||||||
|
fields = ['id', 'name', 'user_id', 'workspace_id', 'parent_id', 'children']
|
||||||
|
|
||||||
|
def get_children(self, obj):
|
||||||
|
return ToolModuleTreeSerializer(obj.get_children(), many=True).data
|
||||||
@ -11,34 +11,34 @@ django = "5.2"
|
|||||||
drf-spectacular = { extras = ["sidecar"], version = "0.28.0" }
|
drf-spectacular = { extras = ["sidecar"], version = "0.28.0" }
|
||||||
django-redis = "5.4.0"
|
django-redis = "5.4.0"
|
||||||
django-db-connection-pool = "1.2.5"
|
django-db-connection-pool = "1.2.5"
|
||||||
|
django-mptt = "0.17.0"
|
||||||
psycopg = { extras = ["binary"], version = "3.2.6" }
|
psycopg = { extras = ["binary"], version = "3.2.6" }
|
||||||
python-dotenv = "1.1.0"
|
python-dotenv = "1.1.0"
|
||||||
uuid-utils = "0.10.0"
|
uuid-utils = "0.10.0"
|
||||||
diskcache2 = "0.1.2"
|
diskcache2 = "0.1.2"
|
||||||
captcha = "0.7.1"
|
captcha = "0.7.1"
|
||||||
langchain-openai = "^0.3.0"
|
langchain-openai = "0.3.14"
|
||||||
langchain-anthropic = "^0.3.0"
|
langchain-anthropic = "0.3.12"
|
||||||
langchain-community = "^0.3.0"
|
langchain-community = "0.3.21"
|
||||||
langchain-deepseek = "^0.1.0"
|
langchain-deepseek = "0.1.3"
|
||||||
langchain-google-genai = "^2.0.9"
|
langchain-google-genai = "2.1.3"
|
||||||
langchain-mcp-adapters = "^0.0.5"
|
langchain-mcp-adapters = "0.0.8"
|
||||||
langchain-huggingface = "^0.1.2"
|
langchain-huggingface = "0.1.2"
|
||||||
langchain-ollama = "^0.3.0"
|
langchain-ollama = "0.3.2"
|
||||||
langgraph = "^0.3.0"
|
langgraph = "0.3.31"
|
||||||
mcp = "^1.4.1"
|
qianfan = "0.3.18"
|
||||||
qianfan = "^0.3.6.1"
|
zhipuai = "2.1.5.20250415"
|
||||||
zhipuai = "^2.0.1"
|
boto3 = "1.37.36"
|
||||||
boto3 = "^1.34.160"
|
tencentcloud-sdk-python = "3.0.1362"
|
||||||
tencentcloud-sdk-python = "^3.0.1209"
|
xinference-client = "1.4.1"
|
||||||
xinference-client = "^1.3.0"
|
anthropic = "0.49.0"
|
||||||
anthropic = "^0.49.0"
|
dashscope = "1.23.1"
|
||||||
dashscope = "^1.17.0"
|
|
||||||
pylint = "3.1.0"
|
pylint = "3.1.0"
|
||||||
pydub = "^0.25.1"
|
pydub = "0.25.1"
|
||||||
cffi = "^1.17.1"
|
cffi = "1.17.1"
|
||||||
pysilk = "^0.0.1"
|
pysilk = "0.0.1"
|
||||||
sentence-transformers = "^4.0.2"
|
sentence-transformers = "4.1.0"
|
||||||
websockets = "^13.0"
|
websockets = "13.1"
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user