fix: glitch UI thinking or duplicate content when multiple think after tools call (#5217)

* fix: update id streaming content think

* fix: glitch UI or duplicate content when multiple thinking
This commit is contained in:
Faisal Amir 2025-06-09 11:36:36 +07:00 committed by GitHub
parent 44b5310a6a
commit 1bbac32d88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 2 deletions

View File

@ -1,20 +1,53 @@
import { useAppState } from '@/hooks/useAppState'
import { ThreadContent } from './ThreadContent'
import { memo } from 'react'
import { memo, useMemo } from 'react'
import { useMessages } from '@/hooks/useMessages'
type Props = {
threadId: string
}
// Helper to extract <think>...</think> segment
function extractReasoningSegment(text: string) {
if (!text) return ''
const match = text.match(/<think>([\s\S]*?)<\/think>/)
if (match) return match[0].trim()
// If only opening <think> and no closing, take everything after <think>
const openIdx = text.indexOf('<think>')
if (openIdx !== -1) return text.slice(openIdx).trim()
return ''
}
// Use memo with no dependencies to allow re-renders when props change
// Avoid duplicate reasoning segments after tool calls
export const StreamingContent = memo(({ threadId }: Props) => {
const { streamingContent } = useAppState()
const { getMessages } = useMessages()
const messages = getMessages(threadId)
const streamingReasoning = useMemo(() => {
const text =
streamingContent?.content?.find((e) => e.type === 'text')?.text?.value ||
''
return extractReasoningSegment(text)
}, [streamingContent])
const lastAssistant = useMemo(() => {
return [...messages].reverse().find((m) => m.role === 'assistant')
}, [messages])
const lastAssistantReasoning = useMemo(() => {
if (!lastAssistant) return ''
const text =
lastAssistant.content?.find((e) => e.type === 'text')?.text?.value || ''
return extractReasoningSegment(text)
}, [lastAssistant])
if (!streamingContent || streamingContent.thread_id !== threadId) return null
if (streamingReasoning && streamingReasoning === lastAssistantReasoning) {
return null
}
// Pass a new object to ThreadContent to avoid reference issues
// The streaming content is always the last message
return (

View File

@ -302,7 +302,7 @@ export const ThreadContent = memo(
<ThinkingBlock
id={
item.isLastMessage
? `${item.thread_id}-last`
? `${item.thread_id}-last-${reasoningSegment.slice(0, 50).replace(/\s/g, '').slice(-10)}`
: `${item.thread_id}-${item.index ?? item.id}`
}
text={reasoningSegment}