Skip to content

Commit d8c24e8

Browse files
fix(bedrock): pre-clamp reasoning_effort for chat invoke; correct test caps
- Add _clamp_adaptive_reasoning_effort_for_bedrock to AmazonAnthropicClaudeConfig so raw reasoning_effort=xhigh degrades to the model's bedrock effort ceiling before AnthropicConfig.map_openai_params converts it to output_config. Mirrors converse path (_handle_reasoning_effort_parameter) and messages path (_clamp_adaptive_reasoning_effort_for_bedrock) so the three Bedrock paths are consistent. - grid_spec: restore caps=_CAPS_4_6 for Bedrock converse/invoke Opus 4.6 entries so the test reflects the model's actual JSON capabilities. Teach expected() to bypass the xhigh/max cap check when bedrock_effort_ceiling will clamp the wire effort, so the test still passes for Bedrock's graceful degradation contract without lying about native model caps. Co-authored-by: Yassin Kortam <[email protected]>
1 parent 1333799 commit d8c24e8

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ def map_openai_params(
7878
# Use a model name that forces tool-based approach
7979
model = "claude-3-sonnet-20240229"
8080

81+
# Clamp ``reasoning_effort`` to the Bedrock effort ceiling before the
82+
# parent mapping converts it to ``output_config.effort`` and the
83+
# downstream effort gate runs. Mirrors the converse path's
84+
# ``_handle_reasoning_effort_parameter`` and the messages path's
85+
# ``_clamp_adaptive_reasoning_effort_for_bedrock`` so adaptive Claude
86+
# requests degrade ``xhigh`` -> ``max`` rather than 400-ing on
87+
# models like Opus 4.6 that don't natively advertise xhigh.
88+
self._clamp_adaptive_reasoning_effort_for_bedrock(
89+
model=original_model, params=non_default_params
90+
)
91+
8192
optional_params = AnthropicConfig.map_openai_params(
8293
self,
8394
non_default_params,
@@ -91,6 +102,27 @@ def map_openai_params(
91102

92103
return optional_params
93104

105+
@staticmethod
106+
def _clamp_adaptive_reasoning_effort_for_bedrock(model: str, params: dict) -> None:
107+
"""Lower ``reasoning_effort`` to the Bedrock effort ceiling before mapping.
108+
109+
Bedrock's adaptive Claude models accept the OpenAI-style
110+
``reasoning_effort`` tier, but the request validator can reject tiers
111+
the model does not natively advertise (e.g. ``xhigh`` on Opus 4.6).
112+
Clamp the raw tier to the model's
113+
``bedrock_output_config_effort_ceiling`` so Claude Code "goal mode"
114+
keeps working. Non-adaptive models and models without a ceiling are
115+
left untouched.
116+
"""
117+
if not AnthropicConfig._is_adaptive_thinking_model(model):
118+
return
119+
effort = params.get("reasoning_effort")
120+
if not isinstance(effort, str):
121+
return
122+
clamped = {"effort": effort}
123+
normalize_bedrock_opus_output_config_effort(model=model, output_config=clamped)
124+
params["reasoning_effort"] = clamped["effort"]
125+
94126
def transform_request(
95127
self,
96128
model: str,

tests/llm_translation/reasoning_effort_grid/grid_spec.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,20 @@ def params(self) -> Dict[str, str]:
7171
_BAD_REQUEST_EFFORTS: FrozenSet[str] = frozenset({"disabled", "invalid", ""})
7272

7373

74+
def _bedrock_clamps_effort(model: "ModelEntry", effort: str) -> bool:
75+
"""Whether Bedrock will clamp ``effort`` down to ``bedrock_effort_ceiling``.
76+
77+
Bedrock chat/messages paths clamp unsupported high tiers (e.g. ``xhigh``
78+
on Opus 4.6) to the model's ceiling rather than rejecting them, so the
79+
missing native capability is OK — the wire effort just degrades.
80+
"""
81+
if model.bedrock_effort_ceiling is None:
82+
return False
83+
if effort not in _EFFORT_RANK or model.bedrock_effort_ceiling not in _EFFORT_RANK:
84+
return False
85+
return _EFFORT_RANK[effort] > _EFFORT_RANK[model.bedrock_effort_ceiling]
86+
87+
7488
def expected(model: ModelEntry, effort: str) -> CellExpectation:
7589
if effort in ("__omit__", "none"):
7690
if model.mode == "budget":
@@ -82,7 +96,7 @@ def expected(model: ModelEntry, effort: str) -> CellExpectation:
8296

8397
if effort in ("xhigh", "max"):
8498
cap = f"supports_{effort}_reasoning_effort"
85-
if cap not in model.caps:
99+
if cap not in model.caps and not _bedrock_clamps_effort(model, effort):
86100
return CellExpectation(status=400, thinking_type=OMIT)
87101

88102
if model.mode == "adaptive":
@@ -233,7 +247,7 @@ def expected(model: ModelEntry, effort: str) -> CellExpectation:
233247
mode="adaptive",
234248
extra_params=(("aws_region_name", "us-east-1"),),
235249
required_env=_BEDROCK_REQ,
236-
caps=_CAPS_OPUS_4_7,
250+
caps=_CAPS_4_6,
237251
bedrock_effort_ceiling="max",
238252
),
239253
ModelEntry(
@@ -262,7 +276,7 @@ def expected(model: ModelEntry, effort: str) -> CellExpectation:
262276
mode="adaptive",
263277
extra_params=(("aws_region_name", "us-east-1"),),
264278
required_env=_BEDROCK_REQ,
265-
caps=_CAPS_OPUS_4_7,
279+
caps=_CAPS_4_6,
266280
bedrock_effort_ceiling="max",
267281
),
268282
ModelEntry(

0 commit comments

Comments
 (0)