Skip to content

Amazon Bedrock and Anthropic report different usages #11532

@Pranav-Wadhwa

Description

@Pranav-Wadhwa

Description

I've noticed that the logic for calculating inputTokens and inputTokenDetails is inconsistent for claude between Anthropic as the provider and Amazon Bedrock as the provider. Here's an example script:

import "dotenv/config"
import logger from "@/utils/logger"
import { generateText } from "ai"
import { anthropic } from "@ai-sdk/anthropic"
import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock"
import { env } from "@/env.mjs"

const bedrock = createAmazonBedrock({
  region: "us-east-2",
  accessKeyId: env.accessKeyId,
  secretAccessKey: env.secretAccessKey,
})

// System prompt must be at least 1024 tokens for Anthropic caching to work
const systemPrompt = `<a long system prompt...>`

const userMessage = "What is 2 + 2? Reply with just the number."

async function testAnthropicProvider() {
  logger.info("=== Testing Anthropic Provider ===")

  const result = await generateText({
    model: anthropic("claude-sonnet-4-5-20250929"),
    messages: [
      {
        role: "system",
        content: systemPrompt,
        providerOptions: {
          anthropic: { cacheControl: { type: "ephemeral" } },
        },
      },
      {
        role: "user",
        content: userMessage,
      },
    ],
  })

  logger.info("Anthropic response", { text: result.text })
  logger.info("Anthropic usage", { usage: result.usage })
}

async function testBedrockProvider() {
  logger.info("=== Testing Bedrock Provider ===")

  const result = await generateText({
    model: bedrock("global.anthropic.claude-sonnet-4-5-20250929-v1:0"),
    messages: [
      {
        role: "system",
        content: systemPrompt,
        providerOptions: {
          bedrock: { cachePoint: { type: "default" } },
        },
      },
      {
        role: "user",
        content: userMessage,
      },
    ],
  })

  logger.info("Bedrock response", { text: result.text })
  logger.info("Bedrock usage", { usage: result.usage })
}

export default async function main() {
  try {
    await testAnthropicProvider()
  } catch (error) {
    logger.error("Anthropic error", { error })
  }

  try {
    await testBedrockProvider()
  } catch (error) {
    logger.error("Bedrock error", { error })
  }
}

logger.info(`${scriptName}...`)
await main()
logger.info(`${scriptName}... done`)

Here is the Anthropic usage:

{
  "inputTokens": 2590,
  "inputTokenDetails": {
    "noCacheTokens": 21,
    "cacheReadTokens": 0,
    "cacheWriteTokens": 2569
  },
  "outputTokens": 5,
  "outputTokenDetails": {},
  "totalTokens": 2595,
  "raw": {
    "input_tokens": 21,
    "output_tokens": 5,
    "cache_creation_input_tokens": 2569,
    "cache_read_input_tokens": 0,
    "cache_creation": {
      "ephemeral_5m_input_tokens": 2569,
      "ephemeral_1h_input_tokens": 0
    },
    "service_tier": "standard"
  },
  "cachedInputTokens": 0
}

Here is the Bedrock usage

{
  "inputTokens": 21,
  "inputTokenDetails": {
    "noCacheTokens": 21,
    "cacheReadTokens": 0,
    "cacheWriteTokens": 2569
  },
  "outputTokens": 5,
  "outputTokenDetails": {
    "textTokens": 5
  },
  "totalTokens": 26,
  "raw": {
    "inputTokens": 21,
    "outputTokens": 5,
    "totalTokens": 2595,
    "cacheReadInputTokens": 0,
    "cacheWriteInputTokens": 2569
  },
  "cachedInputTokens": 0
}

Anthropic includes the cacheReadTokens and cacheWriteTokens in the inputTokens whereas Bedrock does not. Additionally Bedrock computes the noCacheTokens as inputTokens - cacheReadTokens which often times results in a negative value.

AI SDK Version

"@ai-sdk/amazon-bedrock": "^4.0.4",
"@ai-sdk/anthropic": "^3.0.1",
"ai": "^6.0.3",

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions