Skip to content

Conversation

@Leundai
Copy link

@Leundai Leundai commented Dec 4, 2025

Summary

The BedrockProvider.model_profile method previously only handled 2-character regional prefixes (e.g., us., eu.), causing issues with models using longer prefixes like us-gov. (AWS GovCloud) and global..

Problem

When using extended thinking with us-gov.anthropic.* models via Bedrock, the model_profile method doesn't recognize the us-gov. prefix because it only strips 2-character prefixes:

# Previous implementation:
parts = model_name.split('.', 2)
if len(parts) > 2 and len(parts[0]) == 2:
    parts = parts[1:]

This causes bedrock_send_back_thinking_parts to stay at its default False value. As a result, ThinkingPart blocks from previous conversation turns are converted to text blocks with thinking tags instead of reasoningContent, causing Bedrock to reject the request with:

Expected `thinking` or `redacted_thinking`, but found `text`

Solution

  1. Added _strip_geo_prefix() helper function that properly handles all known geo prefixes including us-gov. and global.
  2. Updated _AWS_BEDROCK_INFERENCE_GEO_PREFIXES constant to include us-gov. (the comment in the code already suggested this)
  3. Added comprehensive parametrized tests for all 8 geo prefixes

Affected Models

  • us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0
  • us-gov.anthropic.claude-3-7-sonnet-20250219-v1:0
  • global.anthropic.claude-opus-4-5-20251101-v1:0
  • And any other cross-region inference profile using multi-character prefixes

Testing

All existing tests pass, plus new parametrized tests for all geo prefixes

The BedrockProvider.model_profile method previously only handled 2-character
regional prefixes (e.g., 'us.', 'eu.'), causing issues with models using
longer prefixes like 'us-gov.' (AWS GovCloud) and 'global.'.

This caused extended thinking to fail in multi-turn conversations because
bedrock_send_back_thinking_parts stayed at its default False value for these
models. ThinkingPart blocks from previous turns were converted to text blocks
instead of reasoningContent, causing Bedrock to reject requests with:
'Expected thinking or redacted_thinking, but found text'

Changes:
- Add _strip_geo_prefix() helper function to properly handle all known
  geo prefixes including us-gov. and global.
- Update _AWS_BEDROCK_INFERENCE_GEO_PREFIXES to include us-gov.
- Add comprehensive tests for all geo prefixes

Fixes cross-region inference for AWS GovCloud environments.


# Known geo prefixes for cross-region inference profile IDs
_BEDROCK_GEO_PREFIXES: tuple[str, ...] = ('us.', 'eu.', 'apac.', 'jp.', 'au.', 'ca.', 'global.', 'us-gov.')
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think at some point it might be worth unifying this and the other private constant in models/bedrock.py

But this just extends it to support govcloud and global. Its been a real issue since Claude 4.5 came out and AWS doing cross-region inferencing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should definitely not duplicate it, since we're already importing the BedrockModelProfile in models/bedrock.py we should make this public and import it there.

from pydantic_ai.providers.bedrock import BedrockModelProfile

@DouweM do you agree?

Copy link
Contributor

@dsfaccini dsfaccini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for the PR @Leundai ! I've requested a couple changes, let me know if you have any questions.



# Known geo prefixes for cross-region inference profile IDs
_BEDROCK_GEO_PREFIXES: tuple[str, ...] = ('us.', 'eu.', 'apac.', 'jp.', 'au.', 'ca.', 'global.', 'us-gov.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should definitely not duplicate it, since we're already importing the BedrockModelProfile in models/bedrock.py we should make this public and import it there.

from pydantic_ai.providers.bedrock import BedrockModelProfile

@DouweM do you agree?

# Handle regional prefixes (e.g. "us.")
if len(parts) > 2 and len(parts[0]) == 2:
parts = parts[1:]
# Strip regional/geo prefix if present (e.g. "us.", "eu.", "us-gov.", "global.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can remove this comment

assert unknown_model is None


@pytest.mark.parametrize('prefix', ['us.', 'eu.', 'apac.', 'jp.', 'au.', 'ca.', 'global.', 'us-gov.'])
Copy link
Contributor

@dsfaccini dsfaccini Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use the BEDROCK_GEO_PREFIXES you defined above here?

Comment on lines +120 to +122
assert isinstance(profile, BedrockModelProfile)
assert profile.bedrock_supports_tool_choice is True
assert profile.bedrock_send_back_thinking_parts is True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if these don't vary across models we don't need to test them, right? the test just tests that all prefixes are hadnled properly

Comment on lines +125 to +149
def test_bedrock_provider_model_profile_us_gov_anthropic(env: TestEnv, mocker: MockerFixture):
"""Test that us-gov. prefixed Anthropic models get the correct profile.
This specifically tests the us-gov. prefix which was previously broken
because the provider only handled 2-character prefixes.
"""
env.set('AWS_DEFAULT_REGION', 'us-east-1')
provider = BedrockProvider()

ns = 'pydantic_ai.providers.bedrock'
anthropic_model_profile_mock = mocker.patch(f'{ns}.anthropic_model_profile', wraps=anthropic_model_profile)

# Test us-gov. prefix (AWS GovCloud cross-region inference)
profile = provider.model_profile('us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0')
anthropic_model_profile_mock.assert_called_with('claude-sonnet-4-5-20250929')
assert isinstance(profile, BedrockModelProfile)
assert profile.bedrock_supports_tool_choice is True
assert profile.bedrock_send_back_thinking_parts is True

# Test global. prefix
profile = provider.model_profile('global.anthropic.claude-opus-4-5-20251101-v1:0')
anthropic_model_profile_mock.assert_called_with('claude-opus-4-5-20251101')
assert isinstance(profile, BedrockModelProfile)
assert profile.bedrock_supports_tool_choice is True
assert profile.bedrock_send_back_thinking_parts is True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're already testing this above, aren't we?

let's add a test to check that we don't have any bedrock shorthand in KnownModelName with a geo prefix that we haven't added in the BEDROCK_GEO_PREFIXES

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants