From 49e68f9a5b7bfcd821bae59a876acc7857447fdd Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Mon, 21 Jul 2025 14:14:39 -0400 Subject: [PATCH 01/17] ci: enable integ tests for anthropic, cohere, mistral, openai, writer --- .github/workflows/integration-test.yml | 2 +- tests_integ/conftest.py | 53 +++++++++++++++++++ tests_integ/models/providers.py | 4 +- .../{conformance.py => test_conformance.py} | 2 +- tests_integ/models/test_model_anthropic.py | 12 ++++- 5 files changed, 67 insertions(+), 6 deletions(-) rename tests_integ/models/{conformance.py => test_conformance.py} (94%) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index a1d86364a..6e754fd0e 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -69,4 +69,4 @@ jobs: AWS_REGION_NAME: us-east-1 # Needed for LiteLLM id: tests run: | - hatch test tests_integ + hatch test tests_integ/models diff --git a/tests_integ/conftest.py b/tests_integ/conftest.py index f83f0e299..c6a275e46 100644 --- a/tests_integ/conftest.py +++ b/tests_integ/conftest.py @@ -1,5 +1,17 @@ +import json +import logging +import os + +import boto3 import pytest +logger = logging.getLogger(__name__) + + +def pytest_sessionstart(session): + _load_api_keys_from_secrets_manager() + + ## Data @@ -28,3 +40,44 @@ async def alist(items): return [item async for item in items] return alist + + +## Models + + +def _load_api_keys_from_secrets_manager(): + """Load API keys as environment variables from AWS Secrets Manager.""" + session = boto3.session.Session() + client = session.client(service_name="secretsmanager") + secret_name = os.getenv("STRANDS_TEST_API_KEYS_SECRET_NAME") + try: + response = client.get_secret_value(SecretId=secret_name) + + if "SecretString" in response: + secret = json.loads(response["SecretString"]) + for key, value in secret.items(): + os.environ[f"{key.upper()}_API_KEY"] = str(value) + else: + raise Exception("No API keys found in secrets manager") + + except Exception as e: + logger.warning("Error retrieving secret", e) + + """ + Validate that required environment variables are set when running in GitHub Actions. + This prevents tests from being unintentionally skipped due to missing credentials. + """ + if os.environ.get("GITHUB_ACTIONS") != "true": + logger.warning("Tests running outside GitHub Actions, skipping required provider validation") + return + + required_providers = { + "ANTHROPIC_API_KEY", + "COHERE_API_KEY", + "MISTRAL_API_KEY", + "OPENAI_API_KEY", + "WRITER_API_KEY", + } + for provider in required_providers: + if provider not in os.environ or not os.environ[provider]: + raise ValueError(f"Missing required environment variables for {provider}") diff --git a/tests_integ/models/providers.py b/tests_integ/models/providers.py index 543f58480..d2ac148d3 100644 --- a/tests_integ/models/providers.py +++ b/tests_integ/models/providers.py @@ -72,11 +72,11 @@ def __init__(self): bedrock = ProviderInfo(id="bedrock", factory=lambda: BedrockModel()) cohere = ProviderInfo( id="cohere", - environment_variable="CO_API_KEY", + environment_variable="COHERE_API_KEY", factory=lambda: OpenAIModel( client_args={ "base_url": "https://api.cohere.com/compatibility/v1", - "api_key": os.getenv("CO_API_KEY"), + "api_key": os.getenv("COHERE_API_KEY"), }, model_id="command-a-03-2025", params={"stream_options": None}, diff --git a/tests_integ/models/conformance.py b/tests_integ/models/test_conformance.py similarity index 94% rename from tests_integ/models/conformance.py rename to tests_integ/models/test_conformance.py index 262e41e42..96351778c 100644 --- a/tests_integ/models/conformance.py +++ b/tests_integ/models/test_conformance.py @@ -1,6 +1,6 @@ import pytest -from strands.types.models import Model +from strands.models import Model from tests_integ.models.providers import ProviderInfo, all_providers diff --git a/tests_integ/models/test_model_anthropic.py b/tests_integ/models/test_model_anthropic.py index 2ee5e7f23..267d7d74b 100644 --- a/tests_integ/models/test_model_anthropic.py +++ b/tests_integ/models/test_model_anthropic.py @@ -8,8 +8,16 @@ from strands.models.anthropic import AnthropicModel from tests_integ.models import providers -# these tests only run if we have the anthropic api key -pytestmark = providers.anthropic.mark +""" +These tests only run if we have the anthropic api key + +Because of infrequent burst usage Anthropic is occasionally failing tests with 529s. We retrying with delay to +avoid these false negatives. + +{'type': 'error', 'error': {'details': None, 'type': 'overloaded_error', 'message': 'Overloaded'}} +https://docs.anthropic.com/en/api/errors#http-errors +""" +pytestmark = [providers.anthropic.mark, pytest.mark.flaky(reruns=2, reruns_delay=5)] @pytest.fixture From 0bca5fc52eb8c405816b7ceaf354518d288842c0 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Mon, 21 Jul 2025 17:17:35 -0400 Subject: [PATCH 02/17] fix: add secret to github workflow --- .github/workflows/integration-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 6e754fd0e..a21d1670e 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -67,6 +67,7 @@ jobs: env: AWS_REGION: us-east-1 AWS_REGION_NAME: us-east-1 # Needed for LiteLLM + STRANDS_TEST_API_KEYS_SECRET_NAME: ${{ secrets.STRANDS_TEST_API_KEYS_SECRET_NAME }} id: tests run: | hatch test tests_integ/models From 167d9a83950eb4cab07dd4df2e6c58f214e66196 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Mon, 21 Jul 2025 17:20:38 -0400 Subject: [PATCH 03/17] fix: moved getenv inside the try/catch --- tests_integ/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_integ/conftest.py b/tests_integ/conftest.py index c6a275e46..1f31db137 100644 --- a/tests_integ/conftest.py +++ b/tests_integ/conftest.py @@ -49,8 +49,8 @@ def _load_api_keys_from_secrets_manager(): """Load API keys as environment variables from AWS Secrets Manager.""" session = boto3.session.Session() client = session.client(service_name="secretsmanager") - secret_name = os.getenv("STRANDS_TEST_API_KEYS_SECRET_NAME") try: + secret_name = os.getenv("STRANDS_TEST_API_KEYS_SECRET_NAME") response = client.get_secret_value(SecretId=secret_name) if "SecretString" in response: From 7de2a89d8c33fd90b17fc2baa9e46b5ef77b9203 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Mon, 21 Jul 2025 17:26:03 -0400 Subject: [PATCH 04/17] fix: add secret to github workflow --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index a21d1670e..c347e3805 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -70,4 +70,4 @@ jobs: STRANDS_TEST_API_KEYS_SECRET_NAME: ${{ secrets.STRANDS_TEST_API_KEYS_SECRET_NAME }} id: tests run: | - hatch test tests_integ/models + hatch test tests_integ From ca22508cff2c3a6c79c9817472be49639b4c0b16 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Mon, 21 Jul 2025 17:40:04 -0400 Subject: [PATCH 05/17] fix: remove unecessary if/else in _load_api_keys_from_secrets_manager --- tests_integ/conftest.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests_integ/conftest.py b/tests_integ/conftest.py index 1f31db137..39fea523b 100644 --- a/tests_integ/conftest.py +++ b/tests_integ/conftest.py @@ -57,8 +57,6 @@ def _load_api_keys_from_secrets_manager(): secret = json.loads(response["SecretString"]) for key, value in secret.items(): os.environ[f"{key.upper()}_API_KEY"] = str(value) - else: - raise Exception("No API keys found in secrets manager") except Exception as e: logger.warning("Error retrieving secret", e) From 3ee8c12559e17b2ef011487aa3dd67f1e88a7574 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Tue, 22 Jul 2025 12:48:13 -0400 Subject: [PATCH 06/17] feat: centralize flaky logic from test_model_anthropic.py to providers.py --- tests_integ/conftest.py | 23 +++++++++++----------- tests_integ/models/providers.py | 19 ++++++++++++++++-- tests_integ/models/test_model_anthropic.py | 12 ++--------- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/tests_integ/conftest.py b/tests_integ/conftest.py index 39fea523b..61c2bf9a1 100644 --- a/tests_integ/conftest.py +++ b/tests_integ/conftest.py @@ -49,17 +49,18 @@ def _load_api_keys_from_secrets_manager(): """Load API keys as environment variables from AWS Secrets Manager.""" session = boto3.session.Session() client = session.client(service_name="secretsmanager") - try: - secret_name = os.getenv("STRANDS_TEST_API_KEYS_SECRET_NAME") - response = client.get_secret_value(SecretId=secret_name) - - if "SecretString" in response: - secret = json.loads(response["SecretString"]) - for key, value in secret.items(): - os.environ[f"{key.upper()}_API_KEY"] = str(value) - - except Exception as e: - logger.warning("Error retrieving secret", e) + if "STRANDS_TEST_API_KEYS_SECRET_NAME" in os.environ: + try: + secret_name = os.getenv("STRANDS_TEST_API_KEYS_SECRET_NAME") + response = client.get_secret_value(SecretId=secret_name) + + if "SecretString" in response: + secret = json.loads(response["SecretString"]) + for key, value in secret.items(): + os.environ[f"{key.upper()}_API_KEY"] = str(value) + + except Exception as e: + logger.warning("Error retrieving secret", e) """ Validate that required environment variables are set when running in GitHub Actions. diff --git a/tests_integ/models/providers.py b/tests_integ/models/providers.py index d2ac148d3..476cb9d76 100644 --- a/tests_integ/models/providers.py +++ b/tests_integ/models/providers.py @@ -3,8 +3,9 @@ """ import os -from typing import Callable, Optional +from typing import Callable, Optional, List, Union +import pytest import requests from pytest import mark @@ -26,13 +27,19 @@ def __init__( id: str, factory: Callable[[], Model], environment_variable: Optional[str] = None, + flaky: bool = False, ) -> None: self.id = id self.model_factory = factory - self.mark = mark.skipif( + + skip_mark = mark.skipif( environment_variable is not None and environment_variable not in os.environ, reason=f"{environment_variable} environment variable missing", ) + if flaky: + self.mark = [skip_mark, pytest.mark.flaky(reruns=2, reruns_delay=5)] + else: + self.mark = skip_mark def create_model(self) -> Model: return self.model_factory() @@ -58,6 +65,13 @@ def __init__(self): ) +""" +Because of infrequent burst usage Anthropic is occasionally failing tests with 529s. We retrying with delay to +avoid these false negatives. + +{'type': 'error', 'error': {'details': None, 'type': 'overloaded_error', 'message': 'Overloaded'}} +https://docs.anthropic.com/en/api/errors#http-errors +""" anthropic = ProviderInfo( id="anthropic", environment_variable="ANTHROPIC_API_KEY", @@ -68,6 +82,7 @@ def __init__(self): model_id="claude-3-7-sonnet-20250219", max_tokens=512, ), + flaky=True ) bedrock = ProviderInfo(id="bedrock", factory=lambda: BedrockModel()) cohere = ProviderInfo( diff --git a/tests_integ/models/test_model_anthropic.py b/tests_integ/models/test_model_anthropic.py index 267d7d74b..2ee5e7f23 100644 --- a/tests_integ/models/test_model_anthropic.py +++ b/tests_integ/models/test_model_anthropic.py @@ -8,16 +8,8 @@ from strands.models.anthropic import AnthropicModel from tests_integ.models import providers -""" -These tests only run if we have the anthropic api key - -Because of infrequent burst usage Anthropic is occasionally failing tests with 529s. We retrying with delay to -avoid these false negatives. - -{'type': 'error', 'error': {'details': None, 'type': 'overloaded_error', 'message': 'Overloaded'}} -https://docs.anthropic.com/en/api/errors#http-errors -""" -pytestmark = [providers.anthropic.mark, pytest.mark.flaky(reruns=2, reruns_delay=5)] +# these tests only run if we have the anthropic api key +pytestmark = providers.anthropic.mark @pytest.fixture From 22778d13d9dfc31c2ecd7546d0967822283bf9a3 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Tue, 22 Jul 2025 12:52:26 -0400 Subject: [PATCH 07/17] formatting --- tests_integ/models/providers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests_integ/models/providers.py b/tests_integ/models/providers.py index 476cb9d76..56f99279f 100644 --- a/tests_integ/models/providers.py +++ b/tests_integ/models/providers.py @@ -3,7 +3,7 @@ """ import os -from typing import Callable, Optional, List, Union +from typing import Callable, Optional import pytest import requests @@ -31,7 +31,7 @@ def __init__( ) -> None: self.id = id self.model_factory = factory - + skip_mark = mark.skipif( environment_variable is not None and environment_variable not in os.environ, reason=f"{environment_variable} environment variable missing", @@ -82,7 +82,7 @@ def __init__(self): model_id="claude-3-7-sonnet-20250219", max_tokens=512, ), - flaky=True + flaky=True, ) bedrock = ProviderInfo(id="bedrock", factory=lambda: BedrockModel()) cohere = ProviderInfo( From 23b0aae34e5e5590824b5a174e5cccc372bb404e Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Tue, 22 Jul 2025 14:07:51 -0400 Subject: [PATCH 08/17] feat: centralize flaky logic from test_model_anthropic.py to providers.py --- tests_integ/models/providers.py | 4 ++-- tests_integ/models/test_conformance.py | 2 +- tests_integ/models/test_model_anthropic.py | 2 +- tests_integ/models/test_model_cohere.py | 4 ++-- tests_integ/models/test_model_llamaapi.py | 2 +- tests_integ/models/test_model_mistral.py | 2 +- tests_integ/models/test_model_ollama.py | 2 +- tests_integ/models/test_model_openai.py | 2 +- tests_integ/models/test_model_writer.py | 2 +- .../test_summarizing_conversation_manager_integration.py | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests_integ/models/providers.py b/tests_integ/models/providers.py index 56f99279f..0acdb7bf2 100644 --- a/tests_integ/models/providers.py +++ b/tests_integ/models/providers.py @@ -37,9 +37,9 @@ def __init__( reason=f"{environment_variable} environment variable missing", ) if flaky: - self.mark = [skip_mark, pytest.mark.flaky(reruns=2, reruns_delay=5)] + self.marks = [skip_mark, pytest.mark.flaky(reruns=2, reruns_delay=5)] else: - self.mark = skip_mark + self.marks = [skip_mark] def create_model(self) -> Model: return self.model_factory() diff --git a/tests_integ/models/test_conformance.py b/tests_integ/models/test_conformance.py index 96351778c..fe58bc688 100644 --- a/tests_integ/models/test_conformance.py +++ b/tests_integ/models/test_conformance.py @@ -9,7 +9,7 @@ def get_models(): pytest.param( provider_info, id=provider_info.id, # Adds the provider name to the test name - marks=[provider_info.mark], # ignores tests that don't have the requirements + marks=provider_info.marks, # ignores tests that don't have the requirements ) for provider_info in all_providers ] diff --git a/tests_integ/models/test_model_anthropic.py b/tests_integ/models/test_model_anthropic.py index 2ee5e7f23..658b2f2ed 100644 --- a/tests_integ/models/test_model_anthropic.py +++ b/tests_integ/models/test_model_anthropic.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the anthropic api key -pytestmark = providers.anthropic.mark +pytestmark = providers.anthropic.marks @pytest.fixture diff --git a/tests_integ/models/test_model_cohere.py b/tests_integ/models/test_model_cohere.py index 996b0f326..e452e57af 100644 --- a/tests_integ/models/test_model_cohere.py +++ b/tests_integ/models/test_model_cohere.py @@ -8,7 +8,7 @@ from tests_integ.models import providers # these tests only run if we have the cohere api key -pytestmark = providers.cohere.mark +pytestmark = providers.cohere.marks @pytest.fixture @@ -16,7 +16,7 @@ def model(): return OpenAIModel( client_args={ "base_url": "https://api.cohere.com/compatibility/v1", - "api_key": os.getenv("CO_API_KEY"), + "api_key": os.getenv("COHERE_API_KEY"), }, model_id="command-a-03-2025", params={"stream_options": None}, diff --git a/tests_integ/models/test_model_llamaapi.py b/tests_integ/models/test_model_llamaapi.py index b36a63a28..2234600e3 100644 --- a/tests_integ/models/test_model_llamaapi.py +++ b/tests_integ/models/test_model_llamaapi.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the llama api key -pytestmark = providers.llama.mark +pytestmark = providers.llama.marks @pytest.fixture diff --git a/tests_integ/models/test_model_mistral.py b/tests_integ/models/test_model_mistral.py index 3b13e5911..364cd0921 100644 --- a/tests_integ/models/test_model_mistral.py +++ b/tests_integ/models/test_model_mistral.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the mistral api key -pytestmark = providers.mistral.mark +pytestmark = providers.mistral.marks @pytest.fixture() diff --git a/tests_integ/models/test_model_ollama.py b/tests_integ/models/test_model_ollama.py index 5b97bd2ef..9440c6b33 100644 --- a/tests_integ/models/test_model_ollama.py +++ b/tests_integ/models/test_model_ollama.py @@ -7,7 +7,7 @@ from tests_integ.models import providers # these tests only run if we have the ollama is running -pytestmark = providers.ollama.mark +pytestmark = providers.ollama.marks @pytest.fixture diff --git a/tests_integ/models/test_model_openai.py b/tests_integ/models/test_model_openai.py index 7054b222a..3f2919400 100644 --- a/tests_integ/models/test_model_openai.py +++ b/tests_integ/models/test_model_openai.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the openai api key -pytestmark = providers.openai.mark +pytestmark = providers.openai.marks @pytest.fixture diff --git a/tests_integ/models/test_model_writer.py b/tests_integ/models/test_model_writer.py index e715d3187..ca0a017c9 100644 --- a/tests_integ/models/test_model_writer.py +++ b/tests_integ/models/test_model_writer.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the writer api key -pytestmark = providers.writer.mark +pytestmark = providers.writer.marks @pytest.fixture diff --git a/tests_integ/test_summarizing_conversation_manager_integration.py b/tests_integ/test_summarizing_conversation_manager_integration.py index 719520b8d..451bf9b57 100644 --- a/tests_integ/test_summarizing_conversation_manager_integration.py +++ b/tests_integ/test_summarizing_conversation_manager_integration.py @@ -23,7 +23,7 @@ from strands.models.anthropic import AnthropicModel from tests_integ.models import providers -pytestmark = providers.anthropic.mark +pytestmark = providers.anthropic.marks @pytest.fixture From c1ddcb956037d8a8d9d20bcb16a28f85669f7d8a Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Tue, 22 Jul 2025 14:10:13 -0400 Subject: [PATCH 09/17] fix: increase reruns delay --- tests_integ/models/providers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_integ/models/providers.py b/tests_integ/models/providers.py index 0acdb7bf2..433a56430 100644 --- a/tests_integ/models/providers.py +++ b/tests_integ/models/providers.py @@ -37,7 +37,7 @@ def __init__( reason=f"{environment_variable} environment variable missing", ) if flaky: - self.marks = [skip_mark, pytest.mark.flaky(reruns=2, reruns_delay=5)] + self.marks = [skip_mark, pytest.mark.flaky(reruns=2, reruns_delay=10)] else: self.marks = [skip_mark] From 341ad82c863b3e2f987b36591c24e76df2f8af24 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Tue, 22 Jul 2025 14:11:44 -0400 Subject: [PATCH 10/17] fix: increase reruns delay --- tests_integ/models/providers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_integ/models/providers.py b/tests_integ/models/providers.py index 433a56430..4b18e2820 100644 --- a/tests_integ/models/providers.py +++ b/tests_integ/models/providers.py @@ -59,7 +59,7 @@ def __init__(self): except requests.exceptions.ConnectionError: pass - self.mark = mark.skipif( + self.marks = mark.skipif( not is_server_available, reason="Local Ollama endpoint not available at localhost:11434", ) From cd2b3ff1c74b186712383d566f17fd6687b505ee Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Tue, 22 Jul 2025 14:30:17 -0400 Subject: [PATCH 11/17] skip all anthropic tests --- tests_integ/models/providers.py | 19 ++----------------- tests_integ/models/test_conformance.py | 2 +- tests_integ/models/test_model_anthropic.py | 15 +++++++++++---- tests_integ/models/test_model_cohere.py | 2 +- tests_integ/models/test_model_llamaapi.py | 2 +- tests_integ/models/test_model_mistral.py | 2 +- tests_integ/models/test_model_ollama.py | 2 +- tests_integ/models/test_model_openai.py | 2 +- tests_integ/models/test_model_writer.py | 2 +- ...rizing_conversation_manager_integration.py | 2 +- 10 files changed, 21 insertions(+), 29 deletions(-) diff --git a/tests_integ/models/providers.py b/tests_integ/models/providers.py index 4b18e2820..d2ac148d3 100644 --- a/tests_integ/models/providers.py +++ b/tests_integ/models/providers.py @@ -5,7 +5,6 @@ import os from typing import Callable, Optional -import pytest import requests from pytest import mark @@ -27,19 +26,13 @@ def __init__( id: str, factory: Callable[[], Model], environment_variable: Optional[str] = None, - flaky: bool = False, ) -> None: self.id = id self.model_factory = factory - - skip_mark = mark.skipif( + self.mark = mark.skipif( environment_variable is not None and environment_variable not in os.environ, reason=f"{environment_variable} environment variable missing", ) - if flaky: - self.marks = [skip_mark, pytest.mark.flaky(reruns=2, reruns_delay=10)] - else: - self.marks = [skip_mark] def create_model(self) -> Model: return self.model_factory() @@ -59,19 +52,12 @@ def __init__(self): except requests.exceptions.ConnectionError: pass - self.marks = mark.skipif( + self.mark = mark.skipif( not is_server_available, reason="Local Ollama endpoint not available at localhost:11434", ) -""" -Because of infrequent burst usage Anthropic is occasionally failing tests with 529s. We retrying with delay to -avoid these false negatives. - -{'type': 'error', 'error': {'details': None, 'type': 'overloaded_error', 'message': 'Overloaded'}} -https://docs.anthropic.com/en/api/errors#http-errors -""" anthropic = ProviderInfo( id="anthropic", environment_variable="ANTHROPIC_API_KEY", @@ -82,7 +68,6 @@ def __init__(self): model_id="claude-3-7-sonnet-20250219", max_tokens=512, ), - flaky=True, ) bedrock = ProviderInfo(id="bedrock", factory=lambda: BedrockModel()) cohere = ProviderInfo( diff --git a/tests_integ/models/test_conformance.py b/tests_integ/models/test_conformance.py index fe58bc688..d9875bc07 100644 --- a/tests_integ/models/test_conformance.py +++ b/tests_integ/models/test_conformance.py @@ -9,7 +9,7 @@ def get_models(): pytest.param( provider_info, id=provider_info.id, # Adds the provider name to the test name - marks=provider_info.marks, # ignores tests that don't have the requirements + marks=provider_info.mark, # ignores tests that don't have the requirements ) for provider_info in all_providers ] diff --git a/tests_integ/models/test_model_anthropic.py b/tests_integ/models/test_model_anthropic.py index 658b2f2ed..bff3c265e 100644 --- a/tests_integ/models/test_model_anthropic.py +++ b/tests_integ/models/test_model_anthropic.py @@ -2,14 +2,21 @@ import pydantic import pytest - import strands from strands import Agent from strands.models.anthropic import AnthropicModel -from tests_integ.models import providers -# these tests only run if we have the anthropic api key -pytestmark = providers.anthropic.marks +""" +These tests only run if we have the anthropic api key + +Because of infrequent burst usage, Anthropic tests are unreliable, failing tests with 529s. +{'type': 'error', 'error': {'details': None, 'type': 'overloaded_error', 'message': 'Overloaded'}} +https://docs.anthropic.com/en/api/errors#http-errors +""" +pytestmark = pytest.skip( + "Because of infrequent burst usage, Anthropic tests are unreliable, failing with 529s", + allow_module_level=True +) @pytest.fixture diff --git a/tests_integ/models/test_model_cohere.py b/tests_integ/models/test_model_cohere.py index e452e57af..33fb1a8c6 100644 --- a/tests_integ/models/test_model_cohere.py +++ b/tests_integ/models/test_model_cohere.py @@ -8,7 +8,7 @@ from tests_integ.models import providers # these tests only run if we have the cohere api key -pytestmark = providers.cohere.marks +pytestmark = providers.cohere.mark @pytest.fixture diff --git a/tests_integ/models/test_model_llamaapi.py b/tests_integ/models/test_model_llamaapi.py index 2234600e3..b36a63a28 100644 --- a/tests_integ/models/test_model_llamaapi.py +++ b/tests_integ/models/test_model_llamaapi.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the llama api key -pytestmark = providers.llama.marks +pytestmark = providers.llama.mark @pytest.fixture diff --git a/tests_integ/models/test_model_mistral.py b/tests_integ/models/test_model_mistral.py index 364cd0921..3b13e5911 100644 --- a/tests_integ/models/test_model_mistral.py +++ b/tests_integ/models/test_model_mistral.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the mistral api key -pytestmark = providers.mistral.marks +pytestmark = providers.mistral.mark @pytest.fixture() diff --git a/tests_integ/models/test_model_ollama.py b/tests_integ/models/test_model_ollama.py index 9440c6b33..5b97bd2ef 100644 --- a/tests_integ/models/test_model_ollama.py +++ b/tests_integ/models/test_model_ollama.py @@ -7,7 +7,7 @@ from tests_integ.models import providers # these tests only run if we have the ollama is running -pytestmark = providers.ollama.marks +pytestmark = providers.ollama.mark @pytest.fixture diff --git a/tests_integ/models/test_model_openai.py b/tests_integ/models/test_model_openai.py index 3f2919400..7054b222a 100644 --- a/tests_integ/models/test_model_openai.py +++ b/tests_integ/models/test_model_openai.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the openai api key -pytestmark = providers.openai.marks +pytestmark = providers.openai.mark @pytest.fixture diff --git a/tests_integ/models/test_model_writer.py b/tests_integ/models/test_model_writer.py index ca0a017c9..e715d3187 100644 --- a/tests_integ/models/test_model_writer.py +++ b/tests_integ/models/test_model_writer.py @@ -9,7 +9,7 @@ from tests_integ.models import providers # these tests only run if we have the writer api key -pytestmark = providers.writer.marks +pytestmark = providers.writer.mark @pytest.fixture diff --git a/tests_integ/test_summarizing_conversation_manager_integration.py b/tests_integ/test_summarizing_conversation_manager_integration.py index 451bf9b57..719520b8d 100644 --- a/tests_integ/test_summarizing_conversation_manager_integration.py +++ b/tests_integ/test_summarizing_conversation_manager_integration.py @@ -23,7 +23,7 @@ from strands.models.anthropic import AnthropicModel from tests_integ.models import providers -pytestmark = providers.anthropic.marks +pytestmark = providers.anthropic.mark @pytest.fixture From 7d5bcbfda1256831d64fea800784fd2e2f134eed Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Tue, 22 Jul 2025 15:17:29 -0400 Subject: [PATCH 12/17] linting --- tests_integ/models/test_model_anthropic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests_integ/models/test_model_anthropic.py b/tests_integ/models/test_model_anthropic.py index bff3c265e..cc013e4b4 100644 --- a/tests_integ/models/test_model_anthropic.py +++ b/tests_integ/models/test_model_anthropic.py @@ -14,8 +14,7 @@ https://docs.anthropic.com/en/api/errors#http-errors """ pytestmark = pytest.skip( - "Because of infrequent burst usage, Anthropic tests are unreliable, failing with 529s", - allow_module_level=True + "Because of infrequent burst usage, Anthropic tests are unreliable, failing with 529s", allow_module_level=True ) From d4a67d91edb54bd555ea10e443b4b5cf7f403f75 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Tue, 22 Jul 2025 15:18:40 -0400 Subject: [PATCH 13/17] linting --- tests_integ/models/test_model_anthropic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests_integ/models/test_model_anthropic.py b/tests_integ/models/test_model_anthropic.py index cc013e4b4..62a95d06d 100644 --- a/tests_integ/models/test_model_anthropic.py +++ b/tests_integ/models/test_model_anthropic.py @@ -2,6 +2,7 @@ import pydantic import pytest + import strands from strands import Agent from strands.models.anthropic import AnthropicModel From 7c0bbd3a01d1ea327608b9e9e70f9a3ca7da730d Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Thu, 23 Oct 2025 11:35:47 -0400 Subject: [PATCH 14/17] Create pr-sizer.yml --- .github/workflows/pr-sizer.yml | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 .github/workflows/pr-sizer.yml diff --git a/.github/workflows/pr-sizer.yml b/.github/workflows/pr-sizer.yml new file mode 100644 index 000000000..1ed1c12a5 --- /dev/null +++ b/.github/workflows/pr-sizer.yml @@ -0,0 +1,75 @@ +name: PR Size Labeler + +on: + pull_request_target: + branches: main + +jobs: + authorization-check: + permissions: read-all + runs-on: ubuntu-latest + outputs: + approval-env: ${{ steps.collab-check.outputs.result }} + steps: + - name: Collaborator Check + uses: actions/github-script@v8 + id: collab-check + with: + result-encoding: string + script: | + try { + const permissionResponse = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: context.payload.pull_request.user.login, + }); + const permission = permissionResponse.data.permission; + const hasWriteAccess = ['write', 'admin'].includes(permission); + if (!hasWriteAccess) { + console.log(`User ${context.payload.pull_request.user.login} does not have write access`); + return "manual-approval" + } else { + console.log(`Verified ${context.payload.pull_request.user.login} has write access. Auto approving.`) + return "auto-approve" + } + } catch (error) { + console.log(`${context.payload.pull_request.user.login} does not have write access.`) + return "manual-approval" + } + + size-label: + runs-on: ubuntu-latest + needs: authorization-check + if: needs.authorization-check.outputs.approval-env == 'auto-approve' + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Calculate PR size and apply label + uses: actions/github-script@v8 + with: + script: | + const { execSync } = require('child_process'); + const additions = parseInt(execSync('git diff --numstat HEAD~1 | awk "{sum += $1} END {print sum}"').toString().trim()) || 0; + const deletions = parseInt(execSync('git diff --numstat HEAD~1 | awk "{sum += $2} END {print sum}"').toString().trim()) || 0; + const totalChanges = additions + deletions; + + let sizeLabel; + if (totalChanges <= 20) sizeLabel = 'size/xs'; + else if (totalChanges <= 100) sizeLabel = 'size/s'; + else if (totalChanges <= 500) sizeLabel = 'size/m'; + else if (totalChanges <= 1000) sizeLabel = 'size/l'; + else { + sizeLabel = 'size/xl'; + core.setFailed(`PR is too large (${totalChanges} lines). Please split into smaller PRs.`); + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: [sizeLabel] + }); From 4ba455ff5364b09ec297274191f0b3779a126935 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Thu, 23 Oct 2025 11:36:47 -0400 Subject: [PATCH 15/17] Update pr-sizer.yml --- .github/workflows/pr-sizer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-sizer.yml b/.github/workflows/pr-sizer.yml index 1ed1c12a5..98158a119 100644 --- a/.github/workflows/pr-sizer.yml +++ b/.github/workflows/pr-sizer.yml @@ -2,7 +2,7 @@ name: PR Size Labeler on: pull_request_target: - branches: main + branches: integ-testing jobs: authorization-check: From 8a477c3ee00b29ca340bb638eebd97f6a821a22f Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Thu, 23 Oct 2025 11:42:31 -0400 Subject: [PATCH 16/17] Update pr-sizer.yml --- .github/workflows/pr-sizer.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr-sizer.yml b/.github/workflows/pr-sizer.yml index 98158a119..00e1ac553 100644 --- a/.github/workflows/pr-sizer.yml +++ b/.github/workflows/pr-sizer.yml @@ -2,7 +2,7 @@ name: PR Size Labeler on: pull_request_target: - branches: integ-testing + branches: main jobs: authorization-check: @@ -52,10 +52,8 @@ jobs: uses: actions/github-script@v8 with: script: | - const { execSync } = require('child_process'); - const additions = parseInt(execSync('git diff --numstat HEAD~1 | awk "{sum += $1} END {print sum}"').toString().trim()) || 0; - const deletions = parseInt(execSync('git diff --numstat HEAD~1 | awk "{sum += $2} END {print sum}"').toString().trim()) || 0; - const totalChanges = additions + deletions; + const pr = context.payload.pull_request; + const totalChanges = pr.additions + pr.deletions; let sizeLabel; if (totalChanges <= 20) sizeLabel = 'size/xs'; From 60f8463c16ec0afa8d01b5fc769a4cffbe6c86d4 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Thu, 23 Oct 2025 11:43:17 -0400 Subject: [PATCH 17/17] Update pr-sizer.yml --- .github/workflows/pr-sizer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-sizer.yml b/.github/workflows/pr-sizer.yml index 00e1ac553..afdaa5d47 100644 --- a/.github/workflows/pr-sizer.yml +++ b/.github/workflows/pr-sizer.yml @@ -2,7 +2,7 @@ name: PR Size Labeler on: pull_request_target: - branches: main + branches: integ-testing jobs: authorization-check: