Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/application/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
path('workspace/<str:workspace_id>/application/<str:application_id>/speech_to_text', views.SpeechToText.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/play_demo_text', views.PlayDemoText.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/mcp_tools', views.McpServers.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/model/<str:model_id>/prompt_generate', views.PromptGenerateView.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/chat/<str:chat_id>/chat_message', views.ChatView.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/chat/<str:chat_id>/cancel_chat_message', views.CancelWorkflowView.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/chat/<str:chat_id>/chat_record/<str:chat_record_id>/resume_chat_message', views.ResumeStreamView.as_view()),
Expand Down
40 changes: 3 additions & 37 deletions apps/application/views/application_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@
)
from application.models import ChatUserType, Application, ChatSourceChoices
from application.serializers.application_chat import ApplicationChatQuerySerializers
from chat.api.chat_api import ChatAPI, PromptGenerateAPI, PageHistoricalConversationAPI, HistoricalConversationRecordAPI
from chat.api.chat_api import ChatAPI, PageHistoricalConversationAPI, HistoricalConversationRecordAPI
from chat.api.chat_authentication_api import ChatOpenAPI
from chat.serializers.chat import DebugChatSerializers, OpenChatSerializers, PromptGenerateSerializer, ResumeSerializers
from chat.serializers.chat import DebugChatSerializers, OpenChatSerializers, ResumeSerializers
from common.auth import TokenAuth
from common.auth.authentication import has_permissions
from common.auth.constants.compare_constants import CompareConstants
from common.auth.constants.permission_constants import PermissionConstants
from common.auth.constants.role_constants import RoleConstants
from common.auth.struct.aggregate_permission import ViewPermission
from common.log.log import log, _get_ip_address
from common.log.log import _get_ip_address
from common.result import result
from common.utils.common import query_params_to_single_dict

Expand Down Expand Up @@ -275,40 +275,6 @@ def post(self, request: Request, workspace_id: str, application_id: str, chat_id
return ResumeSerializers(data={"chat_id": chat_id, "chat_record_id": chat_record_id}).resume(request)


class PromptGenerateView(APIView):
authentication_classes = [TokenAuth]

@extend_schema(
methods=["POST"],
description=_("generate prompt"),
summary=_("generate prompt"),
operation_id=_("generate prompt"), # type: ignore
request=PromptGenerateAPI.get_request(),
parameters=PromptGenerateAPI.get_parameters(),
responses=None,
tags=[_("Application")], # type: ignore
)
@has_permissions(
PermissionConstants.APPLICATION_READ.get_workspace_application_permission(),
PermissionConstants.APPLICATION_READ.get_workspace_permission_workspace_manage_role(),
ViewPermission(
[RoleConstants.USER.get_workspace_role()],
[PermissionConstants.APPLICATION.get_workspace_application_permission()],
compare=CompareConstants.AND,
),
RoleConstants.WORKSPACE_MANAGE.get_workspace_role(),
)
@log(
menu="Application",
operate="Generate prompt",
get_operation_object=lambda r, k: get_application_operation_object(k.get("application_id")),
)
def post(self, request: Request, workspace_id: str, model_id: str, application_id: str):
return PromptGenerateSerializer(
data={"workspace_id": workspace_id, "model_id": model_id, "application_id": application_id}
).generate_prompt(instance=request.data)


class DebugHistoricalConversation(APIView):
authentication_classes = [TokenAuth]

Expand Down
98 changes: 36 additions & 62 deletions apps/chat/api/chat_api.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,35 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: chat_api.py
@date:2025/6/9 15:23
@desc:
@project: MaxKB
@Author:虎虎
@file: chat_api.py
@date:2025/6/9 15:23
@desc:
"""

from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter

from application.serializers.application_chat_record import ChatRecordSerializerModel
from chat.serializers.chat import ChatMessageSerializers, GeneratePromptSerializers
from chat.serializers.chat import ChatMessageSerializers
from chat.serializers.chat_record import HistoryChatModel, EditAbstractSerializer
from common.mixins.api_mixin import APIMixin
from common.result import ResultSerializer, ResultPageSerializer, DefaultResultSerializer


class PromptGenerateAPI(APIMixin):
class ChatAPI(APIMixin):
@staticmethod
def get_parameters():
return [OpenApiParameter(
name="workspace_id",
description="工作空间id",
type=OpenApiTypes.STR,
location='path',
required=True,
),
OpenApiParameter(
name="model_id",
description="模型id",
type=OpenApiTypes.STR,
location='path',
required=True,
),
return [
OpenApiParameter(
name="application_id",
description="应用id",
name="chat_id",
description="对话id",
type=OpenApiTypes.STR,
location='path',
location="path",
required=True,
),
)
]

@staticmethod
def get_request():
return GeneratePromptSerializers


class ChatAPI(APIMixin):
@staticmethod
def get_parameters():
return [OpenApiParameter(
name="chat_id",
description="对话id",
type=OpenApiTypes.STR,
location='path',
required=True,
)]

@staticmethod
def get_request():
return ChatMessageSerializers
Expand Down Expand Up @@ -106,13 +78,11 @@ def get_response():
class HistoricalConversationOperateAPI(APIMixin):
@staticmethod
def get_parameters():
return [OpenApiParameter(
name="chat_id",
description="对话id",
type=OpenApiTypes.STR,
location='path',
required=True
)]
return [
OpenApiParameter(
name="chat_id", description="对话id", type=OpenApiTypes.STR, location="path", required=True
)
]

@staticmethod
def get_request():
Expand All @@ -126,13 +96,15 @@ def get_response():
class HistoricalConversationRecordAPI(APIMixin):
@staticmethod
def get_parameters():
return [OpenApiParameter(
name="chat_id",
description="对话id",
type=OpenApiTypes.STR,
location='path',
required=True,
)]
return [
OpenApiParameter(
name="chat_id",
description="对话id",
type=OpenApiTypes.STR,
location="path",
required=True,
)
]

@staticmethod
def get_response():
Expand All @@ -142,13 +114,15 @@ def get_response():
class PageHistoricalConversationRecordAPI(APIMixin):
@staticmethod
def get_parameters():
return [OpenApiParameter(
name="chat_id",
description="对话id",
type=OpenApiTypes.STR,
location='path',
required=True,
)]
return [
OpenApiParameter(
name="chat_id",
description="对话id",
type=OpenApiTypes.STR,
location="path",
required=True,
)
]

@staticmethod
def get_response():
Expand Down
94 changes: 1 addition & 93 deletions apps/chat/serializers/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"""

import json
import os
import queue
import queue as thread_queue
import threading
Expand All @@ -20,7 +19,6 @@
from django.http import StreamingHttpResponse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from rest_framework import serializers
from rest_framework.request import Request

Expand Down Expand Up @@ -53,11 +51,8 @@
from common.handle.base_to_response import BaseToResponse
from common.handle.impl.response.openai_to_response import OpenaiToResponse
from common.handle.impl.response.system_to_response import SystemToResponse
from common.utils.common import get_file_content
from common.utils.logger import maxkb_logger
from maxkb.conf import PROJECT_DIR
from models_provider.models import Model, Status
from models_provider.tools import get_model_instance_by_model_workspace_id
from models_provider.models import Status
from system_manage.models.chat_user_token_quota import ChatUserTokenQuota

_CHAT_UNSET = object()
Expand Down Expand Up @@ -676,93 +671,6 @@ def _stream_from_db(self, chat_record, start_id: str):
yield "data: [DONE]\n\n"


# ==================== 提示词生成 ====================

SYSTEM_ROLE = get_file_content(os.path.join(PROJECT_DIR, "apps", "chat", "template", "generate_prompt_system"))


class ChatMessagesSerializers(serializers.Serializer):
role = serializers.CharField(required=True, label=_("Role"))
content = serializers.CharField(required=True, label=_("Content"))


class GeneratePromptSerializers(serializers.Serializer):
prompt = serializers.CharField(required=True, label=_("Prompt template"))
messages = serializers.ListSerializer(child=ChatMessagesSerializers(), required=True, label=_("Chat context"))

def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
messages = self.data.get("messages")

if len(messages) > 30:
raise AppApiException(400, _("Too many messages"))

for index in range(len(messages)):
role = messages[index].get("role")
if role == "ai" and index % 2 != 1:
raise AppApiException(400, _("Authentication failed. Please verify that the parameters are correct."))
if role == "user" and index % 2 != 0:
raise AppApiException(400, _("Authentication failed. Please verify that the parameters are correct."))
if role not in ["user", "ai"]:
raise AppApiException(400, _("Authentication failed. Please verify that the parameters are correct."))


class PromptGenerateSerializer(serializers.Serializer):
workspace_id = serializers.CharField(required=False, label=_("Workspace ID"))
model_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("Model"))
application_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("Application"))

