Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
3 changes: 3 additions & 0 deletions docs/ref/extensions/memory/advanced_sqlite_session.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `AdvancedSQLiteSession`

::: agents.extensions.memory.advanced_sqlite_session.AdvancedSQLiteSession

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Remove reference to nonexistent AdvancedSQLiteSession module

The new API reference page renders agents.extensions.memory.advanced_sqlite_session.AdvancedSQLiteSession, but there is no corresponding module anywhere under agents.extensions.memory (the package only contains encrypt_session.py and sqlalchemy_session.py). MkDocStrings will raise a ModuleNotFoundError and the page cannot build, and the documentation links to a class that is not implemented. Either implement the class or drop the reference so the docs build successfully.

Useful? React with 👍 / 👎.

3 changes: 3 additions & 0 deletions docs/ref/extensions/memory/encrypted_session.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `EncryptedSession`

::: agents.extensions.memory.encrypt_session.EncryptedSession
1 change: 1 addition & 0 deletions docs/ref/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
members:
- Session
- SQLiteSession
- OpenAIConversationsSession
4 changes: 2 additions & 2 deletions docs/running_agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ async def main():

### Automatic conversation management with Sessions

For a simpler approach, you can use [Sessions](sessions.md) to automatically handle conversation history without manually calling `.to_input_list()`:
For a simpler approach, you can use [Sessions](sessions/index.md) to automatically handle conversation history without manually calling `.to_input_list()`:

```python
from agents import Agent, Runner, SQLiteSession
Expand Down Expand Up @@ -119,7 +119,7 @@ Sessions automatically:
- Stores new messages after each run
- Maintains separate conversations for different session IDs

See the [Sessions documentation](sessions.md) for more details.
See the [Sessions documentation](sessions/index.md) for more details.

## Long running agents & human-in-the-loop

Expand Down
303 changes: 303 additions & 0 deletions docs/sessions/advanced_sqlite_session.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
# Advanced SQLite Sessions

`AdvancedSQLiteSession` is an enhanced version of the basic `SQLiteSession` that provides advanced conversation management capabilities including conversation branching, detailed usage analytics, and structured conversation queries.

## Features

- **Conversation branching**: Create alternative conversation paths from any user message
- **Usage tracking**: Detailed token usage analytics per turn with full JSON breakdowns
- **Structured queries**: Get conversations by turns, tool usage statistics, and more
- **Branch management**: Independent branch switching and management
- **Message structure metadata**: Track message types, tool usage, and conversation flow

## Quick start

```python
from agents import Agent, Runner
from agents.extensions.memory import AdvancedSQLiteSession

# Create agent
agent = Agent(
name="Assistant",
instructions="Reply very concisely.",
)

# Create an advanced session
session = AdvancedSQLiteSession(
session_id="conversation_123",
db_path="conversations.db",
create_tables=True
)

# First conversation turn
result = await Runner.run(
agent,
"What city is the Golden Gate Bridge in?",
session=session
)
print(result.final_output) # "San Francisco"

# IMPORTANT: Store usage data
await session.store_run_usage(result)

# Continue conversation
result = await Runner.run(
agent,
"What state is it in?",
session=session
)
print(result.final_output) # "California"
await session.store_run_usage(result)
```

## Initialization

```python
from agents.extensions.memory import AdvancedSQLiteSession

# Basic initialization
session = AdvancedSQLiteSession(
session_id="my_conversation",
create_tables=True # Auto-create advanced tables
)

# With persistent storage
session = AdvancedSQLiteSession(
session_id="user_123",
db_path="path/to/conversations.db",
create_tables=True
)

# With custom logger
import logging
logger = logging.getLogger("my_app")
session = AdvancedSQLiteSession(
session_id="session_456",
create_tables=True,
logger=logger
)
```

### Parameters

- `session_id` (str): Unique identifier for the conversation session
- `db_path` (str | Path): Path to SQLite database file. Defaults to `:memory:` for in-memory storage
- `create_tables` (bool): Whether to automatically create the advanced tables. Defaults to `False`
- `logger` (logging.Logger | None): Custom logger for the session. Defaults to module logger

## Usage tracking

AdvancedSQLiteSession provides detailed usage analytics by storing token usage data per conversation turn. **This is entirely dependent on the `store_run_usage` method being called after each agent run.**

### Storing usage data

```python
# After each agent run, store the usage data
result = await Runner.run(agent, "Hello", session=session)
await session.store_run_usage(result)

# This stores:
# - Total tokens used
# - Input/output token breakdown
# - Request count
# - Detailed JSON token information (if available)
```

### Retrieving usage statistics

```python
# Get session-level usage (all branches)
session_usage = await session.get_session_usage()
if session_usage:
print(f"Total requests: {session_usage['requests']}")
print(f"Total tokens: {session_usage['total_tokens']}")
print(f"Input tokens: {session_usage['input_tokens']}")
print(f"Output tokens: {session_usage['output_tokens']}")
print(f"Total turns: {session_usage['total_turns']}")

