From 161ce6e55e484af1e90ce7e8d2ad38d2beb2b6d1 Mon Sep 17 00:00:00 2001 From: Phil Merrell Date: Tue, 8 Sep 2026 20:19:33 -0600 Subject: [PATCH] chore(deps): upgrade strands-agents 1.51.0 -> 1.55.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrates the deprecated model-level `cache_tools` key to `CacheConfig(tools_ttl=...)` and fixes the Nova Sonic provider rename that 1.55.0 makes, which would otherwise have silently disabled voice. Prompt-cache contract (the governing constraint here) is unchanged, proven two ways rather than by code reading: - Offline: the full formatted ConverseStream request for our production config is byte-identical between 1.51.0 and 1.55.0 (same request SHA). - Live against dev Bedrock: a request formatted by 1.55.0 READ the exact cache entry (7,059 tokens) that the same request formatted by 1.51.0 had just written. Bedrock itself confirms the cacheable prefix did not move. `cache_tools` -> `tools_ttl` `_warn_on_deprecated_cache_tools` fires on the old key in 1.55.0. With `cache_config.ttl` unset, `_build_tools_cache_point` resolves `tools_ttl=True` to the same bare `{"cachePoint": {"type": "default"}}` the deprecated path emitted, so the toolConfig tail — which sits inside the cached prefix — is unchanged. The unsupported branch now pins `tools_ttl=False` explicitly instead of falling through the deprecated key. New auto-injected system cache point 1.55.0 adds `_should_cache_system()`, guarded by `not any("cachePoint" in block ...)`. `AgentFactory.create_agent` already appends its own whenever `bedrock_cache_points_supported()`, and that predicate is equivalent to the SDK's, so no path gains a second point. Audited every system-prompt path: the chat agent goes through the factory; voice uses BidiNovaSonicModel (no cache_config); `/chat/api-converse` uses raw boto3 on the Bedrock branch and OpenAI-surface models otherwise; there is exactly one `BedrockModel` construction site. `system_prompt_ttl` is left at its default True as a safety net for any future path that bypasses the factory. `_apply_system_cache_ttl` is inert because `cache_config.ttl` stays unset. Nova Sonic provider rename (not in the release notes) 1.55.0 moved `models.nova_sonic.BidiNovaSonicModel` to `models.bedrock.BedrockNovaSonicModel` and flattened its constructor (`provider_config["audio"]` -> `audio`, `client_config["region"]` -> `region`). `voice_agent` imports the provider inside a `try/except ImportError` that degrades to `BIDI_AVAILABLE = False`, so the stale import would not have crashed — it would have silently turned voice off in the inference-api image, which does install `--extra bidi`. Adds a contract test that reads the pinned SDK's source rather than importing it, since CI installs `agentcore`/`dev` but not `bidi`. Also updates the sequential-executor cancellation canary: 1.55.0 moved the per-tool check from `agent._cancel_signal` to `Agent._observe_cancellation()`, same semantics plus an external-signal mirror. boto3 stays at 1.43.68 in all five files that pin it — 1.55.0 floors at >=1.26.0 and `bedrock-agentcore` is unchanged, so nothing forces it. Fixes we gain on the GPT-5.6 / Mantle Responses path: `length` now beats `tool_calls` (1.51.0 executed truncated tool arguments), `cachePoint` blocks are filtered instead of raising `TypeError` on a mid-session model switch, and document attachments use `filename`/`file_data` instead of `file_url`. Verified: full backend suite 7783 passed / 0 failed; a 3-turn dev session on 1.55.0 went first_write -> hit -> hit with constant toolConfigHash and systemPromptHash, no partial_miss, cacheRead:cacheWrite 9097:126 then 9223:34; a GPT-5.6 (Terra) turn with a text attachment read the document correctly. Co-Authored-By: Claude Opus 5 --- backend/pyproject.toml | 4 +- .../agents/main_agent/core/model_config.py | 36 +++++++-- backend/src/agents/main_agent/voice_agent.py | 39 ++++++---- .../core/test_bedrock_cache_points.py | 61 +++++++++++++-- .../streaming/test_cancellation_state.py | 26 ++++++- .../agents/main_agent/test_voice_agent.py | 76 +++++++++++++++++++ backend/uv.lock | 67 +++++++++------- 7 files changed, 248 insertions(+), 61 deletions(-) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index ddbec65bf..80a7f9884 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -56,7 +56,7 @@ dependencies = [ [project.optional-dependencies] # AgentCore-specific dependencies (for inference_api) agentcore = [ - "strands-agents==1.51.0", + "strands-agents==1.55.0", "strands-agents-tools==0.8.8", "aws-opentelemetry-distro==0.19.0", @@ -71,7 +71,7 @@ agentcore = [ # Voice/BidiAgent dependencies (Nova Sonic speech-to-speech) bidi = [ - "strands-agents[bidi]==1.51.0", + "strands-agents[bidi]==1.55.0", ] # Document ingestion pipeline dependencies (for Lambda deployment) diff --git a/backend/src/agents/main_agent/core/model_config.py b/backend/src/agents/main_agent/core/model_config.py index 13c4a5cbf..83fcdf01c 100644 --- a/backend/src/agents/main_agent/core/model_config.py +++ b/backend/src/agents/main_agent/core/model_config.py @@ -382,7 +382,7 @@ def to_bedrock_config(self) -> Dict[str, Any]: # allows max 4; nothing else in this codebase adds one, see the # position test in tests/agents/main_agent/core/test_bedrock_cache_points.py): # - # 1. toolConfig tail — cache_tools="default" (_build_tools_cache_point) + # 1. toolConfig tail — CacheConfig(tools_ttl=True) (_build_tools_cache_point) # 2. system tail — SystemContentBlock list built by # AgentFactory.create_agent (the deprecated # cache_prompt config key is NOT used) @@ -402,9 +402,9 @@ def to_bedrock_config(self) -> Dict[str, Any]: # ~28k-token static prefix still reads from cache on those turns. # # For a model whose id Strands doesn't recognize as cache-capable, - # auto strategy logs a warning and no-ops — but cache_tools and a - # system cachePoint are sent unconditionally once configured, so both - # are gated on bedrock_cache_points_supported() (the same predicate + # auto strategy logs a warning and no-ops — but the tools and system + # cachePoints are sent unconditionally once configured, so both are + # gated on bedrock_cache_points_supported() (the same predicate # Strands' auto mode uses). Requires strands-agents>=1.48.0: a # cachePoint trailing a non-PDF `document` attachment is rejected by # Bedrock's Anthropic adapter with "ValidationException ... @@ -415,11 +415,33 @@ def to_bedrock_config(self) -> Dict[str, Any]: # document is the first content block. Cache hits are user-visible in # the cost/context badge the moment this is on. # See: https://github.com/strands-agents/sdk-python/issues/1966 + # tools_ttl replaces the model-level cache_tools key, deprecated in + # strands-agents 1.55.0 (_warn_on_deprecated_cache_tools). The emitted + # block is byte-identical either way, which matters because it is the + # tail of the cached prefix: with cache_config.ttl unset, + # _build_tools_cache_point resolves ttl to None for tools_ttl=True + # exactly as _build_deprecated_cache_tools_point did for + # cache_tools="default", so both emit {"cachePoint": {"type": "default"}} + # with no ttl key. False (not None) on the unsupported branch pins the + # off state explicitly rather than falling back through the deprecated + # key. Since cache_config.ttl stays unset, _apply_system_cache_ttl is + # also a no-op — it only rewrites a TTL-less cache point when one is + # configured. + # + # system_prompt_ttl keeps its 1.55 default of True, which appends a + # system cachePoint via _should_cache_system. That is inert on every + # path here: the guard is `not any("cachePoint" in block ...)`, and + # AgentFactory.create_agent already appends its own whenever + # bedrock_cache_points_supported() — the same predicate, so the two + # can't disagree. It stays on as the safety net for a system prompt + # that reaches Bedrock without going through that factory. if self.caching_enabled: from strands.models import CacheConfig - config["cache_config"] = CacheConfig(strategy="auto") - if self.bedrock_cache_points_supported(): - config["cache_tools"] = "default" + config["cache_config"] = CacheConfig( + strategy="auto", + system_prompt_ttl=True, + tools_ttl=self.bedrock_cache_points_supported(), + ) if self.retry_config: from botocore.config import Config as BotocoreConfig diff --git a/backend/src/agents/main_agent/voice_agent.py b/backend/src/agents/main_agent/voice_agent.py index 41da05588..69f7067af 100644 --- a/backend/src/agents/main_agent/voice_agent.py +++ b/backend/src/agents/main_agent/voice_agent.py @@ -5,7 +5,7 @@ real-time voice interaction. Shares session history with text ChatAgent for voice-text continuity. -Requires: strands-agents[bidi] extra for BidiAgent and BidiNovaSonicModel. +Requires: strands-agents[bidi] extra for BidiAgent and BedrockNovaSonicModel. Based on the voice agent pattern from: https://github.com/aws-samples/sample-strands-agent-with-agentcore @@ -23,10 +23,18 @@ logger = logging.getLogger(__name__) -# Optional imports — BidiAgent requires the strands bidi extra +# Optional imports — BidiAgent requires the strands bidi extra. +# +# strands-agents 1.55.0 renamed the provider: the module went +# ``models.nova_sonic`` -> ``models.bedrock`` and the class +# ``BidiNovaSonicModel`` -> ``BedrockNovaSonicModel``. That is an ImportError, +# which this block swallows into BIDI_AVAILABLE=False — so a stale import would +# not crash, it would silently turn voice off everywhere with one INFO line. +# Import the name explicitly rather than leaning on the package's lazy +# ``__getattr__``, so a future rename fails loudly here too. try: from strands.experimental.bidi import BidiAgent - from strands.experimental.bidi.models.nova_sonic import BidiNovaSonicModel + from strands.experimental.bidi.models.bedrock import BedrockNovaSonicModel BIDI_AVAILABLE = True except ImportError: BIDI_AVAILABLE = False @@ -45,7 +53,7 @@ class VoiceAgent(BaseAgent): Bidirectional voice agent using AWS Nova Sonic 2. Provides: - - Real-time speech-to-speech via BidiNovaSonicModel + - Real-time speech-to-speech via BedrockNovaSonicModel - Voice-text continuity (loads previous text chat history) - Separate agent_id ("voice") to avoid session state conflicts - Configurable voice, sample rate, and model via environment variables @@ -95,18 +103,21 @@ def _create_agent(self) -> None: EnvVars.NOVA_SONIC_MODEL_ID, Defaults.NOVA_SONIC_MODEL_ID ) - model = BidiNovaSonicModel( + # 1.55.0 flattened the provider's constructor: the audio settings + # moved from provider_config["audio"] to the `audio` kwarg (an + # AudioConfig TypedDict with these same five keys), and the region + # moved from client_config["region"] to `region`. Both are + # keyword-only now. + model = BedrockNovaSonicModel( model_id=model_id, - provider_config={ - "audio": { - "voice": self._voice, - "input_rate": Defaults.NOVA_SONIC_INPUT_RATE, - "output_rate": Defaults.NOVA_SONIC_OUTPUT_RATE, - "channels": 1, - "format": "pcm", - }, + audio={ + "voice": self._voice, + "input_rate": Defaults.NOVA_SONIC_INPUT_RATE, + "output_rate": Defaults.NOVA_SONIC_OUTPUT_RATE, + "channels": 1, + "format": "pcm", }, - client_config={"region": os.environ.get(EnvVars.AWS_REGION, Defaults.AWS_REGION)}, + region=os.environ.get(EnvVars.AWS_REGION, Defaults.AWS_REGION), ) # Build voice-specific system prompt diff --git a/backend/tests/agents/main_agent/core/test_bedrock_cache_points.py b/backend/tests/agents/main_agent/core/test_bedrock_cache_points.py index ad8255168..16d2caeee 100644 --- a/backend/tests/agents/main_agent/core/test_bedrock_cache_points.py +++ b/backend/tests/agents/main_agent/core/test_bedrock_cache_points.py @@ -8,7 +8,7 @@ the system prompt keep the stable prefix readable from cache on those turns. Contract under test (see ModelConfig.to_bedrock_config comment): - 1. toolConfig.tools tail — via cache_tools="default" + 1. toolConfig.tools tail — via CacheConfig(tools_ttl=True) 2. system tail — via SystemContentBlock list from AgentFactory 3. last user message tail — via CacheConfig(strategy="auto") Bedrock allows max 4 cachePoints per request; nothing else may add one, so the @@ -37,22 +37,41 @@ def _count_cache_points(node) -> int: # --------------------------------------------------------------------------- -# ModelConfig: cache_tools + support predicate +# ModelConfig: tools caching + support predicate # --------------------------------------------------------------------------- class TestCacheToolsConfig: - def test_cache_tools_set_for_claude_with_caching(self): + def test_tools_ttl_set_for_claude_with_caching(self): config = ModelConfig(model_id=CLAUDE_MODEL_ID, caching_enabled=True) - assert config.to_bedrock_config()["cache_tools"] == "default" + bedrock_config = config.to_bedrock_config() + assert bedrock_config["cache_config"].tools_ttl is True + # The model-level key was deprecated in strands-agents 1.55.0 + # (_warn_on_deprecated_cache_tools); tools_ttl supersedes it. + assert "cache_tools" not in bedrock_config - def test_no_cache_tools_when_caching_disabled(self): + def test_no_cache_config_when_caching_disabled(self): config = ModelConfig(model_id=CLAUDE_MODEL_ID, caching_enabled=False) - assert "cache_tools" not in config.to_bedrock_config() + bedrock_config = config.to_bedrock_config() + assert "cache_config" not in bedrock_config + assert "cache_tools" not in bedrock_config - def test_no_cache_tools_for_non_anthropic_bedrock_model(self): + def test_tools_ttl_off_for_non_anthropic_bedrock_model(self): """A model Strands' auto strategy would no-op on must not get explicit cachePoints either — Bedrock would reject them with ValidationException.""" config = ModelConfig(model_id="amazon.nova-pro-v1:0", caching_enabled=True) - assert "cache_tools" not in config.to_bedrock_config() + bedrock_config = config.to_bedrock_config() + assert bedrock_config["cache_config"].tools_ttl is False + assert "cache_tools" not in bedrock_config + + def test_no_ttl_configured_so_the_emitted_points_carry_none(self): + """cache_config.ttl must stay unset. + + It is what makes tools_ttl=True emit a bare ``{"type": "default"}`` + (byte-identical to the old cache_tools="default"), and what keeps + _apply_system_cache_ttl from rewriting the TTL on the cache point + AgentFactory places. Both sit inside the cached prefix. + """ + config = ModelConfig(model_id=CLAUDE_MODEL_ID, caching_enabled=True) + assert config.to_bedrock_config()["cache_config"].ttl is None def test_support_predicate_false_for_non_bedrock_provider(self): config = ModelConfig( @@ -187,6 +206,32 @@ def test_system_tail_is_cache_point(self, request_parts): # system point must survive. assert request_parts["system"][0] == {"text": "You are a helpful assistant."} + def test_system_blocks_hold_exactly_one_cache_point(self, request_parts): + """1.55's _should_cache_system must not double the point we placed. + + Its guard is ``not any("cachePoint" in block ...)``, so a second point + can only appear if AgentFactory stops placing ours — which would move + the system boundary and rewrite the cached prefix. A count of 2 would + also be a ValidationException (adjacent cache points). + """ + assert _count_cache_points(request_parts["system"]) == 1 + + def test_system_prompt_without_our_point_gets_exactly_one(self, model): + """The 1.55 safety net, for any path that bypasses AgentFactory. + + Pinned deliberately: cache_config.system_prompt_ttl stays at its + default True, so a system prompt reaching Bedrock without our + trailing cachePoint still ends the static prefix at the same place + rather than at the tools tail. + """ + request = model.format_request( + [{"role": "user", "content": [{"text": "hi"}]}], + None, + system_prompt_content=[{"text": "You are a helpful assistant."}], + ) + assert _count_cache_points(request["system"]) == 1 + assert request["system"][-1] == {"cachePoint": {"type": "default"}} + def test_last_user_message_tail_is_cache_point(self, request_parts): last_user = [m for m in request_parts["messages"] if m["role"] == "user"][-1] assert last_user["content"][-1] == {"cachePoint": {"type": "default"}} diff --git a/backend/tests/agents/main_agent/streaming/test_cancellation_state.py b/backend/tests/agents/main_agent/streaming/test_cancellation_state.py index 338ee11f0..11f2874e0 100644 --- a/backend/tests/agents/main_agent/streaming/test_cancellation_state.py +++ b/backend/tests/agents/main_agent/streaming/test_cancellation_state.py @@ -152,10 +152,32 @@ def test_agent_exposes_cancel(self): assert callable(getattr(Agent, "cancel", None)) def test_sequential_executor_honors_the_cancel_signal(self): - """Queued tools must be skipped once cancel is armed (new in 1.51.0).""" + """Queued tools must be skipped once cancel is armed (new in 1.51.0). + + 1.51.0 read ``agent._cancel_signal`` inline; 1.55.0 moved the same read + behind ``Agent._observe_cancellation``. Accept either spelling — what + must not disappear is the per-tool check. + """ from strands.tools.executors import sequential - assert "_cancel_signal" in inspect.getsource(sequential) + source = inspect.getsource(sequential) + assert "_cancel_signal" in source or "_observe_cancellation" in source + + def test_observe_cancellation_reads_the_signal_we_clear(self): + """``reset_cancellation_state`` clears ``agent._cancel_signal`` by name. + + 1.55.0's ``_observe_cancellation`` is the only reader the executor goes + through, and it also mirrors a caller-supplied ``_external_cancel_signal`` + onto the internal one. We never pass ``cancel_signal`` to ``stream_async``, + so that stays None — but if a future change starts passing one, clearing + the internal signal alone would stop being enough and a cancelled turn + would wedge every turn after it. + """ + from strands import Agent + + source = inspect.getsource(Agent._observe_cancellation) + assert "self._cancel_signal.is_set()" in source + assert "_external_cancel_signal" in source def test_mcp_tool_forwards_the_cancel_signal(self): """The in-flight MCP call must see the signal (new in 1.51.0).""" diff --git a/backend/tests/agents/main_agent/test_voice_agent.py b/backend/tests/agents/main_agent/test_voice_agent.py index fcf162cf5..a67ecdf3b 100644 --- a/backend/tests/agents/main_agent/test_voice_agent.py +++ b/backend/tests/agents/main_agent/test_voice_agent.py @@ -21,6 +21,82 @@ def test_voice_agent_is_base_agent_subclass(self): assert issubclass(VoiceAgent, BaseAgent) +class TestBidiProviderContract: + """Bind the voice provider import against the pinned SDK. + + ``voice_agent`` imports the provider inside a ``try/except ImportError`` + that degrades to ``BIDI_AVAILABLE = False`` and one INFO line. A rename + upstream therefore does not crash — it silently turns voice off. That is + exactly what strands-agents 1.55.0 did: ``models.nova_sonic``'s + ``BidiNovaSonicModel`` became ``models.bedrock``'s + ``BedrockNovaSonicModel``. + + These assertions read the pinned SDK's *source*, not a live import, because + ``tests.yml`` installs ``--extra agentcore --extra dev`` but not + ``--extra bidi``: the provider module ships in the base wheel while its + runtime dependencies do not, so importing it here would fail on CI even + when the pin is correct. + """ + + def _provider_source(self): + import importlib.util + import pathlib + + spec = importlib.util.find_spec("strands.experimental.bidi") + assert spec is not None and spec.origin, "strands bidi package not found" + provider = pathlib.Path(spec.origin).parent / "models" / "bedrock.py" + assert provider.is_file(), ( + f"{provider} is missing — the bidi provider module was renamed again; " + "update the import in agents/main_agent/voice_agent.py" + ) + return provider.read_text() + + def test_provider_module_defines_the_class_we_import(self): + assert "class BedrockNovaSonicModel" in self._provider_source() + + def test_voice_agent_imports_the_current_provider_name(self): + """Read the module's import statements, not its prose. + + The comment above the import names the old symbol on purpose, so match + against the parsed AST rather than the raw text. + """ + import ast + import inspect + + import agents.main_agent.voice_agent as va + + imported = { + f"{node.module}.{alias.name}" + for node in ast.walk(ast.parse(inspect.getsource(va))) + if isinstance(node, ast.ImportFrom) and node.module + for alias in node.names + } + assert ( + "strands.experimental.bidi.models.bedrock.BedrockNovaSonicModel" in imported + ) + assert not any("BidiNovaSonicModel" in name for name in imported), ( + f"stale 1.51 provider name still imported: {sorted(imported)}" + ) + + def test_provider_takes_flattened_audio_and_region_kwargs(self): + """1.55.0 replaced provider_config/client_config with audio/region.""" + source = self._provider_source() + assert "audio: AudioConfig | None = None" in source + assert "region: str | None = None" in source + assert "provider_config" not in source + + def test_audio_config_still_carries_the_five_keys_we_send(self): + from strands.experimental.bidi.types.model import AudioConfig + + assert {"voice", "input_rate", "output_rate", "channels", "format"} <= set( + AudioConfig.__annotations__ + ) + + def test_nova_sonic_usage_is_still_cumulative(self): + """VoiceAgent de-cumulates bidi_usage; a switch to deltas would double-count.""" + assert "usage_is_cumulative = True" in self._provider_source() + + class TestVoiceConstants: """Req VA-2: Voice configuration constants.""" diff --git a/backend/uv.lock b/backend/uv.lock index 5f113bd94..473a00372 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -123,8 +123,8 @@ requires-dist = [ { name = "ruff", marker = "extra == 'dev'", specifier = "==0.15.12" }, { name = "soupsieve", specifier = "==2.8.4" }, { name = "starlette", specifier = "==1.3.1" }, - { name = "strands-agents", marker = "extra == 'agentcore'", specifier = "==1.51.0" }, - { name = "strands-agents", extras = ["bidi"], marker = "extra == 'bidi'", specifier = "==1.51.0" }, + { name = "strands-agents", marker = "extra == 'agentcore'", specifier = "==1.55.0" }, + { name = "strands-agents", extras = ["bidi"], marker = "extra == 'bidi'", specifier = "==1.55.0" }, { name = "strands-agents-tools", marker = "extra == 'agentcore'", specifier = "==0.8.8" }, { name = "tiktoken", marker = "extra == 'dev'", specifier = "==0.12.0" }, { name = "trafilatura", specifier = "==2.0.0" }, @@ -478,25 +478,30 @@ wheels = [ [[package]] name = "aws-sdk-bedrock-runtime" -version = "0.5.0" +version = "0.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smithy-aws-core", extra = ["eventstream", "json"], marker = "python_full_version >= '3.12'" }, { name = "smithy-core", marker = "python_full_version >= '3.12'" }, - { name = "smithy-http", extra = ["awscrt"], marker = "python_full_version >= '3.12'" }, + { name = "smithy-http", extra = ["aiohttp"], marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/30/ff/c5396e82ab39a30cebd08c875250e72c1116f57c8c1f3e0dda1c1db9b177/aws_sdk_bedrock_runtime-0.5.0.tar.gz", hash = "sha256:cc0af7330bda9e398ad9ebf39c021a59dbc28c616a6c9c7765c355773b2402f4", size = 159805, upload-time = "2026-04-07T19:24:24.564Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/b3/9c225cbfe9f17ea2e3d75a0fdd0b325ef79839b9c09a376bda63a7bf3bb3/aws_sdk_bedrock_runtime-0.11.0.tar.gz", hash = "sha256:f2c45d34625bf6a7b56375e29a53a16b376880bda771e4bbf7d84491622eb193", size = 173854, upload-time = "2026-08-24T21:17:16.304Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/1c/b6f1b10435beee0c6289e2ce5e736bbd30555ce9b5898bd9f98f982827ad/aws_sdk_bedrock_runtime-0.5.0-py3-none-any.whl", hash = "sha256:87a002ba4d070e32e4343858c3aacc2cf040beed7f03c93e2b984e78a4f4ceb6", size = 84911, upload-time = "2026-04-07T19:24:25.49Z" }, + { url = "https://files.pythonhosted.org/packages/29/0c/9512304ed017ce49992df6661eac2b914550247e13bccb55be6ca594170d/aws_sdk_bedrock_runtime-0.11.0-py3-none-any.whl", hash = "sha256:ef01c26ddfd83a5d3e438ab72ebb3c13b41fc0ef11d81095b22c8016f97e9795", size = 97112, upload-time = "2026-08-24T21:17:17.396Z" }, +] + +[package.optional-dependencies] +awscrt = [ + { name = "smithy-http", extra = ["awscrt"], marker = "python_full_version >= '3.12'" }, ] [[package]] name = "aws-sdk-signers" -version = "0.2.0" +version = "0.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/1e/e93f5b7711bb9c3b97a4d5486ee3a74db412a66bf6160977bb69f0434830/aws_sdk_signers-0.2.0.tar.gz", hash = "sha256:3b23fb02ababb9e768fc102ab7584e344be8d65324ef3c33df4b8cad6cee26e7", size = 18069, upload-time = "2026-04-07T19:24:09.72Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/ed/3bc0697ea9c4fed38d3742f373e2a3bb77937f9e1f9b6ef08309e326f195/aws_sdk_signers-0.3.1.tar.gz", hash = "sha256:1f305b753968c98f1cd4bdc498f52a1b7dbedc8d7a07cb3631170de2e572e9a9", size = 18675, upload-time = "2026-08-24T21:16:54.564Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/f3/cb2cd5177bb6c21d339b96079ece8e30ffe9954540494bae73a6498677ac/aws_sdk_signers-0.2.0-py3-none-any.whl", hash = "sha256:e770dcc390e18093840ef4ce1ac70fc419fe6ac8737809ffce9a9fab56d8ac2d", size = 21621, upload-time = "2026-04-07T19:24:08.57Z" }, + { url = "https://files.pythonhosted.org/packages/c8/51/0a3e4314715f80956ea73e3b02b7d3bccdbc9d96950000ed21cbea5ef15a/aws_sdk_signers-0.3.1-py3-none-any.whl", hash = "sha256:a2d26c085f36e8069a8b499a55ac4dff68c8d2cceafe31797c977fd2c5c73579", size = 21934, upload-time = "2026-08-24T21:16:55.512Z" }, ] [[package]] @@ -4560,16 +4565,16 @@ wheels = [ [[package]] name = "smithy-aws-core" -version = "0.5.0" +version = "0.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aws-sdk-signers", marker = "python_full_version >= '3.12'" }, { name = "smithy-core", marker = "python_full_version >= '3.12'" }, { name = "smithy-http", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/a5/abc799cb5263136c94838b8acc987b4c423c08a048ba63d5ebb934b5dde9/smithy_aws_core-0.5.0.tar.gz", hash = "sha256:8f6c758f986657de6e4e182d28cfd5761789478f1b12275b5686789728488f08", size = 15475, upload-time = "2026-04-07T19:24:19.529Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/d3/501c0023548173416109ac42298ca33b708469dc922005770811a597949f/smithy_aws_core-0.11.0.tar.gz", hash = "sha256:29ee89976a520a87e3db557e03e115fdc21a0a60b81161e95174395a1b064da1", size = 38791, upload-time = "2026-08-24T21:16:59.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/3f/878740067fb1ee8f5d551eed016b69dafa9a8eb3c968a637986cdd528a41/smithy_aws_core-0.5.0-py3-none-any.whl", hash = "sha256:08a4d2b5b71fb09f06f43405c82c4c5b466011c3dde1b4ae578e240a383473a0", size = 24802, upload-time = "2026-04-07T19:24:18.596Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f6/fefda9aab809fa1a62bf7073bd6d8ab427bd9989f39b13c0d6e29d4d1045/smithy_aws_core-0.11.0-py3-none-any.whl", hash = "sha256:77cf130c22deac14a8cbeb8ccc4bcfe5a91798f4b38cb53a987080ec58c89f23", size = 58855, upload-time = "2026-08-24T21:16:58.657Z" }, ] [package.optional-dependencies] @@ -4582,53 +4587,57 @@ json = [ [[package]] name = "smithy-aws-event-stream" -version = "0.2.1" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smithy-core", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5c/3f/c1544c3336122571b371eea2dd0dc2542112f172029f06bb43e4c09c128e/smithy_aws_event_stream-0.2.1.tar.gz", hash = "sha256:ae67ea3e582f2c2c556e302e57bc90a19b9b5847bcc8520e89396bcafc26235f", size = 12334, upload-time = "2025-12-30T23:23:27.493Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/0e/6efb3a4ed92c0f1ada6de060ac92e7115a1e34d0ab1fb99a6056734a88ea/smithy_aws_event_stream-0.3.0.tar.gz", hash = "sha256:a0e227367a973144e205a075d0a424f95c92f26656a1018d08900da2ae547c49", size = 12818, upload-time = "2026-05-05T18:04:14.317Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/80/b52689e3dc39d478f7ba69ca18be2e762b3866efe4ae5312d69059f9fe01/smithy_aws_event_stream-0.2.1-py3-none-any.whl", hash = "sha256:31d1089306732b4f35e1c6be2e8c2a3f7524c72c59aa2fd1dcba77b97bef4bc3", size = 15569, upload-time = "2025-12-30T23:23:26.411Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b4/c4c4a9aa5a4cf3ee285f1bd71999441177c651d911a82a6e64c57b6e3e65/smithy_aws_event_stream-0.3.0-py3-none-any.whl", hash = "sha256:8b505cc28230e4fe9c5e025333209b44ca2db560451ac9ed9a7d74939edf4413", size = 15845, upload-time = "2026-05-05T18:04:13.351Z" }, ] [[package]] name = "smithy-core" -version = "0.4.0" +version = "0.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/9c/d4eb7fb59f377b712477a468132ad483179c7877064a16110d6f2109c309/smithy_core-0.4.0.tar.gz", hash = "sha256:eee8bfdcb1f1453c1f20d8b6214fac579b57a7c585ee2f503c404c93d274bb9a", size = 50880, upload-time = "2026-04-07T19:24:11.851Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/c6/93e9eea3c6163228dfe972c3e989e0553047858805ab7aa4a59f074ba129/smithy_core-0.8.1.tar.gz", hash = "sha256:3d2f8fca5960d74bd7ef380f70901c7bcdebe53f929d2d3d2fa6cb790b3f5214", size = 54259, upload-time = "2026-08-20T17:55:30.354Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/25/56488440a801f462db96500ef3d543bf860c4319e77c36d09900a8c27587/smithy_core-0.4.0-py3-none-any.whl", hash = "sha256:50b99e7debecadc6a29a483717ac707ac0480d948f87793b73c26a6b37b03cc5", size = 65010, upload-time = "2026-04-07T19:24:10.874Z" }, + { url = "https://files.pythonhosted.org/packages/0c/23/c6430bbf406477fc7d16254b9908a723b299a4a21a94c99db9d12c84a8bf/smithy_core-0.8.1-py3-none-any.whl", hash = "sha256:44bd9bdf702f76919af58e44a6a1bb3dc136a745b2f955281743022ce767e347", size = 68805, upload-time = "2026-08-20T17:55:29.366Z" }, ] [[package]] name = "smithy-http" -version = "0.4.0" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "smithy-core", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/b4/7422f8c06ab8bd70e6b04e8cd570fb16bc5068bc21066f318bee2b8e8e29/smithy_http-0.4.0.tar.gz", hash = "sha256:212b233d55f9006cbf4b8df7a42e17b841d4cfbe9b25d9afe2ad903e364fc79c", size = 29021, upload-time = "2026-04-07T19:24:14.24Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/78/b5f3113d6c8f0bc1f9777a7f5ca84b892d29efac05850e14f7d4f7e645b5/smithy_http-0.5.0.tar.gz", hash = "sha256:bb4a19672f7c7eeb872a308f777eb505281a5bafb1ee3d1ea9c760c06c352510", size = 31122, upload-time = "2026-08-24T21:16:56.488Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/cd/953eb19e4683f76dac97b9ac30656955d4420211dc1464f5de2faa065fe1/smithy_http-0.4.0-py3-none-any.whl", hash = "sha256:ae381d8bee199d1604ae1a983e59e0b8f51b3bf3561587be9381f4c62818ff72", size = 40536, upload-time = "2026-04-07T19:24:13.075Z" }, + { url = "https://files.pythonhosted.org/packages/27/27/e414082643028846b73afa52a1a8f934548196ee12b187a06803f02a3e66/smithy_http-0.5.0-py3-none-any.whl", hash = "sha256:af273d5f42e7733ce7a6e9bd6fdd6a59ef1b61f6cd1f4a89dd53dfce99da7bef", size = 42198, upload-time = "2026-08-24T21:16:57.52Z" }, ] [package.optional-dependencies] +aiohttp = [ + { name = "aiohttp", marker = "python_full_version >= '3.12'" }, + { name = "yarl", marker = "python_full_version >= '3.12'" }, +] awscrt = [ { name = "awscrt", marker = "python_full_version >= '3.12'" }, ] [[package]] name = "smithy-json" -version = "0.2.2" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ijson", marker = "python_full_version >= '3.12'" }, { name = "smithy-core", marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/50/5ea5bdb003e2d89afa6dfa8702208f2e61db56b4261656435aa15e176f55/smithy_json-0.2.2.tar.gz", hash = "sha256:e40f154455ebceb0552ef37310c5d0c297c6e6690087396adacf06fb170f59de", size = 7644, upload-time = "2026-04-07T19:24:15.136Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/ac/04164eefb3da7479f52f6535b4b39cc8384c292cb2bb74279f2acc4f4b4d/smithy_json-0.3.0.tar.gz", hash = "sha256:c81c7034587e01bc64767cbbecb05a7d65ca9070612fd94e8a03e80540290a22", size = 7956, upload-time = "2026-08-20T17:55:32.177Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/bd/761ae4148699ec2a20ff975379aa439a6123420d102bc23a356dd64667d9/smithy_json-0.2.2-py3-none-any.whl", hash = "sha256:4c7be8532f4ae0429a8a6676adf41e40941893ce7d491bc7faa50590077f97da", size = 10177, upload-time = "2026-04-07T19:24:15.846Z" }, + { url = "https://files.pythonhosted.org/packages/9d/cf/0104c40a0e18fa307ea3da4310eba949f474a5bc1df3cc2b5851a72e8486/smithy_json-0.3.0-py3-none-any.whl", hash = "sha256:ffb73d2e60cf5e616e5d0a1019e7b9f518edba076cb423f10981457725dcddc4", size = 10252, upload-time = "2026-08-20T17:55:31.204Z" }, ] [[package]] @@ -4685,7 +4694,7 @@ wheels = [ [[package]] name = "strands-agents" -version = "1.51.0" +version = "1.55.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, @@ -4702,15 +4711,17 @@ dependencies = [ { name = "typing-extensions" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1a/07/cb77bb1773d885e96dc0412920c620cdc660ef5113e9b61c3cd026240102/strands_agents-1.51.0.tar.gz", hash = "sha256:4cbd0b183aad0bb60ac7c6043d29841c8933937c97228e91669d2d97fc40776d", size = 1285092, upload-time = "2026-08-07T18:07:43.833Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/37/04f19549c24bd7dbdbf5010317ed9b090f454aae550ed188b8b030b1be50/strands_agents-1.55.0.tar.gz", hash = "sha256:8ad2ad0b5306e1e0419cde03534d3a725d8fb80aa19ed7065c844bb2ffbd7dc1", size = 1599495, upload-time = "2026-09-08T16:55:24.132Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/4c/937595709a56e966bc471d2fe14071910b2f57d1975a7fd701da18eedabe/strands_agents-1.51.0-py3-none-any.whl", hash = "sha256:1e0a8d125652d7a863cae8fd717b22e4fa5c7983b499438d1f0f50bf328e8832", size = 663778, upload-time = "2026-08-07T18:07:42.253Z" }, + { url = "https://files.pythonhosted.org/packages/33/90/6db60946626fb7f83249ac9f8e3037947f3aeac9b93bbbf116a4edd7be6c/strands_agents-1.55.0-py3-none-any.whl", hash = "sha256:01cd3ebd0606042b0e0c099aa6e549080a945ed3f4e063c223bf34f11373cd75", size = 807217, upload-time = "2026-09-08T16:55:22.37Z" }, ] [package.optional-dependencies] bidi = [ - { name = "aws-sdk-bedrock-runtime", marker = "python_full_version >= '3.12'" }, + { name = "aws-sdk-bedrock-runtime", extra = ["awscrt"], marker = "python_full_version >= '3.12'" }, + { name = "prompt-toolkit" }, { name = "smithy-aws-core", marker = "python_full_version >= '3.12'" }, + { name = "smithy-core", marker = "python_full_version >= '3.12'" }, ] [[package]]