From 7e545c2a120bc09430f697d6a682bb32e0356df8 Mon Sep 17 00:00:00 2001 From: milan Date: Wed, 22 Jul 2026 22:33:36 +0000 Subject: [PATCH] fix(bedrock): keep cross-region prefix in count_tokens token counter path Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../count_tokens/bedrock_token_counter.py | 5 +- .../test_bedrock_token_counter.py | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/litellm/llms/bedrock/count_tokens/bedrock_token_counter.py b/litellm/llms/bedrock/count_tokens/bedrock_token_counter.py index 1ea870a1d32..a440035f788 100644 --- a/litellm/llms/bedrock/count_tokens/bedrock_token_counter.py +++ b/litellm/llms/bedrock/count_tokens/bedrock_token_counter.py @@ -6,7 +6,7 @@ from typing import Any, Dict, List, Optional from litellm._logging import verbose_logger from litellm.llms.base_llm.base_utils import BaseTokenCounter -from litellm.llms.bedrock.common_utils import BedrockError, get_bedrock_base_model +from litellm.llms.bedrock.common_utils import BedrockError, strip_bedrock_routing_prefix from litellm.llms.bedrock.count_tokens.handler import BedrockCountTokensHandler from litellm.types.utils import LlmProviders, TokenCountResponse @@ -67,8 +67,7 @@ class BedrockTokenCounter(BaseTokenCounter): if system: request_data["system"] = system - # Get the resolved model (strip prefixes like bedrock/, converse/, etc.) - resolved_model = get_bedrock_base_model(model_to_use) + resolved_model = strip_bedrock_routing_prefix(model_to_use) try: handler = BedrockCountTokensHandler() diff --git a/tests/litellm_utils_tests/test_bedrock_token_counter.py b/tests/litellm_utils_tests/test_bedrock_token_counter.py index 9fb2463e8b5..62611d26a62 100644 --- a/tests/litellm_utils_tests/test_bedrock_token_counter.py +++ b/tests/litellm_utils_tests/test_bedrock_token_counter.py @@ -21,6 +21,7 @@ sys.path.insert( from litellm.llms.base_llm.base_utils import BaseTokenCounter from litellm.llms.bedrock.count_tokens.bedrock_token_counter import BedrockTokenCounter +from litellm.llms.bedrock.count_tokens.handler import BedrockCountTokensHandler from tests.litellm_utils_tests.base_token_counter_test import BaseTokenCounterTest @@ -112,6 +113,51 @@ class TestBedrockTokenCounter(BaseTokenCounterTest): ), f"Token counting should not error: {result.error_message}" +@pytest.mark.parametrize( + "model_to_use, expected_resolved_model", + [ + ( + "bedrock/global.anthropic.claude-opus-4-8", + "global.anthropic.claude-opus-4-8", + ), + ( + "bedrock/eu.anthropic.claude-sonnet-4-6", + "eu.anthropic.claude-sonnet-4-6", + ), + ( + "bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0", + "anthropic.claude-3-5-sonnet-20241022-v2:0", + ), + ], +) +@pytest.mark.asyncio +async def test_count_tokens_preserves_cross_region_inference_profile_prefix( + model_to_use, expected_resolved_model +): + """The cross-region inference-profile prefix (global./us./eu./apac./...) must be + preserved in the model passed to the count-tokens handler. Stripping it down to the + bare foundation-model ID makes Bedrock reject inference-profile-only models with a 400 + on the count-tokens route (issue #32683).""" + from unittest.mock import AsyncMock + + counter = BedrockTokenCounter() + + with patch.object( + BedrockCountTokensHandler, + "handle_count_tokens_request", + new=AsyncMock(return_value={"input_tokens": 7}), + ) as mock_handle: + await counter.count_tokens( + model_to_use=model_to_use, + messages=[{"role": "user", "content": "Hello"}], + contents=None, + deployment={"litellm_params": {"aws_region_name": "eu-central-1"}}, + request_model=model_to_use, + ) + + assert mock_handle.call_args.kwargs["resolved_model"] == expected_resolved_model + + class TestBedrockCountTokensEndpoint: """Unit tests for custom endpoint URL resolution in BedrockCountTokensConfig."""