balibabu
commited on
Commit
·
809dc5c
1
Parent(s):
35bb186
fix: Fixed an issue where the first message would be displayed when sending the second message #2625 (#2626)
Browse files### What problem does this PR solve?
fix: Fixed an issue where the first message would be displayed when
sending the second message #2625
### Type of change
- [x] Bug Fix (non-breaking change which fixes an issue)
- [ ] New Feature (non-breaking change which adds functionality)
- [ ] Documentation Update
- [ ] Refactoring
- [ ] Performance Improvement
- [ ] Other (please describe):
- api/apps/conversation_app.py +4 -2
- web/.umirc.ts +1 -1
- web/src/components/message-input/index.tsx +8 -2
- web/src/components/message-item/hooks.ts +7 -7
- web/src/constants/chat.ts +1 -0
- web/src/hooks/chat-hooks.ts +57 -19
- web/src/hooks/logic-hooks.ts +6 -0
- web/src/interfaces/database/chat.ts +1 -0
- web/src/locales/en.ts +1 -1
- web/src/pages/chat/chat-container/index.tsx +33 -33
- web/src/pages/chat/constants.ts +0 -5
- web/src/pages/chat/hooks.ts +118 -115
- web/src/pages/chat/index.tsx +25 -27
- web/src/pages/user-setting/setting-model/fish-audio-modal/index.tsx +1 -1
- web/src/utils/chat.ts +4 -0
api/apps/conversation_app.py
CHANGED
|
@@ -37,7 +37,9 @@ from graphrag.mind_map_extractor import MindMapExtractor
|
|
| 37 |
def set_conversation():
|
| 38 |
req = request.json
|
| 39 |
conv_id = req.get("conversation_id")
|
| 40 |
-
|
|
|
|
|
|
|
| 41 |
del req["conversation_id"]
|
| 42 |
try:
|
| 43 |
if not ConversationService.update_by_id(conv_id, req):
|
|
@@ -56,7 +58,7 @@ def set_conversation():
|
|
| 56 |
if not e:
|
| 57 |
return get_data_error_result(retmsg="Dialog not found")
|
| 58 |
conv = {
|
| 59 |
-
"id":
|
| 60 |
"dialog_id": req["dialog_id"],
|
| 61 |
"name": req.get("name", "New conversation"),
|
| 62 |
"message": [{"role": "assistant", "content": dia.prompt_config["prologue"]}]
|
|
|
|
| 37 |
def set_conversation():
|
| 38 |
req = request.json
|
| 39 |
conv_id = req.get("conversation_id")
|
| 40 |
+
is_new = req.get("is_new")
|
| 41 |
+
del req["is_new"]
|
| 42 |
+
if not is_new:
|
| 43 |
del req["conversation_id"]
|
| 44 |
try:
|
| 45 |
if not ConversationService.update_by_id(conv_id, req):
|
|
|
|
| 58 |
if not e:
|
| 59 |
return get_data_error_result(retmsg="Dialog not found")
|
| 60 |
conv = {
|
| 61 |
+
"id": conv_id,
|
| 62 |
"dialog_id": req["dialog_id"],
|
| 63 |
"name": req.get("name", "New conversation"),
|
| 64 |
"message": [{"role": "assistant", "content": dia.prompt_config["prologue"]}]
|
web/.umirc.ts
CHANGED
|
@@ -30,7 +30,7 @@ export default defineConfig({
|
|
| 30 |
copy: ['src/conf.json'],
|
| 31 |
proxy: {
|
| 32 |
'/v1': {
|
| 33 |
-
target: 'http://127.0.0.1:
|
| 34 |
changeOrigin: true,
|
| 35 |
ws: true,
|
| 36 |
logger: console,
|
|
|
|
| 30 |
copy: ['src/conf.json'],
|
| 31 |
proxy: {
|
| 32 |
'/v1': {
|
| 33 |
+
target: 'http://127.0.0.1:9380/',
|
| 34 |
changeOrigin: true,
|
| 35 |
ws: true,
|
| 36 |
logger: console,
|
web/src/components/message-input/index.tsx
CHANGED
|
@@ -117,7 +117,7 @@ const MessageInput = ({
|
|
| 117 |
file,
|
| 118 |
}) => {
|
| 119 |
let nextConversationId: string = conversationId;
|
| 120 |
-
if (createConversationBeforeUploadDocument
|
| 121 |
const creatingRet = await createConversationBeforeUploadDocument(
|
| 122 |
file.name,
|
| 123 |
);
|
|
@@ -234,8 +234,14 @@ const MessageInput = ({
|
|
| 234 |
>
|
| 235 |
<Button
|
| 236 |
type={'text'}
|
|
|
|
| 237 |
icon={
|
| 238 |
-
<SvgIcon
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
}
|
| 240 |
></Button>
|
| 241 |
</Upload>
|
|
|
|
| 117 |
file,
|
| 118 |
}) => {
|
| 119 |
let nextConversationId: string = conversationId;
|
| 120 |
+
if (createConversationBeforeUploadDocument) {
|
| 121 |
const creatingRet = await createConversationBeforeUploadDocument(
|
| 122 |
file.name,
|
| 123 |
);
|
|
|
|
| 234 |
>
|
| 235 |
<Button
|
| 236 |
type={'text'}
|
| 237 |
+
disabled={disabled}
|
| 238 |
icon={
|
| 239 |
+
<SvgIcon
|
| 240 |
+
name="paper-clip"
|
| 241 |
+
width={18}
|
| 242 |
+
height={22}
|
| 243 |
+
disabled={disabled}
|
| 244 |
+
></SvgIcon>
|
| 245 |
}
|
| 246 |
></Button>
|
| 247 |
</Upload>
|
web/src/components/message-item/hooks.ts
CHANGED
|
@@ -2,11 +2,10 @@ import { useDeleteMessage, useFeedback } from '@/hooks/chat-hooks';
|
|
| 2 |
import { useSetModalState } from '@/hooks/common-hooks';
|
| 3 |
import { IRemoveMessageById, useSpeechWithSse } from '@/hooks/logic-hooks';
|
| 4 |
import { IFeedbackRequestBody } from '@/interfaces/request/chat';
|
| 5 |
-
import { ConversationContext } from '@/pages/chat/context';
|
| 6 |
import { getMessagePureId } from '@/utils/chat';
|
| 7 |
import { hexStringToUint8Array } from '@/utils/common-util';
|
| 8 |
import { SpeechPlayer } from 'openai-speech-stream-player';
|
| 9 |
-
import { useCallback,
|
| 10 |
|
| 11 |
export const useSendFeedback = (messageId: string) => {
|
| 12 |
const { visible, hideModal, showModal } = useSetModalState();
|
|
@@ -59,24 +58,21 @@ export const useSpeech = (content: string, audioBinary?: string) => {
|
|
| 59 |
const { read } = useSpeechWithSse();
|
| 60 |
const player = useRef<SpeechPlayer>();
|
| 61 |
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
| 62 |
-
const callback = useContext(ConversationContext);
|
| 63 |
|
| 64 |
const initialize = useCallback(async () => {
|
| 65 |
player.current = new SpeechPlayer({
|
| 66 |
audio: ref.current!,
|
| 67 |
onPlaying: () => {
|
| 68 |
setIsPlaying(true);
|
| 69 |
-
callback?.(true);
|
| 70 |
},
|
| 71 |
onPause: () => {
|
| 72 |
setIsPlaying(false);
|
| 73 |
-
callback?.(false);
|
| 74 |
},
|
| 75 |
onChunkEnd: () => {},
|
| 76 |
mimeType: 'audio/mpeg',
|
| 77 |
});
|
| 78 |
await player.current.init();
|
| 79 |
-
}, [
|
| 80 |
|
| 81 |
const pause = useCallback(() => {
|
| 82 |
player.current?.pause();
|
|
@@ -103,7 +99,11 @@ export const useSpeech = (content: string, audioBinary?: string) => {
|
|
| 103 |
if (audioBinary) {
|
| 104 |
const units = hexStringToUint8Array(audioBinary);
|
| 105 |
if (units) {
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
}
|
| 108 |
}
|
| 109 |
}, [audioBinary]);
|
|
|
|
| 2 |
import { useSetModalState } from '@/hooks/common-hooks';
|
| 3 |
import { IRemoveMessageById, useSpeechWithSse } from '@/hooks/logic-hooks';
|
| 4 |
import { IFeedbackRequestBody } from '@/interfaces/request/chat';
|
|
|
|
| 5 |
import { getMessagePureId } from '@/utils/chat';
|
| 6 |
import { hexStringToUint8Array } from '@/utils/common-util';
|
| 7 |
import { SpeechPlayer } from 'openai-speech-stream-player';
|
| 8 |
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
| 9 |
|
| 10 |
export const useSendFeedback = (messageId: string) => {
|
| 11 |
const { visible, hideModal, showModal } = useSetModalState();
|
|
|
|
| 58 |
const { read } = useSpeechWithSse();
|
| 59 |
const player = useRef<SpeechPlayer>();
|
| 60 |
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
|
|
|
| 61 |
|
| 62 |
const initialize = useCallback(async () => {
|
| 63 |
player.current = new SpeechPlayer({
|
| 64 |
audio: ref.current!,
|
| 65 |
onPlaying: () => {
|
| 66 |
setIsPlaying(true);
|
|
|
|
| 67 |
},
|
| 68 |
onPause: () => {
|
| 69 |
setIsPlaying(false);
|
|
|
|
| 70 |
},
|
| 71 |
onChunkEnd: () => {},
|
| 72 |
mimeType: 'audio/mpeg',
|
| 73 |
});
|
| 74 |
await player.current.init();
|
| 75 |
+
}, []);
|
| 76 |
|
| 77 |
const pause = useCallback(() => {
|
| 78 |
player.current?.pause();
|
|
|
|
| 99 |
if (audioBinary) {
|
| 100 |
const units = hexStringToUint8Array(audioBinary);
|
| 101 |
if (units) {
|
| 102 |
+
try {
|
| 103 |
+
player.current?.feed(units);
|
| 104 |
+
} catch (error) {
|
| 105 |
+
console.warn(error);
|
| 106 |
+
}
|
| 107 |
}
|
| 108 |
}
|
| 109 |
}, [audioBinary]);
|
web/src/constants/chat.ts
CHANGED
|
@@ -19,6 +19,7 @@ export enum SharedFrom {
|
|
| 19 |
export enum ChatSearchParams {
|
| 20 |
DialogId = 'dialogId',
|
| 21 |
ConversationId = 'conversationId',
|
|
|
|
| 22 |
}
|
| 23 |
|
| 24 |
export const EmptyConversationId = 'empty';
|
|
|
|
| 19 |
export enum ChatSearchParams {
|
| 20 |
DialogId = 'dialogId',
|
| 21 |
ConversationId = 'conversationId',
|
| 22 |
+
isNew = 'isNew',
|
| 23 |
}
|
| 24 |
|
| 25 |
export const EmptyConversationId = 'empty';
|
web/src/hooks/chat-hooks.ts
CHANGED
|
@@ -12,18 +12,22 @@ import {
|
|
| 12 |
import i18n from '@/locales/config';
|
| 13 |
import { IClientConversation } from '@/pages/chat/interface';
|
| 14 |
import chatService from '@/services/chat-service';
|
| 15 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
| 17 |
import { message } from 'antd';
|
| 18 |
import dayjs, { Dayjs } from 'dayjs';
|
| 19 |
import { has, set } from 'lodash';
|
| 20 |
import { useCallback, useMemo, useState } from 'react';
|
| 21 |
-
import { useSearchParams } from 'umi';
|
| 22 |
|
| 23 |
//#region logic
|
| 24 |
|
| 25 |
export const useClickDialogCard = () => {
|
| 26 |
-
const [, setSearchParams] = useSearchParams();
|
| 27 |
|
| 28 |
const newQueryParameters: URLSearchParams = useMemo(() => {
|
| 29 |
return new URLSearchParams();
|
|
@@ -44,6 +48,25 @@ export const useClickDialogCard = () => {
|
|
| 44 |
return { handleClickDialog };
|
| 45 |
};
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
export const useGetChatSearchParams = () => {
|
| 48 |
const [currentQueryParameters] = useSearchParams();
|
| 49 |
|
|
@@ -51,6 +74,7 @@ export const useGetChatSearchParams = () => {
|
|
| 51 |
dialogId: currentQueryParameters.get(ChatSearchParams.DialogId) || '',
|
| 52 |
conversationId:
|
| 53 |
currentQueryParameters.get(ChatSearchParams.ConversationId) || '',
|
|
|
|
| 54 |
};
|
| 55 |
};
|
| 56 |
|
|
@@ -60,6 +84,7 @@ export const useGetChatSearchParams = () => {
|
|
| 60 |
|
| 61 |
export const useFetchNextDialogList = () => {
|
| 62 |
const { handleClickDialog } = useClickDialogCard();
|
|
|
|
| 63 |
|
| 64 |
const {
|
| 65 |
data,
|
|
@@ -70,11 +95,20 @@ export const useFetchNextDialogList = () => {
|
|
| 70 |
initialData: [],
|
| 71 |
gcTime: 0,
|
| 72 |
refetchOnWindowFocus: false,
|
| 73 |
-
|
|
|
|
|
|
|
| 74 |
const { data } = await chatService.listDialog();
|
| 75 |
|
| 76 |
-
if (data.retcode === 0
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
}
|
| 79 |
|
| 80 |
return data?.data ?? [];
|
|
@@ -86,6 +120,7 @@ export const useFetchNextDialogList = () => {
|
|
| 86 |
|
| 87 |
export const useSetNextDialog = () => {
|
| 88 |
const queryClient = useQueryClient();
|
|
|
|
| 89 |
const {
|
| 90 |
data,
|
| 91 |
isPending: loading,
|
|
@@ -96,8 +131,10 @@ export const useSetNextDialog = () => {
|
|
| 96 |
const { data } = await chatService.setDialog(params);
|
| 97 |
if (data.retcode === 0) {
|
| 98 |
queryClient.invalidateQueries({
|
|
|
|
| 99 |
queryKey: ['fetchDialogList'],
|
| 100 |
});
|
|
|
|
| 101 |
queryClient.invalidateQueries({
|
| 102 |
queryKey: ['fetchDialog'],
|
| 103 |
});
|
|
@@ -166,6 +203,7 @@ export const useRemoveNextDialog = () => {
|
|
| 166 |
const { data } = await chatService.removeDialog({ dialogIds });
|
| 167 |
if (data.retcode === 0) {
|
| 168 |
queryClient.invalidateQueries({ queryKey: ['fetchDialogList'] });
|
|
|
|
| 169 |
message.success(i18n.t('message.deleted'));
|
| 170 |
}
|
| 171 |
return data.retcode;
|
|
@@ -181,6 +219,7 @@ export const useRemoveNextDialog = () => {
|
|
| 181 |
|
| 182 |
export const useFetchNextConversationList = () => {
|
| 183 |
const { dialogId } = useGetChatSearchParams();
|
|
|
|
| 184 |
const {
|
| 185 |
data,
|
| 186 |
isFetching: loading,
|
|
@@ -193,7 +232,9 @@ export const useFetchNextConversationList = () => {
|
|
| 193 |
enabled: !!dialogId,
|
| 194 |
queryFn: async () => {
|
| 195 |
const { data } = await chatService.listConversation({ dialogId });
|
| 196 |
-
|
|
|
|
|
|
|
| 197 |
return data?.data;
|
| 198 |
},
|
| 199 |
});
|
|
@@ -202,7 +243,7 @@ export const useFetchNextConversationList = () => {
|
|
| 202 |
};
|
| 203 |
|
| 204 |
export const useFetchNextConversation = () => {
|
| 205 |
-
const { conversationId } = useGetChatSearchParams();
|
| 206 |
const {
|
| 207 |
data,
|
| 208 |
isFetching: loading,
|
|
@@ -214,17 +255,9 @@ export const useFetchNextConversation = () => {
|
|
| 214 |
gcTime: 0,
|
| 215 |
refetchOnWindowFocus: false,
|
| 216 |
queryFn: async () => {
|
| 217 |
-
if (isConversationIdExist(conversationId)) {
|
| 218 |
const { data } = await chatService.getConversation({ conversationId });
|
| 219 |
-
|
| 220 |
-
// yield put({
|
| 221 |
-
// type: 'kFModel/fetch_document_thumbnails',
|
| 222 |
-
// payload: {
|
| 223 |
-
// doc_ids: getDocumentIdsFromConversionReference(data.data),
|
| 224 |
-
// },
|
| 225 |
-
// });
|
| 226 |
-
// yield put({ type: 'setCurrentConversation', payload: data.data });
|
| 227 |
-
// }
|
| 228 |
const conversation = data?.data ?? {};
|
| 229 |
|
| 230 |
const messageList = buildMessageListWithUuid(conversation?.message);
|
|
@@ -265,7 +298,12 @@ export const useUpdateNextConversation = () => {
|
|
| 265 |
} = useMutation({
|
| 266 |
mutationKey: ['updateConversation'],
|
| 267 |
mutationFn: async (params: Record<string, any>) => {
|
| 268 |
-
const { data } = await chatService.setConversation(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
if (data.retcode === 0) {
|
| 270 |
queryClient.invalidateQueries({ queryKey: ['fetchConversationList'] });
|
| 271 |
}
|
|
|
|
| 12 |
import i18n from '@/locales/config';
|
| 13 |
import { IClientConversation } from '@/pages/chat/interface';
|
| 14 |
import chatService from '@/services/chat-service';
|
| 15 |
+
import {
|
| 16 |
+
buildMessageListWithUuid,
|
| 17 |
+
getConversationId,
|
| 18 |
+
isConversationIdExist,
|
| 19 |
+
} from '@/utils/chat';
|
| 20 |
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
| 21 |
import { message } from 'antd';
|
| 22 |
import dayjs, { Dayjs } from 'dayjs';
|
| 23 |
import { has, set } from 'lodash';
|
| 24 |
import { useCallback, useMemo, useState } from 'react';
|
| 25 |
+
import { history, useSearchParams } from 'umi';
|
| 26 |
|
| 27 |
//#region logic
|
| 28 |
|
| 29 |
export const useClickDialogCard = () => {
|
| 30 |
+
const [_, setSearchParams] = useSearchParams();
|
| 31 |
|
| 32 |
const newQueryParameters: URLSearchParams = useMemo(() => {
|
| 33 |
return new URLSearchParams();
|
|
|
|
| 48 |
return { handleClickDialog };
|
| 49 |
};
|
| 50 |
|
| 51 |
+
export const useClickConversationCard = () => {
|
| 52 |
+
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
| 53 |
+
const newQueryParameters: URLSearchParams = useMemo(
|
| 54 |
+
() => new URLSearchParams(currentQueryParameters.toString()),
|
| 55 |
+
[currentQueryParameters],
|
| 56 |
+
);
|
| 57 |
+
|
| 58 |
+
const handleClickConversation = useCallback(
|
| 59 |
+
(conversationId: string, isNew: string) => {
|
| 60 |
+
newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
|
| 61 |
+
newQueryParameters.set(ChatSearchParams.isNew, isNew);
|
| 62 |
+
setSearchParams(newQueryParameters);
|
| 63 |
+
},
|
| 64 |
+
[setSearchParams, newQueryParameters],
|
| 65 |
+
);
|
| 66 |
+
|
| 67 |
+
return { handleClickConversation };
|
| 68 |
+
};
|
| 69 |
+
|
| 70 |
export const useGetChatSearchParams = () => {
|
| 71 |
const [currentQueryParameters] = useSearchParams();
|
| 72 |
|
|
|
|
| 74 |
dialogId: currentQueryParameters.get(ChatSearchParams.DialogId) || '',
|
| 75 |
conversationId:
|
| 76 |
currentQueryParameters.get(ChatSearchParams.ConversationId) || '',
|
| 77 |
+
isNew: currentQueryParameters.get(ChatSearchParams.isNew) || '',
|
| 78 |
};
|
| 79 |
};
|
| 80 |
|
|
|
|
| 84 |
|
| 85 |
export const useFetchNextDialogList = () => {
|
| 86 |
const { handleClickDialog } = useClickDialogCard();
|
| 87 |
+
const { dialogId } = useGetChatSearchParams();
|
| 88 |
|
| 89 |
const {
|
| 90 |
data,
|
|
|
|
| 95 |
initialData: [],
|
| 96 |
gcTime: 0,
|
| 97 |
refetchOnWindowFocus: false,
|
| 98 |
+
refetchOnMount: false,
|
| 99 |
+
queryFn: async (...params) => {
|
| 100 |
+
console.log('🚀 ~ queryFn: ~ params:', params);
|
| 101 |
const { data } = await chatService.listDialog();
|
| 102 |
|
| 103 |
+
if (data.retcode === 0) {
|
| 104 |
+
const list: IDialog[] = data.data;
|
| 105 |
+
if (list.length > 0) {
|
| 106 |
+
if (list.every((x) => x.id !== dialogId)) {
|
| 107 |
+
handleClickDialog(data.data[0].id);
|
| 108 |
+
}
|
| 109 |
+
} else {
|
| 110 |
+
history.push('/chat');
|
| 111 |
+
}
|
| 112 |
}
|
| 113 |
|
| 114 |
return data?.data ?? [];
|
|
|
|
| 120 |
|
| 121 |
export const useSetNextDialog = () => {
|
| 122 |
const queryClient = useQueryClient();
|
| 123 |
+
|
| 124 |
const {
|
| 125 |
data,
|
| 126 |
isPending: loading,
|
|
|
|
| 131 |
const { data } = await chatService.setDialog(params);
|
| 132 |
if (data.retcode === 0) {
|
| 133 |
queryClient.invalidateQueries({
|
| 134 |
+
exact: false,
|
| 135 |
queryKey: ['fetchDialogList'],
|
| 136 |
});
|
| 137 |
+
|
| 138 |
queryClient.invalidateQueries({
|
| 139 |
queryKey: ['fetchDialog'],
|
| 140 |
});
|
|
|
|
| 203 |
const { data } = await chatService.removeDialog({ dialogIds });
|
| 204 |
if (data.retcode === 0) {
|
| 205 |
queryClient.invalidateQueries({ queryKey: ['fetchDialogList'] });
|
| 206 |
+
|
| 207 |
message.success(i18n.t('message.deleted'));
|
| 208 |
}
|
| 209 |
return data.retcode;
|
|
|
|
| 219 |
|
| 220 |
export const useFetchNextConversationList = () => {
|
| 221 |
const { dialogId } = useGetChatSearchParams();
|
| 222 |
+
const { handleClickConversation } = useClickConversationCard();
|
| 223 |
const {
|
| 224 |
data,
|
| 225 |
isFetching: loading,
|
|
|
|
| 232 |
enabled: !!dialogId,
|
| 233 |
queryFn: async () => {
|
| 234 |
const { data } = await chatService.listConversation({ dialogId });
|
| 235 |
+
if (data.retcode === 0 && data.data.length > 0) {
|
| 236 |
+
handleClickConversation(data.data[0].id, '');
|
| 237 |
+
}
|
| 238 |
return data?.data;
|
| 239 |
},
|
| 240 |
});
|
|
|
|
| 243 |
};
|
| 244 |
|
| 245 |
export const useFetchNextConversation = () => {
|
| 246 |
+
const { isNew, conversationId } = useGetChatSearchParams();
|
| 247 |
const {
|
| 248 |
data,
|
| 249 |
isFetching: loading,
|
|
|
|
| 255 |
gcTime: 0,
|
| 256 |
refetchOnWindowFocus: false,
|
| 257 |
queryFn: async () => {
|
| 258 |
+
if (isNew !== 'true' && isConversationIdExist(conversationId)) {
|
| 259 |
const { data } = await chatService.getConversation({ conversationId });
|
| 260 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
const conversation = data?.data ?? {};
|
| 262 |
|
| 263 |
const messageList = buildMessageListWithUuid(conversation?.message);
|
|
|
|
| 298 |
} = useMutation({
|
| 299 |
mutationKey: ['updateConversation'],
|
| 300 |
mutationFn: async (params: Record<string, any>) => {
|
| 301 |
+
const { data } = await chatService.setConversation({
|
| 302 |
+
...params,
|
| 303 |
+
conversation_id: params.conversation_id
|
| 304 |
+
? params.conversation_id
|
| 305 |
+
: getConversationId(),
|
| 306 |
+
});
|
| 307 |
if (data.retcode === 0) {
|
| 308 |
queryClient.invalidateQueries({ queryKey: ['fetchConversationList'] });
|
| 309 |
}
|
web/src/hooks/logic-hooks.ts
CHANGED
|
@@ -224,6 +224,7 @@ export const useSendMessageWithSse = (
|
|
| 224 |
const send = useCallback(
|
| 225 |
async (
|
| 226 |
body: any,
|
|
|
|
| 227 |
): Promise<{ response: Response; data: ResponseType } | undefined> => {
|
| 228 |
try {
|
| 229 |
setDone(false);
|
|
@@ -234,6 +235,7 @@ export const useSendMessageWithSse = (
|
|
| 234 |
'Content-Type': 'application/json',
|
| 235 |
},
|
| 236 |
body: JSON.stringify(body),
|
|
|
|
| 237 |
});
|
| 238 |
|
| 239 |
const res = response.clone().json();
|
|
@@ -249,6 +251,7 @@ export const useSendMessageWithSse = (
|
|
| 249 |
const { done, value } = x;
|
| 250 |
if (done) {
|
| 251 |
console.info('done');
|
|
|
|
| 252 |
break;
|
| 253 |
}
|
| 254 |
try {
|
|
@@ -268,9 +271,12 @@ export const useSendMessageWithSse = (
|
|
| 268 |
}
|
| 269 |
console.info('done?');
|
| 270 |
setDone(true);
|
|
|
|
| 271 |
return { data: await res, response };
|
| 272 |
} catch (e) {
|
| 273 |
setDone(true);
|
|
|
|
|
|
|
| 274 |
console.warn(e);
|
| 275 |
}
|
| 276 |
},
|
|
|
|
| 224 |
const send = useCallback(
|
| 225 |
async (
|
| 226 |
body: any,
|
| 227 |
+
controller?: AbortController,
|
| 228 |
): Promise<{ response: Response; data: ResponseType } | undefined> => {
|
| 229 |
try {
|
| 230 |
setDone(false);
|
|
|
|
| 235 |
'Content-Type': 'application/json',
|
| 236 |
},
|
| 237 |
body: JSON.stringify(body),
|
| 238 |
+
signal: controller?.signal,
|
| 239 |
});
|
| 240 |
|
| 241 |
const res = response.clone().json();
|
|
|
|
| 251 |
const { done, value } = x;
|
| 252 |
if (done) {
|
| 253 |
console.info('done');
|
| 254 |
+
setAnswer({} as IAnswer);
|
| 255 |
break;
|
| 256 |
}
|
| 257 |
try {
|
|
|
|
| 271 |
}
|
| 272 |
console.info('done?');
|
| 273 |
setDone(true);
|
| 274 |
+
setAnswer({} as IAnswer);
|
| 275 |
return { data: await res, response };
|
| 276 |
} catch (e) {
|
| 277 |
setDone(true);
|
| 278 |
+
setAnswer({} as IAnswer);
|
| 279 |
+
|
| 280 |
console.warn(e);
|
| 281 |
}
|
| 282 |
},
|
web/src/interfaces/database/chat.ts
CHANGED
|
@@ -63,6 +63,7 @@ export interface IConversation {
|
|
| 63 |
name: string;
|
| 64 |
update_date: string;
|
| 65 |
update_time: number;
|
|
|
|
| 66 |
}
|
| 67 |
|
| 68 |
export interface Message {
|
|
|
|
| 63 |
name: string;
|
| 64 |
update_date: string;
|
| 65 |
update_time: number;
|
| 66 |
+
is_new: true;
|
| 67 |
}
|
| 68 |
|
| 69 |
export interface Message {
|
web/src/locales/en.ts
CHANGED
|
@@ -580,7 +580,7 @@ The above is the content you need to summarize.`,
|
|
| 580 |
addGoogleRegion: 'Google Cloud Region',
|
| 581 |
GoogleRegionMessage: 'Please input Google Cloud Region',
|
| 582 |
modelProvidersWarn:
|
| 583 |
-
'Please add both embedding model and LLM in <b>Settings > Model</b>
|
| 584 |
},
|
| 585 |
message: {
|
| 586 |
registered: 'Registered!',
|
|
|
|
| 580 |
addGoogleRegion: 'Google Cloud Region',
|
| 581 |
GoogleRegionMessage: 'Please input Google Cloud Region',
|
| 582 |
modelProvidersWarn:
|
| 583 |
+
'Please add both embedding model and LLM in <b>Settings > Model providers</b> firstly.',
|
| 584 |
},
|
| 585 |
message: {
|
| 586 |
registered: 'Registered!',
|
web/src/pages/chat/chat-container/index.tsx
CHANGED
|
@@ -19,10 +19,13 @@ import {
|
|
| 19 |
} from '@/hooks/chat-hooks';
|
| 20 |
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
| 21 |
import { memo } from 'react';
|
| 22 |
-
import { ConversationContext } from '../context';
|
| 23 |
import styles from './index.less';
|
| 24 |
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
const { conversationId } = useGetChatSearchParams();
|
| 27 |
const { data: conversation } = useFetchNextConversation();
|
| 28 |
|
|
@@ -36,8 +39,7 @@ const ChatContainer = () => {
|
|
| 36 |
handlePressEnter,
|
| 37 |
regenerateMessage,
|
| 38 |
removeMessageById,
|
| 39 |
-
|
| 40 |
-
} = useSendNextMessage();
|
| 41 |
|
| 42 |
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
| 43 |
useClickDrawer();
|
|
@@ -54,35 +56,33 @@ const ChatContainer = () => {
|
|
| 54 |
<Flex flex={1} vertical className={styles.messageContainer}>
|
| 55 |
<div>
|
| 56 |
<Spin spinning={loading}>
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
})}
|
| 85 |
-
</ConversationContext.Provider>
|
| 86 |
</Spin>
|
| 87 |
</div>
|
| 88 |
<div ref={ref} />
|
|
|
|
| 19 |
} from '@/hooks/chat-hooks';
|
| 20 |
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
| 21 |
import { memo } from 'react';
|
|
|
|
| 22 |
import styles from './index.less';
|
| 23 |
|
| 24 |
+
interface IProps {
|
| 25 |
+
controller: AbortController;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
const ChatContainer = ({ controller }: IProps) => {
|
| 29 |
const { conversationId } = useGetChatSearchParams();
|
| 30 |
const { data: conversation } = useFetchNextConversation();
|
| 31 |
|
|
|
|
| 39 |
handlePressEnter,
|
| 40 |
regenerateMessage,
|
| 41 |
removeMessageById,
|
| 42 |
+
} = useSendNextMessage(controller);
|
|
|
|
| 43 |
|
| 44 |
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
| 45 |
useClickDrawer();
|
|
|
|
| 56 |
<Flex flex={1} vertical className={styles.messageContainer}>
|
| 57 |
<div>
|
| 58 |
<Spin spinning={loading}>
|
| 59 |
+
{derivedMessages?.map((message, i) => {
|
| 60 |
+
return (
|
| 61 |
+
<MessageItem
|
| 62 |
+
loading={
|
| 63 |
+
message.role === MessageType.Assistant &&
|
| 64 |
+
sendLoading &&
|
| 65 |
+
derivedMessages.length - 1 === i
|
| 66 |
+
}
|
| 67 |
+
key={message.id}
|
| 68 |
+
item={message}
|
| 69 |
+
nickname={userInfo.nickname}
|
| 70 |
+
avatar={userInfo.avatar}
|
| 71 |
+
reference={buildMessageItemReference(
|
| 72 |
+
{
|
| 73 |
+
message: derivedMessages,
|
| 74 |
+
reference: conversation.reference,
|
| 75 |
+
},
|
| 76 |
+
message,
|
| 77 |
+
)}
|
| 78 |
+
clickDocumentButton={clickDocumentButton}
|
| 79 |
+
index={i}
|
| 80 |
+
removeMessageById={removeMessageById}
|
| 81 |
+
regenerateMessage={regenerateMessage}
|
| 82 |
+
sendLoading={sendLoading}
|
| 83 |
+
></MessageItem>
|
| 84 |
+
);
|
| 85 |
+
})}
|
|
|
|
|
|
|
| 86 |
</Spin>
|
| 87 |
</div>
|
| 88 |
<div ref={ref} />
|
web/src/pages/chat/constants.ts
CHANGED
|
@@ -1,6 +1 @@
|
|
| 1 |
-
export enum ChatSearchParams {
|
| 2 |
-
DialogId = 'dialogId',
|
| 3 |
-
ConversationId = 'conversationId',
|
| 4 |
-
}
|
| 5 |
-
|
| 6 |
export const EmptyConversationId = 'empty';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
export const EmptyConversationId = 'empty';
|
web/src/pages/chat/hooks.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { MessageType } from '@/constants/chat';
|
| 2 |
import { fileIconMap } from '@/constants/common';
|
| 3 |
import {
|
| 4 |
useFetchManualConversation,
|
|
@@ -24,6 +24,8 @@ import {
|
|
| 24 |
} from '@/hooks/logic-hooks';
|
| 25 |
import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
|
| 26 |
import { getFileExtension } from '@/utils';
|
|
|
|
|
|
|
| 27 |
import { useMutationState } from '@tanstack/react-query';
|
| 28 |
import { get } from 'lodash';
|
| 29 |
import trim from 'lodash/trim';
|
|
@@ -32,18 +34,57 @@ import {
|
|
| 32 |
useCallback,
|
| 33 |
useEffect,
|
| 34 |
useMemo,
|
| 35 |
-
useRef,
|
| 36 |
useState,
|
| 37 |
} from 'react';
|
| 38 |
import { useSearchParams } from 'umi';
|
| 39 |
import { v4 as uuid } from 'uuid';
|
| 40 |
-
import { ChatSearchParams } from './constants';
|
| 41 |
import {
|
| 42 |
IClientConversation,
|
| 43 |
IMessage,
|
| 44 |
VariableTableDataType,
|
| 45 |
} from './interface';
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
export const useSelectCurrentDialog = () => {
|
| 48 |
const data = useMutationState({
|
| 49 |
filters: { mutationKey: ['fetchDialog'] },
|
|
@@ -169,22 +210,26 @@ export const useSelectDerivedConversationList = () => {
|
|
| 169 |
const { data: conversationList, loading } = useFetchNextConversationList();
|
| 170 |
const { dialogId } = useGetChatSearchParams();
|
| 171 |
const prologue = currentDialog?.prompt_config?.prologue ?? '';
|
|
|
|
| 172 |
|
| 173 |
const addTemporaryConversation = useCallback(() => {
|
|
|
|
| 174 |
setList((pre) => {
|
| 175 |
if (dialogId) {
|
|
|
|
| 176 |
const nextList = [
|
| 177 |
{
|
| 178 |
-
id:
|
| 179 |
name: t('newConversation'),
|
| 180 |
dialog_id: dialogId,
|
|
|
|
| 181 |
message: [
|
| 182 |
{
|
| 183 |
content: prologue,
|
| 184 |
role: MessageType.Assistant,
|
| 185 |
},
|
| 186 |
],
|
| 187 |
-
} as
|
| 188 |
...conversationList,
|
| 189 |
];
|
| 190 |
return nextList;
|
|
@@ -192,42 +237,32 @@ export const useSelectDerivedConversationList = () => {
|
|
| 192 |
|
| 193 |
return pre;
|
| 194 |
});
|
| 195 |
-
}, [conversationList, dialogId, prologue, t]);
|
|
|
|
|
|
|
| 196 |
|
| 197 |
useEffect(() => {
|
| 198 |
-
|
| 199 |
-
}, [
|
| 200 |
|
| 201 |
return { list, addTemporaryConversation, loading };
|
| 202 |
};
|
| 203 |
|
| 204 |
-
export const useClickConversationCard = () => {
|
| 205 |
-
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
| 206 |
-
const newQueryParameters: URLSearchParams = useMemo(
|
| 207 |
-
() => new URLSearchParams(currentQueryParameters.toString()),
|
| 208 |
-
[currentQueryParameters],
|
| 209 |
-
);
|
| 210 |
-
|
| 211 |
-
const handleClickConversation = useCallback(
|
| 212 |
-
(conversationId: string) => {
|
| 213 |
-
newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
|
| 214 |
-
setSearchParams(newQueryParameters);
|
| 215 |
-
},
|
| 216 |
-
[newQueryParameters, setSearchParams],
|
| 217 |
-
);
|
| 218 |
-
|
| 219 |
-
return { handleClickConversation };
|
| 220 |
-
};
|
| 221 |
-
|
| 222 |
export const useSetConversation = () => {
|
| 223 |
const { dialogId } = useGetChatSearchParams();
|
| 224 |
const { updateConversation } = useUpdateNextConversation();
|
| 225 |
|
| 226 |
const setConversation = useCallback(
|
| 227 |
-
(
|
| 228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
dialog_id: dialogId,
|
| 230 |
name: message,
|
|
|
|
|
|
|
| 231 |
message: [
|
| 232 |
{
|
| 233 |
role: MessageType.Assistant,
|
|
@@ -235,6 +270,8 @@ export const useSetConversation = () => {
|
|
| 235 |
},
|
| 236 |
],
|
| 237 |
});
|
|
|
|
|
|
|
| 238 |
},
|
| 239 |
[updateConversation, dialogId],
|
| 240 |
);
|
|
@@ -242,22 +279,6 @@ export const useSetConversation = () => {
|
|
| 242 |
return { setConversation };
|
| 243 |
};
|
| 244 |
|
| 245 |
-
// export const useScrollToBottom = (currentConversation: IClientConversation) => {
|
| 246 |
-
// const ref = useRef<HTMLDivElement>(null);
|
| 247 |
-
|
| 248 |
-
// const scrollToBottom = useCallback(() => {
|
| 249 |
-
// if (currentConversation.id) {
|
| 250 |
-
// ref.current?.scrollIntoView({ behavior: 'instant' });
|
| 251 |
-
// }
|
| 252 |
-
// }, [currentConversation]);
|
| 253 |
-
|
| 254 |
-
// useEffect(() => {
|
| 255 |
-
// scrollToBottom();
|
| 256 |
-
// }, [scrollToBottom]);
|
| 257 |
-
|
| 258 |
-
// return ref;
|
| 259 |
-
// };
|
| 260 |
-
|
| 261 |
export const useSelectNextMessages = () => {
|
| 262 |
const {
|
| 263 |
ref,
|
|
@@ -271,10 +292,10 @@ export const useSelectNextMessages = () => {
|
|
| 271 |
} = useSelectDerivedMessages();
|
| 272 |
const { data: conversation, loading } = useFetchNextConversation();
|
| 273 |
const { data: dialog } = useFetchNextDialog();
|
| 274 |
-
const { conversationId, dialogId } = useGetChatSearchParams();
|
| 275 |
|
| 276 |
const addPrologue = useCallback(() => {
|
| 277 |
-
if (dialogId !== '' &&
|
| 278 |
const prologue = dialog.prompt_config?.prologue;
|
| 279 |
|
| 280 |
const nextMessage = {
|
|
@@ -285,17 +306,25 @@ export const useSelectNextMessages = () => {
|
|
| 285 |
|
| 286 |
setDerivedMessages([nextMessage]);
|
| 287 |
}
|
| 288 |
-
}, [
|
| 289 |
|
| 290 |
useEffect(() => {
|
| 291 |
addPrologue();
|
| 292 |
}, [addPrologue]);
|
| 293 |
|
| 294 |
useEffect(() => {
|
| 295 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
setDerivedMessages(conversation.message);
|
| 297 |
}
|
| 298 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
|
| 300 |
return {
|
| 301 |
ref,
|
|
@@ -325,12 +354,14 @@ export const useHandleMessageInputChange = () => {
|
|
| 325 |
};
|
| 326 |
};
|
| 327 |
|
| 328 |
-
export const useSendNextMessage = () => {
|
| 329 |
const { setConversation } = useSetConversation();
|
| 330 |
-
const { conversationId } = useGetChatSearchParams();
|
| 331 |
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
| 332 |
-
|
| 333 |
-
const { send, answer, done
|
|
|
|
|
|
|
| 334 |
const {
|
| 335 |
ref,
|
| 336 |
derivedMessages,
|
|
@@ -341,17 +372,8 @@ export const useSendNextMessage = () => {
|
|
| 341 |
removeMessageById,
|
| 342 |
removeMessagesAfterCurrentMessage,
|
| 343 |
} = useSelectNextMessages();
|
| 344 |
-
const {
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
const redirectToNewConversation = useCallback(
|
| 348 |
-
(isPlaying: boolean) => {
|
| 349 |
-
if (!conversationId && dialog?.prompt_config?.tts && !isPlaying) {
|
| 350 |
-
handleClickConversation(currentConversationIdRef.current);
|
| 351 |
-
}
|
| 352 |
-
},
|
| 353 |
-
[dialog, handleClickConversation, conversationId],
|
| 354 |
-
);
|
| 355 |
|
| 356 |
const sendMessage = useCallback(
|
| 357 |
async ({
|
|
@@ -363,49 +385,46 @@ export const useSendNextMessage = () => {
|
|
| 363 |
currentConversationId?: string;
|
| 364 |
messages?: Message[];
|
| 365 |
}) => {
|
| 366 |
-
const res = await send(
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
|
|
|
|
|
|
|
|
|
| 370 |
|
| 371 |
if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) {
|
| 372 |
// cancel loading
|
| 373 |
setValue(message.content);
|
| 374 |
console.info('removeLatestMessage111');
|
| 375 |
removeLatestMessage();
|
| 376 |
-
} else {
|
| 377 |
-
if (currentConversationId) {
|
| 378 |
-
console.info('111');
|
| 379 |
-
// new conversation
|
| 380 |
-
if (!dialog?.prompt_config?.tts) {
|
| 381 |
-
handleClickConversation(currentConversationId);
|
| 382 |
-
}
|
| 383 |
-
} else {
|
| 384 |
-
console.info('222');
|
| 385 |
-
// fetchConversation(conversationId);
|
| 386 |
-
}
|
| 387 |
}
|
| 388 |
},
|
| 389 |
[
|
| 390 |
-
dialog,
|
| 391 |
derivedMessages,
|
| 392 |
conversationId,
|
| 393 |
-
handleClickConversation,
|
| 394 |
removeLatestMessage,
|
| 395 |
setValue,
|
| 396 |
send,
|
|
|
|
| 397 |
],
|
| 398 |
);
|
| 399 |
|
| 400 |
const handleSendMessage = useCallback(
|
| 401 |
async (message: Message) => {
|
| 402 |
-
|
|
|
|
| 403 |
sendMessage({ message });
|
| 404 |
} else {
|
| 405 |
-
const data = await setConversation(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 406 |
if (data.retcode === 0) {
|
|
|
|
| 407 |
const id = data.data.id;
|
| 408 |
-
currentConversationIdRef.current = id;
|
| 409 |
sendMessage({
|
| 410 |
message,
|
| 411 |
currentConversationId: id,
|
|
@@ -414,7 +433,13 @@ export const useSendNextMessage = () => {
|
|
| 414 |
}
|
| 415 |
}
|
| 416 |
},
|
| 417 |
-
[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
);
|
| 419 |
|
| 420 |
const { regenerateMessage } = useRegenerateMessage({
|
|
@@ -425,24 +450,10 @@ export const useSendNextMessage = () => {
|
|
| 425 |
|
| 426 |
useEffect(() => {
|
| 427 |
// #1289
|
| 428 |
-
|
| 429 |
-
if (
|
| 430 |
-
answer.answer &&
|
| 431 |
-
(answer?.conversationId === conversationId ||
|
| 432 |
-
((!done || (done && answer.audio_binary)) && conversationId === ''))
|
| 433 |
-
) {
|
| 434 |
addNewestAnswer(answer);
|
| 435 |
}
|
| 436 |
-
}, [answer, addNewestAnswer, conversationId,
|
| 437 |
-
|
| 438 |
-
useEffect(() => {
|
| 439 |
-
// #1289 switch to another conversion window when the last conversion answer doesn't finish.
|
| 440 |
-
if (conversationId) {
|
| 441 |
-
setDone(true);
|
| 442 |
-
} else {
|
| 443 |
-
resetAnswer();
|
| 444 |
-
}
|
| 445 |
-
}, [setDone, conversationId, resetAnswer]);
|
| 446 |
|
| 447 |
const handlePressEnter = useCallback(
|
| 448 |
(documentIds: string[]) => {
|
|
@@ -479,7 +490,6 @@ export const useSendNextMessage = () => {
|
|
| 479 |
ref,
|
| 480 |
derivedMessages,
|
| 481 |
removeMessageById,
|
| 482 |
-
redirectToNewConversation,
|
| 483 |
};
|
| 484 |
};
|
| 485 |
|
|
@@ -494,15 +504,12 @@ export const useGetFileIcon = () => {
|
|
| 494 |
};
|
| 495 |
|
| 496 |
export const useDeleteConversation = () => {
|
| 497 |
-
const { handleClickConversation } = useClickConversationCard();
|
| 498 |
const showDeleteConfirm = useShowDeleteConfirm();
|
| 499 |
const { removeConversation } = useRemoveNextConversation();
|
| 500 |
|
| 501 |
const deleteConversation = (conversationIds: Array<string>) => async () => {
|
| 502 |
const ret = await removeConversation(conversationIds);
|
| 503 |
-
|
| 504 |
-
handleClickConversation('');
|
| 505 |
-
}
|
| 506 |
return ret;
|
| 507 |
};
|
| 508 |
|
|
@@ -531,6 +538,7 @@ export const useRenameConversation = () => {
|
|
| 531 |
...conversation,
|
| 532 |
conversation_id: conversation.id,
|
| 533 |
name,
|
|
|
|
| 534 |
});
|
| 535 |
|
| 536 |
if (ret.retcode === 0) {
|
|
@@ -564,7 +572,7 @@ export const useRenameConversation = () => {
|
|
| 564 |
export const useGetSendButtonDisabled = () => {
|
| 565 |
const { dialogId, conversationId } = useGetChatSearchParams();
|
| 566 |
|
| 567 |
-
return dialogId === ''
|
| 568 |
};
|
| 569 |
|
| 570 |
export const useSendButtonDisabled = (value: string) => {
|
|
@@ -575,18 +583,13 @@ export const useCreateConversationBeforeUploadDocument = () => {
|
|
| 575 |
const { setConversation } = useSetConversation();
|
| 576 |
const { dialogId } = useGetChatSearchParams();
|
| 577 |
|
| 578 |
-
const { handleClickConversation } = useClickConversationCard();
|
| 579 |
-
|
| 580 |
const createConversationBeforeUploadDocument = useCallback(
|
| 581 |
async (message: string) => {
|
| 582 |
-
const data = await setConversation(message);
|
| 583 |
-
|
| 584 |
-
const id = data.data.id;
|
| 585 |
-
handleClickConversation(id);
|
| 586 |
-
}
|
| 587 |
return data;
|
| 588 |
},
|
| 589 |
-
[setConversation
|
| 590 |
);
|
| 591 |
|
| 592 |
return {
|
|
|
|
| 1 |
+
import { ChatSearchParams, MessageType } from '@/constants/chat';
|
| 2 |
import { fileIconMap } from '@/constants/common';
|
| 3 |
import {
|
| 4 |
useFetchManualConversation,
|
|
|
|
| 24 |
} from '@/hooks/logic-hooks';
|
| 25 |
import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
|
| 26 |
import { getFileExtension } from '@/utils';
|
| 27 |
+
import api from '@/utils/api';
|
| 28 |
+
import { getConversationId } from '@/utils/chat';
|
| 29 |
import { useMutationState } from '@tanstack/react-query';
|
| 30 |
import { get } from 'lodash';
|
| 31 |
import trim from 'lodash/trim';
|
|
|
|
| 34 |
useCallback,
|
| 35 |
useEffect,
|
| 36 |
useMemo,
|
|
|
|
| 37 |
useState,
|
| 38 |
} from 'react';
|
| 39 |
import { useSearchParams } from 'umi';
|
| 40 |
import { v4 as uuid } from 'uuid';
|
|
|
|
| 41 |
import {
|
| 42 |
IClientConversation,
|
| 43 |
IMessage,
|
| 44 |
VariableTableDataType,
|
| 45 |
} from './interface';
|
| 46 |
|
| 47 |
+
export const useSetChatRouteParams = () => {
|
| 48 |
+
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
| 49 |
+
const newQueryParameters: URLSearchParams = useMemo(
|
| 50 |
+
() => new URLSearchParams(currentQueryParameters.toString()),
|
| 51 |
+
[currentQueryParameters],
|
| 52 |
+
);
|
| 53 |
+
|
| 54 |
+
const setConversationIsNew = useCallback(
|
| 55 |
+
(value: string) => {
|
| 56 |
+
newQueryParameters.set(ChatSearchParams.isNew, value);
|
| 57 |
+
setSearchParams(newQueryParameters);
|
| 58 |
+
},
|
| 59 |
+
[newQueryParameters, setSearchParams],
|
| 60 |
+
);
|
| 61 |
+
|
| 62 |
+
const getConversationIsNew = useCallback(() => {
|
| 63 |
+
return newQueryParameters.get(ChatSearchParams.isNew);
|
| 64 |
+
}, [newQueryParameters]);
|
| 65 |
+
|
| 66 |
+
return { setConversationIsNew, getConversationIsNew };
|
| 67 |
+
};
|
| 68 |
+
|
| 69 |
+
export const useSetNewConversationRouteParams = () => {
|
| 70 |
+
const [currentQueryParameters, setSearchParams] = useSearchParams();
|
| 71 |
+
const newQueryParameters: URLSearchParams = useMemo(
|
| 72 |
+
() => new URLSearchParams(currentQueryParameters.toString()),
|
| 73 |
+
[currentQueryParameters],
|
| 74 |
+
);
|
| 75 |
+
|
| 76 |
+
const setNewConversationRouteParams = useCallback(
|
| 77 |
+
(conversationId: string, isNew: string) => {
|
| 78 |
+
newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
|
| 79 |
+
newQueryParameters.set(ChatSearchParams.isNew, isNew);
|
| 80 |
+
setSearchParams(newQueryParameters);
|
| 81 |
+
},
|
| 82 |
+
[newQueryParameters, setSearchParams],
|
| 83 |
+
);
|
| 84 |
+
|
| 85 |
+
return { setNewConversationRouteParams };
|
| 86 |
+
};
|
| 87 |
+
|
| 88 |
export const useSelectCurrentDialog = () => {
|
| 89 |
const data = useMutationState({
|
| 90 |
filters: { mutationKey: ['fetchDialog'] },
|
|
|
|
| 210 |
const { data: conversationList, loading } = useFetchNextConversationList();
|
| 211 |
const { dialogId } = useGetChatSearchParams();
|
| 212 |
const prologue = currentDialog?.prompt_config?.prologue ?? '';
|
| 213 |
+
const { setNewConversationRouteParams } = useSetNewConversationRouteParams();
|
| 214 |
|
| 215 |
const addTemporaryConversation = useCallback(() => {
|
| 216 |
+
const conversationId = getConversationId();
|
| 217 |
setList((pre) => {
|
| 218 |
if (dialogId) {
|
| 219 |
+
setNewConversationRouteParams(conversationId, 'true');
|
| 220 |
const nextList = [
|
| 221 |
{
|
| 222 |
+
id: conversationId,
|
| 223 |
name: t('newConversation'),
|
| 224 |
dialog_id: dialogId,
|
| 225 |
+
is_new: true,
|
| 226 |
message: [
|
| 227 |
{
|
| 228 |
content: prologue,
|
| 229 |
role: MessageType.Assistant,
|
| 230 |
},
|
| 231 |
],
|
| 232 |
+
} as any,
|
| 233 |
...conversationList,
|
| 234 |
];
|
| 235 |
return nextList;
|
|
|
|
| 237 |
|
| 238 |
return pre;
|
| 239 |
});
|
| 240 |
+
}, [conversationList, dialogId, prologue, t, setNewConversationRouteParams]);
|
| 241 |
+
|
| 242 |
+
// When you first enter the page, select the top conversation card
|
| 243 |
|
| 244 |
useEffect(() => {
|
| 245 |
+
setList([...conversationList]);
|
| 246 |
+
}, [conversationList]);
|
| 247 |
|
| 248 |
return { list, addTemporaryConversation, loading };
|
| 249 |
};
|
| 250 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
export const useSetConversation = () => {
|
| 252 |
const { dialogId } = useGetChatSearchParams();
|
| 253 |
const { updateConversation } = useUpdateNextConversation();
|
| 254 |
|
| 255 |
const setConversation = useCallback(
|
| 256 |
+
async (
|
| 257 |
+
message: string,
|
| 258 |
+
isNew: boolean = false,
|
| 259 |
+
conversationId?: string,
|
| 260 |
+
) => {
|
| 261 |
+
const data = await updateConversation({
|
| 262 |
dialog_id: dialogId,
|
| 263 |
name: message,
|
| 264 |
+
is_new: isNew,
|
| 265 |
+
conversation_id: conversationId,
|
| 266 |
message: [
|
| 267 |
{
|
| 268 |
role: MessageType.Assistant,
|
|
|
|
| 270 |
},
|
| 271 |
],
|
| 272 |
});
|
| 273 |
+
|
| 274 |
+
return data;
|
| 275 |
},
|
| 276 |
[updateConversation, dialogId],
|
| 277 |
);
|
|
|
|
| 279 |
return { setConversation };
|
| 280 |
};
|
| 281 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
export const useSelectNextMessages = () => {
|
| 283 |
const {
|
| 284 |
ref,
|
|
|
|
| 292 |
} = useSelectDerivedMessages();
|
| 293 |
const { data: conversation, loading } = useFetchNextConversation();
|
| 294 |
const { data: dialog } = useFetchNextDialog();
|
| 295 |
+
const { conversationId, dialogId, isNew } = useGetChatSearchParams();
|
| 296 |
|
| 297 |
const addPrologue = useCallback(() => {
|
| 298 |
+
if (dialogId !== '' && isNew === 'true') {
|
| 299 |
const prologue = dialog.prompt_config?.prologue;
|
| 300 |
|
| 301 |
const nextMessage = {
|
|
|
|
| 306 |
|
| 307 |
setDerivedMessages([nextMessage]);
|
| 308 |
}
|
| 309 |
+
}, [isNew, dialog, dialogId, setDerivedMessages]);
|
| 310 |
|
| 311 |
useEffect(() => {
|
| 312 |
addPrologue();
|
| 313 |
}, [addPrologue]);
|
| 314 |
|
| 315 |
useEffect(() => {
|
| 316 |
+
if (
|
| 317 |
+
conversationId &&
|
| 318 |
+
isNew !== 'true' &&
|
| 319 |
+
conversation.message?.length > 0
|
| 320 |
+
) {
|
| 321 |
setDerivedMessages(conversation.message);
|
| 322 |
}
|
| 323 |
+
|
| 324 |
+
if (!conversationId) {
|
| 325 |
+
setDerivedMessages([]);
|
| 326 |
+
}
|
| 327 |
+
}, [conversation.message, conversationId, setDerivedMessages, isNew]);
|
| 328 |
|
| 329 |
return {
|
| 330 |
ref,
|
|
|
|
| 354 |
};
|
| 355 |
};
|
| 356 |
|
| 357 |
+
export const useSendNextMessage = (controller: AbortController) => {
|
| 358 |
const { setConversation } = useSetConversation();
|
| 359 |
+
const { conversationId, isNew } = useGetChatSearchParams();
|
| 360 |
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
| 361 |
+
|
| 362 |
+
const { send, answer, done } = useSendMessageWithSse(
|
| 363 |
+
api.completeConversation,
|
| 364 |
+
);
|
| 365 |
const {
|
| 366 |
ref,
|
| 367 |
derivedMessages,
|
|
|
|
| 372 |
removeMessageById,
|
| 373 |
removeMessagesAfterCurrentMessage,
|
| 374 |
} = useSelectNextMessages();
|
| 375 |
+
const { setConversationIsNew, getConversationIsNew } =
|
| 376 |
+
useSetChatRouteParams();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
|
| 378 |
const sendMessage = useCallback(
|
| 379 |
async ({
|
|
|
|
| 385 |
currentConversationId?: string;
|
| 386 |
messages?: Message[];
|
| 387 |
}) => {
|
| 388 |
+
const res = await send(
|
| 389 |
+
{
|
| 390 |
+
conversation_id: currentConversationId ?? conversationId,
|
| 391 |
+
messages: [...(messages ?? derivedMessages ?? []), message],
|
| 392 |
+
},
|
| 393 |
+
controller,
|
| 394 |
+
);
|
| 395 |
|
| 396 |
if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) {
|
| 397 |
// cancel loading
|
| 398 |
setValue(message.content);
|
| 399 |
console.info('removeLatestMessage111');
|
| 400 |
removeLatestMessage();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
}
|
| 402 |
},
|
| 403 |
[
|
|
|
|
| 404 |
derivedMessages,
|
| 405 |
conversationId,
|
|
|
|
| 406 |
removeLatestMessage,
|
| 407 |
setValue,
|
| 408 |
send,
|
| 409 |
+
controller,
|
| 410 |
],
|
| 411 |
);
|
| 412 |
|
| 413 |
const handleSendMessage = useCallback(
|
| 414 |
async (message: Message) => {
|
| 415 |
+
const isNew = getConversationIsNew();
|
| 416 |
+
if (isNew !== 'true') {
|
| 417 |
sendMessage({ message });
|
| 418 |
} else {
|
| 419 |
+
const data = await setConversation(
|
| 420 |
+
message.content,
|
| 421 |
+
true,
|
| 422 |
+
conversationId,
|
| 423 |
+
);
|
| 424 |
if (data.retcode === 0) {
|
| 425 |
+
setConversationIsNew('');
|
| 426 |
const id = data.data.id;
|
| 427 |
+
// currentConversationIdRef.current = id;
|
| 428 |
sendMessage({
|
| 429 |
message,
|
| 430 |
currentConversationId: id,
|
|
|
|
| 433 |
}
|
| 434 |
}
|
| 435 |
},
|
| 436 |
+
[
|
| 437 |
+
setConversation,
|
| 438 |
+
sendMessage,
|
| 439 |
+
setConversationIsNew,
|
| 440 |
+
getConversationIsNew,
|
| 441 |
+
conversationId,
|
| 442 |
+
],
|
| 443 |
);
|
| 444 |
|
| 445 |
const { regenerateMessage } = useRegenerateMessage({
|
|
|
|
| 450 |
|
| 451 |
useEffect(() => {
|
| 452 |
// #1289
|
| 453 |
+
if (answer.answer && conversationId && isNew !== 'true') {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
addNewestAnswer(answer);
|
| 455 |
}
|
| 456 |
+
}, [answer, addNewestAnswer, conversationId, isNew]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
|
| 458 |
const handlePressEnter = useCallback(
|
| 459 |
(documentIds: string[]) => {
|
|
|
|
| 490 |
ref,
|
| 491 |
derivedMessages,
|
| 492 |
removeMessageById,
|
|
|
|
| 493 |
};
|
| 494 |
};
|
| 495 |
|
|
|
|
| 504 |
};
|
| 505 |
|
| 506 |
export const useDeleteConversation = () => {
|
|
|
|
| 507 |
const showDeleteConfirm = useShowDeleteConfirm();
|
| 508 |
const { removeConversation } = useRemoveNextConversation();
|
| 509 |
|
| 510 |
const deleteConversation = (conversationIds: Array<string>) => async () => {
|
| 511 |
const ret = await removeConversation(conversationIds);
|
| 512 |
+
|
|
|
|
|
|
|
| 513 |
return ret;
|
| 514 |
};
|
| 515 |
|
|
|
|
| 538 |
...conversation,
|
| 539 |
conversation_id: conversation.id,
|
| 540 |
name,
|
| 541 |
+
is_new: false,
|
| 542 |
});
|
| 543 |
|
| 544 |
if (ret.retcode === 0) {
|
|
|
|
| 572 |
export const useGetSendButtonDisabled = () => {
|
| 573 |
const { dialogId, conversationId } = useGetChatSearchParams();
|
| 574 |
|
| 575 |
+
return dialogId === '' || conversationId === '';
|
| 576 |
};
|
| 577 |
|
| 578 |
export const useSendButtonDisabled = (value: string) => {
|
|
|
|
| 583 |
const { setConversation } = useSetConversation();
|
| 584 |
const { dialogId } = useGetChatSearchParams();
|
| 585 |
|
|
|
|
|
|
|
| 586 |
const createConversationBeforeUploadDocument = useCallback(
|
| 587 |
async (message: string) => {
|
| 588 |
+
const data = await setConversation(message, true);
|
| 589 |
+
|
|
|
|
|
|
|
|
|
|
| 590 |
return data;
|
| 591 |
},
|
| 592 |
+
[setConversation],
|
| 593 |
);
|
| 594 |
|
| 595 |
return {
|
web/src/pages/chat/index.tsx
CHANGED
|
@@ -17,15 +17,15 @@ import {
|
|
| 17 |
Space,
|
| 18 |
Spin,
|
| 19 |
Tag,
|
|
|
|
| 20 |
Typography,
|
| 21 |
} from 'antd';
|
| 22 |
import { MenuItemProps } from 'antd/lib/menu/MenuItem';
|
| 23 |
import classNames from 'classnames';
|
| 24 |
-
import { useCallback } from 'react';
|
| 25 |
import ChatConfigurationModal from './chat-configuration-modal';
|
| 26 |
import ChatContainer from './chat-container';
|
| 27 |
import {
|
| 28 |
-
useClickConversationCard,
|
| 29 |
useDeleteConversation,
|
| 30 |
useDeleteDialog,
|
| 31 |
useEditDialog,
|
|
@@ -36,6 +36,7 @@ import {
|
|
| 36 |
|
| 37 |
import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
|
| 38 |
import {
|
|
|
|
| 39 |
useClickDialogCard,
|
| 40 |
useFetchNextDialogList,
|
| 41 |
useGetChatSearchParams,
|
|
@@ -89,6 +90,7 @@ const Chat = () => {
|
|
| 89 |
showModal: showOverviewModal,
|
| 90 |
} = useSetModalState();
|
| 91 |
const { currentRecord, setRecord } = useSetSelectedRecord<IDialog>();
|
|
|
|
| 92 |
|
| 93 |
const handleAppCardEnter = (id: string) => () => {
|
| 94 |
handleItemEnter(id);
|
|
@@ -139,31 +141,28 @@ const Chat = () => {
|
|
| 139 |
showConversationRenameModal(conversationId);
|
| 140 |
};
|
| 141 |
|
| 142 |
-
const handleDialogCardClick = (
|
| 143 |
-
|
| 144 |
-
|
|
|
|
|
|
|
|
|
|
| 145 |
|
| 146 |
-
const handleConversationCardClick = (
|
| 147 |
-
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
const handleCreateTemporaryConversation = useCallback(() => {
|
| 151 |
addTemporaryConversation();
|
| 152 |
}, [addTemporaryConversation]);
|
| 153 |
|
| 154 |
-
const items: MenuProps['items'] = [
|
| 155 |
-
{
|
| 156 |
-
key: '1',
|
| 157 |
-
onClick: handleCreateTemporaryConversation,
|
| 158 |
-
label: (
|
| 159 |
-
<Space>
|
| 160 |
-
<PlusOutlined />
|
| 161 |
-
{t('newChat')}
|
| 162 |
-
</Space>
|
| 163 |
-
),
|
| 164 |
-
},
|
| 165 |
-
];
|
| 166 |
-
|
| 167 |
const buildAppItems = (dialog: IDialog) => {
|
| 168 |
const dialogId = dialog.id;
|
| 169 |
|
|
@@ -297,10 +296,9 @@ const Chat = () => {
|
|
| 297 |
<b>{t('chat')}</b>
|
| 298 |
<Tag>{conversationList.length}</Tag>
|
| 299 |
</Space>
|
| 300 |
-
<
|
| 301 |
-
{
|
| 302 |
-
|
| 303 |
-
</Dropdown>
|
| 304 |
</Flex>
|
| 305 |
<Divider></Divider>
|
| 306 |
<Flex vertical gap={10} className={styles.chatTitleContent}>
|
|
@@ -312,7 +310,7 @@ const Chat = () => {
|
|
| 312 |
<Card
|
| 313 |
key={x.id}
|
| 314 |
hoverable
|
| 315 |
-
onClick={handleConversationCardClick(x.id)}
|
| 316 |
onMouseEnter={handleConversationCardEnter(x.id)}
|
| 317 |
onMouseLeave={handleConversationItemLeave}
|
| 318 |
className={classNames(styles.chatTitleCard, {
|
|
@@ -347,7 +345,7 @@ const Chat = () => {
|
|
| 347 |
</Flex>
|
| 348 |
</Flex>
|
| 349 |
<Divider type={'vertical'} className={styles.divider}></Divider>
|
| 350 |
-
<ChatContainer></ChatContainer>
|
| 351 |
{dialogEditVisible && (
|
| 352 |
<ChatConfigurationModal
|
| 353 |
visible={dialogEditVisible}
|
|
|
|
| 17 |
Space,
|
| 18 |
Spin,
|
| 19 |
Tag,
|
| 20 |
+
Tooltip,
|
| 21 |
Typography,
|
| 22 |
} from 'antd';
|
| 23 |
import { MenuItemProps } from 'antd/lib/menu/MenuItem';
|
| 24 |
import classNames from 'classnames';
|
| 25 |
+
import { useCallback, useState } from 'react';
|
| 26 |
import ChatConfigurationModal from './chat-configuration-modal';
|
| 27 |
import ChatContainer from './chat-container';
|
| 28 |
import {
|
|
|
|
| 29 |
useDeleteConversation,
|
| 30 |
useDeleteDialog,
|
| 31 |
useEditDialog,
|
|
|
|
| 36 |
|
| 37 |
import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
|
| 38 |
import {
|
| 39 |
+
useClickConversationCard,
|
| 40 |
useClickDialogCard,
|
| 41 |
useFetchNextDialogList,
|
| 42 |
useGetChatSearchParams,
|
|
|
|
| 90 |
showModal: showOverviewModal,
|
| 91 |
} = useSetModalState();
|
| 92 |
const { currentRecord, setRecord } = useSetSelectedRecord<IDialog>();
|
| 93 |
+
const [controller, setController] = useState(new AbortController());
|
| 94 |
|
| 95 |
const handleAppCardEnter = (id: string) => () => {
|
| 96 |
handleItemEnter(id);
|
|
|
|
| 141 |
showConversationRenameModal(conversationId);
|
| 142 |
};
|
| 143 |
|
| 144 |
+
const handleDialogCardClick = useCallback(
|
| 145 |
+
(dialogId: string) => () => {
|
| 146 |
+
handleClickDialog(dialogId);
|
| 147 |
+
},
|
| 148 |
+
[handleClickDialog],
|
| 149 |
+
);
|
| 150 |
|
| 151 |
+
const handleConversationCardClick = useCallback(
|
| 152 |
+
(conversationId: string, isNew: boolean) => () => {
|
| 153 |
+
handleClickConversation(conversationId, isNew ? 'true' : '');
|
| 154 |
+
setController((pre) => {
|
| 155 |
+
pre.abort();
|
| 156 |
+
return new AbortController();
|
| 157 |
+
});
|
| 158 |
+
},
|
| 159 |
+
[handleClickConversation],
|
| 160 |
+
);
|
| 161 |
|
| 162 |
const handleCreateTemporaryConversation = useCallback(() => {
|
| 163 |
addTemporaryConversation();
|
| 164 |
}, [addTemporaryConversation]);
|
| 165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
const buildAppItems = (dialog: IDialog) => {
|
| 167 |
const dialogId = dialog.id;
|
| 168 |
|
|
|
|
| 296 |
<b>{t('chat')}</b>
|
| 297 |
<Tag>{conversationList.length}</Tag>
|
| 298 |
</Space>
|
| 299 |
+
<Tooltip title={t('newChat')}>
|
| 300 |
+
<PlusOutlined onClick={handleCreateTemporaryConversation} />
|
| 301 |
+
</Tooltip>
|
|
|
|
| 302 |
</Flex>
|
| 303 |
<Divider></Divider>
|
| 304 |
<Flex vertical gap={10} className={styles.chatTitleContent}>
|
|
|
|
| 310 |
<Card
|
| 311 |
key={x.id}
|
| 312 |
hoverable
|
| 313 |
+
onClick={handleConversationCardClick(x.id, x.is_new)}
|
| 314 |
onMouseEnter={handleConversationCardEnter(x.id)}
|
| 315 |
onMouseLeave={handleConversationItemLeave}
|
| 316 |
className={classNames(styles.chatTitleCard, {
|
|
|
|
| 345 |
</Flex>
|
| 346 |
</Flex>
|
| 347 |
<Divider type={'vertical'} className={styles.divider}></Divider>
|
| 348 |
+
<ChatContainer controller={controller}></ChatContainer>
|
| 349 |
{dialogEditVisible && (
|
| 350 |
<ChatConfigurationModal
|
| 351 |
visible={dialogEditVisible}
|
web/src/pages/user-setting/setting-model/fish-audio-modal/index.tsx
CHANGED
|
@@ -89,7 +89,7 @@ const FishAudioModal = ({
|
|
| 89 |
<Form.Item<FieldType>
|
| 90 |
label={t('addFishAudioRefID')}
|
| 91 |
name="fish_audio_refid"
|
| 92 |
-
rules={[{ required:
|
| 93 |
>
|
| 94 |
<Input placeholder={t('FishAudioRefIDMessage')} />
|
| 95 |
</Form.Item>
|
|
|
|
| 89 |
<Form.Item<FieldType>
|
| 90 |
label={t('addFishAudioRefID')}
|
| 91 |
name="fish_audio_refid"
|
| 92 |
+
rules={[{ required: true, message: t('FishAudioRefIDMessage') }]}
|
| 93 |
>
|
| 94 |
<Input placeholder={t('FishAudioRefIDMessage')} />
|
| 95 |
</Form.Item>
|
web/src/utils/chat.ts
CHANGED
|
@@ -32,3 +32,7 @@ export const buildMessageListWithUuid = (messages?: Message[]) => {
|
|
| 32 |
})) ?? []
|
| 33 |
);
|
| 34 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
})) ?? []
|
| 33 |
);
|
| 34 |
};
|
| 35 |
+
|
| 36 |
+
export const getConversationId = () => {
|
| 37 |
+
return uuid().replace(/-/g, '');
|
| 38 |
+
};
|