# Get usage for specific branch
branch_usage = await session.get_session_usage(branch_id="main")

# Get usage by turn
turn_usage = await session.get_turn_usage()
for turn_data in turn_usage:
print(f"Turn {turn_data['user_turn_number']}: {turn_data['total_tokens']} tokens")
if turn_data['input_tokens_details']:
print(f" Input details: {turn_data['input_tokens_details']}")
if turn_data['output_tokens_details']:
print(f" Output details: {turn_data['output_tokens_details']}")

# Get usage for specific turn
turn_2_usage = await session.get_turn_usage(user_turn_number=2)
```

## Conversation branching

One of the key features of AdvancedSQLiteSession is the ability to create conversation branches from any user message, allowing you to explore alternative conversation paths.

### Creating branches

```python
# Get available turns for branching
turns = await session.get_conversation_turns()
for turn in turns:
print(f"Turn {turn['turn']}: {turn['content']}")
print(f"Can branch: {turn['can_branch']}")

# Create a branch from turn 2
branch_id = await session.create_branch_from_turn(2)
print(f"Created branch: {branch_id}")

# Create a branch with custom name
branch_id = await session.create_branch_from_turn(
2,
branch_name="alternative_path"
)

# Create branch by searching for content
branch_id = await session.create_branch_from_content(
"weather",
branch_name="weather_focus"
)
```

### Branch management

```python
# List all branches
branches = await session.list_branches()
for branch in branches:
current = " (current)" if branch["is_current"] else ""
print(f"{branch['branch_id']}: {branch['user_turns']} turns, {branch['message_count']} messages{current}")

# Switch between branches
await session.switch_to_branch("main")
await session.switch_to_branch(branch_id)

# Delete a branch
await session.delete_branch(branch_id, force=True) # force=True allows deleting current branch
```

### Branch workflow example

```python
# Original conversation
result = await Runner.run(agent, "What's the capital of France?", session=session)
await session.store_run_usage(result)

result = await Runner.run(agent, "What's the weather like there?", session=session)
await session.store_run_usage(result)

# Create branch from turn 2 (weather question)
branch_id = await session.create_branch_from_turn(2, "weather_focus")

# Continue in new branch with different question
result = await Runner.run(
agent,
"What are the main tourist attractions in Paris?",
session=session
)
await session.store_run_usage(result)

# Switch back to main branch
await session.switch_to_branch("main")

# Continue original conversation
result = await Runner.run(
agent,
"How expensive is it to visit?",
session=session
)
await session.store_run_usage(result)
```

## Structured queries

AdvancedSQLiteSession provides several methods for analyzing conversation structure and content.

### Conversation analysis

```python
# Get conversation organized by turns
conversation_by_turns = await session.get_conversation_by_turns()
for turn_num, items in conversation_by_turns.items():
print(f"Turn {turn_num}: {len(items)} items")
for item in items:
if item["tool_name"]:
print(f" - {item['type']} (tool: {item['tool_name']})")
else:
print(f" - {item['type']}")

# Get tool usage statistics
tool_usage = await session.get_tool_usage()
for tool_name, count, turn in tool_usage:
print(f"{tool_name}: used {count} times in turn {turn}")

# Find turns by content
matching_turns = await session.find_turns_by_content("weather")
for turn in matching_turns:
print(f"Turn {turn['turn']}: {turn['content']}")
```

### Message structure

The session automatically tracks message structure including:

- Message types (user, assistant, tool_call, etc.)
- Tool names for tool calls
- Turn numbers and sequence numbers
- Branch associations
- Timestamps

## Database schema

AdvancedSQLiteSession extends the basic SQLite schema with two additional tables:

### message_structure table

```sql
CREATE TABLE message_structure (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
message_id INTEGER NOT NULL,
branch_id TEXT NOT NULL DEFAULT 'main',
message_type TEXT NOT NULL,
sequence_number INTEGER NOT NULL,
user_turn_number INTEGER,
branch_turn_number INTEGER,
tool_name TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE,
FOREIGN KEY (message_id) REFERENCES agent_messages(id) ON DELETE CASCADE
);
```

### turn_usage table

```sql
CREATE TABLE turn_usage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
branch_id TEXT NOT NULL DEFAULT 'main',
user_turn_number INTEGER NOT NULL,
requests INTEGER DEFAULT 0,
input_tokens INTEGER DEFAULT 0,
output_tokens INTEGER DEFAULT 0,
total_tokens INTEGER DEFAULT 0,
input_tokens_details JSON,
output_tokens_details JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE,
UNIQUE(session_id, branch_id, user_turn_number)
);
```

## Complete example

Check out the [complete example](https://github.com/openai/openai-agents-python/tree/main/examples/memory/advanced_sqlite_session_example.py) for a comprehensive demonstration of all features.


## API Reference

- [`AdvancedSQLiteSession`][agents.extensions.memory.advanced_sqlite_session.AdvancedSQLiteSession] - Main class
- [`Session`][agents.memory.session.Session] - Base session protocol
Loading