Skip to content

Commit 36998c8

Browse files
authoredOct 23, 2024
feat: 工作流版本管理 (#1386)
1 parent 0c6e4b7 commit 36998c8

File tree

18 files changed

+653
-26
lines changed

18 files changed

+653
-26
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 4.2.15 on 2024-10-16 15:17
2+
3+
from django.db import migrations, models
4+
5+
sql = """
6+
UPDATE "public".application_work_flow_version
7+
SET "name" = TO_CHAR(create_time, 'YYYY-MM-DD HH24:MI:SS');
8+
"""
9+
10+
11+
class Migration(migrations.Migration):
12+
dependencies = [
13+
('application', '0017_application_tts_model_params_setting'),
14+
]
15+
16+
operations = [
17+
migrations.AddField(
18+
model_name='workflowversion',
19+
name='name',
20+
field=models.CharField(default='', max_length=128, verbose_name='版本名称'),
21+
),
22+
migrations.RunSQL(sql),
23+
migrations.AddField(
24+
model_name='workflowversion',
25+
name='publish_user_id',
26+
field=models.UUIDField(default=None, null=True, verbose_name='发布者id'),
27+
),
28+
migrations.AddField(
29+
model_name='workflowversion',
30+
name='publish_user_name',
31+
field=models.CharField(default='', max_length=128, verbose_name='发布者名称'),
32+
),
33+
]

‎apps/application/models/application.py

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ class Meta:
8888
class WorkFlowVersion(AppModelMixin):
8989
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid1, editable=False, verbose_name="主键id")
9090
application = models.ForeignKey(Application, on_delete=models.CASCADE)
91+
name = models.CharField(verbose_name="版本名称", max_length=128, default="")
92+
publish_user_id = models.UUIDField(verbose_name="发布者id", max_length=128, default=None, null=True)
93+
publish_user_name = models.CharField(verbose_name="发布者名称", max_length=128, default="")
9194
work_flow = models.JSONField(verbose_name="工作流数据", default=dict)
9295

9396
class Meta:

