-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat: application save #3150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: application save #3150
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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', | ||
| }, | ||
| ), | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,3 +6,5 @@ | |
| @date:2025/5/7 15:14 | ||
| @desc: | ||
| """ | ||
| from .application import * | ||
| from .application_access_token import * | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # coding=utf-8 | ||
| """ | ||
| @project: MaxKB | ||
| @Author:虎虎 | ||
| @file: application_access_token.py | ||
| @date:2025/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" | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This Django model
These suggestions should help improve the robustness and security of your system while maintaining performance efficiency. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| @date:2025/5/26 17:03 | ||
| @desc: | ||
| """ | ||
| import hashlib | ||
| import re | ||
| from typing import Dict | ||
|
|
||
|
|
@@ -16,7 +17,8 @@ | |
| 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 WorkflowRequest(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 @@ def to_application_model(user_id: str, application: Dict): | |
| 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 @@ def is_valid(self, *, user_id=None, raise_exception=False): | |
| 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 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code contains several improvements and optimizations that can be applied:
Here’s a revised version of the code with these changes: import hashlib
import uuid
import re
from collections import OrderedDict
from typing import Dict
from rest_framework import serializers
from django.utils.translation import gettext_lazy as _
from project_manager.models.project import Project
from project_manager.serializers.common_serializer import BaseModelSerializerMixin, \
BaseModelViewSetMixin
from project_manager.models.project_user import ProjectUserRoleChoices
from project_common.models.project_permission import has_permission_to_edit_project
from django.db import transaction
from common.exception.app_exception import AppApiException
from knowledge_management.serializers.knowledge import KnowledgeListSerializer
from application.models.application import Application, ApplicationTypeChoices, ApplicationKnowledgeMapping
from application.models.application_access_token import ApplicationAccessToken
from application.serializers.application_knowledge_mapping import ApplicationKnowledgeMappingSerializer
class ModelSettingSerializer(serializers.Serializer):
pass
class SimlateProjectSettingsSerializer(serializers.Serializer):
pass
class ApplicationCreateSerializer(BaseModelSerializerMixin, BaseModelViewSetMixin):
class Meta:
model = Application
fields = [
"name",
"description",
"prologue",
"dialogueNumber",
"user_id",
"model_id",
"dataset_setting",
"model_setting",
"problem_optimization",
"type",
"model_params_setting",
"problem_optimization_prompt",
"stt_model_enable",
"stt_model_id",
"tts_model_id",
"tts_model_enabled",
"tts_model_params_setting",
"tts_type",
"file_upload_enable",
"file_upload_setting",
"workFlow",
]
class WorkflowRequest(serializers.Serializer):
name = serializers.CharField(required=True, max_length=64, min_length=1,
label=_("Application Name"))
desc = serializers.CharField(max_length=255, required=False,
help_text=_("Opening remarks"),
label=_("Description"))
@staticmethod
def validate_stt_or_tts(model: Dict[str]):
"""Check if STT or TTS is disabled."""
tts_enabled = model.get("tts_model_enabled", False)
stt_enabled = model.get("stt_model_enabled", False)
if not stt_enabled and not tts_enabled:
raise serializers.ValidationError({"stt_model_enabled": ["Both STT and TTS must be either enabled"], })
return model
def save(self):
user_uuid: str = self.context["view"].request.user.id
workspace_uuid: str = self.request.args.get('workspace_id')
simlate_settings = SimlateProjectSettingsSerializer(data=self.validated_data).validated_data
with transaction.atomic():
app_instance = self.Meta.model.objects.create(
name=simulate_settings['name'],
description=simulate_settings['desc'] if simulate_settings.get('desc') else None,
prologue='Hello',
dialogue_number=0,
user_id=user_uuid,
model_id=None if simulate_settings['model_id'] is '':
Project.objects.first().default_model
else:
Model.find_by_id(simulate_settings['model_id']),
dataset_setting=json.loads(simulate_settings['dataset_setting']) if simulate_settings.get('dataset_setting') else {},
model_setting=json.loads(simulate_settings['model_setting']) if simulate_settings.get('model_setting') else {},
problem_optimization=json.loads(simulate_settings['problemOptimization']) if \
simulate_settings.get('problemOptimization') else {},
type=UserRoleChoices.WORKFLOW_PROJECTS_MANAGER.value
)
# Insert authentication information
access_token_object = ApplicationAccessToken(
application_id=app_instance.id,
access_token=hashlib.md5(str(uuid.uuid1()).encode()).hexdigest()[8:24]
).save()
class SimpleTemplateRequest(serializers.ModelSerializer):
class Meta:
model = Application
exclude = ['id']
def __init__(self, *args, **kwargs):
super().__init__(*args,
queryset=Application.objects.all(),
context={
"has_change_perm_for_projects": has_permission_to_edit_project(*args, **kwargs)
},
many=isinstance(kwargs.get("queryset"), list),
**kwargs)
class ApplicationSerializer(serializers.ModelSerializer):
user_id = serializers.UUIDField(required=True, label=_('User ID'))
class Meta:
model = Application
fields = [
'id',
'created_at',
'updated_at',
('name', _('App Name')),
('description', _('Desc')),
("dialogue_number", _("Dialogue Number")),
'status',
'prologue'
]
def get_all_applications(self, request):
query_set = self.apply_filters(request=request, qs=Application.objects.all())
return [ApplicationSerializers(app).data for app in query_set]Key Changes:
These adjustments improve the robustness, readability, and efficiency of the provided code. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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', | ||
| ), | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The provided code seems correct but has some minor improvements that can be made for better readability, maintainability, and future-proofing. Here are the suggested adjustments:
Use
auto_createdinstead ofauto_now_add: Thecreate_timeshould useauto_created=Trueto automatically set the field when creating an instance.Ensure
max_lengthvalues are consistent: Adjust any string fields likeaccess_token,language, andshow_sourceto have appropriate maximum lengths if necessary.Use
blank=Trueandnull=Trueappropriately: Ensure all optional fields (white_list) are correctly marked withblank=True.Consider adding verbose names more specifically: You might want to add additional context to some fields' names for clarity.
Here's the corrected version of the
CreateModeloperations section:Summary:
create_timeto useauto_created=True.blank=Trueandnull=Truewhere appropriate to handle optional fields gracefully.verbose_namemore precisely where applicable to add context.These changes ensure that the model is better organized and easier to understand while maintaining its functionality.