diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fa90a70..64cd471d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/chatlas/_provider_bedrock.py b/chatlas/_provider_bedrock.py index 5436df5b..772f9571 100644 --- a/chatlas/_provider_bedrock.py +++ b/chatlas/_provider_bedrock.py @@ -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" @@ -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`. @@ -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, @@ -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, @@ -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, "") diff --git a/tests/test_provider_bedrock_converse.py b/tests/test_provider_bedrock_converse.py index 76f18b3d..c9f7cd64 100644 --- a/tests/test_provider_bedrock_converse.py +++ b/tests/test_provider_bedrock_converse.py @@ -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): diff --git a/tests/test_provider_bedrock_mantle.py b/tests/test_provider_bedrock_mantle.py index b006f462..e0928954 100644 --- a/tests/test_provider_bedrock_mantle.py +++ b/tests/test_provider_bedrock_mantle.py @@ -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. @@ -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"