From 97c94079a91ed26fd556094bf2efd15561853ae6 Mon Sep 17 00:00:00 2001 From: Akarshan Date: Wed, 29 Oct 2025 20:09:56 +0530 Subject: [PATCH] fix: correct thinking time and chat translation keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The update fixes how total thinking time is calculated during a chat message flow. Previously the elapsed time from the initial completion was incorrectly added to the overall thinking time, leading to inflated metrics. The new logic splits the computation into separate phases (initial completion, tool execution, and follow‑up completions) and accumulates them into `totalThinkingTime`, ensuring accurate measurement. Additionally, translation keys for chat components are now namespaced with `chat:` to avoid collisions and clearly indicate the context in which they are used. The diff also removes a stray comment line and keeps metadata updates consistent across recursive calls. --- web-app/src/containers/ThinkingBlock.tsx | 8 +++--- web-app/src/hooks/useChat.ts | 6 +++-- web-app/src/lib/completion.ts | 34 +++++++++++++++++++++--- web-app/src/locales/en/chat.json | 1 + 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/web-app/src/containers/ThinkingBlock.tsx b/web-app/src/containers/ThinkingBlock.tsx index aa5aaff27..ed3e0e328 100644 --- a/web-app/src/containers/ThinkingBlock.tsx +++ b/web-app/src/containers/ThinkingBlock.tsx @@ -10,7 +10,7 @@ import ImageModal from '@/containers/dialogs/ImageModal' // Define ReActStep type (Reasoning-Action Step) type ReActStep = { - type: 'reasoning' | 'tool_call' | 'tool_output' | 'done' // Changed 'thought' to 'reasoning' + type: 'reasoning' | 'tool_call' | 'tool_output' | 'done' content: string metadata?: any time?: number @@ -114,7 +114,7 @@ const ThinkingBlock = ({
- {t('thinking')} + {t('chat:thinking')}
@@ -144,7 +144,7 @@ const ThinkingBlock = ({ const timeInSeconds = formatDuration(step.time ?? 0) const timeDisplay = timeInSeconds > 0 - ? `(${t('for')} ${timeInSeconds} ${t('seconds')})` + ? `(${t('chat:for')} ${timeInSeconds} ${t('chat:seconds')})` : '' return ( @@ -259,7 +259,7 @@ const ThinkingBlock = ({ activeStep.type === 'tool_call' || activeStep.type === 'tool_output' ) { - return `${t('calling_tool')}` // Use a specific translation key for tool + return `${t('chat:calling_tool')}` // Use a specific translation key for tool } else if (activeStep.type === 'reasoning') { return `${t('chat:thinking')}` // Use the generic thinking key } diff --git a/web-app/src/hooks/useChat.ts b/web-app/src/hooks/useChat.ts index ebfdf981f..d2125240c 100644 --- a/web-app/src/hooks/useChat.ts +++ b/web-app/src/hooks/useChat.ts @@ -755,7 +755,9 @@ export const useChat = () => { throw new Error('No response received from the model') } - const totalThinkingTime = Date.now() - startTime // Calculate total elapsed time + const completionFinishTime = Date.now() + // Calculate the time taken for the initial completion (streaming or non-streaming) + const initialCompletionTime = completionFinishTime - startTime const messageMetadata: Record = { tokenSpeed: useAppState.getState().tokenSpeed, @@ -764,7 +766,7 @@ export const useChat = () => { } if (accumulatedText.includes('') || toolCalls.length > 0) { - messageMetadata.totalThinkingTime = totalThinkingTime + messageMetadata.totalThinkingTime = initialCompletionTime } // This is the message object that will be built upon by postMessageProcessing diff --git a/web-app/src/lib/completion.ts b/web-app/src/lib/completion.ts index 6b328143a..9e6be6776 100644 --- a/web-app/src/lib/completion.ts +++ b/web-app/src/lib/completion.ts @@ -406,7 +406,6 @@ export const extractToolCall = ( } return calls } - /** * Helper function to check if a tool call is a browser MCP tool * @param toolName - The name of the tool @@ -558,6 +557,10 @@ export const postMessageProcessing = async ( currentStepCount: number = 0, isProactiveMode: boolean = false ): Promise => { + // Initialize/get the current total thinking time from metadata + // This value is passed from sendMessage (initial completion time) or previous recursive call + let currentTotalTime = (message.metadata?.totalThinkingTime as number) ?? 0 + // Handle completed tool calls if (calls.length > 0) { // Check limit BEFORE processing @@ -604,6 +607,7 @@ export const postMessageProcessing = async ( message.metadata = { ...(message.metadata ?? {}), tool_calls: currentToolCalls, + totalThinkingTime: currentTotalTime, } if (updateStreamingUI) updateStreamingUI({ ...message }) // Show pending call @@ -634,6 +638,8 @@ export const postMessageProcessing = async ( ) : true) + const toolExecutionStartTime = Date.now() + const { promise, cancel } = isRagTool ? ragFeatureAvailable ? { @@ -683,6 +689,8 @@ export const postMessageProcessing = async ( error: 'disallowed', } + const toolExecutionTime = Date.now() - toolExecutionStartTime + if (typeof result === 'string') { result = { content: [{ type: 'text', text: result }], @@ -690,9 +698,15 @@ export const postMessageProcessing = async ( } } + currentTotalTime += toolExecutionTime + // Update the entry in the metadata array toolCallEntry.response = result toolCallEntry.state = 'ready' + message.metadata = { + ...(message.metadata ?? {}), + totalThinkingTime: currentTotalTime, + } if (updateStreamingUI) updateStreamingUI({ ...message }) // Show result const streamEvents = (message.metadata?.streamEvents || []) as any[] @@ -738,6 +752,8 @@ export const postMessageProcessing = async ( try { const messagesWithToolResults = builder.getMessages() + const followUpStartTime = Date.now() + const followUpCompletion = await sendCompletion( thread, provider, @@ -748,6 +764,8 @@ export const postMessageProcessing = async ( {} ) + let streamFinishTime = Date.now() + if (followUpCompletion) { let followUpText = '' const newToolCalls: ChatCompletionMessageToolCall[] = [] @@ -766,6 +784,7 @@ export const postMessageProcessing = async ( } if (textContent?.text) textContent.text.value += followUpText if (updateStreamingUI) updateStreamingUI({ ...message }) + streamFinishTime = Date.now() } else { // Handle streaming response const reasoningProcessor = new ReasoningProcessor() @@ -777,7 +796,6 @@ export const postMessageProcessing = async ( const deltaContent = chunk.choices[0]?.delta?.content || '' if (textContent?.text) { - // if (deltaReasoning) textContent.text.value += deltaReasoning if (deltaContent) { textContent.text.value += deltaContent followUpText += deltaContent @@ -810,6 +828,8 @@ export const postMessageProcessing = async ( message.metadata = { ...(message.metadata ?? {}), streamEvents: streamEvents, + totalThinkingTime: + currentTotalTime + (Date.now() - followUpStartTime), // Optimistic update } if (updateStreamingUI) { @@ -822,7 +842,7 @@ export const postMessageProcessing = async ( updateStreamingUI(uiMessage) } } - + streamFinishTime = Date.now() if (textContent?.text && updateStreamingUI) { // Final UI update after streaming completes const uiMessage: ThreadMessage = { @@ -833,9 +853,17 @@ export const postMessageProcessing = async ( } } + const followUpTotalTime = streamFinishTime - followUpStartTime + currentTotalTime += followUpTotalTime // + message.metadata = { + ...(message.metadata ?? {}), + totalThinkingTime: currentTotalTime, + } + // Recursively process new tool calls if any if (newToolCalls.length > 0) { builder.addAssistantMessage(followUpText, undefined, newToolCalls) + // Recursive call continues accumulation on the same message object await postMessageProcessing( newToolCalls, builder, diff --git a/web-app/src/locales/en/chat.json b/web-app/src/locales/en/chat.json index 05c053e71..ce56d8f21 100644 --- a/web-app/src/locales/en/chat.json +++ b/web-app/src/locales/en/chat.json @@ -13,6 +13,7 @@ "tool_called": "Called tools", "calling_tool": "Calling a tool", "thinking": "Thinking", + "thought": "Thought", "for": "for", "seconds": "seconds" }