mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-12 08:42:18 +00:00
fix(bedrock): forward the compact-2026-09-04 compaction param on Invoke /v1/messages (#51223)
* fix(bedrock): forward the compact-2026-09-04 `compaction` param on Invoke /v1/messages
The compact-2026-09-04 beta is a top-level `compaction` request parameter
({"type": "summarize"}), not a context_management edit. The Bedrock Invoke
allowlist is built from BedrockInvokeAnthropicMessagesRequest, which had no
`compaction` field, so the field was dropped and the request reached Bedrock
as an ordinary turn. The bedrock beta map also dropped compact-2026-09-04, so
adopting requests lost the beta.
- add `compaction` to BedrockInvokeAnthropicMessagesRequest
- add compact-2026-09-04 to the bedrock beta map (Invoke only, not Converse)
- add the beta automatically when a `compaction` param is present
Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(bedrock): send `compaction` only with its beta, and add the beta for an adopted block
Follow-ups to forwarding the compact-2026-09-04 `compaction` parameter on
Bedrock Invoke:
- `compaction` stays in the body only when it is an object and
compact-2026-09-04 survives the route's beta filter. bedrock_mantle has no
entry for that beta, so a mantle request keeps the earlier strip instead of
reaching Bedrock with the field and no beta (400 "Extra inputs are not
permitted"). A null or non-object value is dropped the same way.
- A request that adopts the signed compaction block now gets the beta even
without a client anthropic-beta header, as the Anthropic route does
(carries_signed_compaction_block).
- The compaction tests pin the bundled beta config (local_beta_headers_config)
so they do not read the remote copy an earlier test left cached.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(bedrock): type the compaction helper and field as read-only views
The type discipline gate rejects a bare dict or set annotation and a TypedDict field without ReadOnly; the helper only reads the request and grows the caller's beta set in place.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(bedrock): keep the compaction helper within the basedpyright ceiling
Read the request's messages through a typed adapter and delete the dropped compaction key, so the new call adds no unknown member or argument diagnostics.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore(bedrock): drop comments that restate the compaction checks and type the new test params
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Homen Shum <317650355+homenshum-cheiron@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
587902becf
commit
d5a1602968
6 changed files with 164 additions and 1 deletions
|
|
@ -201,6 +201,7 @@
|
|||
"bash_20250124": null,
|
||||
"code-execution-2025-08-25": null,
|
||||
"compact-2026-01-12": "compact-2026-01-12",
|
||||
"compact-2026-09-04": "compact-2026-09-04",
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from litellm.llms.anthropic.common_utils import (
|
|||
OBJECT_MAPPING_ADAPTER,
|
||||
AnthropicModelInfo,
|
||||
carried_thinking_keys,
|
||||
carries_signed_compaction_block,
|
||||
validated_object_mapping,
|
||||
)
|
||||
from litellm.llms.anthropic.messages.native.transformation import (
|
||||
|
|
@ -82,6 +83,7 @@ else:
|
|||
|
||||
|
||||
_MODEL_INFO: Final[TypeAdapter[Mapping[str, object] | None]] = TypeAdapter(Mapping[str, object] | None)
|
||||
_MESSAGE_LIST: Final[TypeAdapter[tuple[object, ...]]] = TypeAdapter(tuple[object, ...])
|
||||
|
||||
|
||||
class AmazonAnthropicClaudeMessagesConfig(
|
||||
|
|
@ -571,6 +573,19 @@ class AmazonAnthropicClaudeMessagesConfig(
|
|||
"edits": retained_edits,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _add_compaction_beta_for_bedrock_invoke(
|
||||
anthropic_messages_request: Mapping[str, object],
|
||||
beta_set: set[str], # mutable-ok: the caller's beta set is grown in place, like the sibling helpers
|
||||
) -> None:
|
||||
"""``compaction`` and an adopted signed block both need the compact-2026-09-04 beta on Bedrock.
|
||||
|
||||
Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
|
||||
"""
|
||||
messages: Final = _MESSAGE_LIST.validate_python(anthropic_messages_request.get("messages") or ())
|
||||
if isinstance(anthropic_messages_request.get("compaction"), dict) or carries_signed_compaction_block(messages):
|
||||
beta_set.add("compact-2026-09-04")
|
||||
|
||||
def strips_beta_gated_fields_from_final_headers(self) -> bool:
|
||||
return False
|
||||
|
||||
|
|
@ -639,6 +654,7 @@ class AmazonAnthropicClaudeMessagesConfig(
|
|||
anthropic_messages_request=anthropic_messages_request,
|
||||
beta_set=beta_set,
|
||||
)
|
||||
self._add_compaction_beta_for_bedrock_invoke(anthropic_messages_request, beta_set) # pyright: ignore[reportUnknownArgumentType] # built from the base signature's bare dict
|
||||
|
||||
self._get_tool_search_beta_header_for_bedrock(
|
||||
model=model,
|
||||
|
|
@ -858,6 +874,14 @@ class AmazonAnthropicClaudeMessagesConfig(
|
|||
if filtered_betas:
|
||||
anthropic_messages_request["anthropic_beta"] = filtered_betas
|
||||
|
||||
if "compaction" in anthropic_messages_request and not (
|
||||
isinstance(anthropic_messages_request["compaction"], dict) and "compact-2026-09-04" in filtered_betas
|
||||
):
|
||||
verbose_logger.debug(
|
||||
"Bedrock Invoke: dropping `compaction`; compact-2026-09-04 is not served on this route"
|
||||
)
|
||||
del anthropic_messages_request["compaction"]
|
||||
|
||||
final_request: Final = OBJECT_MAPPING_ADAPTER.validate_python(anthropic_messages_request) # pyright: ignore[reportUnknownArgumentType] # built from the base signature's bare dict
|
||||
gated: Final = strip_beta_gated_native_request(
|
||||
{},
|
||||
|
|
|
|||
|
|
@ -1257,6 +1257,13 @@ class BedrockInvokeAnthropicMessagesRequest(TypedDict, total=False):
|
|||
# other edit types (e.g. `clear_thinking_20251015`) never reach Bedrock.
|
||||
context_management: dict
|
||||
|
||||
# `compaction` ({"type": "summarize", "instructions"?: str}) is the
|
||||
# `compact-2026-09-04` beta: one summarization call that returns a single
|
||||
# signed compaction block. Bedrock rejects it together with
|
||||
# `context_management`. Ref:
|
||||
# https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
|
||||
compaction: ReadOnly[Mapping[str, object]]
|
||||
|
||||
|
||||
class BedrockBatchRecordKind(Enum):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1547,6 +1547,112 @@ def test_bedrock_messages_preserves_compact_context_management_and_adds_beta():
|
|||
assert result["max_tokens"] == 4096
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("local_beta_headers_config")
|
||||
def test_bedrock_messages_keeps_compaction_param_and_adds_beta():
|
||||
"""
|
||||
The ``compact-2026-09-04`` beta uses a top-level ``compaction`` parameter, not a
|
||||
``context_management`` edit. Bedrock InvokeModel accepts it with that beta and
|
||||
returns one signed compaction block. Before this fix the top-level allowlist
|
||||
dropped ``compaction``, so the request reached Bedrock as an ordinary turn.
|
||||
|
||||
Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
|
||||
"""
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
cfg = AmazonAnthropicClaudeMessagesConfig()
|
||||
messages = [
|
||||
{"role": "user", "content": [{"type": "text", "text": "Is CHG-TR-0042 Type IB in TR-TITCK?"}]},
|
||||
{"role": "assistant", "content": [{"type": "text", "text": "Type IB."}]},
|
||||
{"role": "user", "content": [{"type": "text", "text": "Now KR-MFDS."}]},
|
||||
]
|
||||
optional_params = {"max_tokens": 1024, "compaction": {"type": "summarize", "instructions": "Keep rule ids."}}
|
||||
|
||||
result = cfg.transform_anthropic_messages_request(
|
||||
model="anthropic.claude-opus-5",
|
||||
messages=messages,
|
||||
anthropic_messages_optional_request_params=optional_params,
|
||||
litellm_params=GenericLiteLLMParams(),
|
||||
headers={},
|
||||
)
|
||||
|
||||
assert result["compaction"] == {"type": "summarize", "instructions": "Keep rule ids."}
|
||||
assert "compact-2026-09-04" in result["anthropic_beta"]
|
||||
assert "context_management" not in result
|
||||
assert set(result).issubset(cfg.BEDROCK_INVOKE_ALLOWED_TOP_LEVEL_FIELDS)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("local_beta_headers_config")
|
||||
def test_bedrock_messages_keeps_compact_2026_09_04_beta_for_adopting_request():
|
||||
"""
|
||||
A request that adopts the signed block sends it first in ``messages`` and must
|
||||
carry the ``compact-2026-09-04`` beta again. The bedrock beta map has to keep it.
|
||||
"""
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
cfg = AmazonAnthropicClaudeMessagesConfig()
|
||||
block = {"type": "compaction", "content": "<summary>TR-TITCK: Type IB.</summary>", "signature": "sig"}
|
||||
messages = [{"role": "user", "content": [block, {"type": "text", "text": "Now KR-MFDS."}]}]
|
||||
|
||||
result = cfg.transform_anthropic_messages_request(
|
||||
model="anthropic.claude-opus-5",
|
||||
messages=messages,
|
||||
anthropic_messages_optional_request_params={"max_tokens": 512},
|
||||
litellm_params=GenericLiteLLMParams(),
|
||||
headers={"anthropic-beta": "compact-2026-09-04"},
|
||||
)
|
||||
|
||||
assert "compact-2026-09-04" in result["anthropic_beta"]
|
||||
assert result["messages"][0]["content"][0] == block
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("local_beta_headers_config")
|
||||
def test_bedrock_messages_adds_compact_2026_09_04_beta_for_signed_block_without_client_header():
|
||||
"""
|
||||
A client that adopts the signed block but sends no anthropic-beta header still
|
||||
needs ``compact-2026-09-04``; the Anthropic route adds it for the block, and so
|
||||
does Bedrock Invoke.
|
||||
"""
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
cfg = AmazonAnthropicClaudeMessagesConfig()
|
||||
block = {"type": "compaction", "content": "<summary>TR-TITCK: Type IB.</summary>", "signature": "sig"}
|
||||
messages = [{"role": "user", "content": [block, {"type": "text", "text": "Now KR-MFDS."}]}]
|
||||
|
||||
result = cfg.transform_anthropic_messages_request(
|
||||
model="anthropic.claude-opus-5",
|
||||
messages=messages,
|
||||
anthropic_messages_optional_request_params={"max_tokens": 512},
|
||||
litellm_params=GenericLiteLLMParams(),
|
||||
headers={},
|
||||
)
|
||||
|
||||
assert result["anthropic_beta"] == ["compact-2026-09-04"]
|
||||
assert result["messages"][0]["content"][0] == block
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("local_beta_headers_config")
|
||||
@pytest.mark.parametrize("compaction", [None, "summarize", []])
|
||||
def test_bedrock_messages_drops_compaction_that_is_not_an_object(compaction: object):
|
||||
"""
|
||||
Only an object ``compaction`` gets the beta. Any other value is dropped, as it
|
||||
was before the field was allowed, instead of reaching Bedrock without its beta.
|
||||
"""
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
cfg = AmazonAnthropicClaudeMessagesConfig()
|
||||
|
||||
result = cfg.transform_anthropic_messages_request(
|
||||
model="anthropic.claude-opus-5",
|
||||
messages=[{"role": "user", "content": [{"type": "text", "text": "Hi"}]}],
|
||||
anthropic_messages_optional_request_params={"max_tokens": 512, "compaction": compaction},
|
||||
litellm_params=GenericLiteLLMParams(),
|
||||
headers={},
|
||||
)
|
||||
|
||||
assert "compaction" not in result
|
||||
assert "compact-2026-09-04" not in result.get("anthropic_beta", [])
|
||||
|
||||
|
||||
def test_bedrock_messages_filters_unsupported_context_management_edits():
|
||||
"""
|
||||
Mixed edit lists must drop the LiteLLM-internal ``clear_thinking_20251015``
|
||||
|
|
|
|||
|
|
@ -796,3 +796,26 @@ async def test_mantle_anthropic_messages_streaming_sends_stream_and_passes_throu
|
|||
assert "event: message_start" in text
|
||||
assert '"text": "pong"' in text
|
||||
assert "event: message_stop" in text
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("local_beta_headers_config")
|
||||
def test_mantle_messages_drops_compaction_without_its_beta():
|
||||
"""
|
||||
The bedrock_mantle beta map has no compact-2026-09-04, so the beta is filtered
|
||||
out on this route. ``compaction`` must go with it, or the body carries a field
|
||||
that Bedrock refuses without the beta.
|
||||
"""
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
config = AmazonMantleMessagesConfig()
|
||||
headers: dict[str, str] = {}
|
||||
request = config.transform_anthropic_messages_request(
|
||||
model="mantle/anthropic.claude-mythos-preview",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
anthropic_messages_optional_request_params={"max_tokens": 100, "compaction": {"type": "summarize"}},
|
||||
litellm_params=GenericLiteLLMParams(),
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
assert "compaction" not in request
|
||||
assert "compact-2026-09-04" not in headers.get("anthropic-beta", "")
|
||||
|
|
|
|||
|
|
@ -99,7 +99,9 @@ class TestAnthropicBetaHeadersFiltering:
|
|||
filtered = filter_and_transform_beta_headers(
|
||||
beta_headers=all_headers, provider=provider
|
||||
)
|
||||
assert ("compact-2026-09-04" in filtered) is (provider in ("anthropic", "azure_ai", "vertex_ai"))
|
||||
# Bedrock InvokeModel serves the compact-2026-09-04 `compaction` parameter; Converse does not.
|
||||
# https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
|
||||
assert ("compact-2026-09-04" in filtered) is (provider in ("anthropic", "azure_ai", "vertex_ai", "bedrock"))
|
||||
assert ("mid-conversation-tool-changes-2026-07-01" in filtered) is (provider != "bedrock_converse")
|
||||
|
||||
for header in unsupported_headers:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue