mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: add knowledge management API and permissions
This commit is contained in:
parent
83ace97ecc
commit
724686762e
|
|
@ -153,8 +153,11 @@ class PermissionConstants(Enum):
|
|||
KNOWLEDGE_MODULE_EDIT = Permission(group=Group.KNOWLEDGE, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
KNOWLEDGE_MODULE_DELETE = Permission(group=Group.KNOWLEDGE, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
|
||||
RoleConstants.USER])
|
||||
KNOWLEDGE_READ = Permission(group=Group.KNOWLEDGE, operate=Operate.READ, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
KNOWLEDGE_CREATE = Permission(group=Group.KNOWLEDGE, operate=Operate.CREATE, role_list=[RoleConstants.ADMIN,
|
||||
RoleConstants.USER])
|
||||
def get_workspace_application_permission(self):
|
||||
return lambda r, kwargs: Permission(group=self.value.group, operate=self.value.operate,
|
||||
resource_path=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
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 knowledge.serializers.knowledge import KnowledgeBaseCreateRequest, KnowledgeModelSerializer
|
||||
|
||||
|
||||
class KnowledgeCreateResponse(ResultSerializer):
|
||||
def get_data(self):
|
||||
return KnowledgeModelSerializer()
|
||||
|
||||
|
||||
class KnowledgeCreateAPI(APIMixin):
|
||||
@staticmethod
|
||||
def get_parameters():
|
||||
return [
|
||||
OpenApiParameter(
|
||||
name="workspace_id",
|
||||
description="工作空间id",
|
||||
type=OpenApiTypes.STR,
|
||||
location='path',
|
||||
required=True,
|
||||
)
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_request():
|
||||
return KnowledgeBaseCreateRequest
|
||||
|
||||
@staticmethod
|
||||
def get_response():
|
||||
return KnowledgeCreateResponse
|
||||
|
||||
|
||||
class KnowledgeTreeReadAPI(APIMixin):
|
||||
@staticmethod
|
||||
def get_parameters():
|
||||
return [
|
||||
OpenApiParameter(
|
||||
name="workspace_id",
|
||||
description="工作空间id",
|
||||
type=OpenApiTypes.STR,
|
||||
location='path',
|
||||
required=True,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="module_id",
|
||||
description="模块id",
|
||||
type=OpenApiTypes.STR,
|
||||
location='query',
|
||||
required=False,
|
||||
)
|
||||
]
|
||||
|
|
@ -14,9 +14,7 @@ def insert_default_data(apps, schema_editor):
|
|||
KnowledgeModule.objects.create(id='root', name='根目录', user_id='f0dd8f71-e4ee-11ee-8c84-a8a1595801ab')
|
||||
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
|
|
@ -30,8 +28,12 @@ class Migration(migrations.Migration):
|
|||
fields=[
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
|
||||
('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')),
|
||||
('file_name', models.CharField(default='', max_length=256, verbose_name='文件名称')),
|
||||
('workspace_id',
|
||||
models.CharField(db_index=True, default='default', max_length=64, verbose_name='工作空间id')),
|
||||
('loid', models.IntegerField(verbose_name='loid')),
|
||||
('meta', models.JSONField(default=dict, verbose_name='文件关联数据')),
|
||||
],
|
||||
|
|
@ -39,41 +41,62 @@ class Migration(migrations.Migration):
|
|||
'db_table': 'file',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Knowledge',
|
||||
fields=[
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
|
||||
('id', models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False, verbose_name='主键id')),
|
||||
('name', models.CharField(max_length=150, verbose_name='知识库名称')),
|
||||
('desc', models.CharField(max_length=256, verbose_name='描述')),
|
||||
('type', models.IntegerField(choices=[(0, '通用类型'), (1, 'web站点类型'), (2, '飞书类型'), (3, '语雀类型')], default=0, verbose_name='类型')),
|
||||
('meta', models.JSONField(default=dict, verbose_name='元数据')),
|
||||
('embedding_mode', models.ForeignKey(default=knowledge.models.knowledge.default_model, on_delete=django.db.models.deletion.DO_NOTHING, to='models_provider.model', verbose_name='向量模型')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='users.user', verbose_name='所属用户')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'knowledge',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='KnowledgeModule',
|
||||
fields=[
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
|
||||
('id', models.CharField(editable=False, max_length=64, primary_key=True, serialize=False, verbose_name='主键id')),
|
||||
('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')),
|
||||
('workspace_id',
|
||||
models.CharField(db_index=True, default='default', max_length=64, verbose_name='工作空间id')),
|
||||
('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='knowledge.knowledgemodule')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user', verbose_name='用户id')),
|
||||
('parent',
|
||||
mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='children', to='knowledge.knowledgemodule')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='users.user',
|
||||
verbose_name='用户id')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'knowledge_module',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Knowledge',
|
||||
fields=[
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
|
||||
('id',
|
||||
models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False,
|
||||
verbose_name='主键id')),
|
||||
('name', models.CharField(max_length=150, verbose_name='知识库名称')),
|
||||
('workspace_id',
|
||||
models.CharField(db_index=True, default='default', max_length=64, verbose_name='工作空间id')),
|
||||
('desc', models.CharField(max_length=256, verbose_name='描述')),
|
||||
('type',
|
||||
models.IntegerField(choices=[(0, '通用类型'), (1, 'web站点类型'), (2, '飞书类型'), (3, '语雀类型')],
|
||||
default=0, verbose_name='类型')),
|
||||
('meta', models.JSONField(default=dict, verbose_name='元数据')),
|
||||
('scope',
|
||||
models.CharField(choices=[('SHARED', '共享'), ('WORKSPACE', '工作空间可用')], default='WORKSPACE',
|
||||
max_length=20, verbose_name='可用范围')),
|
||||
('module',
|
||||
models.ForeignKey(default='root', on_delete=django.db.models.deletion.CASCADE,
|
||||
to='knowledge.knowledgemodule',
|
||||
verbose_name='模块id')),
|
||||
('embedding_model', models.ForeignKey(default=knowledge.models.knowledge.default_model,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
to='models_provider.model', verbose_name='向量模型')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='users.user',
|
||||
verbose_name='所属用户')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'knowledge',
|
||||
},
|
||||
),
|
||||
migrations.RunPython(insert_default_data),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -12,10 +12,15 @@ from users.models import User
|
|||
|
||||
|
||||
class KnowledgeType(models.IntegerChoices):
|
||||
base = 0, '通用类型'
|
||||
web = 1, 'web站点类型'
|
||||
lark = 2, '飞书类型'
|
||||
yuque = 3, '语雀类型'
|
||||
BASE = 0, '通用类型'
|
||||
WEB = 1, 'web站点类型'
|
||||
LARK = 2, '飞书类型'
|
||||
YUQUE = 3, '语雀类型'
|
||||
|
||||
|
||||
class KnowledgeScope(models.TextChoices):
|
||||
SHARED = "SHARED", '共享'
|
||||
WORKSPACE = "WORKSPACE", "工作空间可用"
|
||||
|
||||
|
||||
def default_model():
|
||||
|
|
@ -26,7 +31,7 @@ def default_model():
|
|||
class KnowledgeModule(MPTTModel, AppModelMixin):
|
||||
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")
|
||||
user = models.ForeignKey(User, on_delete=models.DO_NOTHING, 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')
|
||||
|
||||
|
|
@ -37,13 +42,17 @@ class KnowledgeModule(MPTTModel, AppModelMixin):
|
|||
order_insertion_by = ['name']
|
||||
|
||||
|
||||
|
||||
class Knowledge(AppModelMixin):
|
||||
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
|
||||
name = models.CharField(max_length=150, verbose_name="知识库名称")
|
||||
workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True)
|
||||
desc = models.CharField(max_length=256, verbose_name="描述")
|
||||
user = models.ForeignKey(User, on_delete=models.DO_NOTHING, verbose_name="所属用户")
|
||||
type = models.IntegerField(verbose_name='类型', choices=KnowledgeType.choices, default=KnowledgeType.base)
|
||||
embedding_mode = models.ForeignKey(Model, on_delete=models.DO_NOTHING, verbose_name="向量模型",
|
||||
type = models.IntegerField(verbose_name='类型', choices=KnowledgeType.choices, default=KnowledgeType.BASE)
|
||||
scope = models.CharField(max_length=20, verbose_name='可用范围', choices=KnowledgeScope.choices, default=KnowledgeScope.WORKSPACE)
|
||||
module = models.ForeignKey(KnowledgeModule, on_delete=models.CASCADE, verbose_name="模块id", default='root')
|
||||
embedding_model = models.ForeignKey(Model, on_delete=models.DO_NOTHING, verbose_name="向量模型",
|
||||
default=default_model)
|
||||
meta = models.JSONField(verbose_name="元数据", default=dict)
|
||||
|
||||
|
|
@ -54,6 +63,7 @@ class Knowledge(AppModelMixin):
|
|||
class File(AppModelMixin):
|
||||
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
|
||||
file_name = models.CharField(max_length=256, verbose_name="文件名称", default="")
|
||||
workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True)
|
||||
loid = models.IntegerField(verbose_name="loid")
|
||||
meta = models.JSONField(verbose_name="文件关联数据", default=dict)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# coding=utf-8
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import uuid_utils as uuid
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from knowledge.models import Knowledge, KnowledgeScope, KnowledgeType
|
||||
|
||||
|
||||
class KnowledgeModelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Knowledge
|
||||
fields = ['id', 'name', 'desc', 'meta', 'module_id', 'type', 'workspace_id', 'create_time', 'update_time']
|
||||
|
||||
|
||||
class KnowledgeBaseCreateRequest(serializers.Serializer):
|
||||
name = serializers.CharField(required=True, label=_('knowledge name'))
|
||||
desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('knowledge description'))
|
||||
embedding = serializers.CharField(required=True, label=_('knowledge embedding'))
|
||||
|
||||
class KnowledgeWebCreateRequest(serializers.Serializer):
|
||||
name = serializers.CharField(required=True, label=_('knowledge name'))
|
||||
desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('knowledge description'))
|
||||
embedding = serializers.CharField(required=True, label=_('knowledge embedding'))
|
||||
|
||||
|
||||
class KnowledgeSerializer(serializers.Serializer):
|
||||
class Create(serializers.Serializer):
|
||||
user_id = serializers.UUIDField(required=True, label=_('user id'))
|
||||
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
|
||||
|
||||
def insert(self, instance, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
KnowledgeBaseCreateRequest(data=instance).is_valid(raise_exception=True)
|
||||
knowledge = Knowledge(
|
||||
id=uuid.uuid7(),
|
||||
name=instance.get('name'),
|
||||
workspace_id=self.data.get('workspace_id'),
|
||||
desc=instance.get('desc'),
|
||||
type=instance.get('type', KnowledgeType.BASE),
|
||||
user_id=self.data.get('user_id'),
|
||||
scope=KnowledgeScope.WORKSPACE,
|
||||
module_id=instance.get('module_id', 'root'),
|
||||
embedding_model_id=instance.get('embedding'),
|
||||
meta=instance.get('meta', {}),
|
||||
)
|
||||
knowledge.save()
|
||||
return KnowledgeModelSerializer(knowledge).data
|
||||
|
||||
|
||||
class KnowledgeTreeSerializer(serializers.Serializer):
|
||||
def get_knowledge_list(self, param):
|
||||
pass
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
from django.shortcuts import render
|
||||
from django.urls import path
|
||||
|
||||
# Create your views here.
|
||||
from . import views
|
||||
|
||||
app_name = "knowledge"
|
||||
urlpatterns = [
|
||||
path('workspace/<str:workspace_id>/knowledge', views.KnowledgeView.as_view()),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
from .knowledge import *
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
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 PermissionConstants
|
||||
from common.result import result
|
||||
from knowledge.api.knowledge import KnowledgeCreateAPI, KnowledgeTreeReadAPI
|
||||
from knowledge.serializers.knowledge import KnowledgeSerializer, KnowledgeTreeSerializer
|
||||
|
||||
|
||||
class KnowledgeView(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@extend_schema(
|
||||
methods=['POST'],
|
||||
description=_('Create knowledge'),
|
||||
operation_id=_('Create knowledge'),
|
||||
parameters=KnowledgeCreateAPI.get_parameters(),
|
||||
request=KnowledgeCreateAPI.get_request(),
|
||||
responses=KnowledgeCreateAPI.get_response(),
|
||||
tags=[_('Knowledge Base')]
|
||||
)
|
||||
@has_permissions(PermissionConstants.KNOWLEDGE_CREATE.get_workspace_permission())
|
||||
def post(self, request: Request, workspace_id: str):
|
||||
return result.success(KnowledgeSerializer.Create(
|
||||
data={'user_id': request.user.id, 'workspace_id': workspace_id}
|
||||
).insert(request.data))
|
||||
|
||||
@extend_schema(
|
||||
methods=['GET'],
|
||||
description=_('Get knowledge by module'),
|
||||
operation_id=_('Get knowledge by module'),
|
||||
parameters=KnowledgeTreeReadAPI.get_parameters(),
|
||||
responses=KnowledgeTreeReadAPI.get_response(),
|
||||
tags=[_('Knowledge Base')]
|
||||
)
|
||||
@has_permissions(PermissionConstants.KNOWLEDGE_READ.get_workspace_permission())
|
||||
def get(self, request: Request, workspace_id: str):
|
||||
return result.success(KnowledgeTreeSerializer(
|
||||
data={'workspace_id': workspace_id}
|
||||
).get_knowledge_list(request.query_params.get('module_id')))
|
||||
|
|
@ -24,7 +24,8 @@ urlpatterns = [
|
|||
path("api/", include("users.urls")),
|
||||
path("api/", include("tools.urls")),
|
||||
path("api/", include("models_provider.urls")),
|
||||
path("api/", include("modules.urls"))
|
||||
path("api/", include("modules.urls")),
|
||||
path("api/", include("knowledge.urls")),
|
||||
]
|
||||
urlpatterns += [
|
||||
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class Migration(migrations.Migration):
|
|||
('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',
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='users.user',
|
||||
verbose_name='用户id')),
|
||||
],
|
||||
options={
|
||||
|
|
@ -70,7 +70,7 @@ class Migration(migrations.Migration):
|
|||
('template_id', models.UUIDField(default=None, null=True, verbose_name='模版id')),
|
||||
('workspace_id', models.CharField(default='default', max_length=64, verbose_name='工作空间id', db_index=True)),
|
||||
('init_params', models.CharField(max_length=102400, null=True, verbose_name='初始化参数')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user',
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='users.user',
|
||||
verbose_name='用户id')),
|
||||
('module',
|
||||
models.ForeignKey(default='root', on_delete=django.db.models.deletion.CASCADE, to='tools.toolmodule',
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from users.models import User
|
|||
class ToolModule(MPTTModel, AppModelMixin):
|
||||
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")
|
||||
user = models.ForeignKey(User, on_delete=models.DO_NOTHING, 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')
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ class ToolType(models.TextChoices):
|
|||
|
||||
class Tool(AppModelMixin):
|
||||
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户id")
|
||||
user = models.ForeignKey(User, on_delete=models.DO_NOTHING, verbose_name="用户id")
|
||||
name = models.CharField(max_length=64, verbose_name="工具名称")
|
||||
desc = models.CharField(max_length=128, verbose_name="描述")
|
||||
code = models.CharField(max_length=102400, verbose_name="python代码")
|
||||
|
|
|
|||
Loading…
Reference in New Issue