feat(bedrock_mantle): route Responses API to native OpenAI endpoint

Register BedrockMantleResponsesAPIConfig so /v1/responses hits
bedrock-mantle.{region}.api.aws/openai/v1/responses instead of the
chat-completions fallback. Fix default api_base to use /openai/v1.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Sameer Kankute 2026-06-02 09:55:59 +05:30
parent e8fcb01215
commit f63ce0445b
No known key found for this signature in database
6 changed files with 96 additions and 6 deletions

View file

@ -1680,6 +1680,9 @@ if TYPE_CHECKING:
from .llms.bedrock_mantle.chat.transformation import (
BedrockMantleChatConfig as BedrockMantleChatConfig,
)
from .llms.bedrock_mantle.responses.transformation import (
BedrockMantleResponsesAPIConfig as BedrockMantleResponsesAPIConfig,
)
from .llms.a2a.chat.transformation import A2AConfig as A2AConfig
from .llms.voyage.embedding.transformation import (
VoyageEmbeddingConfig as VoyageEmbeddingConfig,

View file

@ -219,6 +219,7 @@ LLM_CONFIG_NAMES = (
"OpenAITextCompletionConfig",
"GroqChatConfig",
"BedrockMantleChatConfig",
"BedrockMantleResponsesAPIConfig",
"A2AConfig",
"GenAIHubOrchestrationConfig",
"VoyageEmbeddingConfig",
@ -886,6 +887,10 @@ _LLM_CONFIGS_IMPORT_MAP = {
".llms.bedrock_mantle.chat.transformation",
"BedrockMantleChatConfig",
),
"BedrockMantleResponsesAPIConfig": (
".llms.bedrock_mantle.responses.transformation",
"BedrockMantleResponsesAPIConfig",
),
"A2AConfig": (".llms.a2a.chat.transformation", "A2AConfig"),
"GenAIHubOrchestrationConfig": (
".llms.sap.chat.transformation",

View file

@ -3,7 +3,7 @@ Amazon Bedrock Mantle - OpenAI-compatible inference engine in Amazon Bedrock.
API docs: https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-mantle.html
Base URL: https://bedrock-mantle.{region}.api.aws/v1
Base URL: https://bedrock-mantle.{region}.api.aws/openai/v1
Auth: AWS Bedrock API key as Bearer token (set via BEDROCK_MANTLE_API_KEY env var)
or region-aware key via BEDROCK_MANTLE_{REGION}_API_KEY.
"""
@ -43,7 +43,7 @@ class BedrockMantleChatConfig(OpenAILikeChatConfig):
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/openai/v1"
)
dynamic_api_key = api_key or get_secret_str("BEDROCK_MANTLE_API_KEY")
return api_base, dynamic_api_key

View file

@ -0,0 +1,61 @@
"""
Amazon Bedrock Mantle - OpenAI-compatible Responses API.
Routes /v1/responses to the provider's native Responses endpoint instead of
the chat-completions translation fallback.
"""
from typing import Optional
from litellm.llms.bedrock_mantle.chat.transformation import BedrockMantleChatConfig
from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig
from litellm.types.router import GenericLiteLLMParams
from litellm.types.utils import LlmProviders
class BedrockMantleResponsesAPIConfig(OpenAIResponsesAPIConfig):
@property
def custom_llm_provider(self) -> LlmProviders:
return LlmProviders.BEDROCK_MANTLE
def validate_environment(
self,
headers: dict,
model: str,
litellm_params: Optional[GenericLiteLLMParams],
) -> dict:
litellm_params = litellm_params or GenericLiteLLMParams()
_, api_key = BedrockMantleChatConfig()._get_openai_compatible_provider_info(
api_base=litellm_params.api_base,
api_key=litellm_params.api_key,
)
if api_key:
headers["Authorization"] = f"Bearer {api_key}"
return headers
def get_complete_url(
self,
api_base: Optional[str],
litellm_params: dict,
) -> str:
(
resolved_api_base,
_,
) = BedrockMantleChatConfig()._get_openai_compatible_provider_info(
api_base=api_base or litellm_params.get("api_base"),
api_key=litellm_params.get("api_key"),
)
if not resolved_api_base:
raise ValueError(
"api_base is required for bedrock_mantle Responses API. "
"Set BEDROCK_MANTLE_API_BASE or BEDROCK_MANTLE_REGION."
)
api_base = resolved_api_base.rstrip("/")
if api_base.endswith("/responses"):
return api_base
if api_base.endswith("/v1"):
return f"{api_base}/responses"
return f"{api_base}/v1/responses"
def supports_native_websocket(self) -> bool:
return False

View file

@ -8865,6 +8865,8 @@ class ProviderConfigManager:
return litellm.OpenRouterResponsesAPIConfig()
elif litellm.LlmProviders.HOSTED_VLLM == provider:
return litellm.HostedVLLMResponsesAPIConfig()
elif litellm.LlmProviders.BEDROCK_MANTLE == provider:
return litellm.BedrockMantleResponsesAPIConfig()
return None
@staticmethod

View file

@ -14,7 +14,11 @@ import pytest
import litellm
from litellm.llms.bedrock_mantle.chat.transformation import BedrockMantleChatConfig
from litellm.llms.bedrock_mantle.responses.transformation import (
BedrockMantleResponsesAPIConfig,
)
from litellm.types.utils import LlmProviders
from litellm.utils import ProviderConfigManager
class TestBedrockMantleProviderRegistration:
@ -50,7 +54,7 @@ class TestBedrockMantleConfig:
monkeypatch.delenv("BEDROCK_MANTLE_API_BASE", raising=False)
cfg = BedrockMantleChatConfig()
api_base, _ = cfg._get_openai_compatible_provider_info(None, None)
assert api_base == "https://bedrock-mantle.eu-west-1.api.aws/v1"
assert api_base == "https://bedrock-mantle.eu-west-1.api.aws/openai/v1"
def test_default_api_base_uses_aws_region(self, monkeypatch):
monkeypatch.delenv("BEDROCK_MANTLE_REGION", raising=False)
@ -58,7 +62,7 @@ class TestBedrockMantleConfig:
monkeypatch.setenv("AWS_REGION", "ap-northeast-1")
cfg = BedrockMantleChatConfig()
api_base, _ = cfg._get_openai_compatible_provider_info(None, None)
assert api_base == "https://bedrock-mantle.ap-northeast-1.api.aws/v1"
assert api_base == "https://bedrock-mantle.ap-northeast-1.api.aws/openai/v1"
def test_default_api_base_fallback_to_us_east_1(self, monkeypatch):
monkeypatch.delenv("BEDROCK_MANTLE_REGION", raising=False)
@ -66,10 +70,10 @@ class TestBedrockMantleConfig:
monkeypatch.delenv("AWS_REGION", raising=False)
cfg = BedrockMantleChatConfig()
api_base, _ = cfg._get_openai_compatible_provider_info(None, None)
assert api_base == "https://bedrock-mantle.us-east-1.api.aws/v1"
assert api_base == "https://bedrock-mantle.us-east-1.api.aws/openai/v1"
def test_custom_api_base_overrides_default(self, monkeypatch):
custom_base = "https://bedrock-mantle.us-west-2.api.aws/v1"
custom_base = "https://bedrock-mantle.us-west-2.api.aws/openai/v1"
cfg = BedrockMantleChatConfig()
api_base, _ = cfg._get_openai_compatible_provider_info(custom_base, None)
assert api_base == custom_base
@ -96,6 +100,21 @@ class TestBedrockMantleConfig:
assert "max_tokens" in params
class TestBedrockMantleResponsesConfig:
def test_responses_complete_url(self, monkeypatch):
monkeypatch.setenv("BEDROCK_MANTLE_REGION", "us-east-1")
cfg = BedrockMantleResponsesAPIConfig()
url = cfg.get_complete_url(api_base=None, litellm_params={})
assert url == "https://bedrock-mantle.us-east-1.api.aws/openai/v1/responses"
def test_provider_config_manager_registers_responses(self):
cfg = ProviderConfigManager.get_provider_responses_api_config(
provider="bedrock_mantle",
model="gpt-5.5",
)
assert isinstance(cfg, BedrockMantleResponsesAPIConfig)
class TestBedrockMantleProviderResolution:
def test_get_llm_provider_resolves_correctly(self):
model, provider, _, _ = litellm.get_llm_provider(