diff --git a/litellm/llms/bedrock_mantle/common_utils.py b/litellm/llms/bedrock_mantle/common_utils.py index ac94d9cb922..f8eab32f5f5 100644 --- a/litellm/llms/bedrock_mantle/common_utils.py +++ b/litellm/llms/bedrock_mantle/common_utils.py @@ -31,6 +31,8 @@ BEDROCK_MANTLE_DEFAULT_REGION: Final = "us-east-1" # Standard Mantle host: https://bedrock-mantle..api.aws (group 1 = region). MANTLE_HOST_RE: Final = re.compile(r"^https?://bedrock-mantle\.([^/.]+)\.api\.aws(?=/|$)", re.IGNORECASE) +OPENAI_V1_FAMILY_RE: Final = re.compile(r"^openai\.gpt-(?!oss)") + def resolve_mantle_bearer_token(api_key: str | None) -> str | None: return api_key or get_secret_str("BEDROCK_MANTLE_API_KEY") or get_secret_str("AWS_BEARER_TOKEN_BEDROCK") @@ -145,7 +147,13 @@ def mantle_base_segment(model: str | None, model_cost: dict) -> str: /openai/v1 base (.../openai/v1/responses and .../openai/v1/chat/completions); every other model including gpt-oss uses the standard /v1 base. The segment is the base for the model's whole OpenAI-compatible surface, so both the chat and - responses configs derive from it -- there is no separate model-name rule. + responses configs derive from it. When the flag is absent (no price-map entry, + or a bare entry the Router registers for an unmapped deployment) a family rule + keeps day-0 OpenAI model IDs working before their cost-map entry lands: every + openai.gpt-* model except gpt-oss is on /openai/v1. """ entry: Final = model_cost.get(f"bedrock_mantle/{model}", {}) - return "openai/v1" if entry.get("use_openai_responses_path") is True else "v1" + flag: Final = entry.get("use_openai_responses_path") + if flag is None: + return "openai/v1" if model and OPENAI_V1_FAMILY_RE.match(model) else "v1" + return "openai/v1" if flag is True else "v1" diff --git a/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py b/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py index 40566261c84..262d8dd6fd6 100644 --- a/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py +++ b/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py @@ -1269,7 +1269,8 @@ class TestBedrockMantleResponsesRegistry: class TestMantleBaseSegment: """The wire-path helper is data-driven from the price-map use_openai_responses_path flag (NOT a model-name match): flagged models are on - the /openai/v1 base, everything else on /v1. An unmapped model defaults to /v1. + the /openai/v1 base, everything else on /v1. An unmapped model defaults to /v1 + unless it is a non-gpt-oss openai.gpt-* ID, which falls back to /openai/v1. """ @pytest.mark.parametrize( @@ -1296,6 +1297,16 @@ class TestMantleBaseSegment: ), ("openai.gpt-oss-120b", {}, "v1"), (None, {}, "v1"), + ("openai.gpt-daybreak-blue-5.6-sol", {}, "openai/v1"), + ("openai.gpt-5.7", {}, "openai/v1"), + ("openai.gpt-oss-safeguard-20b", {}, "v1"), + ("somelab.unmapped", {}, "v1"), + ("openai.gpt-5.7", {"bedrock_mantle/openai.gpt-5.7": {}}, "openai/v1"), + ( + "openai.gpt-5.7", + {"bedrock_mantle/openai.gpt-5.7": {"use_openai_responses_path": False}}, + "v1", + ), ], ) def test_base_segment(self, model, model_cost, expected):