litellm/tests/llm_translation/test_bedrock_mantle.py
ishaan-berri f9ba70d357
fix(bedrock-mantle): use /anthropic/v1/messages path for Mantle endpo… (#27976)
* fix(bedrock-mantle): use /anthropic/v1/messages path for Mantle endpoint (#27943)

* docs: add one-line docstring to _disable_debugging (#27894)

Squash-merged by litellm-agent from oss-agent-shin's PR.

* Add jp. Bedrock cross-region inference profile for claude-sonnet-4-6 (#27831)

Squash-merged by litellm-agent from Cyberfilo's PR.

* Sanitize empty text content blocks on /v1/messages (#27832)

Squash-merged by litellm-agent from Cyberfilo's PR.

* fix(bedrock-mantle): use /anthropic/v1/messages path for Mantle endpoint

The bedrock-mantle gateway (Claude Mythos Preview) serves the Anthropic
Messages API at /anthropic/v1/messages; /v1/messages returns 404 Not
Found. Both AmazonMantleConfig (chat/completions caller route) and
AmazonMantleMessagesConfig (anthropic-messages caller route) hardcoded
the wrong path, so every Mantle request 404'd before reaching the model.

Per the Anthropic docs: "[Claude in Amazon Bedrock] uses the Messages
API at /anthropic/v1/messages with SSE streaming."
https://platform.claude.com/docs/en/api/claude-on-amazon-bedrock

Confirmed independently against the live endpoint:
  /v1/chat/completions      -> 200 OK
  /v1/messages              -> 404 Not Found  (what litellm used)
  /anthropic/v1/messages    -> 200 OK         (Claude only)

Adds a regression test asserting both Mantle configs build the
/anthropic/v1/messages path, and updates the existing assertions that
encoded the wrong path.

---------

Co-authored-by: oss-agent-shin <ext-agent-shin@berri.ai>
Co-authored-by: Filippo Menghi <113345637+Cyberfilo@users.noreply.github.com>

* fix: sanitize empty text blocks in sync anthropic_messages_handler path

Co-authored-by: Yassin Kortam <yassin@berri.ai>

---------

Co-authored-by: João Costa <13508071+jpv-costa@users.noreply.github.com>
Co-authored-by: oss-agent-shin <ext-agent-shin@berri.ai>
Co-authored-by: Filippo Menghi <113345637+Cyberfilo@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Yassin Kortam <yassin@berri.ai>
2026-05-15 13:31:59 -07:00

149 lines
5.2 KiB
Python

"""
E2E tests for Bedrock Mantle (Claude Mythos Preview) integration.
Tests use a fake/mocked HTTP layer to verify the full request pipeline:
- correct endpoint URL
- model ID in the request body
- AWS SigV4 Authorization header present
- response parsing
"""
import json
import os
import sys
from unittest.mock import MagicMock, patch
import httpx
import pytest
sys.path.insert(0, os.path.abspath("../.."))
import litellm
from litellm.llms.custom_httpx.http_handler import HTTPHandler
MODEL = "bedrock/mantle/anthropic.claude-mythos-preview"
REGION = "us-east-1"
EXPECTED_URL = f"https://bedrock-mantle.{REGION}.api.aws/anthropic/v1/messages"
FAKE_ANTHROPIC_RESPONSE = {
"id": "msg_fake123",
"type": "message",
"role": "assistant",
"model": "anthropic.claude-mythos-preview",
"content": [{"type": "text", "text": "Hello from Mythos!"}],
"stop_reason": "end_turn",
"stop_sequence": None,
"usage": {"input_tokens": 10, "output_tokens": 5},
}
def _make_fake_response(body: dict) -> MagicMock:
mock_resp = MagicMock(spec=httpx.Response)
mock_resp.status_code = 200
mock_resp.headers = httpx.Headers({"content-type": "application/json"})
mock_resp.text = json.dumps(body)
mock_resp.json.return_value = body
mock_resp.is_error = False
mock_resp.raise_for_status = MagicMock()
return mock_resp
def test_mantle_request_url_and_body():
"""Verify the correct URL is called and model appears in the request body."""
client = HTTPHandler()
with patch.object(
client, "post", return_value=_make_fake_response(FAKE_ANTHROPIC_RESPONSE)
) as mock_post:
try:
litellm.completion(
model=MODEL,
messages=[{"role": "user", "content": "Hello"}],
max_tokens=50,
aws_region_name=REGION,
aws_access_key_id="AKIAIOSFODNN7EXAMPLE",
aws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
client=client,
)
except Exception:
pass # response parsing may fail on mock; we only care about the outgoing call
mock_post.assert_called_once()
call_kwargs = mock_post.call_args.kwargs
# Correct endpoint
assert (
call_kwargs["url"] == EXPECTED_URL
), f"Expected {EXPECTED_URL}, got {call_kwargs['url']}"
# Request body has model ID (without "mantle/" prefix)
raw_data = call_kwargs.get("data") or call_kwargs.get("json")
body = json.loads(raw_data) if isinstance(raw_data, (str, bytes)) else raw_data
assert (
body["model"] == "anthropic.claude-mythos-preview"
), f"body['model'] = {body.get('model')}"
assert "messages" in body
assert body["max_tokens"] == 50
# AWS SigV4 Authorization header must be present
headers = call_kwargs.get("headers", {})
assert "Authorization" in headers, f"No Authorization header in {headers}"
assert headers["Authorization"].startswith(
"AWS4-HMAC-SHA256"
), f"Expected SigV4 auth, got: {headers['Authorization'][:50]}"
def test_mantle_request_does_not_include_mantle_prefix_in_body():
"""Ensure 'mantle/' never leaks into the request body."""
client = HTTPHandler()
with patch.object(
client, "post", return_value=_make_fake_response(FAKE_ANTHROPIC_RESPONSE)
) as mock_post:
try:
litellm.completion(
model=MODEL,
messages=[{"role": "user", "content": "Hi"}],
max_tokens=10,
aws_region_name=REGION,
aws_access_key_id="AKIAIOSFODNN7EXAMPLE",
aws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
client=client,
)
except Exception:
pass
call_kwargs = mock_post.call_args.kwargs
raw_data = call_kwargs.get("data") or call_kwargs.get("json")
body = json.loads(raw_data) if isinstance(raw_data, (str, bytes)) else raw_data
body_str = json.dumps(body)
assert "mantle/" not in body_str, f"'mantle/' leaked into body: {body_str}"
def test_mantle_region_reflected_in_url():
"""The region from aws_region_name must appear in the endpoint URL."""
client = HTTPHandler()
for region in ["us-east-1", "us-west-2", "eu-west-1"]:
with patch.object(
client, "post", return_value=_make_fake_response(FAKE_ANTHROPIC_RESPONSE)
) as mock_post:
try:
litellm.completion(
model=MODEL,
messages=[{"role": "user", "content": "Hi"}],
max_tokens=10,
aws_region_name=region,
aws_access_key_id="AKIAIOSFODNN7EXAMPLE",
aws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
client=client,
)
except Exception:
pass
call_kwargs = mock_post.call_args.kwargs
expected = f"https://bedrock-mantle.{region}.api.aws/anthropic/v1/messages"
assert (
call_kwargs["url"] == expected
), f"region={region}: expected URL {expected}, got {call_kwargs['url']}"