fix: prevent chat messages from duplicating or disappearing (#507)
- Tighten message deduplication and turn-structure checks in chat rendering - Avoid stale cached turn grouping data during streaming and incremental updates - Keep message order stable so headers and grouped messages render consistently
This commit is contained in:
committed by
GitHub
parent
10851bd7ac
commit
5473381720
@@ -57,6 +57,10 @@ const hasSameTurnStructure = (prev: ChatMessageEntry[], next: ChatMessageEntry[]
|
|||||||
const prevMessage = prev[index];
|
const prevMessage = prev[index];
|
||||||
const nextMessage = next[index];
|
const nextMessage = next[index];
|
||||||
|
|
||||||
|
if (prevMessage !== nextMessage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (prevMessage.info.id !== nextMessage.info.id) {
|
if (prevMessage.info.id !== nextMessage.info.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -466,19 +470,24 @@ const MessageList: React.FC<MessageListProps> = ({
|
|||||||
}, [permissions, questions, onMessageContentChange]);
|
}, [permissions, questions, onMessageContentChange]);
|
||||||
|
|
||||||
const baseDisplayMessages = React.useMemo(() => {
|
const baseDisplayMessages = React.useMemo(() => {
|
||||||
const seenIds = new Set<string>();
|
const seenIdsFromTail = new Set<string>();
|
||||||
const nextNormalizedCache = new Map<string, { source: ChatMessageEntry; normalized: ChatMessageEntry }>();
|
const nextNormalizedCache = new Map<string, { source: ChatMessageEntry; normalized: ChatMessageEntry }>();
|
||||||
const normalizedMessages = messages
|
|
||||||
.filter((message) => {
|
const dedupedMessages: ChatMessageEntry[] = [];
|
||||||
const messageId = message.info?.id;
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
||||||
if (typeof messageId === 'string') {
|
const message = messages[index];
|
||||||
if (seenIds.has(messageId)) {
|
const messageId = message.info?.id;
|
||||||
return false;
|
if (typeof messageId === 'string') {
|
||||||
}
|
if (seenIdsFromTail.has(messageId)) {
|
||||||
seenIds.add(messageId);
|
continue;
|
||||||
}
|
}
|
||||||
return true;
|
seenIdsFromTail.add(messageId);
|
||||||
})
|
}
|
||||||
|
dedupedMessages.push(message);
|
||||||
|
}
|
||||||
|
dedupedMessages.reverse();
|
||||||
|
|
||||||
|
const normalizedMessages = dedupedMessages
|
||||||
.map((message, index) => {
|
.map((message, index) => {
|
||||||
const messageId = typeof message.info?.id === 'string' && message.info.id.length > 0
|
const messageId = typeof message.info?.id === 'string' && message.info.id.length > 0
|
||||||
? message.info.id
|
? message.info.id
|
||||||
|
|||||||
@@ -519,6 +519,9 @@ const hasSameTurnStructure = (prev: ChatMessageEntry[], next: ChatMessageEntry[]
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let index = 0; index < prev.length; index += 1) {
|
for (let index = 0; index < prev.length; index += 1) {
|
||||||
|
if (prev[index] !== next[index]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (prev[index]?.info?.id !== next[index]?.info?.id) {
|
if (prev[index]?.info?.id !== next[index]?.info?.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -543,6 +546,9 @@ const isAppendOnlyChange = (prev: ChatMessageEntry[], next: ChatMessageEntry[]):
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let index = 0; index < prev.length; index += 1) {
|
for (let index = 0; index < prev.length; index += 1) {
|
||||||
|
if (prev[index] !== next[index]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (prev[index]?.info?.id !== next[index]?.info?.id) {
|
if (prev[index]?.info?.id !== next[index]?.info?.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -658,6 +664,7 @@ export const TurnGroupingProvider: React.FC<TurnGroupingProviderProps> = ({ mess
|
|||||||
const cached = staticCacheRef.current;
|
const cached = staticCacheRef.current;
|
||||||
if (
|
if (
|
||||||
cached &&
|
cached &&
|
||||||
|
hasSameTurnStructure(cached.messages, messages) &&
|
||||||
cached.structureKey === structureKey &&
|
cached.structureKey === structureKey &&
|
||||||
cached.defaultActivityExpanded === defaultActivityExpanded &&
|
cached.defaultActivityExpanded === defaultActivityExpanded &&
|
||||||
cached.showTextJustificationActivity === showTextJustificationActivity
|
cached.showTextJustificationActivity === showTextJustificationActivity
|
||||||
|
|||||||
Reference in New Issue
Block a user