Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,6 @@ cython_debug/
# PyPI configuration file
.pypirc
.aider*

# Redis database files
dump.rdb
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pip install openai-agents

For voice support, install with the optional `voice` group: `pip install 'openai-agents[voice]'`.

For Redis session support, install with the optional `redis` group: `pip install 'openai-agents[redis]'`.

### uv

If you're familiar with [uv](https://docs.astral.sh/uv/), using the tool would be even similar:
Expand All @@ -42,6 +44,8 @@ uv add openai-agents

For voice support, install with the optional `voice` group: `uv add 'openai-agents[voice]'`.

For Redis session support, install with the optional `redis` group: `uv add 'openai-agents[redis]'`.

## Hello world example

```python
Expand Down Expand Up @@ -211,8 +215,13 @@ print(result.final_output) # "Approximately 39 million"
```python
from agents import Agent, Runner, SQLiteSession

# Custom SQLite database file
# SQLite - file-based or in-memory database
session = SQLiteSession("user_123", "conversations.db")

# Redis - for scalable, distributed deployments
# from agents.extensions.memory import RedisSession
# session = RedisSession.from_url("user_123", url="redis://localhost:6379/0")

agent = Agent(name="Assistant")

# Different session IDs maintain separate conversation histories
Expand Down
3 changes: 3 additions & 0 deletions docs/ref/extensions/memory/redis_session.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `RedisSession`

::: agents.extensions.memory.redis_session.RedisSession
1 change: 1 addition & 0 deletions examples/basic/dynamic_system_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def custom_instructions(
instructions=custom_instructions,
)


async def main():
context = CustomContext(style=random.choice(["haiku", "pirate", "robot"]))
print(f"Using style: {context.style}\n")
Expand Down
1 change: 1 addition & 0 deletions examples/basic/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def get_weather(city: Annotated[str, "The city to get the weather for"]) -> Weat
print("[debug] get_weather called")
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")


