Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/langgraph-checkpointer/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def assistant(state: MessagesState):
)
builder.add_edge('tools', 'assistant')

memory = DaprCheckpointer(store_name='statestore', key_prefix='dapr')
memory = DaprCheckpointer(state_store_name='statestore', key_prefix='dapr')
react_graph_memory = builder.compile(checkpointer=memory)

config = {'configurable': {'thread_id': '1'}}
Expand Down
14 changes: 14 additions & 0 deletions examples/langgraph-checkpointer/components/agent-registry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: agent-registry
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: keyPrefix
value: none
12 changes: 12 additions & 0 deletions examples/langgraph-checkpointer/dapr-llm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties
version: 1
common:
resourcesPath: ./components
logLevel: info
appLogDestination: console
daprdLogDestination: console

apps:
- appID: langgraph
appDirPath: ./
command: ["python3", "agent.py"]
168 changes: 168 additions & 0 deletions examples/strands-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Dapr For Agents - Strands Agent with Persistent Session Storage

Supporting Dapr-backed session persistence for Strands Agent SDK with distributed state storage.

## Overview

This example demonstrates how to use a **real Strands Agent** from the Strands Agent SDK together with `DaprSessionManager` for distributed session persistence. The example shows:

- Creating a Strands Agent with the official Strands SDK
- Using DaprSessionManager for distributed session storage across restarts
- Tool integration (weather checking example)
- Conversation history persistence and restoration
- Seamless LLM integration through Strands model providers

