Skip to content

Commit 4605706

Browse files
authored
Merge branch 'main' into bugfixed-unhandled-reasoning-content
2 parents 88a0373 + 7e632c2 commit 4605706

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+2269
-750
lines changed

.github/codex/home/config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
model = "o3"

.github/codex/labels/codex-attempt.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Attempt to solve the reported issue.
2+
3+
If a code change is required, create a new branch, commit the fix, and open a pull request that resolves the problem.
4+
5+
Here is the original GitHub issue that triggered this run:
6+
7+
### {CODEX_ACTION_ISSUE_TITLE}
8+
9+
{CODEX_ACTION_ISSUE_BODY}

.github/codex/labels/codex-review.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Review this PR and respond with a very concise final message, formatted in Markdown.
2+
3+
There should be a summary of the changes (1-2 sentences) and a few bullet points if necessary.
4+
5+
Then provide the **review** (1-2 sentences plus bullet points, friendly tone).
6+
7+
{CODEX_ACTION_GITHUB_EVENT_PATH} contains the JSON that triggered this GitHub workflow. It contains the `base` and `head` refs that define this PR. Both refs are available locally.

.github/codex/labels/codex-triage.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Troubleshoot whether the reported issue is valid.
2+
3+
Provide a concise and respectful comment summarizing the findings.
4+
5+
### {CODEX_ACTION_ISSUE_TITLE}
6+
7+
{CODEX_ACTION_ISSUE_BODY}

.github/workflows/codex.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Codex
2+
3+
on:
4+
issues:
5+
types: [opened, labeled]
6+
pull_request:
7+
branches: [main]
8+
types: [labeled]
9+
10+
jobs:
11+
codex:
12+
# This `if` check provides complex filtering logic to avoid running Codex
13+
# on every PR. Admittedly, one thing this does not verify is whether the
14+
# sender has write access to the repo: that must be done as part of a
15+
# runtime step.
16+
#
17+
# Note the label values should match the ones in the .github/codex/labels
18+
# folder.
19+
if: |
20+
(github.event_name == 'issues' && (
21+
(github.event.action == 'labeled' && (github.event.label.name == 'codex-attempt' || github.event.label.name == 'codex-triage'))
22+
)) ||
23+
(github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'codex-review')
24+
runs-on: ubuntu-latest
25+
permissions:
26+
contents: write # can push or create branches
27+
issues: write # for comments + labels on issues/PRs
28+
pull-requests: write # for PR comments/labels
29+
steps:
30+
# TODO: Consider adding an optional mode (--dry-run?) to actions/codex
31+
# that verifies whether Codex should actually be run for this event.
32+
# (For example, it may be rejected because the sender does not have
33+
# write access to the repo.) The benefit would be two-fold:
34+
# 1. As the first step of this job, it gives us a chance to add a reaction
35+
# or comment to the PR/issue ASAP to "ack" the request.
36+
# 2. It saves resources by skipping the clone and setup steps below if
37+
# Codex is not going to run.
38+
39+
- name: Checkout repository
40+
uses: actions/checkout@v4
41+
42+
# We install the dependencies like we would for an ordinary CI job,
43+
# particularly because Codex will not have network access to install
44+
# these dependencies.
45+
- name: Setup uv
46+
uses: astral-sh/setup-uv@v5
47+
with:
48+
enable-cache: true
49+
50+
- name: Install dependencies
51+
run: make sync
52+
53+
# Note it is possible that the `verify` step internal to Run Codex will
54+
# fail, in which case the work to setup the repo was worthless :(
55+
- name: Run Codex
56+
uses: openai/codex/.github/actions/codex@main
57+
with:
58+
openai_api_key: ${{ secrets.PROD_OPENAI_API_KEY }}
59+
github_token: ${{ secrets.GITHUB_TOKEN }}
60+
codex_home: ./.github/codex/home

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ old_version_tests:
4444

4545
.PHONY: build-docs
4646
build-docs:
47+
uv run docs/scripts/generate_ref_files.py
4748
uv run mkdocs build
4849

4950
.PHONY: build-full-docs

README.md

Lines changed: 114 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -17,125 +17,19 @@ The OpenAI Agents SDK is a lightweight yet powerful framework for building multi
1717

