From 9480b515cc96f91606176b88e3a483b23293e06c Mon Sep 17 00:00:00 2001 From: julioVianadev Date: Mon, 25 May 2026 11:13:30 -0300 Subject: [PATCH] feat: expose cached token count in usage statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provider adapters now forward cached/prompt-cache token counts into the unified `cachedTokens` field on `RunFinishedEvent.usage`, `UsageInfo`, and `FinishInfo`. This lets consumers calculate accurate costs by distinguishing cached vs uncached input tokens. Provider mapping: - OpenAI Chat Completions: `prompt_tokens_details.cached_tokens` - OpenAI Responses API: `input_tokens_details.cached_tokens` - Anthropic: `cache_read_input_tokens` - Gemini: `usageMetadata.cachedContentTokenCount` - OpenRouter: `promptTokensDetails.cachedTokens` - Groq/Grok: inherited from openai-base - Ollama: no caching support (field omitted) The field is optional — only present when the provider returns a non-null value, so existing consumers are unaffected. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/typescript/ai-anthropic/src/adapters/text.ts | 6 ++++++ packages/typescript/ai-gemini/src/adapters/text.ts | 3 +++ packages/typescript/ai-openrouter/src/adapters/text.ts | 6 ++++++ .../typescript/ai/src/activities/chat/middleware/types.ts | 4 ++++ packages/typescript/ai/src/types.ts | 4 ++++ .../openai-base/src/adapters/chat-completions-text.ts | 6 ++++++ .../typescript/openai-base/src/adapters/responses-text.ts | 8 ++++++++ 7 files changed, 37 insertions(+) diff --git a/packages/typescript/ai-anthropic/src/adapters/text.ts b/packages/typescript/ai-anthropic/src/adapters/text.ts index 2feafc32b..92940324b 100644 --- a/packages/typescript/ai-anthropic/src/adapters/text.ts +++ b/packages/typescript/ai-anthropic/src/adapters/text.ts @@ -1053,6 +1053,9 @@ export class AnthropicTextAdapter< totalTokens: (event.usage.input_tokens || 0) + (event.usage.output_tokens || 0), + ...(event.usage.cache_read_input_tokens != null && { + cachedTokens: event.usage.cache_read_input_tokens, + }), }, } break @@ -1096,6 +1099,9 @@ export class AnthropicTextAdapter< totalTokens: (event.usage.input_tokens || 0) + (event.usage.output_tokens || 0), + ...(event.usage.cache_read_input_tokens != null && { + cachedTokens: event.usage.cache_read_input_tokens, + }), }, } } diff --git a/packages/typescript/ai-gemini/src/adapters/text.ts b/packages/typescript/ai-gemini/src/adapters/text.ts index b8ff1cbf5..9d7c09ed3 100644 --- a/packages/typescript/ai-gemini/src/adapters/text.ts +++ b/packages/typescript/ai-gemini/src/adapters/text.ts @@ -631,6 +631,9 @@ export class GeminiTextAdapter< promptTokens: chunk.usageMetadata.promptTokenCount ?? 0, completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0, totalTokens: chunk.usageMetadata.totalTokenCount ?? 0, + ...(chunk.usageMetadata.cachedContentTokenCount != null && { + cachedTokens: chunk.usageMetadata.cachedContentTokenCount, + }), }, }), } diff --git a/packages/typescript/ai-openrouter/src/adapters/text.ts b/packages/typescript/ai-openrouter/src/adapters/text.ts index c48394953..f5536920b 100644 --- a/packages/typescript/ai-openrouter/src/adapters/text.ts +++ b/packages/typescript/ai-openrouter/src/adapters/text.ts @@ -549,6 +549,9 @@ export class OpenRouterTextAdapter< promptTokens: lastUsage.promptTokens, completionTokens: lastUsage.completionTokens, totalTokens: lastUsage.totalTokens, + ...(lastUsage.promptTokensDetails?.cachedTokens != null && { + cachedTokens: lastUsage.promptTokensDetails.cachedTokens, + }), }, }), } @@ -1076,6 +1079,9 @@ export class OpenRouterTextAdapter< promptTokens: lastUsage.promptTokens || 0, completionTokens: lastUsage.completionTokens || 0, totalTokens: lastUsage.totalTokens || 0, + ...(lastUsage.promptTokensDetails?.cachedTokens != null && { + cachedTokens: lastUsage.promptTokensDetails.cachedTokens, + }), }, }), finishReason, diff --git a/packages/typescript/ai/src/activities/chat/middleware/types.ts b/packages/typescript/ai/src/activities/chat/middleware/types.ts index e2a724b97..a42572871 100644 --- a/packages/typescript/ai/src/activities/chat/middleware/types.ts +++ b/packages/typescript/ai/src/activities/chat/middleware/types.ts @@ -267,6 +267,8 @@ export interface UsageInfo { promptTokens: number completionTokens: number totalTokens: number + /** Number of prompt tokens served from cache (provider-dependent) */ + cachedTokens?: number } // =========================== @@ -289,6 +291,8 @@ export interface FinishInfo { promptTokens: number completionTokens: number totalTokens: number + /** Number of prompt tokens served from cache (provider-dependent) */ + cachedTokens?: number } | undefined } diff --git a/packages/typescript/ai/src/types.ts b/packages/typescript/ai/src/types.ts index 9566cb32f..ac5506781 100644 --- a/packages/typescript/ai/src/types.ts +++ b/packages/typescript/ai/src/types.ts @@ -925,6 +925,8 @@ export interface RunFinishedEvent extends AGUIRunFinishedEvent { promptTokens: number completionTokens: number totalTokens: number + /** Number of prompt tokens served from cache (provider-dependent) */ + cachedTokens?: number } } @@ -1375,6 +1377,7 @@ export interface TextCompletionChunk { promptTokens: number completionTokens: number totalTokens: number + cachedTokens?: number } } @@ -1403,6 +1406,7 @@ export interface SummarizationResult { promptTokens: number completionTokens: number totalTokens: number + cachedTokens?: number } } diff --git a/packages/typescript/openai-base/src/adapters/chat-completions-text.ts b/packages/typescript/openai-base/src/adapters/chat-completions-text.ts index 8838a0020..bf95d53c6 100644 --- a/packages/typescript/openai-base/src/adapters/chat-completions-text.ts +++ b/packages/typescript/openai-base/src/adapters/chat-completions-text.ts @@ -502,6 +502,9 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter< promptTokens: lastUsage.prompt_tokens, completionTokens: lastUsage.completion_tokens, totalTokens: lastUsage.total_tokens, + ...(lastUsage.prompt_tokens_details?.cached_tokens != null && { + cachedTokens: lastUsage.prompt_tokens_details.cached_tokens, + }), }, }), } @@ -1057,6 +1060,9 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter< promptTokens: lastUsage.prompt_tokens || 0, completionTokens: lastUsage.completion_tokens || 0, totalTokens: lastUsage.total_tokens || 0, + ...(lastUsage.prompt_tokens_details?.cached_tokens != null && { + cachedTokens: lastUsage.prompt_tokens_details.cached_tokens, + }), }, }), finishReason, diff --git a/packages/typescript/openai-base/src/adapters/responses-text.ts b/packages/typescript/openai-base/src/adapters/responses-text.ts index 96dffb01f..f2e029706 100644 --- a/packages/typescript/openai-base/src/adapters/responses-text.ts +++ b/packages/typescript/openai-base/src/adapters/responses-text.ts @@ -594,6 +594,9 @@ export abstract class OpenAIBaseResponsesTextAdapter< promptTokens: usage.input_tokens, completionTokens: usage.output_tokens, totalTokens: usage.total_tokens, + ...(usage.input_tokens_details?.cached_tokens != null && { + cachedTokens: usage.input_tokens_details.cached_tokens, + }), }, }), } @@ -1505,6 +1508,11 @@ export abstract class OpenAIBaseResponsesTextAdapter< promptTokens: chunk.response.usage?.input_tokens || 0, completionTokens: chunk.response.usage?.output_tokens || 0, totalTokens: chunk.response.usage?.total_tokens || 0, + ...(chunk.response.usage?.input_tokens_details?.cached_tokens != + null && { + cachedTokens: + chunk.response.usage.input_tokens_details.cached_tokens, + }), }, finishReason, }