Metadata-Version: 2.4
Name: strands_session_dynamodb
Version: 0.2.0
Summary: Amazon DynamoDB session manager (storage backend) for Strands Agents — persists sessions, agent state, and messages
Project-URL: Homepage, https://github.com/skamalj/strands-agents-session
Project-URL: Repository, https://github.com/skamalj/strands-agents-session.git
Author-email: Kamal <skamalj@github.com>
Keywords: agent-state,aws,dynamodb,memory,persistence,session,strands,strands-agents
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: boto3
Requires-Dist: strands-agents
Requires-Dist: strands-agents-session>=0.2.0
Description-Content-Type: text/markdown

# strands-session-dynamodb

Amazon DynamoDB **session manager** (storage backend) for [Strands Agents](https://strandsagents.com). Persists an agent's sessions, **agent state**, conversation-manager state, and messages to a single DynamoDB table so conversations resume across runs.

It implements Strands' `SessionRepository` and mixes in `RepositorySessionManager` — exactly like the built-in `S3SessionManager` — so all the session lifecycle logic (message indexing, restore, `removed_message_count` offsetting, tool-use repair, change detection) is reused unchanged. This package only supplies the DynamoDB storage.

> **Storage only, by design.** In Strands, message *pruning* is the job of a `ConversationManager` (`SlidingWindow`, `Summarizing`), which is deliberately decoupled from storage. This package does **not** prune — pruning at the storage layer would corrupt Strands' message-index/offset restore logic. Use a `conversation_manager` for that.

## Why DynamoDB instead of S3

Strands ships an `S3SessionManager` out of the box, so why DynamoDB? Because agent sessions are a **request-bound, small-item** workload — the exact shape where DynamoDB wins on both cost and latency.

Strands writes **one item per message** plus an agent record re-synced each turn — lots of tiny (sub-KB) reads and writes. The bill is dominated by *request count*, not bytes stored, and that's where the two services price very differently:

| | S3 Standard | DynamoDB on-demand |
|---|---|---|
| Write | ~$5.00 / million PUT (any size) | ~$1.25 / million WRU (per 1 KB) |
| Read | ~$0.40 / million GET (per object) | ~$0.25 / million RRU (per 4 KB) |
| Free tier | none | 25 WCU + 25 RCU + 25 GB, perpetual |
| Latency | tens of ms | single-digit ms |

Two structural advantages for small items:

- **Writes:** S3 charges per request regardless of size — a 200-byte message costs the same PUT as a 4 MB one. DynamoDB bills per KB, so tiny messages hit the cheapest unit *and* the per-unit price is ~4× lower.
- **Reads:** `list_messages` is **one Query** in DynamoDB, and one RRU covers 4 KB — so several small messages per RRU. In S3 it's **one GET per message object**. Batching crushes the per-item read cost.

For a workload of ~1M small message-writes/month with periodic restores, this is roughly a **3–4× lower bill** on DynamoDB — and often **free** under DynamoDB's perpetual free tier, which S3 has no equivalent of. Add native **TTL** for automatic session expiry and single-digit-ms access, and DynamoDB is the better default for hot session/agent-state storage.

**When S3 still wins:** very large message payloads (big tool results, images) that approach or exceed DynamoDB's 400 KB item limit, or cold/archival sessions rarely read (S3 storage is ~10× cheaper per GB). A robust production setup is DynamoDB for the session/message records + S3 overflow only for oversized blobs.

## Installation

```bash
pip install strands-session-dynamodb
# or, via the family's extra:
pip install "strands-agents-session[dynamodb]"
```

**Requires Python 3.10+.**

## Quick start

```python
from strands import Agent
from strands_session_dynamodb import DynamoDBSessionManager

session_manager = DynamoDBSessionManager(
    session_id="user-123",
    table_name="strands-sessions",
    region_name="us-east-1",
)

agent = Agent(session_manager=session_manager)

agent("Hi, I'm Kamal")
agent("What's my name?")   # remembers within the session
```

Next run, same `session_id` → the agent restores its full history and state from DynamoDB. The table is created automatically (on-demand billing) if it does not exist.

## API

### `DynamoDBSessionManager(session_id, table_name, *, region_name=None, boto_session=None, boto_client_config=None, endpoint_url=None, ttl_seconds=None)`

| Parameter | Description |
|---|---|
| `session_id` | Session identifier |
| `table_name` | DynamoDB table (auto-created if absent) |
| `region_name` | AWS region |
| `boto_session` | Optional pre-built `boto3.Session` |
| `boto_client_config` | Optional `botocore` client config |
| `endpoint_url` | Custom endpoint (e.g. DynamoDB Local / LocalStack) |
| `ttl_seconds` | If set, writes a `ttl` epoch attribute and enables table TTL for automatic session expiry |

## AWS setup

Standard AWS credential resolution (env vars, `~/.aws/credentials`, profile, or IAM role). The credentials need permission to create the table (if absent) and read/write items.

## Data model

Single table, both keys strings:

| Item | PK | SK |
|---|---|---|
| Session | `SESSION#<session_id>` | `META` |
| Agent | `SESSION#<session_id>` | `AGENT#<agent_id>` |
| Message | `SESSION#<session_id>#AGENT#<agent_id>` | `MSG#<zero-padded id>` |

Messages live in a per-agent partition with a zero-padded, lexically ordered sort key, so `list_messages(offset, limit)` is a native range Query — matching the `removed_message_count` offset semantics Strands relies on. Payloads are stored as a JSON string.

> **Item-size note:** DynamoDB items are capped at 400 KB. A single message with a very large payload (e.g. big tool results / images) could exceed that; S3 has no such limit. For such workloads, keep large blobs in S3 and reference them.

## License

MIT
