fix(bedrock): drop client_metadata for ARNs that hide the model family

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
mateo 2026-09-01 19:26:19 +00:00
parent ed4343a026
commit bba951c5eb
3 changed files with 55 additions and 1 deletions

View file

@ -86,6 +86,7 @@ from litellm.utils import (
from ..common_utils import (
BedrockError,
BedrockModelInfo,
bedrock_arn_hides_model_family,
bedrock_converse_supports_parallel_tool_use_config,
get_anthropic_beta_from_headers,
get_bedrock_tool_name,
@ -1325,7 +1326,8 @@ class AmazonConverseConfig(BaseConfig):
additional_request_params.pop("parallel_tool_calls", None)
if base_model.startswith("anthropic") and additional_request_params.pop("client_metadata", None) is not None:
drops_client_metadata: Final = base_model.startswith("anthropic") or bedrock_arn_hides_model_family(model)
if drops_client_metadata and additional_request_params.pop("client_metadata", None) is not None:
litellm.verbose_logger.debug(
"Bedrock Converse: dropping `client_metadata` for model=%s, Anthropic rejects it with "
"'client_metadata: Extra inputs are not permitted'",

View file

@ -720,6 +720,15 @@ def get_bedrock_base_model(model: str) -> str:
return model
def bedrock_arn_hides_model_family(model: str) -> bool:
"""
True for an ARN-addressed model whose base name carries no ``provider.model``
id, such as an application inference profile or a provisioned throughput ARN.
Callers that gate behavior on the model family cannot resolve one here.
"""
return "arn:" in model.lower() and "." not in get_bedrock_base_model(model)
def bedrock_converse_supports_parallel_tool_use_config(model: str) -> bool:
return any(
(litellm.model_cost.get(candidate) or {}).get("supports_parallel_tool_use_config") is True

View file

@ -1021,6 +1021,49 @@ def test_client_metadata_kept_for_non_anthropic_converse_request():
assert data["additionalModelRequestFields"]["client_metadata"] == {"originator": "codex_cli_rs"}
@pytest.mark.parametrize(
"model",
[
"arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abcdef123456",
"arn:aws:bedrock:us-east-1:123456789012:provisioned-model/abcdef123456",
],
)
def test_client_metadata_stripped_for_arn_models_converse(model):
"""An ARN hides which family serves the request, and pointing one at Claude is how
teams route codex traffic, so the field has to go there too or the 400 comes back."""
config = AmazonConverseConfig()
data = config._transform_request_helper(
model=model,
system_content_blocks=[],
optional_params={
"maxTokens": 16,
"client_metadata": {"originator": "codex_cli_rs"},
},
messages=None,
)
assert "client_metadata" not in data.get("additionalModelRequestFields", {})
def test_client_metadata_kept_for_arn_naming_a_non_anthropic_family():
"""An inference profile ARN that still spells out the family is resolvable, so a
non-Anthropic one keeps its passthrough."""
config = AmazonConverseConfig()
data = config._transform_request_helper(
model="arn:aws:bedrock:us-east-1:123456789012:inference-profile/us.amazon.nova-pro-v1:0",
system_content_blocks=[],
optional_params={
"maxTokens": 16,
"client_metadata": {"originator": "codex_cli_rs"},
},
messages=None,
)
assert data["additionalModelRequestFields"]["client_metadata"] == {"originator": "codex_cli_rs"}
def test_parallel_tool_calls_config_kept_for_sonnet_5(monkeypatch):
old_env = os.environ.get("LITELLM_LOCAL_MODEL_COST_MAP")
old_cost = litellm.model_cost