diff --git a/docs/community/session-managers/redis-session-manager.md b/docs/community/session-managers/redis-session-manager.md new file mode 100644 index 000000000..253f73313 --- /dev/null +++ b/docs/community/session-managers/redis-session-manager.md @@ -0,0 +1,54 @@ +# Redis Session Manager + +{{ community_contribution_banner }} + +The [Redis Session Manager](https://github.com/lekhnath/strands-redis-session-manager) is a Redis-backed `SessionManager` implementation for Strands Agents. It persists agent conversation history and session state to Redis, enabling durable, stateful conversations across restarts and across multiple app instances in distributed deployments. + +## Features + +- Redis-backed persistence for Strands agent sessions +- Optional TTL support for auto-expiring sessions +- Simple integration with Strands Agents SDK + +## Requirements + +- Python 3.12+ +- Strands Agents SDK 1.16.0+ +- Redis 7.0.1+ + +## Installation + +```bash +pip install strands-agents strands-redis-session-manager +``` + +## Usage + +```python +from redis_session_manager import RedisSessionManager +from strands import Agent + +# Initialize Redis session manager +session_manager = RedisSessionManager( + session_id="my-session-id", + redis_client=get_redis_client(), + ttl_seconds=600, # Optional: auto-expire sessions after 10 minutes +) + +# Create your agent with session management +agent = Agent( + agent_id="my-agent", + model=your_model, + session_manager=session_manager, +) + +# Conversations are automatically persisted to Redis +agent("Hello, remember my name is Alice.") +agent("What is my name?") +``` + +## Resources + +- **PyPI**: [strands-redis-session-manager](https://pypi.org/project/strands-redis-session-manager/) +- **GitHub**: [lekhnath/strands-redis-session-manager](https://github.com/lekhnath/strands-redis-session-manager) +- **Issues**: Report bugs and feature requests in the [GitHub repository](https://github.com/lekhnath/strands-redis-session-manager/issues) diff --git a/docs/user-guide/concepts/streaming/async-iterators.md b/docs/user-guide/concepts/streaming/async-iterators.md index 23c95e254..747f8c39e 100644 --- a/docs/user-guide/concepts/streaming/async-iterators.md +++ b/docs/user-guide/concepts/streaming/async-iterators.md @@ -69,7 +69,7 @@ async def stream_response(request: PromptRequest): ) ``` -### Example - Event Loop Lifecycle Tracking +## Event Loop Lifecycle Tracking This async stream processor illustrates the event loop lifecycle events and how they relate to each other. It's useful for understanding the flow of execution in the Strands agent: @@ -115,4 +115,117 @@ The output will show the sequence of events: 2. Then the cycle begins (`start_event_loop`) 3. New cycles may start multiple times during execution (`start`) 4. Text generation and tool usage events occur during the cycle -5. Finally, the cycle completes (`complete`) or may be force-stopped \ No newline at end of file +5. Finally, the cycle completes (`complete`) or may be force-stopped + +## Retrieving ToolResult from Tool Events + +When tools are executed during streaming, you can access their results through the `result` event. The [`ToolResult`](../../../api-reference/types.md#strands.types.tools.ToolResult) contains the complete response from tool execution, including status and content. + +```python +import asyncio +from strands import Agent +from strands_tools import calculator + +async def process_tool_results(): + agent = Agent( + tools=[calculator], + callback_handler=None + ) + + async for event in agent.stream_async("Calculate 15 * 23 and then add 100"): + # Track when tools are being used + if "current_tool_use" in event: + tool_name = event["current_tool_use"].get("name") + tool_id = event["current_tool_use"].get("toolUseId") + if tool_name: + print(f"🔧 Using tool: {tool_name} (ID: {tool_id})") + + # Access the final agent result containing all tool results + if "result" in event: + agent_result = event["result"] + + # Iterate through messages to find tool results + for message in agent_result.messages: + if message.role == "user": + continue + + for content in message.content: + # Check if this content is a tool result + if hasattr(content, 'toolResult'): + tool_result = content.toolResult + print(f"📊 Tool Result:") + print(f" Status: {tool_result.status}") + print(f" Tool Use ID: {tool_result.toolUseId}") + + # Access the content of the tool result + for result_content in tool_result.content: + if 'text' in result_content: + print(f" Text: {result_content['text']}") + elif 'json' in result_content: + print(f" JSON: {result_content['json']}") + +asyncio.run(process_tool_results()) +``` + +### Accessing Individual Tool Results During Execution + +For more granular access to tool results as they complete, you can also monitor the message events: + +```python +async def track_individual_tool_results(): + agent = Agent( + tools=[calculator], + callback_handler=None + ) + + async for event in agent.stream_async("What is 50 divided by 2, then multiply by 3?"): + # Check for new messages that might contain tool results + if "message" in event: + message = event["message"] + + # Look for assistant messages with tool results + if message.role == "assistant": + for content in message.content: + if hasattr(content, 'toolResult'): + tool_result = content.toolResult + print(f"✅ Tool completed: {tool_result.toolUseId}") + print(f" Status: {tool_result.status}") + + # Extract the actual result content + for result_content in tool_result.content: + if 'text' in result_content: + print(f" Result: {result_content['text']}") + +asyncio.run(track_individual_tool_results()) +``` + +### Error Handling with ToolResult + +ToolResult also provides error information when tools fail: + +```python +async def handle_tool_errors(): + agent = Agent( + tools=[calculator], + callback_handler=None + ) + + async for event in agent.stream_async("Calculate the square root of -1"): + if "result" in event: + agent_result = event["result"] + + for message in agent_result.messages: + for content in message.content: + if hasattr(content, 'toolResult'): + tool_result = content.toolResult + + if tool_result.status == "error": + print(f"❌ Tool failed: {tool_result.toolUseId}") + for error_content in tool_result.content: + if 'text' in error_content: + print(f" Error: {error_content['text']}") + else: + print(f"✅ Tool succeeded: {tool_result.toolUseId}") + +asyncio.run(handle_tool_errors()) +``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 145185655..d3d419c7f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -163,6 +163,7 @@ nav: - Fireworks AI: community/model-providers/fireworksai.md - Session Managers: - Amazon AgentCore Memory: community/session-managers/agentcore-memory.md + - Redis: community/session-managers/redis-session-manager.md - Contribute ❤️: https://github.com/strands-agents/sdk-python/blob/main/CONTRIBUTING.md - API Reference: