From 163a7e9de0e7c8329471fba5fd2744f70de02cac Mon Sep 17 00:00:00 2001 From: Strands Agent <217235299+strands-agent@users.noreply.github.com> Date: Mon, 5 Jan 2026 20:26:46 +0000 Subject: [PATCH 1/2] feat: add concurrent S3 message reading to list_messages Improves performance when listing messages by reading S3 objects concurrently using asyncio.to_thread and asyncio.gather. The list_messages method now spawns a threaded async event loop to perform concurrent reads while maintaining the synchronous external API. This change provides significant performance benefits for sessions with many messages without requiring any changes to consuming code or tests. --- src/strands/session/s3_session_manager.py | 87 +++++++++++++---------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/src/strands/session/s3_session_manager.py b/src/strands/session/s3_session_manager.py index 7d081cf09..cac01a749 100644 --- a/src/strands/session/s3_session_manager.py +++ b/src/strands/session/s3_session_manager.py @@ -1,5 +1,6 @@ """S3-based session manager for cloud storage.""" +import asyncio import json import logging from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast @@ -9,6 +10,7 @@ from botocore.exceptions import ClientError from .. import _identifier +from .._async import run_async from ..types.exceptions import SessionException from ..types.session import Session, SessionAgent, SessionMessage from .repository_session_manager import RepositorySessionManager @@ -259,45 +261,56 @@ def update_message(self, session_id: str, agent_id: str, session_message: Sessio def list_messages( self, session_id: str, agent_id: str, limit: Optional[int] = None, offset: int = 0, **kwargs: Any ) -> List[SessionMessage]: - """List messages for an agent with pagination from S3.""" - messages_prefix = f"{self._get_agent_path(session_id, agent_id)}messages/" - try: - paginator = self.client.get_paginator("list_objects_v2") - pages = paginator.paginate(Bucket=self.bucket, Prefix=messages_prefix) + """List messages for an agent with pagination from S3. - # Collect all message keys and extract their indices - message_index_keys: list[tuple[int, str]] = [] - for page in pages: - if "Contents" in page: - for obj in page["Contents"]: - key = obj["Key"] - if key.endswith(".json") and MESSAGE_PREFIX in key: - # Extract the filename part from the full S3 key - filename = key.split("/")[-1] - # Extract index from message_.json format - index = int(filename[len(MESSAGE_PREFIX) : -5]) # Remove prefix and .json suffix - message_index_keys.append((index, key)) - - # Sort by index and extract just the keys - message_keys = [k for _, k in sorted(message_index_keys)] - - # Apply pagination to keys before loading content - if limit is not None: - message_keys = message_keys[offset : offset + limit] - else: - message_keys = message_keys[offset:] - - # Load only the required message objects - messages: List[SessionMessage] = [] - for key in message_keys: - message_data = self._read_s3_object(key) - if message_data: - messages.append(SessionMessage.from_dict(message_data)) - - return messages + Uses concurrent async reading for improved performance when reading multiple messages. + """ - except ClientError as e: - raise SessionException(f"S3 error reading messages: {e}") from e + async def async_list_messages() -> List[SessionMessage]: + messages_prefix = f"{self._get_agent_path(session_id, agent_id)}messages/" + try: + # List message keys using sync client (listing is fast) + paginator = self.client.get_paginator("list_objects_v2") + pages = paginator.paginate(Bucket=self.bucket, Prefix=messages_prefix) + + # Collect all message keys and extract their indices + message_index_keys: list[tuple[int, str]] = [] + for page in pages: + if "Contents" in page: + for obj in page["Contents"]: + key = obj["Key"] + if key.endswith(".json") and MESSAGE_PREFIX in key: + # Extract the filename part from the full S3 key + filename = key.split("/")[-1] + # Extract index from message_.json format + index = int(filename[len(MESSAGE_PREFIX) : -5]) # Remove prefix and .json suffix + message_index_keys.append((index, key)) + + # Sort by index and extract just the keys + message_keys = [k for _, k in sorted(message_index_keys)] + + # Apply pagination to keys before loading content + if limit is not None: + message_keys = message_keys[offset : offset + limit] + else: + message_keys = message_keys[offset:] + + # Read all message objects concurrently using asyncio.to_thread + tasks = [asyncio.to_thread(self._read_s3_object, key) for key in message_keys] + message_data_list = await asyncio.gather(*tasks) + + # Parse messages and filter out None values + messages: List[SessionMessage] = [] + for message_data in message_data_list: + if message_data: + messages.append(SessionMessage.from_dict(message_data)) + + return messages + + except ClientError as e: + raise SessionException(f"S3 error reading messages: {e}") from e + + return run_async(async_list_messages) def _get_multi_agent_path(self, session_id: str, multi_agent_id: str) -> str: """Get multi-agent S3 prefix.""" From c2f94e94563f258728c34bc95084feda83a38d84 Mon Sep 17 00:00:00 2001 From: Strands Agent <217235299+strands-agent@users.noreply.github.com> Date: Mon, 5 Jan 2026 20:28:20 +0000 Subject: [PATCH 2/2] Additional changes from write operations --- =2.11.0 | 0 aiobotocore_install.log | 34 +++++++++++++++++++ dev_install_output.log | 54 +++++++++++++++++++++++++++++++ full_test_output.log | 47 +++++++++++++++++++++++++++ install_output.log | 72 +++++++++++++++++++++++++++++++++++++++++ lint_install.log | 21 ++++++++++++ test_output.log | 47 +++++++++++++++++++++++++++ 7 files changed, 275 insertions(+) create mode 100644 =2.11.0 create mode 100644 aiobotocore_install.log create mode 100644 dev_install_output.log create mode 100644 full_test_output.log create mode 100644 install_output.log create mode 100644 lint_install.log create mode 100644 test_output.log diff --git a/=2.11.0 b/=2.11.0 new file mode 100644 index 000000000..e69de29bb diff --git a/aiobotocore_install.log b/aiobotocore_install.log new file mode 100644 index 000000000..3ccd72295 --- /dev/null +++ b/aiobotocore_install.log @@ -0,0 +1,34 @@ +Collecting aiobotocore + Downloading aiobotocore-3.1.0-py3-none-any.whl.metadata (26 kB) +Requirement already satisfied: aiohttp<4.0.0,>=3.12.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiobotocore) (3.13.3) +Collecting aioitertools<1.0.0,>=0.5.1 (from aiobotocore) + Downloading aioitertools-0.13.0-py3-none-any.whl.metadata (3.3 kB) +Collecting botocore<1.42.20,>=1.41.0 (from aiobotocore) + Downloading botocore-1.42.19-py3-none-any.whl.metadata (5.9 kB) +Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiobotocore) (2.9.0.post0) +Requirement already satisfied: jmespath<2.0.0,>=0.7.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiobotocore) (1.0.1) +Requirement already satisfied: multidict<7.0.0,>=6.0.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiobotocore) (6.7.0) +Requirement already satisfied: wrapt<3.0.0,>=1.10.10 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiobotocore) (1.17.3) +Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiohttp<4.0.0,>=3.12.0->aiobotocore) (2.6.1) +Requirement already satisfied: aiosignal>=1.4.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiohttp<4.0.0,>=3.12.0->aiobotocore) (1.4.0) +Requirement already satisfied: attrs>=17.3.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiohttp<4.0.0,>=3.12.0->aiobotocore) (25.4.0) +Requirement already satisfied: frozenlist>=1.1.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiohttp<4.0.0,>=3.12.0->aiobotocore) (1.8.0) +Requirement already satisfied: propcache>=0.2.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiohttp<4.0.0,>=3.12.0->aiobotocore) (0.4.1) +Requirement already satisfied: yarl<2.0,>=1.17.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from aiohttp<4.0.0,>=3.12.0->aiobotocore) (1.22.0) +Requirement already satisfied: urllib3!=2.2.0,<3,>=1.25.4 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from botocore<1.42.20,>=1.41.0->aiobotocore) (2.6.2) +Requirement already satisfied: six>=1.5 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from python-dateutil<3.0.0,>=2.1->aiobotocore) (1.17.0) +Requirement already satisfied: idna>=2.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from yarl<2.0,>=1.17.0->aiohttp<4.0.0,>=3.12.0->aiobotocore) (3.11) +Downloading aiobotocore-3.1.0-py3-none-any.whl (87 kB) +Downloading aioitertools-0.13.0-py3-none-any.whl (24 kB) +Downloading botocore-1.42.19-py3-none-any.whl (14.6 MB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.6/14.6 MB 126.9 MB/s 0:00:00 +Installing collected packages: aioitertools, botocore, aiobotocore + Attempting uninstall: botocore + Found existing installation: botocore 1.42.21 + Uninstalling botocore-1.42.21: + Successfully uninstalled botocore-1.42.21 + +ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. +strands-agents-tools 0.2.19 requires strands-agents>=1.0.0, but you have strands-agents 0.1.dev1+g695ca6654 which is incompatible. +boto3 1.42.21 requires botocore<1.43.0,>=1.42.21, but you have botocore 1.42.19 which is incompatible. +Successfully installed aiobotocore-3.1.0 aioitertools-0.13.0 botocore-1.42.19 diff --git a/dev_install_output.log b/dev_install_output.log new file mode 100644 index 000000000..e74452f77 --- /dev/null +++ b/dev_install_output.log @@ -0,0 +1,54 @@ +Collecting pytest + Downloading pytest-9.0.2-py3-none-any.whl.metadata (7.6 kB) +Collecting pytest-asyncio + Downloading pytest_asyncio-1.3.0-py3-none-any.whl.metadata (4.1 kB) +Collecting moto + Downloading moto-5.1.19-py3-none-any.whl.metadata (12 kB) +Requirement already satisfied: boto3 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (1.42.21) +Collecting iniconfig>=1.0.1 (from pytest) + Downloading iniconfig-2.3.0-py3-none-any.whl.metadata (2.5 kB) +Requirement already satisfied: packaging>=22 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from pytest) (25.0) +Collecting pluggy<2,>=1.5 (from pytest) + Using cached pluggy-1.6.0-py3-none-any.whl.metadata (4.8 kB) +Requirement already satisfied: pygments>=2.7.2 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from pytest) (2.19.2) +Requirement already satisfied: botocore!=1.35.45,!=1.35.46,>=1.20.88 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from moto) (1.42.21) +Requirement already satisfied: cryptography>=35.0.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from moto) (46.0.3) +Requirement already satisfied: requests>=2.5 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from moto) (2.32.5) +Collecting xmltodict (from moto) + Downloading xmltodict-1.0.2-py3-none-any.whl.metadata (15 kB) +Collecting werkzeug!=2.2.0,!=2.2.1,>=0.5 (from moto) + Downloading werkzeug-3.1.4-py3-none-any.whl.metadata (4.0 kB) +Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from moto) (2.9.0.post0) +Collecting responses!=0.25.5,>=0.15.0 (from moto) + Downloading responses-0.25.8-py3-none-any.whl.metadata (47 kB) +Collecting Jinja2>=2.10.1 (from moto) + Downloading jinja2-3.1.6-py3-none-any.whl.metadata (2.9 kB) +Requirement already satisfied: six>=1.5 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from python-dateutil<3.0.0,>=2.1->moto) (1.17.0) +Requirement already satisfied: jmespath<2.0.0,>=0.7.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from boto3) (1.0.1) +Requirement already satisfied: s3transfer<0.17.0,>=0.16.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from boto3) (0.16.0) +Requirement already satisfied: urllib3!=2.2.0,<3,>=1.25.4 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from botocore!=1.35.45,!=1.35.46,>=1.20.88->moto) (2.6.2) +Requirement already satisfied: cffi>=2.0.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from cryptography>=35.0.0->moto) (2.0.0) +Requirement already satisfied: pycparser in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from cffi>=2.0.0->cryptography>=35.0.0->moto) (2.23) +Collecting MarkupSafe>=2.0 (from Jinja2>=2.10.1->moto) + Downloading markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (2.7 kB) +Requirement already satisfied: charset_normalizer<4,>=2 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from requests>=2.5->moto) (3.4.4) +Requirement already satisfied: idna<4,>=2.5 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from requests>=2.5->moto) (3.11) +Requirement already satisfied: certifi>=2017.4.17 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from requests>=2.5->moto) (2026.1.4) +Collecting pyyaml (from responses!=0.25.5,>=0.15.0->moto) + Downloading pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (2.4 kB) +Downloading pytest-9.0.2-py3-none-any.whl (374 kB) +Using cached pluggy-1.6.0-py3-none-any.whl (20 kB) +Downloading pytest_asyncio-1.3.0-py3-none-any.whl (15 kB) +Downloading moto-5.1.19-py3-none-any.whl (6.5 MB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.5/6.5 MB 97.8 MB/s 0:00:00 +Downloading iniconfig-2.3.0-py3-none-any.whl (7.5 kB) +Downloading jinja2-3.1.6-py3-none-any.whl (134 kB) +Downloading markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB) +Downloading responses-0.25.8-py3-none-any.whl (34 kB) +Downloading werkzeug-3.1.4-py3-none-any.whl (224 kB) +Downloading pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (801 kB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 801.6/801.6 kB 97.5 MB/s 0:00:00 +Downloading xmltodict-1.0.2-py3-none-any.whl (13 kB) +Installing collected packages: xmltodict, pyyaml, pluggy, MarkupSafe, iniconfig, werkzeug, responses, pytest, Jinja2, pytest-asyncio, moto + +Successfully installed Jinja2-3.1.6 MarkupSafe-3.0.3 iniconfig-2.3.0 moto-5.1.19 pluggy-1.6.0 pytest-9.0.2 pytest-asyncio-1.3.0 pyyaml-6.0.3 responses-0.25.8 werkzeug-3.1.4 xmltodict-1.0.2 diff --git a/full_test_output.log b/full_test_output.log new file mode 100644 index 000000000..4de5f4d0e --- /dev/null +++ b/full_test_output.log @@ -0,0 +1,47 @@ +============================= test session starts ============================== +platform linux -- Python 3.13.11, pytest-9.0.2, pluggy-1.6.0 -- /opt/hostedtoolcache/Python/3.13.11/x64/bin/python +cachedir: .pytest_cache +rootdir: /home/runner/work/sdk-python/sdk-python +configfile: pyproject.toml +plugins: anyio-4.12.0, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +collecting ... collected 36 items + +tests/strands/session/test_s3_session_manager.py::test_init_s3_session_manager PASSED [ 2%] +tests/strands/session/test_s3_session_manager.py::test_init_s3_session_manager_with_config PASSED [ 5%] +tests/strands/session/test_s3_session_manager.py::test_init_s3_session_manager_with_existing_user_agent PASSED [ 8%] +tests/strands/session/test_s3_session_manager.py::test_create_session PASSED [ 11%] +tests/strands/session/test_s3_session_manager.py::test_create_session_already_exists PASSED [ 13%] +tests/strands/session/test_s3_session_manager.py::test_read_session PASSED [ 16%] +tests/strands/session/test_s3_session_manager.py::test_read_nonexistent_session PASSED [ 19%] +tests/strands/session/test_s3_session_manager.py::test_delete_session PASSED [ 22%] +tests/strands/session/test_s3_session_manager.py::test_create_agent PASSED [ 25%] +tests/strands/session/test_s3_session_manager.py::test_read_agent PASSED [ 27%] +tests/strands/session/test_s3_session_manager.py::test_read_nonexistent_agent PASSED [ 30%] +tests/strands/session/test_s3_session_manager.py::test_update_agent PASSED [ 33%] +tests/strands/session/test_s3_session_manager.py::test_update_nonexistent_agent PASSED [ 36%] +tests/strands/session/test_s3_session_manager.py::test_create_message PASSED [ 38%] +tests/strands/session/test_s3_session_manager.py::test_read_message PASSED [ 41%] +tests/strands/session/test_s3_session_manager.py::test_read_nonexistent_message PASSED [ 44%] +tests/strands/session/test_s3_session_manager.py::test_list_messages_all PASSED [ 47%] +tests/strands/session/test_s3_session_manager.py::test_list_messages_with_pagination PASSED [ 50%] +tests/strands/session/test_s3_session_manager.py::test_update_message PASSED [ 52%] +tests/strands/session/test_s3_session_manager.py::test_update_nonexistent_message PASSED [ 55%] +tests/strands/session/test_s3_session_manager.py::test__get_session_path_invalid_session_id[a/../b] PASSED [ 58%] +tests/strands/session/test_s3_session_manager.py::test__get_session_path_invalid_session_id[a/b] PASSED [ 61%] +tests/strands/session/test_s3_session_manager.py::test__get_agent_path_invalid_agent_id[a/../b] PASSED [ 63%] +tests/strands/session/test_s3_session_manager.py::test__get_agent_path_invalid_agent_id[a/b] PASSED [ 66%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[../../../secret] PASSED [ 69%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[../../attack] PASSED [ 72%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[../escape] PASSED [ 75%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[path/traversal] PASSED [ 77%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[not_an_int] PASSED [ 80%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[None] PASSED [ 83%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[message_id6] PASSED [ 86%] +tests/strands/session/test_s3_session_manager.py::test_create_multi_agent PASSED [ 88%] +tests/strands/session/test_s3_session_manager.py::test_read_multi_agent PASSED [ 91%] +tests/strands/session/test_s3_session_manager.py::test_read_nonexistent_multi_agent PASSED [ 94%] +tests/strands/session/test_s3_session_manager.py::test_update_multi_agent PASSED [ 97%] +tests/strands/session/test_s3_session_manager.py::test_update_nonexistent_multi_agent PASSED [100%] + +============================== 36 passed in 8.10s ============================== diff --git a/install_output.log b/install_output.log new file mode 100644 index 000000000..8aea287c2 --- /dev/null +++ b/install_output.log @@ -0,0 +1,72 @@ +Obtaining file:///home/runner/work/sdk-python/sdk-python + Installing build dependencies: started + Installing build dependencies: finished with status 'done' + Checking if build backend supports build_editable: started + Checking if build backend supports build_editable: finished with status 'done' + Getting requirements to build editable: started + Getting requirements to build editable: finished with status 'done' + Installing backend dependencies: started + Installing backend dependencies: finished with status 'done' + Preparing editable metadata (pyproject.toml): started + Preparing editable metadata (pyproject.toml): finished with status 'done' +Requirement already satisfied: boto3<2.0.0,>=1.26.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (1.42.21) +Requirement already satisfied: botocore<2.0.0,>=1.29.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (1.42.21) +Requirement already satisfied: docstring-parser<1.0,>=0.15 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (0.17.0) +Requirement already satisfied: jsonschema<5.0.0,>=4.0.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (4.25.1) +Requirement already satisfied: mcp<2.0.0,>=1.11.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (1.25.0) +Requirement already satisfied: opentelemetry-api<2.0.0,>=1.30.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (1.39.1) +Requirement already satisfied: opentelemetry-instrumentation-threading<1.00b0,>=0.51b0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (0.60b1) +Requirement already satisfied: opentelemetry-sdk<2.0.0,>=1.30.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (1.39.1) +Requirement already satisfied: pydantic<3.0.0,>=2.4.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (2.12.5) +Requirement already satisfied: typing-extensions<5.0.0,>=4.13.2 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (4.15.0) +Requirement already satisfied: watchdog<7.0.0,>=6.0.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from strands-agents==0.1.dev1+g695ca6654) (6.0.0) +Requirement already satisfied: jmespath<2.0.0,>=0.7.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from boto3<2.0.0,>=1.26.0->strands-agents==0.1.dev1+g695ca6654) (1.0.1) +Requirement already satisfied: s3transfer<0.17.0,>=0.16.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from boto3<2.0.0,>=1.26.0->strands-agents==0.1.dev1+g695ca6654) (0.16.0) +Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from botocore<2.0.0,>=1.29.0->strands-agents==0.1.dev1+g695ca6654) (2.9.0.post0) +Requirement already satisfied: urllib3!=2.2.0,<3,>=1.25.4 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from botocore<2.0.0,>=1.29.0->strands-agents==0.1.dev1+g695ca6654) (2.6.2) +Requirement already satisfied: attrs>=22.2.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from jsonschema<5.0.0,>=4.0.0->strands-agents==0.1.dev1+g695ca6654) (25.4.0) +Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from jsonschema<5.0.0,>=4.0.0->strands-agents==0.1.dev1+g695ca6654) (2025.9.1) +Requirement already satisfied: referencing>=0.28.4 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from jsonschema<5.0.0,>=4.0.0->strands-agents==0.1.dev1+g695ca6654) (0.37.0) +Requirement already satisfied: rpds-py>=0.7.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from jsonschema<5.0.0,>=4.0.0->strands-agents==0.1.dev1+g695ca6654) (0.30.0) +Requirement already satisfied: anyio>=4.5 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (4.12.0) +Requirement already satisfied: httpx-sse>=0.4 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (0.4.3) +Requirement already satisfied: httpx>=0.27.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (0.28.1) +Requirement already satisfied: pydantic-settings>=2.5.2 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (2.12.0) +Requirement already satisfied: pyjwt>=2.10.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from pyjwt[crypto]>=2.10.1->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (2.10.1) +Requirement already satisfied: python-multipart>=0.0.9 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (0.0.21) +Requirement already satisfied: sse-starlette>=1.6.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (3.1.2) +Requirement already satisfied: starlette>=0.27 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (0.50.0) +Requirement already satisfied: typing-inspection>=0.4.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (0.4.2) +Requirement already satisfied: uvicorn>=0.31.1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (0.40.0) +Requirement already satisfied: importlib-metadata<8.8.0,>=6.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from opentelemetry-api<2.0.0,>=1.30.0->strands-agents==0.1.dev1+g695ca6654) (8.7.1) +Requirement already satisfied: zipp>=3.20 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from importlib-metadata<8.8.0,>=6.0->opentelemetry-api<2.0.0,>=1.30.0->strands-agents==0.1.dev1+g695ca6654) (3.23.0) +Requirement already satisfied: opentelemetry-instrumentation==0.60b1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from opentelemetry-instrumentation-threading<1.00b0,>=0.51b0->strands-agents==0.1.dev1+g695ca6654) (0.60b1) +Requirement already satisfied: wrapt<2.0.0,>=1.0.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from opentelemetry-instrumentation-threading<1.00b0,>=0.51b0->strands-agents==0.1.dev1+g695ca6654) (1.17.3) +Requirement already satisfied: opentelemetry-semantic-conventions==0.60b1 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from opentelemetry-instrumentation==0.60b1->opentelemetry-instrumentation-threading<1.00b0,>=0.51b0->strands-agents==0.1.dev1+g695ca6654) (0.60b1) +Requirement already satisfied: packaging>=18.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from opentelemetry-instrumentation==0.60b1->opentelemetry-instrumentation-threading<1.00b0,>=0.51b0->strands-agents==0.1.dev1+g695ca6654) (25.0) +Requirement already satisfied: annotated-types>=0.6.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from pydantic<3.0.0,>=2.4.0->strands-agents==0.1.dev1+g695ca6654) (0.7.0) +Requirement already satisfied: pydantic-core==2.41.5 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from pydantic<3.0.0,>=2.4.0->strands-agents==0.1.dev1+g695ca6654) (2.41.5) +Requirement already satisfied: six>=1.5 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from python-dateutil<3.0.0,>=2.1->botocore<2.0.0,>=1.29.0->strands-agents==0.1.dev1+g695ca6654) (1.17.0) +Requirement already satisfied: idna>=2.8 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from anyio>=4.5->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (3.11) +Requirement already satisfied: certifi in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from httpx>=0.27.1->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (2026.1.4) +Requirement already satisfied: httpcore==1.* in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from httpx>=0.27.1->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (1.0.9) +Requirement already satisfied: h11>=0.16 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from httpcore==1.*->httpx>=0.27.1->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (0.16.0) +Requirement already satisfied: python-dotenv>=0.21.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from pydantic-settings>=2.5.2->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (1.2.1) +Requirement already satisfied: cryptography>=3.4.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from pyjwt[crypto]>=2.10.1->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (46.0.3) +Requirement already satisfied: cffi>=2.0.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from cryptography>=3.4.0->pyjwt[crypto]>=2.10.1->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (2.0.0) +Requirement already satisfied: pycparser in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from cffi>=2.0.0->cryptography>=3.4.0->pyjwt[crypto]>=2.10.1->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (2.23) +Requirement already satisfied: click>=7.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from uvicorn>=0.31.1->mcp<2.0.0,>=1.11.0->strands-agents==0.1.dev1+g695ca6654) (8.3.1) +Building wheels for collected packages: strands-agents + Building editable for strands-agents (pyproject.toml): started + Building editable for strands-agents (pyproject.toml): finished with status 'done' + Created wheel for strands-agents: filename=strands_agents-0.1.dev1+g695ca6654-py3-none-any.whl size=10022 sha256=99f2c3ed0190d2d6d4558208e27db0ee7a370d327368839c5b2b1dab4d3ff7be + Stored in directory: /tmp/pip-ephem-wheel-cache-tcbl7msa/wheels/94/60/63/fc2d04fbd73b5e7d5ee8ee2c7af924f44becb4b085a98d9503 +Successfully built strands-agents +Installing collected packages: strands-agents + Attempting uninstall: strands-agents + Found existing installation: strands-agents 1.21.0 + Uninstalling strands-agents-1.21.0: + Successfully uninstalled strands-agents-1.21.0 +ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. +strands-agents-tools 0.2.19 requires strands-agents>=1.0.0, but you have strands-agents 0.1.dev1+g695ca6654 which is incompatible. +Successfully installed strands-agents-0.1.dev1+g695ca6654 diff --git a/lint_install.log b/lint_install.log new file mode 100644 index 000000000..f349ff8d4 --- /dev/null +++ b/lint_install.log @@ -0,0 +1,21 @@ +Collecting ruff + Downloading ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (26 kB) +Collecting mypy + Downloading mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (2.2 kB) +Requirement already satisfied: typing_extensions>=4.6.0 in /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages (from mypy) (4.15.0) +Collecting mypy_extensions>=1.0.0 (from mypy) + Downloading mypy_extensions-1.1.0-py3-none-any.whl.metadata (1.1 kB) +Collecting pathspec>=0.9.0 (from mypy) + Using cached pathspec-0.12.1-py3-none-any.whl.metadata (21 kB) +Collecting librt>=0.6.2 (from mypy) + Downloading librt-0.7.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (1.3 kB) +Downloading ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (14.2 MB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 14.2/14.2 MB 154.0 MB/s 0:00:00 +Downloading mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (13.6 MB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.6/13.6 MB 225.8 MB/s 0:00:00 +Downloading librt-0.7.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (189 kB) +Downloading mypy_extensions-1.1.0-py3-none-any.whl (5.0 kB) +Using cached pathspec-0.12.1-py3-none-any.whl (31 kB) +Installing collected packages: ruff, pathspec, mypy_extensions, librt, mypy + +Successfully installed librt-0.7.7 mypy-1.19.1 mypy_extensions-1.1.0 pathspec-0.12.1 ruff-0.14.10 diff --git a/test_output.log b/test_output.log new file mode 100644 index 000000000..8160e1d15 --- /dev/null +++ b/test_output.log @@ -0,0 +1,47 @@ +============================= test session starts ============================== +platform linux -- Python 3.13.11, pytest-9.0.2, pluggy-1.6.0 -- /opt/hostedtoolcache/Python/3.13.11/x64/bin/python +cachedir: .pytest_cache +rootdir: /home/runner/work/sdk-python/sdk-python +configfile: pyproject.toml +plugins: anyio-4.12.0, asyncio-1.3.0 +asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function +collecting ... collected 36 items + +tests/strands/session/test_s3_session_manager.py::test_init_s3_session_manager PASSED [ 2%] +tests/strands/session/test_s3_session_manager.py::test_init_s3_session_manager_with_config PASSED [ 5%] +tests/strands/session/test_s3_session_manager.py::test_init_s3_session_manager_with_existing_user_agent PASSED [ 8%] +tests/strands/session/test_s3_session_manager.py::test_create_session PASSED [ 11%] +tests/strands/session/test_s3_session_manager.py::test_create_session_already_exists PASSED [ 13%] +tests/strands/session/test_s3_session_manager.py::test_read_session PASSED [ 16%] +tests/strands/session/test_s3_session_manager.py::test_read_nonexistent_session PASSED [ 19%] +tests/strands/session/test_s3_session_manager.py::test_delete_session PASSED [ 22%] +tests/strands/session/test_s3_session_manager.py::test_create_agent PASSED [ 25%] +tests/strands/session/test_s3_session_manager.py::test_read_agent PASSED [ 27%] +tests/strands/session/test_s3_session_manager.py::test_read_nonexistent_agent PASSED [ 30%] +tests/strands/session/test_s3_session_manager.py::test_update_agent PASSED [ 33%] +tests/strands/session/test_s3_session_manager.py::test_update_nonexistent_agent PASSED [ 36%] +tests/strands/session/test_s3_session_manager.py::test_create_message PASSED [ 38%] +tests/strands/session/test_s3_session_manager.py::test_read_message PASSED [ 41%] +tests/strands/session/test_s3_session_manager.py::test_read_nonexistent_message PASSED [ 44%] +tests/strands/session/test_s3_session_manager.py::test_list_messages_all PASSED [ 47%] +tests/strands/session/test_s3_session_manager.py::test_list_messages_with_pagination PASSED [ 50%] +tests/strands/session/test_s3_session_manager.py::test_update_message PASSED [ 52%] +tests/strands/session/test_s3_session_manager.py::test_update_nonexistent_message PASSED [ 55%] +tests/strands/session/test_s3_session_manager.py::test__get_session_path_invalid_session_id[a/../b] PASSED [ 58%] +tests/strands/session/test_s3_session_manager.py::test__get_session_path_invalid_session_id[a/b] PASSED [ 61%] +tests/strands/session/test_s3_session_manager.py::test__get_agent_path_invalid_agent_id[a/../b] PASSED [ 63%] +tests/strands/session/test_s3_session_manager.py::test__get_agent_path_invalid_agent_id[a/b] PASSED [ 66%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[../../../secret] PASSED [ 69%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[../../attack] PASSED [ 72%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[../escape] PASSED [ 75%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[path/traversal] PASSED [ 77%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[not_an_int] PASSED [ 80%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[None] PASSED [ 83%] +tests/strands/session/test_s3_session_manager.py::test__get_message_path_invalid_message_id[message_id6] PASSED [ 86%] +tests/strands/session/test_s3_session_manager.py::test_create_multi_agent PASSED [ 88%] +tests/strands/session/test_s3_session_manager.py::test_read_multi_agent PASSED [ 91%] +tests/strands/session/test_s3_session_manager.py::test_read_nonexistent_multi_agent PASSED [ 94%] +tests/strands/session/test_s3_session_manager.py::test_update_multi_agent PASSED [ 97%] +tests/strands/session/test_s3_session_manager.py::test_update_nonexistent_multi_agent PASSED [100%] + +============================== 36 passed in 7.83s ==============================