Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Bug fixes

* `ChatBedrock()`'s default model (previously `"us.anthropic.claude-sonnet-4-6"`) is now `"us.anthropic.claude-sonnet-5"`, which mantle's `api="messages"` endpoint actually serves. Separately, the cross-region inference prefix (e.g. `"us."`) is now stripped from the model id sent in requests to `api="messages"` and `api="responses"`, since mantle rejects it even though Converse requires it. Previously, a mantle-only model with a cross-region prefix (e.g. `model="us.openai.gpt-5.4"`) would 404. (#411)
* `ChatDatabricks()` no longer drops the assistant's reply from the conversation when a GPT-OSS endpoint streams typed content. The typed part array was merged into the accumulated completion before it was normalized, so every later text delta was appended to it one character at a time and the finished turn came back empty. (#409)
* `.to_solver()` no longer corrupts the system prompt or the prior turns it reads out of Inspect AI's message state. The system prompt was being set to the `repr()` of the `ChatMessageSystem` object rather than its text, and message content arriving in Inspect AI's `str` form (rather than as a list of `Content`) was iterated one character at a time. (#407)
* `ChatGoogle()` no longer raises `ValueError: Unknown content type: ContentThinking` on the second and later turns when `reasoning` is enabled; thinking content is now replayed to the model as thought parts, and the `thought_signature` on thought parts is preserved (previously only tool-call parts kept it). (#403)
Expand Down
21 changes: 17 additions & 4 deletions chatlas/_provider_bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

BedrockAPI = Literal["converse", "messages", "responses"]

DEFAULT_MODEL = "us.anthropic.claude-sonnet-4-6"
DEFAULT_MODEL = "us.anthropic.claude-sonnet-5"

MANTLE_HOST = "https://bedrock-mantle.{region}.api.aws"

Expand Down Expand Up @@ -98,7 +98,7 @@ def ChatBedrock(
A system prompt to set the behavior of the assistant.
model
The model to use for the chat. Defaults to
`"us.anthropic.claude-sonnet-4-6"`.
`"us.anthropic.claude-sonnet-5"`.
api
Which Bedrock API to use. The default, `None`, picks the API from
`model`.
Expand Down Expand Up @@ -184,7 +184,7 @@ def ChatBedrock(
)
return Chat(
provider=BedrockResponsesProvider(
model=model,
model=bedrock_strip_region_prefix(model),
aws_profile=aws_profile,
aws_region=region,
base_url=base_url,
Expand Down Expand Up @@ -216,7 +216,7 @@ def ChatBedrock(

return Chat(
provider=BedrockMessagesProvider(
model=model,
model=bedrock_strip_region_prefix(model),
aws_profile=aws_profile,
aws_region=region,
base_url=base_url,
Expand Down Expand Up @@ -458,6 +458,19 @@ def bedrock_api_for_model(model: Optional[str]) -> BedrockAPI:
return MODEL_APIS.get(CROSS_REGION_PREFIX.sub("", model), "converse")


def bedrock_strip_region_prefix(model: str) -> str:
"""
Strip a cross-region inference prefix (e.g. `"us."`) from `model`.

Converse needs the prefix on the model id it's sent, since that's how it
picks the inference profile. Mantle's Anthropic and OpenAI-compatible
endpoints have no such concept and 404 if the prefix is included, so it
must be stripped from the model id used in requests to `"messages"` and
`"responses"`.
"""
return CROSS_REGION_PREFIX.sub("", model)


def aws_endpoint_url(var: str, default: str) -> str:
"""An AWS service endpoint override env var, or `default` when unset."""
url = os.environ.get(var, "")
Expand Down
2 changes: 1 addition & 1 deletion tests/test_provider_bedrock_converse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,7 @@ def test_default_model_is_ellmers_claude_sonnet(self):

chat = ChatBedrock(aws_region="us-east-1")

assert chat.provider.model == "us.anthropic.claude-sonnet-4-6"
assert chat.provider.model == "us.anthropic.claude-sonnet-5"
assert isinstance(chat.provider, BedrockConverseProvider)

def test_claude_routes_to_converse_not_mantle(self):
Expand Down
15 changes: 15 additions & 0 deletions tests/test_provider_bedrock_mantle.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ def test_client_points_at_the_openai_v1_mantle_path(self):
"https://bedrock-mantle.us-west-2.api.aws/openai/v1"
)

def test_cross_region_prefix_is_stripped_from_the_responses_model(self):
chat = ChatBedrock(model="us.openai.gpt-5.4", aws_region="us-east-1")
assert chat.provider.model == "openai.gpt-5.4"

def test_list_models_uses_the_v1_mantle_path(self):
# Mantle serves model listings at /v1/models; /openai/v1/models 404s,
# so list_models() must hit a different base URL than chat requests do.
Expand Down Expand Up @@ -223,6 +227,17 @@ def test_client_points_at_the_anthropic_mantle_path(self):
"https://bedrock-mantle.us-east-1.api.aws/anthropic"
)

def test_default_model_is_served_by_mantle(self):
from chatlas._provider_bedrock import DEFAULT_MODEL

chat = ChatBedrock(api="messages", aws_region="us-east-1")
assert chat.provider.model == "anthropic.claude-sonnet-5"
assert DEFAULT_MODEL == "us.anthropic.claude-sonnet-5"

def test_cross_region_prefix_is_stripped_from_the_messages_model(self):
chat = ChatBedrock(model="us.anthropic.claude-mythos-5", aws_region="us-east-1")
assert chat.provider.model == "anthropic.claude-mythos-5"

def test_cache_auto_becomes_a_5m_ttl(self):
chat = ChatBedrock(
model="anthropic.claude-haiku-4-5", api="messages", aws_region="us-east-1"
Expand Down
Loading