From a6bccebc50d9708df33c9f27d81d78b13f516c8e Mon Sep 17 00:00:00 2001 From: RulaKhaled Date: Tue, 21 Oct 2025 14:28:58 +0200 Subject: [PATCH 1/2] fix(core): Fix and add cache attributes in Vercel AI --- .../core/src/utils/ai/gen-ai-attributes.ts | 10 ++++++ packages/core/src/utils/vercel-ai/index.ts | 33 +++++++++++-------- .../utils/vercel-ai/vercel-ai-attributes.ts | 23 +++++++++++++ 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/packages/core/src/utils/ai/gen-ai-attributes.ts b/packages/core/src/utils/ai/gen-ai-attributes.ts index 9124602644e4..d55851927cb6 100644 --- a/packages/core/src/utils/ai/gen-ai-attributes.ts +++ b/packages/core/src/utils/ai/gen-ai-attributes.ts @@ -129,6 +129,16 @@ export const GEN_AI_RESPONSE_STREAMING_ATTRIBUTE = 'gen_ai.response.streaming'; */ export const GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE = 'gen_ai.response.tool_calls'; +/** + * The number of cache write input tokens used + */ +export const GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE_ATTRIBUTE = 'gen_ai.usage.input_tokens.cache_write'; + +/** + * The number of cached input tokens that were used + */ +export const GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE = 'gen_ai.usage.input_tokens.cached'; + // ============================================================================= // OPENAI-SPECIFIC ATTRIBUTES // ============================================================================= diff --git a/packages/core/src/utils/vercel-ai/index.ts b/packages/core/src/utils/vercel-ai/index.ts index 9b1cc2bc8aae..ea239cccbc15 100644 --- a/packages/core/src/utils/vercel-ai/index.ts +++ b/packages/core/src/utils/vercel-ai/index.ts @@ -2,6 +2,10 @@ import type { Client } from '../../client'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes'; import type { Event } from '../../types-hoist/event'; import type { Span, SpanAttributes, SpanAttributeValue, SpanJSON, SpanOrigin } from '../../types-hoist/span'; +import { + GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE_ATTRIBUTE, + GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE, +} from '../ai/gen-ai-attributes'; import { spanToJSON } from '../spanUtils'; import { toolCallSpanMap } from './constants'; import type { TokenSummary } from './types'; @@ -23,6 +27,7 @@ import { AI_TOOL_CALL_ID_ATTRIBUTE, AI_TOOL_CALL_NAME_ATTRIBUTE, AI_TOOL_CALL_RESULT_ATTRIBUTE, + AI_USAGE_CACHED_INPUT_TOKENS_ATTRIBUTE, AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE, AI_USAGE_PROMPT_TOKENS_ATTRIBUTE, GEN_AI_RESPONSE_MODEL_ATTRIBUTE, @@ -107,6 +112,7 @@ function processEndedVercelAiSpan(span: SpanJSON): void { renameAttributeKey(attributes, AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE, GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE); renameAttributeKey(attributes, AI_USAGE_PROMPT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE); + renameAttributeKey(attributes, AI_USAGE_CACHED_INPUT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE); if ( typeof attributes[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE] === 'number' && @@ -287,7 +293,7 @@ function addProviderMetadataToAttributes(attributes: SpanAttributes): void { if (providerMetadataObject.openai) { setAttributeIfDefined( attributes, - 'gen_ai.usage.input_tokens.cached', + GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE, providerMetadataObject.openai.cachedPromptTokens, ); setAttributeIfDefined( @@ -309,27 +315,26 @@ function addProviderMetadataToAttributes(attributes: SpanAttributes): void { } if (providerMetadataObject.anthropic) { - setAttributeIfDefined( - attributes, - 'gen_ai.usage.input_tokens.cached', - providerMetadataObject.anthropic.cacheReadInputTokens, - ); - setAttributeIfDefined( - attributes, - 'gen_ai.usage.input_tokens.cache_write', - providerMetadataObject.anthropic.cacheCreationInputTokens, - ); + const cachedInputTokens = + providerMetadataObject.anthropic.usage?.cache_read_input_tokens ?? + providerMetadataObject.anthropic.cacheCreationInputTokens; + setAttributeIfDefined(attributes, GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE, cachedInputTokens); + + const cacheWriteInputTokens = + providerMetadataObject.anthropic.usage?.cache_creation_input_tokens ?? + providerMetadataObject.anthropic.cacheCreationInputTokens; + setAttributeIfDefined(attributes, GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE_ATTRIBUTE, cacheWriteInputTokens); } if (providerMetadataObject.bedrock?.usage) { setAttributeIfDefined( attributes, - 'gen_ai.usage.input_tokens.cached', + GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE, providerMetadataObject.bedrock.usage.cacheReadInputTokens, ); setAttributeIfDefined( attributes, - 'gen_ai.usage.input_tokens.cache_write', + GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE_ATTRIBUTE, providerMetadataObject.bedrock.usage.cacheWriteInputTokens, ); } @@ -337,7 +342,7 @@ function addProviderMetadataToAttributes(attributes: SpanAttributes): void { if (providerMetadataObject.deepseek) { setAttributeIfDefined( attributes, - 'gen_ai.usage.input_tokens.cached', + GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE, providerMetadataObject.deepseek.promptCacheHitTokens, ); setAttributeIfDefined( diff --git a/packages/core/src/utils/vercel-ai/vercel-ai-attributes.ts b/packages/core/src/utils/vercel-ai/vercel-ai-attributes.ts index ac6774b08a02..95052fc1265a 100644 --- a/packages/core/src/utils/vercel-ai/vercel-ai-attributes.ts +++ b/packages/core/src/utils/vercel-ai/vercel-ai-attributes.ts @@ -288,6 +288,14 @@ export const AI_RESPONSE_PROVIDER_METADATA_ATTRIBUTE = 'ai.response.providerMeta */ export const AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = 'ai.settings.maxRetries'; +/** + * Basic LLM span information + * Multiple spans + * + * The number of cached input tokens that were used + * @see https://ai-sdk.dev/docs/ai-sdk-core/telemetry#basic-llm-span-information + */ +export const AI_USAGE_CACHED_INPUT_TOKENS_ATTRIBUTE = 'ai.usage.cachedInputTokens'; /** * Basic LLM span information * Multiple spans @@ -863,6 +871,21 @@ interface AnthropicProviderMetadata { * @see https://ai-sdk.dev/providers/ai-sdk-providers/anthropic#cache-control */ cacheReadInputTokens?: number; + + /** + * Usage metrics for the Anthropic model. + */ + usage?: { + input_tokens: number; + cache_creation_input_tokens?: number; + cache_read_input_tokens?: number; + cache_creation?: { + ephemeral_5m_input_tokens?: number; + ephemeral_1h_input_tokens?: number; + }; + output_tokens?: number; + service_tier?: string; + }; } /** From b72f770fe1b5f66a1d4e3631fb6b936adbbe5bea Mon Sep 17 00:00:00 2001 From: RulaKhaled Date: Tue, 21 Oct 2025 14:42:00 +0200 Subject: [PATCH 2/2] nice catch cursor --- packages/core/src/utils/vercel-ai/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/utils/vercel-ai/index.ts b/packages/core/src/utils/vercel-ai/index.ts index ea239cccbc15..8f353e88d394 100644 --- a/packages/core/src/utils/vercel-ai/index.ts +++ b/packages/core/src/utils/vercel-ai/index.ts @@ -317,7 +317,7 @@ function addProviderMetadataToAttributes(attributes: SpanAttributes): void { if (providerMetadataObject.anthropic) { const cachedInputTokens = providerMetadataObject.anthropic.usage?.cache_read_input_tokens ?? - providerMetadataObject.anthropic.cacheCreationInputTokens; + providerMetadataObject.anthropic.cacheReadInputTokens; setAttributeIfDefined(attributes, GEN_AI_USAGE_INPUT_TOKENS_CACHED_ATTRIBUTE, cachedInputTokens); const cacheWriteInputTokens =