From cc013c96808e68ca99338841dd2ba997076e826d Mon Sep 17 00:00:00 2001 From: TYY-2005 Date: Fri, 23 Oct 2026 11:24:26 +0800 Subject: [PATCH] fix(frontend): classify thinking-only token steps correctly (#7175) * fix(frontend): classify thinking-only token steps correctly * docs(frontend): explain thinking-only token step classification --------- Co-authored-by: TYY-2005 <334029581+TYY-2005@users.noreply.github.com> Co-authored-by: Willem Jiang --- frontend/src/core/messages/usage-model.ts | 5 ++-- .../unit/core/messages/usage-model.test.ts | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/frontend/src/core/messages/usage-model.ts b/frontend/src/core/messages/usage-model.ts index 2290716022..11dd05604c 100644 --- a/frontend/src/core/messages/usage-model.ts +++ b/frontend/src/core/messages/usage-model.ts @@ -3,7 +3,7 @@ import type { Message } from "@langchain/langgraph-sdk"; import type { Translations } from "@/core/i18n/locales/types"; import { getUsageMetadata, type TokenUsage } from "./usage"; -import { hasContent } from "./utils"; +import { extractContentFromMessage } from "./utils"; export type TokenUsageInlineMode = "off" | "per_turn" | "step_debug"; @@ -166,7 +166,8 @@ export function buildTokenDebugSteps( } if (actionLabels.length === 0) { - if (hasContent(message)) { + // Thinking-only blocks (Anthropic) carry no visible answer content. + if (extractContentFromMessage(message).length > 0) { actionLabels.push(t.tokenUsage.finalAnswer); } else { actionLabels.push(t.common.thinking); diff --git a/frontend/tests/unit/core/messages/usage-model.test.ts b/frontend/tests/unit/core/messages/usage-model.test.ts index f9354268f4..68156ecad9 100644 --- a/frontend/tests/unit/core/messages/usage-model.test.ts +++ b/frontend/tests/unit/core/messages/usage-model.test.ts @@ -127,6 +127,29 @@ test("uses generic todo labels when backend attribution is absent", () => { ]); }); +test("labels Anthropic thinking-only blocks as thinking", () => { + const messages = [ + { + id: "ai-thinking", + type: "ai", + content: [{ type: "thinking", thinking: "Still checking." }], + usage_metadata: { + input_tokens: 20, + output_tokens: 5, + total_tokens: 25, + }, + }, + ] as unknown as Message[]; + + expect(buildTokenDebugSteps(messages, enUS)).toEqual([ + expect.objectContaining({ + messageId: "ai-thinking", + label: "Thinking", + sharedAttribution: false, + }), + ]); +}); + test("marks multi-action AI steps as shared attribution", () => { const messages = [ {