mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
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:
parent
8519d7fc24
commit
41e9cc491e
3 changed files with 211 additions and 19 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 == []
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue