|
| 1 | +# Advanced SQLite Sessions |
| 2 | + |
| 3 | +`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. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Conversation branching**: Create alternative conversation paths from any user message |
| 8 | +- **Usage tracking**: Detailed token usage analytics per turn with full JSON breakdowns |
| 9 | +- **Structured queries**: Get conversations by turns, tool usage statistics, and more |
| 10 | +- **Branch management**: Independent branch switching and management |
| 11 | +- **Message structure metadata**: Track message types, tool usage, and conversation flow |
| 12 | + |
| 13 | +## Quick start |
| 14 | + |
| 15 | +```python |
| 16 | +from agents import Agent, Runner |
| 17 | +from agents.extensions.memory import AdvancedSQLiteSession |
| 18 | + |
| 19 | +# Create agent |
| 20 | +agent = Agent( |
| 21 | + name="Assistant", |
| 22 | + instructions="Reply very concisely.", |
| 23 | +) |
| 24 | + |
| 25 | +# Create an advanced session |
| 26 | +session = AdvancedSQLiteSession( |
| 27 | + session_id="conversation_123", |
| 28 | + db_path="conversations.db", |
| 29 | + create_tables=True |
| 30 | +) |
| 31 | + |
| 32 | +# First conversation turn |
| 33 | +result = await Runner.run( |
| 34 | + agent, |
| 35 | + "What city is the Golden Gate Bridge in?", |
| 36 | + session=session |
| 37 | +) |
| 38 | +print(result.final_output) # "San Francisco" |
| 39 | + |
| 40 | +# IMPORTANT: Store usage data |
| 41 | +await session.store_run_usage(result) |
| 42 | + |
| 43 | +# Continue conversation |
| 44 | +result = await Runner.run( |
| 45 | + agent, |
| 46 | + "What state is it in?", |
| 47 | + session=session |
| 48 | +) |
| 49 | +print(result.final_output) # "California" |
| 50 | +await session.store_run_usage(result) |
| 51 | +``` |
| 52 | + |
| 53 | +## Initialization |
| 54 | + |
| 55 | +```python |
| 56 | +from agents.extensions.memory import AdvancedSQLiteSession |
| 57 | + |
| 58 | +# Basic initialization |
| 59 | +session = AdvancedSQLiteSession( |
| 60 | + session_id="my_conversation", |
| 61 | + create_tables=True # Auto-create advanced tables |
| 62 | +) |
| 63 | + |
| 64 | +# With persistent storage |
| 65 | +session = AdvancedSQLiteSession( |
| 66 | + session_id="user_123", |
| 67 | + db_path="path/to/conversations.db", |
| 68 | + create_tables=True |
| 69 | +) |
| 70 | + |
| 71 | +# With custom logger |
| 72 | +import logging |
| 73 | +logger = logging.getLogger("my_app") |
| 74 | +session = AdvancedSQLiteSession( |
| 75 | + session_id="session_456", |
| 76 | + create_tables=True, |
| 77 | + logger=logger |
| 78 | +) |
| 79 | +``` |
| 80 | + |
| 81 | +### Parameters |
| 82 | + |
| 83 | +- `session_id` (str): Unique identifier for the conversation session |
| 84 | +- `db_path` (str | Path): Path to SQLite database file. Defaults to `:memory:` for in-memory storage |
| 85 | +- `create_tables` (bool): Whether to automatically create the advanced tables. Defaults to `False` |
| 86 | +- `logger` (logging.Logger | None): Custom logger for the session. Defaults to module logger |
| 87 | + |
| 88 | +## Usage tracking |
| 89 | + |
| 90 | +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.** |
| 91 | + |
| 92 | +### Storing usage data |
| 93 | + |
| 94 | +```python |
| 95 | +# After each agent run, store the usage data |
| 96 | +result = await Runner.run(agent, "Hello", session=session) |
| 97 | +await session.store_run_usage(result) |
| 98 | + |
| 99 | +# This stores: |
| 100 | +# - Total tokens used |
| 101 | +# - Input/output token breakdown |
| 102 | +# - Request count |
| 103 | +# - Detailed JSON token information (if available) |
| 104 | +``` |
| 105 | + |
| 106 | +### Retrieving usage statistics |
| 107 | + |
| 108 | +```python |
| 109 | +# Get session-level usage (all branches) |
| 110 | +session_usage = await session.get_session_usage() |
| 111 | +if session_usage: |
| 112 | + print(f"Total requests: {session_usage['requests']}") |
| 113 | + print(f"Total tokens: {session_usage['total_tokens']}") |
| 114 | + print(f"Input tokens: {session_usage['input_tokens']}") |
| 115 | + print(f"Output tokens: {session_usage['output_tokens']}") |
| 116 | + print(f"Total turns: {session_usage['total_turns']}") |
| 117 | + |
| 118 | +# Get usage for specific branch |
| 119 | +branch_usage = await session.get_session_usage(branch_id="main") |
| 120 | + |
| 121 | +# Get usage by turn |
| 122 | +turn_usage = await session.get_turn_usage() |
| 123 | +for turn_data in turn_usage: |
| 124 | + print(f"Turn {turn_data['user_turn_number']}: {turn_data['total_tokens']} tokens") |
| 125 | + if turn_data['input_tokens_details']: |
| 126 | + print(f" Input details: {turn_data['input_tokens_details']}") |
| 127 | + if turn_data['output_tokens_details']: |
| 128 | + print(f" Output details: {turn_data['output_tokens_details']}") |
| 129 | + |
| 130 | +# Get usage for specific turn |
| 131 | +turn_2_usage = await session.get_turn_usage(user_turn_number=2) |
| 132 | +``` |
| 133 | + |
| 134 | +## Conversation branching |
| 135 | + |
| 136 | +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. |
| 137 | + |
| 138 | +### Creating branches |
| 139 | + |
| 140 | +```python |
| 141 | +# Get available turns for branching |
| 142 | +turns = await session.get_conversation_turns() |
| 143 | +for turn in turns: |
| 144 | + print(f"Turn {turn['turn']}: {turn['content']}") |
| 145 | + print(f"Can branch: {turn['can_branch']}") |
| 146 | + |
| 147 | +# Create a branch from turn 2 |
| 148 | +branch_id = await session.create_branch_from_turn(2) |
| 149 | +print(f"Created branch: {branch_id}") |
| 150 | + |
| 151 | +# Create a branch with custom name |
| 152 | +branch_id = await session.create_branch_from_turn( |
| 153 | + 2, |
| 154 | + branch_name="alternative_path" |
| 155 | +) |
| 156 | + |
| 157 | +# Create branch by searching for content |
| 158 | +branch_id = await session.create_branch_from_content( |
| 159 | + "weather", |
| 160 | + branch_name="weather_focus" |
| 161 | +) |
| 162 | +``` |
| 163 | + |
| 164 | +### Branch management |
| 165 | + |
| 166 | +```python |
| 167 | +# List all branches |
| 168 | +branches = await session.list_branches() |
| 169 | +for branch in branches: |
| 170 | + current = " (current)" if branch["is_current"] else "" |
| 171 | + print(f"{branch['branch_id']}: {branch['user_turns']} turns, {branch['message_count']} messages{current}") |
| 172 | + |
| 173 | +# Switch between branches |
| 174 | +await session.switch_to_branch("main") |
| 175 | +await session.switch_to_branch(branch_id) |
| 176 | + |
| 177 | +# Delete a branch |
| 178 | +await session.delete_branch(branch_id, force=True) # force=True allows deleting current branch |
| 179 | +``` |
| 180 | + |
| 181 | +### Branch workflow example |
| 182 | + |
| 183 | +```python |
| 184 | +# Original conversation |
| 185 | +result = await Runner.run(agent, "What's the capital of France?", session=session) |
| 186 | +await session.store_run_usage(result) |
| 187 | + |
| 188 | +result = await Runner.run(agent, "What's the weather like there?", session=session) |
| 189 | +await session.store_run_usage(result) |
| 190 | + |
| 191 | +# Create branch from turn 2 (weather question) |
| 192 | +branch_id = await session.create_branch_from_turn(2, "weather_focus") |
| 193 | + |
| 194 | +# Continue in new branch with different question |
| 195 | +result = await Runner.run( |
| 196 | + agent, |
| 197 | + "What are the main tourist attractions in Paris?", |
| 198 | + session=session |
| 199 | +) |
| 200 | +await session.store_run_usage(result) |
| 201 | + |
| 202 | +# Switch back to main branch |
| 203 | +await session.switch_to_branch("main") |
| 204 | + |
| 205 | +# Continue original conversation |
| 206 | +result = await Runner.run( |
| 207 | + agent, |
| 208 | + "How expensive is it to visit?", |
| 209 | + session=session |
| 210 | +) |
| 211 | +await session.store_run_usage(result) |
| 212 | +``` |
| 213 | + |
| 214 | +## Structured queries |
| 215 | + |
| 216 | +AdvancedSQLiteSession provides several methods for analyzing conversation structure and content. |
| 217 | + |
| 218 | +### Conversation analysis |
| 219 | + |
| 220 | +```python |
| 221 | +# Get conversation organized by turns |
| 222 | +conversation_by_turns = await session.get_conversation_by_turns() |
| 223 | +for turn_num, items in conversation_by_turns.items(): |
| 224 | + print(f"Turn {turn_num}: {len(items)} items") |
| 225 | + for item in items: |
| 226 | + if item["tool_name"]: |
| 227 | + print(f" - {item['type']} (tool: {item['tool_name']})") |
| 228 | + else: |
| 229 | + print(f" - {item['type']}") |
| 230 | + |
| 231 | +# Get tool usage statistics |
| 232 | +tool_usage = await session.get_tool_usage() |
| 233 | +for tool_name, count, turn in tool_usage: |
| 234 | + print(f"{tool_name}: used {count} times in turn {turn}") |
| 235 | + |
| 236 | +# Find turns by content |
| 237 | +matching_turns = await session.find_turns_by_content("weather") |
| 238 | +for turn in matching_turns: |
| 239 | + print(f"Turn {turn['turn']}: {turn['content']}") |
| 240 | +``` |
| 241 | + |
| 242 | +### Message structure |
| 243 | + |
| 244 | +The session automatically tracks message structure including: |
| 245 | + |
| 246 | +- Message types (user, assistant, tool_call, etc.) |
| 247 | +- Tool names for tool calls |
| 248 | +- Turn numbers and sequence numbers |
| 249 | +- Branch associations |
| 250 | +- Timestamps |
| 251 | + |
| 252 | +## Database schema |
| 253 | + |
| 254 | +AdvancedSQLiteSession extends the basic SQLite schema with two additional tables: |
| 255 | + |
| 256 | +### message_structure table |
| 257 | + |
| 258 | +```sql |
| 259 | +CREATE TABLE message_structure ( |
| 260 | + id INTEGER PRIMARY KEY AUTOINCREMENT, |
| 261 | + session_id TEXT NOT NULL, |
| 262 | + message_id INTEGER NOT NULL, |
| 263 | + branch_id TEXT NOT NULL DEFAULT 'main', |
| 264 | + message_type TEXT NOT NULL, |
| 265 | + sequence_number INTEGER NOT NULL, |
| 266 | + user_turn_number INTEGER, |
| 267 | + branch_turn_number INTEGER, |
| 268 | + tool_name TEXT, |
| 269 | + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
| 270 | + FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE, |
| 271 | + FOREIGN KEY (message_id) REFERENCES agent_messages(id) ON DELETE CASCADE |
| 272 | +); |
| 273 | +``` |
| 274 | + |
| 275 | +### turn_usage table |
| 276 | + |
| 277 | +```sql |
| 278 | +CREATE TABLE turn_usage ( |
| 279 | + id INTEGER PRIMARY KEY AUTOINCREMENT, |
| 280 | + session_id TEXT NOT NULL, |
| 281 | + branch_id TEXT NOT NULL DEFAULT 'main', |
| 282 | + user_turn_number INTEGER NOT NULL, |
| 283 | + requests INTEGER DEFAULT 0, |
| 284 | + input_tokens INTEGER DEFAULT 0, |
| 285 | + output_tokens INTEGER DEFAULT 0, |
| 286 | + total_tokens INTEGER DEFAULT 0, |
| 287 | + input_tokens_details JSON, |
| 288 | + output_tokens_details JSON, |
| 289 | + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
| 290 | + FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE, |
| 291 | + UNIQUE(session_id, branch_id, user_turn_number) |
| 292 | +); |
| 293 | +``` |
| 294 | + |
| 295 | +## Complete example |
| 296 | + |
| 297 | +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. |
| 298 | + |
| 299 | + |
| 300 | +## API Reference |
| 301 | + |
| 302 | +- [`AdvancedSQLiteSession`][agents.extensions.memory.advanced_sqlite_session.AdvancedSQLiteSession] - Main class |
| 303 | +- [`Session`][agents.memory.session.Session] - Base session protocol |
0 commit comments