fix(bedrock_mantle): price region-prefixed Claude responses from the bare Bedrock row

This commit is contained in:
mateo-berri 2026-09-19 20:17:55 -07:00
parent 0f0c0fe499
commit f24208f9ca
2 changed files with 39 additions and 3 deletions

View file

@ -5624,6 +5624,12 @@ def _get_model_info_from_generalization(
return None
def _strip_mantle_region_prefix(model: str) -> str:
from litellm.llms.bedrock_mantle.common_utils import split_mantle_region_prefix
return split_mantle_region_prefix(model)[1]
def _get_potential_model_names(model: str, custom_llm_provider: str | None) -> PotentialModelNamesAndCustomLLMProvider:
if custom_llm_provider is None:
# Get custom_llm_provider
@ -5656,17 +5662,22 @@ def _get_potential_model_names(model: str, custom_llm_provider: str | None) -> P
split_model = strip_bedrock_routing_prefix(split_model)
region_free_split_model: Final = (
_strip_mantle_region_prefix(split_model) if custom_llm_provider == "bedrock_mantle" else split_model
)
provider_model_info: Final = (
ProviderConfigManager.get_provider_model_info(model=split_model, provider=LlmProviders(custom_llm_provider))
ProviderConfigManager.get_provider_model_info(
model=region_free_split_model, provider=LlmProviders(custom_llm_provider)
)
if custom_llm_provider in LlmProvidersSet
else None
)
provider_cost_key: Final = (
provider_model_info.get_model_cost_key(split_model) if provider_model_info is not None else None
provider_model_info.get_model_cost_key(region_free_split_model) if provider_model_info is not None else None
)
return PotentialModelNamesAndCustomLLMProvider(
split_model=split_model,
split_model=region_free_split_model,
combined_model_name=combined_model_name,
stripped_model_name=stripped_model_name,
combined_stripped_model_name=combined_stripped_model_name,

View file

@ -3522,6 +3522,31 @@ def test_cost_per_token_region_name_applies_to_provider_prefixed_model(_local_mo
)
def test_completion_cost_mantle_native_messages_prices_claude_from_the_bedrock_row(_local_model_cost_map):
"""Mantle's native Messages API answers with Anthropic's canonical model name and the proxy
resolves a Mantle region for every call, so the first cost candidate is
bedrock_mantle/<region>/claude-sonnet-5. That name has no row of its own and must fall through to
the deployment's bare Bedrock row instead of stopping on an unpriced capability rule at $0."""
response = litellm.ModelResponse(
id="msg_x",
choices=[{"index": 0, "message": {"role": "assistant", "content": "hi"}, "finish_reason": "stop"}],
model="claude-sonnet-5",
usage={"prompt_tokens": 100, "completion_tokens": 10, "total_tokens": 110},
)
row = litellm.model_cost["anthropic.claude-sonnet-5"]
expected = 100 * row["input_cost_per_token"] + 10 * row["output_cost_per_token"]
assert expected > 0
for region_name in ("us-east-1", None):
assert litellm.completion_cost(
completion_response=response,
model="bedrock_mantle/anthropic.claude-sonnet-5",
custom_llm_provider="bedrock_mantle",
region_name=region_name,
) == pytest.approx(expected)
def test_select_model_name_keeps_base_model_free_of_region(_local_model_cost_map):
"""An explicit base_model keeps pricing on that model's own key even when the request carries a
region with different regional rates, so the private provider model never widens region pricing."""