From 022747ba045e62f6f0dedf613c00979c68cc94fd Mon Sep 17 00:00:00 2001 From: Glen Maddern Date: Wed, 25 Jun 2025 17:06:50 +1000 Subject: [PATCH] wip, changing the logic in useStreamResponse --- .../chat-ui/src/hooks/useStreamResponse.ts | 83 ++++++------------- examples/chat-ui/src/types/index.ts | 13 ++- examples/chat-ui/tsconfig.app.json | 2 +- 3 files changed, 35 insertions(+), 63 deletions(-) diff --git a/examples/chat-ui/src/hooks/useStreamResponse.ts b/examples/chat-ui/src/hooks/useStreamResponse.ts index 412fd96..bcf4c95 100644 --- a/examples/chat-ui/src/hooks/useStreamResponse.ts +++ b/examples/chat-ui/src/hooks/useStreamResponse.ts @@ -115,10 +115,6 @@ export const useStreamResponse = ({ const streamResponse = async (messages: Message[]) => { let aiResponse = '' let currentReasoning = '' - let currentReasoningStartTime: number | undefined = undefined - let currentReasoningEndTime: number | undefined = undefined - let assistantMessageIndex = -1 // Track the index of our assistant message - let assistantMessageCreated = false // Track if we've created the assistant message yet // Check if API key is available let apiKey = getApiKey(selectedModel.provider.id) @@ -165,51 +161,38 @@ export const useStreamResponse = ({ console.log(JSON.stringify(event)) try { if (event.type === 'reasoning') { - // Track reasoning start time and create assistant message if needed - if (!currentReasoningStartTime) { - currentReasoningStartTime = Date.now() - currentReasoning = '' // Reset reasoning content for new session + setConversations((prev) => { + const updated = [...prev] + const conv = updated.find((c) => c.id === conversationId) + if (!conv) { + console.error(`Missing conversation for ID ${conversationId}! Ignoring ${JSON.stringify(event)}`) + return updated + } + const lastMessage = conv.messages?.at(-1) - // Create a new assistant message for reasoning (even if we already have one) - // This handles multiple reasoning sessions in the same conversation turn - setConversations((prev) => { - const updated = [...prev] - const conv = updated.find((c) => c.id === conversationId) + if (lastMessage?.role === 'assistant' && lastMessage.type === 'reasoning') { + // We have an existing reasoning block! + lastMessage.content = lastMessage.content + event.textDelta.trim() + } else { + // We need a new reasoning block! + const currentReasoningStartTime = Date.now() + + // Create a new assistant message for reasoning (even if we already have one) + // This handles multiple reasoning sessions in the same conversation turn if (conv) { conv.messages.push({ role: 'assistant', - content: '', - reasoning: '', + type: 'reasoning', + content: event.textDelta.trim(), isReasoningStreaming: true, - reasoningStartTime: currentReasoningStartTime, + reasoningStartTime: currentReasoningStartTime!, }) - assistantMessageIndex = conv.messages.length - 1 - debugMessages('Added new reasoning assistant message at index', assistantMessageIndex) + debugMessages('Added new reasoning assistant message at index', conv.messages.length - 1) debugMessages('Current messages:', JSON.stringify(conv.messages)) } - return updated - }) - assistantMessageCreated = true - } - - currentReasoning += (event as any).textDelta || '' - - // Update the assistant message with streaming reasoning in real-time - if (assistantMessageCreated) { - 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.reasoning = currentReasoning.trim() - assistantMessage.isReasoningStreaming = true - assistantMessage.reasoningStartTime = currentReasoningStartTime - } - } - return updated - }) - } + } + return updated + }) } else if (event.type === 'tool-call') { // End reasoning phase immediately when we see a tool call for linear flow if (currentReasoningStartTime && !currentReasoningEndTime) { @@ -345,21 +328,6 @@ export const useStreamResponse = ({ aiResponse = aiResponse.replace(/.*?<\/chat-title>/, '').trim() } - // Remove thinking tags from the display text (they should be extracted by middleware) - let cleanedResponse = aiResponse - if (supportsReasoning(selectedModel)) { - // Remove complete thinking blocks - cleanedResponse = aiResponse.replace(/[\s\S]*?<\/think>/g, '').trim() - // Remove unclosed think tags (everything from to end) - cleanedResponse = cleanedResponse.replace(/[\s\S]*$/g, '').trim() - // Remove any remaining opening think tags - cleanedResponse = cleanedResponse.replace(//g, '').trim() - // Remove any remaining closing think tags - cleanedResponse = cleanedResponse.replace(/<\/think>/g, '').trim() - // Clean up any remaining whitespace or newlines at the start - cleanedResponse = cleanedResponse.replace(/^\s+/, '') - } - setConversations((prev) => { const updated = [...prev] const conv = updated.find((c) => c.id === conversationId) @@ -367,8 +335,7 @@ export const useStreamResponse = ({ // Update the specific assistant message we created, not just the last message const assistantMessage = conv.messages[assistantMessageIndex] if (assistantMessage && hasContent(assistantMessage) && assistantMessage.role === 'assistant') { - assistantMessage.content = cleanedResponse - } else { + assistantMessage.content = aiResponse } } else { } diff --git a/examples/chat-ui/src/types/index.ts b/examples/chat-ui/src/types/index.ts index 0c55608..3ad1abb 100644 --- a/examples/chat-ui/src/types/index.ts +++ b/examples/chat-ui/src/types/index.ts @@ -12,10 +12,15 @@ export interface SystemMessage extends BaseMessage { export interface AssistantMessage extends BaseMessage { role: 'assistant' - reasoning?: string - reasoningStartTime?: number + type: 'content' +} + +export interface AssistantReasoningMessage extends BaseMessage { + role: 'assistant' + type: 'reasoning' + reasoningStartTime: number reasoningEndTime?: number - isReasoningStreaming?: boolean + isReasoningStreaming: boolean } export interface ToolCallMessage { @@ -33,7 +38,7 @@ export interface ToolResultMessage { callId: string } -export type Message = UserMessage | SystemMessage | AssistantMessage | ToolCallMessage | ToolResultMessage +export type Message = UserMessage | SystemMessage | AssistantMessage | AssistantReasoningMessage | ToolCallMessage | ToolResultMessage export interface Conversation { id?: number diff --git a/examples/chat-ui/tsconfig.app.json b/examples/chat-ui/tsconfig.app.json index 358ca9b..3c03047 100644 --- a/examples/chat-ui/tsconfig.app.json +++ b/examples/chat-ui/tsconfig.app.json @@ -3,7 +3,7 @@ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2020", "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], + "lib": ["ES2022", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true,