mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
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:
commit
99e14fc2e5
2 changed files with 130 additions and 4 deletions
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue