Repository: pydantic/pydantic-ai
File: pydantic_ai_slim/pydantic_ai/providers/bedrock.py (Python)

Code before:


class BedrockProvider(Provider[BaseClient]):
    """Provider for AWS Bedrock."""

    @property
    def name(self) -> str:
        return 'bedrock'

    @property
    def base_url(self) -> str:
        return self._client.meta.endpoint_url

    @property
    def client(self) -> BaseClient:
        return self._client

    def model_profile(self, model_name: str) -> ModelProfile | None:
        provider_to_profile: dict[str, Callable[[str], ModelProfile | None]] = {
            'anthropic': lambda model_name: replace(
                BedrockModelProfile(bedrock_supports_tool_choice=True, bedrock_send_back_thinking_parts=True).update(
                    anthropic_model_profile(model_name)
                ),
                # Bedrock does not support native structured output
                supports_json_schema_output=False,
                json_schema_transformer=None,
            ),
            'mistral': lambda model_name: BedrockModelProfile(bedrock_tool_result_format='json').update(
                mistral_model_profile(model_name)
            ),
            'cohere': cohere_model_profile,
            'amazon': bedrock_amazon_model_profile,
            'meta': meta_model_profile,
            'deepseek': bedrock_deepseek_model_profile,
        }

        # Split the model name into parts
        parts = model_name.split('.', 2)

        # Handle regional prefixes (e.g. "us.")
        if len(parts) > 2 and len(parts[0]) == 2:
            parts = parts[1:]

        if len(parts) < 2:
            return None

        provider = parts[0]
        model_name_with_version = parts[1]

        # Remove version suffix if it matches the format (e.g. "-v1:0" or "-v14")
        version_match = re.match(r'(.+)-v\d+(?::\d+)?$', model_name_with_version)

Review comment (suggestion):
`BedrockProvider` is not the only one that uses `anthropic_model_profile`, so with this changes the other providers still have the same issue.

I think we should implement this as I suggested in https://github.com/pydantic/pydantic-ai/issues/3625#issuecomment-3618020309:

> That means moving the `AnthropicJsonSchemaTransformer` bit out of `pydantic_ai.profiles.anthropic.anthropic_model_profile`, and into `AnthropicProvider.model_profile`.

So `anthropic_model_profile` should not have any `json_schema_transformer`, and we should add it in `AnthropicProvider.model_profile` That way, `AnthropicJsonSchemaTransformer` will only be used with `AnthropicProvider`, not any other provider. 

Code after:


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


class BedrockProvider(Provider[BaseClient]):
    """Provider for AWS Bedrock."""

    @property
    def name(self) -> str:
        return 'bedrock'

    @property
    def base_url(self) -> str:
        return self._client.meta.endpoint_url

    @property
    def client(self) -> BaseClient:
        return self._client

    def model_profile(self, model_name: str) -> ModelProfile | None:
        provider_to_profile: dict[str, Callable[[str], ModelProfile | None]] = {
            'anthropic': lambda model_name: BedrockModelProfile(
                bedrock_supports_tool_choice=True, bedrock_send_back_thinking_parts=True
            ).update(anthropic_model_profile(model_name)),
            'mistral': lambda model_name: BedrockModelProfile(bedrock_tool_result_format='json').update(
                mistral_model_profile(model_name)
            ),
            'cohere': cohere_model_profile,
            'amazon': bedrock_amazon_model_profile,
            'meta': meta_model_profile,
            'deepseek': bedrock_deepseek_model_profile,
        }

        # Split the model name into parts
        parts = model_name.split('.', 2)

        # Handle regional prefixes
        if len(parts) > 2 and parts[0] in BEDROCK_GEO_PREFIXES:
            parts = parts[1:]

        # required format is provider.model-name-with-version
        if len(parts) < 2:
            return None

        provider = parts[0]
        model_name_with_version = parts[1]

        # Remove version suffix if it matches the format (e.g. "-v1:0" or "-v14")
        version_match = re.match(r'(.+)-v\d+(?::\d+)?$', model_name_with_version)