UNPKG

langsmith

Version:

Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.

114 lines (113 loc) 5.02 kB
export function extractOutputTokenDetails(reasoningTokens) { const outputTokenDetails = {}; if (typeof reasoningTokens === "number") { outputTokenDetails.reasoning = reasoningTokens; } return outputTokenDetails; } export function extractInputTokenDetails(providerMetadata, cachedTokenUsage) { const inputTokenDetails = {}; if (providerMetadata.anthropic != null && typeof providerMetadata.anthropic === "object") { const anthropic = providerMetadata.anthropic; if (anthropic.usage != null && typeof anthropic.usage === "object") { // Raw usage from Anthropic returned in AI SDK 5 const usage = anthropic.usage; if (usage.cache_creation != null && typeof usage.cache_creation === "object") { const cacheCreation = usage.cache_creation; if (typeof cacheCreation.ephemeral_5m_input_tokens === "number") { inputTokenDetails.ephemeral_5m_input_tokens = cacheCreation.ephemeral_5m_input_tokens; } if (typeof cacheCreation.ephemeral_1h_input_tokens === "number") { inputTokenDetails.ephemeral_1hr_input_tokens = cacheCreation.ephemeral_1h_input_tokens; } // If cache_creation not returned (no beta header passed), // fallback to assuming 5m cache tokens } else if (typeof usage.cache_creation_input_tokens === "number") { inputTokenDetails.ephemeral_5m_input_tokens = usage.cache_creation_input_tokens; } if (typeof usage.cache_read_input_tokens === "number") { inputTokenDetails.cache_read = usage.cache_read_input_tokens; } } else { // AI SDK 4 fields if (anthropic.cacheReadInputTokens != null && typeof anthropic.cacheReadInputTokens === "number") { inputTokenDetails.cache_read = anthropic.cacheReadInputTokens; } if (anthropic.cacheCreationInputTokens != null && typeof anthropic.cacheCreationInputTokens === "number") { inputTokenDetails.ephemeral_5m_input_tokens = anthropic.cacheCreationInputTokens; } } return inputTokenDetails; } else if (providerMetadata.openai != null && typeof providerMetadata.openai === "object") { const openai = providerMetadata.openai; if (openai.cachedPromptTokens != null && typeof openai.cachedPromptTokens === "number") { inputTokenDetails.cache_read = openai.cachedPromptTokens; } else if (typeof cachedTokenUsage === "number") { inputTokenDetails.cache_read = cachedTokenUsage; } } return inputTokenDetails; } export function extractUsageMetadata(span) { const isError = span?.status?.code === 2; if (isError || !span || !span.attributes) { return { input_tokens: 0, output_tokens: 0, total_tokens: 0, }; } const usageMetadata = { input_tokens: 0, output_tokens: 0, total_tokens: 0, }; if (typeof span.attributes["ai.usage.promptTokens"] === "number" || typeof span.attributes["ai.usage.inputTokens"] === "number") { usageMetadata.input_tokens = span.attributes["ai.usage.promptTokens"] ?? span.attributes["ai.usage.inputTokens"]; } if (typeof span.attributes["ai.usage.completionTokens"] === "number" || typeof span.attributes["ai.usage.outputTokens"] === "number") { usageMetadata.output_tokens = span.attributes["ai.usage.completionTokens"] ?? span.attributes["ai.usage.outputTokens"]; } if (typeof span.attributes["ai.response.providerMetadata"] === "string") { try { const providerMetadata = JSON.parse(span.attributes["ai.response.providerMetadata"]); usageMetadata.input_token_details = extractInputTokenDetails(providerMetadata, typeof span.attributes["ai.usage.cachedInputTokens"] === "number" ? span.attributes["ai.usage.cachedInputTokens"] : undefined); if (providerMetadata.anthropic != null && typeof providerMetadata.anthropic === "object") { // AI SDK does not include Anthropic cache tokens in their stated input token // numbers, so we need to add them manually for (const key in usageMetadata.input_token_details) { usageMetadata.input_tokens += usageMetadata.input_token_details[key]; } } } catch { // pass } } usageMetadata.total_tokens = usageMetadata.input_tokens + usageMetadata.output_tokens; return usageMetadata; }