Skip to content

Commit b3d9e54

Browse files
TheodoreSpeaksTheodore Li
andauthored
fix(ui) fix task switch causing duplicate text renderings (#3624)
* Fix task switch causing duplicate text renderings * Fix lint * Pass expectedGen --------- Co-authored-by: Theodore Li <theo@sim.ai>
1 parent b930ee3 commit b3d9e54

File tree

1 file changed

+28
-8
lines changed
  • apps/sim/app/workspace/[workspaceId]/home/hooks

1 file changed

+28
-8
lines changed

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,11 @@ export function useChat(
279279

280280
const sendMessageRef = useRef<UseChatReturn['sendMessage']>(async () => {})
281281
const processSSEStreamRef = useRef<
282-
(reader: ReadableStreamDefaultReader<Uint8Array>, assistantId: string) => Promise<void>
282+
(
283+
reader: ReadableStreamDefaultReader<Uint8Array>,
284+
assistantId: string,
285+
expectedGen?: number
286+
) => Promise<void>
283287
>(async () => {})
284288
const finalizeRef = useRef<(options?: { error?: boolean }) => void>(() => {})
285289

@@ -379,7 +383,8 @@ export function useChat(
379383
}
380384

381385
appliedChatIdRef.current = chatHistory.id
382-
setMessages(chatHistory.messages.map(mapStoredMessage))
386+
const mappedMessages = chatHistory.messages.map(mapStoredMessage)
387+
setMessages(mappedMessages)
383388

384389
if (chatHistory.resources.length > 0) {
385390
setResources(chatHistory.resources)
@@ -392,6 +397,7 @@ export function useChat(
392397
}
393398

394399
if (activeStreamId && !sendingRef.current) {
400+
abortControllerRef.current?.abort()
395401
const gen = ++streamGenRef.current
396402
const abortController = new AbortController()
397403
abortControllerRef.current = abortController
@@ -461,7 +467,7 @@ export function useChat(
461467
},
462468
})
463469

464-
await processSSEStreamRef.current(combinedStream.getReader(), assistantId)
470+
await processSSEStreamRef.current(combinedStream.getReader(), assistantId, gen)
465471
} catch (err) {
466472
if (err instanceof Error && err.name === 'AbortError') return
467473
} finally {
@@ -489,7 +495,11 @@ export function useChat(
489495
}, [activeResourceId, resources])
490496

491497
const processSSEStream = useCallback(
492-
async (reader: ReadableStreamDefaultReader<Uint8Array>, assistantId: string) => {
498+
async (
499+
reader: ReadableStreamDefaultReader<Uint8Array>,
500+
assistantId: string,
501+
expectedGen?: number
502+
) => {
493503
const decoder = new TextDecoder()
494504
let buffer = ''
495505
const blocks: ContentBlock[] = []
@@ -511,10 +521,14 @@ export function useChat(
511521
return b
512522
}
513523

524+
const isStale = () => expectedGen !== undefined && streamGenRef.current !== expectedGen
525+
514526
const flush = () => {
527+
if (isStale()) return
515528
streamingBlocksRef.current = [...blocks]
516529
const snapshot = { content: runningText, contentBlocks: [...blocks] }
517530
setMessages((prev) => {
531+
if (expectedGen !== undefined && streamGenRef.current !== expectedGen) return prev
518532
const idx = prev.findIndex((m) => m.id === assistantId)
519533
if (idx >= 0) {
520534
return prev.map((m) => (m.id === assistantId ? { ...m, ...snapshot } : m))
@@ -524,6 +538,10 @@ export function useChat(
524538
}
525539

526540
while (true) {
541+
if (isStale()) {
542+
reader.cancel().catch(() => {})
543+
break
544+
}
527545
const { done, value } = await reader.read()
528546
if (done) break
529547

@@ -975,15 +993,15 @@ export function useChat(
975993
content: message,
976994
...(storedAttachments && { fileAttachments: storedAttachments }),
977995
}
978-
queryClient.setQueryData<TaskChatHistory>(taskKeys.detail(chatIdRef.current), (old) =>
979-
old
996+
queryClient.setQueryData<TaskChatHistory>(taskKeys.detail(chatIdRef.current), (old) => {
997+
return old
980998
? {
981999
...old,
9821000
messages: [...old.messages, cachedUserMsg],
9831001
activeStreamId: userMessageId,
9841002
}
9851003
: undefined
986-
)
1004+
})
9871005
}
9881006

9891007
const userAttachments = storedAttachments?.map(toDisplayAttachment)
@@ -1049,7 +1067,7 @@ export function useChat(
10491067

10501068
if (!response.body) throw new Error('No response body')
10511069

1052-
await processSSEStream(response.body.getReader(), assistantId)
1070+
await processSSEStream(response.body.getReader(), assistantId, gen)
10531071
} catch (err) {
10541072
if (err instanceof Error && err.name === 'AbortError') return
10551073
setError(err instanceof Error ? err.message : 'Failed to send message')
@@ -1180,6 +1198,8 @@ export function useChat(
11801198

11811199
useEffect(() => {
11821200
return () => {
1201+
abortControllerRef.current?.abort()
1202+
abortControllerRef.current = null
11831203
streamGenRef.current++
11841204
sendingRef.current = false
11851205
}

0 commit comments

Comments
 (0)