fix(bedrock): send the anthropic-workspace-id header to Bedrock Mantle (#44173)

aws_bedrock_project_id reached Bedrock Mantle as anthropic-workspace, a
header AWS ignores, so requests ran under account defaults and models
that require a project data-retention mode failed with a 400. Send the
header AWS documents, anthropic-workspace-id, on both Mantle routes.

Re-lands #31994 by @gunjanjaswal, merged into the since-deleted
litellm_oss_staging branch on 2026-08-22, which never reached main.

Fixes #31947

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Koh Jun Hao 2026-10-03 12:12:33 +08:00 • committed by GitHub
parent 2394fc3682
commit 5ce81631fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 14 additions and 9 deletions

View file

@ -73,7 +73,7 @@ class AmazonMantleConfig(AmazonAnthropicClaudeConfig):
)
project_id: Final = litellm_params.get("aws_bedrock_project_id")
if project_id:
headers["anthropic-workspace"] = project_id
headers["anthropic-workspace-id"] = project_id
return headers
def transform_request(

View file

@ -104,7 +104,7 @@ class AmazonMantleMessagesConfig(AmazonAnthropicClaudeMessagesConfig):
{
name: value
for name, value in (
("anthropic-workspace", project_id),
("anthropic-workspace-id", project_id),
("anthropic-version", None if has_version else DEFAULT_ANTHROPIC_API_VERSION),
)
if value

View file

@ -18,6 +18,10 @@ from litellm.llms.bedrock.messages.mantle_transformation import (
AmazonMantleMessagesConfig,
)
# AWS names this header for Mantle workspaces on the Anthropic Messages API, checked 2026-10-02:
# https://docs.aws.amazon.com/bedrock/latest/userguide/workspaces.html
_MANTLE_WORKSPACE_HEADER = "anthropic-workspace-id"
def _anthropic_response(url: str) -> httpx.Response:
return httpx.Response(
@ -345,7 +349,7 @@ def test_mantle_validate_environment_sets_workspace_header():
optional_params={},
litellm_params={"aws_bedrock_project_id": "proj_abc123def456"},
)
assert headers["anthropic-workspace"] == "proj_abc123def456"
assert headers[_MANTLE_WORKSPACE_HEADER] == "proj_abc123def456"
def test_mantle_validate_environment_without_project_id():
@ -357,7 +361,7 @@ def test_mantle_validate_environment_without_project_id():
optional_params={},
litellm_params={"aws_bedrock_project_id": None},
)
assert "anthropic-workspace" not in headers
assert _MANTLE_WORKSPACE_HEADER not in headers
def test_mantle_messages_validate_environment_sets_workspace_header():
@ -370,7 +374,7 @@ def test_mantle_messages_validate_environment_sets_workspace_header():
litellm_params={"aws_bedrock_project_id": "proj_abc123def456"},
api_base="https://bedrock-mantle.us-east-1.api.aws/anthropic/v1/messages",
)
assert headers["anthropic-workspace"] == "proj_abc123def456"
assert headers[_MANTLE_WORKSPACE_HEADER] == "proj_abc123def456"
assert api_base == "https://bedrock-mantle.us-east-1.api.aws/anthropic/v1/messages"
@ -383,7 +387,7 @@ def test_mantle_messages_validate_environment_without_project_id():
optional_params={},
litellm_params={},
)
assert "anthropic-workspace" not in headers
assert _MANTLE_WORKSPACE_HEADER not in headers
def test_mantle_completion_sends_workspace_header_and_clean_body():
@ -409,7 +413,7 @@ def test_mantle_completion_sends_workspace_header_and_clean_body():
assert response.choices[0].message.content == "ok"
assert len(requests) == 1
assert requests[0]["path"] == "/anthropic/v1/messages"
assert requests[0]["headers"]["anthropic-workspace"] == "proj_abc123def456"
assert requests[0]["headers"][_MANTLE_WORKSPACE_HEADER] == "proj_abc123def456"
assert "aws_bedrock_project_id" not in requests[0]["body"]
@ -443,7 +447,7 @@ async def test_mantle_anthropic_messages_sends_workspace_header_and_clean_body()
assert response["content"][0]["text"] == "ok"
assert len(requests) == 1
assert requests[0]["path"] == "/anthropic/v1/messages"
assert requests[0]["headers"]["anthropic-workspace"] == "proj_abc123def456"
assert requests[0]["headers"][_MANTLE_WORKSPACE_HEADER] == "proj_abc123def456"
assert "aws_bedrock_project_id" not in requests[0]["body"]

View file

@ -193,7 +193,8 @@ class TestEnvironment:
assert "anthropic-version" not in merged
def test_project_id_becomes_the_workspace_header(self):
assert self._validate({}, {"aws_bedrock_project_id": "proj_123"})["anthropic-workspace"] == "proj_123"
# header name from https://docs.aws.amazon.com/bedrock/latest/userguide/workspaces.html, checked 2026-10-02
assert self._validate({}, {"aws_bedrock_project_id": "proj_123"})["anthropic-workspace-id"] == "proj_123"
class TestRequestBody: