fix: 修复历史消息消失和用户消息重复问题
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

1. 移除 wrappedSendMessage,避免用户消息 ID 冲突导致重复
2. 添加 initialMessagesRef 保存初始消息,防止历史消息丢失
3. 改进 mergedMessages 逻辑:合并 初始消息 + useChat消息 + appendedMessages
4. 使用 Map 进行去重,按创建时间排序

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name 2026-02-01 11:04:29 +08:00
parent 05236a7b86
commit 12ae592746

View File

@ -398,26 +398,15 @@ export function Chat({
},
});
// 🆕 包装 sendMessage,在发送时保存用户消息到 appendedMessages
// 这样即使 useChat 重置消息状态,用户消息也会保留在 mergedMessages 中
const wrappedSendMessage = useCallback((message: Parameters<typeof sendMessage>[0]) => {
// 保存用户消息到 appendedMessages
const textPart = message?.parts?.find((p: any) => p.type === "text") as any;
const userMessage: ChatMessage = {
id: generateUUID(),
role: "user",
content: textPart?.text || "",
createdAt: new Date(),
parts: message?.parts || [],
attachments: (message as any)?.attachments || [],
} as ChatMessage;
console.log("[Chat] Saving user message to appendedMessages:", userMessage.id);
setAppendedMessages((prev) => [...prev, userMessage]);
// 调用原始的 sendMessage
return sendMessage(message);
}, [sendMessage]);
// 🆕 保存 initialMessages 的引用,防止历史消息丢失
// 当 useChat 内部状态重置时,我们仍然有初始消息
const initialMessagesRef = useRef<ChatMessage[]>(initialMessages);
useEffect(() => {
// 只在初始化时设置,不在后续更新
if (initialMessagesRef.current.length === 0 && initialMessages.length > 0) {
initialMessagesRef.current = initialMessages;
}
}, [initialMessages]);
// 调试:监控聊天状态变化 & 持久化生成状态
// 使用 ref 来访问最新的 modelsData,避免依赖数组问题
@ -456,7 +445,7 @@ export function Chat({
useEffect(() => {
if (query && !hasAppendedQuery) {
wrappedSendMessage({
sendMessage({
role: "user" as const,
parts: [{ type: "text", text: query }],
});
@ -1107,21 +1096,38 @@ export function Chat({
setMessages,
});
// 🆕 合并 useChat 的 messages 和 appendedMessages
// appendedMessages 包含通过 data-appendMessage 添加的消息
// 🆕 合并消息:初始消息 + useChat 消息 + appendedMessages
// 确保历史消息不会因为 useChat 内部状态重置而丢失
const mergedMessages = useMemo(() => {
if (appendedMessages.length === 0) {
return messages;
}
// 过滤掉已存在的消息,然后合并
const newMessages = appendedMessages.filter(
(appended) => !messages.some((m) => m.id === appended.id)
);
if (newMessages.length > 0) {
console.log("[Chat] Rendering with merged messages:", messages.length, "+", newMessages.length);
return [...messages, ...newMessages];
}
return messages;
// 使用 Map 进行去重,以 ID 为键
const messageMap = new Map<string, ChatMessage>();
// 1. 首先添加初始消息(历史记录)
initialMessagesRef.current.forEach(m => {
messageMap.set(m.id, m);
});
// 2. 然后添加 useChat 的当前消息(会覆盖相同 ID 的旧消息)
messages.forEach(m => {
messageMap.set(m.id, m);
});
// 3. 最后添加 appendedMessages(data-appendMessage 的消息)
appendedMessages.forEach(m => {
if (!messageMap.has(m.id)) {
messageMap.set(m.id, m);
}
});
// 按创建时间排序
const result = Array.from(messageMap.values()).sort((a, b) => {
const timeA = (a as any).createdAt ? new Date((a as any).createdAt).getTime() : 0;
const timeB = (b as any).createdAt ? new Date((b as any).createdAt).getTime() : 0;
return timeA - timeB;
});
console.log("[Chat] Merged messages:", result.length, "(initial:", initialMessagesRef.current.length, ", current:", messages.length, ", appended:", appendedMessages.length, ")");
return result;
}, [messages, appendedMessages]);
return (
@ -1139,7 +1145,7 @@ export function Chat({
isReadonly={isReadonly}
messages={mergedMessages}
regenerate={regenerate}
sendMessage={wrappedSendMessage}
sendMessage={sendMessage}
selectedModelId={currentModelId}
setMessages={setMessages}
status={status}
@ -1169,7 +1175,7 @@ export function Chat({
onModelChange={setCurrentModelId}
selectedModelId={currentModelId}
selectedVisibilityType={visibilityType}
sendMessage={wrappedSendMessage}
sendMessage={sendMessage}
setAttachments={setAttachments}
setInput={setInput}
setMessages={setMessages}
@ -1202,7 +1208,7 @@ export function Chat({
regenerate={regenerate}
selectedModelId={currentModelId}
selectedVisibilityType={visibilityType}
sendMessage={wrappedSendMessage}
sendMessage={sendMessage}
setAttachments={setAttachments}
setInput={setInput}
setMessages={setMessages}