Skip to content

Commit 080a603

Browse files
authored
feat: application save (#3150)
1 parent 60ff19b commit 080a603

File tree

15 files changed

+240
-21
lines changed

15 files changed

+240
-21
lines changed

apps/application/api/application_api.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,18 @@
1313

1414
from application.serializers.application import ApplicationCreateSerializer
1515
from common.mixins.api_mixin import APIMixin
16+
from common.result import ResultSerializer
1617

1718

1819
class ApplicationCreateRequest(ApplicationCreateSerializer.SimplateRequest):
1920
work_flow = serializers.DictField(required=True, label=_("Workflow Objects"))
2021

2122

23+
class ApplicationCreateResponse(ResultSerializer):
24+
def get_data(self):
25+
return ApplicationCreateSerializer.ApplicationResponse()
26+
27+
2228
class ApplicationCreateAPI(APIMixin):
2329
@staticmethod
2430
def get_parameters():
@@ -38,4 +44,4 @@ def get_request():
3844

3945
@staticmethod
4046
def get_response():
41-
return FolderCreateResponse
47+
return ApplicationCreateResponse
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Generated by Django 5.2 on 2025-05-27 03:05
2+
3+
import django.contrib.postgres.fields
4+
import django.db.models.deletion
5+
import uuid_utils.compat
6+
from django.db import migrations, models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('application', '0002_applicationapikey'),
13+
('knowledge', '0007_alter_document_status_alter_paragraph_status_and_more'),
14+
]
15+
16+
operations = [
17+
migrations.CreateModel(
18+
name='ApplicationAccessToken',
19+
fields=[
20+
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
21+
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
22+
('application', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='application.application', verbose_name='应用id')),
23+
('access_token', models.CharField(max_length=128, unique=True, verbose_name='用户公开访问 认证token')),
24+
('is_active', models.BooleanField(default=True, verbose_name='是否开启公开访问')),
25+
('access_num', models.IntegerField(default=100, verbose_name='访问次数')),
26+
('white_active', models.BooleanField(default=False, verbose_name='是否开启白名单')),
27+
('white_list', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=128), default=list, size=None, verbose_name='白名单列表')),
28+
('show_source', models.BooleanField(default=False, verbose_name='是否显示知识来源')),
29+
('language', models.CharField(default=None, max_length=10, null=True, verbose_name='语言')),
30+
],
31+
options={
32+
'db_table': 'application_access_token',
33+
},
34+
),
35+
migrations.AddField(
36+
model_name='application',
37+
name='is_publish',
38+
field=models.BooleanField(default=False, verbose_name='是否发布'),
39+
),
40+
migrations.CreateModel(
41+
name='ApplicationKnowledgeMapping',
42+
fields=[
43+
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
44+
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
45+
('id', models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False, verbose_name='主键id')),
46+
('application', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='application.application')),
47+
('knowledge', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='knowledge.knowledge')),
48+
],
49+
options={
50+
'db_table': 'application_knowledge_mapping',
51+
},
52+
),
53+
]

