Skip to content

Commit c370e99

Browse files
authored
feat: Support openai dialogue (#3551)
1 parent 691d7ce commit c370e99

File tree

7 files changed

+180
-57
lines changed

7 files changed

+180
-57
lines changed

apps/chat/api/chat_authentication_api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,17 @@
1111
from drf_spectacular.types import OpenApiTypes
1212
from drf_spectacular.utils import OpenApiParameter
1313

14+
from chat.serializers.chat import OpenAIInstanceSerializer
1415
from chat.serializers.chat_authentication import AnonymousAuthenticationSerializer
1516
from common.mixins.api_mixin import APIMixin
1617

1718

19+
class OpenAIAPI(APIMixin):
20+
@staticmethod
21+
def get_request():
22+
return OpenAIInstanceSerializer
23+
24+
1825
class ChatAuthenticationAPI(APIMixin):
1926
@staticmethod
2027
def get_request():

apps/chat/serializers/chat.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"""
99

1010
from gettext import gettext
11-
from typing import List
11+
from typing import List, Dict
1212

1313
import uuid_utils.compat as uuid
1414
from django.db.models import QuerySet
@@ -31,6 +31,7 @@
3131
from application.serializers.common import ChatInfo
3232
from common.exception.app_exception import AppApiException, AppChatNumOutOfBoundsFailed, ChatException
3333
from common.handle.base_to_response import BaseToResponse
34+
from common.handle.impl.response.openai_to_response import OpenaiToResponse
3435
from common.handle.impl.response.system_to_response import SystemToResponse
3536
from common.utils.common import flat_map
3637
from knowledge.models import Document, Paragraph
@@ -111,6 +112,66 @@ def chat(self, instance: dict, base_to_response: BaseToResponse = SystemToRespon
111112
}).chat(instance, base_to_response)
112113

113114

115+
class OpenAIMessage(serializers.Serializer):
116+
content = serializers.CharField(required=True, label=_('content'))
117+
role = serializers.CharField(required=True, label=_('Role'))
118+
119+
120+
class OpenAIInstanceSerializer(serializers.Serializer):
121+
messages = serializers.ListField(child=OpenAIMessage())
122+
chat_id = serializers.UUIDField(required=False, label=_("Conversation ID"))
123+
re_chat = serializers.BooleanField(required=False, label=_("Regenerate"))
124+
stream = serializers.BooleanField(required=False, label=_("Streaming Output"))
125+
126+
127+
class OpenAIChatSerializer(serializers.Serializer):
128+
application_id = serializers.UUIDField(required=True, label=_("Application ID"))
129+
chat_user_id = serializers.CharField(required=True, label=_("Client id"))
130+
chat_user_type = serializers.CharField(required=True, label=_("Client Type"))
131+
132+
@staticmethod
133+
def get_message(instance):
134+
return instance.get('messages')[-1].get('content')
135+
136+
@staticmethod
137+
def generate_chat(chat_id, application_id, message, chat_user_id, chat_user_type):
138+
if chat_id is None:
139+
chat_id = str(uuid.uuid1())
140+
chat_info = ChatInfo(chat_id, chat_user_id, chat_user_type, [], [],
141+
application_id)
142+
chat_info.set_cache()
143+
return chat_id
144+
145+
def chat(self, instance: Dict, with_valid=True):
146+
if with_valid:
147+
self.is_valid(raise_exception=True)
148+
OpenAIInstanceSerializer(data=instance).is_valid(raise_exception=True)
149+
chat_id = instance.get('chat_id')
150+
message = self.get_message(instance)
151+
re_chat = instance.get('re_chat', False)
152+
stream = instance.get('stream', False)
153+
application_id = self.data.get('application_id')
154+
chat_user_id = self.data.get('chat_user_id')
155+
chat_user_type = self.data.get('chat_user_type')
156+
chat_id = self.generate_chat(chat_id, application_id, message, chat_user_id, chat_user_type)
157+
return ChatSerializers(
158+
data={
159+
'chat_id': chat_id,
160+
'chat_user_id': chat_user_id,
161+
'chat_user_type': chat_user_type,
162+
'application_id': application_id
163+
}
164+
).chat({'message': message,
165+
're_chat': re_chat,
166+
'stream': stream,
167+
'form_data': instance.get('form_data', {}),
168+
'image_list': instance.get('image_list', []),
169+
'document_list': instance.get('document_list', []),
170+
'audio_list': instance.get('audio_list', []),
171+
'other_list': instance.get('other_list', [])},
172+
base_to_response=OpenaiToResponse())
173+
174+
114175
class ChatSerializers(serializers.Serializer):
115176
chat_id = serializers.UUIDField(required=True, label=_("Conversation ID"))
116177
chat_user_id = serializers.CharField(required=True, label=_("Client id"))

apps/chat/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
path('text_to_speech', views.TextToSpeech.as_view()),
1515
path('speech_to_text', views.SpeechToText.as_view()),
1616
path('captcha', views.CaptchaView.as_view(), name='captcha'),
17+
path('<str:application_id>/chat/completions', views.OpenAIView.as_view(),
18+
name='application/chat_completions'),
1719
path('vote/chat/<str:chat_id>/chat_record/<str:chat_record_id>', views.VoteView.as_view(), name='vote'),
1820
path('historical_conversation', views.HistoricalConversationView.as_view(), name='historical_conversation'),
1921
path('historical_conversation/<str:chat_id>/record/<str:chat_record_id>',views.ChatRecordView.as_view(),name='conversation_details'),

apps/chat/views/chat.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414
from rest_framework.views import APIView
1515

1616
from application.api.application_api import SpeechToTextAPI, TextToSpeechAPI
17-
from application.serializers.application import ApplicationOperateSerializer
1817
from chat.api.chat_api import ChatAPI
19-
from chat.api.chat_authentication_api import ChatAuthenticationAPI, ChatAuthenticationProfileAPI, ChatOpenAPI
20-
from chat.serializers.chat import OpenChatSerializers, ChatSerializers, SpeechToTextSerializers, TextToSpeechSerializers
18+
from chat.api.chat_authentication_api import ChatAuthenticationAPI, ChatAuthenticationProfileAPI, ChatOpenAPI, OpenAIAPI
19+
from chat.serializers.chat import OpenChatSerializers, ChatSerializers, SpeechToTextSerializers, \
20+
TextToSpeechSerializers, OpenAIChatSerializer
2121
from chat.serializers.chat_authentication import AnonymousAuthenticationSerializer, ApplicationProfileSerializer, \
2222
AuthProfileSerializer
2323
from common.auth import TokenAuth
24-
from common.auth.authentication import has_permissions
2524
from common.constants.permission_constants import ChatAuth
2625
from common.exception.app_exception import AppAuthenticationFailed
2726
from common.result import result
@@ -31,6 +30,23 @@
3130
from users.serializers.login import CaptchaSerializer
3231

3332

33+
class OpenAIView(APIView):
34+
authentication_classes = [TokenAuth]
35+
36+
@extend_schema(
37+
methods=['POST'],
38+
description=_('OpenAI Interface Dialogue'),
39+
summary=_('OpenAI Interface Dialogue'),
40+
operation_id=_('OpenAI Interface Dialogue'), # type: ignore
41+
request=OpenAIAPI.get_request(),
42+
responses=None,
43+
tags=[_('Chat')] # type: ignore
44+
)
45+
def post(self, request: Request, application_id: str):
46+
return OpenAIChatSerializer(data={'application_id': application_id, 'chat_user_id': request.auth.chat_user_id,
47+
'chat_user_type': request.auth.chat_user_type}).chat(request.data)
48+
49+
3450
class AnonymousAuthentication(APIView):
3551
def options(self, request, *args, **kwargs):
3652
return HttpResponse(
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: application_key.py
6+
@date:2025/7/10 03:02
7+
@desc: 应用api key认证
8+
"""
9+
from django.db.models import QuerySet
10+
from django.utils.translation import gettext_lazy as _
11+
12+
from application.models import ApplicationApiKey, ChatUserType
13+
from common.auth.handle.auth_base_handle import AuthBaseHandle
14+
from common.constants.permission_constants import Permission, Group, Operate, RoleConstants, ChatAuth
15+
from common.database_model_manage.database_model_manage import DatabaseModelManage
16+
from common.exception.app_exception import AppAuthenticationFailed
17+
18+
19+
class ApplicationKey(AuthBaseHandle):
20+
def handle(self, request, token: str, get_token_details):
21+
application_api_key = QuerySet(ApplicationApiKey).filter(secret_key=token).first()
22+
if application_api_key is None:
23+
raise AppAuthenticationFailed(500, _('Secret key is invalid'))
24+
if not application_api_key.is_active:
25+
raise AppAuthenticationFailed(500, _('Secret key is invalid'))
26+
application_setting_model = DatabaseModelManage.get_model("application_setting")
27+
if application_setting_model is not None:
28+
application_setting = QuerySet(application_setting_model).filter(
29+
application_id=application_api_key.application_id).first()
30+
if application_setting.authentication:
31+
if application_setting.authentication != 'password':
32+
raise AppAuthenticationFailed(1002, _('Authentication information is incorrect'))
33+
return None, ChatAuth(
34+
current_role_list=[RoleConstants.CHAT_ANONYMOUS_USER],
35+
permission_list=[
36+
Permission(group=Group.APPLICATION,
37+
operate=Operate.READ)],
38+
application_id=application_api_key.application_id,
39+
chat_user_id=str(application_api_key.id),
40+
chat_user_type=ChatUserType.ANONYMOUS_USER.value)
41+
42+
def support(self, request, token: str, get_token_details):
43+
return str(token).startswith("application-")

apps/maxkb/settings/auth.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
"""
99
USER_TOKEN_AUTH = 'common.auth.handle.impl.user_token.UserToken'
1010
CHAT_ANONYMOUS_USER_AURH = 'common.auth.handle.impl.chat_anonymous_user_token.ChatAnonymousUserToken'
11-
11+
APPLICATION_KEY_AUTH = 'common.auth.handle.impl.application_key.ApplicationKey'
1212
AUTH_HANDLES = [
1313
USER_TOKEN_AUTH,
14-
CHAT_ANONYMOUS_USER_AURH
14+
CHAT_ANONYMOUS_USER_AURH,
15+
APPLICATION_KEY_AUTH
1516
]

0 commit comments

Comments
 (0)