[Fix] Claude Code (/messages) - Litellm fix claude code Bedrock Invoke usage, request signing (#19111)

* test_should_not_fail_with_forwarded_headers_bedrock_invoke_messages

* use common get_request_headers for BaseAWS

* fix get_request_headers

* test_should_not_fail_with_forwarded_headers_bedrock_invoke_messages
This commit is contained in:
Ishaan Jaff
2026-01-14 14:51:50 -08:00
committed by GitHub
parent dcac090de0
commit 06ded8750e
3 changed files with 56 additions and 15 deletions
+7 -2
View File
@@ -87,7 +87,7 @@ class BaseAWSLLM:
"""
import litellm
from litellm.secret_managers.main import str_to_bool
# Check environment variable first (highest priority)
ssl_verify = os.getenv("SSL_VERIFY", litellm.ssl_verify)
@@ -1232,15 +1232,20 @@ class BaseAWSLLM:
else:
headers = {"Content-Type": "application/json"}
aws_signature_headers = self._filter_headers_for_aws_signature(headers)
request = AWSRequest(
method="POST",
url=api_base,
data=json.dumps(request_data),
headers=headers,
headers=aws_signature_headers,
)
sigv4.add_auth(request)
request_headers_dict = dict(request.headers)
# Add back original headers after signing. Only headers in SignedHeaders
# are integrity-protected; forwarded headers (x-forwarded-*) must remain unsigned.
for header_name, header_value in headers.items():
request_headers_dict[header_name] = header_value
if (
headers is not None and "Authorization" in headers
): # prevent sigv4 from overwriting the auth header
+7 -12
View File
@@ -729,8 +729,6 @@ class BedrockLLM(BaseAWSLLM):
client: Optional[Union[AsyncHTTPHandler, HTTPHandler]] = None,
) -> Union[ModelResponse, CustomStreamWrapper]:
try:
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials
except ImportError:
raise ImportError("Missing boto3 to call bedrock. Run 'pip install boto3'.")
@@ -808,8 +806,6 @@ class BedrockLLM(BaseAWSLLM):
endpoint_url = f"{endpoint_url}/model/{modelId}/invoke"
proxy_endpoint_url = f"{proxy_endpoint_url}/model/{modelId}/invoke"
sigv4 = SigV4Auth(credentials, "bedrock", aws_region_name)
prompt, chat_history = self.convert_messages_to_prompt(
model, messages, provider, custom_prompt_dict
)
@@ -970,15 +966,14 @@ class BedrockLLM(BaseAWSLLM):
headers = {"Content-Type": "application/json"}
if extra_headers is not None:
headers = {"Content-Type": "application/json", **extra_headers}
request = AWSRequest(
method="POST", url=endpoint_url, data=data, headers=headers
prepped = self.get_request_headers(
credentials=credentials,
aws_region_name=aws_region_name,
extra_headers=extra_headers,
endpoint_url=endpoint_url,
data=data,
headers=headers,
)
sigv4.add_auth(request)
if (
extra_headers is not None and "Authorization" in extra_headers
): # prevent sigv4 from overwriting the auth header
request.headers["Authorization"] = extra_headers["Authorization"]
prepped = request.prepare()
## LOGGING
logging_obj.pre_call(
@@ -6,7 +6,7 @@ from datetime import datetime
from typing import AsyncIterator, Dict, Any
import asyncio
import unittest.mock
from unittest.mock import AsyncMock, MagicMock
from unittest.mock import MagicMock
import pytest
from litellm.router import Router
@@ -98,3 +98,44 @@ async def test_anthropic_messages_bedrock_converse_with_thinking():
# Verify response
INSTANCE_BASE_ANTHROPIC_MESSAGES_TEST._validate_response(response)
@pytest.mark.asyncio
async def test_should_not_fail_with_forwarded_headers_bedrock_invoke_messages():
"""
E2E test for Bedrock invoke messages with header forwarding enabled.
This calls the real Bedrock endpoint (no mocks) and should not raise
SigV4 signature mismatch errors when forwarded headers are present.
"""
router = Router(
model_list=[
{
"model_name": "claude-sonnet-4-5-20250929",
"litellm_params": {
"model": "bedrock/invoke/us.anthropic.claude-sonnet-4-5-20250929-v1:0",
"aws_region_name": os.getenv("AWS_REGION_NAME"),
},
}
]
)
forwarded_headers = {
"x-forwarded-for": "10.11.232.194",
"x-forwarded-port": "443",
"x-forwarded-proto": "https",
"x-app": "cli",
}
response = await router.aanthropic_messages(
messages=[{"role": "user", "content": "hi"}],
model="claude-sonnet-4-5-20250929",
max_tokens=5,
stream=False,
headers=forwarded_headers, # simulates forward_client_headers_to_llm_api
aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
aws_region_name=os.getenv("AWS_REGION_NAME"),
)
print("INVOKE API RESPONSE: ", response)
INSTANCE_BASE_ANTHROPIC_MESSAGES_TEST._validate_response(response)