Skip to content

fix(bedrock/converse): drop toolSpec.strict for Opus 4.7/4.8 (#31582) - #31585

Closed
ly-wang19 wants to merge 2 commits into
BerriAI:mainfrom
ly-wang19:fix/bedrock-opus-47-strict-tools-31582
Closed

fix(bedrock/converse): drop toolSpec.strict for Opus 4.7/4.8 (#31582)#31585
ly-wang19 wants to merge 2 commits into
BerriAI:mainfrom
ly-wang19:fix/bedrock-opus-47-strict-tools-31582

Conversation

@ly-wang19

Copy link
Copy Markdown
Contributor

What Problem This Solves

Closes #31582.

Sending a tool with strict: true to Claude Opus 4.7 or Opus 4.8 on Bedrock Converse fails with a 400:

litellm.BadRequestError: BedrockException - {"message":"The model returned the following errors: tools.0.custom.strict: Extra inputs are not permitted"}

The same request works on direct Anthropic and on Bedrock Sonnet 4.5/4.6 and Opus 4.5/4.6, so this is specific to Opus 4.7/4.8 on Bedrock Converse. It is a regression: working on 1.85.1, broken on 1.90.0/1.90.0-rc.1.

Why This Change Was Made

PR #29814 ("feat(bedrock): forward strict and additionalProperties to Converse toolSpec") started forwarding strict into the Bedrock Converse toolSpec, gated on get_bedrock_base_model(model).startswith("anthropic"). That gate is too broad: Bedrock Converse routes Opus 4.7/4.8 through an Anthropic-compatible validator that maps toolSpec to the native tool shape (the custom variant) and rejects the extra strict key, even though Anthropic's native API accepts strict as a top-level tool field for these models. Sonnet 4.5/4.6 and Opus ≤4.6 accept toolSpec.strict and keep the existing behavior.

The fix is a focused exclusion: replace the broad startswith("anthropic") gate in litellm_core_utils/prompt_templates/factory.py with a small helper bedrock_converse_supports_strict_tools in litellm/llms/bedrock/common_utils.py that:

The alternative — always drop strict on Bedrock and re-add it elsewhere — would undo the user-visible enum-constraint fix from #29814 for Sonnet/Opus ≤4.6 callers. The targeted exclusion preserves the #29814 fix where it works and removes it only where Bedrock rejects the field.

User Impact

  • Callers sending strict: true tools to bedrock/...claude-opus-4-7 or bedrock/...claude-opus-4-8 stop getting 400 BadRequestError: tools.N.custom.strict: Extra inputs are not permitted. The tool call succeeds (with strict dropped on the Bedrock hop, matching pre-1.86 behavior).
  • Callers using Sonnet 4.5/4.6, Opus ≤4.6, or any other Anthropic model on Bedrock see no change — strict is still forwarded to toolSpec as before.
  • Non-Anthropic Bedrock families (Nova, Llama, GPT-OSS) see no change.

Evidence

  • New regression test test_bedrock_tools_pt_strict_dropped_for_opus_47_48 covers: Opus 4.7 (3 id spellings — bedrock/us.anthropic.claude-opus-4-7, anthropic.claude-opus-4-7-v1:0, mixed dash/underscore) and Opus 4.8 (same spellings) all drop strict; Sonnet 4.5 and Opus 4.6 still forward strict.
  • Existing test_bedrock_tools_pt_strict_parameter (the feat(bedrock): forward strict and additionalProperties to Converse toolSpec #29814 regression test) still passes — confirms the Sonnet/Nova behavior is unchanged.
  • Reproducer from the issue (bedrock/us.anthropic.claude-opus-4-7 with strict: true) is the case the new test locks in.
  • Local run: python3 -m pytest tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_factory.py -k strict → 2 passed.
  • One unrelated test (test_bedrock_converse_messages_pt_document_various_formats) fails in this file both with and without my changes (verified via git stash) — it's a pre-existing MIME-type environment issue, not caused by this PR.

Bedrock Converse routes Claude Opus 4.7/4.8 through an Anthropic-compatible
validator that maps toolSpec to the native tool shape and rejects the extra
`strict` key with `tools.N.custom.strict: Extra inputs are not permitted`,
even though Anthropic's native API accepts `strict` as a top-level tool field
for the same models. Sonnet 4.5/4.6 and Opus <=4.6 accept `toolSpec.strict`
unchanged.

The existing gate `get_bedrock_base_model(model).startswith("anthropic")`
(introduced in BerriAI#29814 to forward `strict` for Claude on Bedrock Converse) is
too broad and regressed Opus 4.7/4.8 callers — see BerriAI#31582.

Replace the inline check with a small `bedrock_converse_supports_strict_tools`
helper that excludes the Opus 4.7/4.8 family from strict forwarding. All
other Anthropic models on Bedrock keep the existing behavior.

Closes BerriAI#31582.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codspeed-hq

codspeed-hq Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing ly-wang19:fix/bedrock-opus-47-strict-tools-31582 (73658ad) with main (0ade44f)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a targeted exclusion so that toolSpec.strict is dropped when calling Claude Opus 4.7/4.8 on Bedrock Converse, fixing a 400 error introduced in 1.86+ where Bedrock's validator rejects the strict key for those model variants. All other Anthropic models (Sonnet 4.5/4.6, Opus ≤4.6) continue to forward strict unchanged.

  • Adds bedrock_converse_supports_strict_tools() in llms/bedrock/common_utils.py to replace the overly broad startswith("anthropic") gate in factory.py, returning False only for the Opus 4.7/4.8 family and non-Anthropic Bedrock models.
  • Adds a new test file covering the Opus 4.7/4.8 exclusion (multiple model ID spellings), the "keep strict" path for Sonnet/Opus ≤4.6, and non-Anthropic families — all as pure unit tests with no network calls.

Confidence Score: 5/5

Safe to merge — the change is a narrow, well-tested exclusion that restores pre-regression behaviour for Opus 4.7/4.8 without touching any other code path.

The fix is a single-function swap in factory.py backed by a clearly scoped helper. The regression test covers all meaningful model-ID spellings for both the excluded (Opus 4.7/4.8) and preserved (Sonnet/Opus ≤4.6, non-Anthropic) paths. No existing tests were weakened, and the change has no impact on routing, auth, or any other critical path.

No files require special attention beyond common_utils.py, where the hardcoded model-name pattern list was already flagged in a prior review thread.

Important Files Changed

Filename Overview
litellm/llms/bedrock/common_utils.py Adds bedrock_converse_supports_strict_tools() helper with a hardcoded tuple of Opus 4.7/4.8 model name patterns to gate toolSpec.strict forwarding; logic is correct and well-documented.
litellm/litellm_core_utils/prompt_templates/factory.py Single-line swap of get_bedrock_base_model(model).startswith("anthropic") for the new bedrock_converse_supports_strict_tools(model) helper — minimal, correct change.
tests/test_litellm/litellm_core_utils/prompt_templates/test_bedrock_converse_strict_tools_opus_47_48.py New regression test file covering Opus 4.7/4.8 strict-drop, Sonnet/Opus ≤4.6 strict-keep, and non-Anthropic drop — pure unit tests with no network calls.

Reviews (2): Last reviewed commit: "fix(bedrock/converse): move strict-tools..." | Re-trigger Greptile

Comment on lines +725 to +734
_BEDROCK_CONVERSE_STRICT_REJECTED_OPUS_PATTERNS = (
"claude-opus-4-7",
"claude_opus_4_7",
"claude-opus-4.7",
"claude_opus_4.7",
"claude-opus-4-8",
"claude_opus_4_8",
"claude-opus-4.8",
"claude_opus_4.8",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Move capability data
This adds another hardcoded model-specific capability list for Bedrock strict support. The repo rule asks these flags to live in model_prices_and_context_window.json and be read via model info; keeping claude-opus-4-7 / 4-8 in code means the next Bedrock validator exception requires a LiteLLM release instead of a model-data update.

Rule Used: What: Do not hardcode model-specific flags in the ... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

The original regression test was added to
test_litellm_core_utils_prompt_templates_factory.py, which has
pre-existing ruff-format violations throughout (multi-line asserts that
fit on one line). The lint workflow runs `ruff format --check` on
changed files only, so touching that file surfaces those pre-existing
violations and fails CI for unrelated reasons.

Move the BerriAI#31582 regression coverage into a new dedicated test file so
the format check stays green. Also collapses the helper's `not any(...)`
onto a single line to satisfy ruff format.

Covers: BerriAI#31582
@mateo-berri

Copy link
Copy Markdown
Contributor

Thanks for the PR! I'll make a quick copy branch to test this on internal e2e tests

@mateo-berri

Copy link
Copy Markdown
Contributor

This has been merged. Thank you again for your contribution!

@mateo-berri mateo-berri closed this Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants