Skip to content

Commit d27f204

Browse files
authored
fix: user and assistant messages now properly sent to chat API (#167)
1 parent 072d2a4 commit d27f204

File tree

1 file changed

+40
-4
lines changed

1 file changed

+40
-4
lines changed

src/components/Chat.tsx

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useMemo, useRef, useState } from 'react'
1+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
22
import { useChat } from 'ai/react'
33
import { MessageSquarePlus } from 'lucide-react'
44
import { useModel } from '../contexts/ModelContext'
@@ -61,6 +61,7 @@ const TIMEOUT_ERROR_MESSAGE =
6161
export function Chat() {
6262
const hasMounted = useHasMounted()
6363
const messagesEndRef = useRef<HTMLDivElement>(null)
64+
const seenMessageIds = useRef(new Set<string>())
6465
const [hasStartedChat, setHasStartedChat] = useState(false)
6566
const [focusTimestamp, setFocusTimestamp] = useState(Date.now())
6667
const [servers, setServers] = useState<Servers>({})
@@ -127,16 +128,50 @@ export function Chat() {
127128
onResponse: handleResponse,
128129
})
129130

131+
useEffect(() => {
132+
if (!streaming && streamBuffer.length > 0) {
133+
// Extract only assistant messages from streamBuffer to sync with useChat messages
134+
// (user messages are already in messages via append)
135+
const assistantEvents = streamBuffer.filter(
136+
(event): event is Extract<StreamEvent, { type: 'assistant' }> =>
137+
event.type === 'assistant',
138+
)
139+
140+
if (assistantEvents.length > 0) {
141+
setMessages((prevMessages) => {
142+
// Convert assistant stream events to Message objects, filtering duplicates using ref
143+
const newMessages = assistantEvents
144+
.filter((event) => !seenMessageIds.current.has(event.id))
145+
.map((event) => ({
146+
id: event.id,
147+
role: 'assistant' as const,
148+
content: event.content,
149+
}))
150+
151+
// Track newly added message IDs
152+
newMessages.forEach((msg) => seenMessageIds.current.add(msg.id))
153+
154+
return [...prevMessages, ...newMessages]
155+
})
156+
}
157+
158+
// DO NOT clear the buffer here - we need to preserve all events for rendering
159+
// The buffer contains tool calls, code interpreter events, etc. that need to be displayed
160+
}
161+
}, [streaming, streamBuffer, setMessages])
162+
130163
const renderEvents = useMemo<Array<StreamEvent | Message>>(() => {
131-
if (streaming || streamBuffer.length > 0) {
132-
return [...streamBuffer]
164+
// If we have streamBuffer content, render it directly (contains all events in order)
165+
if (streamBuffer.length > 0) {
166+
return streamBuffer
133167
}
134168
// Show initial message only when there are no real messages and no streaming
135169
if (messages.length === 0 && !hasStartedChat) {
136170
return [initialMessage]
137171
}
172+
// After buffer is cleared (new chat), render from messages
138173
return messages
139-
}, [streaming, streamBuffer, messages, hasStartedChat, initialMessage])
174+
}, [streamBuffer, messages, hasStartedChat, initialMessage])
140175

141176
const handleSendMessage = useCallback(
142177
(prompt: string) => {
@@ -164,6 +199,7 @@ export function Chat() {
164199
setHasStartedChat(false)
165200
clearBuffer()
166201
setMessages([])
202+
seenMessageIds.current.clear()
167203
setFocusTimestamp(Date.now())
168204
setUseCodeInterpreter(false)
169205
setUseWebSearch(false)

0 commit comments

Comments
 (0)