From 3788337d974289a313ef6c1918f097f4a18feff9 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Fri, 11 Jul 2025 13:39:14 -0500 Subject: [PATCH 1/8] fix: exclude cache tokens from context window calculation - Modified getApiMetrics.ts to calculate contextTokens as tokensIn + tokensOut only - Updated tests to reflect the new calculation behavior - Cache tokens (cacheWrites and cacheReads) no longer count towards context window - Fixes inflated context token display in UI that was showing ~170k instead of ~44k --- src/shared/__tests__/getApiMetrics.spec.ts | 20 ++++++++++---------- src/shared/getApiMetrics.ts | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/shared/__tests__/getApiMetrics.spec.ts b/src/shared/__tests__/getApiMetrics.spec.ts index a1b1eecaed8..ff901d088c3 100644 --- a/src/shared/__tests__/getApiMetrics.spec.ts +++ b/src/shared/__tests__/getApiMetrics.spec.ts @@ -61,7 +61,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(5) expect(result.totalCacheReads).toBe(10) expect(result.totalCost).toBe(0.005) - expect(result.contextTokens).toBe(315) // 100 + 200 + 5 + 10 + expect(result.contextTokens).toBe(300) // 100 + 200 }) it("should calculate metrics from multiple api_req_started messages", () => { @@ -83,7 +83,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(8) // 5 + 3 expect(result.totalCacheReads).toBe(17) // 10 + 7 expect(result.totalCost).toBe(0.008) // 0.005 + 0.003 - expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (from the last message) + expect(result.contextTokens).toBe(200) // 50 + 150 (from the last message) }) it("should calculate metrics from condense_context messages", () => { @@ -123,7 +123,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(8) // 5 + 3 expect(result.totalCacheReads).toBe(17) // 10 + 7 expect(result.totalCost).toBe(0.01) // 0.005 + 0.002 + 0.003 - expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (from the last api_req_started message) + expect(result.contextTokens).toBe(200) // 50 + 150 (from the last api_req_started message) }) }) @@ -242,9 +242,9 @@ describe("getApiMetrics", () => { expect(result.totalCacheReads).toBe(10) expect(result.totalCost).toBe(0.005) - // The implementation will use the last message with tokens for contextTokens - // In this case, it's the cacheReads message - expect(result.contextTokens).toBe(10) + // The implementation will use the last message with tokensIn or tokensOut for contextTokens + // In this case, it's the tokensOut message (200) + expect(result.contextTokens).toBe(200) // From the message with tokensOut }) it("should handle non-number values in api_req_started message", () => { @@ -265,7 +265,7 @@ describe("getApiMetrics", () => { expect(result.totalCost).toBe(0) // The implementation concatenates string values for contextTokens - expect(result.contextTokens).toBe("not-a-numbernot-a-numbernot-a-numbernot-a-number") + expect(result.contextTokens).toBe("not-a-numbernot-a-number") // Only tokensIn + tokensOut }) }) @@ -279,7 +279,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should use the values from the last api_req_started message - expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 + expect(result.contextTokens).toBe(200) // 50 + 150 }) it("should calculate contextTokens from the last condense_context message", () => { @@ -305,7 +305,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should use the values from the last api_req_started message - expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 + expect(result.contextTokens).toBe(200) // 50 + 150 }) it("should handle missing values when calculating contextTokens", () => { @@ -320,7 +320,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should handle missing or invalid values - expect(result.contextTokens).toBe(15) // 0 + 0 + 5 + 10 + expect(result.contextTokens).toBe(0) // 0 + 0 (no tokensIn or tokensOut) // Restore console.error console.error = originalConsoleError diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index 49476fdbb6d..32ba2f2060e 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -73,7 +73,7 @@ export function getApiMetrics(messages: ClineMessage[]) { try { const parsedText: ParsedApiReqStartedTextType = JSON.parse(message.text) const { tokensIn, tokensOut, cacheWrites, cacheReads } = parsedText - result.contextTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0) + result.contextTokens = (tokensIn || 0) + (tokensOut || 0) } catch (error) { console.error("Error parsing JSON:", error) continue From 26cfd92cc3dd95b83828b86940e15a82cdaa30f2 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Fri, 11 Jul 2025 13:47:05 -0500 Subject: [PATCH 2/8] fix: remove unused cacheWrites and cacheReads from API metrics calculation --- src/shared/getApiMetrics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index 32ba2f2060e..6ebb953aaf8 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -72,7 +72,7 @@ export function getApiMetrics(messages: ClineMessage[]) { if (message.type === "say" && message.say === "api_req_started" && message.text) { try { const parsedText: ParsedApiReqStartedTextType = JSON.parse(message.text) - const { tokensIn, tokensOut, cacheWrites, cacheReads } = parsedText + const { tokensIn, tokensOut } = parsedText result.contextTokens = (tokensIn || 0) + (tokensOut || 0) } catch (error) { console.error("Error parsing JSON:", error) From 1459ddccc07344d7b80f233d7f425fed16c783fd Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Fri, 11 Jul 2025 14:01:40 -0500 Subject: [PATCH 3/8] fix: yield incremental token usage for Claude Code assistant messages - Modified claude-code.ts to yield incremental token usage after each assistant message - This ensures the UI receives real-time token updates during streaming - Prevents accumulation issues in Task.ts which was causing incorrect totals - Fixes #5600 where assistant messages showed incorrect low token counts --- src/api/providers/claude-code.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/api/providers/claude-code.ts b/src/api/providers/claude-code.ts index bc72e658fe5..aae319d90fc 100644 --- a/src/api/providers/claude-code.ts +++ b/src/api/providers/claude-code.ts @@ -108,6 +108,16 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler { } } + // Yield incremental usage for this message only + yield { + type: "usage" as const, + inputTokens: message.usage.input_tokens, + outputTokens: message.usage.output_tokens, + cacheReadTokens: message.usage.cache_read_input_tokens || 0, + cacheWriteTokens: message.usage.cache_creation_input_tokens || 0, + } + + // Continue accumulating for the final result usage.inputTokens += message.usage.input_tokens usage.outputTokens += message.usage.output_tokens usage.cacheReadTokens = (usage.cacheReadTokens || 0) + (message.usage.cache_read_input_tokens || 0) From 6ff2a93dc4c15e90e7da482268c0fde81e9d3147 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Fri, 11 Jul 2025 15:11:10 -0500 Subject: [PATCH 4/8] feat: add API protocol handling for providers and update related types --- packages/types/src/message.ts | 1 + packages/types/src/provider-settings.ts | 8 ++++++++ src/core/task/Task.ts | 10 +++++++++- src/shared/ExtensionMessage.ts | 1 + src/shared/getApiMetrics.ts | 14 ++++++++++++-- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index 49d5203c664..0c87655fc0c 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -155,6 +155,7 @@ export const clineMessageSchema = z.object({ progressStatus: toolProgressStatusSchema.optional(), contextCondense: contextCondenseSchema.optional(), isProtected: z.boolean().optional(), + apiProtocol: z.union([z.literal("openai"), z.literal("anthropic")]).optional(), }) export type ClineMessage = z.infer diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index e940ececd1d..0d6dd613070 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -292,3 +292,11 @@ export const getModelId = (settings: ProviderSettings): string | undefined => { const modelIdKey = MODEL_ID_KEYS.find((key) => settings[key]) return modelIdKey ? (settings[modelIdKey] as string) : undefined } + +// Providers that use Anthropic-style API protocol +export const ANTHROPIC_STYLE_PROVIDERS: ProviderName[] = ["anthropic", "claude-code"] + +// Helper function to determine API protocol for a provider +export const getApiProtocol = (provider: ProviderName | undefined): "anthropic" | "openai" => { + return provider && ANTHROPIC_STYLE_PROVIDERS.includes(provider) ? "anthropic" : "openai" +} diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 31260cd6fa2..c8553a8fc67 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -21,6 +21,7 @@ import { type HistoryItem, TelemetryEventName, TodoItem, + getApiProtocol, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" import { CloudService } from "@roo-code/cloud" @@ -1207,11 +1208,16 @@ export class Task extends EventEmitter { // top-down build file structure of project which for large projects can // take a few seconds. For the best UX we show a placeholder api_req_started // message with a loading spinner as this happens. + + // Determine API protocol based on provider + const apiProtocol = getApiProtocol(this.apiConfiguration.apiProvider) + await this.say( "api_req_started", JSON.stringify({ request: userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n") + "\n\nLoading...", + apiProtocol, }), ) @@ -1243,6 +1249,7 @@ export class Task extends EventEmitter { this.clineMessages[lastApiReqIndex].text = JSON.stringify({ request: finalUserContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n"), + apiProtocol, } satisfies ClineApiReqInfo) await this.saveClineMessages() @@ -1263,8 +1270,9 @@ export class Task extends EventEmitter { // of prices in tasks from history (it's worth removing a few months // from now). const updateApiReqMsg = (cancelReason?: ClineApiReqCancelReason, streamingFailedMessage?: string) => { + const existingData = JSON.parse(this.clineMessages[lastApiReqIndex].text || "{}") this.clineMessages[lastApiReqIndex].text = JSON.stringify({ - ...JSON.parse(this.clineMessages[lastApiReqIndex].text || "{}"), + ...existingData, tokensIn: inputTokens, tokensOut: outputTokens, cacheWrites: cacheWriteTokens, diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 953c0c1070e..8aead4674a5 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -379,6 +379,7 @@ export interface ClineApiReqInfo { cost?: number cancelReason?: ClineApiReqCancelReason streamingFailedMessage?: string + apiProtocol?: "anthropic" | "openai" } export type ClineApiReqCancelReason = "streaming_failed" | "user_cancelled" diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index 6ebb953aaf8..9242b896401 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -6,6 +6,7 @@ export type ParsedApiReqStartedTextType = { cacheWrites: number cacheReads: number cost?: number // Only present if combineApiRequests has been called + apiProtocol?: "anthropic" | "openai" } /** @@ -72,8 +73,17 @@ export function getApiMetrics(messages: ClineMessage[]) { if (message.type === "say" && message.say === "api_req_started" && message.text) { try { const parsedText: ParsedApiReqStartedTextType = JSON.parse(message.text) - const { tokensIn, tokensOut } = parsedText - result.contextTokens = (tokensIn || 0) + (tokensOut || 0) + const { tokensIn, tokensOut, cacheWrites, cacheReads, apiProtocol } = parsedText + + console.log("APi Protocol:", apiProtocol) + + // Calculate context tokens based on API protocol + if (apiProtocol === "openai") { + result.contextTokens = (tokensIn || 0) + (tokensOut || 0) + } else { + // For Anthropic (or when protocol is not specified) + result.contextTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0) + } } catch (error) { console.error("Error parsing JSON:", error) continue From 2f61f1576c2495a4cbf1c4e8264131a0da438cb7 Mon Sep 17 00:00:00 2001 From: Daniel <57051444+daniel-lxs@users.noreply.github.com> Date: Fri, 11 Jul 2025 15:18:48 -0500 Subject: [PATCH 5/8] Update src/shared/getApiMetrics.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- src/shared/getApiMetrics.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index 9242b896401..9b9cb966ba8 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -75,7 +75,6 @@ export function getApiMetrics(messages: ClineMessage[]) { const parsedText: ParsedApiReqStartedTextType = JSON.parse(message.text) const { tokensIn, tokensOut, cacheWrites, cacheReads, apiProtocol } = parsedText - console.log("APi Protocol:", apiProtocol) // Calculate context tokens based on API protocol if (apiProtocol === "openai") { From 1f10c2765f901a4163aa1b5007c2b7a5b62df3ee Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Fri, 11 Jul 2025 15:23:35 -0500 Subject: [PATCH 6/8] revert this --- src/api/providers/claude-code.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/api/providers/claude-code.ts b/src/api/providers/claude-code.ts index aae319d90fc..bc72e658fe5 100644 --- a/src/api/providers/claude-code.ts +++ b/src/api/providers/claude-code.ts @@ -108,16 +108,6 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler { } } - // Yield incremental usage for this message only - yield { - type: "usage" as const, - inputTokens: message.usage.input_tokens, - outputTokens: message.usage.output_tokens, - cacheReadTokens: message.usage.cache_read_input_tokens || 0, - cacheWriteTokens: message.usage.cache_creation_input_tokens || 0, - } - - // Continue accumulating for the final result usage.inputTokens += message.usage.input_tokens usage.outputTokens += message.usage.output_tokens usage.cacheReadTokens = (usage.cacheReadTokens || 0) + (message.usage.cache_read_input_tokens || 0) From 527f36a65c2ab7047cf23a7bc5d6377935d6dc13 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Fri, 11 Jul 2025 15:26:48 -0500 Subject: [PATCH 7/8] fix: update contextTokens calculation for API protocols --- src/shared/__tests__/getApiMetrics.spec.ts | 22 +++++++++++----------- src/shared/getApiMetrics.ts | 9 ++++----- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/shared/__tests__/getApiMetrics.spec.ts b/src/shared/__tests__/getApiMetrics.spec.ts index ff901d088c3..ed16b6c7d9a 100644 --- a/src/shared/__tests__/getApiMetrics.spec.ts +++ b/src/shared/__tests__/getApiMetrics.spec.ts @@ -61,7 +61,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(5) expect(result.totalCacheReads).toBe(10) expect(result.totalCost).toBe(0.005) - expect(result.contextTokens).toBe(300) // 100 + 200 + expect(result.contextTokens).toBe(315) // 100 + 200 + 5 + 10 (includes cache tokens) }) it("should calculate metrics from multiple api_req_started messages", () => { @@ -83,7 +83,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(8) // 5 + 3 expect(result.totalCacheReads).toBe(17) // 10 + 7 expect(result.totalCost).toBe(0.008) // 0.005 + 0.003 - expect(result.contextTokens).toBe(200) // 50 + 150 (from the last message) + expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (from the last message, includes cache tokens) }) it("should calculate metrics from condense_context messages", () => { @@ -123,7 +123,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(8) // 5 + 3 expect(result.totalCacheReads).toBe(17) // 10 + 7 expect(result.totalCost).toBe(0.01) // 0.005 + 0.002 + 0.003 - expect(result.contextTokens).toBe(200) // 50 + 150 (from the last api_req_started message) + expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (from the last api_req_started message, includes cache tokens) }) }) @@ -242,9 +242,9 @@ describe("getApiMetrics", () => { expect(result.totalCacheReads).toBe(10) expect(result.totalCost).toBe(0.005) - // The implementation will use the last message with tokensIn or tokensOut for contextTokens - // In this case, it's the tokensOut message (200) - expect(result.contextTokens).toBe(200) // From the message with tokensOut + // The implementation will use the last message that has any tokens + // In this case, it's the cacheReads message (10) + expect(result.contextTokens).toBe(10) // Only cacheReads from the last message }) it("should handle non-number values in api_req_started message", () => { @@ -264,8 +264,8 @@ describe("getApiMetrics", () => { expect(result.totalCacheReads).toBeUndefined() expect(result.totalCost).toBe(0) - // The implementation concatenates string values for contextTokens - expect(result.contextTokens).toBe("not-a-numbernot-a-number") // Only tokensIn + tokensOut + // The implementation concatenates all token values including cache tokens + expect(result.contextTokens).toBe("not-a-numbernot-a-numbernot-a-numbernot-a-number") // tokensIn + tokensOut + cacheWrites + cacheReads }) }) @@ -279,7 +279,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should use the values from the last api_req_started message - expect(result.contextTokens).toBe(200) // 50 + 150 + expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (includes cache tokens) }) it("should calculate contextTokens from the last condense_context message", () => { @@ -305,7 +305,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should use the values from the last api_req_started message - expect(result.contextTokens).toBe(200) // 50 + 150 + expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (includes cache tokens) }) it("should handle missing values when calculating contextTokens", () => { @@ -320,7 +320,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should handle missing or invalid values - expect(result.contextTokens).toBe(0) // 0 + 0 (no tokensIn or tokensOut) + expect(result.contextTokens).toBe(15) // 0 + 0 + 5 + 10 (only cache tokens) // Restore console.error console.error = originalConsoleError diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index 9b9cb966ba8..dcd9ae9efe6 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -75,13 +75,12 @@ export function getApiMetrics(messages: ClineMessage[]) { const parsedText: ParsedApiReqStartedTextType = JSON.parse(message.text) const { tokensIn, tokensOut, cacheWrites, cacheReads, apiProtocol } = parsedText - // Calculate context tokens based on API protocol - if (apiProtocol === "openai") { - result.contextTokens = (tokensIn || 0) + (tokensOut || 0) - } else { - // For Anthropic (or when protocol is not specified) + if (apiProtocol === "anthropic") { result.contextTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0) + } else { + // For OpenAI (or when protocol is not specified) + result.contextTokens = (tokensIn || 0) + (tokensOut || 0) } } catch (error) { console.error("Error parsing JSON:", error) From 62c9d5dd78999db3edc0d1718d0954e35032dfe1 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Fri, 11 Jul 2025 15:40:31 -0500 Subject: [PATCH 8/8] test: update getApiMetrics tests for inverted API protocol logic --- src/shared/__tests__/getApiMetrics.spec.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/shared/__tests__/getApiMetrics.spec.ts b/src/shared/__tests__/getApiMetrics.spec.ts index ed16b6c7d9a..02f45c5cc4c 100644 --- a/src/shared/__tests__/getApiMetrics.spec.ts +++ b/src/shared/__tests__/getApiMetrics.spec.ts @@ -61,7 +61,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(5) expect(result.totalCacheReads).toBe(10) expect(result.totalCost).toBe(0.005) - expect(result.contextTokens).toBe(315) // 100 + 200 + 5 + 10 (includes cache tokens) + expect(result.contextTokens).toBe(300) // 100 + 200 (OpenAI default, no cache tokens) }) it("should calculate metrics from multiple api_req_started messages", () => { @@ -83,7 +83,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(8) // 5 + 3 expect(result.totalCacheReads).toBe(17) // 10 + 7 expect(result.totalCost).toBe(0.008) // 0.005 + 0.003 - expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (from the last message, includes cache tokens) + expect(result.contextTokens).toBe(200) // 50 + 150 (OpenAI default, no cache tokens) }) it("should calculate metrics from condense_context messages", () => { @@ -123,7 +123,7 @@ describe("getApiMetrics", () => { expect(result.totalCacheWrites).toBe(8) // 5 + 3 expect(result.totalCacheReads).toBe(17) // 10 + 7 expect(result.totalCost).toBe(0.01) // 0.005 + 0.002 + 0.003 - expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (from the last api_req_started message, includes cache tokens) + expect(result.contextTokens).toBe(200) // 50 + 150 (OpenAI default, no cache tokens) }) }) @@ -243,8 +243,8 @@ describe("getApiMetrics", () => { expect(result.totalCost).toBe(0.005) // The implementation will use the last message that has any tokens - // In this case, it's the cacheReads message (10) - expect(result.contextTokens).toBe(10) // Only cacheReads from the last message + // In this case, it's the message with tokensOut:200 (since the last few messages have no tokensIn/Out) + expect(result.contextTokens).toBe(200) // 0 + 200 (from the tokensOut message) }) it("should handle non-number values in api_req_started message", () => { @@ -265,7 +265,7 @@ describe("getApiMetrics", () => { expect(result.totalCost).toBe(0) // The implementation concatenates all token values including cache tokens - expect(result.contextTokens).toBe("not-a-numbernot-a-numbernot-a-numbernot-a-number") // tokensIn + tokensOut + cacheWrites + cacheReads + expect(result.contextTokens).toBe("not-a-numbernot-a-number") // tokensIn + tokensOut (OpenAI default) }) }) @@ -279,7 +279,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should use the values from the last api_req_started message - expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (includes cache tokens) + expect(result.contextTokens).toBe(200) // 50 + 150 (OpenAI default, no cache tokens) }) it("should calculate contextTokens from the last condense_context message", () => { @@ -305,7 +305,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should use the values from the last api_req_started message - expect(result.contextTokens).toBe(210) // 50 + 150 + 3 + 7 (includes cache tokens) + expect(result.contextTokens).toBe(200) // 50 + 150 (OpenAI default, no cache tokens) }) it("should handle missing values when calculating contextTokens", () => { @@ -320,7 +320,7 @@ describe("getApiMetrics", () => { const result = getApiMetrics(messages) // Should handle missing or invalid values - expect(result.contextTokens).toBe(15) // 0 + 0 + 5 + 10 (only cache tokens) + expect(result.contextTokens).toBe(0) // 0 + 0 (OpenAI default, no cache tokens) // Restore console.error console.error = originalConsoleError