This commit is contained in:
Scott Wilson 2026-10-03 03:30:00 -04:00 • committed by GitHub
commit ab1ee4944b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 3 deletions

View file

@ -129,7 +129,7 @@
"mcp-servers-2025-12-04": null,
"output-128k-2025-02-19": null,
"structured-output-2024-03-01": null,
"per-turn-control-2026-07-01": null,
"per-turn-control-2026-07-01": "per-turn-control-2026-07-01",
"prompt-caching-scope-2026-01-05": null,
"skills-2025-10-02": null,
"structured-outputs-2025-11-13": null,

View file

@ -95,14 +95,14 @@ def test_added_per_turn_control_beta_survives_the_anthropic_allowlist():
assert PER_TURN_CONTROL in _betas(filtered)
@pytest.mark.parametrize("provider", ["bedrock", "bedrock_converse", "databricks"])
@pytest.mark.parametrize("provider", ["bedrock_converse", "databricks"])
def test_per_turn_control_beta_is_dropped_for_providers_without_it(provider):
filtered = update_headers_with_filtered_beta(headers={"anthropic-beta": PER_TURN_CONTROL}, provider=provider)
assert "anthropic-beta" not in filtered
@pytest.mark.parametrize("provider", ["azure_ai", "vertex_ai"])
@pytest.mark.parametrize("provider", ["azure_ai", "bedrock", "vertex_ai"])
def test_per_turn_control_beta_is_forwarded_for_providers_with_it(provider):
filtered = update_headers_with_filtered_beta(headers={"anthropic-beta": PER_TURN_CONTROL}, provider=provider)

View file

@ -2302,6 +2302,37 @@ def test_bedrock_invoke_transform_hoists_only_leading_system_run(local_model_cos
]
def test_bedrock_invoke_transform_forwards_per_turn_control_beta_with_message_output_config(
local_model_cost_map, local_beta_headers_config
):
"""Claude Code's per-turn control sends a mid-conversation ``role: "system"``
message carrying a message-level ``output_config`` together with the
per-turn-control-2026-07-01 beta. Bedrock Invoke rejects that message with
"messages.N.output_config: Extra inputs are not permitted" unless the beta
is present, so the beta must reach ``anthropic_beta`` and the message must
stay in place with its ``output_config``."""
from litellm.types.router import GenericLiteLLMParams
cfg = AmazonAnthropicClaudeMessagesConfig()
messages = [
{"role": "user", "content": "hi"},
{"role": "assistant", "content": "hello"},
{"role": "system", "content": [], "output_config": {"effort": "low"}},
{"role": "user", "content": "continue"},
]
result = cfg.transform_anthropic_messages_request(
model="us.anthropic.claude-opus-5-5",
messages=copy.deepcopy(messages),
anthropic_messages_optional_request_params={"max_tokens": 256, "stream": False},
litellm_params=GenericLiteLLMParams(),
headers={"anthropic-beta": "per-turn-control-2026-07-01"},
)
assert result["messages"] == messages
assert result["anthropic_beta"].count("per-turn-control-2026-07-01") == 1
def test_bedrock_invoke_transform_converts_mid_conversation_system_for_older_claude(local_model_cost_map):
"""Invoke rejects ``role: "system"`` in every position on Opus 4.7, Sonnet
4.6, Haiku 4.5, etc. ("role 'system' is not supported on this model"), but

View file

@ -458,6 +458,21 @@ class TestAnthropicBetaHeadersFiltering:
assert filtered == ["dangerous-tool-use-2026-09-03"]
@pytest.mark.parametrize("provider", ["anthropic", "bedrock", "bedrock_mantle"])
def test_per_turn_control_forwarded(self, provider):
"""Claude Code sends per-turn-control-2026-07-01 together with a
mid-conversation `role: system` message that carries a message-level
`output_config` (e.g. an effort change). Bedrock Invoke accepts that
message only when the beta is present and otherwise answers
"messages.N.output_config: Extra inputs are not permitted", so dropping
the header (previously mapped to null) turned every such turn into a 400."""
filtered = filter_and_transform_beta_headers(
beta_headers=["per-turn-control-2026-07-01"],
provider=provider,
)
assert filtered == ["per-turn-control-2026-07-01"]
def test_null_value_headers_filtered(self):
"""Test that headers with null values are always filtered out."""
for provider in [