balibabu commited on
Commit
8a4bb0c
·
1 Parent(s): 8f5dc1b

feat: Select derived messages from backend #2088 (#2176)

Browse files

### What problem does this PR solve?

feat: Select derived messages from backend #2088

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

web/src/components/message-item/group-button.tsx CHANGED
@@ -91,7 +91,7 @@ export const AssistantGroupButton = ({
91
  interface UserGroupButtonProps extends Partial<IRemoveMessageById> {
92
  messageId: string;
93
  content: string;
94
- regenerateMessage(): void;
95
  sendLoading: boolean;
96
  }
97
 
@@ -113,15 +113,17 @@ export const UserGroupButton = ({
113
  <Radio.Button value="a">
114
  <CopyToClipboard text={content}></CopyToClipboard>
115
  </Radio.Button>
116
- <Radio.Button
117
- value="b"
118
- onClick={regenerateMessage}
119
- disabled={sendLoading}
120
- >
121
- <Tooltip title={t('chat.regenerate')}>
122
- <SyncOutlined spin={sendLoading} />
123
- </Tooltip>
124
- </Radio.Button>
 
 
125
  {removeMessageById && (
126
  <Radio.Button value="c" onClick={onRemoveMessage} disabled={loading}>
127
  <Tooltip title={t('common.delete')}>
 
91
  interface UserGroupButtonProps extends Partial<IRemoveMessageById> {
92
  messageId: string;
93
  content: string;
94
+ regenerateMessage?: () => void;
95
  sendLoading: boolean;
96
  }
97
 
 
113
  <Radio.Button value="a">
114
  <CopyToClipboard text={content}></CopyToClipboard>
115
  </Radio.Button>
116
+ {regenerateMessage && (
117
+ <Radio.Button
118
+ value="b"
119
+ onClick={regenerateMessage}
120
+ disabled={sendLoading}
121
+ >
122
+ <Tooltip title={t('chat.regenerate')}>
123
+ <SyncOutlined spin={sendLoading} />
124
+ </Tooltip>
125
+ </Radio.Button>
126
+ )}
127
  {removeMessageById && (
128
  <Radio.Button value="c" onClick={onRemoveMessage} disabled={loading}>
129
  <Tooltip title={t('common.delete')}>
web/src/components/message-item/index.tsx CHANGED
@@ -79,7 +79,7 @@ const MessageItem = ({
79
  );
80
 
81
  const handleRegenerateMessage = useCallback(() => {
82
- regenerateMessage(item);
83
  }, [regenerateMessage, item]);
84
 
85
  useEffect(() => {
@@ -138,7 +138,9 @@ const MessageItem = ({
138
  content={item.content}
139
  messageId={item.id}
140
  removeMessageById={removeMessageById}
141
- regenerateMessage={handleRegenerateMessage}
 
 
142
  sendLoading={sendLoading}
143
  ></UserGroupButton>
144
  )}
 
79
  );
80
 
81
  const handleRegenerateMessage = useCallback(() => {
82
+ regenerateMessage?.(item);
83
  }, [regenerateMessage, item]);
84
 
85
  useEffect(() => {
 
138
  content={item.content}
139
  messageId={item.id}
140
  removeMessageById={removeMessageById}
141
+ regenerateMessage={
142
+ regenerateMessage && handleRegenerateMessage
143
+ }
144
  sendLoading={sendLoading}
145
  ></UserGroupButton>
146
  )}
web/src/hooks/chat-hooks.ts CHANGED
@@ -4,13 +4,12 @@ import {
4
  IDialog,
5
  IStats,
6
  IToken,
7
- Message,
8
  } from '@/interfaces/database/chat';
9
  import { IFeedbackRequestBody } from '@/interfaces/request/chat';
10
  import i18n from '@/locales/config';
11
- import { IClientConversation, IMessage } from '@/pages/chat/interface';
12
  import chatService from '@/services/chat-service';
13
- import { buildMessageUuid, isConversationIdExist } from '@/utils/chat';
14
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
15
  import { message } from 'antd';
16
  import dayjs, { Dayjs } from 'dayjs';
@@ -18,15 +17,6 @@ import { set } from 'lodash';
18
  import { useCallback, useMemo, useState } from 'react';
19
  import { useSearchParams } from 'umi';
20
 
21
- const buildMessageListWithUuid = (messages?: Message[]) => {
22
- return (
23
- messages?.map((x: Message | IMessage) => ({
24
- ...x,
25
- id: buildMessageUuid(x),
26
- })) ?? []
27
- );
28
- };
29
-
30
  //#region logic
31
 
32
  export const useClickDialogCard = () => {
@@ -465,14 +455,11 @@ export const useCreateNextSharedConversation = () => {
465
  return { data, loading, createSharedConversation: mutateAsync };
466
  };
467
 
468
- export const useFetchNextSharedConversation = () => {
469
- const {
470
- data,
471
- isPending: loading,
472
- mutateAsync,
473
- } = useMutation({
474
- mutationKey: ['fetchSharedConversation'],
475
- mutationFn: async (conversationId: string) => {
476
  const { data } = await chatService.getExternalConversation(
477
  null,
478
  conversationId,
@@ -486,7 +473,7 @@ export const useFetchNextSharedConversation = () => {
486
  },
487
  });
488
 
489
- return { data, loading, fetchConversation: mutateAsync };
490
  };
491
 
492
  //#endregion
 
4
  IDialog,
5
  IStats,
6
  IToken,
 
7
  } from '@/interfaces/database/chat';
8
  import { IFeedbackRequestBody } from '@/interfaces/request/chat';
9
  import i18n from '@/locales/config';
10
+ import { IClientConversation } from '@/pages/chat/interface';
11
  import chatService from '@/services/chat-service';
12
+ import { buildMessageListWithUuid, isConversationIdExist } from '@/utils/chat';
13
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
14
  import { message } from 'antd';
15
  import dayjs, { Dayjs } from 'dayjs';
 
17
  import { useCallback, useMemo, useState } from 'react';
18
  import { useSearchParams } from 'umi';
19
 
 
 
 
 
 
 
 
 
 
20
  //#region logic
21
 
22
  export const useClickDialogCard = () => {
 
455
  return { data, loading, createSharedConversation: mutateAsync };
456
  };
457
 
458
+ export const useFetchNextSharedConversation = (conversationId: string) => {
459
+ const { data, isPending: loading } = useQuery({
460
+ queryKey: ['fetchSharedConversation'],
461
+ enabled: !!conversationId,
462
+ queryFn: async () => {
 
 
 
463
  const { data } = await chatService.getExternalConversation(
464
  null,
465
  conversationId,
 
473
  },
474
  });
475
 
476
+ return { data, loading };
477
  };
478
 
479
  //#endregion
web/src/hooks/flow-hooks.ts CHANGED
@@ -2,8 +2,11 @@ import { ResponseType } from '@/interfaces/database/base';
2
  import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
3
  import i18n from '@/locales/config';
4
  import flowService from '@/services/flow-service';
 
5
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
6
  import { message } from 'antd';
 
 
7
  import { useParams } from 'umi';
8
  import { v4 as uuid } from 'uuid';
9
 
@@ -101,6 +104,11 @@ export const useFetchFlow = (): {
101
  queryFn: async () => {
102
  const { data } = await flowService.getCanvas({}, id);
103
 
 
 
 
 
 
104
  return data?.data ?? {};
105
  },
106
  });
 
2
  import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
3
  import i18n from '@/locales/config';
4
  import flowService from '@/services/flow-service';
5
+ import { buildMessageListWithUuid } from '@/utils/chat';
6
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
7
  import { message } from 'antd';
8
+ import { set } from 'lodash';
9
+ import get from 'lodash/get';
10
  import { useParams } from 'umi';
11
  import { v4 as uuid } from 'uuid';
12
 
 
104
  queryFn: async () => {
105
  const { data } = await flowService.getCanvas({}, id);
106
 
107
+ const messageList = buildMessageListWithUuid(
108
+ get(data, 'data.dsl.messages', []),
109
+ );
110
+ set(data, 'data.dsl.messages', messageList);
111
+
112
  return data?.data ?? {};
113
  },
114
  });
web/src/hooks/logic-hooks.ts CHANGED
@@ -1,14 +1,15 @@
1
  import { Authorization } from '@/constants/authorization';
 
2
  import { LanguageTranslationMap } from '@/constants/common';
3
  import { Pagination } from '@/interfaces/common';
4
  import { ResponseType } from '@/interfaces/database/base';
5
  import { IAnswer, Message } from '@/interfaces/database/chat';
6
  import { IKnowledgeFile } from '@/interfaces/database/knowledge';
7
  import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
8
- import { IClientConversation } from '@/pages/chat/interface';
9
  import api from '@/utils/api';
10
  import { getAuthorization } from '@/utils/authorization-util';
11
- import { getMessagePureId } from '@/utils/chat';
12
  import { PaginationProps } from 'antd';
13
  import { FormInstance } from 'antd/lib';
14
  import axios from 'axios';
@@ -309,6 +310,108 @@ export const useHandleMessageInputChange = () => {
309
  };
310
  };
311
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  export interface IRemoveMessageById {
313
  removeMessageById(messageId: string): void;
314
  }
@@ -375,7 +478,7 @@ export const useRemoveMessagesAfterCurrentMessage = (
375
  };
376
 
377
  export interface IRegenerateMessage {
378
- regenerateMessage(message: Message): void;
379
  }
380
 
381
  export const useRegenerateMessage = ({
@@ -384,7 +487,12 @@ export const useRegenerateMessage = ({
384
  messages,
385
  }: {
386
  removeMessagesAfterCurrentMessage(messageId: string): void;
387
- sendMessage({ message }: { message: Message; messages?: Message[] }): void;
 
 
 
 
 
388
  messages: Message[];
389
  }) => {
390
  const regenerateMessage = useCallback(
 
1
  import { Authorization } from '@/constants/authorization';
2
+ import { MessageType } from '@/constants/chat';
3
  import { LanguageTranslationMap } from '@/constants/common';
4
  import { Pagination } from '@/interfaces/common';
5
  import { ResponseType } from '@/interfaces/database/base';
6
  import { IAnswer, Message } from '@/interfaces/database/chat';
7
  import { IKnowledgeFile } from '@/interfaces/database/knowledge';
8
  import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
9
+ import { IClientConversation, IMessage } from '@/pages/chat/interface';
10
  import api from '@/utils/api';
11
  import { getAuthorization } from '@/utils/authorization-util';
12
+ import { buildMessageUuid, getMessagePureId } from '@/utils/chat';
13
  import { PaginationProps } from 'antd';
14
  import { FormInstance } from 'antd/lib';
15
  import axios from 'axios';
 
310
  };
311
  };
312
 
313
+ export const useSelectDerivedMessages = () => {
314
+ const [derivedMessages, setDerivedMessages] = useState<IMessage[]>([]);
315
+
316
+ const ref = useScrollToBottom(derivedMessages);
317
+
318
+ const addNewestQuestion = useCallback(
319
+ (message: Message, answer: string = '') => {
320
+ setDerivedMessages((pre) => {
321
+ return [
322
+ ...pre,
323
+ {
324
+ ...message,
325
+ id: buildMessageUuid(message),
326
+ },
327
+ {
328
+ role: MessageType.Assistant,
329
+ content: answer,
330
+ id: buildMessageUuid({ ...message, role: MessageType.Assistant }),
331
+ },
332
+ ];
333
+ });
334
+ },
335
+ [],
336
+ );
337
+
338
+ // Add the streaming message to the last item in the message list
339
+ const addNewestAnswer = useCallback((answer: IAnswer) => {
340
+ setDerivedMessages((pre) => {
341
+ return [
342
+ ...(pre?.slice(0, -1) ?? []),
343
+ {
344
+ role: MessageType.Assistant,
345
+ content: answer.answer,
346
+ reference: answer.reference,
347
+ id: buildMessageUuid({
348
+ id: answer.id,
349
+ role: MessageType.Assistant,
350
+ }),
351
+ prompt: answer.prompt,
352
+ },
353
+ ];
354
+ });
355
+ }, []);
356
+
357
+ const removeLatestMessage = useCallback(() => {
358
+ setDerivedMessages((pre) => {
359
+ const nextMessages = pre?.slice(0, -2) ?? [];
360
+ return nextMessages;
361
+ });
362
+ }, []);
363
+
364
+ const removeMessageById = useCallback(
365
+ (messageId: string) => {
366
+ setDerivedMessages((pre) => {
367
+ const nextMessages =
368
+ pre?.filter(
369
+ (x) => getMessagePureId(x.id) !== getMessagePureId(messageId),
370
+ ) ?? [];
371
+ return nextMessages;
372
+ });
373
+ },
374
+ [setDerivedMessages],
375
+ );
376
+
377
+ const removeMessagesAfterCurrentMessage = useCallback(
378
+ (messageId: string) => {
379
+ setDerivedMessages((pre) => {
380
+ const index = pre.findIndex((x) => x.id === messageId);
381
+ if (index !== -1) {
382
+ let nextMessages = pre.slice(0, index + 2) ?? [];
383
+ const latestMessage = nextMessages.at(-1);
384
+ nextMessages = latestMessage
385
+ ? [
386
+ ...nextMessages.slice(0, -1),
387
+ {
388
+ ...latestMessage,
389
+ content: '',
390
+ reference: undefined,
391
+ prompt: undefined,
392
+ },
393
+ ]
394
+ : nextMessages;
395
+ return nextMessages;
396
+ }
397
+ return pre;
398
+ });
399
+ },
400
+ [setDerivedMessages],
401
+ );
402
+
403
+ return {
404
+ ref,
405
+ derivedMessages,
406
+ setDerivedMessages,
407
+ addNewestQuestion,
408
+ addNewestAnswer,
409
+ removeLatestMessage,
410
+ removeMessageById,
411
+ removeMessagesAfterCurrentMessage,
412
+ };
413
+ };
414
+
415
  export interface IRemoveMessageById {
416
  removeMessageById(messageId: string): void;
417
  }
 
478
  };
479
 
480
  export interface IRegenerateMessage {
481
+ regenerateMessage?: (message: Message) => void;
482
  }
483
 
484
  export const useRegenerateMessage = ({
 
487
  messages,
488
  }: {
489
  removeMessagesAfterCurrentMessage(messageId: string): void;
490
+ sendMessage({
491
+ message,
492
+ }: {
493
+ message: Message;
494
+ messages?: Message[];
495
+ }): void | Promise<any>;
496
  messages: Message[];
497
  }) => {
498
  const regenerateMessage = useCallback(
web/src/pages/chat/chat-container/index.tsx CHANGED
@@ -5,44 +5,38 @@ import { Drawer, Flex, Spin } from 'antd';
5
  import {
6
  useClickDrawer,
7
  useCreateConversationBeforeUploadDocument,
8
- useFetchConversationOnMount,
9
  useGetFileIcon,
10
  useGetSendButtonDisabled,
11
  useSendButtonDisabled,
12
- useSendMessage,
13
  } from '../hooks';
14
  import { buildMessageItemReference } from '../utils';
15
 
16
  import MessageInput from '@/components/message-input';
 
 
 
 
17
  import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
18
  import { memo } from 'react';
19
  import styles from './index.less';
20
 
21
  const ChatContainer = () => {
 
 
 
22
  const {
23
  ref,
24
- currentConversation: conversation,
25
- addNewestConversation,
26
- removeLatestMessage,
27
- addNewestAnswer,
28
- conversationId,
29
  loading,
30
- removeMessageById,
31
- removeMessagesAfterCurrentMessage,
32
- } = useFetchConversationOnMount();
33
- const {
34
  handleInputChange,
35
  handlePressEnter,
36
  value,
37
- loading: sendLoading,
38
  regenerateMessage,
39
- } = useSendMessage(
40
- conversation,
41
- addNewestConversation,
42
- removeLatestMessage,
43
- addNewestAnswer,
44
- removeMessagesAfterCurrentMessage,
45
- );
46
  const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
47
  useClickDrawer();
48
  const disabled = useGetSendButtonDisabled();
@@ -58,19 +52,25 @@ const ChatContainer = () => {
58
  <Flex flex={1} vertical className={styles.messageContainer}>
59
  <div>
60
  <Spin spinning={loading}>
61
- {conversation?.message?.map((message, i) => {
62
  return (
63
  <MessageItem
64
  loading={
65
  message.role === MessageType.Assistant &&
66
  sendLoading &&
67
- conversation?.message.length - 1 === i
68
  }
69
  key={message.id}
70
  item={message}
71
  nickname={userInfo.nickname}
72
  avatar={userInfo.avatar}
73
- reference={buildMessageItemReference(conversation, message)}
 
 
 
 
 
 
74
  clickDocumentButton={clickDocumentButton}
75
  index={i}
76
  removeMessageById={removeMessageById}
 
5
  import {
6
  useClickDrawer,
7
  useCreateConversationBeforeUploadDocument,
 
8
  useGetFileIcon,
9
  useGetSendButtonDisabled,
10
  useSendButtonDisabled,
11
+ useSendNextMessage,
12
  } from '../hooks';
13
  import { buildMessageItemReference } from '../utils';
14
 
15
  import MessageInput from '@/components/message-input';
16
+ import {
17
+ useFetchNextConversation,
18
+ useGetChatSearchParams,
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
  const ChatContainer = () => {
25
+ const { conversationId } = useGetChatSearchParams();
26
+ const { data: conversation } = useFetchNextConversation();
27
+
28
  const {
29
  ref,
 
 
 
 
 
30
  loading,
31
+ sendLoading,
32
+ derivedMessages,
 
 
33
  handleInputChange,
34
  handlePressEnter,
35
  value,
 
36
  regenerateMessage,
37
+ removeMessageById,
38
+ } = useSendNextMessage();
39
+
 
 
 
 
40
  const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
41
  useClickDrawer();
42
  const disabled = useGetSendButtonDisabled();
 
52
  <Flex flex={1} vertical className={styles.messageContainer}>
53
  <div>
54
  <Spin spinning={loading}>
55
+ {derivedMessages?.map((message, i) => {
56
  return (
57
  <MessageItem
58
  loading={
59
  message.role === MessageType.Assistant &&
60
  sendLoading &&
61
+ derivedMessages.length - 1 === i
62
  }
63
  key={message.id}
64
  item={message}
65
  nickname={userInfo.nickname}
66
  avatar={userInfo.avatar}
67
+ reference={buildMessageItemReference(
68
+ {
69
+ message: derivedMessages,
70
+ reference: conversation.reference,
71
+ },
72
+ message,
73
+ )}
74
  clickDocumentButton={clickDocumentButton}
75
  index={i}
76
  removeMessageById={removeMessageById}
web/src/pages/chat/hooks.ts CHANGED
@@ -21,6 +21,8 @@ import {
21
  useRegenerateMessage,
22
  useRemoveMessageById,
23
  useRemoveMessagesAfterCurrentMessage,
 
 
24
  useSendMessageWithSse,
25
  } from '@/hooks/logic-hooks';
26
  import {
@@ -40,7 +42,6 @@ import {
40
  useCallback,
41
  useEffect,
42
  useMemo,
43
- useRef,
44
  useState,
45
  } from 'react';
46
  import { useSearchParams } from 'umi';
@@ -362,20 +363,71 @@ export const useSelectCurrentConversation = () => {
362
  };
363
  };
364
 
365
- export const useScrollToBottom = (currentConversation: IClientConversation) => {
366
- const ref = useRef<HTMLDivElement>(null);
367
 
368
- const scrollToBottom = useCallback(() => {
369
- if (currentConversation.id) {
370
- ref.current?.scrollIntoView({ behavior: 'instant' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  }
372
- }, [currentConversation]);
 
 
 
 
373
 
374
  useEffect(() => {
375
- scrollToBottom();
376
- }, [scrollToBottom]);
 
 
377
 
378
- return ref;
 
 
 
 
 
 
 
 
 
379
  };
380
 
381
  export const useFetchConversationOnMount = () => {
@@ -544,6 +596,137 @@ export const useSendMessage = (
544
  };
545
  };
546
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
  export const useGetFileIcon = () => {
548
  const getFileIcon = (filename: string) => {
549
  const ext: string = getFileExtension(filename);
 
21
  useRegenerateMessage,
22
  useRemoveMessageById,
23
  useRemoveMessagesAfterCurrentMessage,
24
+ useScrollToBottom,
25
+ useSelectDerivedMessages,
26
  useSendMessageWithSse,
27
  } from '@/hooks/logic-hooks';
28
  import {
 
42
  useCallback,
43
  useEffect,
44
  useMemo,
 
45
  useState,
46
  } from 'react';
47
  import { useSearchParams } from 'umi';
 
363
  };
364
  };
365
 
366
+ // export const useScrollToBottom = (currentConversation: IClientConversation) => {
367
+ // const ref = useRef<HTMLDivElement>(null);
368
 
369
+ // const scrollToBottom = useCallback(() => {
370
+ // if (currentConversation.id) {
371
+ // ref.current?.scrollIntoView({ behavior: 'instant' });
372
+ // }
373
+ // }, [currentConversation]);
374
+
375
+ // useEffect(() => {
376
+ // scrollToBottom();
377
+ // }, [scrollToBottom]);
378
+
379
+ // return ref;
380
+ // };
381
+
382
+ export const useSelectNextMessages = () => {
383
+ const {
384
+ ref,
385
+ setDerivedMessages,
386
+ derivedMessages,
387
+ addNewestAnswer,
388
+ addNewestQuestion,
389
+ removeLatestMessage,
390
+ removeMessageById,
391
+ removeMessagesAfterCurrentMessage,
392
+ } = useSelectDerivedMessages();
393
+ const { data: conversation, loading } = useFetchNextConversation();
394
+ const { data: dialog } = useFetchNextDialog();
395
+ const { conversationId, dialogId } = useGetChatSearchParams();
396
+
397
+ const addPrologue = useCallback(() => {
398
+ if (dialogId !== '' && conversationId === '') {
399
+ const prologue = dialog.prompt_config?.prologue;
400
+
401
+ const nextMessage = {
402
+ role: MessageType.Assistant,
403
+ content: prologue,
404
+ id: uuid(),
405
+ } as IMessage;
406
+
407
+ setDerivedMessages([nextMessage]);
408
  }
409
+ }, [conversationId, dialog, dialogId, setDerivedMessages]);
410
+
411
+ useEffect(() => {
412
+ addPrologue();
413
+ }, [addPrologue]);
414
 
415
  useEffect(() => {
416
+ if (conversationId) {
417
+ setDerivedMessages(conversation.message);
418
+ }
419
+ }, [conversation.message, conversationId, setDerivedMessages]);
420
 
421
+ return {
422
+ ref,
423
+ derivedMessages,
424
+ loading,
425
+ addNewestAnswer,
426
+ addNewestQuestion,
427
+ removeLatestMessage,
428
+ removeMessageById,
429
+ removeMessagesAfterCurrentMessage,
430
+ };
431
  };
432
 
433
  export const useFetchConversationOnMount = () => {
 
596
  };
597
  };
598
 
599
+ export const useSendNextMessage = () => {
600
+ const { setConversation } = useSetConversation();
601
+ const { conversationId } = useGetChatSearchParams();
602
+ const { handleInputChange, value, setValue } = useHandleMessageInputChange();
603
+ const { handleClickConversation } = useClickConversationCard();
604
+ const { send, answer, done, setDone } = useSendMessageWithSse();
605
+ const {
606
+ ref,
607
+ derivedMessages,
608
+ loading,
609
+ addNewestAnswer,
610
+ addNewestQuestion,
611
+ removeLatestMessage,
612
+ removeMessageById,
613
+ removeMessagesAfterCurrentMessage,
614
+ } = useSelectNextMessages();
615
+
616
+ const sendMessage = useCallback(
617
+ async ({
618
+ message,
619
+ currentConversationId,
620
+ messages,
621
+ }: {
622
+ message: Message;
623
+ currentConversationId?: string;
624
+ messages?: Message[];
625
+ }) => {
626
+ const res = await send({
627
+ conversation_id: currentConversationId ?? conversationId,
628
+ messages: [...(messages ?? derivedMessages ?? []), message],
629
+ });
630
+
631
+ if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) {
632
+ // cancel loading
633
+ setValue(message.content);
634
+ console.info('removeLatestMessage111');
635
+ removeLatestMessage();
636
+ } else {
637
+ if (currentConversationId) {
638
+ console.info('111');
639
+ // new conversation
640
+ handleClickConversation(currentConversationId);
641
+ } else {
642
+ console.info('222');
643
+ // fetchConversation(conversationId);
644
+ }
645
+ }
646
+ },
647
+ [
648
+ derivedMessages,
649
+ conversationId,
650
+ handleClickConversation,
651
+ removeLatestMessage,
652
+ setValue,
653
+ send,
654
+ ],
655
+ );
656
+
657
+ const handleSendMessage = useCallback(
658
+ async (message: Message) => {
659
+ if (conversationId !== '') {
660
+ sendMessage({ message });
661
+ } else {
662
+ const data = await setConversation(message.content);
663
+ if (data.retcode === 0) {
664
+ const id = data.data.id;
665
+ sendMessage({ message, currentConversationId: id });
666
+ }
667
+ }
668
+ },
669
+ [conversationId, setConversation, sendMessage],
670
+ );
671
+
672
+ const { regenerateMessage } = useRegenerateMessage({
673
+ removeMessagesAfterCurrentMessage,
674
+ sendMessage,
675
+ messages: derivedMessages,
676
+ });
677
+
678
+ useEffect(() => {
679
+ // #1289
680
+ if (answer.answer && answer?.conversationId === conversationId) {
681
+ addNewestAnswer(answer);
682
+ }
683
+ }, [answer, addNewestAnswer, conversationId]);
684
+
685
+ useEffect(() => {
686
+ // #1289 switch to another conversion window when the last conversion answer doesn't finish.
687
+ if (conversationId) {
688
+ setDone(true);
689
+ }
690
+ }, [setDone, conversationId]);
691
+
692
+ const handlePressEnter = useCallback(
693
+ (documentIds: string[]) => {
694
+ if (trim(value) === '') return;
695
+ const id = uuid();
696
+
697
+ addNewestQuestion({
698
+ content: value,
699
+ doc_ids: documentIds,
700
+ id,
701
+ role: MessageType.User,
702
+ });
703
+ if (done) {
704
+ setValue('');
705
+ handleSendMessage({
706
+ id,
707
+ content: value.trim(),
708
+ role: MessageType.User,
709
+ doc_ids: documentIds,
710
+ });
711
+ }
712
+ },
713
+ [addNewestQuestion, handleSendMessage, done, setValue, value],
714
+ );
715
+
716
+ return {
717
+ handlePressEnter,
718
+ handleInputChange,
719
+ value,
720
+ setValue,
721
+ regenerateMessage,
722
+ sendLoading: !done,
723
+ loading,
724
+ ref,
725
+ derivedMessages,
726
+ removeMessageById,
727
+ };
728
+ };
729
+
730
  export const useGetFileIcon = () => {
731
  const getFileIcon = (filename: string) => {
732
  const ext: string = getFileExtension(filename);
web/src/pages/chat/share/large.tsx CHANGED
@@ -1,13 +1,13 @@
1
  import MessageInput from '@/components/message-input';
2
  import MessageItem from '@/components/message-item';
3
  import { MessageType, SharedFrom } from '@/constants/chat';
 
4
  import { useSendButtonDisabled } from '@/pages/chat/hooks';
5
  import { Flex, Spin } from 'antd';
6
  import { forwardRef } from 'react';
7
  import {
8
  useCreateSharedConversationOnMount,
9
  useGetSharedChatSearchParams,
10
- useSelectCurrentSharedConversation,
11
  useSendSharedMessage,
12
  } from '../shared-hooks';
13
  import { buildMessageItemReference } from '../utils';
@@ -15,28 +15,17 @@ import styles from './index.less';
15
 
16
  const ChatContainer = () => {
17
  const { conversationId } = useCreateSharedConversationOnMount();
18
- const {
19
- currentConversation: conversation,
20
- addNewestConversation,
21
- removeLatestMessage,
22
- ref,
23
- loading,
24
- setCurrentConversation,
25
- addNewestAnswer,
26
- } = useSelectCurrentSharedConversation(conversationId);
27
 
28
  const {
29
  handlePressEnter,
30
  handleInputChange,
31
  value,
32
- loading: sendLoading,
33
- } = useSendSharedMessage(
34
- conversation,
35
- addNewestConversation,
36
- removeLatestMessage,
37
- setCurrentConversation,
38
- addNewestAnswer,
39
- );
40
  const sendDisabled = useSendButtonDisabled(value);
41
  const { from } = useGetSharedChatSearchParams();
42
 
@@ -46,17 +35,23 @@ const ChatContainer = () => {
46
  <Flex flex={1} vertical className={styles.messageContainer}>
47
  <div>
48
  <Spin spinning={loading}>
49
- {conversation?.message?.map((message, i) => {
50
  return (
51
  <MessageItem
52
  key={message.id}
53
  item={message}
54
  nickname="You"
55
- reference={buildMessageItemReference(conversation, message)}
 
 
 
 
 
 
56
  loading={
57
  message.role === MessageType.Assistant &&
58
  sendLoading &&
59
- conversation?.message.length - 1 === i
60
  }
61
  index={i}
62
  ></MessageItem>
 
1
  import MessageInput from '@/components/message-input';
2
  import MessageItem from '@/components/message-item';
3
  import { MessageType, SharedFrom } from '@/constants/chat';
4
+ import { useFetchNextSharedConversation } from '@/hooks/chat-hooks';
5
  import { useSendButtonDisabled } from '@/pages/chat/hooks';
6
  import { Flex, Spin } from 'antd';
7
  import { forwardRef } from 'react';
8
  import {
9
  useCreateSharedConversationOnMount,
10
  useGetSharedChatSearchParams,
 
11
  useSendSharedMessage,
12
  } from '../shared-hooks';
13
  import { buildMessageItemReference } from '../utils';
 
15
 
16
  const ChatContainer = () => {
17
  const { conversationId } = useCreateSharedConversationOnMount();
18
+ const { data } = useFetchNextSharedConversation(conversationId);
 
 
 
 
 
 
 
 
19
 
20
  const {
21
  handlePressEnter,
22
  handleInputChange,
23
  value,
24
+ sendLoading,
25
+ loading,
26
+ ref,
27
+ derivedMessages,
28
+ } = useSendSharedMessage(conversationId);
 
 
 
29
  const sendDisabled = useSendButtonDisabled(value);
30
  const { from } = useGetSharedChatSearchParams();
31
 
 
35
  <Flex flex={1} vertical className={styles.messageContainer}>
36
  <div>
37
  <Spin spinning={loading}>
38
+ {derivedMessages?.map((message, i) => {
39
  return (
40
  <MessageItem
41
  key={message.id}
42
  item={message}
43
  nickname="You"
44
+ reference={buildMessageItemReference(
45
+ {
46
+ message: derivedMessages,
47
+ reference: data?.data?.reference,
48
+ },
49
+ message,
50
+ )}
51
  loading={
52
  message.role === MessageType.Assistant &&
53
  sendLoading &&
54
+ derivedMessages?.length - 1 === i
55
  }
56
  index={i}
57
  ></MessageItem>
web/src/pages/chat/shared-hooks.ts CHANGED
@@ -3,22 +3,17 @@ import {
3
  useCreateNextSharedConversation,
4
  useFetchNextSharedConversation,
5
  } from '@/hooks/chat-hooks';
6
- import { useSendMessageWithSse } from '@/hooks/logic-hooks';
7
- import { IAnswer, Message } from '@/interfaces/database/chat';
 
 
 
8
  import api from '@/utils/api';
9
- import { buildMessageUuid } from '@/utils/chat';
10
  import trim from 'lodash/trim';
11
- import {
12
- Dispatch,
13
- SetStateAction,
14
- useCallback,
15
- useEffect,
16
- useState,
17
- } from 'react';
18
  import { useSearchParams } from 'umi';
19
  import { v4 as uuid } from 'uuid';
20
- import { useHandleMessageInputChange, useScrollToBottom } from './hooks';
21
- import { IClientConversation, IMessage } from './interface';
22
 
23
  export const useCreateSharedConversationOnMount = () => {
24
  const [currentQueryParameters] = useSearchParams();
@@ -46,91 +41,30 @@ export const useCreateSharedConversationOnMount = () => {
46
  return { conversationId };
47
  };
48
 
49
- export const useSelectCurrentSharedConversation = (conversationId: string) => {
50
- const [currentConversation, setCurrentConversation] =
51
- useState<IClientConversation>({} as IClientConversation);
52
- const { fetchConversation, loading } = useFetchNextSharedConversation();
53
-
54
- const ref = useScrollToBottom(currentConversation);
55
-
56
- const addNewestConversation = useCallback((message: Partial<Message>) => {
57
- setCurrentConversation((pre) => {
58
- return {
59
- ...pre,
60
- message: [
61
- ...(pre.message ?? []),
62
- {
63
- ...message,
64
- id: buildMessageUuid(message),
65
- } as IMessage,
66
- {
67
- role: MessageType.Assistant,
68
- content: '',
69
- id: buildMessageUuid({ ...message, role: MessageType.Assistant }),
70
- reference: {},
71
- } as IMessage,
72
- ],
73
- };
74
- });
75
- }, []);
76
-
77
- const addNewestAnswer = useCallback((answer: IAnswer) => {
78
- setCurrentConversation((pre) => {
79
- const latestMessage = pre.message?.at(-1);
80
-
81
- if (latestMessage) {
82
- return {
83
- ...pre,
84
- message: [
85
- ...pre.message.slice(0, -1),
86
- {
87
- ...latestMessage,
88
- content: answer.answer,
89
- reference: answer.reference,
90
- id: buildMessageUuid({
91
- id: answer.id,
92
- role: MessageType.Assistant,
93
- }),
94
- prompt: answer.prompt,
95
- } as IMessage,
96
- ],
97
- };
98
- }
99
- return pre;
100
- });
101
- }, []);
102
-
103
- const removeLatestMessage = useCallback(() => {
104
- setCurrentConversation((pre) => {
105
- const nextMessages = pre.message.slice(0, -2);
106
- return {
107
- ...pre,
108
- message: nextMessages,
109
- };
110
- });
111
- }, []);
112
-
113
- const fetchConversationOnMount = useCallback(async () => {
114
- if (conversationId) {
115
- const data = await fetchConversation(conversationId);
116
- if (data.retcode === 0) {
117
- setCurrentConversation(data.data);
118
- }
119
- }
120
- }, [conversationId, fetchConversation]);
121
 
122
  useEffect(() => {
123
- fetchConversationOnMount();
124
- }, [fetchConversationOnMount]);
125
 
126
  return {
127
- currentConversation,
128
- addNewestConversation,
 
129
  removeLatestMessage,
130
  loading,
131
  ref,
132
- setCurrentConversation,
133
- addNewestAnswer,
134
  };
135
  };
136
 
@@ -138,28 +72,28 @@ export const useSendButtonDisabled = (value: string) => {
138
  return trim(value) === '';
139
  };
140
 
141
- export const useSendSharedMessage = (
142
- conversation: IClientConversation,
143
- addNewestConversation: (message: Partial<Message>, answer?: string) => void,
144
- removeLatestMessage: () => void,
145
- setCurrentConversation: Dispatch<SetStateAction<IClientConversation>>,
146
- addNewestAnswer: (answer: IAnswer) => void,
147
- ) => {
148
- const conversationId = conversation.id;
149
  const { createSharedConversation: setConversation } =
150
  useCreateNextSharedConversation();
151
  const { handleInputChange, value, setValue } = useHandleMessageInputChange();
152
-
153
  const { send, answer, done } = useSendMessageWithSse(
154
  api.completeExternalConversation,
155
  );
 
 
 
 
 
 
 
 
156
 
157
  const sendMessage = useCallback(
158
  async (message: Message, id?: string) => {
159
  const res = await send({
160
  conversation_id: id ?? conversationId,
161
  quote: false,
162
- messages: [...(conversation?.message ?? []), message],
163
  });
164
 
165
  if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) {
@@ -168,15 +102,7 @@ export const useSendSharedMessage = (
168
  removeLatestMessage();
169
  }
170
  },
171
- [
172
- conversationId,
173
- conversation?.message,
174
- // fetchConversation,
175
- removeLatestMessage,
176
- setValue,
177
- send,
178
- // setCurrentConversation,
179
- ],
180
  );
181
 
182
  const handleSendMessage = useCallback(
@@ -206,7 +132,7 @@ export const useSendSharedMessage = (
206
  const id = uuid();
207
  if (done) {
208
  setValue('');
209
- addNewestConversation({
210
  content: value,
211
  doc_ids: documentIds,
212
  id,
@@ -219,14 +145,17 @@ export const useSendSharedMessage = (
219
  });
220
  }
221
  },
222
- [addNewestConversation, done, handleSendMessage, setValue, value],
223
  );
224
 
225
  return {
226
  handlePressEnter,
227
  handleInputChange,
228
  value,
229
- loading: !done,
 
 
 
230
  };
231
  };
232
 
 
3
  useCreateNextSharedConversation,
4
  useFetchNextSharedConversation,
5
  } from '@/hooks/chat-hooks';
6
+ import {
7
+ useSelectDerivedMessages,
8
+ useSendMessageWithSse,
9
+ } from '@/hooks/logic-hooks';
10
+ import { Message } from '@/interfaces/database/chat';
11
  import api from '@/utils/api';
 
12
  import trim from 'lodash/trim';
13
+ import { useCallback, useEffect, useState } from 'react';
 
 
 
 
 
 
14
  import { useSearchParams } from 'umi';
15
  import { v4 as uuid } from 'uuid';
16
+ import { useHandleMessageInputChange } from './hooks';
 
17
 
18
  export const useCreateSharedConversationOnMount = () => {
19
  const [currentQueryParameters] = useSearchParams();
 
41
  return { conversationId };
42
  };
43
 
44
+ export const useSelectNextSharedMessages = (conversationId: string) => {
45
+ const { data, loading } = useFetchNextSharedConversation(conversationId);
46
+
47
+ const {
48
+ derivedMessages,
49
+ ref,
50
+ setDerivedMessages,
51
+ addNewestAnswer,
52
+ addNewestQuestion,
53
+ removeLatestMessage,
54
+ } = useSelectDerivedMessages();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  useEffect(() => {
57
+ setDerivedMessages(data?.data?.message);
58
+ }, [setDerivedMessages, data]);
59
 
60
  return {
61
+ derivedMessages,
62
+ addNewestAnswer,
63
+ addNewestQuestion,
64
  removeLatestMessage,
65
  loading,
66
  ref,
67
+ setDerivedMessages,
 
68
  };
69
  };
70
 
 
72
  return trim(value) === '';
73
  };
74
 
75
+ export const useSendSharedMessage = (conversationId: string) => {
 
 
 
 
 
 
 
76
  const { createSharedConversation: setConversation } =
77
  useCreateNextSharedConversation();
78
  const { handleInputChange, value, setValue } = useHandleMessageInputChange();
 
79
  const { send, answer, done } = useSendMessageWithSse(
80
  api.completeExternalConversation,
81
  );
82
+ const {
83
+ derivedMessages,
84
+ ref,
85
+ removeLatestMessage,
86
+ addNewestAnswer,
87
+ addNewestQuestion,
88
+ loading,
89
+ } = useSelectNextSharedMessages(conversationId);
90
 
91
  const sendMessage = useCallback(
92
  async (message: Message, id?: string) => {
93
  const res = await send({
94
  conversation_id: id ?? conversationId,
95
  quote: false,
96
+ messages: [...(derivedMessages ?? []), message],
97
  });
98
 
99
  if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) {
 
102
  removeLatestMessage();
103
  }
104
  },
105
+ [conversationId, derivedMessages, removeLatestMessage, setValue, send],
 
 
 
 
 
 
 
 
106
  );
107
 
108
  const handleSendMessage = useCallback(
 
132
  const id = uuid();
133
  if (done) {
134
  setValue('');
135
+ addNewestQuestion({
136
  content: value,
137
  doc_ids: documentIds,
138
  id,
 
145
  });
146
  }
147
  },
148
+ [addNewestQuestion, done, handleSendMessage, setValue, value],
149
  );
150
 
151
  return {
152
  handlePressEnter,
153
  handleInputChange,
154
  value,
155
+ sendLoading: !done,
156
+ ref,
157
+ loading,
158
+ derivedMessages,
159
  };
160
  };
161
 
web/src/pages/chat/utils.ts CHANGED
@@ -36,7 +36,7 @@ export const buildMessageItemReference = (
36
  );
37
  const reference = message?.reference
38
  ? message?.reference
39
- : conversation.reference[referenceIndex];
40
 
41
  return reference;
42
  };
 
36
  );
37
  const reference = message?.reference
38
  ? message?.reference
39
+ : (conversation?.reference ?? {})[referenceIndex];
40
 
41
  return reference;
42
  };
web/src/pages/flow/chat/box.tsx CHANGED
@@ -6,28 +6,23 @@ import { useClickDrawer, useGetFileIcon } from '@/pages/chat/hooks';
6
  import { buildMessageItemReference } from '@/pages/chat/utils';
7
  import { Button, Drawer, Flex, Input, Spin } from 'antd';
8
 
9
- import { useSelectCurrentMessages, useSendMessage } from './hooks';
10
 
11
  import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
12
  import styles from './index.less';
13
 
14
  const FlowChatBox = () => {
15
  const {
16
- ref,
17
- currentMessages,
18
- reference,
19
- addNewestAnswer,
20
- addNewestQuestion,
21
- removeLatestMessage,
22
- loading,
23
- } = useSelectCurrentMessages();
24
-
25
- const {
26
  handleInputChange,
27
  handlePressEnter,
28
  value,
29
- loading: sendLoading,
30
- } = useSendMessage(addNewestQuestion, removeLatestMessage, addNewestAnswer);
 
 
 
 
31
  const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
32
  useClickDrawer();
33
  useGetFileIcon();
@@ -40,26 +35,26 @@ const FlowChatBox = () => {
40
  <Flex flex={1} vertical className={styles.messageContainer}>
41
  <div>
42
  <Spin spinning={loading}>
43
- {currentMessages?.map((message, i) => {
44
  return (
45
  <MessageItem
46
  loading={
47
  message.role === MessageType.Assistant &&
48
  sendLoading &&
49
- currentMessages.length - 1 === i
50
  }
51
  key={message.id}
52
  nickname={userInfo.nickname}
53
  avatar={userInfo.avatar}
54
  item={message}
55
  reference={buildMessageItemReference(
56
- { message: currentMessages, reference },
57
  message,
58
  )}
59
  clickDocumentButton={clickDocumentButton}
60
  index={i}
61
- regenerateMessage={() => {}}
62
  showLikeButton={false}
 
63
  ></MessageItem>
64
  );
65
  })}
 
6
  import { buildMessageItemReference } from '@/pages/chat/utils';
7
  import { Button, Drawer, Flex, Input, Spin } from 'antd';
8
 
9
+ import { useSendNextMessage } from './hooks';
10
 
11
  import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
12
  import styles from './index.less';
13
 
14
  const FlowChatBox = () => {
15
  const {
16
+ sendLoading,
 
 
 
 
 
 
 
 
 
17
  handleInputChange,
18
  handlePressEnter,
19
  value,
20
+ loading,
21
+ ref,
22
+ derivedMessages,
23
+ reference,
24
+ } = useSendNextMessage();
25
+
26
  const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
27
  useClickDrawer();
28
  useGetFileIcon();
 
35
  <Flex flex={1} vertical className={styles.messageContainer}>
36
  <div>
37
  <Spin spinning={loading}>
38
+ {derivedMessages?.map((message, i) => {
39
  return (
40
  <MessageItem
41
  loading={
42
  message.role === MessageType.Assistant &&
43
  sendLoading &&
44
+ derivedMessages.length - 1 === i
45
  }
46
  key={message.id}
47
  nickname={userInfo.nickname}
48
  avatar={userInfo.avatar}
49
  item={message}
50
  reference={buildMessageItemReference(
51
+ { message: derivedMessages, reference },
52
  message,
53
  )}
54
  clickDocumentButton={clickDocumentButton}
55
  index={i}
 
56
  showLikeButton={false}
57
+ sendLoading={sendLoading}
58
  ></MessageItem>
59
  );
60
  })}
web/src/pages/flow/chat/hooks.ts CHANGED
@@ -3,6 +3,7 @@ import { useFetchFlow } from '@/hooks/flow-hooks';
3
  import {
4
  useHandleMessageInputChange,
5
  useScrollToBottom,
 
6
  useSendMessageWithSse,
7
  } from '@/hooks/logic-hooks';
8
  import { IAnswer, Message } from '@/interfaces/database/chat';
@@ -91,6 +92,42 @@ export const useSelectCurrentMessages = () => {
91
  };
92
  };
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  export const useSendMessage = (
95
  addNewestQuestion: (message: Message, answer?: string) => void,
96
  removeLatestMessage: () => void,
@@ -160,3 +197,84 @@ export const useSendMessage = (
160
  loading: !done,
161
  };
162
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import {
4
  useHandleMessageInputChange,
5
  useScrollToBottom,
6
+ useSelectDerivedMessages,
7
  useSendMessageWithSse,
8
  } from '@/hooks/logic-hooks';
9
  import { IAnswer, Message } from '@/interfaces/database/chat';
 
92
  };
93
  };
94
 
95
+ export const useSelectNextMessages = () => {
96
+ const { id: id } = useParams();
97
+ const { data: flowDetail, loading } = useFetchFlow();
98
+ const messages = flowDetail.dsl.messages;
99
+ const reference = flowDetail.dsl.reference;
100
+ const {
101
+ derivedMessages,
102
+ setDerivedMessages,
103
+ ref,
104
+ addNewestQuestion,
105
+ addNewestAnswer,
106
+ removeLatestMessage,
107
+ removeMessageById,
108
+ removeMessagesAfterCurrentMessage,
109
+ } = useSelectDerivedMessages();
110
+
111
+ useEffect(() => {
112
+ if (id) {
113
+ const nextMessages = messages.map((x) => ({ ...x, id: uuid() }));
114
+ setDerivedMessages(nextMessages);
115
+ }
116
+ }, [messages, id, setDerivedMessages]);
117
+
118
+ return {
119
+ reference,
120
+ loading,
121
+ derivedMessages,
122
+ ref,
123
+ addNewestQuestion,
124
+ addNewestAnswer,
125
+ removeLatestMessage,
126
+ removeMessageById,
127
+ removeMessagesAfterCurrentMessage,
128
+ };
129
+ };
130
+
131
  export const useSendMessage = (
132
  addNewestQuestion: (message: Message, answer?: string) => void,
133
  removeLatestMessage: () => void,
 
197
  loading: !done,
198
  };
199
  };
200
+
201
+ export const useSendNextMessage = () => {
202
+ const {
203
+ reference,
204
+ loading,
205
+ derivedMessages,
206
+ ref,
207
+ addNewestQuestion,
208
+ addNewestAnswer,
209
+ removeLatestMessage,
210
+ removeMessageById,
211
+ } = useSelectNextMessages();
212
+ const { id: flowId } = useParams();
213
+ const { handleInputChange, value, setValue } = useHandleMessageInputChange();
214
+ const { refetch } = useFetchFlow();
215
+
216
+ const { send, answer, done } = useSendMessageWithSse(api.runCanvas);
217
+
218
+ const sendMessage = useCallback(
219
+ async ({ message }: { message: Message; messages?: Message[] }) => {
220
+ const params: Record<string, unknown> = {
221
+ id: flowId,
222
+ };
223
+ if (message.content) {
224
+ params.message = message.content;
225
+ params.message_id = message.id;
226
+ }
227
+ const res = await send(params);
228
+
229
+ if (receiveMessageError(res)) {
230
+ antMessage.error(res?.data?.retmsg);
231
+
232
+ // cancel loading
233
+ setValue(message.content);
234
+ removeLatestMessage();
235
+ } else {
236
+ refetch(); // pull the message list after sending the message successfully
237
+ }
238
+ },
239
+ [flowId, removeLatestMessage, setValue, send, refetch],
240
+ );
241
+
242
+ const handleSendMessage = useCallback(
243
+ async (message: Message) => {
244
+ sendMessage({ message });
245
+ },
246
+ [sendMessage],
247
+ );
248
+
249
+ useEffect(() => {
250
+ if (answer.answer) {
251
+ addNewestAnswer(answer);
252
+ }
253
+ }, [answer, addNewestAnswer]);
254
+
255
+ const handlePressEnter = useCallback(() => {
256
+ if (trim(value) === '') return;
257
+ const id = uuid();
258
+ if (done) {
259
+ setValue('');
260
+ handleSendMessage({ id, content: value.trim(), role: MessageType.User });
261
+ }
262
+ addNewestQuestion({
263
+ content: value,
264
+ id,
265
+ role: MessageType.User,
266
+ });
267
+ }, [addNewestQuestion, handleSendMessage, done, setValue, value]);
268
+
269
+ return {
270
+ handlePressEnter,
271
+ handleInputChange,
272
+ value,
273
+ sendLoading: !done,
274
+ reference,
275
+ loading,
276
+ derivedMessages,
277
+ ref,
278
+ removeMessageById,
279
+ };
280
+ };
web/src/utils/chat.ts CHANGED
@@ -23,3 +23,12 @@ export const getMessagePureId = (id?: string) => {
23
  }
24
  return id;
25
  };
 
 
 
 
 
 
 
 
 
 
23
  }
24
  return id;
25
  };
26
+
27
+ export const buildMessageListWithUuid = (messages?: Message[]) => {
28
+ return (
29
+ messages?.map((x: Message | IMessage) => ({
30
+ ...x,
31
+ id: buildMessageUuid(x),
32
+ })) ?? []
33
+ );
34
+ };