**Note:** This uses the actual [Strands Agent SDK](https://strandsagents.com/), not just session types.

## Pre-requisites

- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started)
- [Install Python 3.10+](https://www.python.org/downloads/)
- OpenAI API key (or configure a different model provider)

## Install Dependencies

```sh
pip3 install -r requirements.txt
```

Set your API key:
```sh
export OPENAI_API_KEY=your-key-here
```

## Run the Example

Run the following command in a terminal/command prompt:

```sh
dapr run --app-id strands-agent --resources-path ./components -- python3 agent.py
```

### What to Expect

The example will:

1. Create a Strands Agent with:
- GPT-4o model via OpenAIModel provider
- A `get_weather` tool function
- System prompt and agent metadata
- DaprSessionManager for session persistence
2. Process user queries:
- "What's the weather in San Francisco?" → Agent uses get_weather tool
- "How about New York?" → Agent continues conversation with context
3. Persist all conversation state to Dapr state store
4. On subsequent runs, automatically restore full conversation history

Run the example again to see the conversation resume from where it left off!

### Example Output

**First run:**
```
📂 Using session: assistant-session-1
✅ Created Strands Agent: weather-assistant
Model: OpenAIModel(model='gpt-4o')
Tools: ['get_weather']
Session Manager: DaprSessionManager

🆕 Starting fresh conversation

👤 USER: What's the weather in San Francisco?
🤖 ASSISTANT: The weather in San Francisco is sunny and 72°F

👤 USER: How about New York?
🤖 ASSISTANT: Let me check that for you. The weather in New York is sunny and 72°F

✅ Conversation complete!
🔄 Run again to resume the conversation with full history from Dapr state store.
```

**Second run (conversation resumes):**
```
📂 Using session: assistant-session-1
✅ Created Strands Agent: weather-assistant
Model: OpenAIModel(model='gpt-4o')
Tools: ['get_weather']
Session Manager: DaprSessionManager

💬 Resuming conversation with 4 previous messages
────────────────────────────────────────────────────────────
👤 USER: What's the weather in San Francisco?
🤖 ASSISTANT: The weather in San Francisco is sunny and 72°F
👤 USER: How about New York?
🤖 ASSISTANT: Let me check that for you. The weather in New York is sunny and 72°F
────────────────────────────────────────────────────────────

👤 USER: What's the weather in San Francisco?
🤖 ASSISTANT: I just checked that - it's still sunny and 72°F in San Francisco!
```

## Key Features

### Real Strands Agent
- Uses the official Strands Agent SDK from strandsagents.com
- Full agent capabilities: tools, system prompts, state management
- Multiple LLM provider support (Anthropic, OpenAI, Bedrock, etc.)

### Distributed Session Persistence
- DaprSessionManager stores all conversation state in Dapr state stores
- Supports any Dapr state store: Redis, PostgreSQL, MongoDB, Cosmos DB, etc.
- Automatic conversation restoration across application restarts
- Full message history maintained

### Tool Integration
- Define Python functions as tools
- Agent automatically calls tools when needed
- Tool results integrated into conversation flow

### LLM Provider Flexibility
- Easy to swap model providers (AnthropicModel, OpenAIModel, etc.)
- Configure model parameters (temperature, max tokens, etc.)
- Strands handles all LLM interactions

### State Persistence
- Automatic state synchronization with Dapr
- Support for TTL and consistency levels
- Compatible with any Dapr state store (Redis, PostgreSQL, Cosmos DB, etc.)

## Customization

You can customize the session manager with:

```python
session_manager = DaprSessionManager(
session_id='my-session',
state_store_name='statestore',
dapr_client=dapr_client,
ttl=3600, # Optional: TTL in seconds
consistency='strong', # Optional: 'eventual' or 'strong'
)
```

## Configuration

### State Store

The example uses a Redis state store component. You can modify [components/statestore.yaml](./components/statestore.yaml) to use a different state store backend supported by Dapr.

### Conversation Provider

The example uses the `echo` conversation component by default (which echoes back your input). To use a real LLM:

1. Set up a conversation component (e.g., OpenAI, Anthropic) in `components/conversation.yaml`
2. Update the `conversation_provider` variable in `agent.py` to match your component name
3. Set required API keys as environment variables

Example for OpenAI:
```bash
export OPENAI_API_KEY="your-api-key"
```

See [examples/conversation](../conversation/) for more conversation component examples.

## Learn More

- [Dapr State Management](https://docs.dapr.io/developing-applications/building-blocks/state-management/)
- [Strands Framework](https://github.com/microsoft/strands)
- [Dapr Python SDK](https://github.com/dapr/python-sdk)
103 changes: 103 additions & 0 deletions examples/strands-agent/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""
Example demonstrating a Strands Agent with DaprSessionManager for persistent session storage.

This example shows how to:
- Create a Strands Agent with the Strands Agent SDK
- Use DaprSessionManager for distributed session persistence
- Leverage LLM providers through Strands
- Maintain conversation history across restarts
"""

import os
from strands import Agent, tool
from strands.models import OpenAIModel
from dapr.ext.strands import DaprSessionManager
from dapr.clients import DaprClient


@tool
def get_weather(location: str) -> str:
"""Get the current weather for a location.

Args:
location: The city and state, e.g. "San Francisco, CA"

Returns:
A description of the current weather
"""
return f'The weather in {location} is sunny and 72°F'


def run_agent_conversation():
"""Run a Strands Agent with Dapr session persistence."""

openai_api_key = os.getenv('OPENAI_API_KEY')
if not openai_api_key:
print('❌ Error: OPENAI_API_KEY environment variable not set')
print('💡 Set it with: export OPENAI_API_KEY=your-key-here')
return

session_id = 'assistant-session-1'
agent_id = 'weather-assistant'

with DaprClient() as dapr_client:
session_manager = DaprSessionManager(
session_id=session_id, state_store_name='statestore', dapr_client=dapr_client
)

agent = Agent(
model=OpenAIModel(model_id='gpt-4o'),
system_prompt=(
'You are a helpful weather assistant. '
'You can check the weather for any location. '
'Be concise and friendly in your responses.'
),
tools=[get_weather],
agent_id=agent_id,
name='Weather Assistant',
description='An AI assistant that helps users check the weather',
state={
'role': 'Weather Assistant',
'goal': 'Help users get weather information',
'instructions': ['Be concise', 'Be friendly', 'Always use the get_weather tool'],
'max_iterations': 5,
},
session_manager=session_manager,
)

queries = [
"What's the weather in San Francisco?",
'How about New York?',
]

for query in queries:
print(f'👤 USER: {query}')

try:
import asyncio

response = asyncio.run(agent.invoke_async(query))

if hasattr(response, 'content'):
content = response.content
elif hasattr(response, 'text'):
content = response.text
else:
content = str(response)

print(f'🤖 ASSISTANT: {content}')

except Exception as e:
print(f'❌ Error: {e}')
print('💡 Tip: Make sure you have OPENAI_API_KEY set in your environment.')
print(' Or switch to a different model provider (Anthropic, Bedrock, etc.)')
break

print()

print('✅ Conversation complete!')
print('🔄 Run again to resume the conversation with full history from Dapr state store.')


if __name__ == '__main__':
run_agent_conversation()
14 changes: 14 additions & 0 deletions examples/strands-agent/components/agent-registry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: agent-registry
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: keyPrefix
value: none
8 changes: 8 additions & 0 deletions examples/strands-agent/components/conversation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: echo
spec:
type: conversation.echo
version: v1
metadata: []
14 changes: 14 additions & 0 deletions examples/strands-agent/components/statestore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"
12 changes: 12 additions & 0 deletions examples/strands-agent/dapr-llm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties
version: 1
common:
resourcesPath: ./components
logLevel: info
appLogDestination: console
daprdLogDestination: console

apps:
- appID: strands-agent
appDirPath: ./
command: ["python3", "agent.py"]
3 changes: 3 additions & 0 deletions examples/strands-agent/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dapr>=1.15.0
dapr-ext-strands>=0.1.0
strands-agents>=1.24.0
Loading
Loading