From b810f639aadab826846aca567bc5ef22a10b5a4a Mon Sep 17 00:00:00 2001 From: Glen Maddern Date: Tue, 24 Jun 2025 19:03:43 +1000 Subject: [PATCH] fix: Create assistant message immediately when reasoning starts - Create assistant message as soon as first reasoning event arrives - This ensures reasoning block is visible from the beginning of thinking - Prevent duplicate message creation in text-delta handler - Move reasoning end time logic to text-delta start (when thinking finishes) This fixes the issue where reasoning blocks weren't appearing because the assistant message was only created after reasoning completed. --- .../chat-ui/src/hooks/useStreamResponse.ts | 63 ++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/examples/chat-ui/src/hooks/useStreamResponse.ts b/examples/chat-ui/src/hooks/useStreamResponse.ts index d8d0408..669922c 100644 --- a/examples/chat-ui/src/hooks/useStreamResponse.ts +++ b/examples/chat-ui/src/hooks/useStreamResponse.ts @@ -203,10 +203,32 @@ export const useStreamResponse = ({ if (event.type === 'reasoning') { debugLog(`[useStreamResponse] Reasoning event:`, event) - // Track reasoning start time + // Track reasoning start time and create assistant message if needed if (!reasoningStartTime) { reasoningStartTime = Date.now() debugLog(`[useStreamResponse] Reasoning started at:`, reasoningStartTime) + + // Create assistant message immediately when reasoning starts + if (!assistantMessageCreated) { + debugLog(`[useStreamResponse] Creating assistant message for reasoning`) + setConversations((prev) => { + const updated = [...prev] + const conv = updated.find((c) => c.id === conversationId) + if (conv) { + conv.messages.push({ + role: 'assistant', + content: '', + reasoning: '', + isReasoningStreaming: true, + reasoningStartTime: reasoningStartTime + }) + assistantMessageIndex = conv.messages.length - 1 + debugLog(`[useStreamResponse] Created assistant message at index ${assistantMessageIndex}`) + } + return updated + }) + assistantMessageCreated = true + } } reasoning += (event as any).textDelta || '' @@ -269,16 +291,9 @@ export const useStreamResponse = ({ } else if (event.type === 'text-delta') { debugLog(`[useStreamResponse] Text delta:`, event.textDelta) - // Create assistant message on first text delta (after all tool calls/results) + // Create assistant message on first text delta if not already created if (!assistantMessageCreated) { debugLog(`[useStreamResponse] Creating assistant message after tool calls/results`) - - // Mark reasoning end time when text starts (reasoning is done) - if (reasoningStartTime && !reasoningEndTime) { - reasoningEndTime = Date.now() - debugLog(`[useStreamResponse] Reasoning ended at:`, reasoningEndTime, 'Duration:', reasoningEndTime - reasoningStartTime, 'ms') - } - setConversations((prev) => { const updated = [...prev] const conv = updated.find((c) => c.id === conversationId) @@ -290,22 +305,26 @@ export const useStreamResponse = ({ return updated }) assistantMessageCreated = true + } + + // Mark reasoning end time when text starts (reasoning is done) + if (reasoningStartTime && !reasoningEndTime) { + reasoningEndTime = Date.now() + debugLog(`[useStreamResponse] Reasoning ended at:`, reasoningEndTime, 'Duration:', reasoningEndTime - reasoningStartTime, 'ms') // Update reasoning end time and stop streaming - if (reasoningEndTime) { - setConversations((prev) => { - const updated = [...prev] - const conv = updated.find((c) => c.id === conversationId) - if (conv && assistantMessageIndex >= 0) { - const assistantMessage = conv.messages[assistantMessageIndex] - if (assistantMessage && hasContent(assistantMessage) && assistantMessage.role === 'assistant') { - assistantMessage.isReasoningStreaming = false - assistantMessage.reasoningEndTime = reasoningEndTime - } + setConversations((prev) => { + const updated = [...prev] + const conv = updated.find((c) => c.id === conversationId) + if (conv && assistantMessageIndex >= 0) { + const assistantMessage = conv.messages[assistantMessageIndex] + if (assistantMessage && hasContent(assistantMessage) && assistantMessage.role === 'assistant') { + assistantMessage.isReasoningStreaming = false + assistantMessage.reasoningEndTime = reasoningEndTime } - return updated - }) - } + } + return updated + }) } aiResponse += event.textDelta