From 921e3d01c714a235469f483608a48a989391d3c5 Mon Sep 17 00:00:00 2001 From: Mackenzie Zastrow Date: Thu, 26 Jun 2025 17:01:39 -0400 Subject: [PATCH 1/3] fix: Load region from session when possible Only use a default region if the session doesn't provide one or if the AWS_REGION environment variable is not set. Fixes #238 --- src/strands/models/bedrock.py | 15 +---- tests/strands/models/test_bedrock.py | 97 ++++++++++++++++++---------- 2 files changed, 67 insertions(+), 45 deletions(-) diff --git a/src/strands/models/bedrock.py b/src/strands/models/bedrock.py index 31d57fd61..1c4734a5e 100644 --- a/src/strands/models/bedrock.py +++ b/src/strands/models/bedrock.py @@ -25,6 +25,7 @@ logger = logging.getLogger(__name__) DEFAULT_BEDROCK_MODEL_ID = "us.anthropic.claude-3-7-sonnet-20250219-v1:0" +DEFAULT_BEDROCK_REGION = "us-west-2" BEDROCK_CONTEXT_WINDOW_OVERFLOW_MESSAGES = [ "Input is too long for requested model", @@ -117,18 +118,7 @@ def __init__( logger.debug("config=<%s> | initializing", self.config) - region_for_boto = region_name or os.getenv("AWS_REGION") - if region_for_boto is None: - region_for_boto = "us-west-2" - logger.warning("defaulted to us-west-2 because no region was specified") - logger.warning( - "issue=<%s> | this behavior will change in an upcoming release", - "https://github.com/strands-agents/sdk-python/issues/238", - ) - - session = boto_session or boto3.Session( - region_name=region_for_boto, - ) + session = boto_session or boto3.Session() # Add strands-agents to the request user agent if boto_client_config: @@ -147,6 +137,7 @@ def __init__( self.client = session.client( service_name="bedrock-runtime", config=client_config, + region_name=region_name or session.region_name or os.environ.get("AWS_REGION") or DEFAULT_BEDROCK_REGION, ) @override diff --git a/tests/strands/models/test_bedrock.py b/tests/strands/models/test_bedrock.py index 1d045f3b1..274305bcb 100644 --- a/tests/strands/models/test_bedrock.py +++ b/tests/strands/models/test_bedrock.py @@ -1,6 +1,7 @@ import os import sys import unittest.mock +from unittest.mock import ANY import boto3 import pydantic @@ -10,17 +11,30 @@ import strands from strands.models import BedrockModel -from strands.models.bedrock import DEFAULT_BEDROCK_MODEL_ID +from strands.models.bedrock import DEFAULT_BEDROCK_MODEL_ID, DEFAULT_BEDROCK_REGION from strands.types.exceptions import ModelThrottledException @pytest.fixture -def bedrock_client(): +def session_cls(): + # Mock the creation of a Session so that we don't depend on environment variables or profiles with unittest.mock.patch.object(strands.models.bedrock.boto3, "Session") as mock_session_cls: - mock_client = mock_session_cls.return_value.client.return_value - mock_client.meta = unittest.mock.MagicMock() - mock_client.meta.region_name = "us-west-2" - yield mock_client + mock_session_cls.return_value.region_name = None + yield mock_session_cls + + +@pytest.fixture +def mock_client_method(session_cls): + # the boto3.Session().client(...) method + return session_cls.return_value.client + + +@pytest.fixture +def bedrock_client(session_cls): + mock_client = session_cls.return_value.client.return_value + mock_client.meta = unittest.mock.MagicMock() + mock_client.meta.region_name = "us-west-2" + yield mock_client @pytest.fixture @@ -105,41 +119,58 @@ def test__init__default_model_id(bedrock_client): assert tru_model_id == exp_model_id -def test__init__with_default_region(bedrock_client): +def test__init__with_default_region(session_cls, mock_client_method): """Test that BedrockModel uses the provided region.""" - _ = bedrock_client - default_region = "us-west-2" + BedrockModel() - with unittest.mock.patch("strands.models.bedrock.boto3.Session") as mock_session_cls: - with unittest.mock.patch("strands.models.bedrock.logger.warning") as mock_warning: - _ = BedrockModel() - mock_session_cls.assert_called_once_with(region_name=default_region) - # Assert that warning logs are emitted - mock_warning.assert_any_call("defaulted to us-west-2 because no region was specified") - mock_warning.assert_any_call( - "issue=<%s> | this behavior will change in an upcoming release", - "https://github.com/strands-agents/sdk-python/issues/238", - ) - - -def test__init__with_custom_region(bedrock_client): + session_cls.return_value.client.assert_called_with(region_name=DEFAULT_BEDROCK_REGION, config=ANY, service_name=ANY) + + +def test__init__with_session_region(session_cls, mock_client_method): """Test that BedrockModel uses the provided region.""" - _ = bedrock_client - custom_region = "us-east-1" + session_cls.return_value.region_name = "eu-blah-1" - with unittest.mock.patch("strands.models.bedrock.boto3.Session") as mock_session_cls: - _ = BedrockModel(region_name=custom_region) - mock_session_cls.assert_called_once_with(region_name=custom_region) + BedrockModel() + mock_client_method.assert_called_with(region_name="eu-blah-1", config=ANY, service_name=ANY) -def test__init__with_environment_variable_region(bedrock_client): + +def test__init__with_custom_region(mock_client_method): """Test that BedrockModel uses the provided region.""" - _ = bedrock_client - os.environ["AWS_REGION"] = "eu-west-1" + custom_region = "us-east-1" + BedrockModel(region_name=custom_region) + mock_client_method.assert_called_with(region_name=custom_region, config=ANY, service_name=ANY) - with unittest.mock.patch("strands.models.bedrock.boto3.Session") as mock_session_cls: - _ = BedrockModel() - mock_session_cls.assert_called_once_with(region_name="eu-west-1") + +def test__init__with_default_environment_variable_region(mock_client_method): + """Test that BedrockModel uses the AWS_REGION since we code that in.""" + with unittest.mock.patch.object(os, "environ", {"AWS_REGION": "eu-west-2"}): + BedrockModel() + + mock_client_method.assert_called_with(region_name="eu-west-2", config=ANY, service_name=ANY) + + +def test__init__region_precedence(mock_client_method, session_cls): + """Test that BedrockModel uses the correct ordering of precedence when determining region.""" + with unittest.mock.patch.object(os, "environ", {"AWS_REGION": "us-environment-1"}): + session_cls.return_value.region_name = "us-session-1" + + # specifying a region always wins out + BedrockModel(region_name="us-specified-1") + mock_client_method.assert_called_with(region_name="us-specified-1", config=ANY, service_name=ANY) + + # other-wise uses the session's + BedrockModel() + mock_client_method.assert_called_with(region_name="us-session-1", config=ANY, service_name=ANY) + + # environment variable next + session_cls.return_value.region_name = None + BedrockModel() + mock_client_method.assert_called_with(region_name="us-environment-1", config=ANY, service_name=ANY) + + # Finally default + BedrockModel() + mock_client_method.assert_called_with(region_name=DEFAULT_BEDROCK_REGION, config=ANY, service_name=ANY) def test__init__with_region_and_session_raises_value_error(): From 6924b6a923f5b3e85400118a94441f053cb785b5 Mon Sep 17 00:00:00 2001 From: Mackenzie Zastrow Date: Fri, 27 Jun 2025 13:22:05 -0400 Subject: [PATCH 2/3] Add logging for the resolved region --- src/strands/models/bedrock.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/strands/models/bedrock.py b/src/strands/models/bedrock.py index 1c4734a5e..1e043b3ed 100644 --- a/src/strands/models/bedrock.py +++ b/src/strands/models/bedrock.py @@ -134,12 +134,16 @@ def __init__( else: client_config = BotocoreConfig(user_agent_extra="strands-agents") + resolved_region = region_name or session.region_name or os.environ.get("AWS_REGION") or DEFAULT_BEDROCK_REGION + self.client = session.client( service_name="bedrock-runtime", config=client_config, - region_name=region_name or session.region_name or os.environ.get("AWS_REGION") or DEFAULT_BEDROCK_REGION, + region_name=resolved_region, ) + logger.debug("region=<%s> | bedrock client created", resolved_region) + @override def update_config(self, **model_config: Unpack[BedrockConfig]) -> None: # type: ignore """Update the Bedrock Model configuration with the provided arguments. From 3913e18bda5145f8864b3e6f3dd29884db8325ff Mon Sep 17 00:00:00 2001 From: Mackenzie Zastrow Date: Fri, 27 Jun 2025 13:24:00 -0400 Subject: [PATCH 3/3] Log the region from the actual client --- src/strands/models/bedrock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strands/models/bedrock.py b/src/strands/models/bedrock.py index 1e043b3ed..da5d633f9 100644 --- a/src/strands/models/bedrock.py +++ b/src/strands/models/bedrock.py @@ -142,7 +142,7 @@ def __init__( region_name=resolved_region, ) - logger.debug("region=<%s> | bedrock client created", resolved_region) + logger.debug("region=<%s> | bedrock client created", self.client.meta.region_name) @override def update_config(self, **model_config: Unpack[BedrockConfig]) -> None: # type: ignore