fix(bedrock_mantle): harden reasoning summary validation

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
shivam 2026-09-11 23:34:14 +00:00
parent bf9e8ea9ef
commit 37a1b859d7
2 changed files with 28 additions and 11 deletions

View file

@ -188,12 +188,16 @@ 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:
def _handle_unsupported_reasoning_summary(
self, params: dict[str, object], model: str, drop_params: bool
) -> dict[str, object]:
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:
if summary is None or (
isinstance(summary, str) and summary in _BEDROCK_MANTLE_OPENAI_PATH_SUPPORTED_REASONING_SUMMARIES
):
return params
if not drop_params:
raise litellm.utils.UnsupportedParamsError(
@ -210,11 +214,15 @@ class BedrockMantleResponsesAPIConfig(BedrockMantleAuthMixin, OpenAIResponsesAPI
summary,
sorted(_BEDROCK_MANTLE_OPENAI_PATH_SUPPORTED_REASONING_SUMMARIES),
)
stripped: Final = {key: value for key, value in reasoning.items() if key != "summary"}
stripped: Final = { # mutable-ok: dynamic reasoning fields must remain a mutable dict
key: value for key, value in reasoning.items() if key != "summary"
}
return (
{**params, "reasoning": stripped}
{**params, "reasoning": stripped} # mutable-ok: downstream parameter filtering needs a mutable dict
if stripped
else {key: value for key, value in params.items() if key != "reasoning"}
else { # mutable-ok: downstream parameter filtering needs a mutable dict
key: value for key, value in params.items() if key != "reasoning"
}
)
def transform_responses_api_request(

View file

@ -572,6 +572,16 @@ class TestBedrockMantleReasoningSummary:
assert "reasoning.summary" in str(excinfo.value)
assert "drop_params" in str(excinfo.value)
def test_unhashable_reasoning_summary_raises_unsupported_params_error(self):
cfg = BedrockMantleResponsesAPIConfig()
with pytest.raises(litellm.UnsupportedParamsError) as excinfo:
cfg.map_openai_params(
response_api_optional_params={"reasoning": {"summary": ["detailed"]}},
model="openai.gpt-5.6-sol",
drop_params=False,
)
assert "reasoning.summary" in str(excinfo.value)
@pytest.mark.parametrize("drop_params", [True, False])
def test_supported_reasoning_summary_kept(self, drop_params):
cfg = BedrockMantleResponsesAPIConfig()
@ -600,18 +610,17 @@ class TestBedrockMantleReasoningSummary:
)
assert params == {"stream": True}
def test_drop_logged_at_warning_level(self):
from unittest.mock import patch
def test_drop_logged_at_warning_level(self, caplog):
cfg = BedrockMantleResponsesAPIConfig()
with patch("litellm.llms.bedrock_mantle.responses.transformation.verbose_logger.warning") as mock_warning:
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
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)
warnings = [record for record in caplog.records if "dropping unsupported reasoning.summary" in record.getMessage()]
assert len(warnings) == 1
assert "detailed" in warnings[0].getMessage()
class TestBedrockMantleCodexRequestEndToEnd: