Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
58 changes: 58 additions & 0 deletions docs/sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,64 @@ result2 = await Runner.run(
)
```

### SQLAlchemy-powered sessions

For more advanced use cases, you can use a SQLAlchemy-powered session backend. This allows you to use any database supported by SQLAlchemy (PostgreSQL, MySQL, SQLite, etc.) for session storage.

**Example 1: Using `from_url` with in-memory SQLite**

This is the simplest way to get started, ideal for development and testing.

```python
import asyncio
from agents import Agent, Runner
from agents.extensions.memory.sqlalchemy_session import SQLAlchemySession

async def main():
agent = Agent("Assistant")
session = SQLAlchemySession.from_url(
"user-123",
url="sqlite+aiosqlite:///:memory:",
create_tables=True, # Auto-create tables for the demo
)

result = await Runner.run(agent, "Hello", session=session)

if __name__ == "__main__":
asyncio.run(main())
```

**Example 2: Using an existing SQLAlchemy engine**

In a production application, you likely already have a SQLAlchemy `AsyncEngine` instance. You can pass it directly to the session.

```python
import asyncio
from agents import Agent, Runner
from agents.extensions.memory.sqlalchemy_session import SQLAlchemySession
from sqlalchemy.ext.asyncio import create_async_engine

async def main():
# In your application, you would use your existing engine
engine = create_async_engine("sqlite+aiosqlite:///conversations.db")

agent = Agent("Assistant")
session = SQLAlchemySession(
"user-456",
engine=engine,
create_tables=True, # Auto-create tables for the demo
)

result = await Runner.run(agent, "Hello", session=session)
print(result.final_output)

await engine.dispose()

if __name__ == "__main__":
asyncio.run(main())
```


## Custom memory implementations

You can implement your own session memory by creating a class that follows the [`Session`][agents.memory.session.Session] protocol:
Expand Down
50 changes: 50 additions & 0 deletions examples/basic/sqlalchemy_session_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import asyncio

from agents import Agent, Runner
from agents.extensions.memory.sqlalchemy_session import SQLAlchemySession


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

# Create a session instance with a session ID.
# This example uses an in-memory SQLite database.
# The `create_tables=True` flag is useful for development and testing.
session = SQLAlchemySession.from_url(
"conversation_123",
url="sqlite+aiosqlite:///:memory:",
create_tables=True,
)

print("=== SQLAlchemySession Example ===")
print("The agent will remember previous messages automatically.\n")

# 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}\n")

# Second turn - the agent will remember the previous conversation
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}\n")

print("=== Conversation Complete ===")


if __name__ == "__main__":
# To run this example, you need to install the sqlalchemy extras:
# pip install "agents[sqlalchemy]"
asyncio.run(main())
3 changes: 2 additions & 1 deletion examples/realtime/app/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import logging
import struct
from contextlib import asynccontextmanager
from typing import TYPE_CHECKING, Any, assert_never
from typing import TYPE_CHECKING, Any

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from typing_extensions import assert_never

from agents.realtime import RealtimeRunner, RealtimeSession, RealtimeSessionEvent

Expand Down
2 changes: 1 addition & 1 deletion examples/realtime/cli/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self) -> None:
# Audio output state for callback system
self.output_queue: queue.Queue[Any] = queue.Queue(maxsize=10) # Buffer more chunks
self.interrupt_event = threading.Event()
self.current_audio_chunk: np.ndarray | None = None # type: ignore
self.current_audio_chunk: np.ndarray[Any, np.dtype[Any]] | None = None
self.chunk_position = 0

def _output_callback(self, outdata, frames: int, time, status) -> None:
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ voice = ["numpy>=2.2.0, <3; python_version>='3.10'", "websockets>=15.0, <16"]
viz = ["graphviz>=0.17"]
litellm = ["litellm>=1.67.4.post1, <2"]
realtime = ["websockets>=15.0, <16"]
sqlalchemy = ["SQLAlchemy>=2.0", "asyncpg>=0.29.0"]

[dependency-groups]
dev = [
Expand All @@ -63,6 +64,7 @@ dev = [
"mkdocs-static-i18n>=1.3.0",
"eval-type-backport>=0.2.2",
"fastapi >= 0.110.0, <1",
"aiosqlite>=0.21.0",
]

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

"""Session memory backends living in the extensions namespace.

This package contains optional, production-grade session implementations that
introduce extra third-party dependencies (database drivers, ORMs, etc.). They
conform to the :class:`agents.memory.session.Session` protocol so they can be
used as a drop-in replacement for :class:`agents.memory.session.SQLiteSession`.
"""
from __future__ import annotations

from .sqlalchemy_session import SQLAlchemySession # noqa: F401

__all__: list[str] = [
"SQLAlchemySession",
]
Loading