‎apps/application/serializers/application_serializers.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@date:2023/11/7 10:02
77
@desc:
88
"""
9+
import datetime
910
import hashlib
1011
import json
1112
import os
@@ -50,6 +51,7 @@
5051
from setting.models_provider.tools import get_model_instance_by_model_user_id
5152
from setting.serializers.provider_serializers import ModelSerializer
5253
from smartdoc.conf import PROJECT_DIR
54+
from users.models import User
5355

5456
chat_cache = cache.caches['chat_cache']
5557

@@ -684,6 +686,8 @@ def delete(self, with_valid=True):
684686
def publish(self, instance, with_valid=True):
685687
if with_valid:
686688
self.is_valid()
689+
user_id = self.data.get('user_id')
690+
user = QuerySet(User).filter(id=user_id).first()
687691
application = QuerySet(Application).filter(id=self.data.get("application_id")).first()
688692
work_flow = instance.get('work_flow')
689693
if work_flow is None:
@@ -703,7 +707,10 @@ def publish(self, instance, with_valid=True):
703707
application.save()
704708
# 插入知识库关联关系
705709
self.save_application_mapping(application_dataset_id_list, dataset_id_list, application.id)
706-
work_flow_version = WorkFlowVersion(work_flow=work_flow, application=application)
710+
work_flow_version = WorkFlowVersion(work_flow=work_flow, application=application,
711+
name=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
712+
publish_user_id=user_id,
713+
publish_user_name=user.username)
707714
chat_cache.clear_by_application_id(str(application.id))
708715
work_flow_version.save()
709716
return True
@@ -1002,6 +1009,7 @@ def text_to_speech(self, text, with_valid=True):
10021009
if application.tts_model_enable:
10031010
model = get_model_instance_by_model_user_id(application.tts_model_id, application.user_id,
10041011
**application.tts_model_params_setting)
1012+
10051013
return model.text_to_speech(text)
10061014

10071015
def play_demo_text(self, form_data, with_valid=True):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎
5+
@file: application_version_serializers.py
6+
@date:2024/10/15 16:42
7+
@desc:
8+
"""
9+
from typing import Dict
10+
11+
from django.db.models import QuerySet
12+
from rest_framework import serializers
13+
14+
from application.models import WorkFlowVersion
15+
from common.db.search import page_search
16+
from common.exception.app_exception import AppApiException
17+
from common.util.field_message import ErrMessage
18+
19+
20+
class ApplicationVersionModelSerializer(serializers.ModelSerializer):
21+
class Meta:
22+
model = WorkFlowVersion
23+
fields = ['id', 'name', 'application_id', 'work_flow', 'publish_user_id', 'publish_user_name', 'create_time',
24+
'update_time']
25+
26+
27+
class ApplicationVersionEditSerializer(serializers.Serializer):
28+
name = serializers.CharField(required=False, max_length=128, allow_null=True, allow_blank=True,
29+
error_messages=ErrMessage.char("版本名称"))
30+
31+
32+
class ApplicationVersionSerializer(serializers.Serializer):
33+
class Query(serializers.Serializer):
34+
application_id = serializers.UUIDField(required=True, error_messages=ErrMessage.char("应用id"))
35+
name = serializers.CharField(required=False, allow_null=True, allow_blank=True,
36+
error_messages=ErrMessage.char("摘要"))
37+
38+
def get_query_set(self):
39+
query_set = QuerySet(WorkFlowVersion).filter(application_id=self.data.get('application_id'))
40+
if 'name' in self.data and self.data.get('name') is not None:
41+
query_set = query_set.filter(name__contains=self.data.get('name'))
42+
return query_set.order_by("-create_time")
43+
44+
def list(self, with_valid=True):
45+
if with_valid:
46+
self.is_valid(raise_exception=True)
47+
query_set = self.get_query_set()
48+
return [ApplicationVersionModelSerializer(v).data for v in query_set]
49+
50+
def page(self, current_page, page_size, with_valid=True):
51+
if with_valid:
52+
self.is_valid(raise_exception=True)
53+
return page_search(current_page, page_size,
54+
self.get_query_set(),
55+
post_records_handler=lambda v: ApplicationVersionModelSerializer(v).data)
56+
57+
class Operate(serializers.Serializer):
58+
application_id = serializers.UUIDField(required=True, error_messages=ErrMessage.char("应用id"))
59+
work_flow_version_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("工作流版本id"))
60+
61+
def one(self, with_valid=True):
62+
if with_valid:
63+
self.is_valid(raise_exception=True)
64+
work_flow_version = QuerySet(WorkFlowVersion).filter(application_id=self.data.get('application_id'),
65+
id=self.data.get('work_flow_version_id')).first()
66+
if work_flow_version is not None:
67+
return ApplicationVersionModelSerializer(work_flow_version).data
68+
else:
69+
raise AppApiException(500, '不存在的工作流版本')
70+
71+
def edit(self, instance: Dict, with_valid=True):
72+
if with_valid:
73+
self.is_valid(raise_exception=True)
74+
ApplicationVersionEditSerializer(data=instance).is_valid(raise_exception=True)
75+
work_flow_version = QuerySet(WorkFlowVersion).filter(application_id=self.data.get('application_id'),
76+
id=self.data.get('work_flow_version_id')).first()
77+
if work_flow_version is not None:
78+
name = instance.get('name', None)
79+
if name is not None and len(name) > 0:
80+
work_flow_version.name = name
81+
work_flow_version.save()
82+
return ApplicationVersionModelSerializer(work_flow_version).data
83+
else:
84+
raise AppApiException(500, '不存在的工作流版本')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎
5+
@file: application_version_api.py
6+
@date:2024/10/15 17:18
7+
@desc:
8+
"""
9+
from drf_yasg import openapi
10+
11+
from common.mixins.api_mixin import ApiMixin
12+
13+
14+
class ApplicationVersionApi(ApiMixin):
15+
@staticmethod
16+
def get_response_body_api():
17+
return openapi.Schema(
18+
type=openapi.TYPE_OBJECT,
19+
required=['id', 'name', 'work_flow', 'create_time', 'update_time'],
20+
properties={
21+
'id': openapi.Schema(type=openapi.TYPE_NUMBER, title="主键id",
22+
description="主键id"),
23+
'name': openapi.Schema(type=openapi.TYPE_NUMBER, title="版本名称",
24+
description="版本名称"),
25+
'work_flow': openapi.Schema(type=openapi.TYPE_STRING, title="工作流数据", description='工作流数据'),
26+
'create_time': openapi.Schema(type=openapi.TYPE_STRING, title="创建时间", description='创建时间'),
27+
'update_time': openapi.Schema(type=openapi.TYPE_STRING, title="修改时间", description='修改时间')
28+
}
29+
)
30+
31+
class Query(ApiMixin):
32+
@staticmethod
33+
def get_request_params_api():
34+
return [openapi.Parameter(name='application_id',
35+
in_=openapi.IN_PATH,
36+
type=openapi.TYPE_STRING,
37+
required=True,
38+
description='应用id'),
39+
openapi.Parameter(name='name',
40+
in_=openapi.IN_QUERY,
41+
type=openapi.TYPE_STRING,
42+
required=False,
43+
description='版本名称')]
44+
45+
class Operate(ApiMixin):
46+
@staticmethod
47+
def get_request_params_api():
48+
return [openapi.Parameter(name='application_id',
49+
in_=openapi.IN_PATH,
50+
type=openapi.TYPE_STRING,
51+
required=True,
52+
description='应用id'),
53+
openapi.Parameter(name='work_flow_version_id',
54+
in_=openapi.IN_PATH,
55+
type=openapi.TYPE_STRING,
56+
required=True,
57+
description='应用版本id'), ]
58+
59+
class Edit(ApiMixin):
60+
@staticmethod
61+
def get_request_body_api():
62+
return openapi.Schema(
63+
type=openapi.TYPE_OBJECT,
64+
required=[],
65+
properties={
66+
'name': openapi.Schema(type=openapi.TYPE_STRING, title="版本名称",
67+
description="版本名称")
68+
}
69+
)

‎apps/application/urls.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@
7070
name='application/audio'),
7171
path('application/<str:application_id>/text_to_speech', views.Application.TextToSpeech.as_view(),
7272
name='application/audio'),
73+
path('application/<str:application_id>/work_flow_version', views.ApplicationVersionView.as_view()),
74+
path('application/<str:application_id>/work_flow_version/<int:current_page>/<int:page_size>',
75+
views.ApplicationVersionView.Page.as_view()),
76+
path('application/<str:application_id>/work_flow_version/<str:work_flow_version_id>',
77+
views.ApplicationVersionView.Operate.as_view()),
7378
path('application/<str:application_id>/play_demo_text', views.Application.PlayDemoText.as_view(),
74-
name='application/audio'),
79+
name='application/audio')
7580

7681
]

‎apps/application/views/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
"""
99
from .application_views import *
1010
from .chat_views import *
11+
from .application_version_views import *
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎
5+
@file: application_version_views.py
6+
@date:2024/10/15 16:49
7+
@desc:
8+
"""
9+
from drf_yasg.utils import swagger_auto_schema
10+
from rest_framework.decorators import action
11+
from rest_framework.request import Request
12+
from rest_framework.views import APIView
13+
14+
from application.serializers.application_version_serializers import ApplicationVersionSerializer
15+
from application.swagger_api.application_version_api import ApplicationVersionApi
16+
from common.auth import has_permissions, TokenAuth
17+
from common.constants.permission_constants import PermissionConstants, CompareConstants, ViewPermission, RoleConstants, \
18+
Permission, Group, Operate
19+
from common.response import result
20+
21+
22+
class ApplicationVersionView(APIView):
23+
authentication_classes = [TokenAuth]
24+
25+
@action(methods=['GET'], detail=False)
26+
@swagger_auto_schema(operation_summary="获取应用列表",
27+
operation_id="获取应用列表",
28+
manual_parameters=ApplicationVersionApi.Query.get_request_params_api(),
29+
responses=result.get_api_array_response(ApplicationVersionApi.get_response_body_api()),
30+
tags=['应用/版本'])
31+
@has_permissions(PermissionConstants.APPLICATION_READ, compare=CompareConstants.AND)
32+
def get(self, request: Request, application_id: str):
33+
return result.success(
34+
ApplicationVersionSerializer.Query(
35+
data={'name': request.query_params.get('name'), 'user_id': request.user.id,
36+
'application_id': application_id}).list())
37+
38+
class Page(APIView):
39+
authentication_classes = [TokenAuth]
40+
41+
@action(methods=['GET'], detail=False)
42+
@swagger_auto_schema(operation_summary="分页获取应用版本列表",
43+
operation_id="分页获取应用版本列表",
44+
manual_parameters=result.get_page_request_params(
45+
ApplicationVersionApi.Query.get_request_params_api()),
46+
responses=result.get_page_api_response(ApplicationVersionApi.get_response_body_api()),
47+
tags=['应用/版本'])
48+
@has_permissions(PermissionConstants.APPLICATION_READ, compare=CompareConstants.AND)
49+
def get(self, request: Request, application_id: str, current_page: int, page_size: int):
50+
return result.success(
51+
ApplicationVersionSerializer.Query(
52+
data={'name': request.query_params.get('name'), 'user_id': request.user,
53+
'application_id': application_id}).page(
54+
current_page, page_size))
55+
56+
class Operate(APIView):
57+
authentication_classes = [TokenAuth]
58+
59+
@action(methods=['GET'], detail=False)
60+
@swagger_auto_schema(operation_summary="获取应用版本详情",
61+
operation_id="获取应用版本详情",
62+
manual_parameters=ApplicationVersionApi.Operate.get_request_params_api(),
63+
responses=result.get_api_response(ApplicationVersionApi.get_response_body_api()),
64+
tags=['应用/版本'])
65+
@has_permissions(PermissionConstants.APPLICATION_READ, compare=CompareConstants.AND)
66+
def get(self, request: Request, application_id: str, work_flow_version_id: str):
67+
return result.success(
68+
ApplicationVersionSerializer.Operate(
69+
data={'user_id': request.user,
70+
'application_id': application_id, 'work_flow_version_id': work_flow_version_id}).one())
71+
72+
@action(methods=['PUT'], detail=False)
73+
@swagger_auto_schema(operation_summary="修改应用版本信息",
74+
operation_id="修改应用版本信息",
75+
manual_parameters=ApplicationVersionApi.Operate.get_request_params_api(),
76+
request_body=ApplicationVersionApi.Edit.get_request_body_api(),
77+
responses=result.get_api_response(ApplicationVersionApi.get_response_body_api()),
78+
tags=['应用/版本'])
79+
@has_permissions(ViewPermission(
80+
[RoleConstants.ADMIN, RoleConstants.USER],
81+
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.MANAGE,
82+
dynamic_tag=keywords.get('application_id'))],
83+
compare=CompareConstants.AND))
84+
def put(self, request: Request, application_id: str, work_flow_version_id: str):
85+
return result.success(
86+
ApplicationVersionSerializer.Operate(
87+
data={'application_id': application_id, 'work_flow_version_id': work_flow_version_id,
88+
'user_id': request.user.id}).edit(
89+
request.data))

‎apps/setting/models_provider/impl/tencent_model_provider/model/embedding.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
from typing import Dict, List
23

34
from langchain_core.embeddings import Embeddings
@@ -34,3 +35,7 @@ def new_instance(model_type: str, model_name: str, model_credential: Dict[str, s
3435
secret_key=model_credential.get('SecretKey'),
3536
model_name=model_name,
3637
)
38+
39+
def _generate_auth_token(self):
40+
# Example method to generate an authentication token for the model API
41+
return f"{self.secret_id}:{self.secret_key}"

0 commit comments

Comments
 (0)