1818
Explore the [examples](examples) directory to see the SDK in action, and read our [documentation](https://openai.github.io/openai-agents-python/) for more details.
1919

20-
## Sessions
21-
22-
The Agents SDK provides built-in session memory to automatically maintain conversation history across multiple agent runs, eliminating the need to manually handle `.to_input_list()` between turns.
23-
24-
### Quick start
25-
26-
```python
27-
from agents import Agent, Runner, SQLiteSession
28-
29-
# Create agent
30-
agent = Agent(
31-
name="Assistant",
32-
instructions="Reply very concisely.",
33-
)
34-
35-
# Create a session instance
36-
session = SQLiteSession("conversation_123")
37-
38-
# First turn
39-
result = await Runner.run(
40-
agent,
41-
"What city is the Golden Gate Bridge in?",
42-
session=session
43-
)
44-
print(result.final_output) # "San Francisco"
45-
46-
# Second turn - agent automatically remembers previous context
47-
result = await Runner.run(
48-
agent,
49-
"What state is it in?",
50-
session=session
51-
)
52-
print(result.final_output) # "California"
53-
54-
# Also works with synchronous runner
55-
result = Runner.run_sync(
56-
agent,
57-
"What's the population?",
58-
session=session
59-
)
60-
print(result.final_output) # "Approximately 39 million"
61-
```
62-
63-
### Session options
64-
65-
- **No memory** (default): No session memory when session parameter is omitted
66-
- **`session: Session = DatabaseSession(...)`**: Use a Session instance to manage conversation history
67-
68-
```python
69-
from agents import Agent, Runner, SQLiteSession
70-
71-
# Custom SQLite database file
72-
session = SQLiteSession("user_123", "conversations.db")
73-
agent = Agent(name="Assistant")
74-
75-
# Different session IDs maintain separate conversation histories
76-
result1 = await Runner.run(
77-
agent,
78-
"Hello",
79-
session=session
80-
)
81-
result2 = await Runner.run(
82-
agent,
83-
"Hello",
84-
session=SQLiteSession("user_456", "conversations.db")
85-
)
86-
```
87-
88-
### Custom session implementations
89-
90-
You can implement your own session memory by creating a class that follows the `Session` protocol:
91-
92-
```python
93-
from agents.memory import Session
94-
from typing import List
95-
96-
class MyCustomSession:
97-
"""Custom session implementation following the Session protocol."""
98-
99-
def __init__(self, session_id: str):
100-
self.session_id = session_id
101-
# Your initialization here
102-
103-
async def get_items(self, limit: int | None = None) -> List[dict]:
104-
# Retrieve conversation history for the session
105-
pass
106-
107-
async def add_items(self, items: List[dict]) -> None:
108-
# Store new items for the session
109-
pass
110-
111-
async def pop_item(self) -> dict | None:
112-
# Remove and return the most recent item from the session
113-
pass
114-
115-
async def clear_session(self) -> None:
116-
# Clear all items for the session
117-
pass
118-
119-
# Use your custom session
120-
agent = Agent(name="Assistant")
121-
result = await Runner.run(
122-
agent,
123-
"Hello",
124-
session=MyCustomSession("my_session")
125-
)
126-
```
127-
12820
## Get started
12921

13022
1. Set up your Python environment
13123

132-
- Option A: Using venv (traditional method)
24+
- Option A: Using venv (traditional method)
25+
13326
```bash
13427
python -m venv env
13528
source env/bin/activate # On Windows: env\Scripts\activate
13629
```
13730

138-
- Option B: Using uv (recommended)
31+
- Option B: Using uv (recommended)
32+
13933
```bash
14034
uv venv
14135
source .venv/bin/activate # On Windows: .venv\Scripts\activate
@@ -263,6 +157,114 @@ The Agents SDK is designed to be highly flexible, allowing you to model a wide r
263157

264158
The Agents SDK automatically traces your agent runs, making it easy to track and debug the behavior of your agents. Tracing is extensible by design, supporting custom spans and a wide variety of external destinations, including [Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents), [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk), [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk), [Scorecard](https://docs.scorecard.io/docs/documentation/features/tracing#openai-agents-sdk-integration), and [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent). For more details about how to customize or disable tracing, see [Tracing](http://openai.github.io/openai-agents-python/tracing), which also includes a larger list of [external tracing processors](http://openai.github.io/openai-agents-python/tracing/#external-tracing-processors-list).
265159

160+
## Sessions
161+
162+
The Agents SDK provides built-in session memory to automatically maintain conversation history across multiple agent runs, eliminating the need to manually handle `.to_input_list()` between turns.
163+
164+
### Quick start
165+
166+
```python
167+
from agents import Agent, Runner, SQLiteSession
168+
169+
# Create agent
170+
agent = Agent(
171+
name="Assistant",
172+
instructions="Reply very concisely.",
173+
)
174+
175+
# Create a session instance
176+
session = SQLiteSession("conversation_123")
177+
178+
# First turn
179+
result = await Runner.run(
180+
agent,
181+
"What city is the Golden Gate Bridge in?",
182+
session=session
183+
)
184+
print(result.final_output) # "San Francisco"
185+
186+
# Second turn - agent automatically remembers previous context
187+
result = await Runner.run(
188+
agent,
189+
"What state is it in?",
190+
session=session
191+
)
192+
print(result.final_output) # "California"
193+
194+
# Also works with synchronous runner
195+
result = Runner.run_sync(
196+
agent,
197+
"What's the population?",
198+
session=session
199+
)
200+
print(result.final_output) # "Approximately 39 million"
201+
```
202+
203+
### Session options
204+
205+
- **No memory** (default): No session memory when session parameter is omitted
206+
- **`session: Session = DatabaseSession(...)`**: Use a Session instance to manage conversation history
207+
208+
```python
209+
from agents import Agent, Runner, SQLiteSession
210+
211+
# Custom SQLite database file
212+
session = SQLiteSession("user_123", "conversations.db")
213+
agent = Agent(name="Assistant")
214+
215+
# Different session IDs maintain separate conversation histories
216+
result1 = await Runner.run(
217+
agent,
218+
"Hello",
219+
session=session
220+
)
221+
result2 = await Runner.run(
222+
agent,
223+
"Hello",
224+
session=SQLiteSession("user_456", "conversations.db")
225+
)
226+
```
227+
228+
### Custom session implementations
229+
230+
You can implement your own session memory by creating a class that follows the `Session` protocol:
231+
232+
```python
233+
from agents.memory import Session
234+
from typing import List
235+
236+
class MyCustomSession:
237+
"""Custom session implementation following the Session protocol."""
238+
239+
def __init__(self, session_id: str):
240+
self.session_id = session_id
241+
# Your initialization here
242+
243+
async def get_items(self, limit: int | None = None) -> List[dict]:
244+
# Retrieve conversation history for the session
245+
pass
246+
247+
async def add_items(self, items: List[dict]) -> None:
248+
# Store new items for the session
249+
pass
250+
251+
async def pop_item(self) -> dict | None:
252+
# Remove and return the most recent item from the session
253+
pass
254+
255+
async def clear_session(self) -> None:
256+
# Clear all items for the session
257+
pass
258+
259+
# Use your custom session
260+
agent = Agent(name="Assistant")
261+
result = await Runner.run(
262+
agent,
263+
"Hello",
264+
session=MyCustomSession("my_session")
265+
)
266+
```
267+
266268
## Development (only needed if you need to edit the SDK/examples)
267269

268270
0. Ensure you have [`uv`](https://docs.astral.sh/uv/) installed.
@@ -284,6 +286,7 @@ make check # run tests linter and typechecker
284286
```
285287

286288
Or to run them individually:
289+
287290
```
288291
make tests # run tests
289292
make mypy # run typechecker
@@ -296,6 +299,7 @@ make format-check # run style checker
296299
We'd like to acknowledge the excellent work of the open-source community, especially:
297300

298301
- [Pydantic](https://docs.pydantic.dev/latest/) (data validation) and [PydanticAI](https://ai.pydantic.dev/) (advanced agent framework)
302+
- [LiteLLM](https://github.com/BerriAI/litellm) (unified interface for 100+ LLMs)
299303
- [MkDocs](https://github.com/squidfunk/mkdocs-material)
300304
- [Griffe](https://github.com/mkdocstrings/griffe)
301305
- [uv](https://github.com/astral-sh/uv) and [ruff](https://github.com/astral-sh/ruff)

docs/agents.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ from agents import Agent, ModelSettings, function_tool
1616

1717
@function_tool
1818
def get_weather(city: str) -> str:
19+
"""returns weather info for the specified city."""
1920
return f"The weather in {city} is sunny"
2021

2122
agent = Agent(

0 commit comments

Comments
 (0)