Skip to content

Commit e4b3150

Browse files
authored
Docs: Improvements for SQLAlchemy Sessions (#1576)
1 parent 714ee0d commit e4b3150

File tree

4 files changed

+52
-32
lines changed

4 files changed

+52
-32
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# `SQLAlchemySession`
2+
3+
::: agents.extensions.memory.sqlalchemy_session.SQLAlchemySession

docs/sessions.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,8 @@ Use meaningful session IDs that help you organize conversations:
280280

281281
- Use in-memory SQLite (`SQLiteSession("session_id")`) for temporary conversations
282282
- Use file-based SQLite (`SQLiteSession("session_id", "path/to/db.sqlite")`) for persistent conversations
283-
- Consider implementing custom session backends for production systems (Redis, PostgreSQL, etc.)
283+
- Use SQLAlchemy-powered sessions (`SQLAlchemySession("session_id", engine=engine, create_tables=True)`) for production systems with existing databases supported by SQLAlchemy
284+
- Consider implementing custom session backends for other production systems (Redis, Django, etc.) for more advanced use cases
284285

285286
### Session management
286287

@@ -376,3 +377,4 @@ For detailed API documentation, see:
376377

377378
- [`Session`][agents.memory.Session] - Protocol interface
378379
- [`SQLiteSession`][agents.memory.SQLiteSession] - SQLite implementation
380+
- [`SQLAlchemySession`][agents.extensions.memory.sqlalchemy_session.SQLAlchemySession] - SQLAlchemy-powered implementation

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ plugins:
144144
- ref/extensions/handoff_filters.md
145145
- ref/extensions/handoff_prompt.md
146146
- ref/extensions/litellm.md
147+
- ref/extensions/memory/sqlalchemy_session.md
147148

148149
- locale: ja
149150
name: 日本語

src/agents/extensions/memory/sqlalchemy_session.py

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,19 @@ def __init__(
6464
create_tables: bool = False,
6565
sessions_table: str = "agent_sessions",
6666
messages_table: str = "agent_messages",
67-
): # noqa: D401 – short description on the class-level docstring
68-
"""Create a new session.
69-
70-
Parameters
71-
----------
72-
session_id
73-
Unique identifier for the conversation.
74-
engine
75-
A pre-configured SQLAlchemy *async* engine. The engine **must** be
76-
created with an async driver (``postgresql+asyncpg://``,
77-
``mysql+aiomysql://`` or ``sqlite+aiosqlite://``).
78-
create_tables
79-
Whether to automatically create the required tables & indexes.
80-
Defaults to *False* for production use. Set to *True* for development
81-
and testing when migrations aren't used.
82-
sessions_table, messages_table
83-
Override default table names if needed.
67+
):
68+
"""Initializes a new SQLAlchemySession.
69+
70+
Args:
71+
session_id (str): Unique identifier for the conversation.
72+
engine (AsyncEngine): A pre-configured SQLAlchemy async engine. The engine
73+
must be created with an async driver (e.g., 'postgresql+asyncpg://',
74+
'mysql+aiomysql://', or 'sqlite+aiosqlite://').
75+
create_tables (bool, optional): Whether to automatically create the required
76+
tables and indexes. Defaults to False for production use. Set to True for
77+
development and testing when migrations aren't used.
78+
sessions_table (str, optional): Override the default table name for sessions if needed.
79+
messages_table (str, optional): Override the default table name for messages if needed.
8480
"""
8581
self.session_id = session_id
8682
self._engine = engine
@@ -132,9 +128,7 @@ def __init__(
132128
)
133129

134130
# Async session factory
135-
self._session_factory = async_sessionmaker(
136-
self._engine, expire_on_commit=False
137-
)
131+
self._session_factory = async_sessionmaker(self._engine, expire_on_commit=False)
138132

139133
self._create_tables = create_tables
140134

@@ -152,16 +146,16 @@ def from_url(
152146
) -> SQLAlchemySession:
153147
"""Create a session from a database URL string.
154148
155-
Parameters
156-
----------
157-
session_id
158-
Conversation ID.
159-
url
160-
Any SQLAlchemy async URL – e.g. ``"postgresql+asyncpg://user:pass@host/db"``.
161-
engine_kwargs
162-
Additional kwargs forwarded to :pyfunc:`sqlalchemy.ext.asyncio.create_async_engine`.
163-
kwargs
164-
Forwarded to the main constructor (``create_tables``, custom table names, …).
149+
Args:
150+
session_id (str): Conversation ID.
151+
url (str): Any SQLAlchemy async URL, e.g. "postgresql+asyncpg://user:pass@host/db".
152+
engine_kwargs (dict[str, Any] | None): Additional keyword arguments forwarded to
153+
sqlalchemy.ext.asyncio.create_async_engine.
154+
**kwargs: Additional keyword arguments forwarded to the main constructor
155+
(e.g., create_tables, custom table names, etc.).
156+
157+
Returns:
158+
SQLAlchemySession: An instance of SQLAlchemySession connected to the specified database.
165159
"""
166160
engine_kwargs = engine_kwargs or {}
167161
engine = create_async_engine(url, **engine_kwargs)
@@ -186,6 +180,15 @@ async def _ensure_tables(self) -> None:
186180
self._create_tables = False # Only create once
187181

188182
async def get_items(self, limit: int | None = None) -> list[TResponseInputItem]:
183+
"""Retrieve the conversation history for this session.
184+
185+
Args:
186+
limit: Maximum number of items to retrieve. If None, retrieves all items.
187+
When specified, returns the latest N items in chronological order.
188+
189+
Returns:
190+
List of input items representing the conversation history
191+
"""
189192
await self._ensure_tables()
190193
async with self._session_factory() as sess:
191194
if limit is None:
@@ -220,6 +223,11 @@ async def get_items(self, limit: int | None = None) -> list[TResponseInputItem]:
220223
return items
221224

222225
async def add_items(self, items: list[TResponseInputItem]) -> None:
226+
"""Add new items to the conversation history.
227+
228+
Args:
229+
items: List of input items to add to the history
230+
"""
223231
if not items:
224232
return
225233

@@ -258,6 +266,11 @@ async def add_items(self, items: list[TResponseInputItem]) -> None:
258266
)
259267

260268
async def pop_item(self) -> TResponseInputItem | None:
269+
"""Remove and return the most recent item from the session.
270+
271+
Returns:
272+
The most recent item if it exists, None if the session is empty
273+
"""
261274
await self._ensure_tables()
262275
async with self._session_factory() as sess:
263276
async with sess.begin():
@@ -286,7 +299,8 @@ async def pop_item(self) -> TResponseInputItem | None:
286299
except json.JSONDecodeError:
287300
return None
288301

289-
async def clear_session(self) -> None: # noqa: D401 – imperative mood is fine
302+
async def clear_session(self) -> None:
303+
"""Clear all items for this session."""
290304
await self._ensure_tables()
291305
async with self._session_factory() as sess:
292306
async with sess.begin():

0 commit comments

Comments
 (0)