### What problem does this PR solve? feat: Send message with uuid #2088 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.11.0
| @@ -77,11 +77,16 @@ export const AssistantGroupButton = ({ | |||
| ); | |||
| }; | |||
| export const UserGroupButton = () => { | |||
| interface UserGroupButtonProps { | |||
| messageId: string; | |||
| content: string; | |||
| } | |||
| export const UserGroupButton = ({ content }: UserGroupButtonProps) => { | |||
| return ( | |||
| <Radio.Group size="small"> | |||
| <Radio.Button value="a"> | |||
| <CopyToClipboard text="xxx"></CopyToClipboard> | |||
| <CopyToClipboard text={content}></CopyToClipboard> | |||
| </Radio.Button> | |||
| <Radio.Button value="b"> | |||
| <SyncOutlined /> | |||
| @@ -30,6 +30,7 @@ interface IProps { | |||
| nickname?: string; | |||
| avatar?: string; | |||
| clickDocumentButton?: (documentId: string, chunk: IChunk) => void; | |||
| index: number; | |||
| } | |||
| const MessageItem = ({ | |||
| @@ -38,6 +39,7 @@ const MessageItem = ({ | |||
| loading = false, | |||
| avatar = '', | |||
| clickDocumentButton, | |||
| index, | |||
| }: IProps) => { | |||
| const isAssistant = item.role === MessageType.Assistant; | |||
| const isUser = item.role === MessageType.User; | |||
| @@ -112,13 +114,18 @@ const MessageItem = ({ | |||
| <Flex vertical gap={8} flex={1}> | |||
| <Space> | |||
| {isAssistant ? ( | |||
| <AssistantGroupButton | |||
| messageId={item.id} | |||
| content={item.content} | |||
| prompt={item.prompt} | |||
| ></AssistantGroupButton> | |||
| index !== 0 && ( | |||
| <AssistantGroupButton | |||
| messageId={item.id} | |||
| content={item.content} | |||
| prompt={item.prompt} | |||
| ></AssistantGroupButton> | |||
| ) | |||
| ) : ( | |||
| <UserGroupButton></UserGroupButton> | |||
| <UserGroupButton | |||
| content={item.content} | |||
| messageId={item.id} | |||
| ></UserGroupButton> | |||
| )} | |||
| {/* <b>{isAssistant ? '' : nickname}</b> */} | |||
| @@ -14,9 +14,19 @@ import { buildMessageUuid, isConversationIdExist } from '@/utils/chat'; | |||
| import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | |||
| import { message } from 'antd'; | |||
| import dayjs, { Dayjs } from 'dayjs'; | |||
| import { set } from 'lodash'; | |||
| import { useCallback, useMemo, useState } from 'react'; | |||
| import { useSearchParams } from 'umi'; | |||
| const buildMessageListWithUuid = (messages?: Message[]) => { | |||
| return ( | |||
| messages?.map((x: Message | IMessage) => ({ | |||
| ...x, | |||
| id: buildMessageUuid(x), | |||
| })) ?? [] | |||
| ); | |||
| }; | |||
| //#region logic | |||
| export const useClickDialogCard = () => { | |||
| @@ -215,11 +225,7 @@ export const useFetchNextConversation = () => { | |||
| // } | |||
| const conversation = data?.data ?? {}; | |||
| const messageList = | |||
| conversation?.message?.map((x: Message | IMessage) => ({ | |||
| ...x, | |||
| id: buildMessageUuid(x), | |||
| })) ?? []; | |||
| const messageList = buildMessageListWithUuid(conversation?.message); | |||
| return { ...conversation, message: messageList }; | |||
| } | |||
| @@ -294,7 +300,6 @@ export const useRemoveNextConversation = () => { | |||
| }; | |||
| export const useDeleteMessage = () => { | |||
| // const queryClient = useQueryClient(); | |||
| const { conversationId } = useGetChatSearchParams(); | |||
| const { | |||
| @@ -308,9 +313,7 @@ export const useDeleteMessage = () => { | |||
| messageId, | |||
| conversationId, | |||
| }); | |||
| if (data.retcode === 0) { | |||
| // queryClient.invalidateQueries({ queryKey: ['fetchConversationList'] }); | |||
| } | |||
| return data.retcode; | |||
| }, | |||
| }); | |||
| @@ -471,6 +474,10 @@ export const useFetchNextSharedConversation = () => { | |||
| conversationId, | |||
| ); | |||
| const messageList = buildMessageListWithUuid(data?.data?.message); | |||
| set(data, 'data.message', messageList); | |||
| return data; | |||
| }, | |||
| }); | |||
| @@ -68,6 +68,7 @@ const ChatContainer = () => { | |||
| avatar={userInfo.avatar} | |||
| reference={buildMessageItemReference(conversation, message)} | |||
| clickDocumentButton={clickDocumentButton} | |||
| index={i} | |||
| ></MessageItem> | |||
| ); | |||
| })} | |||
| @@ -26,9 +26,9 @@ import { | |||
| } from '@/interfaces/database/chat'; | |||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||
| import { getFileExtension } from '@/utils'; | |||
| import { buildMessageUuid } from '@/utils/chat'; | |||
| import { useMutationState } from '@tanstack/react-query'; | |||
| import { get } from 'lodash'; | |||
| import omit from 'lodash/omit'; | |||
| import trim from 'lodash/trim'; | |||
| import { | |||
| ChangeEventHandler, | |||
| @@ -252,23 +252,22 @@ export const useSelectCurrentConversation = () => { | |||
| const { data: dialog } = useFetchNextDialog(); | |||
| const { conversationId, dialogId } = useGetChatSearchParams(); | |||
| // Show the entered message in the conversation immediately after sending the message | |||
| const addNewestConversation = useCallback( | |||
| (message: Partial<Message>, answer: string = '') => { | |||
| (message: Message, answer: string = '') => { | |||
| setCurrentConversation((pre) => { | |||
| return { | |||
| ...pre, | |||
| message: [ | |||
| ...pre.message, | |||
| { | |||
| role: MessageType.User, | |||
| content: message.content, | |||
| doc_ids: message.doc_ids, | |||
| id: uuid(), | |||
| ...message, | |||
| id: buildMessageUuid(message), | |||
| } as IMessage, | |||
| { | |||
| role: MessageType.Assistant, | |||
| content: answer, | |||
| id: uuid(), | |||
| id: buildMessageUuid({ ...message, role: MessageType.Assistant }), | |||
| reference: {}, | |||
| } as IMessage, | |||
| ], | |||
| @@ -278,6 +277,7 @@ export const useSelectCurrentConversation = () => { | |||
| [], | |||
| ); | |||
| // Add the streaming message to the last item in the message list | |||
| const addNewestAnswer = useCallback((answer: IAnswer) => { | |||
| setCurrentConversation((pre) => { | |||
| const latestMessage = pre.message?.at(-1); | |||
| @@ -291,6 +291,11 @@ export const useSelectCurrentConversation = () => { | |||
| ...latestMessage, | |||
| content: answer.answer, | |||
| reference: answer.reference, | |||
| id: buildMessageUuid({ | |||
| id: answer.id, | |||
| role: MessageType.Assistant, | |||
| }), | |||
| prompt: answer.prompt, | |||
| } as IMessage, | |||
| ], | |||
| }; | |||
| @@ -415,15 +420,13 @@ export const useSendMessage = ( | |||
| const { send, answer, done, setDone } = useSendMessageWithSse(); | |||
| const sendMessage = useCallback( | |||
| async (message: string, documentIds: string[], id?: string) => { | |||
| async (message: Message, documentIds: string[], id?: string) => { | |||
| const res = await send({ | |||
| conversation_id: id ?? conversationId, | |||
| messages: [ | |||
| ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')), | |||
| ...(conversation?.message ?? []), | |||
| { | |||
| id: uuid(), | |||
| role: MessageType.User, | |||
| content: message, | |||
| ...message, | |||
| doc_ids: documentIds, | |||
| }, | |||
| ], | |||
| @@ -431,7 +434,7 @@ export const useSendMessage = ( | |||
| if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) { | |||
| // cancel loading | |||
| setValue(message); | |||
| setValue(message.content); | |||
| console.info('removeLatestMessage111'); | |||
| removeLatestMessage(); | |||
| } else { | |||
| @@ -456,11 +459,11 @@ export const useSendMessage = ( | |||
| ); | |||
| const handleSendMessage = useCallback( | |||
| async (message: string, documentIds: string[]) => { | |||
| async (message: Message, documentIds: string[]) => { | |||
| if (conversationId !== '') { | |||
| sendMessage(message, documentIds); | |||
| } else { | |||
| const data = await setConversation(message); | |||
| const data = await setConversation(message.content); | |||
| if (data.retcode === 0) { | |||
| const id = data.data.id; | |||
| sendMessage(message, documentIds, id); | |||
| @@ -487,11 +490,20 @@ export const useSendMessage = ( | |||
| const handlePressEnter = useCallback( | |||
| (documentIds: string[]) => { | |||
| if (trim(value) === '') return; | |||
| const id = uuid(); | |||
| addNewestConversation({ content: value, doc_ids: documentIds }); | |||
| addNewestConversation({ | |||
| content: value, | |||
| doc_ids: documentIds, | |||
| id, | |||
| role: MessageType.User, | |||
| }); | |||
| if (done) { | |||
| setValue(''); | |||
| handleSendMessage(value.trim(), documentIds); | |||
| handleSendMessage( | |||
| { id, content: value.trim(), role: MessageType.User }, | |||
| documentIds, | |||
| ); | |||
| } | |||
| }, | |||
| [addNewestConversation, handleSendMessage, done, setValue, value], | |||
| @@ -134,8 +134,8 @@ const MarkdownContent = ({ | |||
| let replacedText = reactStringReplace(text, reg, (match, i) => { | |||
| const chunkIndex = getChunkIndex(match); | |||
| return ( | |||
| <Popover content={getPopoverContent(chunkIndex)}> | |||
| <InfoCircleOutlined key={i} className={styles.referenceIcon} /> | |||
| <Popover content={getPopoverContent(chunkIndex)} key={i}> | |||
| <InfoCircleOutlined className={styles.referenceIcon} /> | |||
| </Popover> | |||
| ); | |||
| }); | |||
| @@ -58,6 +58,7 @@ const ChatContainer = () => { | |||
| sendLoading && | |||
| conversation?.message.length - 1 === i | |||
| } | |||
| index={i} | |||
| ></MessageItem> | |||
| ); | |||
| })} | |||
| @@ -6,7 +6,7 @@ import { | |||
| import { useSendMessageWithSse } from '@/hooks/logic-hooks'; | |||
| import { IAnswer, Message } from '@/interfaces/database/chat'; | |||
| import api from '@/utils/api'; | |||
| import omit from 'lodash/omit'; | |||
| import { buildMessageUuid } from '@/utils/chat'; | |||
| import trim from 'lodash/trim'; | |||
| import { | |||
| Dispatch, | |||
| @@ -60,15 +60,13 @@ export const useSelectCurrentSharedConversation = (conversationId: string) => { | |||
| message: [ | |||
| ...(pre.message ?? []), | |||
| { | |||
| role: MessageType.User, | |||
| content: message.content, | |||
| doc_ids: message.doc_ids, | |||
| id: uuid(), | |||
| ...message, | |||
| id: buildMessageUuid(message), | |||
| } as IMessage, | |||
| { | |||
| role: MessageType.Assistant, | |||
| content: '', | |||
| id: uuid(), | |||
| id: buildMessageUuid({ ...message, role: MessageType.Assistant }), | |||
| reference: {}, | |||
| } as IMessage, | |||
| ], | |||
| @@ -89,6 +87,11 @@ export const useSelectCurrentSharedConversation = (conversationId: string) => { | |||
| ...latestMessage, | |||
| content: answer.answer, | |||
| reference: answer.reference, | |||
| id: buildMessageUuid({ | |||
| id: answer.id, | |||
| role: MessageType.Assistant, | |||
| }), | |||
| prompt: answer.prompt, | |||
| } as IMessage, | |||
| ], | |||
| }; | |||
| @@ -152,22 +155,16 @@ export const useSendSharedMessage = ( | |||
| ); | |||
| const sendMessage = useCallback( | |||
| async (message: string, id?: string) => { | |||
| async (message: Message, id?: string) => { | |||
| const res = await send({ | |||
| conversation_id: id ?? conversationId, | |||
| quote: false, | |||
| messages: [ | |||
| ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')), | |||
| { | |||
| role: MessageType.User, | |||
| content: message, | |||
| }, | |||
| ], | |||
| messages: [...(conversation?.message ?? []), message], | |||
| }); | |||
| if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) { | |||
| // cancel loading | |||
| setValue(message); | |||
| setValue(message.content); | |||
| removeLatestMessage(); | |||
| } | |||
| }, | |||
| @@ -183,7 +180,7 @@ export const useSendSharedMessage = ( | |||
| ); | |||
| const handleSendMessage = useCallback( | |||
| async (message: string) => { | |||
| async (message: Message) => { | |||
| if (conversationId !== '') { | |||
| sendMessage(message); | |||
| } else { | |||
| @@ -206,10 +203,20 @@ export const useSendSharedMessage = ( | |||
| const handlePressEnter = useCallback( | |||
| (documentIds: string[]) => { | |||
| if (trim(value) === '') return; | |||
| const id = uuid(); | |||
| if (done) { | |||
| setValue(''); | |||
| addNewestConversation({ content: value, doc_ids: documentIds }); | |||
| handleSendMessage(value.trim()); | |||
| addNewestConversation({ | |||
| content: value, | |||
| doc_ids: documentIds, | |||
| id, | |||
| role: MessageType.User, | |||
| }); | |||
| handleSendMessage({ | |||
| content: value.trim(), | |||
| id, | |||
| role: MessageType.User, | |||
| }); | |||
| } | |||
| }, | |||
| [addNewestConversation, done, handleSendMessage, setValue, value], | |||
| @@ -57,6 +57,7 @@ const FlowChatBox = () => { | |||
| message, | |||
| )} | |||
| clickDocumentButton={clickDocumentButton} | |||
| index={i} | |||
| ></MessageItem> | |||
| ); | |||
| })} | |||
| @@ -5,10 +5,12 @@ import { | |||
| useScrollToBottom, | |||
| useSendMessageWithSse, | |||
| } from '@/hooks/logic-hooks'; | |||
| import { IAnswer } from '@/interfaces/database/chat'; | |||
| import { IAnswer, Message } from '@/interfaces/database/chat'; | |||
| import { IMessage } from '@/pages/chat/interface'; | |||
| import api from '@/utils/api'; | |||
| import { buildMessageUuid } from '@/utils/chat'; | |||
| import { message } from 'antd'; | |||
| import trim from 'lodash/trim'; | |||
| import { useCallback, useEffect, useState } from 'react'; | |||
| import { useParams } from 'umi'; | |||
| import { v4 as uuid } from 'uuid'; | |||
| @@ -27,19 +29,18 @@ export const useSelectCurrentMessages = () => { | |||
| const ref = useScrollToBottom(currentMessages); | |||
| const addNewestQuestion = useCallback( | |||
| (message: string, answer: string = '') => { | |||
| (message: Message, answer: string = '') => { | |||
| setCurrentMessages((pre) => { | |||
| return [ | |||
| ...pre, | |||
| { | |||
| role: MessageType.User, | |||
| content: message, | |||
| id: uuid(), | |||
| ...message, | |||
| id: buildMessageUuid(message), | |||
| }, | |||
| { | |||
| role: MessageType.Assistant, | |||
| content: answer, | |||
| id: uuid(), | |||
| id: buildMessageUuid({ ...message, role: MessageType.Assistant }), | |||
| }, | |||
| ]; | |||
| }); | |||
| @@ -52,10 +53,13 @@ export const useSelectCurrentMessages = () => { | |||
| return [ | |||
| ...pre.slice(0, -1), | |||
| { | |||
| id: uuid(), | |||
| role: MessageType.Assistant, | |||
| content: answer.answer, | |||
| reference: answer.reference, | |||
| id: buildMessageUuid({ | |||
| id: answer.id, | |||
| role: MessageType.Assistant, | |||
| }), | |||
| }, | |||
| ]; | |||
| }); | |||
| @@ -88,7 +92,7 @@ export const useSelectCurrentMessages = () => { | |||
| }; | |||
| export const useSendMessage = ( | |||
| addNewestQuestion: (message: string, answer?: string) => void, | |||
| addNewestQuestion: (message: Message, answer?: string) => void, | |||
| removeLatestMessage: () => void, | |||
| addNewestAnswer: (answer: IAnswer) => void, | |||
| ) => { | |||
| @@ -99,12 +103,13 @@ export const useSendMessage = ( | |||
| const { send, answer, done } = useSendMessageWithSse(api.runCanvas); | |||
| const sendMessage = useCallback( | |||
| async (message: string) => { | |||
| async (message: Message) => { | |||
| const params: Record<string, unknown> = { | |||
| id: flowId, | |||
| }; | |||
| if (message) { | |||
| params.message = message; | |||
| if (message.content) { | |||
| params.message = message.content; | |||
| params.message_id = message.id; | |||
| } | |||
| const res = await send(params); | |||
| @@ -112,7 +117,7 @@ export const useSendMessage = ( | |||
| antMessage.error(res?.data?.retmsg); | |||
| // cancel loading | |||
| setValue(message); | |||
| setValue(message.content); | |||
| removeLatestMessage(); | |||
| } else { | |||
| refetch(); // pull the message list after sending the message successfully | |||
| @@ -122,7 +127,7 @@ export const useSendMessage = ( | |||
| ); | |||
| const handleSendMessage = useCallback( | |||
| async (message: string) => { | |||
| async (message: Message) => { | |||
| sendMessage(message); | |||
| }, | |||
| [sendMessage], | |||
| @@ -135,11 +140,17 @@ export const useSendMessage = ( | |||
| }, [answer, addNewestAnswer]); | |||
| const handlePressEnter = useCallback(() => { | |||
| if (trim(value) === '') return; | |||
| const id = uuid(); | |||
| if (done) { | |||
| setValue(''); | |||
| handleSendMessage(value.trim()); | |||
| handleSendMessage({ id, content: value.trim(), role: MessageType.User }); | |||
| } | |||
| addNewestQuestion(value); | |||
| addNewestQuestion({ | |||
| content: value, | |||
| id, | |||
| role: MessageType.User, | |||
| }); | |||
| }, [addNewestQuestion, handleSendMessage, done, setValue, value]); | |||
| return { | |||
| @@ -7,7 +7,7 @@ export const isConversationIdExist = (conversationId: string) => { | |||
| return conversationId !== EmptyConversationId && conversationId !== ''; | |||
| }; | |||
| export const buildMessageUuid = (message: Message | IMessage) => { | |||
| export const buildMessageUuid = (message: Partial<Message | IMessage>) => { | |||
| if ('id' in message && message.id) { | |||
| return message.role === MessageType.User | |||
| ? `${MessageType.User}_${message.id}` | |||