fix(bedrock_mantle): gate reasoning.summary on the OpenAI Responses path

Mantle's /openai/v1/responses rejects reasoning.summary values other than "auto" with 400 unsupported_parameter. Drop it with a warning under drop_params, otherwise raise UnsupportedParamsError naming the remedy.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
shivam 2026-09-11 23:19:17 +00:00
parent 22c60ef9e7
commit bf9e8ea9ef
2 changed files with 113 additions and 4 deletions

View file

@ -56,6 +56,7 @@ _BEDROCK_MANTLE_SUPPORTED_RESPONSE_TOOL_TYPES: Final = frozenset(
)
_BEDROCK_MANTLE_SUPPORTED_SERVICE_TIERS: Final = frozenset({"auto", "default"})
_BEDROCK_MANTLE_OPENAI_PATH_SUPPORTED_REASONING_SUMMARIES: Final = frozenset({"auto"})
_CODEX_ADDITIONAL_TOOLS_INPUT_ITEM_TYPE: Final = "additional_tools"
@ -187,6 +188,35 @@ class BedrockMantleResponsesAPIConfig(BedrockMantleAuthMixin, OpenAIResponsesAPI
)
return {key: value for key, value in params.items() if key != "service_tier"}
def _handle_unsupported_reasoning_summary(self, params: dict, model: str, drop_params: bool) -> dict:
reasoning: Final = params.get("reasoning")
if not self.use_openai_path or not isinstance(reasoning, dict):
return params
summary: Final = reasoning.get("summary")
if summary is None or summary in _BEDROCK_MANTLE_OPENAI_PATH_SUPPORTED_REASONING_SUMMARIES:
return params
if not drop_params:
raise litellm.utils.UnsupportedParamsError(
status_code=400,
message=(
f"bedrock_mantle does not support reasoning.summary={summary!r} for {model!r}; the Bedrock Mantle "
"OpenAI Responses path only accepts 'auto'. Set `drop_params: true` (litellm_settings or this "
'deployment\'s litellm_params) to have LiteLLM drop it, or set `model_reasoning_summary = "auto"` '
"in the client (Codex CLI: ~/.codex/config.toml)."
),
)
verbose_logger.warning(
"Bedrock Mantle Responses API: dropping unsupported reasoning.summary %r (supported: %s).",
summary,
sorted(_BEDROCK_MANTLE_OPENAI_PATH_SUPPORTED_REASONING_SUMMARIES),
)
stripped: Final = {key: value for key, value in reasoning.items() if key != "summary"}
return (
{**params, "reasoning": stripped}
if stripped
else {key: value for key, value in params.items() if key != "reasoning"}
)
def transform_responses_api_request(
self,
model: str,
@ -343,12 +373,16 @@ class BedrockMantleResponsesAPIConfig(BedrockMantleAuthMixin, OpenAIResponsesAPI
model: str,
drop_params: bool,
) -> dict:
params: Final = self._handle_unsupported_service_tier(
super().map_openai_params(
response_api_optional_params=response_api_optional_params,
model=model,
params: Final = self._handle_unsupported_reasoning_summary(
self._handle_unsupported_service_tier(
super().map_openai_params(
response_api_optional_params=response_api_optional_params,
model=model,
drop_params=drop_params,
),
drop_params=drop_params,
),
model=model,
drop_params=drop_params,
)

View file

@ -539,6 +539,81 @@ class TestBedrockMantleServiceTier:
assert "priority" in str(mock_warning.call_args)
class TestBedrockMantleReasoningSummary:
@pytest.mark.parametrize("summary", ["concise", "detailed"])
def test_unsupported_reasoning_summary_dropped_when_drop_params_true(self, summary):
cfg = BedrockMantleResponsesAPIConfig()
params = cfg.map_openai_params(
response_api_optional_params={"reasoning": {"effort": "medium", "summary": summary}},
model="openai.gpt-5.6-sol",
drop_params=True,
)
assert params["reasoning"] == {"effort": "medium"}
def test_reasoning_summary_only_field_drops_reasoning(self):
cfg = BedrockMantleResponsesAPIConfig()
params = cfg.map_openai_params(
response_api_optional_params={"reasoning": {"summary": "detailed"}},
model="openai.gpt-5.6-sol",
drop_params=True,
)
assert "reasoning" not in params
@pytest.mark.parametrize("summary", ["concise", "detailed"])
def test_unsupported_reasoning_summary_raises_when_drop_params_false(self, summary):
cfg = BedrockMantleResponsesAPIConfig()
with pytest.raises(litellm.UnsupportedParamsError) as excinfo:
cfg.map_openai_params(
response_api_optional_params={"reasoning": {"effort": "medium", "summary": summary}},
model="openai.gpt-5.6-sol",
drop_params=False,
)
assert summary in str(excinfo.value)
assert "reasoning.summary" in str(excinfo.value)
assert "drop_params" in str(excinfo.value)
@pytest.mark.parametrize("drop_params", [True, False])
def test_supported_reasoning_summary_kept(self, drop_params):
cfg = BedrockMantleResponsesAPIConfig()
params = cfg.map_openai_params(
response_api_optional_params={"reasoning": {"effort": "medium", "summary": "auto"}},
model="openai.gpt-5.6-sol",
drop_params=drop_params,
)
assert params["reasoning"] == {"effort": "medium", "summary": "auto"}
def test_reasoning_summary_kept_on_standard_path(self):
cfg = BedrockMantleResponsesAPIConfig(use_openai_path=False)
params = cfg.map_openai_params(
response_api_optional_params={"reasoning": {"effort": "medium", "summary": "detailed"}},
model="openai.gpt-oss-120b",
drop_params=False,
)
assert params["reasoning"] == {"effort": "medium", "summary": "detailed"}
def test_absent_reasoning_untouched(self):
cfg = BedrockMantleResponsesAPIConfig()
params = cfg.map_openai_params(
response_api_optional_params={"stream": True},
model="openai.gpt-5.6-sol",
drop_params=False,
)
assert params == {"stream": True}
def test_drop_logged_at_warning_level(self):
from unittest.mock import patch
cfg = BedrockMantleResponsesAPIConfig()
with patch("litellm.llms.bedrock_mantle.responses.transformation.verbose_logger.warning") as mock_warning:
cfg.map_openai_params(
response_api_optional_params={"reasoning": {"effort": "medium", "summary": "detailed"}},
model="openai.gpt-5.6-sol",
drop_params=True,
)
assert mock_warning.call_count == 1
assert "detailed" in str(mock_warning.call_args)
class TestBedrockMantleCodexRequestEndToEnd:
def test_codex_priority_tier_request_becomes_mantle_acceptable(self):
cfg = BedrockMantleResponsesAPIConfig()