diff --git a/apps/application/serializers/chat_serializers.py b/apps/application/serializers/chat_serializers.py
index d8a3e648b..cedfffbf0 100644
--- a/apps/application/serializers/chat_serializers.py
+++ b/apps/application/serializers/chat_serializers.py
@@ -56,6 +56,18 @@ class ChatSerializers(serializers.Serializer):
QuerySet(Chat).filter(id=self.data.get('chat_id'), application_id=self.data.get('application_id')).delete()
return True
+ class ClientChatHistory(serializers.Serializer):
+ application_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("应用id"))
+ client_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("客户端id"))
+
+ def page(self, current_page: int, page_size: int, with_valid=True):
+ if with_valid:
+ self.is_valid(raise_exception=True)
+ queryset = QuerySet(Chat).filter(client_id=self.data.get('client_id'),
+ application_id=self.data.get('application_id'))
+ queryset = queryset.order_by('-create_time')
+ return page_search(current_page, page_size, queryset, lambda row: ChatSerializerModel(row).data)
+
class Query(serializers.Serializer):
abstract = serializers.CharField(required=False, error_messages=ErrMessage.char("摘要"))
history_day = serializers.IntegerField(required=True, error_messages=ErrMessage.integer("历史天数"))
@@ -282,6 +294,12 @@ class ChatRecordSerializerModel(serializers.ModelSerializer):
'create_time', 'update_time']
+class ChatSerializerModel(serializers.ModelSerializer):
+ class Meta:
+ model = Chat
+ fields = ['id', 'application_id', 'abstract', 'client_id']
+
+
class ChatRecordSerializer(serializers.Serializer):
class Operate(serializers.Serializer):
chat_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("对话id"))
@@ -319,13 +337,16 @@ class ChatRecordSerializer(serializers.Serializer):
class Query(serializers.Serializer):
application_id = serializers.UUIDField(required=True)
chat_id = serializers.UUIDField(required=True)
+ order_asc = serializers.BooleanField(required=False)
def list(self, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id'))
+ order_by = 'create_time' if self.data.get('order_asc') is None or self.data.get(
+ 'order_asc') else '-create_time'
return [ChatRecordSerializerModel(chat_record).data for chat_record in
- QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id')).order_by("create_time")]
+ QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id')).order_by(order_by)]
@staticmethod
def reset_chat_record(chat_record):
@@ -354,8 +375,10 @@ class ChatRecordSerializer(serializers.Serializer):
def page(self, current_page: int, page_size: int, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
+ order_by = 'create_time' if self.data.get('order_asc') is None or self.data.get(
+ 'order_asc') else '-create_time'
page = page_search(current_page, page_size,
- QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id')).order_by("create_time"),
+ QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id')).order_by(order_by),
post_records_handler=lambda chat_record: self.reset_chat_record(chat_record))
return page
diff --git a/apps/application/swagger_api/chat_api.py b/apps/application/swagger_api/chat_api.py
index 29f60c3d0..9c56cd21e 100644
--- a/apps/application/swagger_api/chat_api.py
+++ b/apps/application/swagger_api/chat_api.py
@@ -12,6 +12,17 @@ from application.swagger_api.application_api import ApplicationApi
from common.mixins.api_mixin import ApiMixin
+class ChatClientHistoryApi(ApiMixin):
+ @staticmethod
+ def get_request_params_api():
+ return [openapi.Parameter(name='application_id',
+ in_=openapi.IN_PATH,
+ type=openapi.TYPE_STRING,
+ required=True,
+ description='应用id')
+ ]
+
+
class ChatApi(ApiMixin):
@staticmethod
def get_request_body_api():
@@ -80,7 +91,7 @@ class ChatApi(ApiMixin):
'problem_optimization'],
properties={
'id': openapi.Schema(type=openapi.TYPE_STRING, title="应用id",
- description="应用id,修改的时候传,创建的时候不传"),
+ description="应用id,修改的时候传,创建的时候不传"),
'model_id': openapi.Schema(type=openapi.TYPE_STRING, title="模型id", description="模型id"),
'dataset_id_list': openapi.Schema(type=openapi.TYPE_ARRAY,
items=openapi.Schema(type=openapi.TYPE_STRING),
diff --git a/apps/application/template/embed.js b/apps/application/template/embed.js
index 33545af47..ec4e6b9f2 100644
--- a/apps/application/template/embed.js
+++ b/apps/application/template/embed.js
@@ -53,14 +53,17 @@ const chatButtonHtml=
const getChatContainerHtml=(protocol,host,token)=>{
return `
-
-
+
+
`
}
@@ -94,6 +97,7 @@ const initChat=(root)=>{
const closeviewport=root.querySelector('.maxkb-closeviewport')
const close_func=()=>{
chat_container.style['display']=chat_container.style['display']=='block'?'none':'block'
+ chat_button.style['display']=chat_container.style['display']=='block'?'none':'block'
}
close_icon=chat_container.querySelector('.maxkb-chat-close')
chat_button.onclick = close_func
@@ -250,12 +254,12 @@ function initMaxkbStyle(root){
border: 1px solid #ffffff;
background: linear-gradient(188deg, rgba(235, 241, 255, 0.20) 39.6%, rgba(231, 249, 255, 0.20) 94.3%), #EFF0F1;
box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.10);
- position: fixed;bottom: 20px;right: 45px;overflow: hidden;
+ position: fixed;bottom: 16px;right: 16px;overflow: hidden;
}
#maxkb #maxkb-chat-container .maxkb-operate{
- top: 15px;
- right: 10px;
+ top: 18px;
+ right: 15px;
position: absolute;
display: flex;
align-items: center;
diff --git a/apps/application/urls.py b/apps/application/urls.py
index 30866c81a..624b5b45b 100644
--- a/apps/application/urls.py
+++ b/apps/application/urls.py
@@ -30,6 +30,8 @@ urlpatterns = [
path('application/
/', views.Application.Page.as_view(), name='application_page'),
path('application//chat/open', views.ChatView.Open.as_view()),
path("application/chat/open", views.ChatView.OpenTemp.as_view()),
+ path("application//chat/client//",
+ views.ChatView.ClientChatHistoryPage.as_view()),
path('application//chat/export', views.ChatView.Export.as_view(), name='export'),
path('application//chat', views.ChatView.as_view(), name='chats'),
path('application//chat//', views.ChatView.Page.as_view()),
diff --git a/apps/application/views/chat_views.py b/apps/application/views/chat_views.py
index 2d6ef10f1..3bae2b877 100644
--- a/apps/application/views/chat_views.py
+++ b/apps/application/views/chat_views.py
@@ -13,7 +13,8 @@ from rest_framework.views import APIView
from application.serializers.chat_message_serializers import ChatMessageSerializer
from application.serializers.chat_serializers import ChatSerializers, ChatRecordSerializer
-from application.swagger_api.chat_api import ChatApi, VoteApi, ChatRecordApi, ImproveApi, ChatRecordImproveApi
+from application.swagger_api.chat_api import ChatApi, VoteApi, ChatRecordApi, ImproveApi, ChatRecordImproveApi, \
+ ChatClientHistoryApi
from common.auth import TokenAuth, has_permissions
from common.constants.authentication_type import AuthenticationType
from common.constants.permission_constants import Permission, Group, Operate, \
@@ -137,6 +138,28 @@ class ChatView(APIView):
data={'application_id': application_id, 'user_id': request.user.id,
'chat_id': chat_id}).delete())
+ class ClientChatHistoryPage(APIView):
+ authentication_classes = [TokenAuth]
+
+ @action(methods=['GET'], detail=False)
+ @swagger_auto_schema(operation_summary="分页获取客户端对话列表",
+ operation_id="分页获取客户端对话列表",
+ manual_parameters=result.get_page_request_params(
+ ChatClientHistoryApi.get_request_params_api()),
+ responses=result.get_page_api_response(ChatApi.get_response_body_api()),
+ tags=["应用/对话日志"]
+ )
+ @has_permissions(
+ ViewPermission([RoleConstants.APPLICATION_ACCESS_TOKEN],
+ [lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.USE,
+ dynamic_tag=keywords.get('application_id'))])
+ )
+ def get(self, request: Request, application_id: str, current_page: int, page_size: int):
+ return result.success(ChatSerializers.ClientChatHistory(
+ data={'client_id': request.auth.client_id, 'application_id': application_id}).page(
+ current_page=current_page,
+ page_size=page_size))
+
class Page(APIView):
authentication_classes = [TokenAuth]
@@ -198,7 +221,7 @@ class ChatView(APIView):
def get(self, request: Request, application_id: str, chat_id: str):
return result.success(ChatRecordSerializer.Query(
data={'application_id': application_id,
- 'chat_id': chat_id}).list())
+ 'chat_id': chat_id, 'order_asc': request.query_params.get('order_asc')}).list())
class Page(APIView):
authentication_classes = [TokenAuth]
@@ -219,7 +242,8 @@ class ChatView(APIView):
def get(self, request: Request, application_id: str, chat_id: str, current_page: int, page_size: int):
return result.success(ChatRecordSerializer.Query(
data={'application_id': application_id,
- 'chat_id': chat_id}).page(current_page, page_size))
+ 'chat_id': chat_id, 'order_asc': request.query_params.get('order_asc')}).page(current_page,
+ page_size))
class Vote(APIView):
authentication_classes = [TokenAuth]
diff --git a/ui/public/embeb.js b/ui/public/embeb.js
deleted file mode 100644
index 23517cf59..000000000
--- a/ui/public/embeb.js
+++ /dev/null
@@ -1,307 +0,0 @@
-function auth(token, protocol, host) {
- const XML = new XMLHttpRequest()
- XML.open('POST', `${protocol}//${host}/api/application/authentication`, false)
- XML.setRequestHeader('Content-Type', 'application/json')
- res = XML.send(JSON.stringify({ access_token: token }))
- return XML.status == 200
-}
-
-const guideHtml=`
-
-
-
-
-
🌟 遇见问题,不再有障碍!
-
你好,我是你的智能小助手。
- 点我,开启高效解答模式,让问题变成过去式。
-
-
-
-
-
-`
-const chatButtonHtml=
-``
-
-
-
-const getChatContainerHtml=(protocol,host,token)=>{
- return `
-
-
-
-
`
-}
-/**
- * 初始化引导
- * @param {*} root
- */
-const initGuide=(root)=>{
- root.insertAdjacentHTML("beforeend",guideHtml)
- const button=root.querySelector(".maxkb-button")
- const close_icon=root.querySelector('.maxkb-close')
- const close_func=()=>{
- root.removeChild(root.querySelector('.maxkb-tips'))
- root.removeChild(root.querySelector('.maxkb-mask'))
- localStorage.setItem('maxkbMaskTip',true)
- }
- button.onclick=close_func
- close_icon.onclick=close_func
-}
-const initChat=(root)=>{
- // 添加对话icon
- root.insertAdjacentHTML("beforeend",chatButtonHtml)
- // 添加对话框
- root.insertAdjacentHTML('beforeend',getChatContainerHtml(window.maxkbChatConfig.protocol,window.maxkbChatConfig.host,window.maxkbChatConfig.token))
- // 按钮元素
- const chat_button=root.querySelector('.maxkb-chat-button')
- // 对话框元素
- const chat_container=root.querySelector('#maxkb-chat-container')
-
- const viewport=root.querySelector('.maxkb-openviewport')
- const closeviewport=root.querySelector('.maxkb-closeviewport')
- const close_func=()=>{
- chat_container.style['display']=chat_container.style['display']=='block'?'none':'block'
- }
- close_icon=chat_container.querySelector('.maxkb-close')
- chat_button.onclick = close_func
- close_icon.onclick=close_func
- const viewport_func=()=>{
- if(chat_container.classList.contains('maxkb-enlarge')){
- chat_container.classList.remove("maxkb-enlarge");
- viewport.classList.remove('maxkb-viewportnone')
- closeviewport.classList.add('maxkb-viewportnone')
- }else{
- chat_container.classList.add("maxkb-enlarge");
- viewport.classList.add('maxkb-viewportnone')
- closeviewport.classList.remove('maxkb-viewportnone')
- }
- }
- viewport.onclick=viewport_func
- closeviewport.onclick=viewport_func
-}
-/**
- * 第一次进来的引导提示
- */
-function initMaxkb(){
- const maxkb=document.createElement('div')
- const root=document.createElement('div')
- root.id="maxkb"
- initMaxkbStyle(maxkb)
- maxkb.appendChild(root)
- document.body.appendChild(maxkb)
- const maxkbMaskTip=localStorage.getItem('maxkbMaskTip')
- if(maxkbMaskTip==null){
- initGuide(root)
- }
- initChat(root)
-}
-
-
-// 初始化全局样式
-function initMaxkbStyle(root){
- style=document.createElement('style')
- style.type='text/css'
- style.innerText= `
- /* 放大 */
- #maxkb .maxkb-enlarge {
- width: 50%!important;
- height: 100%!important;
- bottom: 0!important;
- right: 0 !important;
- }
- @media only screen and (max-width: 768px){
- #maxkb .maxkb-enlarge {
- width: 100%!important;
- height: 100%!important;
- right: 0 !important;
- bottom: 0!important;
- }
- }
-
- /* 引导 */
-
- #maxkb .maxkb-mask {
- position: fixed;
- z-index: 999;
- background-color: transparent;
- height: 100%;
- width: 100%;
- top: 0;
- left: 0;
- }
- #maxkb .maxkb-mask .maxkb-content {
- width: 45px;
- height: 50px;
- box-shadow: 1px 1px 1px 2000px rgba(0,0,0,.6);
- border-radius: 50% 0 0 50%;
- position: absolute;
- right: 0;
- bottom: 42px;
- z-index: 1000;
- }
- #maxkb .maxkb-tips {
- position: fixed;
- bottom: 30px;
- right: 60px;
- padding: 22px 24px 24px;
- border-radius: 6px;
- color: #ffffff;
- font-size: 14px;
- background: #3370FF;
- z-index: 1000;
- }
- #maxkb .maxkb-tips .maxkb-arrow {
- position: absolute;
- background: #3370FF;
- width: 10px;
- height: 10px;
- pointer-events: none;
- transform: rotate(45deg);
- box-sizing: border-box;
- /* left */
- right: -5px;
- bottom: 33px;
- border-left-color: transparent;
- border-bottom-color: transparent
- }
- #maxkb .maxkb-tips .maxkb-title {
- font-size: 20px;
- font-weight: 500;
- margin-bottom: 8px;
- }
- #maxkb .maxkb-tips .maxkb-button {
- text-align: right;
- margin-top: 24px;
- }
- #maxkb .maxkb-tips .maxkb-button button {
- border-radius: 4px;
- background: #FFF;
- padding: 3px 12px;
- color: #3370FF;
- cursor: pointer;
- outline: none;
- border: none;
- }
- #maxkb .maxkb-tips .maxkb-button button::after{
- border: none;
- }
- #maxkb .maxkb-tips .maxkb-close {
- position: absolute;
- right: 20px;
- top: 20px;
- cursor: pointer;
-
- }
- #maxkb-chat-container {
- width: 420px;
- height: 600px;
- display:none;
- }
- @media only screen and (max-width: 768px) {
- #maxkb-chat-container {
- width: 100%;
- height: 70%;
- right: 0 !important;
- }
- }
-
- #maxkb .maxkb-chat-button{
- position: fixed;
- bottom: 30px;
- right: 0;
- cursor: pointer;
- }
- #maxkb #maxkb-chat-container{
- z-index:10000;position: relative;
- border-radius: 8px;
- border: 1px solid var(--N300, #DEE0E3);
- background: linear-gradient(188deg, rgba(235, 241, 255, 0.20) 39.6%, rgba(231, 249, 255, 0.20) 94.3%), #EFF0F1;
- box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.10);
- position: fixed;bottom: 20px;right: 45px;overflow: hidden;
- }
- #maxkb #maxkb-chat-container .maxkb-chat-close{
- position: absolute;
- top: 15px;
- right: 10px;
- cursor: pointer;
- }
- #maxkb #maxkb-chat-container .maxkb-openviewport{
- position: absolute;
- top: 15px;
- right: 50px;
- cursor: pointer;
- }
- #maxkb #maxkb-chat-container .maxkb-closeviewport{
- position: absolute;
- top: 15px;
- right: 50px;
- cursor: pointer;
- }
- #maxkb #maxkb-chat-container .maxkb-viewportnone{
- display:none;
- }
- #maxkb #maxkb-chat-container #maxkb-chat{
- height:100%;
- width:100%;
- border: none;
-}
- #maxkb #maxkb-chat-container {
- animation: appear .4s ease-in-out;
- }
- @keyframes appear {
- from {
- height: 0;;
- }
-
- to {
- height: 600px;
- }
- }`
- root.appendChild(style)
-}
-
-function embedChatbot() {
- const t = window.maxkbChatConfig
- check = auth(t.token, t.protocol, t.host)
- if (t && t.token && t.protocol && t.host && check) {
- // 初始化maxkb智能小助手
- initMaxkb()
- } else console.error('invalid parameter')
-}
-window.onload = embedChatbot
diff --git a/ui/src/api/log.ts b/ui/src/api/log.ts
index 1904e96b0..67b88ab8f 100644
--- a/ui/src/api/log.ts
+++ b/ui/src/api/log.ts
@@ -1,5 +1,5 @@
import { Result } from '@/request/Result'
-import { get, post, del, put, exportExcel } from '@/request/index'
+import { get, del, put, exportExcel } from '@/request/index'
import type { pageRequest } from '@/api/type/common'
import { type Ref } from 'vue'
@@ -64,11 +64,12 @@ const getChatRecordLog: (
application_id: String,
chart_id: String,
page: pageRequest,
- loading?: Ref
-) => Promise> = (application_id, chart_id, page, loading) => {
+ loading?: Ref,
+ order_asc?: boolean
+) => Promise> = (application_id, chart_id, page, loading, order_asc) => {
return get(
`${prefix}/${application_id}/chat/${chart_id}/chat_record/${page.current_page}/${page.page_size}`,
- undefined,
+ { order_asc: order_asc !== undefined ? order_asc : true },
loading
)
}
@@ -173,6 +174,18 @@ const getRecordDetail: (
)
}
+const getChatLogClient: (
+ application_id: String,
+ page: pageRequest,
+ loading?: Ref
+) => Promise> = (application_id, page, loading) => {
+ return get(
+ `${prefix}/${application_id}/chat/client/${page.current_page}/${page.page_size}`,
+ null,
+ loading
+ )
+}
+
export default {
getChatLog,
delChatLog,
@@ -181,5 +194,6 @@ export default {
getMarkRecord,
getRecordDetail,
delMarkRecord,
- exportChatLog
+ exportChatLog,
+ getChatLogClient
}
diff --git a/ui/src/components/ai-chat/LogOperationButton.vue b/ui/src/components/ai-chat/LogOperationButton.vue
index 59cc48634..7a6dcb74d 100644
--- a/ui/src/components/ai-chat/LogOperationButton.vue
+++ b/ui/src/components/ai-chat/LogOperationButton.vue
@@ -41,7 +41,7 @@
diff --git a/ui/src/views/chat/embed/index.vue b/ui/src/views/chat/embed/index.vue
new file mode 100644
index 000000000..01740ad45
--- /dev/null
+++ b/ui/src/views/chat/embed/index.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+ 新建对话
+
+
+
+
+
+
+
+ 历史记录
+
+
+
+
+
+
+
+ {{ row.abstract }}
+
+
+
+
+ 暂无历史记录
+
+
+
+
+
+ 仅显示最近 20 条对话
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/views/chat/index.vue b/ui/src/views/chat/index.vue
index f04d44242..16f77d8fb 100644
--- a/ui/src/views/chat/index.vue
+++ b/ui/src/views/chat/index.vue
@@ -1,144 +1,23 @@
-
+
-
+onMounted(() => {})
+
+
diff --git a/ui/src/views/chat/pc/index.vue b/ui/src/views/chat/pc/index.vue
new file mode 100644
index 000000000..6d8dcd10b
--- /dev/null
+++ b/ui/src/views/chat/pc/index.vue
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.abstract }}
+
+
+
+
+ 暂无历史记录
+
+
+
+
+
+ 仅显示最近 20 条对话
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/views/dataset/component/SyncWebDialog.vue b/ui/src/views/dataset/component/SyncWebDialog.vue
index 08ccc9674..60dda86e7 100644
--- a/ui/src/views/dataset/component/SyncWebDialog.vue
+++ b/ui/src/views/dataset/component/SyncWebDialog.vue
@@ -35,8 +35,6 @@