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"
}