feat: application save (#3150)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run

This commit is contained in:
shaohuzhang1 2025-05-27 11:08:55 +08:00 committed by GitHub
parent 60ff19be17
commit 080a6031bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 240 additions and 21 deletions

View File

@ -13,12 +13,18 @@ from rest_framework import serializers
from application.serializers.application import ApplicationCreateSerializer
from common.mixins.api_mixin import APIMixin
from common.result import ResultSerializer
class ApplicationCreateRequest(ApplicationCreateSerializer.SimplateRequest):
work_flow = serializers.DictField(required=True, label=_("Workflow Objects"))
class ApplicationCreateResponse(ResultSerializer):
def get_data(self):
return ApplicationCreateSerializer.ApplicationResponse()
class ApplicationCreateAPI(APIMixin):
@staticmethod
def get_parameters():
@ -38,4 +44,4 @@ class ApplicationCreateAPI(APIMixin):
@staticmethod
def get_response():
return FolderCreateResponse
return ApplicationCreateResponse

View File

@ -0,0 +1,53 @@
# Generated by Django 5.2 on 2025-05-27 03:05
import django.contrib.postgres.fields
import django.db.models.deletion
import uuid_utils.compat
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('application', '0002_applicationapikey'),
('knowledge', '0007_alter_document_status_alter_paragraph_status_and_more'),
]
operations = [
migrations.CreateModel(
name='ApplicationAccessToken',
fields=[
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
('application', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='application.application', verbose_name='应用id')),
('access_token', models.CharField(max_length=128, unique=True, verbose_name='用户公开访问 认证token')),
('is_active', models.BooleanField(default=True, verbose_name='是否开启公开访问')),
('access_num', models.IntegerField(default=100, verbose_name='访问次数')),
('white_active', models.BooleanField(default=False, verbose_name='是否开启白名单')),
('white_list', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=128), default=list, size=None, verbose_name='白名单列表')),
('show_source', models.BooleanField(default=False, verbose_name='是否显示知识来源')),
('language', models.CharField(default=None, max_length=10, null=True, verbose_name='语言')),
],
options={
'db_table': 'application_access_token',
},
),
migrations.AddField(
model_name='application',
name='is_publish',
field=models.BooleanField(default=False, verbose_name='是否发布'),
),
migrations.CreateModel(
name='ApplicationKnowledgeMapping',
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')),
('application', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='application.application')),
('knowledge', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='knowledge.knowledge')),
],
options={
'db_table': 'application_knowledge_mapping',
},
),
]

View File

