Merge pull request #40798 from BerriAI/litellm_lit7523_mantle_reasoning_summary

fix(bedrock_mantle): gate reasoning.summary on the OpenAI Responses path
This commit is contained in:
Mateo Wang 2026-09-11 20:30:49 -07:00 committed by GitHub
commit 99e14fc2e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 130 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,43 @@ 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[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 (
isinstance(summary, str) and 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 = { # mutable-ok: map_openai_params contract returns a plain dict
key: value for key, value in reasoning.items() if key != "summary"
}
return (
{**params, "reasoning": stripped} # mutable-ok: map_openai_params contract returns a plain dict
if stripped
else { # mutable-ok: map_openai_params contract returns a plain dict
key: value for key, value in params.items() if key != "reasoning"
}
)
def transform_responses_api_request(
self,
model: str,
@ -343,12 +381,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,90 @@ 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)
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()
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, caplog):
cfg = BedrockMantleResponsesAPIConfig()
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,
)
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:
def test_codex_priority_tier_request_becomes_mantle_acceptable(self):
cfg = BedrockMantleResponsesAPIConfig()