def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
workspace_id = self.data.get("workspace_id")
query_set = QuerySet(Application).filter(id=self.data.get("application_id"))
if workspace_id:
query_set = query_set.filter(workspace_id=workspace_id)
application = query_set.first()
if application is None:
raise AppApiException(500, _("Application id does not exist"))
return application

def generate_prompt(self, instance: dict):
application = self.is_valid(raise_exception=True)
GeneratePromptSerializers(data=instance).is_valid(raise_exception=True)
workspace_id = self.data.get("workspace_id")
model_id = self.data.get("model_id")
prompt = instance.get("prompt")
messages = instance.get("messages")

message = messages[-1]["content"]
q = prompt.replace("{userInput}", message)

messages[-1]["content"] = q
SUPPORTED_MODEL_TYPES = ["LLM", "IMAGE"]
model_exist = QuerySet(Model).filter(id=model_id, model_type__in=SUPPORTED_MODEL_TYPES).exists()
if not model_exist:
raise Exception(_("Model does not exists or is not an LLM model"))

def process():
model = get_model_instance_by_model_workspace_id(
model_id=model_id, workspace_id=workspace_id, **application.model_params_setting
)
try:
for r in model.stream(
[
SystemMessage(content=SYSTEM_ROLE),
*[
HumanMessage(content=m.get("content"))
if m.get("role") == "user"
else AIMessage(content=m.get("content"))
for m in messages
],
]
):
yield "data: " + json.dumps({"content": r.content}) + "\n\n"
except Exception as e:
yield "data: " + json.dumps({"error": str(e)}) + "\n\n"

return to_stream_response_simple(process())


# ==================== 语音 ====================


Expand Down
36 changes: 36 additions & 0 deletions apps/models_provider/api/prompt_generate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# coding=utf-8
from common.mixins.api_mixin import APIMixin
from django.utils.translation import gettext_lazy as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter
from models_provider.serializers.prompt_generate_serializers import PromptGenerateSerializers


class PromptGenerateAPI(APIMixin):
@staticmethod
def get_parameters(workspace: bool = True):
parameters = []
if workspace:
parameters.append(
OpenApiParameter(
name="workspace_id",
description=_("workspace id"),
type=OpenApiTypes.STR,
location=OpenApiParameter.PATH, # type: ignore
required=True,
)
)
parameters.append(
OpenApiParameter(
name="model_id",
description=_("model id"),
type=OpenApiTypes.STR,
location=OpenApiParameter.PATH, # type: ignore
required=True,
)
)
return parameters

@staticmethod
def get_request():
return PromptGenerateSerializers
Loading
Loading