Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion litellm/litellm_core_utils/get_llm_provider_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ def _get_openai_compatible_provider_info(
api_base,
dynamic_api_key,
) = litellm.BedrockMantleChatConfig()._get_openai_compatible_provider_info(
api_base, api_key, litellm_params=litellm_params
api_base, api_key, litellm_params=litellm_params, model=model
)
elif custom_llm_provider == "nvidia_nim":
# nvidia_nim is openai compatible, we just need to set this to custom_openai and have the api_base be https://api.endpoints.anyscale.com/v1
Expand Down
7 changes: 6 additions & 1 deletion litellm/llms/bedrock_mantle/chat/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from litellm.types.llms.openai import AllMessageValues
from litellm.types.router import GenericLiteLLMParams

from ..common_utils import mantle_base_segment
from ...openai_like.chat.transformation import OpenAILikeChatConfig

BEDROCK_MANTLE_DEFAULT_REGION = "us-east-1"
Expand All @@ -40,6 +41,7 @@ def _get_openai_compatible_provider_info(
api_base: Optional[str],
api_key: Optional[str],
litellm_params: Optional[GenericLiteLLMParams] = None,
model: str | None = None,
) -> Tuple[Optional[str], Optional[str]]:
region = (
(litellm_params.aws_region_name if litellm_params else None)
Expand All @@ -49,10 +51,13 @@ def _get_openai_compatible_provider_info(
or BEDROCK_MANTLE_DEFAULT_REGION
)
BaseAWSLLM._validate_aws_region_name(region)
# The base path segment is data-driven per model (use_openai_responses_path
# flag): gemma-4-* and gpt-5.x are served on /openai/v1, everything else on
# /v1. An explicit api_base still wins over the derived default.
api_base = (
api_base
or get_secret_str("BEDROCK_MANTLE_API_BASE")
or f"https://bedrock-mantle.{region}.api.aws/v1"
or f"https://bedrock-mantle.{region}.api.aws/{mantle_base_segment(model, litellm.model_cost)}"
)
dynamic_api_key = api_key or get_secret_str("BEDROCK_MANTLE_API_KEY")
return api_base, dynamic_api_key
Expand Down
38 changes: 38 additions & 0 deletions litellm/llms/bedrock_mantle/common_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Shared helpers for the Amazon Bedrock Mantle OpenAI-compatible provider.

Both helpers are pure functions of (model, model_cost) so the routing rules can be
unit-tested without patching global state.
"""


def mantle_supports_responses(model: str | None, model_cost: dict) -> bool:
"""Whether a Bedrock Mantle model can serve the native Responses API.

Purely data-driven from the model's price-map capability signal -- either
/v1/responses in supported_endpoints, or mode=responses -- both overridable
via register_model and proxy model_info, so onboarding a model is a JSON
change, never a code change. There is deliberately NO model-name match here:
capability is per-model, not per-family (openai.gpt-oss-120b supports
Responses while openai.gpt-oss-safeguard-120b does not, despite sharing the
gpt-oss substring), so a substring gate would be wrong. A model absent from
model_cost simply has no signal and returns False (chat-completions emulation).
"""
entry = model_cost.get(f"bedrock_mantle/{model}", {})
if "/v1/responses" in (entry.get("supported_endpoints") or []):
return True
return entry.get("mode") == "responses"


def mantle_base_segment(model: str | None, model_cost: dict) -> str:
"""Return the base path segment for a Bedrock Mantle model's OpenAI surface.

Data-driven from the model's price-map use_openai_responses_path flag
(overridable via register_model / proxy model_info). Per the AWS model cards,
gpt-5.x and the google gemma-4-* family carry that flag and are served on the
/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.
"""
entry = model_cost.get(f"bedrock_mantle/{model}", {})
return "openai/v1" if entry.get("use_openai_responses_path") is True else "v1"
10 changes: 10 additions & 0 deletions litellm/model_prices_and_context_window_backup.json
Original file line number Diff line number Diff line change
Expand Up @@ -41993,6 +41993,7 @@
"max_output_tokens": 32768,
"max_tokens": 32768,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_reasoning": true,
Expand All @@ -42007,6 +42008,7 @@
"max_output_tokens": 32768,
"max_tokens": 32768,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_reasoning": true,
Expand All @@ -42021,6 +42023,7 @@
"max_output_tokens": 65536,
"max_tokens": 65536,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supports_function_calling": true,
"supports_reasoning": true,
"supports_response_schema": true,
Expand All @@ -42034,6 +42037,7 @@
"max_output_tokens": 65536,
"max_tokens": 65536,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supports_function_calling": true,
"supports_reasoning": true,
"supports_response_schema": true,
Expand Down Expand Up @@ -42087,6 +42091,8 @@
"max_output_tokens": 256000,
"max_tokens": 256000,
"mode": "chat",
"use_openai_responses_path": true,
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": false,
"supports_reasoning": true,
Expand All @@ -42101,6 +42107,8 @@
"max_output_tokens": 256000,
"max_tokens": 256000,
"mode": "chat",
"use_openai_responses_path": true,
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": false,
"supports_reasoning": true,
Expand All @@ -42115,6 +42123,8 @@
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"use_openai_responses_path": true,
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": false,
"supports_reasoning": true,
Expand Down
46 changes: 19 additions & 27 deletions litellm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9070,34 +9070,26 @@ def _get_python_responses_api_config(
elif litellm.LlmProviders.HOSTED_VLLM == provider:
return litellm.HostedVLLMResponsesAPIConfig()
elif litellm.LlmProviders.BEDROCK_MANTLE == provider:
# Mantle serves Responses on two upstream paths. A model takes the
# /openai/v1/responses path when its price-map entry declares
# use_openai_responses_path (data-driven, so a non-gpt-named frontier
# model can be onboarded by JSON alone), or, as a fallback needing no
# price-map entry, when its name matches the openai.gpt- frontier
# convention (minus gpt-oss) -- this keeps a future gpt-6 routing
# correctly before its entry loads. Any other model declared
# mode=responses takes the standard /v1/responses path. Everything
# else returns None and keeps the chat-completions emulation (see
# responses/main.py "config is None").
if not model:
# Both decisions are data-driven from the model's price-map entry, with
# no model-name logic. Capability (can it serve Responses?) comes from
# mantle_supports_responses (supported_endpoints / mode);
# chat-only models (gpt-oss safeguard, nvidia, ...) return None and keep
# the chat-completions emulation (responses/main.py "config is None").
# The wire path comes from mantle_base_segment, which reads the
# use_openai_responses_path flag: gpt-5.x and gemma-4-* on
# /openai/v1/responses, everything else (incl. gpt-oss) on
# /v1/responses.
from litellm.llms.bedrock_mantle.common_utils import (
mantle_base_segment,
mantle_supports_responses,
)

if not model or not mantle_supports_responses(model, litellm.model_cost):
return None
model_lower = model.lower()
entry = litellm.model_cost.get(f"bedrock_mantle/{model}", {})
on_openai_path = entry.get("use_openai_responses_path") is True
name_is_frontier = (
"openai.gpt-" in model_lower and "gpt-oss" not in model_lower
)
if on_openai_path or name_is_frontier:
return litellm.BedrockMantleResponsesAPIConfig(use_openai_path=True)
try:
if get_model_info(model, "bedrock_mantle").get("mode") == "responses":
return litellm.BedrockMantleResponsesAPIConfig(
use_openai_path=False
)
except Exception:
pass
return None
return litellm.BedrockMantleResponsesAPIConfig(
use_openai_path=mantle_base_segment(model, litellm.model_cost)
== "openai/v1"
)
return None

@staticmethod
Expand Down
10 changes: 10 additions & 0 deletions model_prices_and_context_window.json
Original file line number Diff line number Diff line change
Expand Up @@ -42195,6 +42195,7 @@
"max_output_tokens": 32768,
"max_tokens": 32768,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_reasoning": true,
Expand All @@ -42209,6 +42210,7 @@
"max_output_tokens": 32768,
"max_tokens": 32768,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": true,
"supports_reasoning": true,
Expand All @@ -42223,6 +42225,7 @@
"max_output_tokens": 65536,
"max_tokens": 65536,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supports_function_calling": true,
"supports_reasoning": true,
"supports_response_schema": true,
Expand All @@ -42236,6 +42239,7 @@
"max_output_tokens": 65536,
"max_tokens": 65536,
"mode": "chat",
"supported_endpoints": ["/v1/chat/completions"],
"supports_function_calling": true,
"supports_reasoning": true,
"supports_response_schema": true,
Expand Down Expand Up @@ -42289,6 +42293,8 @@
"max_output_tokens": 256000,
"max_tokens": 256000,
"mode": "chat",
"use_openai_responses_path": true,
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": false,
"supports_reasoning": true,
Expand All @@ -42303,6 +42309,8 @@
"max_output_tokens": 256000,
"max_tokens": 256000,
"mode": "chat",
"use_openai_responses_path": true,
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": false,
"supports_reasoning": true,
Expand All @@ -42317,6 +42325,8 @@
"max_output_tokens": 128000,
"max_tokens": 128000,
"mode": "chat",
"use_openai_responses_path": true,
"supported_endpoints": ["/v1/chat/completions", "/v1/responses"],
"supports_function_calling": true,
"supports_parallel_function_calling": false,
"supports_reasoning": true,
Expand Down
Loading
Loading