Skip to content

Commit 526f464

Browse files
author
Rafael Pierre
committed
restructure agents.memory, create redis session, tests and docs
1 parent 7b84678 commit 526f464

File tree

12 files changed

+3385
-1999
lines changed

12 files changed

+3385
-1999
lines changed

docs/scripts/generate_ref_files.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def md_target(py_path: Path) -> Path:
3131
rel = py_path.relative_to(SRC_ROOT).with_suffix(".md")
3232
return DOCS_ROOT / rel
3333

34+
3435
def pretty_title(last_segment: str) -> str:
3536
"""
3637
Convert a module/file segment like 'tool_context' to 'Tool Context'.
@@ -39,6 +40,7 @@ def pretty_title(last_segment: str) -> str:
3940
cleaned = last_segment.replace("_", " ").replace("-", " ")
4041
return capwords(cleaned)
4142

43+
4244
# ---- Main ------------------------------------------------------------
4345

4446

docs/sessions.md

Lines changed: 241 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,38 @@ The Agents SDK provides built-in session memory to automatically maintain conver
44

55
Sessions stores conversation history for a specific session, allowing agents to maintain context without requiring explicit manual memory management. This is particularly useful for building chat applications or multi-turn conversations where you want the agent to remember previous interactions.
66

7+
## Installation
8+
9+
### SQLite Sessions
10+
SQLite sessions are available by default with no additional dependencies required.
11+
12+
### Redis Sessions
13+
Redis sessions require the Redis package, which is included as a dependency:
14+
15+
```bash
16+
# Redis support is included by default
17+
pip install openai-agents
18+
19+
# Or if you already have the package installed
20+
pip install redis[hiredis]
21+
```
22+
23+
You'll also need a Redis server running. For development, you can use Docker:
24+
25+
```bash
26+
# Start Redis with Docker
27+
docker run -d -p 6379:6379 redis:latest
28+
29+
# Or install Redis locally (macOS)
30+
brew install redis
31+
redis-server
32+
```
33+
734
## Quick start
835

936
```python
10-
from agents import Agent, Runner, SQLiteSession
37+
from agents import Agent, Runner
38+
from agents.memory.providers.sqlite import SQLiteSession
1139

1240
# Create agent
1341
agent = Agent(
@@ -60,7 +88,7 @@ This eliminates the need to manually call `.to_input_list()` and manage conversa
6088
Sessions supports several operations for managing conversation history:
6189

6290
```python
63-
from agents import SQLiteSession
91+
from agents.memory.providers.sqlite import SQLiteSession
6492

6593
session = SQLiteSession("user_123", "conversations.db")
6694

@@ -87,7 +115,8 @@ await session.clear_session()
87115
The `pop_item` method is particularly useful when you want to undo or modify the last item in a conversation:
88116

89117
```python
90-
from agents import Agent, Runner, SQLiteSession
118+
from agents import Agent, Runner
119+
from agents.memory.providers.sqlite import SQLiteSession
91120

92121
agent = Agent(name="Assistant")
93122
session = SQLiteSession("correction_example")
@@ -125,7 +154,7 @@ result = await Runner.run(agent, "Hello")
125154
### SQLite memory
126155

127156
```python
128-
from agents import SQLiteSession
157+
from agents.memory.providers.sqlite import SQLiteSession
129158

130159
# In-memory database (lost when process ends)
131160
session = SQLiteSession("user_123")
@@ -141,10 +170,89 @@ result = await Runner.run(
141170
)
142171
```
143172

173+
### Redis memory
174+
175+
```python
176+
from agents.memory.providers.redis import RedisSession
177+
178+
# Basic Redis session (localhost:6379, default database)
179+
session = RedisSession("user_123")
180+
181+
# Redis session with custom configuration
182+
session = RedisSession(
183+
session_id="user_123",
184+
redis_url="redis://localhost:6379",
185+
db=1,
186+
session_prefix="chat_session",
187+
messages_prefix="chat_messages",
188+
ttl=3600 # Session expires after 1 hour
189+
)
190+
191+
# Use the session
192+
result = await Runner.run(
193+
agent,
194+
"Hello",
195+
session=session
196+
)
197+
198+
# Remember to close the Redis connection when done
199+
await session.close()
200+
201+
# Or use async context manager for automatic cleanup
202+
async with RedisSession("user_123") as session:
203+
result = await Runner.run(
204+
agent,
205+
"Hello",
206+
session=session
207+
)
208+
# Connection automatically closed when exiting the context
209+
```
210+
211+
### Redis Session Manager
212+
213+
For production applications with multiple sessions, use the Redis Session Manager for connection pooling:
214+
215+
```python
216+
from agents.memory.providers.redis import RedisSessionManager
217+
218+
# Create a session manager with connection pooling
219+
manager = RedisSessionManager(
220+
redis_url="redis://localhost:6379",
221+
db=0,
222+
default_ttl=7200, # 2 hours default TTL
223+
max_connections=10
224+
)
225+
226+
# Get session instances that share the connection pool
227+
session1 = manager.get_session("user_123")
228+
session2 = manager.get_session("user_456", ttl=3600) # Custom TTL
229+
230+
# Use sessions normally
231+
result1 = await Runner.run(agent, "Hello", session=session1)
232+
result2 = await Runner.run(agent, "Hi there", session=session2)
233+
234+
# List all sessions
235+
session_ids = await manager.list_sessions()
236+
print(f"Active sessions: {session_ids}")
237+
238+
# Delete a specific session
239+
await manager.delete_session("user_123")
240+
241+
# Close the manager and all connections
242+
await manager.close()
243+
244+
# Or use async context manager
245+
async with RedisSessionManager() as manager:
246+
session = manager.get_session("user_123")
247+
result = await Runner.run(agent, "Hello", session=session)
248+
# Connections automatically closed when exiting
249+
```
250+
144251
### Multiple sessions
145252

146253
```python
147-
from agents import Agent, Runner, SQLiteSession
254+
from agents import Agent, Runner
255+
from agents.memory.providers.sqlite import SQLiteSession
148256

149257
agent = Agent(name="Assistant")
150258

@@ -168,7 +276,7 @@ result2 = await Runner.run(
168276

169277
You can implement your own session memory by creating a class that follows the [`Session`][agents.memory.session.Session] protocol:
170278

171-
````python
279+
```python
172280
from agents.memory import Session
173281
from typing import List
174282

@@ -200,12 +308,15 @@ class MyCustomSession:
200308
pass
201309

202310
# Use your custom session
311+
from agents import Agent, Runner
312+
203313
agent = Agent(name="Assistant")
204314
result = await Runner.run(
205315
agent,
206316
"Hello",
207317
session=MyCustomSession("my_session")
208318
)
319+
```
209320

210321
## Session management
211322

@@ -219,9 +330,10 @@ Use meaningful session IDs that help you organize conversations:
219330

220331
### Memory persistence
221332

222-
- Use in-memory SQLite (`SQLiteSession("session_id")`) for temporary conversations
223-
- Use file-based SQLite (`SQLiteSession("session_id", "path/to/db.sqlite")`) for persistent conversations
224-
- Consider implementing custom session backends for production systems (Redis, PostgreSQL, etc.)
333+
- Use in-memory SQLite (`SQLiteSession("session_id")`) for temporary conversations
334+
- Use file-based SQLite (`SQLiteSession("session_id", "path/to/db.sqlite")`) for persistent conversations
335+
- Use Redis (`RedisSession("session_id")`) for distributed applications and when you need features like automatic expiration
336+
- Consider implementing custom session backends for specialized production systems
225337

226338
### Session management
227339

@@ -232,8 +344,15 @@ await session.clear_session()
232344
# Different agents can share the same session
233345
support_agent = Agent(name="Support")
234346
billing_agent = Agent(name="Billing")
347+
348+
# SQLite session example
349+
from agents.memory.providers.sqlite import SQLiteSession
235350
session = SQLiteSession("user_123")
236351

352+
# Redis session example
353+
from agents.memory.providers.redis import RedisSession
354+
session = RedisSession("user_123", ttl=3600)
355+
237356
# Both agents will see the same conversation history
238357
result1 = await Runner.run(
239358
support_agent,
@@ -253,7 +372,8 @@ Here's a complete example showing session memory in action:
253372

254373
```python
255374
import asyncio
256-
from agents import Agent, Runner, SQLiteSession
375+
from agents import Agent, Runner
376+
from agents.memory.providers.sqlite import SQLiteSession
257377

258378

259379
async def main():
@@ -311,9 +431,118 @@ if __name__ == "__main__":
311431
asyncio.run(main())
312432
```
313433

434+
## Redis Example
435+
436+
Here's a complete example showing Redis session memory in action:
437+
438+
```python
439+
import asyncio
440+
from agents import Agent, Runner
441+
from agents.memory.providers.redis import RedisSession, RedisSessionManager
442+
443+
444+
async def redis_example():
445+
# Create an agent
446+
agent = Agent(
447+
name="Assistant",
448+
instructions="Reply very concisely.",
449+
)
450+
451+
print("=== Redis Session Example ===")
452+
print("Using Redis for distributed session memory.\n")
453+
454+
# Example 1: Basic Redis session
455+
async with RedisSession("user_456", ttl=3600) as session:
456+
print("First turn with Redis:")
457+
print("User: What's the capital of France?")
458+
result = await Runner.run(
459+
agent,
460+
"What's the capital of France?",
461+
session=session
462+
)
463+
print(f"Assistant: {result.final_output}")
464+
print()
465+
466+
print("Second turn:")
467+
print("User: What's the population?")
468+
result = await Runner.run(
469+
agent,
470+
"What's the population?",
471+
session=session
472+
)
473+
print(f"Assistant: {result.final_output}")
474+
print()
475+
476+
# Example 2: Redis Session Manager for production use
477+
async with RedisSessionManager(default_ttl=7200) as manager:
478+
# Get multiple sessions that share connection pool
479+
session1 = manager.get_session("user_123")
480+
session2 = manager.get_session("user_789")
481+
482+
# Use sessions concurrently
483+
result1 = await Runner.run(
484+
agent,
485+
"Hello from session 1",
486+
session=session1
487+
)
488+
result2 = await Runner.run(
489+
agent,
490+
"Hello from session 2",
491+
session=session2
492+
)
493+
494+
print("Session Manager Example:")
495+
print(f"Session 1 response: {result1.final_output}")
496+
print(f"Session 2 response: {result2.final_output}")
497+
print()
498+
499+
# List all active sessions
500+
session_ids = await manager.list_sessions()
501+
print(f"Active sessions: {session_ids}")
502+
503+
# Clean up specific session
504+
await manager.delete_session("user_789")
505+
print("Deleted session user_789")
506+
507+
print("=== Redis Example Complete ===")
508+
509+
510+
if __name__ == "__main__":
511+
asyncio.run(redis_example())
512+
```
513+
514+
## Choosing the Right Session Backend
515+
516+
### SQLite vs Redis
517+
518+
| Feature | SQLite | Redis |
519+
|---------|--------|-------|
520+
| **Setup** | No additional services required | Requires Redis server |
521+
| **Persistence** | File-based or in-memory | In-memory with optional persistence |
522+
| **Distribution** | Single process only | Multi-process/multi-server |
523+
| **TTL/Expiration** | Manual cleanup required | Automatic expiration with TTL |
524+
| **Concurrency** | Good for single application | Excellent for distributed systems |
525+
| **Performance** | Fast for local access | Very fast, especially for concurrent access |
526+
| **Use Cases** | Single-server applications, development | Production systems, microservices, scaling |
527+
528+
### When to use SQLite:
529+
- Single-server applications
530+
- Development and testing
531+
- When you need file-based persistence
532+
- Simple deployment requirements
533+
534+
### When to use Redis:
535+
- Multi-server applications
536+
- Microservices architecture
537+
- When you need automatic session expiration
538+
- High-concurrency applications
539+
- Distributed systems
540+
314541
## API Reference
315542

316543
For detailed API documentation, see:
317544

318-
- [`Session`][agents.memory.Session] - Protocol interface
319-
- [`SQLiteSession`][agents.memory.SQLiteSession] - SQLite implementation
545+
- [`Session`][agents.memory.Session] - Protocol interface
546+
- [`SQLiteSession`][agents.memory.providers.sqlite.SQLiteSession] - SQLite implementation
547+
- [`RedisSession`][agents.memory.providers.redis.RedisSession] - Redis implementation
548+
- [`RedisSessionManager`][agents.memory.providers.redis.RedisSessionManager] - Redis connection manager

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies = [
1414
"requests>=2.0, <3",
1515
"types-requests>=2.0, <3",
1616
"mcp>=1.11.0, <2; python_version >= '3.10'",
17+
"redis[hiredis]>=6.2.0",
1718
]
1819
classifiers = [
1920
"Typing :: Typed",

src/agents/memory/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
from .session import Session, SQLiteSession
1+
from .providers.sqlite import SQLiteSession
2+
from .session import Session
23

34
__all__ = ["Session", "SQLiteSession"]

src/agents/memory/providers/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)