agent = Agent(
name="Hello world",
instructions="You are a helpful agent.",
Expand Down
170 changes: 170 additions & 0 deletions examples/memory/redis_session_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
"""
Example demonstrating Redis session memory functionality.

This example shows how to use Redis-backed session memory to maintain conversation
history across multiple agent runs with persistence and scalability.
"""

import asyncio

from agents import Agent, Runner
from agents.extensions.memory import RedisSession


async def main():
# Create an agent
agent = Agent(
name="Assistant",
instructions="Reply very concisely.",
)

print("=== Redis Session Example ===")
print("This example requires Redis to be running on localhost:6379")
print("Start Redis with: redis-server")
print()

# Create a Redis session instance
session_id = "redis_conversation_123"
try:
session = RedisSession.from_url(
session_id,
url="redis://localhost:6379/0", # Use database 0
)

# Test Redis connectivity
if not await session.ping():
print("Redis server is not available!")
print("Please start Redis server and try again.")
return

print("Connected to Redis successfully!")
print(f"Session ID: {session_id}")
print("The agent will remember previous messages automatically.\n")

# First turn
print("First turn:")
print("User: What city is the Golden Gate Bridge in?")
result = await Runner.run(
agent,
"What city is the Golden Gate Bridge in?",
session=session,
)
print(f"Assistant: {result.final_output}")
print()

# Second turn - the agent will remember the previous conversation
print("Second turn:")
print("User: What state is it in?")
result = await Runner.run(agent, "What state is it in?", session=session)
print(f"Assistant: {result.final_output}")
print()

# Third turn - continuing the conversation
print("Third turn:")
print("User: What's the population of that state?")
result = await Runner.run(
agent,
"What's the population of that state?",
session=session,
)
print(f"Assistant: {result.final_output}")
print()

print("=== Conversation Complete ===")
print("Notice how the agent remembered the context from previous turns!")
print("Redis session automatically handles conversation history with persistence.")

# Demonstrate session persistence
print("\n=== Session Persistence Demo ===")
all_items = await session.get_items()
print(f"Total messages stored in Redis: {len(all_items)}")

# Demonstrate the limit parameter
print("\n=== Latest Items Demo ===")
latest_items = await session.get_items(limit=2)
print("Latest 2 items:")
for i, msg in enumerate(latest_items, 1):
role = msg.get("role", "unknown")
content = msg.get("content", "")
print(f" {i}. {role}: {content}")

# Demonstrate session isolation with a new session
print("\n=== Session Isolation Demo ===")
new_session = RedisSession.from_url(
"different_conversation_456",
url="redis://localhost:6379/0",
)

print("Creating a new session with different ID...")
result = await Runner.run(
agent,
"Hello, this is a new conversation!",
session=new_session,
)
print(f"New session response: {result.final_output}")

# Show that sessions are isolated
original_items = await session.get_items()
new_items = await new_session.get_items()
print(f"Original session has {len(original_items)} items")
print(f"New session has {len(new_items)} items")
print("Sessions are completely isolated!")

# Clean up the new session
await new_session.clear_session()
await new_session.close()

# Optional: Demonstrate TTL (time-to-live) functionality
print("\n=== TTL Demo ===")
ttl_session = RedisSession.from_url(
"ttl_demo_session",
url="redis://localhost:6379/0",
ttl=3600, # 1 hour TTL
)

await Runner.run(
agent,
"This message will expire in 1 hour",
session=ttl_session,
)
print("Created session with 1-hour TTL - messages will auto-expire")

await ttl_session.close()

# Close the main session
await session.close()

except Exception as e:
print(f"Error: {e}")
print("Make sure Redis is running on localhost:6379")


async def demonstrate_advanced_features():
"""Demonstrate advanced Redis session features."""
print("\n=== Advanced Features Demo ===")

# Custom key prefix for multi-tenancy
tenant_session = RedisSession.from_url(
"user_123",
url="redis://localhost:6379/0",
key_prefix="tenant_abc:sessions", # Custom prefix for isolation
)

try:
if await tenant_session.ping():
print("Custom key prefix demo:")
await Runner.run(
Agent(name="Support", instructions="Be helpful"),
"Hello from tenant ABC",
session=tenant_session,
)
print("Session with custom key prefix created successfully")

await tenant_session.close()
except Exception as e:
print(f"Advanced features error: {e}")


if __name__ == "__main__":
asyncio.run(main())
asyncio.run(demonstrate_advanced_features())
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ litellm = ["litellm>=1.67.4.post1, <2"]
realtime = ["websockets>=15.0, <16"]
sqlalchemy = ["SQLAlchemy>=2.0", "asyncpg>=0.29.0"]
encrypt = ["cryptography>=45.0, <46"]
redis = ["redis>=6.4.0"]

[dependency-groups]
dev = [
Expand Down Expand Up @@ -67,6 +68,7 @@ dev = [
"fastapi >= 0.110.0, <1",
"aiosqlite>=0.21.0",
"cryptography>=45.0, <46",
"fakeredis>=2.31.3",
]

[tool.uv.workspace]
Expand Down
12 changes: 12 additions & 0 deletions src/agents/extensions/memory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

__all__: list[str] = [
"EncryptedSession",
"RedisSession",
"SQLAlchemySession",
]

Expand All @@ -28,6 +29,17 @@ def __getattr__(name: str) -> Any:
"Install it with: pip install openai-agents[encrypt]"
) from e

if name == "RedisSession":
try:
from .redis_session import RedisSession # noqa: F401

return RedisSession
except ModuleNotFoundError as e:
raise ImportError(
"RedisSession requires the 'redis' extra. "
"Install it with: pip install openai-agents[redis]"
) from e

if name == "SQLAlchemySession":
try:
from .sqlalchemy_session import SQLAlchemySession # noqa: F401
Expand Down
Loading