Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用useMemo优化sendMessage中的消息生成 #315

Merged
merged 1 commit into from
Sep 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 109 additions & 85 deletions src/hooks/useChatList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ModelConfig } from '@/types/config';
import { processSSE } from '@/utils/fetch';
import { genUUID } from '@/utils/uuid';
import { useMergedState } from 'rc-util';
import React, { useEffect, useMemo, useRef } from 'react';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { useRefFunction } from './useRefFunction';

export const initialModelConfig: ModelConfig = {
Expand Down Expand Up @@ -129,6 +129,7 @@ export const useChatList = (props: ProChatUIUseListChatProps) => {
value: props.loading,
});
const [isLoadingMessage, setIsLoadingMessage] = useMergedState<boolean>(false);

/**
* Fetches the chat list using the provided request function.
* If the request function is not provided, it sets the loading state to false and returns.
Expand Down Expand Up @@ -180,102 +181,125 @@ export const useChatList = (props: ProChatUIUseListChatProps) => {
* @param {string} message - The message to send.
* @returns {Promise<void>} - A promise that resolves when the message is sent.
*/
const getLastLoadingMessage = useCallback(() => {
return chatListRef.current[chatListRef.current.length - 1];
}, [chatListRef]);

const sendMessage = useRefFunction(async (message: string | Partial<ChatMessage>) => {
controller.current = new AbortController();

setChatList((prevState) => [
...prevState,
genMessageRecord(
typeof message === 'string'
? { content: message }
: {
...message,
content: message.content,
},
'user',
),
]);
const userMessage = genMessageRecord(
typeof message === 'string' ? { content: message } : { ...message, content: message.content },
'user',
);
chatList.push(userMessage);
setChatList([...chatList]);

if (!props?.sendMessageRequest) return;
setIsLoadingMessage(true);
setChatList((prevState) => [
...prevState,
genMessageRecord(
{
content: LOADING_FLAT,
},
'assistant',
),
]);

const res = (await Promise.race([
props.sendMessageRequest?.(chatListRef.current),
new Promise((_, reject) => {
controller.current?.signal.addEventListener('abort', () => {
reject();
});
}),
])) as Response | ChatMessage<any>;

if (res instanceof Response) {
processSSE(res, {
signal: controller.current.signal,
onFinish: async () => {
setChatList((prevState) => {
const updatedList = [...prevState];
updatedList[updatedList.length - 1].isFinished = true;
return updatedList;
const loadingMessage = genMessageRecord({ content: LOADING_FLAT }, 'assistant');
chatList.push(loadingMessage);
setChatList([...chatList]);

try {
const res = (await Promise.race([
props.sendMessageRequest?.([chatList[chatList.length - 2]]),
new Promise((_, reject) => {
controller.current.signal.addEventListener('abort', () => {
reject();
});
setIsLoadingMessage(false);
},
onMessageHandle: async (text, res, type) => {
if (type === 'done' || controller.current?.signal.aborted) {
}),
])) as Response | ChatMessage<any>;

if (res instanceof Response) {
await processSSE(res, {
signal: controller.current.signal,
onFinish: async () => {
const message = getLastLoadingMessage();
if (!message) return;
setChatList((prev) => {
message.isFinished = true;
return [...prev];
});
setIsLoadingMessage(false);
setChatList((prevState) => {
const updatedList = [...prevState];
updatedList[updatedList.length - 1].isFinished = true;
return updatedList;
},
onMessageHandle: async (text, res, type) => {
const loadingMessage = getLastLoadingMessage();
if (!loadingMessage) return;

if (type === 'done' || controller.current.signal.aborted) {
loadingMessage.isFinished = true;
setChatList((prev) => [...prev]);
setIsLoadingMessage(false);
return;
}

const content =
loadingMessage.content === LOADING_FLAT ? text : loadingMessage.content + text;
const updatedMessage = {
...loadingMessage,
updateAt: Date.now(),
originContent: text,
isFinished: false,
content,
};

const transformMessage = await props.transformToChatMessage?.(updatedMessage, {
preContent: loadingMessage.content === LOADING_FLAT ? '' : loadingMessage.content,
currentContent: text,
});
return;
}

setChatList((prevState) => {
const updatedList = [...prevState];
const currentMessage = updatedList[updatedList.length - 1];
currentMessage.content =
currentMessage.content === LOADING_FLAT ? text : currentMessage.content + text;
currentMessage.updateAt = Date.now();
return updatedList;
});
},
onErrorHandle: async (error) => {
const content = error.message;
setChatList((prevState) => {
const updatedList = [...prevState];
const currentMessage = updatedList[updatedList.length - 1];
currentMessage.content = content;
currentMessage.originContent = content;
currentMessage.updateAt = Date.now();
currentMessage.isFinished = true;
return updatedList;

setChatList((prev) => {
const newList = [...prev];
newList[newList.length - 1] = transformMessage || updatedMessage;
return newList;
});
},
onErrorHandle: async (error) => {
const loadingMessage = getLastLoadingMessage();
if (loadingMessage) {
const content = error.message;
const errorMessage = {
...loadingMessage,
content,
originContent: content,
updateAt: Date.now(),
isFinished: true,
};

const transformMessage = await props.transformToChatMessage?.(errorMessage, {
preContent: loadingMessage.content,
currentContent: content,
});

setChatList((prev) => [...prev, transformMessage || errorMessage]);
}
setIsLoadingMessage(false);
},
});
} else {
const loadingMessage = getLastLoadingMessage();
if (loadingMessage) {
const updatedMessage = {
...loadingMessage,
...res,
updateAt: Date.now(),
};

const transformMessage = await props.transformToChatMessage?.(updatedMessage, {
preContent: loadingMessage.content,
currentContent: updatedMessage.originContent,
});
setIsLoadingMessage(false);
},
});
} else {
setChatList((prevState) => {
const updatedList = [...prevState];
const currentMessage = {
...updatedList[updatedList.length - 1],
...res,
updateAt: Date.now(),
};
updatedList[updatedList.length - 1] = currentMessage;
return updatedList;
});
setIsLoadingMessage(false);

setChatList((prev) => [...prev, transformMessage || updatedMessage]);
}
setIsLoadingMessage(false);
}
} catch (error) {
console.error('Error sending message:', error);
}
});

/**
* Stops the generation of messages.
*/
Expand Down
Loading