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 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
- if conv_id:
 
 
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": get_uuid(),
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:9456/',
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 && !conversationId) {
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 name="paper-clip" width={18} height={22}></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, useContext, useEffect, useRef, useState } from 'react';
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
- }, [callback]);
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
- player.current?.feed(units);
 
 
 
 
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 { buildMessageListWithUuid, isConversationIdExist } from '@/utils/chat';
 
 
 
 
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
- queryFn: async () => {
 
 
74
  const { data } = await chatService.listDialog();
75
 
76
- if (data.retcode === 0 && data.data.length > 0) {
77
- handleClickDialog(data.data[0].id);
 
 
 
 
 
 
 
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
- // if (data.retcode === 0 && needToBeSaved) {
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(params);
 
 
 
 
 
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> providers firstly.',
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
- const ChatContainer = () => {
 
 
 
 
26
  const { conversationId } = useGetChatSearchParams();
27
  const { data: conversation } = useFetchNextConversation();
28
 
@@ -36,8 +39,7 @@ const ChatContainer = () => {
36
  handlePressEnter,
37
  regenerateMessage,
38
  removeMessageById,
39
- redirectToNewConversation,
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
- <ConversationContext.Provider value={redirectToNewConversation}>
58
- {derivedMessages?.map((message, i) => {
59
- return (
60
- <MessageItem
61
- loading={
62
- message.role === MessageType.Assistant &&
63
- sendLoading &&
64
- derivedMessages.length - 1 === i
65
- }
66
- key={message.id}
67
- item={message}
68
- nickname={userInfo.nickname}
69
- avatar={userInfo.avatar}
70
- reference={buildMessageItemReference(
71
- {
72
- message: derivedMessages,
73
- reference: conversation.reference,
74
- },
75
- message,
76
- )}
77
- clickDocumentButton={clickDocumentButton}
78
- index={i}
79
- removeMessageById={removeMessageById}
80
- regenerateMessage={regenerateMessage}
81
- sendLoading={sendLoading}
82
- ></MessageItem>
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 IConversation,
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
- addTemporaryConversation();
199
- }, [addTemporaryConversation]);
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
- (message: string) => {
228
- return updateConversation({
 
 
 
 
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 !== '' && conversationId === '') {
278
  const prologue = dialog.prompt_config?.prologue;
279
 
280
  const nextMessage = {
@@ -285,17 +306,25 @@ export const useSelectNextMessages = () => {
285
 
286
  setDerivedMessages([nextMessage]);
287
  }
288
- }, [conversationId, dialog, dialogId, setDerivedMessages]);
289
 
290
  useEffect(() => {
291
  addPrologue();
292
  }, [addPrologue]);
293
 
294
  useEffect(() => {
295
- if (conversationId) {
 
 
 
 
296
  setDerivedMessages(conversation.message);
297
  }
298
- }, [conversation.message, conversationId, setDerivedMessages]);
 
 
 
 
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
- const { handleClickConversation } = useClickConversationCard();
333
- const { send, answer, done, setDone, resetAnswer } = useSendMessageWithSse();
 
 
334
  const {
335
  ref,
336
  derivedMessages,
@@ -341,17 +372,8 @@ export const useSendNextMessage = () => {
341
  removeMessageById,
342
  removeMessagesAfterCurrentMessage,
343
  } = useSelectNextMessages();
344
- const { data: dialog } = useFetchNextDialog();
345
- const currentConversationIdRef = useRef<string>('');
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
- conversation_id: currentConversationId ?? conversationId,
368
- messages: [...(messages ?? derivedMessages ?? []), message],
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
- if (conversationId !== '') {
 
403
  sendMessage({ message });
404
  } else {
405
- const data = await setConversation(message.content);
 
 
 
 
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
- [conversationId, setConversation, sendMessage],
 
 
 
 
 
 
418
  );
419
 
420
  const { regenerateMessage } = useRegenerateMessage({
@@ -425,24 +450,10 @@ export const useSendNextMessage = () => {
425
 
426
  useEffect(() => {
427
  // #1289
428
- console.log('🚀 ~ useEffect ~ answer:', answer, done);
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, done]);
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
- if (ret === 0) {
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 === '' && conversationId === '';
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
- if (data.retcode === 0) {
584
- const id = data.data.id;
585
- handleClickConversation(id);
586
- }
587
  return data;
588
  },
589
- [setConversation, handleClickConversation],
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 = (dialogId: string) => () => {
143
- handleClickDialog(dialogId);
144
- };
 
 
 
145
 
146
- const handleConversationCardClick = (dialogId: string) => () => {
147
- handleClickConversation(dialogId);
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
- <Dropdown menu={{ items }}>
301
- {/* <FormOutlined /> */}
302
- <PlusOutlined />
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: false, message: t('FishAudioRefIDMessage') }]}
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
+ };