fix(bedrock-invoke): retain clear_tool_uses_20250919 context_management edits and emit context-management-2025-06-27 beta (LIT-3393) (#32658)

* fix(bedrock-invoke): retain clear_tool_uses_20250919 context_management edits and emit context-management-2025-06-27 beta (LIT-3393)

Copy of #29206 by oss-agent-shin, rebased onto litellm_internal_staging so CircleCI can run.

Bedrock InvokeModel supports automatic tool-call clearing (clear_tool_uses_20250919) under the context-management-2025-06-27 beta, but LiteLLM stripped the edit and dropped the beta header, causing a Bedrock 400. This maps bedrock.context-management-2025-06-27 to itself in anthropic_beta_headers_config.json (bedrock_converse stays null) and rewrites _filter_context_management_for_bedrock_invoke around an allowlist of supported edit types that keeps each supported edit and adds its matching beta.

* test(bedrock-invoke): restore beta-headers config cache with a shared fixture in LIT-3393 tests

Greptile flagged that three of the four new tests reloaded the module-level
beta-headers config into local mode without restoring it on teardown, leaking
state into later tests in the same process. Move setup/teardown into a
local_beta_headers_config fixture used by all four tests.

---------

Co-authored-by: oss-agent-shin <ext-agent-shin@berri.ai>
This commit is contained in:
Mateo Wang 2026-07-09 14:31:27 -07:00 • committed by GitHub
parent 8519d7fc24
commit 41e9cc491e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 211 additions and 19 deletions

View file

@ -102,7 +102,7 @@
"computer-use-2025-01-24": "computer-use-2025-01-24",
"computer-use-2025-11-24": "computer-use-2025-11-24",
"context-1m-2025-08-07": "context-1m-2025-08-07",
"context-management-2025-06-27": null,
"context-management-2025-06-27": "context-management-2025-06-27",
"effort-2025-11-24": "effort-2025-11-24",
"fast-mode-2026-02-01": null,
"files-api-2025-04-14": null,

View file

@ -489,24 +489,43 @@ class AmazonAnthropicClaudeMessagesConfig(
if self._supports_tool_search_on_bedrock(model):
beta_set.add("tool-search-tool-2025-10-19")
# Bedrock-InvokeModel-supported ``context_management.edits`` types and the
# ``anthropic-beta`` header that each one requires. ``clear_thinking_20251015``
# is intentionally absent — it is LiteLLM-internal, consumed via
# ``_ensure_thinking_for_clear_thinking_context_management``, and forwarding
# the raw edit trips Bedrock's
# ``"context_management: Extra inputs are not permitted"`` 400.
#
# Bedrock InvokeModel DOES support ``clear_tool_uses_20250919`` under the
# ``context-management-2025-06-27`` beta. AWS docs:
# https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md
_BEDROCK_INVOKE_SUPPORTED_CONTEXT_MANAGEMENT_EDITS: Dict[str, str] = {
"compact_20260112": ANTHROPIC_BETA_HEADER_VALUES.COMPACT_2026_01_12.value,
"clear_tool_uses_20250919": ANTHROPIC_BETA_HEADER_VALUES.CONTEXT_MANAGEMENT_2025_06_27.value,
}
@staticmethod
def _filter_context_management_for_bedrock_invoke(
anthropic_messages_request: Dict,
beta_set: set,
) -> None:
"""
Bedrock InvokeModel accepts ``context_management`` only when it carries
``compact_20260112`` edits paired with the ``compact-2026-01-12``
anthropic-beta header. Other edit types (notably ``clear_thinking_20251015``,
which Claude Code sends on every request) are LiteLLM-internal and would
cause Bedrock to 400 with ``"context_management: Extra inputs are not
permitted"``.
Filter ``context_management.edits`` to the subset that Bedrock InvokeModel
accepts and add the matching ``anthropic-beta`` header for each surviving
edit type.
Filter the edits list to the supported subset, add the beta header when
compact edits remain, and drop ``context_management`` entirely when no
supported edits are left so the safety-net allowlist can pass it through.
- ``compact_20260112`` -> ``compact-2026-01-12``
- ``clear_tool_uses_20250919`` -> ``context-management-2025-06-27``
Ref: https://github.com/BerriAI/litellm/issues/27532
Other edit types (notably ``clear_thinking_20251015``, which Claude Code
sends on every request) are LiteLLM-internal: thinking is injected
separately via ``_ensure_thinking_for_clear_thinking_context_management``,
and forwarding the raw edit would trip Bedrock's
``"context_management: Extra inputs are not permitted"`` 400.
Refs:
* https://github.com/BerriAI/litellm/issues/27532
* https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md
"""
cm = anthropic_messages_request.get("context_management")
if not isinstance(cm, dict):
@ -516,15 +535,17 @@ class AmazonAnthropicClaudeMessagesConfig(
anthropic_messages_request.pop("context_management", None)
return
compact_edits = [e for e in edits if isinstance(e, dict) and e.get("type") == "compact_20260112"]
if compact_edits:
beta_set.add(ANTHROPIC_BETA_HEADER_VALUES.COMPACT_2026_01_12.value)
anthropic_messages_request["context_management"] = {
**cm,
"edits": compact_edits,
}
else:
supported = AmazonAnthropicClaudeMessagesConfig._BEDROCK_INVOKE_SUPPORTED_CONTEXT_MANAGEMENT_EDITS
retained_edits = [e for e in edits if isinstance(e, dict) and e.get("type") in supported]
if not retained_edits:
anthropic_messages_request.pop("context_management", None)
return
beta_set.update(supported[e["type"]] for e in retained_edits)
anthropic_messages_request["context_management"] = {
**cm,
"edits": retained_edits,
}
def _get_bedrock_invoke_anthropic_beta_headers(
self,

View file

@ -2090,3 +2090,174 @@ def test_bedrock_clear_thinking_leaves_enabled_thinking_on_non_adaptive_model():
assert changed is False
assert request["thinking"] == {"type": "enabled", "budget_tokens": 8000}
assert "output_config" not in request
@pytest.fixture
def local_beta_headers_config(monkeypatch):
from litellm.anthropic_beta_headers_manager import reload_beta_headers_config
monkeypatch.setenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", "True")
reload_beta_headers_config()
yield
monkeypatch.delenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", raising=False)
reload_beta_headers_config()
def test_bedrock_messages_preserves_clear_tool_uses_context_management_and_adds_beta(
local_beta_headers_config,
):
"""
LIT-3393: Bedrock InvokeModel supports automatic tool-call clearing via
``clear_tool_uses_20250919`` under the ``context-management-2025-06-27``
beta. Before the LIT-3393 fix, the transformation stripped this edit (only
``compact_20260112`` survived) AND the beta was filtered out by
``filter_and_transform_beta_headers`` for ``bedrock``, producing a Bedrock
400 ``"context_management: Extra inputs are not permitted"``.
Post-fix, the edit must reach the body and the beta must reach
``anthropic_beta``.
AWS docs ("Automatic tool call clearing (Beta)"):
https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages-tool-use.md
"""
from litellm.types.router import GenericLiteLLMParams
cfg = AmazonAnthropicClaudeMessagesConfig()
messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}]
optional_params = {
"max_tokens": 4096,
"context_management": {
"edits": [{"type": "clear_tool_uses_20250919"}]
},
}
result = cfg.transform_anthropic_messages_request(
model="anthropic.claude-haiku-4-5-20251001-v1:0",
messages=messages,
anthropic_messages_optional_request_params=optional_params,
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert result.get("context_management") == {
"edits": [{"type": "clear_tool_uses_20250919"}]
}, "clear_tool_uses_20250919 edit must reach Bedrock InvokeModel body"
assert "context-management-2025-06-27" in result.get("anthropic_beta", []), (
"context-management-2025-06-27 beta must reach the InvokeModel body so "
"the tool-call-clearing edit is accepted"
)
def test_bedrock_messages_preserves_mixed_compact_and_clear_tool_uses_edits(
local_beta_headers_config,
):
"""
LIT-3393: a request mixing ``compact_20260112`` and
``clear_tool_uses_20250919`` must keep BOTH edits and emit BOTH
anthropic-beta values (``compact-2026-01-12`` + ``context-management-2025-06-27``).
"""
from litellm.types.router import GenericLiteLLMParams
cfg = AmazonAnthropicClaudeMessagesConfig()
messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}]
optional_params = {
"max_tokens": 4096,
"context_management": {
"edits": [
{"type": "compact_20260112"},
{"type": "clear_tool_uses_20250919"},
]
},
}
result = cfg.transform_anthropic_messages_request(
model="anthropic.claude-sonnet-4-6-20250929-v1:0",
messages=messages,
anthropic_messages_optional_request_params=optional_params,
litellm_params=GenericLiteLLMParams(),
headers={},
)
cm = result.get("context_management")
assert cm is not None
edit_types = sorted(e.get("type") for e in cm["edits"])
assert edit_types == ["clear_tool_uses_20250919", "compact_20260112"]
betas = result.get("anthropic_beta", [])
assert "compact-2026-01-12" in betas
assert "context-management-2025-06-27" in betas
def test_bedrock_messages_filters_clear_thinking_keeps_clear_tool_uses(
local_beta_headers_config,
):
"""
LIT-3393: ``clear_thinking_20251015`` remains LiteLLM-internal (consumed via
thinking-injection) and MUST be stripped from the body, while
``clear_tool_uses_20250919`` (officially supported on Bedrock InvokeModel)
survives in the same request.
"""
from litellm.types.router import GenericLiteLLMParams
cfg = AmazonAnthropicClaudeMessagesConfig()
messages = [{"role": "user", "content": [{"type": "text", "text": "Hi"}]}]
optional_params = {
"max_tokens": 4096,
"context_management": {
"edits": [
{"type": "clear_thinking_20251015", "keep": "all"},
{"type": "clear_tool_uses_20250919"},
]
},
}
result = cfg.transform_anthropic_messages_request(
model="anthropic.claude-opus-4-7",
messages=messages,
anthropic_messages_optional_request_params=optional_params,
litellm_params=GenericLiteLLMParams(),
headers={},
)
cm = result.get("context_management")
assert cm is not None
assert [e.get("type") for e in cm["edits"]] == [
"clear_tool_uses_20250919"
], "clear_thinking_20251015 must still be stripped (LiteLLM-internal)"
betas = result.get("anthropic_beta", [])
assert "context-management-2025-06-27" in betas
# ``compact-2026-01-12`` was not requested.
assert "compact-2026-01-12" not in betas
def test_filter_and_transform_beta_headers_passes_context_management_for_bedrock(
local_beta_headers_config,
):
"""
LIT-3393: ``anthropic_beta_headers_config.json`` previously mapped
``bedrock.context-management-2025-06-27`` to ``null``, so
``filter_and_transform_beta_headers`` dropped the header even when the
transformation tried to set it. This regression guard locks the bundled
mapping in place.
Pinned to the bundled local config via ``LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS``
so the assertion is not subject to whatever the upstream remote currently
serves or what previous tests left in the module cache.
"""
from litellm.anthropic_beta_headers_manager import filter_and_transform_beta_headers
out = filter_and_transform_beta_headers(
["context-management-2025-06-27"],
provider="bedrock",
)
assert out == ["context-management-2025-06-27"]
# Bedrock_converse genuinely lacks it per AWS docs; this guard prevents
# an accidental flip there.
out_converse = filter_and_transform_beta_headers(
["context-management-2025-06-27"],
provider="bedrock_converse",
)
assert out_converse == []