@ -6,3 +6,5 @@
@date2025/5/7 15:14
@desc:
"""
from .application import *
from .application_access_token import *

View File

@ -12,6 +12,7 @@ from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel
from common.mixins.app_model_mixin import AppModelMixin
from knowledge.models import Knowledge
from models_provider.models import Model
from users.models import User
@ -59,6 +60,7 @@ class Application(AppModelMixin):
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True)
folder = models.ForeignKey(ApplicationFolder, on_delete=models.DO_NOTHING, verbose_name="文件夹id", default='root')
is_publish = models.BooleanField(verbose_name="是否发布", default=False)
name = models.CharField(max_length=128, verbose_name="应用名称")
desc = models.CharField(max_length=512, verbose_name="引用描述", default="")
prologue = models.CharField(max_length=40960, verbose_name="开场白", default="")
@ -106,3 +108,12 @@ class Application(AppModelMixin):
class Meta:
db_table = "application"
class ApplicationKnowledgeMapping(AppModelMixin):
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
application = models.ForeignKey(Application, on_delete=models.DO_NOTHING)
knowledge = models.ForeignKey(Knowledge, on_delete=models.DO_NOTHING)
class Meta:
db_table = "application_knowledge_mapping"

View File

@ -0,0 +1,33 @@
# coding=utf-8
"""
@project: MaxKB
@Author虎虎
@file application_access_token.py
@date2025/5/27 9:55
@desc:
"""
from django.contrib.postgres.fields import ArrayField
from django.db import models
from application.models.application import Application
from common.mixins.app_model_mixin import AppModelMixin
class ApplicationAccessToken(AppModelMixin):
"""
应用认证token
"""
application = models.OneToOneField(Application, primary_key=True, on_delete=models.CASCADE, verbose_name="应用id")
access_token = models.CharField(max_length=128, verbose_name="用户公开访问 认证token", unique=True)
is_active = models.BooleanField(default=True, verbose_name="是否开启公开访问")
access_num = models.IntegerField(default=100, verbose_name="访问次数")
white_active = models.BooleanField(default=False, verbose_name="是否开启白名单")
white_list = ArrayField(verbose_name="白名单列表",
base_field=models.CharField(max_length=128, blank=True)
, default=list)
show_source = models.BooleanField(default=False, verbose_name="是否显示知识来源")
language = models.CharField(max_length=10, verbose_name="语言", default=None, null=True)
class Meta:
db_table = "application_access_token"

View File

@ -6,6 +6,7 @@
@date2025/5/26 17:03
@desc:
"""
import hashlib
import re
from typing import Dict
@ -16,7 +17,8 @@ from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.models.application import Application, ApplicationTypeChoices
from application.models.application import Application, ApplicationTypeChoices, ApplicationKnowledgeMapping
from application.models.application_access_token import ApplicationAccessToken
from common.exception.app_exception import AppApiException
from knowledge.models import Knowledge
from models_provider.models import Model
@ -94,6 +96,11 @@ class ModelSettingSerializer(serializers.Serializer):
class ApplicationCreateSerializer(serializers.Serializer):
class ApplicationResponse(serializers.ModelSerializer):
class Meta:
model = Application
fields = "__all__"
class WorkflowRequest(serializers.Serializer):
name = serializers.CharField(required=True, max_length=64, min_length=1,
label=_("Application Name"))
@ -105,7 +112,7 @@ class ApplicationCreateSerializer(serializers.Serializer):
label=_("Opening remarks"))
@staticmethod
def to_application_model(user_id: str, application: Dict):
def to_application_model(user_id: str, workspace_id: str, application: Dict):
default_workflow = application.get('work_flow')
for node in default_workflow.get('nodes'):
if node.get('id') == 'base-node':
@ -115,6 +122,7 @@ class ApplicationCreateSerializer(serializers.Serializer):
return Application(id=uuid.uuid7(),
name=application.get('name'),
desc=application.get('desc'),
workspace_id=workspace_id,
prologue="",
dialogue_number=0,
user_id=user_id, model_id=None,
@ -176,7 +184,70 @@ class ApplicationCreateSerializer(serializers.Serializer):
ModelKnowledgeAssociation(data={'user_id': user_id, 'model_id': self.data.get('model_id'),
'knowledge_id_list': self.data.get('knowledge_id_list')}).is_valid()
@staticmethod
def to_application_model(user_id: str, application: Dict):
return Application(id=uuid.uuid1(), name=application.get('name'), desc=application.get('desc'),
prologue=application.get('prologue'),
dialogue_number=application.get('dialogue_number', 0),
user_id=user_id, model_id=application.get('model_id'),
dataset_setting=application.get('dataset_setting'),
model_setting=application.get('model_setting'),
problem_optimization=application.get('problem_optimization'),
type=ApplicationTypeChoices.SIMPLE,
model_params_setting=application.get('model_params_setting', {}),
problem_optimization_prompt=application.get('problem_optimization_prompt', None),
stt_model_enable=application.get('stt_model_enable', False),
stt_model_id=application.get('stt_model', None),
tts_model_id=application.get('tts_model', None),
tts_model_enable=application.get('tts_model_enable', False),
tts_model_params_setting=application.get('tts_model_params_setting', {}),
tts_type=application.get('tts_type', None),
file_upload_enable=application.get('file_upload_enable', False),
file_upload_setting=application.get('file_upload_setting', {}),
work_flow={}
)
class ApplicationSerializer(serializers.Serializer):
def insert(self):
pass
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
user_id = serializers.UUIDField(required=True, label=_("User ID"))
def insert(self, instance: Dict, with_valid=True):
application_type = instance.get('type')
if 'WORK_FLOW' == application_type:
return self.insert_workflow(instance)
else:
return self.insert_simple(instance)
def insert_workflow(self, instance: Dict):
self.is_valid(raise_exception=True)
user_id = self.data.get('user_id')
ApplicationCreateSerializer.WorkflowRequest(data=instance).is_valid(raise_exception=True)
application_model = ApplicationCreateSerializer.WorkflowRequest.to_application_model(user_id, instance)
application_model.save()
# 插入认证信息
ApplicationAccessToken(application_id=application_model.id,
access_token=hashlib.md5(str(uuid.uuid1()).encode()).hexdigest()[8:24]).save()
return ApplicationCreateSerializer.ApplicationResponse(application_model).data
@staticmethod
def to_application_knowledge_mapping(application_id: str, dataset_id: str):
return ApplicationKnowledgeMapping(id=uuid.uuid1(), application_id=application_id, dataset_id=dataset_id)
def insert_simple(self, instance: Dict):
self.is_valid(raise_exception=True)
user_id = self.data.get('user_id')
ApplicationCreateSerializer.SimplateRequest(data=instance).is_valid(user_id=user_id, raise_exception=True)
application_model = ApplicationCreateSerializer.SimplateRequest.to_application_model(user_id, instance)
dataset_id_list = instance.get('knowledge_id_list', [])
application_knowledge_mapping_model_list = [
self.to_application_knowledge_mapping(application_model.id, dataset_id) for
dataset_id in dataset_id_list]
# 插入应用
application_model.save()
# 插入认证信息
ApplicationAccessToken(application_id=application_model.id,
access_token=hashlib.md5(str(uuid.uuid1()).encode()).hexdigest()[8:24]).save()
# 插入关联数据
QuerySet(ApplicationKnowledgeMapping).bulk_create(application_knowledge_mapping_model_list)
return ApplicationCreateSerializer.ApplicationResponse(application_model).data

View File

@ -5,5 +5,6 @@ from . import views
app_name = 'application'
urlpatterns = [
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key', views.ApplicationKey.as_view()),
]
path('workspace/<str:workspace_id>/application', views.Application.as_view(), name='application'),
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key',
views.ApplicationKey.as_view())]

View File

@ -6,4 +6,5 @@
@date2025/5/9 18:51
@desc:
"""
from .application_api_key import *
from .application_api_key import *
from .application import *

View File

@ -14,9 +14,11 @@ from rest_framework.views import APIView
from application.api.application_api import ApplicationCreateAPI
from application.serializers.application import ApplicationSerializer
from common import result
from common.auth import TokenAuth
class Application(APIView):
authentication_classes = [TokenAuth]
@extend_schema(
methods=['POST'],
@ -28,5 +30,6 @@ class Application(APIView):
responses=ApplicationCreateAPI.get_response(),
tags=[_('Application')] # type: ignore
)
def post(self, request: Request):
return result.success(ApplicationSerializer.insert(request.data))
def post(self, request: Request, workspace_id: str):
return result.success(
ApplicationSerializer(data={'workspace_id': workspace_id, 'user_id': request.user.id}).insert(request.data))

View File

@ -0,0 +1,27 @@
# Generated by Django 5.2 on 2025-05-27 03:05
import knowledge.models.knowledge
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('knowledge', '0006_knowledgefolder_desc_and_more'),
]
operations = [
migrations.AlterField(
model_name='document',
name='status',
field=models.CharField(default=knowledge.models.knowledge.Status.__str__, max_length=20, verbose_name='状态'),
),
migrations.AlterField(
model_name='paragraph',
name='status',
field=models.CharField(default=knowledge.models.knowledge.Status.__str__, max_length=20, verbose_name='状态'),
),
migrations.DeleteModel(
name='ApplicationKnowledgeMapping',
),
]

View File

@ -202,15 +202,6 @@ class ProblemParagraphMapping(AppModelMixin):
db_table = "problem_paragraph_mapping"
class ApplicationKnowledgeMapping(AppModelMixin):
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
# application = models.ForeignKey(Application, on_delete=models.DO_NOTHING)
knowledge = models.ForeignKey(Knowledge, on_delete=models.DO_NOTHING)
class Meta:
db_table = "application_knowledge_mapping"
class SourceType(models.IntegerChoices):
"""订单类型"""
PROBLEM = 0, '问题'

View File

@ -14,6 +14,7 @@ from django.db.models.functions import Reverse, Substr
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.models import ApplicationKnowledgeMapping
from common.config.embedding_config import VectorStore
from common.db.search import native_search, get_dynamics_model, native_page_search
from common.db.sql_execute import select_list
@ -23,7 +24,7 @@ from common.utils.common import valid_license, post, get_file_content
from common.utils.fork import Fork, ChildLink
from common.utils.split_model import get_split_model
from knowledge.models import Knowledge, KnowledgeScope, KnowledgeType, Document, Paragraph, Problem, \
ProblemParagraphMapping, ApplicationKnowledgeMapping, TaskType, State, SearchMode, KnowledgeFolder
ProblemParagraphMapping, TaskType, State, SearchMode, KnowledgeFolder
from knowledge.serializers.common import ProblemParagraphManage, get_embedding_model_id_by_knowledge_id, MetaSerializer, \
GenerateRelatedSerializer, get_embedding_model_by_knowledge_id, list_paragraph
from knowledge.serializers.document import DocumentSerializers

View File

@ -40,7 +40,7 @@ INSTALLED_APPS = [
'drf_spectacular_sidecar',
'users.apps.UsersConfig',
'tools.apps.ToolConfig',
'knowledge.apps.KnowledgeConfig',
'knowledge',
'common',
'system_manage',
'models_provider',

View File

@ -27,6 +27,7 @@ urlpatterns = [
path("api/", include("folders.urls")),
path("api/", include("knowledge.urls")),
path("api/", include("system_manage.urls")),
path("api/", include("application.urls"))
]
urlpatterns += [
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由下面两个ui也是根据这个配置文件来生成的

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2 on 2025-05-27 02:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='nick_name',
field=models.CharField(max_length=150, unique=True, verbose_name='昵称'),
),
]