From 4a2478379da48300838c671a324d813cb202e972 Mon Sep 17 00:00:00 2001 From: Sudharsan Rajagopal Date: Fri, 17 Jul 2026 21:56:03 +0000 Subject: [PATCH] fix(bedrock): serve 1M context window for Claude Opus 4.6+/Sonnet 4.6 The AnthropicBedrock client attaches the context-1m-2025-08-07 beta header on every Bedrock Claude request (build_anthropic_bedrock_client in agent/anthropic_adapter.py), unlocking the 1M context window for Opus 4.6/4.7/4.8 and Sonnet 4.6. But BEDROCK_CONTEXT_LENGTHS still capped every Claude model at 200K, so get_model_context_length() budgeted these models at 200K even though 1M was unlocked on the wire. opus-4-8 had no entry at all and substring-matched the generic anthropic.claude-opus-4 -> 200K key, so global.anthropic.claude-opus-4-8 sessions were capped at 200K. Set the 1M-capable models to 1_000_000 to match the beta header and DEFAULT_CONTEXT_LENGTHS in agent/model_metadata.py. Older models (Sonnet 4.5, Opus 4, Sonnet 4, Haiku 4.5, 3.x) stay at 200K. Longer keys win the substring match, so the generic opus-4 fallback still catches unversioned IDs at 200K. --- agent/bedrock_adapter.py | 17 ++++++++++++++--- tests/agent/test_bedrock_adapter.py | 19 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/agent/bedrock_adapter.py b/agent/bedrock_adapter.py index 51d0afbe3cbe8..9b718936ea467 100644 --- a/agent/bedrock_adapter.py +++ b/agent/bedrock_adapter.py @@ -1305,9 +1305,20 @@ def classify_bedrock_error(error_message: str) -> str: # detection is unavailable. BEDROCK_CONTEXT_LENGTHS: Dict[str, int] = { - # Anthropic Claude models on Bedrock - "anthropic.claude-opus-4-6": 200_000, - "anthropic.claude-sonnet-4-6": 200_000, + # Anthropic Claude models on Bedrock. + # Opus 4.6/4.7/4.8 and Sonnet 4.6 serve a 1M context window on Bedrock + # when the request carries the ``context-1m-2025-08-07`` beta header, + # which build_anthropic_bedrock_client() attaches by default (see + # agent/anthropic_adapter.py). These entries must stay in sync with that + # header and with DEFAULT_CONTEXT_LENGTHS in agent/model_metadata.py — + # otherwise the 1M window is unlocked on the wire but the agent's budget + # is capped at 200K. Longer keys win the substring match in + # get_bedrock_context_length(), so the versioned 1M entries below take + # precedence over the generic ``anthropic.claude-opus-4`` fallback. + "anthropic.claude-opus-4-8": 1_000_000, + "anthropic.claude-opus-4-7": 1_000_000, + "anthropic.claude-opus-4-6": 1_000_000, + "anthropic.claude-sonnet-4-6": 1_000_000, "anthropic.claude-sonnet-4-5": 200_000, "anthropic.claude-haiku-4-5": 200_000, "anthropic.claude-opus-4": 200_000, diff --git a/tests/agent/test_bedrock_adapter.py b/tests/agent/test_bedrock_adapter.py index 26a59a33e2a35..0aebad8e61a45 100644 --- a/tests/agent/test_bedrock_adapter.py +++ b/tests/agent/test_bedrock_adapter.py @@ -1167,11 +1167,19 @@ class TestBedrockContextLength: def test_claude_opus_4_6(self): from agent.bedrock_adapter import get_bedrock_context_length - assert get_bedrock_context_length("anthropic.claude-opus-4-6-20250514-v1:0") == 200_000 + # Opus 4.6 serves 1M on Bedrock via the context-1m-2025-08-07 beta. + assert get_bedrock_context_length("anthropic.claude-opus-4-6-20250514-v1:0") == 1_000_000 + + def test_claude_opus_4_8(self): + from agent.bedrock_adapter import get_bedrock_context_length + # Opus 4.8 (incl. global/us inference profiles) serves 1M on Bedrock. + assert get_bedrock_context_length("global.anthropic.claude-opus-4-8") == 1_000_000 + assert get_bedrock_context_length("anthropic.claude-opus-4-8-20270101-v1:0") == 1_000_000 def test_claude_sonnet_versioned(self): from agent.bedrock_adapter import get_bedrock_context_length - assert get_bedrock_context_length("anthropic.claude-sonnet-4-6-20250514-v1:0") == 200_000 + # Sonnet 4.6 serves 1M on Bedrock via the context-1m-2025-08-07 beta. + assert get_bedrock_context_length("anthropic.claude-sonnet-4-6-20250514-v1:0") == 1_000_000 def test_nova_pro(self): from agent.bedrock_adapter import get_bedrock_context_length @@ -1187,8 +1195,11 @@ def test_unknown_model_gets_default(self): def test_inference_profile_resolves(self): from agent.bedrock_adapter import get_bedrock_context_length - # Cross-region inference profiles contain the base model ID - assert get_bedrock_context_length("us.anthropic.claude-sonnet-4-6") == 200_000 + # Cross-region inference profiles contain the base model ID. + # Sonnet 4.6 serves 1M on Bedrock via the context-1m beta. + assert get_bedrock_context_length("us.anthropic.claude-sonnet-4-6") == 1_000_000 + # Sonnet 4.5 (no 1M) still resolves to 200K via the same path. + assert get_bedrock_context_length("us.anthropic.claude-sonnet-4-5") == 200_000 def test_longest_prefix_wins(self): from agent.bedrock_adapter import get_bedrock_context_length