mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-05 08:07:05 +00:00
Add e2e tests for checking if all beta headers in the mapping works
This commit is contained in:
parent
5075b8e2ee
commit
f2c7156512
4 changed files with 191 additions and 11 deletions
|
|
@ -3600,6 +3600,7 @@ jobs:
|
|||
-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
|
||||
-e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
|
||||
-e AWS_REGION_NAME="us-east-1" \
|
||||
-e LITELLM_LOCAL_ANTHROPIC_BETA_HEADERS="True" \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name my-app \
|
||||
-v $(pwd)/tests/proxy_e2e_anthropic_messages_tests/test_config.yaml:/app/config.yaml \
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
"description": "Mapping of Anthropic beta headers for each provider. Keys are input header names, values are provider-specific header names (or null if unsupported). Only headers present in mapping keys with non-null values can be forwarded.",
|
||||
"anthropic": {
|
||||
"advanced-tool-use-2025-11-20": "advanced-tool-use-2025-11-20",
|
||||
"bash_20241022": "bash_20241022",
|
||||
"bash_20250124": "bash_20250124",
|
||||
"bash_20241022": null,
|
||||
"bash_20250124": null,
|
||||
"code-execution-2025-08-25": "code-execution-2025-08-25",
|
||||
"compact-2026-01-12": "compact-2026-01-12",
|
||||
"computer-use-2025-01-24": "computer-use-2025-01-24",
|
||||
|
|
@ -13,26 +13,26 @@
|
|||
"effort-2025-11-24": "effort-2025-11-24",
|
||||
"fast-mode-2026-02-01": "fast-mode-2026-02-01",
|
||||
"files-api-2025-04-14": "files-api-2025-04-14",
|
||||
"structured-output-2024-03-01": "structured-output-2024-03-01",
|
||||
"structured-output-2024-03-01": null,
|
||||
"fine-grained-tool-streaming-2025-05-14": "fine-grained-tool-streaming-2025-05-14",
|
||||
"interleaved-thinking-2025-05-14": "interleaved-thinking-2025-05-14",
|
||||
"mcp-client-2025-11-20": "mcp-client-2025-11-20",
|
||||
"mcp-client-2025-04-04": "mcp-client-2025-04-04",
|
||||
"mcp-servers-2025-12-04": "mcp-servers-2025-12-04",
|
||||
"mcp-servers-2025-12-04": null,
|
||||
"output-128k-2025-02-19": "output-128k-2025-02-19",
|
||||
"prompt-caching-scope-2026-01-05": "prompt-caching-scope-2026-01-05",
|
||||
"skills-2025-10-02": "skills-2025-10-02",
|
||||
"structured-outputs-2025-11-13": "structured-outputs-2025-11-13",
|
||||
"text_editor_20241022": "text_editor_20241022",
|
||||
"text_editor_20250124": "text_editor_20250124",
|
||||
"text_editor_20241022": null,
|
||||
"text_editor_20250124": null,
|
||||
"token-efficient-tools-2025-02-19": "token-efficient-tools-2025-02-19",
|
||||
"web-fetch-2025-09-10": "web-fetch-2025-09-10",
|
||||
"web-search-2025-03-05": "web-search-2025-03-05"
|
||||
},
|
||||
"azure_ai": {
|
||||
"advanced-tool-use-2025-11-20": "advanced-tool-use-2025-11-20",
|
||||
"bash_20241022": "bash_20241022",
|
||||
"bash_20250124": "bash_20250124",
|
||||
"bash_20241022": null,
|
||||
"bash_20250124": null,
|
||||
"code-execution-2025-08-25": "code-execution-2025-08-25",
|
||||
"compact-2026-01-12": null,
|
||||
"computer-use-2025-01-24": "computer-use-2025-01-24",
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
"interleaved-thinking-2025-05-14": "interleaved-thinking-2025-05-14",
|
||||
"mcp-client-2025-11-20": "mcp-client-2025-11-20",
|
||||
"mcp-client-2025-04-04": "mcp-client-2025-04-04",
|
||||
"mcp-servers-2025-12-04": "mcp-servers-2025-12-04",
|
||||
"mcp-servers-2025-12-04": null,
|
||||
"output-128k-2025-02-19": null,
|
||||
"structured-output-2024-03-01": null,
|
||||
"prompt-caching-scope-2026-01-05": "prompt-caching-scope-2026-01-05",
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
"web-search-2025-03-05": "web-search-2025-03-05"
|
||||
},
|
||||
"bedrock_converse": {
|
||||
"advanced-tool-use-2025-11-20": "tool-search-tool-2025-10-19",
|
||||
"advanced-tool-use-2025-11-20": null,
|
||||
"bash_20241022": null,
|
||||
"bash_20250124": null,
|
||||
"code-execution-2025-08-25": null,
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
"text_editor_20241022": null,
|
||||
"text_editor_20250124": null,
|
||||
"token-efficient-tools-2025-02-19": null,
|
||||
"tool-search-tool-2025-10-19": "tool-search-tool-2025-10-19",
|
||||
"tool-search-tool-2025-10-19": null,
|
||||
"web-fetch-2025-09-10": null,
|
||||
"web-search-2025-03-05": null
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,169 @@
|
|||
"""
|
||||
This test ensures that the proxy can passthrough anthropic requests
|
||||
"""
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
import aiohttp
|
||||
import json
|
||||
|
||||
def get_all_supported_anthropic_beta_headers(provider: str):
|
||||
config_path = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "litellm"
|
||||
/ "anthropic_beta_headers_config.json"
|
||||
)
|
||||
|
||||
with open(config_path, "r") as f:
|
||||
config = json.load(f)
|
||||
|
||||
anthropic_mapping = config.get(provider, {})
|
||||
|
||||
# Only include headers that have a non-null mapping value
|
||||
return [
|
||||
header_name
|
||||
for header_name, provider_value in anthropic_mapping.items()
|
||||
if provider_value is not None
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.flaky(retries=3, delay=2)
|
||||
@pytest.mark.parametrize(
|
||||
"model_name,provider_name",
|
||||
[
|
||||
("claude-sonnet-4-5-20250929", "anthropic"),
|
||||
("azure-ai-claude-opus-4.5", "azure_ai"),
|
||||
("vertex-ai-claude-opus-4-6", "vertex_ai"),
|
||||
],
|
||||
)
|
||||
async def test_anthropic_messages_with_all_beta_headers(model_name, provider_name):
|
||||
"""
|
||||
Test that v1/messages endpoint works with all non-null Anthropic beta headers
|
||||
and doesn't throw errors
|
||||
"""
|
||||
print("Testing v1/messages with all non-null Anthropic beta headers")
|
||||
|
||||
|
||||
headers = {
|
||||
"Authorization": "Bearer sk-1234",
|
||||
"Content-Type": "application/json",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"anthropic-beta": ",".join(get_all_supported_anthropic_beta_headers(provider_name)),
|
||||
}
|
||||
|
||||
payload = {
|
||||
"model": model_name,
|
||||
"max_tokens": 10,
|
||||
"messages": [{"role": "user", "content": "Say 'hello' and nothing else"}],
|
||||
"tools": [{
|
||||
"type": "code_execution_20250825",
|
||||
"name": "code_execution"
|
||||
}]
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
"http://0.0.0.0:4000/v1/messages",
|
||||
json=payload,
|
||||
headers=headers
|
||||
) as response:
|
||||
response_text = await response.text()
|
||||
print(f"Response status: {response.status}")
|
||||
print(f"Response text: {response_text}")
|
||||
|
||||
# The request should succeed without errors
|
||||
assert response.status == 200, f"Request should succeed, got status {response.status}: {response_text}"
|
||||
|
||||
response_json = await response.json()
|
||||
print(f"Response JSON: {json.dumps(response_json, indent=4, default=str)}")
|
||||
|
||||
# Basic response validation
|
||||
assert "id" in response_json, "Response should have an id"
|
||||
assert "content" in response_json, "Response should have content"
|
||||
assert "model" in response_json, "Response should have model"
|
||||
assert "usage" in response_json, "Response should have usage"
|
||||
|
||||
# Verify usage information
|
||||
usage = response_json["usage"]
|
||||
assert "input_tokens" in usage, "Usage should have input_tokens"
|
||||
assert "output_tokens" in usage, "Usage should have output_tokens"
|
||||
assert usage["input_tokens"] > 0, "Should have some input tokens"
|
||||
assert usage["output_tokens"] > 0, "Should have some output tokens"
|
||||
|
||||
print(f"✅ Test passed: Request with all beta headers succeeded")
|
||||
print(f" Model: {response_json['model']}")
|
||||
print(f" Input tokens: {usage['input_tokens']}")
|
||||
print(f" Output tokens: {usage['output_tokens']}")
|
||||
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.flaky(retries=3, delay=2)
|
||||
@pytest.mark.parametrize(
|
||||
"model_name,provider_name",
|
||||
[
|
||||
("bedrock-claude-opus-4.5", "bedrock"),
|
||||
("bedrock-converse-claude-sonnet-4.5", "bedrock_converse")
|
||||
],
|
||||
)
|
||||
async def test_bedrock_invoke_messages_with_all_beta_headers(
|
||||
model_name, provider_name
|
||||
):
|
||||
"""
|
||||
Test that v1/messages endpoint works with all non-null Anthropic beta headers
|
||||
for both bedrock and bedrock_converse providers.
|
||||
"""
|
||||
print(f"Testing v1/messages for model={model_name}, provider={provider_name}")
|
||||
|
||||
beta_headers = get_all_supported_anthropic_beta_headers(provider_name)
|
||||
|
||||
headers = {
|
||||
"Authorization": "Bearer sk-1234",
|
||||
"Content-Type": "application/json",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"anthropic-beta": ",".join(beta_headers),
|
||||
}
|
||||
|
||||
payload = {
|
||||
"model": model_name,
|
||||
"max_tokens": 10,
|
||||
"messages": [
|
||||
{"role": "user", "content": "Say 'hello' and nothing else"}
|
||||
],
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
"http://0.0.0.0:4000/v1/messages",
|
||||
json=payload,
|
||||
headers=headers,
|
||||
) as response:
|
||||
|
||||
response_text = await response.text()
|
||||
print(f"Response status: {response.status}")
|
||||
print(f"Response text: {response_text}")
|
||||
|
||||
assert (
|
||||
response.status == 200
|
||||
), f"{provider_name} request failed: {response.status}: {response_text}"
|
||||
|
||||
response_json = await response.json()
|
||||
|
||||
# Basic response validation
|
||||
assert "id" in response_json
|
||||
assert "content" in response_json
|
||||
assert "model" in response_json
|
||||
assert "usage" in response_json
|
||||
|
||||
usage = response_json["usage"]
|
||||
|
||||
assert "input_tokens" in usage
|
||||
assert "output_tokens" in usage
|
||||
assert usage["input_tokens"] > 0
|
||||
assert usage["output_tokens"] > 0
|
||||
|
||||
print("✅ Test passed")
|
||||
print(f" Provider: {provider_name}")
|
||||
print(f" Model: {response_json['model']}")
|
||||
print(f" Input tokens: {usage['input_tokens']}")
|
||||
print(f" Output tokens: {usage['output_tokens']}")
|
||||
|
|
@ -29,3 +29,13 @@ model_list:
|
|||
litellm_params:
|
||||
model: "bedrock/converse/us.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
aws_region_name: "us-east-1"
|
||||
|
||||
# Vertex AI models
|
||||
- model_name: vertex-ai-claude-opus-4-6
|
||||
litellm_params:
|
||||
model: "vertex_ai/claude-opus-4-6"
|
||||
vertex_ai_project: "pathrise-convert-1606954137718"
|
||||
vertex_ai_location: "asia-southeast1"
|
||||
|
||||
general_settings:
|
||||
forward_client_headers_to_llm_api: true
|
||||
Loading…
Add table
Reference in a new issue