Commit Graph

18 Commits

Author SHA1 Message Date
devtalker cb0125c18e Add streaming function call arguments example (#1052)
Co-authored-by: Kazuhiro Sera <seratch@openai.com>
2025-08-11 15:51:53 +09:00
Adeniji Adekunle James 3a16a24dac Examples: use agent to interact with Remote PDF Documents (#1319) 2025-07-31 01:34:24 +00:00
Stephan Fitzpatrick 6b94ad0f85 Add Sessions for Automatic Conversation History Management (#752)
# Overview

Resolves #745

This PR introduces **Sessions**, a new core feature that automatically
maintains conversation history across multiple agent runs, eliminating
the need to manually handle `.to_input_list()` between turns.

## Key Features

### 🧠 Automatic Memory Management
- **Zero-effort conversation continuity**: Agents automatically remember
previous context without manual state management
- **Session-based organization**: Each conversation is isolated by
unique session IDs
- **Seamless integration**: Works with existing `Runner.run()`,
`Runner.run_sync()`, and `Runner.run_streamed()` methods

### 🔌 Extensible Session Protocol
- **Library-agnostic design**: Clean protocol interface allows any
storage backend
- **Drop-in implementations**: Easy integration with Redis, PostgreSQL,
MongoDB, or any custom storage
- **Production-ready interface**: Async-first design with proper error
handling and type safety
- **Vendor flexibility**: Library authors can provide their own Session
implementations

### 💾 Built-in SQLite Implementation
- **In-memory SQLite**: Perfect for temporary conversations during
development
- **Persistent SQLite**: File-based storage for conversations that
survive application restarts
- **Thread-safe operations**: Production-ready with connection pooling
and proper concurrency handling

### 🔧 Simple API
```python
# Before: Manual conversation management
result1 = await Runner.run(agent, "What's the weather?")
new_input = result1.to_input_list() + [{"role": "user", "content": "How about tomorrow?"}]
result2 = await Runner.run(agent, new_input)

# After: Automatic with Sessions
session = SQLiteSession("user_123")

result1 = await Runner.run(agent, "What's the weather?", session=session)
result2 = await Runner.run(agent, "How about tomorrow?", session=session)  # Remembers context automatically
```

## What's Included

### Core Session Protocol
- **`Session` Protocol**: Clean, async interface that any storage
backend can implement
- **Type-safe design**: Full type hints and runtime validation
- **Standard operations**: `get_items()`, `add_items()`, `pop_item()`,
`clear_session()`
- **Extensibility-first**: Designed for third-party implementations

### Reference Implementation
- **`SQLiteSession` Class**: Production-ready SQLite implementation
- **Automatic schema management**: Creates tables and indexes
automatically
- **Connection pooling**: Thread-safe operations with proper resource
management
- **Flexible storage**: In-memory or persistent file-based databases

### Runner Integration
- **New `session` parameter**: Drop-in addition to existing `Runner`
methods
- **Backward compatibility**: Zero breaking changes to existing code
- **Automatic history management**: Prepends conversation history before
each run

## Session Protocol for Library Authors

The Session protocol provides a clean interface for implementing custom
storage backends:

```python
from agents.memory import Session
from typing import List

class MyCustomSession:
    """Custom session implementation following the Session protocol."""

    def __init__(self, session_id: str):
        self.session_id = session_id
        # Your initialization here

    async def get_items(self, limit: int | None = None) -> List[dict]:
        """Retrieve conversation history for this session."""
        # Your implementation here
        pass

    async def add_items(self, items: List[dict]) -> None:
        """Store new items for this session."""
        # Your implementation here
        pass

    async def pop_item(self) -> dict | None:
        """Remove and return the most recent item from this session."""
        # Your implementation here
        pass

    async def clear_session(self) -> None:
        """Clear all items for this session."""
        # Your implementation here
        pass

# Works seamlessly with any custom implementation
result = await Runner.run(agent, "Hello", session=MyCustomSession("session_123"))
```

### Example Third-Party Implementations

```python
# Redis-based session (hypothetical library implementation)
from redis_sessions import RedisSession
session = RedisSession("user_123", redis_url="redis://localhost:6379")

# PostgreSQL-based session (hypothetical library implementation) 
from postgres_sessions import PostgreSQLSession
session = PostgreSQLSession("user_123", connection_string="postgresql://...")

# Cloud-based session (hypothetical library implementation)
from cloud_sessions import CloudSession
session = CloudSession("user_123", api_key="...", region="us-east-1")

# All work identically with the Runner
result = await Runner.run(agent, "Hello", session=session)
```

## Benefits

### For Application Developers
- **Reduces boilerplate**: No more manual `.to_input_list()` management
- **Prevents memory leaks**: Automatic cleanup and organized storage
- **Easier debugging**: Clear conversation history tracking
- **Flexible storage**: Choose the right backend for your needs

### For Library Authors
- **Clean integration**: Simple protocol to implement for any storage
backend
- **Type safety**: Full type hints and runtime validation
- **Async-first**: Modern async/await design throughout
- **Documentation**: Comprehensive examples and API reference

### For Applications
- **Better user experience**: Seamless conversation continuity
- **Scalable architecture**: Support for multiple concurrent
conversations
- **Flexible deployment**: In-memory for development, production storage
for scale
- **Multi-agent support**: Same conversation history can be shared
across different agents

## Usage Examples

### Basic Usage with SQLiteSession
```python
from agents import Agent, Runner, SQLiteSession

agent = Agent(name="Assistant", instructions="Reply concisely.")
session = SQLiteSession("conversation_123")

# Conversation flows naturally
await Runner.run(agent, "Hi, I'm planning a trip to Japan", session=session)
await Runner.run(agent, "What's the best time to visit?", session=session)
await Runner.run(agent, "How about cherry blossom season?", session=session)
```

### Multiple Sessions with Isolation
```python
# Different users get separate conversation histories
session_alice = SQLiteSession("user_alice")
session_bob = SQLiteSession("user_bob")

# Completely isolated conversations
await Runner.run(agent, "I like pizza", session=session_alice)
await Runner.run(agent, "I like sushi", session=session_bob)
```

### Persistent vs In-Memory Storage
```python
# In-memory database (lost when process ends)
session = SQLiteSession("user_123")

# Persistent file-based database
session = SQLiteSession("user_123", "conversations.db")
```

### Session Management Operations
```python
session = SQLiteSession("user_123")

# Get all items in a session
items = await session.get_items()

# Add new items to a session
new_items = [
    {"role": "user", "content": "Hello"},
    {"role": "assistant", "content": "Hi there!"}
]
await session.add_items(new_items)

# Remove and return the most recent item (useful for corrections)
last_item = await session.pop_item()

# Clear all items from a session
await session.clear_session()
```

### Message Correction Pattern
```python
# User wants to correct their last question
user_message = await session.pop_item()  # Remove user's question
assistant_message = await session.pop_item()  # Remove agent's response

# Ask a corrected question
result = await Runner.run(
    agent,
    "What's 2 + 3?",  # Corrected question
    session=session
)
```

## Technical Details

### Session Protocol Design
- **Async-first**: All operations are async for non-blocking I/O
- **Type-safe**: Full type hints with runtime validation
- **Error handling**: Graceful degradation and detailed error messages
- **Resource management**: Proper cleanup and connection handling

### SQLiteSession Implementation
- **Thread-safe operations** with connection pooling
- **Automatic schema management** with proper indexing
- **JSON serialization** for message storage
- **Memory-efficient** conversation retrieval and storage
- **Cross-platform compatibility**

## Breaking Changes

None. This is a purely additive feature that doesn't affect existing
functionality.

## Documentation

- Updated core concepts in `docs/index.md` to highlight Sessions as a
key primitive
- New comprehensive guide at `docs/sessions.md` with protocol
implementation examples
- Enhanced `docs/running_agents.md` with automatic vs manual
conversation management
- Full API reference integration via `docs/ref/memory.md`
- Implementation guide for library authors

Sessions represent a significant architectural improvement for building
conversational AI applications with the Agents SDK. The extensible
Session protocol enables the ecosystem to provide specialized storage
backends while maintaining a consistent, simple API for application
developers.

---------

Co-authored-by: Rohan Mehta <rm@openai.com>
2025-07-10 12:18:49 -04:00
Daniel Hashmi f09874c6be Fix agent lifecycle example output to reflect correct hook execution order (#1015)
**Problem:**
The expected output in the agent lifecycle example incorrectly shows
agent start hooks (`on_start`) running after tool execution and multiple
times for the same agent. This misleads developers about when these
lifecycle events actually occur.

**Solution:**
Updated the expected output to accurately reflect the OpenAI Agents
framework behavior:
- Agent start hooks run immediately when an agent begins execution
- Start hooks only run once per agent activation, controlled by the
`should_run_agent_start_hooks` flag
- After handoffs, the new agent's start hook runs as the first event
2025-07-08 11:14:16 -04:00
CCM 0475450d59 replace .py file with .ipynb for Jupyter example (#262)
Co-authored-by: chenchaomin <chenchaomin@guazi.com>
2025-06-27 10:39:49 -04:00
Rohan Mehta 2b9b8f7e73 Prompts support (#876)
Add support for the new openai prompts feature.
2025-06-16 15:47:48 -04:00
Rohan Mehta e3698f32b1 Enable non-strict output types (#539)
See #528, some folks are having issues because their output types are
not strict-compatible.

My approach was:
1. Create `AgentOutputSchemaBase`, which represents the base methods for
an output type - the json schema + validation
2. Make the existing `AgentOutputSchema` subclass
`AgentOutputSchemaBase`
3. Allow users to pass a `AgentOutputSchemaBase` to
`Agent(output_type=...)`
2025-04-21 11:58:36 -04:00
Rohan Mehta 4b8472da7e Examples for image inputs (#553) 2025-04-21 11:14:25 -04:00
Rohan Mehta f329eef7e8 Examples and tests for previous_response_id (#512)
Examples + tests
2025-04-15 12:46:31 -04:00
Raduan77 65264b6b8a fix typos in /examples 2025-03-20 11:23:57 +01:00
Rohan Mehta 10aa5555af Introduce tool_use_behavior on agents 2025-03-18 21:55:12 -04:00
Rohan Mehta fad23f1a3d fix typo in agent_lifecycle_example.py (#161) 2025-03-17 15:59:00 -04:00
Rohan Mehta 09d70c074d utils directory 2025-03-16 18:48:45 -04:00
CCM 8540b1e65b fix typo in agent_lifecycle_example.py 2025-03-14 19:14:26 +08:00
Dmitry Pimenov 26f9cb42de fixing mypy error 2025-03-12 17:30:59 -07:00
Dmitry Pimenov 65546703cb fixing lint issues 2025-03-12 17:28:44 -07:00
Dmitry Pimenov 536d7826d5 added a Jupyter example to clarify how to use the SDK with an existing event loop 2025-03-12 17:24:35 -07:00
Rohan Mehta aaec57a426 Initial commit 2025-03-11 09:42:28 -07:00