apps/application/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@
66
@date:2025/5/7 15:14
77
@desc:
88
"""
9+
from .application import *
10+
from .application_access_token import *

apps/application/models/application.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from mptt.models import MPTTModel
1313

1414
from common.mixins.app_model_mixin import AppModelMixin
15+
from knowledge.models import Knowledge
1516
from models_provider.models import Model
1617
from users.models import User
1718

@@ -59,6 +60,7 @@ class Application(AppModelMixin):
5960
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
6061
workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True)
6162
folder = models.ForeignKey(ApplicationFolder, on_delete=models.DO_NOTHING, verbose_name="文件夹id", default='root')
63+
is_publish = models.BooleanField(verbose_name="是否发布", default=False)
6264
name = models.CharField(max_length=128, verbose_name="应用名称")
6365
desc = models.CharField(max_length=512, verbose_name="引用描述", default="")
6466
prologue = models.CharField(max_length=40960, verbose_name="开场白", default="")
@@ -106,3 +108,12 @@ def get_default_model_prompt():
106108

107109
class Meta:
108110
db_table = "application"
111+
112+
113+
class ApplicationKnowledgeMapping(AppModelMixin):
114+
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
115+
application = models.ForeignKey(Application, on_delete=models.DO_NOTHING)
116+
knowledge = models.ForeignKey(Knowledge, on_delete=models.DO_NOTHING)
117+
118+
class Meta:
119+
db_table = "application_knowledge_mapping"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: application_access_token.py
6+
@date:2025/5/27 9:55
7+
@desc:
8+
"""
9+
from django.contrib.postgres.fields import ArrayField
10+
from django.db import models
11+
12+
from application.models.application import Application
13+
from common.mixins.app_model_mixin import AppModelMixin
14+
15+
16+
class ApplicationAccessToken(AppModelMixin):
17+
"""
18+
应用认证token
19+
"""
20+
application = models.OneToOneField(Application, primary_key=True, on_delete=models.CASCADE, verbose_name="应用id")
21+
access_token = models.CharField(max_length=128, verbose_name="用户公开访问 认证token", unique=True)
22+
is_active = models.BooleanField(default=True, verbose_name="是否开启公开访问")
23+
access_num = models.IntegerField(default=100, verbose_name="访问次数")
24+
white_active = models.BooleanField(default=False, verbose_name="是否开启白名单")
25+
white_list = ArrayField(verbose_name="白名单列表",
26+
base_field=models.CharField(max_length=128, blank=True)
27+
, default=list)
28+
show_source = models.BooleanField(default=False, verbose_name="是否显示知识来源")
29+
30+
language = models.CharField(max_length=10, verbose_name="语言", default=None, null=True)
31+
32+
class Meta:
33+
db_table = "application_access_token"

