diff --git a/apps/application/api/application_version.py b/apps/application/api/application_version.py new file mode 100644 index 00000000000..e1f4a3d3bd1 --- /dev/null +++ b/apps/application/api/application_version.py @@ -0,0 +1,96 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: application_version.py + @date:2025/6/4 17:33 + @desc: +""" +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import OpenApiParameter + +from application.serializers.application_version import ApplicationVersionModelSerializer +from common.mixins.api_mixin import APIMixin +from common.result import ResultSerializer, PageDataResponse, ResultPageSerializer + + +class ApplicationListVersionResult(ResultSerializer): + def get_data(self): + return ApplicationVersionModelSerializer(many=True) + + +class ApplicationPageVersionResult(ResultPageSerializer): + def get_data(self): + return ApplicationVersionModelSerializer(many=True) + + +class ApplicationWorkflowVersionResult(ResultSerializer): + def get_data(self): + return ApplicationVersionModelSerializer() + + +class ApplicationVersionAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True, + ), + OpenApiParameter( + name="application_id", + description="application ID", + type=OpenApiTypes.STR, + location='path', + required=True, + ) + ] + + +class ApplicationVersionOperateAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="work_flow_version_id", + description="工作流版本id", + type=OpenApiTypes.STR, + location='path', + required=True, + ) + , *ApplicationVersionAPI.get_parameters() + ] + + @staticmethod + def get_response(): + return ApplicationWorkflowVersionResult + + +class ApplicationVersionListAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="name", + description="Version Name", + type=OpenApiTypes.STR, + required=False, + ) + , *ApplicationVersionAPI.get_parameters()] + + @staticmethod + def get_response(): + return ApplicationListVersionResult + + +class ApplicationVersionPageAPI(APIMixin): + @staticmethod + def get_parameters(): + return ApplicationVersionListAPI.get_parameters() + + @staticmethod + def get_response(): + return ApplicationPageVersionResult diff --git a/apps/application/migrations/0002_chat_chatrecord_workflowversion_and_more.py b/apps/application/migrations/0002_chat_chatrecord_workflowversion_and_more.py new file mode 100644 index 00000000000..0d92af56fab --- /dev/null +++ b/apps/application/migrations/0002_chat_chatrecord_workflowversion_and_more.py @@ -0,0 +1,92 @@ +# Generated by Django 5.2 on 2025-06-04 11:57 + +import application.models.application_chat +import common.encoder.encoder +import django.contrib.postgres.fields +import django.db.models.deletion +import uuid +import uuid_utils.compat +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('application', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Chat', + 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.UUID('01973acd-fe4c-7fd1-94a8-f7cd668de562'), editable=False, primary_key=True, serialize=False, verbose_name='主键id')), + ('abstract', models.CharField(max_length=1024, verbose_name='摘要')), + ('asker', models.JSONField(default=application.models.application_chat.default_asker, encoder=common.encoder.encoder.SystemEncoder, verbose_name='访问者')), + ('client_id', models.UUIDField(default=None, null=True, verbose_name='客户端id')), + ('is_deleted', models.BooleanField(default=False, verbose_name='')), + ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='application.application')), + ], + options={ + 'db_table': 'application_chat', + }, + ), + migrations.CreateModel( + name='ChatRecord', + 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.uuid1, editable=False, primary_key=True, serialize=False, verbose_name='主键id')), + ('vote_status', models.CharField(choices=[('-1', '未投票'), ('0', '赞同'), ('1', '反对')], default='-1', max_length=10, verbose_name='投票')), + ('problem_text', models.CharField(max_length=10240, verbose_name='问题')), + ('answer_text', models.CharField(max_length=40960, verbose_name='答案')), + ('answer_text_list', django.contrib.postgres.fields.ArrayField(base_field=models.JSONField(), default=list, size=None, verbose_name='改进标注列表')), + ('message_tokens', models.IntegerField(default=0, verbose_name='请求token数量')), + ('answer_tokens', models.IntegerField(default=0, verbose_name='响应token数量')), + ('const', models.IntegerField(default=0, verbose_name='总费用')), + ('details', models.JSONField(default=dict, encoder=common.encoder.encoder.SystemEncoder, verbose_name='对话详情')), + ('improve_paragraph_id_list', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(blank=True), default=list, size=None, verbose_name='改进标注列表')), + ('run_time', models.FloatField(default=0, verbose_name='运行时长')), + ('index', models.IntegerField(verbose_name='对话下标')), + ('chat', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='application.chat')), + ], + options={ + 'db_table': 'application_chat_record', + }, + ), + migrations.CreateModel( + name='WorkFlowVersion', + 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.uuid1, editable=False, primary_key=True, serialize=False, verbose_name='主键id')), + ('workspace_id', models.CharField(db_index=True, default='default', max_length=64, verbose_name='工作空间id')), + ('name', models.CharField(default='', max_length=128, verbose_name='版本名称')), + ('publish_user_id', models.UUIDField(default=None, null=True, verbose_name='发布者id')), + ('publish_user_name', models.CharField(default='', max_length=128, verbose_name='发布者名称')), + ('work_flow', models.JSONField(default=dict, verbose_name='工作流数据')), + ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='application.application')), + ], + options={ + 'db_table': 'application_work_flow_version', + }, + ), + migrations.CreateModel( + name='ApplicationPublicAccessClient', + 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.uuid1, editable=False, primary_key=True, serialize=False, verbose_name='主键id')), + ('client_id', models.UUIDField(default=uuid.uuid1, verbose_name='公共访问链接客户端id')), + ('client_type', models.CharField(max_length=64, verbose_name='客户端类型')), + ('access_num', models.IntegerField(default=0, verbose_name='访问总次数次数')), + ('intraday_access_num', models.IntegerField(default=0, verbose_name='当日访问次数')), + ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='application.application', verbose_name='应用id')), + ], + options={ + 'db_table': 'application_public_access_client', + 'indexes': [models.Index(fields=['application_id', 'client_id'], name='application_applica_8aaf45_idx')], + }, + ), + ] diff --git a/apps/application/models/application_chat.py b/apps/application/models/application_chat.py index 1c3184e135a..428658f240d 100644 --- a/apps/application/models/application_chat.py +++ b/apps/application/models/application_chat.py @@ -40,7 +40,6 @@ class VoteChoices(models.TextChoices): TRAMPLE = "1", '反对' - class ChatRecord(AppModelMixin): """ 对话日志 详情 diff --git a/apps/application/serializers/application_version.py b/apps/application/serializers/application_version.py index 569542f75df..3080491cb9c 100644 --- a/apps/application/serializers/application_version.py +++ b/apps/application/serializers/application_version.py @@ -9,8 +9,8 @@ from typing import Dict from django.db.models import QuerySet -from rest_framework import serializers from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers from application.models import WorkFlowVersion from common.db.search import page_search @@ -26,7 +26,8 @@ class ApplicationVersionQuerySerializer(serializers.Serializer): class ApplicationVersionModelSerializer(serializers.ModelSerializer): class Meta: model = WorkFlowVersion - fields = ['id', 'name', 'application_id', 'work_flow', 'publish_user_id', 'publish_user_name', 'create_time', + fields = ['id', 'name', 'workspace_id', 'application_id', 'work_flow', 'publish_user_id', 'publish_user_name', + 'create_time', 'update_time'] @@ -36,26 +37,30 @@ class ApplicationVersionEditSerializer(serializers.Serializer): class ApplicationVersionSerializer(serializers.Serializer): + workspace_id = serializers.UUIDField(required=False, label=_("Workspace ID")) + class Query(serializers.Serializer): - def get_query_set(self): - query_set = QuerySet(WorkFlowVersion).filter(application_id=self.data.get('application_id')) - if 'name' in self.data and self.data.get('name') is not None: - query_set = query_set.filter(name__contains=self.data.get('name')) + def get_query_set(self, query): + query_set = QuerySet(WorkFlowVersion).filter(application_id=query.get('application_id')) + if 'name' in query and query.get('name') is not None: + query_set = query_set.filter(name__contains=query.get('name')) + if 'workspace_id' in self.data and self.data.get('workspace_id') is not None: + query_set = query_set.filter(workspace_id=self.data.get('workspace_id').get('name')) return query_set.order_by("-create_time") - def list(self, instance, with_valid=True): + def list(self, query, with_valid=True): if with_valid: self.is_valid(raise_exception=True) - ApplicationVersionQuerySerializer(data=instance).is_valid(raise_exception=True) - query_set = self.get_query_set() + ApplicationVersionQuerySerializer(data=query).is_valid(raise_exception=True) + query_set = self.get_query_set(query) return [ApplicationVersionModelSerializer(v).data for v in query_set] - def page(self, current_page, page_size, with_valid=True): + def page(self, query, current_page, page_size, with_valid=True): if with_valid: self.is_valid(raise_exception=True) return page_search(current_page, page_size, - self.get_query_set(), + self.get_query_set(query), post_records_handler=lambda v: ApplicationVersionModelSerializer(v).data) class Operate(serializers.Serializer): diff --git a/apps/application/urls.py b/apps/application/urls.py index 320f90bb4b3..a57ac301d3e 100644 --- a/apps/application/urls.py +++ b/apps/application/urls.py @@ -14,4 +14,13 @@ path('workspace//application//application_key', views.ApplicationKey.as_view()), path('workspace//application//export', views.Application.Export.as_view()), + + path('workspace//application//work_flow_version', + views.ApplicationVersionView.as_view()), + path( + 'workspace//application//work_flow_version//', + views.ApplicationVersionView.Page.as_view()), + path( + 'workspace//application//work_flow_version/', + views.ApplicationVersionView.Operate.as_view()), ] diff --git a/apps/application/views/__init__.py b/apps/application/views/__init__.py index 478281e5086..36140c05487 100644 --- a/apps/application/views/__init__.py +++ b/apps/application/views/__init__.py @@ -8,3 +8,4 @@ """ from .application_api_key import * from .application import * +from .application_version import * diff --git a/apps/application/views/application.py b/apps/application/views/application.py index 41d053b49fa..4d8f17e4a6d 100644 --- a/apps/application/views/application.py +++ b/apps/application/views/application.py @@ -99,6 +99,7 @@ class Export(APIView): summary=_('Export application'), operation_id=_('Export application'), # type: ignore parameters=ApplicationExportAPI.get_parameters(), + request=None, responses=ApplicationExportAPI.get_response(), tags=[_('Application')] # type: ignore ) diff --git a/apps/application/views/application_version.py b/apps/application/views/application_version.py index 0bb6b705b0e..f1904ea4c38 100644 --- a/apps/application/views/application_version.py +++ b/apps/application/views/application_version.py @@ -11,6 +11,8 @@ from rest_framework.request import Request from rest_framework.views import APIView +from application.api.application_version import ApplicationVersionListAPI, ApplicationVersionPageAPI, \ + ApplicationVersionAPI, ApplicationVersionOperateAPI from application.serializers.application_version import ApplicationVersionSerializer from common import result from common.auth import TokenAuth @@ -22,42 +24,39 @@ class ApplicationVersionView(APIView): authentication_classes = [TokenAuth] @extend_schema( - methods=['POST'], - description=_("Get the application list"), - summary=_("Get the application list"), - operation_id=_("Get the application list"), # type: ignore - # parameters=ApplicationCreateAPI.get_parameters(), - # request=ApplicationCreateAPI.get_request(), - # responses=ApplicationCreateAPI.get_response(), + methods=['GET'], + description=_("Get the application version list"), + summary=_("Get the application version list"), + operation_id=_("Get the application version list"), # type: ignore + parameters=ApplicationVersionListAPI.get_parameters(), + responses=ApplicationVersionListAPI.get_response(), tags=[_('Application/Version')] # type: ignore ) - @has_permissions(PermissionConstants.APPLICATION_READ) + @has_permissions(PermissionConstants.APPLICATION_READ.get_workspace_application_permission()) def get(self, request: Request, workspace_id, application_id: str): return result.success( ApplicationVersionSerializer.Query( - data={'name': request.query_params.get('name'), 'user_id': request.user.id, - 'application_id': application_id}).list(request.data)) + data={'workspace_id': workspace_id}).list( + {'name': request.query_params.get("name"), 'application_id': application_id})) class Page(APIView): authentication_classes = [TokenAuth] @extend_schema( - methods=['GET' - ''], + methods=['GET'], description=_("Get the list of application versions by page"), summary=_("Get the list of application versions by page"), operation_id=_("Get the list of application versions by page"), # type: ignore - # parameters=ApplicationCreateAPI.get_parameters(), - # request=ApplicationCreateAPI.get_request(), - # responses=ApplicationCreateAPI.get_response(), + parameters=ApplicationVersionPageAPI.get_parameters(), + responses=ApplicationVersionPageAPI.get_response(), tags=[_('Application/Version')] # type: ignore ) - @has_permissions(PermissionConstants.APPLICATION_READ) - def get(self, request: Request, application_id: str, current_page: int, page_size: int): + @has_permissions(PermissionConstants.APPLICATION_READ.get_workspace_application_permission()) + def get(self, request: Request, workspace_id: str, application_id: str, current_page: int, page_size: int): return result.success( ApplicationVersionSerializer.Query( - data={'name': request.query_params.get('name'), 'user_id': request.user, - 'application_id': application_id}).page( + data={'workspace_id': workspace_id}).page( + {'name': request.query_params.get("name"), 'application_id': application_id}, current_page, page_size)) class Operate(APIView): @@ -68,13 +67,12 @@ class Operate(APIView): description=_("Get application version details"), summary=_("Get application version details"), operation_id=_("Get application version details"), # type: ignore - # parameters=ApplicationCreateAPI.get_parameters(), - # request=ApplicationCreateAPI.get_request(), - # responses=ApplicationCreateAPI.get_response(), + parameters=ApplicationVersionOperateAPI.get_parameters(), + responses=ApplicationVersionOperateAPI.get_response(), tags=[_('Application/Version')] # type: ignore ) - @has_permissions(PermissionConstants.APPLICATION_READ) - def get(self, request: Request, application_id: str, work_flow_version_id: str): + @has_permissions(PermissionConstants.APPLICATION_EDIT.get_workspace_application_permission()) + def get(self, request: Request, workspace_id: str, application_id: str, work_flow_version_id: str): return result.success( ApplicationVersionSerializer.Operate( data={'user_id': request.user, @@ -85,12 +83,12 @@ def get(self, request: Request, application_id: str, work_flow_version_id: str): description=_("Modify application version information"), summary=_("Modify application version information"), operation_id=_("Modify application version information"), # type: ignore - # parameters=ApplicationCreateAPI.get_parameters(), - # request=ApplicationCreateAPI.get_request(), - # responses=ApplicationCreateAPI.get_response(), + parameters=ApplicationVersionOperateAPI.get_parameters(), + request=None, + responses=ApplicationVersionOperateAPI.get_response(), tags=[_('Application/Version')] # type: ignore ) - def put(self, request: Request, application_id: str, work_flow_version_id: str): + def put(self, request: Request, workspace_id: str, application_id: str, work_flow_version_id: str): return result.success( ApplicationVersionSerializer.Operate( data={'application_id': application_id, 'work_flow_version_id': work_flow_version_id, diff --git a/apps/common/constants/permission_constants.py b/apps/common/constants/permission_constants.py index f9780293450..bb296b92fe1 100644 --- a/apps/common/constants/permission_constants.py +++ b/apps/common/constants/permission_constants.py @@ -729,7 +729,7 @@ def get_permission_list_by_resource_group(resource_group: ResourcePermissionGrou """ 根据资源组获取权限 """ - return [PermissionConstants[k] for k in PermissionConstants.__members__ if + return [PermissionConstants[k].value for k in PermissionConstants.__members__ if PermissionConstants[k].value.resource_permission_group_list.__contains__(resource_group)] diff --git a/apps/knowledge/migrations/0002_alter_document_status_alter_file_source_type_and_more.py b/apps/knowledge/migrations/0002_alter_document_status_alter_file_source_type_and_more.py new file mode 100644 index 00000000000..cde467cfcdc --- /dev/null +++ b/apps/knowledge/migrations/0002_alter_document_status_alter_file_source_type_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.2 on 2025-06-04 11:57 + +import knowledge.models.knowledge +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('knowledge', '0001_initial'), + ] + + 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='file', + name='source_type', + field=models.CharField(choices=[('KNOWLEDGE', 'Knowledge'), ('APPLICATION', 'Application'), ('TOOL', 'Tool'), ('DOCUMENT', 'Document'), ('TEMPORARY_30_MINUTE', 'Temporary 30 Minute'), ('TEMPORARY_100_MINUTE', 'Temporary 120 Minute'), ('TEMPORARY_1_DAY', 'Temporary 1 Day')], default='TEMPORARY_100_MINUTE', verbose_name='资源类型'), + ), + migrations.AlterField( + model_name='paragraph', + name='status', + field=models.CharField(default=knowledge.models.knowledge.Status.__str__, max_length=20, verbose_name='状态'), + ), + ]