test(anthropic): fold deployment-row tests into the filtering file and mirror the new beta in the Rust table

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
mateo 2026-10-27 20:40:46 +00:00
parent a524926eba
commit 07c4d4d7f1
4 changed files with 145 additions and 221 deletions

View file

@ -98,6 +98,8 @@ pub enum AnthropicBeta {
McpServers20251204,
#[strum(serialize = "mid-conversation-output-config-2026-07-01")]
MidConversationOutputConfig20260701,
#[strum(serialize = "mid-conversation-system-clear-at-2026-08-21")]
MidConversationSystemClearAt20260821,
#[strum(serialize = "mid-conversation-tool-changes-2026-07-01")]
MidConversationToolChanges20260701,
#[strum(serialize = "output-128k-2025-02-19")]
@ -129,7 +131,7 @@ pub enum AnthropicBeta {
}
impl AnthropicBeta {
pub const KNOWN: [Self; 40] = [
pub const KNOWN: [Self; 41] = [
Self::Oauth20250420,
Self::WebFetch20250910,
Self::WebSearch20250305,
@ -157,6 +159,7 @@ impl AnthropicBeta {
Self::McpClient20251120,
Self::McpServers20251204,
Self::MidConversationOutputConfig20260701,
Self::MidConversationSystemClearAt20260821,
Self::MidConversationToolChanges20260701,
Self::Output128k20250219,
Self::PromptCachingScope20260105,
@ -257,10 +260,14 @@ impl AnthropicBeta {
}
Self::MidConversationOutputConfig20260701
| Self::MidConversationToolChanges20260701 => &[
BetaProvider::AzureAi,
BetaProvider::BedrockConverse,
BetaProvider::BedrockMantle,
BetaProvider::VertexAi,
BetaProvider::Databricks,
],
Self::MidConversationSystemClearAt20260821 => &[
BetaProvider::BedrockConverse,
BetaProvider::Bedrock,
BetaProvider::BedrockMantle,
BetaProvider::Databricks,
],
Self::Output128k20250219 | Self::TokenEfficientTools20250219 => &[
@ -429,6 +436,7 @@ mod tests {
AnthropicBeta::McpClient20251120,
AnthropicBeta::McpServers20251204,
AnthropicBeta::MidConversationOutputConfig20260701,
AnthropicBeta::MidConversationSystemClearAt20260821,
AnthropicBeta::MidConversationToolChanges20260701,
AnthropicBeta::Output128k20250219,
AnthropicBeta::PromptCachingScope20260105,

View file

@ -5726,8 +5726,8 @@ async def test_async_anthropic_messages_handler_filters_betas_by_the_azure_ai_ro
monkeypatch.setenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", "True")
anthropic_beta_headers_manager._BETA_HEADERS_CONFIG = None
foundry_api_base = "https://my-resource.services.ai.azure.com/anthropic"
upstream_response = httpx.Response(
foundry_api_base: Final = "https://my-resource.services.ai.azure.com/anthropic"
upstream_response: Final = httpx.Response(
200,
json={
"id": "msg_123",
@ -5740,9 +5740,9 @@ async def test_async_anthropic_messages_handler_filters_betas_by_the_azure_ai_ro
},
request=httpx.Request("POST", f"{foundry_api_base}/v1/messages"),
)
mock_client = AsyncMock(spec=AsyncHTTPHandler)
mock_client: Final = AsyncMock(spec=AsyncHTTPHandler)
mock_client.post = AsyncMock(return_value=upstream_response)
mock_logging_obj = Mock()
mock_logging_obj: Final = Mock()
mock_logging_obj.model_call_details = {}
mock_logging_obj.stream = False
mock_logging_obj.dynamic_success_callbacks = None
@ -5761,5 +5761,5 @@ async def test_async_anthropic_messages_handler_filters_betas_by_the_azure_ai_ro
kwargs={"extra_headers": {"anthropic-beta": "advisor-tool-2026-03-01,thinking-binding-controls-2026-08-01"}},
)
sent_headers = mock_client.post.call_args.kwargs["headers"]
sent_headers: Final = mock_client.post.call_args.kwargs["headers"]
assert sent_headers["anthropic-beta"] == "thinking-binding-controls-2026-08-01"

View file

@ -1,115 +0,0 @@
"""The beta allowlist row is picked per deployment: an ``anthropic`` deployment on a Foundry host gets ``azure_ai``."""
import json
import os
from typing import Final
import pytest
import litellm
from litellm import anthropic_beta_headers_manager
from litellm.anthropic_beta_headers_manager import (
is_foundry_api_base,
resolve_beta_provider,
update_headers_with_filtered_beta,
update_request_with_filtered_beta,
)
FOUNDRY_API_BASE: Final = "https://my-resource.services.ai.azure.com/anthropic"
DIRECT_API_BASE: Final = "https://api.anthropic.com"
DIRECT_ONLY_BETA: Final = "advisor-tool-2026-03-01"
PRESERVED_THINKING_BETAS: Final = (
"thinking-binding-controls-2026-08-01",
"mid-conversation-system-clear-at-2026-08-21",
"mid-conversation-tool-changes-2026-07-01",
"mid-conversation-output-config-2026-07-01",
"thinking-display-updates-2026-08-18",
)
@pytest.fixture(autouse=True)
def _bundled_config(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", "True")
anthropic_beta_headers_manager._BETA_HEADERS_CONFIG = None
def _bundled_rows() -> dict[str, dict[str, str | None]]:
with open(os.path.join(os.path.dirname(litellm.__file__), "anthropic_beta_headers_config.json")) as f:
rows: dict[str, dict[str, str | None]] = json.load(f)
return rows
@pytest.mark.parametrize(
("api_base", "expected"),
(
(FOUNDRY_API_BASE, True),
("https://my-resource.openai.azure.com", True),
("my-resource.services.ai.azure.com/anthropic", True),
(DIRECT_API_BASE, False),
("https://services.ai.azure.com.example.com", False),
("", False),
(None, False),
),
)
def test_is_foundry_api_base_matches_only_foundry_hosts(api_base: str | None, expected: bool) -> None:
assert is_foundry_api_base(api_base) is expected
@pytest.mark.parametrize(
("provider", "api_base", "expected"),
(
("anthropic", FOUNDRY_API_BASE, "azure_ai"),
("anthropic", DIRECT_API_BASE, "anthropic"),
("anthropic", None, "anthropic"),
("vertex_ai", FOUNDRY_API_BASE, "vertex_ai"),
("bedrock_converse", None, "bedrock_converse"),
),
)
def test_resolve_beta_provider_picks_the_row_per_deployment(provider: str, api_base: str | None, expected: str) -> None:
assert resolve_beta_provider(provider, api_base) == expected
def test_foundry_deployment_headers_follow_the_azure_ai_row() -> None:
headers: Final = {"anthropic-beta": f"{DIRECT_ONLY_BETA},thinking-binding-controls-2026-08-01"}
result: dict[str, str] = update_headers_with_filtered_beta(
headers=headers, provider="anthropic", api_base=FOUNDRY_API_BASE
)
assert result == {"anthropic-beta": "thinking-binding-controls-2026-08-01"}
def test_direct_deployment_headers_keep_every_anthropic_row_beta() -> None:
headers: Final = {"anthropic-beta": f"{DIRECT_ONLY_BETA},thinking-binding-controls-2026-08-01"}
result: dict[str, str] = update_headers_with_filtered_beta(
headers=headers, provider="anthropic", api_base=DIRECT_API_BASE
)
assert result == {"anthropic-beta": f"{DIRECT_ONLY_BETA},thinking-binding-controls-2026-08-01"}
def test_foundry_deployment_request_filters_header_and_body_betas_together() -> None:
headers: dict[str, str]
data: dict[str, object]
headers, data = update_request_with_filtered_beta(
headers={"anthropic-beta": DIRECT_ONLY_BETA},
request_data={"anthropic_beta": [DIRECT_ONLY_BETA, "thinking-binding-controls-2026-08-01"], "max_tokens": 8},
provider="anthropic",
api_base=FOUNDRY_API_BASE,
)
assert headers == {}
assert data == {"anthropic_beta": ["thinking-binding-controls-2026-08-01"], "max_tokens": 8}
@pytest.mark.parametrize("provider", ("anthropic", "azure_ai", "vertex_ai"))
@pytest.mark.parametrize("beta", PRESERVED_THINKING_BETAS)
def test_live_verified_platforms_forward_every_preserved_thinking_beta(provider: str, beta: str) -> None:
assert _bundled_rows()[provider][beta] == beta
@pytest.mark.parametrize("provider", ("bedrock", "bedrock_converse", "bedrock_mantle", "databricks"))
def test_unverified_platforms_hold_back_the_system_clear_beta(provider: str) -> None:
assert _bundled_rows()[provider]["mid-conversation-system-clear-at-2026-08-21"] is None
assert (
update_headers_with_filtered_beta(
headers={"anthropic-beta": "mid-conversation-system-clear-at-2026-08-21"}, provider=provider
)
== {}
)

View file

@ -16,7 +16,10 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
import litellm
from litellm import anthropic_beta_headers_manager
from litellm.anthropic_beta_headers_manager import (
resolve_beta_provider,
is_foundry_api_base,
GetAnthropicBetaHeadersConfig,
filter_and_transform_beta_headers,
get_beta_headers_config,
@ -53,9 +56,7 @@ class TestAnthropicBetaHeadersFiltering:
def get_supported_headers(self, provider: str) -> List[str]:
"""Get headers with non-null values for a provider."""
provider_config = self.config.get(provider, {})
return [
header for header, value in provider_config.items() if value is not None
]
return [header for header, value in provider_config.items() if value is not None]
def get_unsupported_headers(self, provider: str) -> List[str]:
"""Get headers with null values for a provider."""
@ -65,11 +66,7 @@ class TestAnthropicBetaHeadersFiltering:
def get_mapped_headers(self, provider: str) -> Dict[str, str]:
"""Get mapping of input headers to provider-specific headers."""
provider_config = self.config.get(provider, {})
return {
header: value
for header, value in provider_config.items()
if value is not None
}
return {header: value for header, value in provider_config.items() if value is not None}
@pytest.mark.parametrize(
"provider",
@ -82,24 +79,20 @@ class TestAnthropicBetaHeadersFiltering:
unsupported_headers = self.get_unsupported_headers(provider)
mapped_headers = self.get_mapped_headers(provider)
filtered = filter_and_transform_beta_headers(
beta_headers=all_headers, provider=provider
)
filtered = filter_and_transform_beta_headers(beta_headers=all_headers, provider=provider)
assert ("compact-2026-09-04" in filtered) is (provider == "anthropic")
for header in unsupported_headers:
assert (
header not in filtered
), f"Unsupported header '{header}' should be filtered out for {provider}"
assert (
mapped_headers.get(header) not in filtered
), f"Mapped value of unsupported header '{header}' should not appear for {provider}"
assert header not in filtered, f"Unsupported header '{header}' should be filtered out for {provider}"
assert mapped_headers.get(header) not in filtered, (
f"Mapped value of unsupported header '{header}' should not appear for {provider}"
)
for header in supported_headers:
expected_mapped = mapped_headers[header]
assert (
expected_mapped in filtered
), f"Supported header '{header}' should be mapped to '{expected_mapped}' for {provider}"
assert expected_mapped in filtered, (
f"Supported header '{header}' should be mapped to '{expected_mapped}' for {provider}"
)
@pytest.mark.parametrize(
"provider",
@ -114,20 +107,14 @@ class TestAnthropicBetaHeadersFiltering:
]
all_headers = self.get_all_beta_headers() + unknown_headers
filtered = filter_and_transform_beta_headers(
beta_headers=all_headers, provider=provider
)
filtered = filter_and_transform_beta_headers(beta_headers=all_headers, provider=provider)
for unknown in unknown_headers:
assert (
unknown not in filtered
), f"Unknown header '{unknown}' should be filtered out for {provider}"
assert unknown not in filtered, f"Unknown header '{unknown}' should be filtered out for {provider}"
def test_update_request_with_filtered_beta_vertex_ai(self):
"""Test combined filtering for both HTTP headers and request body betas."""
headers = {
"anthropic-beta": "files-api-2025-04-14,context-management-2025-06-27,code-execution-2025-05-22"
}
headers = {"anthropic-beta": "files-api-2025-04-14,context-management-2025-06-27,code-execution-2025-05-22"}
request_data = {
"anthropic_beta": [
"files-api-2025-04-14",
@ -143,9 +130,7 @@ class TestAnthropicBetaHeadersFiltering:
)
assert filtered_headers.get("anthropic-beta") == "context-management-2025-06-27"
assert filtered_request_data.get("anthropic_beta") == [
"context-management-2025-06-27"
]
assert filtered_request_data.get("anthropic_beta") == ["context-management-2025-06-27"]
@pytest.mark.asyncio
async def test_anthropic_messages_http_headers_filtering(self):
@ -153,9 +138,7 @@ class TestAnthropicBetaHeadersFiltering:
all_headers = self.get_all_beta_headers()
unsupported = self.get_unsupported_headers("anthropic")
with patch(
"litellm.llms.custom_httpx.http_handler.get_async_httpx_client"
) as mock_client_factory:
with patch("litellm.llms.custom_httpx.http_handler.get_async_httpx_client") as mock_client_factory:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
@ -191,9 +174,9 @@ class TestAnthropicBetaHeadersFiltering:
if beta_header:
beta_values = [b.strip() for b in beta_header.split(",")]
for unsupported_header in unsupported:
assert (
unsupported_header not in beta_values
), f"Unsupported header '{unsupported_header}' should not be in HTTP headers for Anthropic"
assert unsupported_header not in beta_values, (
f"Unsupported header '{unsupported_header}' should not be in HTTP headers for Anthropic"
)
@pytest.mark.asyncio
async def test_azure_ai_messages_http_headers_filtering(self):
@ -201,9 +184,7 @@ class TestAnthropicBetaHeadersFiltering:
all_headers = self.get_all_beta_headers()
unsupported = self.get_unsupported_headers("azure_ai")
with patch(
"litellm.llms.custom_httpx.http_handler.get_async_httpx_client"
) as mock_client_factory:
with patch("litellm.llms.custom_httpx.http_handler.get_async_httpx_client") as mock_client_factory:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
@ -241,9 +222,9 @@ class TestAnthropicBetaHeadersFiltering:
if beta_header:
beta_values = [b.strip() for b in beta_header.split(",")]
for unsupported_header in unsupported:
assert (
unsupported_header not in beta_values
), f"Unsupported header '{unsupported_header}' should not be in HTTP headers for Azure AI"
assert unsupported_header not in beta_values, (
f"Unsupported header '{unsupported_header}' should not be in HTTP headers for Azure AI"
)
@pytest.mark.asyncio
async def test_bedrock_converse_headers_and_body_filtering(self):
@ -256,9 +237,7 @@ class TestAnthropicBetaHeadersFiltering:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"output": {
"message": {"role": "assistant", "content": [{"text": "Hello"}]}
},
"output": {"message": {"role": "assistant", "content": [{"text": "Hello"}]}},
"stopReason": "end_turn",
"usage": {"inputTokens": 10, "outputTokens": 20},
}
@ -290,27 +269,25 @@ class TestAnthropicBetaHeadersFiltering:
if beta_header:
beta_values = [b.strip() for b in beta_header.split(",")]
for unsupported_header in unsupported:
assert (
unsupported_header not in beta_values
), f"Unsupported header '{unsupported_header}' should not be in HTTP headers for Bedrock Converse"
assert unsupported_header not in beta_values, (
f"Unsupported header '{unsupported_header}' should not be in HTTP headers for Bedrock Converse"
)
data = call_kwargs.get("data")
if data:
body = json.loads(data)
body_beta = body.get("additionalModelRequestFields", {}).get(
"anthropic_beta", []
)
body_beta = body.get("additionalModelRequestFields", {}).get("anthropic_beta", [])
for unsupported_header in unsupported:
assert (
unsupported_header not in body_beta
), f"Unsupported header '{unsupported_header}' should not be in request body for Bedrock Converse"
assert unsupported_header not in body_beta, (
f"Unsupported header '{unsupported_header}' should not be in request body for Bedrock Converse"
)
for header, mapped_value in mapped_headers.items():
if header in all_headers and mapped_value in body_beta:
assert (
mapped_value in body_beta
), f"Supported header '{header}' should be mapped to '{mapped_value}' in request body for Bedrock Converse"
assert mapped_value in body_beta, (
f"Supported header '{header}' should be mapped to '{mapped_value}' in request body for Bedrock Converse"
)
@pytest.mark.asyncio
async def test_vertex_ai_messages_http_headers_filtering(self):
@ -318,9 +295,7 @@ class TestAnthropicBetaHeadersFiltering:
all_headers = self.get_all_beta_headers()
unsupported = self.get_unsupported_headers("vertex_ai")
with patch(
"litellm.llms.custom_httpx.http_handler.get_async_httpx_client"
) as mock_client_factory:
with patch("litellm.llms.custom_httpx.http_handler.get_async_httpx_client") as mock_client_factory:
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
@ -338,9 +313,7 @@ class TestAnthropicBetaHeadersFiltering:
mock_client.post = AsyncMock(return_value=mock_response)
mock_client_factory.return_value = mock_client
with patch(
"litellm.llms.vertex_ai.vertex_llm_base.VertexBase._ensure_access_token"
) as mock_token:
with patch("litellm.llms.vertex_ai.vertex_llm_base.VertexBase._ensure_access_token") as mock_token:
mock_token.return_value = ("test-token", "test-project")
try:
@ -363,9 +336,9 @@ class TestAnthropicBetaHeadersFiltering:
if beta_header:
beta_values = [b.strip() for b in beta_header.split(",")]
for unsupported_header in unsupported:
assert (
unsupported_header not in beta_values
), f"Unsupported header '{unsupported_header}' should not be in HTTP headers for Vertex AI"
assert unsupported_header not in beta_values, (
f"Unsupported header '{unsupported_header}' should not be in HTTP headers for Vertex AI"
)
def test_header_mapping_correctness(self):
"""Test that headers are mapped correctly for providers with transformations."""
@ -402,17 +375,15 @@ class TestAnthropicBetaHeadersFiltering:
beta_headers=[test_case["input"]], provider=test_case["provider"]
)
assert (
test_case["expected"] in filtered
), f"Header '{test_case['input']}' should be mapped to '{test_case['expected']}' for {test_case['provider']}, but got: {filtered}"
assert test_case["expected"] in filtered, (
f"Header '{test_case['input']}' should be mapped to '{test_case['expected']}' for {test_case['provider']}, but got: {filtered}"
)
def test_filter_and_transform_beta_headers_vertex_ai_keeps_compact(self):
"""Vertex AI supports compact context edits, so the compact beta header
must be forwarded instead of stripped (it was previously mapped to null,
which broke compact_20260112 context edits over /v1/messages)."""
filtered = filter_and_transform_beta_headers(
beta_headers=["compact-2026-01-12"], provider="vertex_ai"
)
filtered = filter_and_transform_beta_headers(beta_headers=["compact-2026-01-12"], provider="vertex_ai")
assert filtered == ["compact-2026-01-12"]
@ -486,13 +457,11 @@ class TestAnthropicBetaHeadersFiltering:
unsupported = self.get_unsupported_headers(provider)
if unsupported:
filtered = filter_and_transform_beta_headers(
beta_headers=unsupported, provider=provider
)
filtered = filter_and_transform_beta_headers(beta_headers=unsupported, provider=provider)
assert (
len(filtered) == 0
), f"All null-value headers should be filtered out for {provider}, but got: {filtered}"
assert len(filtered) == 0, (
f"All null-value headers should be filtered out for {provider}, but got: {filtered}"
)
def test_empty_headers_list(self):
"""Test that empty headers list returns empty result."""
@ -503,13 +472,9 @@ class TestAnthropicBetaHeadersFiltering:
"bedrock",
"vertex_ai",
]:
filtered = filter_and_transform_beta_headers(
beta_headers=[], provider=provider
)
filtered = filter_and_transform_beta_headers(beta_headers=[], provider=provider)
assert (
len(filtered) == 0
), f"Empty headers list should return empty result for {provider}"
assert len(filtered) == 0, f"Empty headers list should return empty result for {provider}"
def test_mixed_supported_and_unsupported_headers(self):
"""Test filtering with a mix of supported, unsupported, and unknown headers."""
@ -529,20 +494,12 @@ class TestAnthropicBetaHeadersFiltering:
test_headers = [supported[0]] + [unsupported[0]] + ["unknown-header-123"]
filtered = filter_and_transform_beta_headers(
beta_headers=test_headers, provider=provider
)
filtered = filter_and_transform_beta_headers(beta_headers=test_headers, provider=provider)
expected_mapped = mapped_headers[supported[0]]
assert (
expected_mapped in filtered
), f"Supported header should be in result for {provider}"
assert (
unsupported[0] not in filtered
), f"Unsupported header should not be in result for {provider}"
assert (
"unknown-header-123" not in filtered
), f"Unknown header should not be in result for {provider}"
assert expected_mapped in filtered, f"Supported header should be in result for {provider}"
assert unsupported[0] not in filtered, f"Unsupported header should not be in result for {provider}"
assert "unknown-header-123" not in filtered, f"Unknown header should not be in result for {provider}"
@pytest.mark.parametrize("provider", ["anthropic", "bedrock", "bedrock_mantle", "vertex_ai"])
def test_blank_anthropic_beta_header_is_removed(self, provider):
@ -570,3 +527,77 @@ def test_local_flags_load_bundled_beta_headers_without_fetching(monkeypatch, fla
config = get_beta_headers_config(url="https://example.test/anthropic_beta_headers_config.json")
remote_get.assert_not_called()
assert config == GetAnthropicBetaHeadersConfig.load_local_beta_headers_config()
FOUNDRY_API_BASE: Final = "https://my-resource.services.ai.azure.com/anthropic"
DIRECT_API_BASE: Final = "https://api.anthropic.com"
DIRECT_ONLY_BETA: Final = "advisor-tool-2026-03-01"
PRESERVED_THINKING_BETAS: Final = (
"thinking-binding-controls-2026-08-01",
"mid-conversation-system-clear-at-2026-08-21",
"mid-conversation-tool-changes-2026-07-01",
"mid-conversation-output-config-2026-07-01",
"thinking-display-updates-2026-08-18",
)
@pytest.fixture
def bundled_config(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS", "True")
anthropic_beta_headers_manager._BETA_HEADERS_CONFIG = None
def test_foundry_deployment_headers_follow_the_azure_ai_row(bundled_config: None) -> None:
headers: Final = {"anthropic-beta": f"{DIRECT_ONLY_BETA},thinking-binding-controls-2026-08-01"}
result: dict[str, str] = update_headers_with_filtered_beta(
headers=headers, provider="anthropic", api_base=FOUNDRY_API_BASE
)
assert result == {"anthropic-beta": "thinking-binding-controls-2026-08-01"}
def test_direct_deployment_headers_keep_every_anthropic_row_beta(bundled_config: None) -> None:
headers: Final = {"anthropic-beta": f"{DIRECT_ONLY_BETA},thinking-binding-controls-2026-08-01"}
result: dict[str, str] = update_headers_with_filtered_beta(
headers=headers, provider="anthropic", api_base=DIRECT_API_BASE
)
assert result == {"anthropic-beta": f"{DIRECT_ONLY_BETA},thinking-binding-controls-2026-08-01"}
def test_foundry_deployment_request_filters_header_and_body_betas_together(bundled_config: None) -> None:
headers: dict[str, str]
data: dict[str, object]
headers, data = update_request_with_filtered_beta(
headers={"anthropic-beta": DIRECT_ONLY_BETA},
request_data={"anthropic_beta": [DIRECT_ONLY_BETA, "thinking-binding-controls-2026-08-01"], "max_tokens": 8},
provider="anthropic",
api_base=FOUNDRY_API_BASE,
)
assert headers == {}
assert data == {"anthropic_beta": ["thinking-binding-controls-2026-08-01"], "max_tokens": 8}
PRESERVED_THINKING_BETAS: Final = (
"thinking-binding-controls-2026-08-01",
"mid-conversation-system-clear-at-2026-08-21",
"mid-conversation-tool-changes-2026-07-01",
"mid-conversation-output-config-2026-07-01",
"thinking-display-updates-2026-08-18",
)
@pytest.mark.parametrize("provider", ["anthropic", "azure_ai", "vertex_ai"])
def test_live_verified_platforms_forward_every_preserved_thinking_beta(bundled_config: None, provider: str) -> None:
result: dict[str, str] = update_headers_with_filtered_beta(
headers={"anthropic-beta": ",".join(PRESERVED_THINKING_BETAS)}, provider=provider
)
assert sorted(result["anthropic-beta"].split(",")) == sorted(PRESERVED_THINKING_BETAS), result
@pytest.mark.parametrize("provider", ["anthropic", "azure_ai", "vertex_ai", "bedrock", "bedrock_converse"])
def test_each_row_forwards_exactly_its_verified_preserved_thinking_cells(bundled_config: None, provider: str) -> None:
row: Final[dict[str, str | None]] = GetAnthropicBetaHeadersConfig.load_local_beta_headers_config()[provider]
forwarded: Final = tuple(beta for beta in PRESERVED_THINKING_BETAS if row.get(beta) is not None)
result: dict[str, str] = update_headers_with_filtered_beta(
headers={"anthropic-beta": ",".join(PRESERVED_THINKING_BETAS)}, provider=provider
)
assert sorted(result["anthropic-beta"].split(",")) == sorted(forwarded), (provider, result)