apps/application/serializers/application.py

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@date:2025/5/26 17:03
77
@desc:
88
"""
9+
import hashlib
910
import re
1011
from typing import Dict
1112

@@ -16,7 +17,8 @@
1617
from django.utils.translation import gettext_lazy as _
1718
from rest_framework import serializers
1819

19-
from application.models.application import Application, ApplicationTypeChoices
20+
from application.models.application import Application, ApplicationTypeChoices, ApplicationKnowledgeMapping
21+
from application.models.application_access_token import ApplicationAccessToken
2022
from common.exception.app_exception import AppApiException
2123
from knowledge.models import Knowledge
2224
from models_provider.models import Model
@@ -94,6 +96,11 @@ class ModelSettingSerializer(serializers.Serializer):
9496

9597

9698
class ApplicationCreateSerializer(serializers.Serializer):
99+
class ApplicationResponse(serializers.ModelSerializer):
100+
class Meta:
101+
model = Application
102+
fields = "__all__"
103+
97104
class WorkflowRequest(serializers.Serializer):
98105
name = serializers.CharField(required=True, max_length=64, min_length=1,
99106
label=_("Application Name"))
@@ -105,7 +112,7 @@ class WorkflowRequest(serializers.Serializer):
105112
label=_("Opening remarks"))
106113

107114
@staticmethod
108-
def to_application_model(user_id: str, application: Dict):
115+
def to_application_model(user_id: str, workspace_id: str, application: Dict):
109116
default_workflow = application.get('work_flow')
110117
for node in default_workflow.get('nodes'):
111118
if node.get('id') == 'base-node':
@@ -115,6 +122,7 @@ def to_application_model(user_id: str, application: Dict):
115122
return Application(id=uuid.uuid7(),
116123
name=application.get('name'),
117124
desc=application.get('desc'),
125+
workspace_id=workspace_id,
118126
prologue="",
119127
dialogue_number=0,
120128
user_id=user_id, model_id=None,
@@ -176,7 +184,70 @@ def is_valid(self, *, user_id=None, raise_exception=False):
176184
ModelKnowledgeAssociation(data={'user_id': user_id, 'model_id': self.data.get('model_id'),
177185
'knowledge_id_list': self.data.get('knowledge_id_list')}).is_valid()
178186

187+
@staticmethod
188+
def to_application_model(user_id: str, application: Dict):
189+
return Application(id=uuid.uuid1(), name=application.get('name'), desc=application.get('desc'),
190+
prologue=application.get('prologue'),
191+
dialogue_number=application.get('dialogue_number', 0),
192+
user_id=user_id, model_id=application.get('model_id'),
193+
dataset_setting=application.get('dataset_setting'),
194+
model_setting=application.get('model_setting'),
195+
problem_optimization=application.get('problem_optimization'),
196+
type=ApplicationTypeChoices.SIMPLE,
197+
model_params_setting=application.get('model_params_setting', {}),
198+
problem_optimization_prompt=application.get('problem_optimization_prompt', None),
199+
stt_model_enable=application.get('stt_model_enable', False),
200+
stt_model_id=application.get('stt_model', None),
201+
tts_model_id=application.get('tts_model', None),
202+
tts_model_enable=application.get('tts_model_enable', False),
203+
tts_model_params_setting=application.get('tts_model_params_setting', {}),
204+
tts_type=application.get('tts_type', None),
205+
file_upload_enable=application.get('file_upload_enable', False),
206+
file_upload_setting=application.get('file_upload_setting', {}),
207+
work_flow={}
208+
)
209+
179210

180211
class ApplicationSerializer(serializers.Serializer):
181-
def insert(self):
182-
pass
212+
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
213+
user_id = serializers.UUIDField(required=True, label=_("User ID"))
214+
215+
def insert(self, instance: Dict, with_valid=True):
216+
application_type = instance.get('type')
217+
if 'WORK_FLOW' == application_type:
218+
return self.insert_workflow(instance)
219+
else:
220+
return self.insert_simple(instance)
221+
222+
def insert_workflow(self, instance: Dict):
223+
self.is_valid(raise_exception=True)
224+
user_id = self.data.get('user_id')
225+
ApplicationCreateSerializer.WorkflowRequest(data=instance).is_valid(raise_exception=True)
226+
application_model = ApplicationCreateSerializer.WorkflowRequest.to_application_model(user_id, instance)
227+
application_model.save()
228+
# 插入认证信息
229+
ApplicationAccessToken(application_id=application_model.id,
230+
access_token=hashlib.md5(str(uuid.uuid1()).encode()).hexdigest()[8:24]).save()
231+
return ApplicationCreateSerializer.ApplicationResponse(application_model).data
232+
233+
@staticmethod
234+
def to_application_knowledge_mapping(application_id: str, dataset_id: str):
235+
return ApplicationKnowledgeMapping(id=uuid.uuid1(), application_id=application_id, dataset_id=dataset_id)
236+
237+
def insert_simple(self, instance: Dict):
238+
self.is_valid(raise_exception=True)
239+
user_id = self.data.get('user_id')
240+
ApplicationCreateSerializer.SimplateRequest(data=instance).is_valid(user_id=user_id, raise_exception=True)
241+
application_model = ApplicationCreateSerializer.SimplateRequest.to_application_model(user_id, instance)
242+
dataset_id_list = instance.get('knowledge_id_list', [])
243+
application_knowledge_mapping_model_list = [
244+
self.to_application_knowledge_mapping(application_model.id, dataset_id) for
245+
dataset_id in dataset_id_list]
246+
# 插入应用
247+
application_model.save()
248+
# 插入认证信息
249+
ApplicationAccessToken(application_id=application_model.id,
250+
access_token=hashlib.md5(str(uuid.uuid1()).encode()).hexdigest()[8:24]).save()
251+
# 插入关联数据
252+
QuerySet(ApplicationKnowledgeMapping).bulk_create(application_knowledge_mapping_model_list)
253+
return ApplicationCreateSerializer.ApplicationResponse(application_model).data

apps/application/urls.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
app_name = 'application'
66

77
urlpatterns = [
8-
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key', views.ApplicationKey.as_view()),
9-
]
8+
path('workspace/<str:workspace_id>/application', views.Application.as_view(), name='application'),
9+
path('workspace/<str:workspace_id>/application/<str:application_id>/application_key',
10+
views.ApplicationKey.as_view())]

apps/application/views/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@
66
@date:2025/5/9 18:51
77
@desc:
88
"""
9-
from .application_api_key import *
9+
from .application_api_key import *
10+
from .application import *

