Skip to content

Commit 727e729

Browse files
authored
docs: document v0.22.0 behavior changes (#4522)
1 parent 4df9ecf commit 727e729

8 files changed

Lines changed: 44 additions & 2 deletions

File tree

docs/agents.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ robot_agent = pirate_agent.clone(
319319
)
320320
```
321321

322+
`clone()` uses `dataclasses.replace`, so it performs a shallow copy. A list attribute that you do not override, such as `tools`, `handoffs`, `mcp_servers`, `input_guardrails`, or `output_guardrails`, remains the exact list held by the original agent. Mutating that list through either agent therefore affects both agents. To give the clone an independent list container, pass a new list, for example `pirate_agent.clone(tools=[*pirate_agent.tools, extra_tool])`. The entries copied into that new list remain the same tool or handoff objects unless you replace those entries too.
323+
322324
## Forcing tool use
323325

324326
Supplying a list of tools doesn't always mean the LLM will use a tool. You can force tool use by setting [`ModelSettings.tool_choice`][agents.model_settings.ModelSettings.tool_choice]. Valid values are:

docs/config.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ custom_client = AsyncOpenAI(base_url="...", api_key="...")
5151
set_default_openai_client(custom_client)
5252
```
5353

54+
When you pass an explicit client to [`OpenAIProvider`][agents.models.openai_provider.OpenAIProvider], that client owns its connection and account settings. Do not also pass `api_key`, `base_url`, `websocket_base_url`, `organization`, or `project` to `OpenAIProvider`; combining `openai_client` with any of those arguments raises [`UserError`][agents.exceptions.UserError] instead of silently ignoring the duplicate value. Set the intended values when constructing `AsyncOpenAI`.
55+
5456
### Custom HTTP clients with `openai` v3
5557

5658
Version 0.21.0 requires `openai>=3.0.0,<4`. The default OpenAI provider uses HTTPX2, so most applications do not need to configure an HTTP client directly. If your application passes `http_client=` to `AsyncOpenAI`, use HTTPX2 types for the custom client and its transport-facing options:

docs/guardrails.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ Output guardrails run in 3 steps:
5353

5454
An output tripwire and an exception raised by the guardrail function have different session behavior. A tripwire rejects the candidate final output. When a tripwire fires, the runner asks the configured session to persist already-completed tool call and tool output items, together with any reasoning context required to replay those calls, while excluding the rejected candidate final output. The runner applies this tripwire rule to both streaming and non-streaming runs. When the guardrail function raises an exception instead of returning a tripwire result, the runner treats the verdict as unknown and asks the configured session to persist the completed final-turn items before surfacing the guardrail exception. If that session write also fails, the session write error takes precedence. Streaming runs use the same persistence ordering as non-streaming runs and raise the terminal exception from `stream_events()`. An immediate [`RunResultStreaming.cancel()`][agents.result.RunResultStreaming.cancel] call while the output guardrail is running cancels the in-flight guardrail and does not start a final-turn session write.
5555

56+
Terminal function-tool output needs additional handling because the tool has already run before the agent-level output guardrail checks the value. When [`Agent.tool_use_behavior`][agents.agent.Agent.tool_use_behavior] makes that tool result the final output and an output tripwire rejects it, the SDK retains a replay-valid function call/output pair only when it can rebuild the pair from validated fields. The retained `function_call_output` payload is replaced with the fixed text `"Output withheld by an output guardrail."`; the original tool-output payload is not retained in the session, `RunState`, streamed result state, or sandbox memory input. The SDK does retain validated function-call metadata required for replay, including the function arguments, so that metadata can contain data that also appeared in the rejected output. Current-response [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult] objects also replace `agent_output` with the fixed text and clear `output_info`. Current-response [`ToolOutputGuardrailResult`][agents.tool_guardrails.ToolOutputGuardrailResult] objects preserve the allow/reject behavior type but replace payload-bearing `output_info` and rejection messages with the same text. Earlier accepted turns and guardrail results remain unchanged. If the response contains reasoning or another shape that the SDK cannot sanitize safely, the SDK discards the complete current-response suffix instead of retaining the rejected output payload. A guardrail function that raises an exception has not returned a rejection verdict, so the completed terminal-tool turn follows the exception persistence behavior described above.
57+
5658
## Tool guardrails
5759

5860
Tool guardrails wrap **`FunctionTool` instances** and let you validate or block calls to those tools before and after execution. They are configured on the tool itself and run every time that tool is invoked.

docs/release.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,19 @@ We will increment `Z` for non-breaking changes:
1919

2020
## Breaking change changelog
2121

22+
### 0.22.0
23+
24+
Version 0.22.0 tightens failure handling and data isolation for several existing APIs. Applications that construct `OpenAIProvider` with an explicit client and also pass `organization` or `project` to the provider must remove those duplicate arguments.
25+
26+
Highlights:
27+
28+
- When an agent-level output guardrail blocks final output produced directly by a terminal function tool, the SDK retains a replay-valid call/output pair only when validated fields permit safe reconstruction. The original `function_call_output` payload is replaced with the fixed text `"Output withheld by an output guardrail."` in session history, `RunState`, and streamed result state, and payload-bearing current-response guardrail metadata is cleared or replaced. If the current response contains reasoning or another unsupported shape, the SDK discards the complete current-response suffix instead. Earlier accepted turns and guardrail results remain available. See [Output guardrails](guardrails.md#output-guardrails).
29+
- Non-streaming OpenAI Responses calls now raise `ModelBehaviorError` when the returned response has terminal status `failed` or `incomplete`, matching the existing streamed terminal-event handling. This applies to `OpenAIResponsesModel` and the Responses path in `AnyLLMModel`. See [Exceptions](running_agents.md#exceptions).
30+
- [`OpenAIProvider`][agents.models.openai_provider.OpenAIProvider] now also raises `UserError` when `openai_client` is combined with `organization` or `project`. The existing conflicts with `api_key`, `base_url`, and `websocket_base_url` are unchanged. Configure these values on the explicit `AsyncOpenAI` client instead. See [API keys and clients](config.md#api-keys-and-clients).
31+
- Each `RunResult.to_state()` checkpoint now owns an independent usage snapshot. A resumed result starts with the checkpoint totals and adds its own model calls without mutating the source result or sibling checkpoints. Nested `Agent.as_tool()` resumes continue to aggregate post-resume usage into the active outer run. See [Usage in RunState checkpoints](usage.md#usage-in-runstate-checkpoints).
32+
- Agent visualization now recursively expands the tools, MCP servers, and downstream handoffs of a target registered with `handoff(agent)`, matching direct `Agent` entries in an agent's `handoffs` list. See [Generating a graph](visualization.md#generating-a-graph).
33+
- The `Agent.clone()` and `RealtimeAgent.clone()` API guidance now states their existing shallow-copy behavior precisely: list attributes that are not overridden remain the same list objects. Pass a new list when the clone must own the container independently. See [Cloning/copying agents](agents.md#cloningcopying-agents).
34+
2235
### 0.21.0
2336

2437
Version 0.21.0 requires `openai` v3 and moves the Agents SDK's OpenAI HTTP integrations to HTTPX2. Applications that use the default OpenAI client do not need to change their client setup, but applications that customize the OpenAI HTTP layer may need to migrate transport-facing code.

docs/results.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ Tool guardrails are exposed separately as [`tool_input_guardrail_results`][agent
211211

212212
These arrays accumulate across the run, so they are useful for logging decisions, storing extra guardrail metadata, or debugging why a run was blocked.
213213

214+
One redaction rule applies when an agent-level output guardrail blocks final output produced directly by a terminal function tool. For the blocked current response, `output_guardrail_results` replaces the rejected agent output and clears payload-bearing output metadata, while `tool_output_guardrail_results` replaces payload-bearing tool metadata. Earlier accepted results remain unchanged. The sanitized output-guardrail result is exposed as `guardrail_result` on [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered]. Sanitized output-guardrail and tool-output-guardrail results are also exposed through streamed result state and `RunState`; see [Output guardrails](guardrails.md#output-guardrails).
215+
214216
### Context and usage
215217

216218
[`context_wrapper`][agents.result.RunResultBase.context_wrapper] exposes your app context together with SDK-managed runtime metadata such as approvals, usage, and nested `tool_input`.

docs/running_agents.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ The SDK raises exceptions in certain cases. The full list is in [`agents.excepti
593593
- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError]: This exception occurs when the underlying model (LLM) produces unexpected or invalid outputs. This can include:
594594
- Malformed JSON: When the model provides a malformed JSON structure for tool calls or in its direct output, especially if a specific `output_type` is defined.
595595
- Unexpected tool-related failures: When the model fails to use tools in an expected manner
596+
- Failed or incomplete non-streaming Responses calls: `OpenAIResponsesModel` and the Responses path in `AnyLLMModel` raise this exception when the returned response has terminal status `failed` or `incomplete`. The exception identifies the terminal status and includes available error or incomplete details from the response.
596597
- [`ToolTimeoutError`][agents.exceptions.ToolTimeoutError]: This exception is raised when a function tool call exceeds its configured timeout and the tool uses `timeout_behavior="raise_exception"`.
597598
- [`UserError`][agents.exceptions.UserError]: This exception is raised when you (the person writing code using the SDK) make an error while using the SDK. This typically results from incorrect code implementation, invalid configuration, or misuse of the SDK's API.
598599
- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered]: `InputGuardrailTripwireTriggered` is raised when an input guardrail's conditions are met, and `OutputGuardrailTripwireTriggered` is raised when an output guardrail's conditions are met. Input guardrails check incoming messages before processing, while output guardrails check the agent's final response before delivery.

docs/usage.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,24 @@ print(second.context_wrapper.usage.total_tokens) # Usage for second run
9191

9292
Note that while sessions preserve conversation context between runs, the usage metrics returned by each `Runner.run()` call represent only that particular execution. In sessions, previous messages may be re-fed as input to each run, which affects the input token count in subsequent turns.
9393

94+
## Usage in RunState checkpoints
95+
96+
[`RunResult.to_state()`][agents.result.RunResult.to_state] captures an independent snapshot of the usage accumulated so far. A run resumed from that checkpoint starts with the captured totals and adds usage from its own model calls. The resumed run does not add those new totals to the original `RunResult` or to another checkpoint created from that result.
97+
98+
```python
99+
first = await Runner.run(agent, "First request")
100+
checkpoint_a = first.to_state()
101+
checkpoint_b = first.to_state()
102+
103+
resumed_a = await Runner.run(agent, checkpoint_a)
104+
resumed_b = await Runner.run(agent, checkpoint_b)
105+
106+
assert resumed_a.context_wrapper.usage is not first.context_wrapper.usage
107+
assert resumed_b.context_wrapper.usage is not resumed_a.context_wrapper.usage
108+
```
109+
110+
This isolation also applies to the `request_usage_entries` list inside [`Usage`][agents.usage.Usage]. A resumed nested [`Agent.as_tool()`][agents.agent.Agent.as_tool] run is the exception to independent top-level accounting: its post-resume model usage is deliberately aggregated into the active outer run's usage, just like the nested run's earlier model calls.
111+
94112
## Using usage in hooks
95113

96114
If you're using `RunHooks`, the `context` object passed to each hook contains `usage`. This lets you log usage at key lifecycle moments.

docs/visualization.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ You can generate an agent visualization using the `draw_graph` function. This fu
2424
```python
2525
import os
2626

27-
from agents import Agent
27+
from agents import Agent, handoff
2828
from agents.decorators import tool
2929
from agents.mcp.server import MCPServerStdio
3030
from agents.extensions.visualization import draw_graph
@@ -56,7 +56,7 @@ mcp_server = MCPServerStdio(
5656
triage_agent = Agent(
5757
name="Triage agent",
5858
instructions="Handoff to the appropriate agent based on the language of the request.",
59-
handoffs=[spanish_agent, english_agent],
59+
handoffs=[handoff(spanish_agent), handoff(english_agent)],
6060
tools=[get_weather],
6161
mcp_servers=[mcp_server],
6262
)
@@ -68,6 +68,8 @@ draw_graph(triage_agent)
6868

6969
This generates a graph that visually represents the structure of the **triage agent** and its connections to sub-agents and tools.
7070

71+
`draw_graph()` recursively expands target agents supplied directly in `handoffs` or registered through `handoff(agent)`. In both forms, the graph includes each target's tools, MCP servers, and downstream handoffs. A custom `Handoff` without an available target `Agent` is rendered as a named destination only, so the graph cannot expand resources behind that destination.
72+
7173

7274
## Understanding the visualization
7375

0 commit comments

Comments
 (0)