mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-21 00:24:55 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user