apps/application/views/application.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
from application.api.application_api import ApplicationCreateAPI
1515
from application.serializers.application import ApplicationSerializer
1616
from common import result
17+
from common.auth import TokenAuth
1718

1819

1920
class Application(APIView):
21+
authentication_classes = [TokenAuth]
2022

2123
@extend_schema(
2224
methods=['POST'],
@@ -28,5 +30,6 @@ class Application(APIView):
2830
responses=ApplicationCreateAPI.get_response(),
2931
tags=[_('Application')] # type: ignore
3032
)
31-
def post(self, request: Request):
32-
return result.success(ApplicationSerializer.insert(request.data))
33+
def post(self, request: Request, workspace_id: str):
34+
return result.success(
35+
ApplicationSerializer(data={'workspace_id': workspace_id, 'user_id': request.user.id}).insert(request.data))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Generated by Django 5.2 on 2025-05-27 03:05
2+
3+
import knowledge.models.knowledge
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('knowledge', '0006_knowledgefolder_desc_and_more'),
11+
]
12+
13+
operations = [
14+
migrations.AlterField(
15+
model_name='document',
16+
name='status',
17+
field=models.CharField(default=knowledge.models.knowledge.Status.__str__, max_length=20, verbose_name='状态'),
18+
),
19+
migrations.AlterField(
20+
model_name='paragraph',
21+
name='status',
22+
field=models.CharField(default=knowledge.models.knowledge.Status.__str__, max_length=20, verbose_name='状态'),
23+
),
24+
migrations.DeleteModel(
25+
name='ApplicationKnowledgeMapping',
26+
),
27+
]

apps/knowledge/models/knowledge.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -202,15 +202,6 @@ class Meta:
202202
db_table = "problem_paragraph_mapping"
203203

204204

205-
class ApplicationKnowledgeMapping(AppModelMixin):
206-
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
207-
# application = models.ForeignKey(Application, on_delete=models.DO_NOTHING)
208-
knowledge = models.ForeignKey(Knowledge, on_delete=models.DO_NOTHING)
209-
210-
class Meta:
211-
db_table = "application_knowledge_mapping"
212-
213-
214205
class SourceType(models.IntegerChoices):
215206
"""订单类型"""
216207
PROBLEM = 0, '问题'

apps/knowledge/serializers/knowledge.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from django.utils.translation import gettext_lazy as _
1515
from rest_framework import serializers
1616

17+
from application.models import ApplicationKnowledgeMapping
1718
from common.config.embedding_config import VectorStore
1819
from common.db.search import native_search, get_dynamics_model, native_page_search
1920
from common.db.sql_execute import select_list
@@ -23,7 +24,7 @@
2324
from common.utils.fork import Fork, ChildLink
2425
from common.utils.split_model import get_split_model
2526
from knowledge.models import Knowledge, KnowledgeScope, KnowledgeType, Document, Paragraph, Problem, \
26-
ProblemParagraphMapping, ApplicationKnowledgeMapping, TaskType, State, SearchMode, KnowledgeFolder
27+
ProblemParagraphMapping, TaskType, State, SearchMode, KnowledgeFolder
2728
from knowledge.serializers.common import ProblemParagraphManage, get_embedding_model_id_by_knowledge_id, MetaSerializer, \
2829
GenerateRelatedSerializer, get_embedding_model_by_knowledge_id, list_paragraph
2930
from knowledge.serializers.document import DocumentSerializers

apps/maxkb/settings/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
'drf_spectacular_sidecar',
4141
'users.apps.UsersConfig',
4242
'tools.apps.ToolConfig',
43-
'knowledge.apps.KnowledgeConfig',
43+
'knowledge',
4444
'common',
4545
'system_manage',
4646
'models_provider',

apps/maxkb/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
path("api/", include("folders.urls")),
2828
path("api/", include("knowledge.urls")),
2929
path("api/", include("system_manage.urls")),
30+
path("api/", include("application.urls"))
3031
]
3132
urlpatterns += [
3233
path('schema/', SpectacularAPIView.as_view(), name='schema'), # schema的配置文件的路由,下面两个ui也是根据这个配置文件来生成的

0 commit comments

Comments
 (0)