From 23e133731381597ba532575d40a4d75724b32a66 Mon Sep 17 00:00:00 2001 From: Kenneth Wong Date: Sat, 27 Jun 2026 10:47:15 +0800 Subject: [PATCH 1/4] feat(anthropic): add claude-opus-4-8 context limit and pricing Register the newest Opus tier (claude-opus-4-8) in ANTHROPIC_CONTEXT_LIMITS (1M) and ANTHROPIC_PRICING (Opus tier: $15/$75/$1.50 per Mtok), matching the claude-opus-4-7 entry it sits beside. Without this, the proxy cannot size the context window or estimate cost for the model. Add context-limit and pricing-parity tests alongside the existing 4-7 ones. --- headroom/providers/anthropic.py | 4 ++++ tests/test_providers/test_anthropic.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/headroom/providers/anthropic.py b/headroom/providers/anthropic.py index 93d3d1bd6..491739190 100644 --- a/headroom/providers/anthropic.py +++ b/headroom/providers/anthropic.py @@ -83,6 +83,8 @@ def sanitize_anthropic_model_metadata(value: Any) -> Any: # Anthropic model context limits # All Claude 3+ models have 200K context ANTHROPIC_CONTEXT_LIMITS: dict[str, int] = { + # Claude 4.8 (Opus 4.8) - 1M context + "claude-opus-4-8": 1000000, # Claude 4.7 (Opus 4.7) - 1M context "claude-opus-4-7": 1000000, # Claude 4.6 (Opus 4.6) - 1M context @@ -112,6 +114,8 @@ def sanitize_anthropic_model_metadata(value: Any) -> Any: # NOTE: These are ESTIMATES. Always verify against actual Anthropic billing. # Last updated: 2025-01-14 ANTHROPIC_PRICING: dict[str, dict[str, float]] = { + # Claude 4.8 (Opus tier pricing) + "claude-opus-4-8": {"input": 15.00, "output": 75.00, "cached_input": 1.50}, # Claude 4.7 (Opus tier pricing) "claude-opus-4-7": {"input": 15.00, "output": 75.00, "cached_input": 1.50}, # Claude 4.6 (Opus tier pricing) diff --git a/tests/test_providers/test_anthropic.py b/tests/test_providers/test_anthropic.py index cdf503627..a38aa88f1 100644 --- a/tests/test_providers/test_anthropic.py +++ b/tests/test_providers/test_anthropic.py @@ -77,6 +77,9 @@ def test_get_context_limit_claude_opus(self, anthropic_provider): def test_get_context_limit_strips_ansi_model_suffix(self, anthropic_provider): assert anthropic_provider.get_context_limit("claude-opus-4-7[1m]") == 1000000 + def test_get_context_limit_claude_opus_4_8(self, anthropic_provider): + assert anthropic_provider.get_context_limit("claude-opus-4-8") == 1000000 + def test_supports_model_known(self, anthropic_provider): assert anthropic_provider.supports_model("claude-3-5-sonnet-20241022") @@ -110,3 +113,8 @@ def test_pricing_lookup_strips_ansi_model_suffix(self, anthropic_provider): assert anthropic_provider._get_pricing("claude-opus-4-7[1m]") == ( anthropic_provider._get_pricing("claude-opus-4-7") ) + + def test_opus_4_8_pricing_matches_opus_tier(self, anthropic_provider): + assert anthropic_provider._get_pricing("claude-opus-4-8") == ( + anthropic_provider._get_pricing("claude-opus-4-7") + ) From f44e11ef0ee6a3c58e5a257727273afba0dc38e6 Mon Sep 17 00:00:00 2001 From: Kenneth Wong Date: Sat, 27 Jun 2026 13:47:02 +0800 Subject: [PATCH 2/4] fix(anthropic): correct Opus 4.6-4.8 pricing to current Anthropic rates Opus 4.8, 4.7, and 4.6 were stored at the deprecated Opus tier ($15 input / $75 output / $1.50 cache-read), but all three have shipped at the current Opus tier since 4.5: $5/MTok input, $25/MTok output, and a $0.50/MTok cache read (0.1x input). Verified against the live anthropic.com/pricing table. Replaces the fragile parity-with-4.7 assertion with explicit absolute-value checks across 4.6-4.8 so the rate is pinned rather than silently inherited. Refs #1485 --- headroom/providers/anthropic.py | 15 ++++++++------- tests/test_providers/test_anthropic.py | 13 +++++++++---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/headroom/providers/anthropic.py b/headroom/providers/anthropic.py index 491739190..b80ee8a0c 100644 --- a/headroom/providers/anthropic.py +++ b/headroom/providers/anthropic.py @@ -112,14 +112,15 @@ def sanitize_anthropic_model_metadata(value: Any) -> Any: # Fallback pricing - LiteLLM is preferred source # NOTE: These are ESTIMATES. Always verify against actual Anthropic billing. -# Last updated: 2025-01-14 +# Last updated: 2026-06-27 ANTHROPIC_PRICING: dict[str, dict[str, float]] = { - # Claude 4.8 (Opus tier pricing) - "claude-opus-4-8": {"input": 15.00, "output": 75.00, "cached_input": 1.50}, - # Claude 4.7 (Opus tier pricing) - "claude-opus-4-7": {"input": 15.00, "output": 75.00, "cached_input": 1.50}, - # Claude 4.6 (Opus tier pricing) - "claude-opus-4-6": {"input": 15.00, "output": 75.00, "cached_input": 1.50}, + # Claude 4.8 — current Opus tier (anthropic.com/pricing): $5 in / $25 out, + # prompt-cache read = 0.1x input ($0.50). + "claude-opus-4-8": {"input": 5.00, "output": 25.00, "cached_input": 0.50}, + # Claude 4.7 (current Opus tier) + "claude-opus-4-7": {"input": 5.00, "output": 25.00, "cached_input": 0.50}, + # Claude 4.6 (current Opus tier) + "claude-opus-4-6": {"input": 5.00, "output": 25.00, "cached_input": 0.50}, # Claude 4.5 (Opus tier pricing) "claude-opus-4-5-20251101": {"input": 15.00, "output": 75.00, "cached_input": 1.50}, # Claude 4 (Sonnet/Haiku tier pricing) diff --git a/tests/test_providers/test_anthropic.py b/tests/test_providers/test_anthropic.py index a38aa88f1..0306644f7 100644 --- a/tests/test_providers/test_anthropic.py +++ b/tests/test_providers/test_anthropic.py @@ -114,7 +114,12 @@ def test_pricing_lookup_strips_ansi_model_suffix(self, anthropic_provider): anthropic_provider._get_pricing("claude-opus-4-7") ) - def test_opus_4_8_pricing_matches_opus_tier(self, anthropic_provider): - assert anthropic_provider._get_pricing("claude-opus-4-8") == ( - anthropic_provider._get_pricing("claude-opus-4-7") - ) + @pytest.mark.parametrize("model", ["claude-opus-4-6", "claude-opus-4-7", "claude-opus-4-8"]) + def test_current_opus_tier_pricing(self, anthropic_provider, model): + # Opus 4.6–4.8 share the same current-tier rates per anthropic.com/pricing + # (verified 2026-06-27): $5/MTok input, $25/MTok output, cache read = 0.1x input ($0.50). + assert anthropic_provider._get_pricing(model) == { + "input": 5.00, + "output": 25.00, + "cached_input": 0.50, + } From 2205ba30982e5d072c2b1ec055b34593733c8470 Mon Sep 17 00:00:00 2001 From: Kenneth Wong Date: Sat, 27 Jun 2026 13:53:29 +0800 Subject: [PATCH 3/4] feat(anthropic): add Sonnet 4.5/4.6 metadata, align Opus 4.5 pricing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the fallback model tables to cover the newer Sonnet releases and correct the last stale Opus entry, all per the current anthropic.com/pricing page (verified 2026-06-27): - claude-sonnet-4-6: $3/$15/$0.30, 1M context window (ships in the long-context pricing tier — the sonnet pattern default would otherwise report 200K) - claude-sonnet-4-5: $3/$15/$0.30, 200K context - claude-opus-4-5-20251101: corrected from the deprecated $15/$75 Opus tier to the current $5/$25/$0.50 tier shared by 4.5-4.8 Adds Sonnet context-limit and tier-pricing test coverage; extends the Opus tier test to include 4.5. Refs #1485 --- headroom/providers/anthropic.py | 11 +++++++++-- tests/test_providers/test_anthropic.py | 23 +++++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/headroom/providers/anthropic.py b/headroom/providers/anthropic.py index b80ee8a0c..00ee244c4 100644 --- a/headroom/providers/anthropic.py +++ b/headroom/providers/anthropic.py @@ -91,6 +91,10 @@ def sanitize_anthropic_model_metadata(value: Any) -> Any: "claude-opus-4-6": 1000000, # Claude 4.5 (Opus 4.5) "claude-opus-4-5-20251101": 200000, + # Claude Sonnet 4.6 — 1M context window (long-context pricing tier) + "claude-sonnet-4-6": 1000000, + # Claude Sonnet 4.5 + "claude-sonnet-4-5": 200000, # Claude 4 (Sonnet 4, Haiku 4) "claude-sonnet-4-20250514": 200000, "claude-haiku-4-5-20251001": 200000, @@ -121,8 +125,11 @@ def sanitize_anthropic_model_metadata(value: Any) -> Any: "claude-opus-4-7": {"input": 5.00, "output": 25.00, "cached_input": 0.50}, # Claude 4.6 (current Opus tier) "claude-opus-4-6": {"input": 5.00, "output": 25.00, "cached_input": 0.50}, - # Claude 4.5 (Opus tier pricing) - "claude-opus-4-5-20251101": {"input": 15.00, "output": 75.00, "cached_input": 1.50}, + # Claude 4.5 (current Opus tier — same rates as 4.6–4.8) + "claude-opus-4-5-20251101": {"input": 5.00, "output": 25.00, "cached_input": 0.50}, + # Claude Sonnet 4.6 / 4.5 (current Sonnet tier): $3 in / $15 out, cache read $0.30 + "claude-sonnet-4-6": {"input": 3.00, "output": 15.00, "cached_input": 0.30}, + "claude-sonnet-4-5": {"input": 3.00, "output": 15.00, "cached_input": 0.30}, # Claude 4 (Sonnet/Haiku tier pricing) "claude-sonnet-4-20250514": {"input": 3.00, "output": 15.00, "cached_input": 0.30}, "claude-haiku-4-5-20251001": {"input": 0.80, "output": 4.00, "cached_input": 0.08}, diff --git a/tests/test_providers/test_anthropic.py b/tests/test_providers/test_anthropic.py index 0306644f7..f1eefdd3c 100644 --- a/tests/test_providers/test_anthropic.py +++ b/tests/test_providers/test_anthropic.py @@ -80,6 +80,10 @@ def test_get_context_limit_strips_ansi_model_suffix(self, anthropic_provider): def test_get_context_limit_claude_opus_4_8(self, anthropic_provider): assert anthropic_provider.get_context_limit("claude-opus-4-8") == 1000000 + def test_get_context_limit_claude_sonnet_4_6(self, anthropic_provider): + # Sonnet 4.6 ships with the 1M context window (long-context pricing tier). + assert anthropic_provider.get_context_limit("claude-sonnet-4-6") == 1000000 + def test_supports_model_known(self, anthropic_provider): assert anthropic_provider.supports_model("claude-3-5-sonnet-20241022") @@ -114,12 +118,27 @@ def test_pricing_lookup_strips_ansi_model_suffix(self, anthropic_provider): anthropic_provider._get_pricing("claude-opus-4-7") ) - @pytest.mark.parametrize("model", ["claude-opus-4-6", "claude-opus-4-7", "claude-opus-4-8"]) + @pytest.mark.parametrize( + "model", + ["claude-opus-4-5-20251101", "claude-opus-4-6", "claude-opus-4-7", "claude-opus-4-8"], + ) def test_current_opus_tier_pricing(self, anthropic_provider, model): - # Opus 4.6–4.8 share the same current-tier rates per anthropic.com/pricing + # Opus 4.5–4.8 share the same current-tier rates per anthropic.com/pricing # (verified 2026-06-27): $5/MTok input, $25/MTok output, cache read = 0.1x input ($0.50). assert anthropic_provider._get_pricing(model) == { "input": 5.00, "output": 25.00, "cached_input": 0.50, } + + @pytest.mark.parametrize( + "model", ["claude-sonnet-4-5", "claude-sonnet-4-6", "claude-sonnet-4-20250514"] + ) + def test_current_sonnet_tier_pricing(self, anthropic_provider, model): + # Sonnet 4 / 4.5 / 4.6 all sit at the current Sonnet tier per anthropic.com/pricing + # (verified 2026-06-27): $3/MTok input, $15/MTok output, cache read = 0.1x input ($0.30). + assert anthropic_provider._get_pricing(model) == { + "input": 3.00, + "output": 15.00, + "cached_input": 0.30, + } From 37d933dad15a3f9b7f7385c5fb11863e0fc2c488 Mon Sep 17 00:00:00 2001 From: Kenneth Wong Date: Sat, 27 Jun 2026 13:58:05 +0800 Subject: [PATCH 4/4] fix(anthropic): correct Haiku 4.5 fallback pricing to current rates claude-haiku-4-5-20251001 was carrying Haiku 3.5 rates ($0.80/$4/$0.08); the current anthropic.com/pricing page lists Haiku 4.5 at $1/MTok input, $5/MTok output, cache read $0.10 (0.1x input). Adds a Haiku tier test. Refs #1485 --- headroom/providers/anthropic.py | 2 +- tests/test_providers/test_anthropic.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/headroom/providers/anthropic.py b/headroom/providers/anthropic.py index 00ee244c4..20efd0fda 100644 --- a/headroom/providers/anthropic.py +++ b/headroom/providers/anthropic.py @@ -132,7 +132,7 @@ def sanitize_anthropic_model_metadata(value: Any) -> Any: "claude-sonnet-4-5": {"input": 3.00, "output": 15.00, "cached_input": 0.30}, # Claude 4 (Sonnet/Haiku tier pricing) "claude-sonnet-4-20250514": {"input": 3.00, "output": 15.00, "cached_input": 0.30}, - "claude-haiku-4-5-20251001": {"input": 0.80, "output": 4.00, "cached_input": 0.08}, + "claude-haiku-4-5-20251001": {"input": 1.00, "output": 5.00, "cached_input": 0.10}, # Claude 3.5 "claude-3-5-sonnet-20241022": {"input": 3.00, "output": 15.00, "cached_input": 0.30}, "claude-3-5-sonnet-latest": {"input": 3.00, "output": 15.00, "cached_input": 0.30}, diff --git a/tests/test_providers/test_anthropic.py b/tests/test_providers/test_anthropic.py index f1eefdd3c..2402048bc 100644 --- a/tests/test_providers/test_anthropic.py +++ b/tests/test_providers/test_anthropic.py @@ -142,3 +142,13 @@ def test_current_sonnet_tier_pricing(self, anthropic_provider, model): "output": 15.00, "cached_input": 0.30, } + + def test_current_haiku_tier_pricing(self, anthropic_provider): + # Haiku 4.5 (current Haiku tier, verified 2026-06-27): $1/MTok input, + # $5/MTok output, cache read = 0.1x input ($0.10). Was previously stored + # with Haiku 3.5 rates ($0.80/$4/$0.08). + assert anthropic_provider._get_pricing("claude-haiku-4-5-20251001") == { + "input": 1.00, + "output": 5.00, + "cached_input": 0.10, + }