docs: improve translation source clarity (#4306)
This commit is contained in:
+8
-8
@@ -2,7 +2,7 @@
|
||||
|
||||
Agents are the core building block in your apps. An agent is a large language model (LLM) configured with instructions, tools, and optional runtime behavior such as handoffs, guardrails, and structured outputs.
|
||||
|
||||
Use this page when you want to define or customize a single plain `Agent`. If you are deciding how multiple agents should collaborate, read [Agent orchestration](multi_agent.md). If the agent should run inside an isolated workspace with manifest-defined files and sandbox-native capabilities, read [Sandbox agent concepts](sandbox/guide.md).
|
||||
Use this page when you want to define or customize a single base `Agent` rather than a `SandboxAgent`. If you are deciding how multiple agents should collaborate, read [Agent orchestration](multi_agent.md). If the agent should run inside an isolated workspace with manifest-defined files and sandbox-native capabilities, read [Sandbox agent concepts](sandbox/guide.md).
|
||||
|
||||
The SDK uses the Responses API by default for OpenAI models, but the distinction here is orchestration: `Agent` plus `Runner` lets the SDK manage turns, tools, guardrails, handoffs, and sessions for you. If you want to own that loop yourself, use the Responses API directly instead.
|
||||
|
||||
@@ -35,8 +35,8 @@ The most common properties of an agent are:
|
||||
| `model` | no | Which LLM to use. See [Models](models/index.md). |
|
||||
| `model_settings` | no | Model tuning parameters such as `temperature`, `top_p`, and `tool_choice`. |
|
||||
| `tools` | no | Tools the agent can call. See [Tools](tools.md). |
|
||||
| `mcp_servers` | no | MCP-backed tools for the agent. See the [MCP guide](mcp.md). |
|
||||
| `mcp_config` | no | Fine-tune how MCP tools are prepared, such as strict schema conversion and MCP failure formatting. See the [MCP guide](mcp.md#agent-level-mcp-configuration). |
|
||||
| `mcp_servers` | no | MCP servers that provide MCP-backed tools to the agent. See the [MCP guide](mcp.md). |
|
||||
| `mcp_config` | no | Fine-tune how MCP tools are prepared, such as converting their schemas to strict mode and formatting MCP failures. See the [MCP guide](mcp.md#agent-level-mcp-configuration). |
|
||||
| `input_guardrails` | no | Guardrails that run on the first user input for this agent chain. See [Guardrails](guardrails.md). |
|
||||
| `output_guardrails` | no | Guardrails that run on the final output for this agent. See [Guardrails](guardrails.md). |
|
||||
| `output_type` | no | Structured output type instead of plain text. See [Output types](#output-types). |
|
||||
@@ -65,7 +65,7 @@ Everything in this section applies to `Agent`. `SandboxAgent` builds on the same
|
||||
|
||||
## Prompt templates
|
||||
|
||||
You can reference a prompt template created in the OpenAI platform by setting `prompt`. This works with OpenAI models using the Responses API.
|
||||
You can reference a prompt template created in the OpenAI platform by setting `prompt`. This works when OpenAI models are accessed through the Responses API.
|
||||
|
||||
To use it, please:
|
||||
|
||||
@@ -215,7 +215,7 @@ customer_facing_agent = Agent(
|
||||
|
||||
### Handoffs
|
||||
|
||||
Handoffs are sub‑agents the agent can delegate to. When a handoff occurs, the delegated agent receives the conversation history and takes over the conversation. This pattern enables modular, specialized agents that excel at a single task. Read more in the [handoffs](handoffs.md) documentation.
|
||||
Configured handoff targets are sub‑agents to which the agent can delegate. When a handoff occurs, the delegated agent receives the conversation history and takes over the conversation. This pattern enables modular, specialized agents that excel at a single task. Read more in the [handoffs](handoffs.md) documentation.
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
@@ -269,12 +269,12 @@ The callback context also changes depending on the event:
|
||||
|
||||
Typical hook timing:
|
||||
|
||||
- `on_agent_start` / `on_agent_end`: when a specific agent begins or finishes producing a final output.
|
||||
- `on_agent_start`: when a specific agent begins running; `on_agent_end`: when that agent finishes producing a final output.
|
||||
- `on_llm_start` / `on_llm_end`: immediately around each model call.
|
||||
- `on_tool_start` / `on_tool_end`: around each local tool invocation. For function tools, the hook `context` is typically a `ToolContext`, so you can inspect tool-call metadata such as `tool_call_id`.
|
||||
- `on_handoff`: when control moves from one agent to another.
|
||||
|
||||
Use `RunHooks` when you want a single observer for the whole workflow, and `AgentHooks` when one agent needs custom side effects.
|
||||
Use `RunHooks` when you want a single observer for the whole workflow, and `AgentHooks` when you want lifecycle callbacks scoped to a specific agent.
|
||||
|
||||
```python
|
||||
from agents import Agent, RunHooks, Runner
|
||||
@@ -396,7 +396,7 @@ agent = Agent(
|
||||
)
|
||||
```
|
||||
|
||||
- `ToolsToFinalOutputFunction`: A custom function that processes tool results and decides whether to stop or continue with the LLM.
|
||||
- `ToolsToFinalOutputFunction`: A custom function that processes tool results and decides whether to end the run with a final output or continue processing with the LLM.
|
||||
|
||||
```python
|
||||
from agents import Agent, FunctionToolResult, RunContextWrapper
|
||||
|
||||
+6
-6
@@ -14,7 +14,7 @@ If you need to configure a specific agent or run instead, start with:
|
||||
|
||||
## Configuration objects and dictionaries
|
||||
|
||||
SDK-owned configuration parameters generally accept either their typed settings object or a dictionary containing the same fields. This applies across agent, run, model, session, sandbox, and voice configuration boundaries whose type annotations include a dictionary. Nested SDK-owned settings can also use dictionaries.
|
||||
Configuration parameters defined by the SDK generally accept either their typed settings object or a dictionary containing the same fields. This applies across agent, run, model, session, sandbox, and voice configuration boundaries whose type annotations include a dictionary. Nested settings types defined by the SDK can also use dictionaries.
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
@@ -29,7 +29,7 @@ agent = Agent(
|
||||
)
|
||||
```
|
||||
|
||||
The SDK normalizes these dictionaries into the corresponding settings objects. Unknown fields in SDK-owned dataclass configurations raise `TypeError`, which helps catch misspelled option names early. Check the parameter's type annotation or API reference to confirm whether a specific boundary accepts a dictionary.
|
||||
The SDK normalizes these dictionaries into the corresponding settings objects. Unknown fields in dataclass configuration types defined by the SDK raise `TypeError`, which helps catch misspelled option names early. Check the parameter's type annotation or API reference to confirm whether a specific boundary accepts a dictionary.
|
||||
|
||||
## API keys and clients
|
||||
|
||||
@@ -68,7 +68,7 @@ set_default_openai_api("chat_completions")
|
||||
|
||||
## OpenAI provider defaults
|
||||
|
||||
OpenAI-backed providers also read SDK-wide defaults when they resolve model names. Use [`set_default_openai_responses_transport()`][agents.set_default_openai_responses_transport] to make OpenAI Responses models use websocket transport by default:
|
||||
Providers that use the SDK's OpenAI backend also read SDK-wide defaults when they map model-name strings to models. Use [`set_default_openai_responses_transport()`][agents.set_default_openai_responses_transport] to make OpenAI Responses models use websocket transport by default:
|
||||
|
||||
```python
|
||||
from agents import set_default_openai_responses_transport
|
||||
@@ -76,7 +76,7 @@ from agents import set_default_openai_responses_transport
|
||||
set_default_openai_responses_transport("websocket")
|
||||
```
|
||||
|
||||
This affects OpenAI Responses models resolved by the default OpenAI provider. For provider-level setup, connection reuse, keepalive options, and custom websocket endpoints, see [Responses WebSocket transport](models/index.md#responses-websocket-transport).
|
||||
This affects OpenAI Responses models that result when the default OpenAI provider resolves a model name. For provider-level setup, connection reuse, keepalive options, and custom websocket endpoints, see [Responses WebSocket transport](models/index.md#responses-websocket-transport).
|
||||
|
||||
If your OpenAI setup expects provider-level agent registration metadata, configure a default harness ID once at startup:
|
||||
|
||||
@@ -96,7 +96,7 @@ set_default_openai_agent_registration(
|
||||
)
|
||||
```
|
||||
|
||||
If no SDK default is set, OpenAI-backed providers fall back to the `OPENAI_AGENT_HARNESS_ID` environment variable. When a harness ID is configured, the SDK adds it to trace metadata as `agent_harness_id` unless that key is already present in `RunConfig.trace_metadata`.
|
||||
If no SDK default is set, providers that use the SDK's OpenAI backend fall back to the `OPENAI_AGENT_HARNESS_ID` environment variable. When a harness ID is configured, the SDK adds it to trace metadata as `agent_harness_id` unless that key is already present in `RunConfig.trace_metadata`.
|
||||
|
||||
## Tracing
|
||||
|
||||
@@ -219,4 +219,4 @@ export OPENAI_AGENTS_DONT_LOG_MODEL_DATA=0
|
||||
export OPENAI_AGENTS_DONT_LOG_TOOL_DATA=0
|
||||
```
|
||||
|
||||
These flags also control whether affected failures retain payload-bearing diagnostic details. For example, with tool-data redaction enabled, invalid function-tool arguments raise a generic `ModelBehaviorError` without chaining the underlying validation error. Setting either variable to `0` can expose raw model or tool data in logs, exception messages, exception chains, and other diagnostic context, so enable it only in a controlled development environment.
|
||||
These flags also control whether affected failures retain payload-bearing diagnostic details. For example, with tool-data redaction enabled, invalid arguments for a `FunctionTool` raise a generic `ModelBehaviorError` without chaining the underlying validation error. Setting either variable to `0` can expose raw model or tool data in logs, exception messages, exception chains, and other diagnostic context, so enable it only in a controlled development environment.
|
||||
|
||||
+3
-3
@@ -11,9 +11,9 @@ This is represented via the [`RunContextWrapper`][agents.run_context.RunContextW
|
||||
|
||||
1. You create any Python object you want. A common pattern is to use a dataclass or a Pydantic object.
|
||||
2. You pass that object to the various run methods (e.g. `Runner.run(..., context=whatever)`).
|
||||
3. All your tool calls, lifecycle hooks etc will be passed a wrapper object, `RunContextWrapper[T]`, where `T` represents your context object type which you can access via `wrapper.context`.
|
||||
3. All your tool calls, lifecycle hooks etc will be passed a wrapper object, `RunContextWrapper[T]`, where `T` represents the type of your context object; the object itself is available via `wrapper.context`.
|
||||
|
||||
For some runtime-specific callbacks, the SDK may pass a more specialized subclass of `RunContextWrapper[T]`. For example, function-tool lifecycle hooks typically receive `ToolContext`, which also exposes tool-call metadata like `tool_call_id`, `tool_name`, and `tool_arguments`.
|
||||
For some runtime-specific callbacks, the SDK may pass a more specialized subclass of `RunContextWrapper[T]`. For example, lifecycle hooks for `FunctionTool` instances typically receive `ToolContext`, which also exposes tool-call metadata like `tool_call_id`, `tool_name`, and `tool_arguments`.
|
||||
|
||||
The **most important** thing to be aware of: every agent, tool function, lifecycle etc for a given agent run must use the same _type_ of context.
|
||||
|
||||
@@ -142,5 +142,5 @@ When an LLM is called, the **only** data it can see is from the conversation his
|
||||
|
||||
1. You can add it to the Agent `instructions`. This is also known as a "system prompt" or "developer message". System prompts can be static strings, or they can be dynamic functions that receive the context and output a string. This is a common tactic for information that is always useful (for example, the user's name or the current date).
|
||||
2. Add it to the `input` when calling the `Runner.run` functions. This is similar to the `instructions` tactic, but allows you to have messages that are lower in the [chain of command](https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command).
|
||||
3. Expose it via function tools. This is useful for _on-demand_ context - the LLM decides when it needs some data, and can call the tool to fetch that data.
|
||||
3. Expose it through `FunctionTool` instances. This is useful for _on-demand_ context - the LLM decides when it needs some data, and can call the tool to fetch that data.
|
||||
4. Use retrieval or web search. These are special tools that are able to fetch relevant data from files or databases (retrieval), or from the web (web search). This is useful for "grounding" the response in relevant contextual data.
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
# Examples
|
||||
|
||||
Check out a variety of sample implementations of the SDK in the examples section of the [repo](https://github.com/openai/openai-agents-python/tree/main/examples). The examples are organized into several categories that demonstrate different patterns and capabilities.
|
||||
Check out a variety of sample implementations that use the SDK in the examples section of the [repo](https://github.com/openai/openai-agents-python/tree/main/examples). The examples are organized into several categories that demonstrate different patterns and capabilities.
|
||||
|
||||
## Categories
|
||||
|
||||
@@ -12,7 +12,7 @@ Check out a variety of sample implementations of the SDK in the examples section
|
||||
- Agents as tools with structured input parameters (`examples/agent_patterns/agents_as_tools_structured.py`)
|
||||
- Parallel agent execution
|
||||
- Conditional tool usage
|
||||
- Forcing tool use with different behaviors (`examples/agent_patterns/forcing_tool_use.py`)
|
||||
- Forcing tool use while demonstrating different tool-use behaviors (`examples/agent_patterns/forcing_tool_use.py`)
|
||||
- Input/output guardrails
|
||||
- LLM as a judge
|
||||
- Routing
|
||||
@@ -25,11 +25,11 @@ Check out a variety of sample implementations of the SDK in the examples section
|
||||
|
||||
- Hello world examples (Default model, GPT-5, open-weight model)
|
||||
- Agent lifecycle management
|
||||
- Run hooks and agent hooks lifecycle example (`examples/basic/lifecycle_example.py`)
|
||||
- Agent and run lifecycle example using `RunHooks` and `AgentHooks` (`examples/basic/lifecycle_example.py`)
|
||||
- Dynamic system prompts
|
||||
- Basic tool usage (`examples/basic/tools.py`)
|
||||
- Tool input/output guardrails (`examples/basic/tool_guardrails.py`)
|
||||
- Image tool output (`examples/basic/image_tool_output.py`)
|
||||
- Returning an image as tool output (`examples/basic/image_tool_output.py`)
|
||||
- Streaming outputs (text, items, function call args)
|
||||
- Responses websocket transport with a shared session helper across turns (`examples/basic/stream_ws.py`)
|
||||
- Prompt templates
|
||||
@@ -42,7 +42,7 @@ Check out a variety of sample implementations of the SDK in the examples section
|
||||
|
||||
- **[customer_service](https://github.com/openai/openai-agents-python/tree/main/examples/customer_service):** Example customer service system for an airline.
|
||||
|
||||
- **[financial_research_agent](https://github.com/openai/openai-agents-python/tree/main/examples/financial_research_agent):** A financial research agent that demonstrates structured research workflows with agents and tools for financial data analysis.
|
||||
- **[financial_research_agent](https://github.com/openai/openai-agents-python/tree/main/examples/financial_research_agent):** A financial research agent that demonstrates structured research workflows for financial data analysis using agents and tools.
|
||||
|
||||
- **[handoffs](https://github.com/openai/openai-agents-python/tree/main/examples/handoffs):** Practical examples of agent handoffs with message filtering, including:
|
||||
|
||||
@@ -54,7 +54,7 @@ Check out a variety of sample implementations of the SDK in the examples section
|
||||
- Simple hosted MCP without approval (`examples/hosted_mcp/simple.py`)
|
||||
- MCP connectors such as Google Calendar (`examples/hosted_mcp/connectors.py`)
|
||||
- Human-in-the-loop with interruption-based approvals (`examples/hosted_mcp/human_in_the_loop.py`)
|
||||
- On-approval callback for MCP tool calls (`examples/hosted_mcp/on_approval.py`)
|
||||
- Callback for MCP tool approval requests (`examples/hosted_mcp/on_approval.py`)
|
||||
|
||||
- **[mcp](https://github.com/openai/openai-agents-python/tree/main/examples/mcp):** Learn how to build agents with MCP (Model Context Protocol), including:
|
||||
|
||||
@@ -67,7 +67,7 @@ Check out a variety of sample implementations of the SDK in the examples section
|
||||
- Streamable HTTP remote connection (`examples/mcp/streamable_http_remote_example`)
|
||||
- Custom HTTP client factory for Streamable HTTP (`examples/mcp/streamablehttp_custom_client_example`)
|
||||
- Prefetching all MCP tools with `MCPUtil.get_all_function_tools` (`examples/mcp/get_all_mcp_tools_example`)
|
||||
- MCPServerManager with FastAPI (`examples/mcp/manager_example`)
|
||||
- Using `MCPServerManager` in a FastAPI application (`examples/mcp/manager_example`)
|
||||
- MCP tool filtering (`examples/mcp/tool_filter_example`)
|
||||
|
||||
- **[memory](https://github.com/openai/openai-agents-python/tree/main/examples/memory):** Examples of different memory implementations for agents, including:
|
||||
@@ -94,7 +94,7 @@ Check out a variety of sample implementations of the SDK in the examples section
|
||||
- Web application patterns with structured text and image messages
|
||||
- Command-line audio loops and playback handling
|
||||
- Twilio Media Streams integration over WebSocket
|
||||
- Twilio SIP integration using Realtime Calls API attach flows
|
||||
- Twilio SIP integration using the Realtime Calls API's `attach` flows
|
||||
|
||||
- **[reasoning_content](https://github.com/openai/openai-agents-python/tree/main/examples/reasoning_content):** Examples demonstrating how to work with reasoning content, including:
|
||||
|
||||
@@ -112,7 +112,7 @@ Check out a variety of sample implementations of the SDK in the examples section
|
||||
- Sandbox memory and snapshot resume (`examples/sandbox/memory.py`)
|
||||
- Sandbox agents exposed as tools (`examples/sandbox/sandbox_agents_as_tools.py`)
|
||||
|
||||
- **[tools](https://github.com/openai/openai-agents-python/tree/main/examples/tools):** Learn how to implement OAI hosted tools and experimental Codex tooling such as:
|
||||
- **[tools](https://github.com/openai/openai-agents-python/tree/main/examples/tools):** Learn how to implement OpenAI-hosted tools and experimental Codex tooling. Examples include:
|
||||
|
||||
- Web search and web search with filters
|
||||
- File search
|
||||
@@ -123,11 +123,11 @@ Check out a variety of sample implementations of the SDK in the examples section
|
||||
- Hosted container shell with inline skills (`examples/tools/container_shell_inline_skill.py`)
|
||||
- Hosted container shell with skill references (`examples/tools/container_shell_skill_reference.py`)
|
||||
- Local shell with local skills (`examples/tools/local_shell_skill.py`)
|
||||
- Tool search with namespaces and deferred tools (`examples/tools/tool_search.py`)
|
||||
- Tool search with namespaces and tools that use deferred loading (`examples/tools/tool_search.py`)
|
||||
- Programmatic Tool Calling with concurrent structured tool calls (`examples/tools/programmatic_tool_calling.py`)
|
||||
- Computer use
|
||||
- Image generation
|
||||
- Experimental Codex tool workflows (`examples/tools/codex.py`)
|
||||
- Experimental Codex same-thread workflows (`examples/tools/codex_same_thread.py`)
|
||||
- Experimental Codex workflows that reuse the same Codex conversation thread (`examples/tools/codex_same_thread.py`)
|
||||
|
||||
- **[voice](https://github.com/openai/openai-agents-python/tree/main/examples/voice):** See examples of voice agents, using our TTS and STT models, including streamed voice examples.
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
# Guardrails
|
||||
|
||||
Guardrails enable you to do checks and validations of user input and agent output. For example, imagine you have an agent that uses a very smart (and hence slow/expensive) model to help with customer requests. You wouldn't want malicious users to ask the model to help them with their math homework. So, you can run a guardrail with a fast/cheap model. If the guardrail detects malicious usage, it can immediately raise an error and prevent the expensive model from running, saving you time and money (**when using blocking guardrails; for parallel guardrails, the expensive model may have already started running before the guardrail completes. See "Execution modes" below for details**).
|
||||
Guardrails enable you to do checks and validations of user input and agent output. For example, imagine you have an agent that uses a very smart (and hence slow/expensive) model to help with customer requests. You wouldn't want malicious users to ask the model to help them with their math homework. So, you can run a guardrail with a fast/cheap model. If the guardrail detects malicious usage, it can immediately raise an error, saving time and money. Blocking execution guarantees that the expensive model does not start; with parallel execution, the expensive model may already have started before the guardrail completes. See "Execution modes" below for details.
|
||||
|
||||
There are two kinds of guardrails:
|
||||
|
||||
@@ -15,7 +15,7 @@ Guardrails are attached to agents and tools, but they do not all run at the same
|
||||
- **Output guardrails** run only for the agent that produces the final output.
|
||||
- **Tool guardrails** run on every custom function-tool invocation, with input guardrails before execution and output guardrails after execution.
|
||||
|
||||
If you need checks around each custom function-tool call in a workflow that includes managers, handoffs, or delegated specialists, use tool guardrails instead of relying only on agent-level input/output guardrails.
|
||||
If you need checks before and/or after each custom function-tool call in a workflow that includes managers, handoffs, or delegated specialists, use tool guardrails instead of relying only on agent-level input/output guardrails.
|
||||
|
||||
## Input guardrails
|
||||
|
||||
@@ -33,7 +33,7 @@ Input guardrails run in 3 steps:
|
||||
|
||||
Input guardrails support two execution modes:
|
||||
|
||||
- **Parallel execution** (default, `run_in_parallel=True`): The guardrail runs concurrently with the agent's execution. This provides the best latency since both start at the same time. However, if the guardrail fails, the agent may have already consumed tokens and executed tools before being cancelled.
|
||||
- **Parallel execution** (default, `run_in_parallel=True`): The guardrail runs concurrently with the agent's execution. This provides the best latency since both start at the same time. However, if the guardrail's tripwire is triggered, the agent may have already consumed tokens and executed tools before being cancelled.
|
||||
|
||||
- **Blocking execution** (`run_in_parallel=False`): The guardrail runs and completes *before* the agent starts. If the guardrail tripwire is triggered, the agent never executes, preventing token consumption and tool execution. This is ideal for cost optimization and when you want to avoid potential side effects from tool calls.
|
||||
|
||||
@@ -53,7 +53,7 @@ Output guardrails run in 3 steps:
|
||||
|
||||
## Tool guardrails
|
||||
|
||||
Tool guardrails wrap **function tools** and let you validate or block tool calls before and after execution. They are configured on the tool itself and run every time that tool is invoked.
|
||||
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.
|
||||
|
||||
- Input tool guardrails run before the tool executes and can skip the call, replace the output with a message, or raise a tripwire.
|
||||
- Output tool guardrails run after the tool executes and can replace the output or raise a tripwire.
|
||||
@@ -68,7 +68,7 @@ If an agent input or output fails a guardrail, the guardrail can signal this wit
|
||||
|
||||
For agent-level tripwires, the exception's `guardrail_result` identifies the guardrail that triggered the tripwire. For an input tripwire raised by the runner, `exception.run_data.input_guardrail_results` contains every input guardrail result completed before the run stopped, including the result that triggered the tripwire. Output tripwires provide the equivalent accumulated results through `exception.run_data.output_guardrail_results`.
|
||||
|
||||
Tool tripwire exceptions instead expose the triggering `guardrail` and `output` directly. Their `run_data.tool_input_guardrail_results` and `run_data.tool_output_guardrail_results` lists preserve results accumulated from completed turns before the failure; the triggering result is available through the exception's `output`. Other runner-managed failures, such as `MaxTurnsExceeded`, also preserve completed tool guardrail results in these lists. After `stream_events()` raises, the streamed result exposes the same accumulated agent and tool guardrail result lists. `run_data` can be `None` when an exception is raised outside a runner-managed execution path.
|
||||
Tool tripwire exceptions instead expose the triggering `guardrail` and `output` directly. Their `run_data.tool_input_guardrail_results` and `run_data.tool_output_guardrail_results` lists preserve results accumulated from completed turns before the failure; the triggering result is available through the exception's `output`. Other runner-managed failures, such as `MaxTurnsExceeded`, also preserve completed tool guardrail results in these lists. After `stream_events()` raises an exception, the streamed result exposes the same accumulated agent and tool guardrail result lists. `run_data` can be `None` when an exception is raised outside a runner-managed execution path.
|
||||
|
||||
## Implementing a guardrail
|
||||
|
||||
|
||||
+4
-4
@@ -2,7 +2,7 @@
|
||||
|
||||
Handoffs allow an agent to delegate tasks to another agent. This is particularly useful in scenarios where different agents specialize in distinct areas. For example, a customer support app might have agents that each specifically handle tasks like order status, refunds, FAQs, etc.
|
||||
|
||||
Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Refund Agent`, the tool would be called `transfer_to_refund_agent`.
|
||||
Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Refund Agent`, the tool would be named `transfer_to_refund_agent`.
|
||||
|
||||
## Creating a handoff
|
||||
|
||||
@@ -39,7 +39,7 @@ The [`handoff()`][agents.handoffs.handoff] function lets you customize things.
|
||||
- `input_type`: The schema for the handoff tool-call arguments. When set, the parsed payload is passed to `on_handoff`.
|
||||
- `input_filter`: This lets you filter the input received by the next agent. See below for more.
|
||||
- `is_enabled`: Whether the handoff is enabled. This can be a boolean or a function that returns a boolean, allowing you to dynamically enable or disable the handoff at runtime.
|
||||
- `nest_handoff_history`: Optional per-call override for the RunConfig-level `nest_handoff_history` setting. If `None`, the value defined in the active run configuration is used instead.
|
||||
- `nest_handoff_history`: Optional per-handoff override for the RunConfig-level `nest_handoff_history` setting. If `None`, the value defined in the active run configuration is used instead.
|
||||
|
||||
The [`handoff()`][agents.handoffs.handoff] helper always transfers control to the specific `agent` you passed in. If you have multiple possible destinations, register one handoff per destination and let the model choose among them. Use a custom [`Handoff`][agents.handoffs.Handoff] only when your own handoff code must decide which agent to return at invocation time.
|
||||
|
||||
@@ -112,7 +112,7 @@ When a handoff occurs, it's as though the new agent takes over the conversation,
|
||||
- `input_items`: optional items to forward to the next agent instead of `new_items`, allowing you to filter model input while keeping `new_items` intact for session history.
|
||||
- `run_context`: the active [`RunContextWrapper`][agents.run_context.RunContextWrapper] at the time the handoff was invoked.
|
||||
|
||||
Nested handoffs are available as an opt-in beta and are disabled by default while we stabilize them. When you enable [`RunConfig.nest_handoff_history`][agents.run.RunConfig.nest_handoff_history], the runner compacts summarizable history into ordered assistant summary segments while preserving lossless message items in their original positions. Each generated summary segment uses the `<CONVERSATION HISTORY>` wrapper, and later handoffs flatten earlier generated segments before rebuilding the ordered transcript. Sessions, `RunState`, and `RunResult.to_input_list()` track exact message occurrences moved into this SDK-default history so those occurrences are not appended twice; separate identical messages are still preserved. You can provide your own mapping function via [`RunConfig.handoff_history_mapper`][agents.run.RunConfig.handoff_history_mapper] to return the exact list of input items for the next agent instead of using the built-in segmentation. The opt-in only applies when neither the handoff nor the run supplies an explicit `input_filter`, so existing code that already customizes the payload (including the examples in this repository) keeps its current behavior without changes. You can override the nesting behaviour for a single handoff by passing `nest_handoff_history=True` or `False` to [`handoff(...)`][agents.handoffs.handoff], which sets [`Handoff.nest_handoff_history`][agents.handoffs.Handoff.nest_handoff_history]. If you just need to change the wrapper text for generated summary segments, call [`set_conversation_history_wrappers`][agents.handoffs.set_conversation_history_wrappers] (and optionally [`reset_conversation_history_wrappers`][agents.handoffs.reset_conversation_history_wrappers]) before running your agents.
|
||||
Nested handoff history is available as an opt-in beta and is disabled by default while we stabilize it. When you enable [`RunConfig.nest_handoff_history`][agents.run.RunConfig.nest_handoff_history], the runner compacts summarizable history into ordered assistant summary segments while preserving lossless message items in their original positions. Each generated summary segment uses the `<CONVERSATION HISTORY>` wrapper, and later handoffs flatten earlier generated segments before rebuilding the ordered transcript. Sessions, `RunState`, and `RunResult.to_input_list()` track exact message occurrences moved into this SDK-default history so those occurrences are not appended twice; separate identical messages are still preserved. You can provide your own mapping function via [`RunConfig.handoff_history_mapper`][agents.run.RunConfig.handoff_history_mapper] to return the exact list of input items for the next agent instead of using the built-in segmentation. The opt-in applies only when neither the handoff's `input_filter` nor the active run's `RunConfig.handoff_input_filter` is set, so existing code that already customizes the payload (including the examples in this repository) keeps its current behavior without changes. You can override the nesting behaviour for a single handoff by passing `nest_handoff_history=True` or `False` to [`handoff(...)`][agents.handoffs.handoff], which sets [`Handoff.nest_handoff_history`][agents.handoffs.Handoff.nest_handoff_history]. If you just need to change the wrapper text for generated summary segments, call [`set_conversation_history_wrappers`][agents.handoffs.set_conversation_history_wrappers] before running your agents. Call [`reset_conversation_history_wrappers`][agents.handoffs.reset_conversation_history_wrappers] before a later run when you need to restore the default wrappers.
|
||||
|
||||
If both the handoff and the active [`RunConfig.handoff_input_filter`][agents.run.RunConfig.handoff_input_filter] define a filter, the per-handoff [`input_filter`][agents.handoffs.Handoff.input_filter] takes precedence for that specific handoff.
|
||||
|
||||
@@ -134,7 +134,7 @@ handoff_obj = handoff(
|
||||
)
|
||||
```
|
||||
|
||||
1. This will automatically remove all tools from the history when `FAQ agent` is called.
|
||||
1. This will automatically remove all tool-related items from the history when `FAQ agent` is called.
|
||||
|
||||
## Recommended prompts
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Human-in-the-loop
|
||||
|
||||
Use the human-in-the-loop (HITL) flow to pause agent execution until a person approves or rejects sensitive tool calls. Tools declare when they need approval, run results surface pending approvals as interruptions, and `RunState` lets you serialize and resume runs after decisions are made.
|
||||
Use the human-in-the-loop (HITL) flow to pause agent execution until a person approves or rejects sensitive tool calls. Tools declare when they need approval, run results surface pending approvals as interruptions, and `RunState` lets you serialize paused runs and resume them after decisions are made.
|
||||
|
||||
That approval surface is run-wide, not limited to the current top-level agent. The same pattern applies when the tool belongs to the current agent, to an agent reached through a handoff, or to a nested [`Agent.as_tool()`][agents.agent.Agent.as_tool] execution. In the nested `Agent.as_tool()` case, the interruption still surfaces on the outer run, so you approve or reject it on the outer `RunState` and resume the original top-level run.
|
||||
|
||||
@@ -46,7 +46,7 @@ agent = Agent(
|
||||
|
||||
1. When the model emits a tool call, the runner evaluates its approval rule (`needs_approval`, `require_approval`, or the hosted MCP equivalent).
|
||||
2. If an approval decision for that tool call is already stored in the [`RunContextWrapper`][agents.run_context.RunContextWrapper], the runner proceeds without prompting. Per-call approvals are scoped to the specific call ID; pass `always_approve=True` or `always_reject=True` to persist the same decision for future calls to that tool during the rest of the run.
|
||||
3. Otherwise, execution pauses and `RunResult.interruptions` (or `RunResultStreaming.interruptions`) contains [`ToolApprovalItem`][agents.items.ToolApprovalItem] entries with details such as `agent.name`, `tool_name`, and `arguments`. This includes approvals raised after a handoff or inside nested `Agent.as_tool()` executions.
|
||||
3. If the approval rule requires approval and no decision for that tool call is stored, execution pauses, and `RunResult.interruptions` (or `RunResultStreaming.interruptions`) contains [`ToolApprovalItem`][agents.items.ToolApprovalItem] entries with details such as `agent.name`, `tool_name`, and `arguments`. This includes approvals raised after a handoff or inside nested `Agent.as_tool()` executions.
|
||||
4. Convert the result to a `RunState` with `result.to_state()`, call `state.approve(...)` or `state.reject(...)`, and then resume with `Runner.run(agent, state)` or `Runner.run_streamed(agent, state)`, where `agent` is the original top-level agent for the run.
|
||||
5. The resumed run continues where it left off and will re-enter this flow if new approvals are needed.
|
||||
|
||||
@@ -98,7 +98,7 @@ When these callbacks return a decision, the run continues without pausing for a
|
||||
|
||||
The same interruption flow works in streaming runs. After a streamed run pauses, keep consuming [`RunResultStreaming.stream_events()`][agents.result.RunResultStreaming.stream_events] until the iterator finishes, inspect [`RunResultStreaming.interruptions`][agents.result.RunResultStreaming.interruptions], resolve them, and resume with [`Runner.run_streamed(...)`][agents.run.Runner.run_streamed] if you want the resumed output to keep streaming. See [Streaming](streaming.md) for the streamed version of this pattern.
|
||||
|
||||
If you are also using a session, keep passing the same session instance when you resume from `RunState`, or pass another session object that points at the same backing store. The resumed turn is then appended to the same stored conversation history. See [Sessions](sessions/index.md) for the session lifecycle details.
|
||||
If you are also using a session, keep passing the same session instance when you resume from `RunState`, or pass another session object configured for the same session ID and backing store. The resumed turn is then appended to the same stored conversation history. See [Sessions](sessions/index.md) for the session lifecycle details.
|
||||
|
||||
## Example: pause, approve, resume
|
||||
|
||||
@@ -169,16 +169,16 @@ if __name__ == "__main__":
|
||||
|
||||
In this example, `prompt_approval` is synchronous because it uses `input()` and is executed with `run_in_executor(...)`. If your approval source is already asynchronous (for example, an HTTP request or async database query), you can use an `async def` function and `await` it directly instead.
|
||||
|
||||
To stream output while waiting for approvals, call `Runner.run_streamed`, consume `result.stream_events()` until it completes, and then follow the same `result.to_state()` and resume steps shown above.
|
||||
To use streaming in a run that may pause for approvals, call `Runner.run_streamed`, consume `result.stream_events()` until it completes, and then follow the same `result.to_state()` and resume steps shown above.
|
||||
|
||||
## Repository patterns and examples
|
||||
|
||||
- **Streaming approvals**: `examples/agent_patterns/human_in_the_loop_stream.py` shows how to drain `stream_events()` and then approve pending tool calls before resuming with `Runner.run_streamed(agent, state)`.
|
||||
- **Custom rejection text**: `examples/agent_patterns/human_in_the_loop_custom_rejection.py` shows how to combine run-level `tool_error_formatter` with per-call `rejection_message` overrides when approvals are rejected.
|
||||
- **Agent as tool approvals**: `Agent.as_tool(..., needs_approval=...)` applies the same interruption flow when delegated agent tasks need review. Nested interruptions still surface on the outer run, so resume the original top-level agent rather than the nested one.
|
||||
- **Local shell and apply_patch tools**: `ShellTool` and `ApplyPatchTool` also support `needs_approval`. Use `state.approve(interruption, always_approve=True)` or `state.reject(..., always_reject=True)` to cache the decision for future calls. For automatic decisions, provide `on_approval` (see `examples/tools/shell.py`); for manual decisions, handle interruptions (see `examples/tools/shell_human_in_the_loop.py`). Hosted shell environments do not support `needs_approval` or `on_approval`; see the [tools guide](tools.md).
|
||||
- **Local shell and apply_patch tools**: `ShellTool` and `ApplyPatchTool` also support `needs_approval`. Use `state.approve(interruption, always_approve=True)` or `state.reject(..., always_reject=True)` to cache the decision for future calls to that tool during the rest of the run. For automatic decisions, provide `on_approval` (see `examples/tools/shell.py`); for manual decisions, handle interruptions (see `examples/tools/shell_human_in_the_loop.py`). Hosted shell environments do not support `needs_approval` or `on_approval`; see the [tools guide](tools.md).
|
||||
- **Local MCP servers**: Use `require_approval` on `MCPServerStdio` / `MCPServerSse` / `MCPServerStreamableHttp` to gate MCP tool calls (see `examples/mcp/get_all_mcp_tools_example/main.py` and `examples/mcp/tool_filter_example/main.py`).
|
||||
- **Hosted MCP servers**: Set `require_approval` to `"always"` on `HostedMCPTool` to force HITL, optionally providing `on_approval_request` to auto-approve or reject (see `examples/hosted_mcp/human_in_the_loop.py` and `examples/hosted_mcp/on_approval.py`). Use `"never"` for trusted servers (`examples/hosted_mcp/simple.py`).
|
||||
- **Hosted MCP servers**: Set `tool_config={"require_approval": "always"}` on `HostedMCPTool` to force HITL, optionally providing `on_approval_request` to auto-approve or reject (see `examples/hosted_mcp/human_in_the_loop.py` and `examples/hosted_mcp/on_approval.py`). Use `"never"` for trusted servers (`examples/hosted_mcp/simple.py`).
|
||||
- **Sessions and memory**: Pass a session to `Runner.run` so approvals and conversation history survive multiple turns. SQLite and OpenAI Conversations session variants are in `examples/memory/memory_session_hitl_example.py` and `examples/memory/openai_session_hitl_example.py`.
|
||||
- **Realtime agents**: The realtime demo exposes WebSocket messages that approve or reject tool calls via `approve_tool_call` / `reject_tool_call` on the `RealtimeSession` (see `examples/realtime/app/server.py` for the server-side handlers and [Realtime guide](realtime/guide.md#tool-approvals) for the API surface).
|
||||
|
||||
@@ -190,7 +190,7 @@ Useful serialization options:
|
||||
|
||||
- `context_serializer`: Customize how non-mapping context objects are serialized.
|
||||
- `context_deserializer`: Rebuild non-mapping context objects when loading state with `RunState.from_json(...)` or `RunState.from_string(...)`.
|
||||
- `strict_context=True`: Fail serialization or deserialization unless the context is already a mapping or you provide the appropriate serializer/deserializer.
|
||||
- `strict_context=True`: Fail serialization unless the context is already a mapping or you provide `context_serializer`; fail deserialization unless the context is already a mapping or you provide `context_deserializer`.
|
||||
- `context_override`: Replace the serialized context when loading state. This is useful when you do not want to restore the original context object, but it does not remove that context from an already serialized payload.
|
||||
- `include_tracing_api_key=True`: Include the tracing API key in the serialized trace payload when you need resumed work to keep exporting traces with the same credentials.
|
||||
|
||||
|
||||
+4
-4
@@ -18,21 +18,21 @@ The SDK has two driving design principles:
|
||||
Here are the main features of the SDK:
|
||||
|
||||
- **Agents**: Build agents with instructions, tools, guardrails, handoffs, and a built-in loop that continues until the task is complete.
|
||||
- **Sandbox agents**: Run specialists inside real isolated workspaces with manifest-defined files, sandbox client choice, and resumable sandbox sessions.
|
||||
- **Sandbox agents**: Run specialists inside real isolated workspaces. Sandbox agents support manifest-defined files, sandbox client selection, and resumable sandbox sessions.
|
||||
- **Realtime agents**: Build powerful voice agents with `gpt-realtime-2.1`, automatic interruption detection, context management, guardrails, and more.
|
||||
- **Voice agents**: Build voice pipelines that combine speech-to-text, an agent workflow, and text-to-speech.
|
||||
- **Python-first**: Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions.
|
||||
- **Agents as tools / Handoffs**: A powerful mechanism for coordinating and delegating work across multiple agents.
|
||||
- **Guardrails**: Run input validation and safety checks in parallel with agent execution, and fail fast when checks do not pass.
|
||||
- **Function tools**: Turn any Python function into a tool with automatic schema generation and Pydantic-powered validation.
|
||||
- **MCP server tool calling**: Built-in MCP server tool integration that works the same way as function tools.
|
||||
- **MCP server tool calling**: Built-in integration that exposes remote MCP tools to agents alongside function tools.
|
||||
- **Sessions**: A persistent memory layer for maintaining working context within an agent loop.
|
||||
- **Human in the loop**: Built-in mechanisms for involving humans across agent runs.
|
||||
- **Human in the loop**: Built-in mechanisms for involving humans during agent runs.
|
||||
- **Tracing**: Built-in tracing for visualizing, debugging, and monitoring workflows, with support for the OpenAI suite of evaluation, fine-tuning, and distillation tools.
|
||||
|
||||
## Agents SDK or Responses API?
|
||||
|
||||
The SDK uses the Responses API by default for OpenAI models, but it adds a higher-level runtime around model calls.
|
||||
The SDK uses the Responses API by default for OpenAI models, but it wraps model calls in a higher-level runtime.
|
||||
|
||||
Use the Responses API directly when:
|
||||
|
||||
|
||||
+5
-5
@@ -54,7 +54,7 @@ Notes:
|
||||
- `failure_error_function` controls how MCP tool call failures are surfaced to the model.
|
||||
- When `failure_error_function` is unset, the SDK uses the default tool error formatter.
|
||||
- Server-level `failure_error_function` overrides `Agent.mcp_config["failure_error_function"]` for that server.
|
||||
- `include_server_in_tool_names` is opt-in. When enabled, each local MCP tool is exposed to the model with a deterministic server-prefixed name, which helps avoid collisions when multiple MCP servers publish tools with the same name. Generated names are ASCII-safe, stay within the function-tool name length limit, and avoid existing local function tool and enabled handoff names on the same agent. The SDK still invokes the original MCP tool name on the original server.
|
||||
- `include_server_in_tool_names` is opt-in. When enabled, each local MCP tool is exposed to the model with a deterministic server-prefixed name, which helps avoid collisions when multiple MCP servers publish tools with the same name. Generated names are ASCII-safe, stay within the name-length limit for `FunctionTool` instances, and do not collide with the configured names of local `FunctionTool` instances or enabled handoffs on the same agent. The SDK still invokes the original MCP tool name on the original server.
|
||||
|
||||
## Shared patterns across transports
|
||||
|
||||
@@ -229,7 +229,7 @@ The constructor accepts additional options:
|
||||
Supported forms:
|
||||
|
||||
- `"always"` or `"never"` for all tools.
|
||||
- `True` / `False` (equivalent to always/never).
|
||||
- `True` requires approval for all tools, and `False` requires approval for none (equivalent to `"always"` and `"never"`, respectively).
|
||||
- A per-tool map, for example `{"delete_file": "always", "read_file": "never"}`.
|
||||
- A grouped object: `{"always": {"tool_names": [...]}, "never": {"tool_names": [...]}}`.
|
||||
|
||||
@@ -271,7 +271,7 @@ If your run context is a Pydantic model, dataclass, or custom class, read the te
|
||||
|
||||
### MCP tool outputs: text and images
|
||||
|
||||
When an MCP tool returns image content, the SDK maps it to image tool output entries automatically. Mixed text/image responses are forwarded as a list of output items, so agents can consume MCP image results the same way they consume image output from regular function tools.
|
||||
When an MCP tool returns image content, the SDK automatically maps it to image-type entries in the tool output. Mixed text/image responses are forwarded as a list of output items, so agents can consume MCP image results the same way they consume image output from regular function tools.
|
||||
|
||||
## 3. HTTP with SSE MCP servers
|
||||
|
||||
@@ -336,7 +336,7 @@ async with MCPServerStdio(
|
||||
|
||||
## 5. MCP server manager
|
||||
|
||||
When you have multiple MCP servers, use `MCPServerManager` to connect them up front and expose the connected subset to your agents. See the [MCPServerManager API reference](ref/mcp/manager.md) for constructor options and reconnect behavior.
|
||||
When you have multiple MCP servers, use `MCPServerManager` to connect them up front and expose the successfully connected subset of those servers to your agents. See the [MCPServerManager API reference](ref/mcp/manager.md) for constructor options and reconnect behavior.
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
@@ -449,7 +449,7 @@ agent = Agent(
|
||||
|
||||
## Pagination
|
||||
|
||||
The built-in local MCP server classes automatically follow `nextCursor` when listing tools and prompts. `list_tools()` returns the complete tool list before applying filters or populating its cache, and `list_prompts()` returns one combined result with `nextCursor=None`. If a later page fails or a server repeats a cursor, the operation raises an error instead of exposing or caching partial results.
|
||||
The built-in local MCP server classes automatically follow `nextCursor` when listing tools and prompts. `list_tools()` collects the complete tool list before applying filters or populating its cache, and `list_prompts()` returns one combined result with `nextCursor=None`. If a later page fails or a server repeats a cursor, the operation raises an error instead of exposing or caching partial results.
|
||||
|
||||
Resources remain explicitly paginated. Pass the `nextCursor` from `list_resources()` or `list_resource_templates()` back as the `cursor` argument to retrieve the next page.
|
||||
|
||||
|
||||
+14
-14
@@ -73,7 +73,7 @@ my_agent = Agent(
|
||||
|
||||
For lower latency, using `reasoning.effort="none"` with GPT-5 models is recommended.
|
||||
|
||||
GPT-5.6 also supports reasoning mode, persisted reasoning context, and the `"max"` effort level through the existing `reasoning` setting. These controls are available on the Responses API path:
|
||||
GPT-5.6 also supports reasoning mode, reasoning context carried across conversation turns, and the `"max"` effort level through the existing `reasoning` setting. These controls are available on the Responses API path:
|
||||
|
||||
```python
|
||||
from openai.types.shared import Reasoning
|
||||
@@ -94,13 +94,13 @@ agent = Agent(
|
||||
|
||||
`reasoning.mode` and `reasoning.context` are Responses-only settings. Chat Completions uses only `reasoning.effort`, and the supported effort levels depend on the model and API surface. Use the Responses API for GPT-5.6 `"max"` effort. The Chat Completions adapter ignores mode and context with a warning; set `strict_feature_validation=True` on the OpenAI provider to turn that warning into an error.
|
||||
|
||||
When using `context="all_turns"`, preserve the conversation through `previous_response_id`, a server-side conversation, or by replaying prior reasoning items. For stateless `store=False` calls, include `reasoning.encrypted_content` in the response and replay those reasoning items on the next request.
|
||||
When using `context="all_turns"`, preserve the conversation through `previous_response_id`, a server-side Responses API conversation, or by including prior reasoning items in the next request. For stateless `store=False` calls, request `reasoning.encrypted_content` in the response, then include those reasoning items as input in the next request.
|
||||
|
||||
#### ComputerTool model selection
|
||||
|
||||
If an agent includes [`ComputerTool`][agents.tool.ComputerTool], the effective model on the actual Responses request determines which computer-tool payload the SDK sends. Explicit `gpt-5.5` requests use the GA built-in `computer` tool, while explicit `computer-use-preview` requests keep the older `computer_use_preview` payload.
|
||||
|
||||
Prompt-managed calls are the main exception. If a prompt template owns the model and the SDK omits `model` from the request, the SDK defaults to the preview-compatible computer payload so it does not guess which model the prompt pins. To keep the GA path in that flow, either make `model="gpt-5.5"` explicit on the request or force the GA selector with `ModelSettings(tool_choice="computer")` or `ModelSettings(tool_choice="computer_use")`.
|
||||
Prompt-managed calls are the main exception. If a prompt template specifies the model and the SDK omits `model` from the request, the SDK defaults to the preview-compatible computer payload so it does not guess which model the prompt pins. To keep the GA path in that flow, either make `model="gpt-5.5"` explicit on the request or force the GA selector with `ModelSettings(tool_choice="computer")` or `ModelSettings(tool_choice="computer_use")`.
|
||||
|
||||
With a registered [`ComputerTool`][agents.tool.ComputerTool], `tool_choice="computer"`, `"computer_use"`, and `"computer_use_preview"` are normalized to the built-in selector that matches the effective request model. If no `ComputerTool` is registered, those strings continue to behave like ordinary function names.
|
||||
|
||||
@@ -123,7 +123,7 @@ These features are rejected on Chat Completions models and on non-Responses back
|
||||
|
||||
### Responses WebSocket transport
|
||||
|
||||
By default, OpenAI Responses API requests use HTTP transport. You can opt in to websocket transport when using OpenAI-backed models.
|
||||
By default, OpenAI Responses API requests use HTTP transport. You can opt in to websocket transport when using the OpenAI Responses provider path.
|
||||
|
||||
#### Basic setup
|
||||
|
||||
@@ -133,7 +133,7 @@ from agents import set_default_openai_responses_transport
|
||||
set_default_openai_responses_transport("websocket")
|
||||
```
|
||||
|
||||
This affects OpenAI Responses models resolved by the default OpenAI provider (including string model names such as `"gpt-5.6-sol"`).
|
||||
This affects OpenAI Responses models that result when the default OpenAI provider resolves a model name (including string model names such as `"gpt-5.6-sol"`).
|
||||
|
||||
Transport selection happens when the SDK resolves a model name into a model instance. If you pass a concrete [`Model`][agents.models.interface.Model] object, its transport is already fixed: [`OpenAIResponsesWSModel`][agents.models.openai_responses.OpenAIResponsesWSModel] uses websocket, [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel] uses HTTP, and [`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel] stays on Chat Completions. If you pass `RunConfig(model_provider=...)`, that provider controls transport selection instead of the global default.
|
||||
|
||||
@@ -160,7 +160,7 @@ result = await Runner.run(
|
||||
)
|
||||
```
|
||||
|
||||
OpenAI-backed providers also accept optional agent registration config. This is an advanced option for cases where your OpenAI setup expects provider-level registration metadata such as a harness ID.
|
||||
Providers that route through the SDK's OpenAI integration also accept optional agent registration config. This is an advanced option for cases where your OpenAI setup expects provider-level registration metadata such as a harness ID.
|
||||
|
||||
```python
|
||||
from agents import (
|
||||
@@ -227,19 +227,19 @@ If you use a custom OpenAI-compatible endpoint or proxy, websocket transport als
|
||||
|
||||
#### Notes
|
||||
|
||||
- This is the Responses API over websocket transport, not the [Realtime API](../realtime/guide.md). It does not apply to Chat Completions or non-OpenAI providers unless they support the Responses websocket `/responses` endpoint.
|
||||
- This is the Responses API over websocket transport, not the [Realtime API](../realtime/guide.md). It does not apply to Chat Completions. It applies to non-OpenAI providers only if they support the Responses websocket `/responses` endpoint.
|
||||
- Install the `websockets` package if it is not already available in your environment.
|
||||
- You can use [`Runner.run_streamed()`][agents.run.Runner.run_streamed] directly after enabling websocket transport. For multi-turn workflows where you want to reuse the same websocket connection across turns (and nested agent-as-tool calls), the [`responses_websocket_session()`][agents.responses_websocket_session] helper is recommended. See the [Running agents](../running_agents.md) guide and [`examples/basic/stream_ws.py`](https://github.com/openai/openai-agents-python/tree/main/examples/basic/stream_ws.py).
|
||||
- For long reasoning turns or networks with latency spikes, customize websocket keepalive behavior with `responses_websocket_options`. Increase `ping_timeout` to tolerate delayed pong frames, or set `ping_timeout=None` to disable heartbeat timeouts while keeping pings enabled. Prefer HTTP/SSE transport when reliability is more important than websocket latency.
|
||||
- By default the SDK disables the incoming message-size limit (`max_size=None`). For long-lived agent processes behind proxies or in memory-constrained containers, set `responses_websocket_options={"max_size": 8 * 1024 * 1024}` to bound per-message memory usage.
|
||||
- The [Responses API WebSocket service](https://developers.openai.com/api/docs/guides/websocket-mode) processes one response at a time on each connection and limits each connection to 60 minutes. Open a new connection after that limit; use multiple connections when you need parallel runs.
|
||||
- The service keeps only the most recent response in connection-local memory. A failed `4xx` or `5xx` turn evicts the referenced `previous_response_id`. After reconnecting, a stored response can still be continued when available, but `store=False` and ZDR flows have no persisted fallback. Start a new chain with `previous_response_id=None` and send the full input context, or rebuild that context from locally managed session state.
|
||||
- The service keeps only the most recent response in connection-local memory. A failed `4xx` or `5xx` turn evicts from that memory the response referenced by `previous_response_id`. After reconnecting, a stored response can still be continued when available, but `store=False` and ZDR flows have no persisted fallback. Start a new chain with `previous_response_id=None` and send the full input context, or rebuild that context from locally managed session state.
|
||||
|
||||
### Hosted multi-agent (experimental)
|
||||
|
||||
The OpenAI Responses API hosted multi-agent beta lets a GPT-5.6 root model create and coordinate server-hosted subagents. The Agents SDK can keep using its normal `Runner`: hosted orchestration stays on the service, while developer-defined function tools execute in your application.
|
||||
|
||||
This integration is experimental and uses the Responses WebSocket transport so local function outputs can be returned to an active hosted agent with `response.inject`. It requires `openai[realtime]>=2.45.0`, including a beta build that exposes `client.beta.responses.connect`. The interface and beta item schemas may change before general availability.
|
||||
This integration is experimental and uses the Responses WebSocket transport so local function outputs can be returned to an active hosted agent with `response.inject`. It requires a build of `openai[realtime]` version 2.45.0 or later that exposes `client.beta.responses.connect`. The interface and beta item schemas may change before general availability.
|
||||
|
||||
#### Configure the model
|
||||
|
||||
@@ -285,7 +285,7 @@ Hosted agent names are observational metadata, not a local routing mechanism. Ro
|
||||
|
||||
Only a message attributed to `/root` with phase `final_answer` becomes a normal final message. The experimental adapter filters subagent messages and hosted orchestration records out of the high-level `RunResult`; the SDK never executes those records as local functions.
|
||||
|
||||
Raw streaming continues to expose beta Responses events, including hosted output items and `response.inject.created` acknowledgements. The adapter divides one active provider response into SDK-visible logical model turns when a function call is ready, then resumes that same provider response after the Runner produces an output. Use `get_hosted_agent_metadata()` with a raw hosted item or a `ToolContext` to inspect attribution.
|
||||
Raw streaming continues to expose beta Responses events, including hosted output items and `response.inject.created` acknowledgements. The adapter divides one active provider response into SDK-visible logical model turns when a function call is ready, then resumes that same provider response after the Runner produces an output. Use `get_hosted_agent_metadata()` with a raw hosted item or a `ToolContext` to identify the hosted agent to which the item or tool call is attributed.
|
||||
|
||||
#### Relationship to SDK orchestration
|
||||
|
||||
@@ -314,7 +314,7 @@ If you need a non-OpenAI provider, start with the SDK's built-in provider integr
|
||||
| [`set_default_openai_client`][agents.set_default_openai_client] | One OpenAI-compatible endpoint should be the default for most or all agents | Global default |
|
||||
| [`ModelProvider`][agents.models.interface.ModelProvider] | One custom provider should apply to a single run | Per run |
|
||||
| [`Agent.model`][agents.agent.Agent.model] | Different agents need different providers or concrete model objects | Per agent |
|
||||
| Third-party adapter | You need adapter-managed provider coverage or routing that the built-in paths do not provide | See [Third-party adapters](#third-party-adapters) |
|
||||
| Third-party adapter | You need provider coverage or routing from an adapter because the built-in paths do not provide it | See [Third-party adapters](#third-party-adapters) |
|
||||
|
||||
You can integrate other LLM providers with these built-in paths:
|
||||
|
||||
@@ -604,7 +604,7 @@ The SDK uses the Responses API by default, but many other LLM providers still do
|
||||
|
||||
### Chat Completions compatibility options
|
||||
|
||||
When you route through Chat Completions, the SDK preserves compatibility by silently dropping Responses-only fields that Chat Completions cannot send, such as `previous_response_id`, `conversation_id`, prompts, or non-text-only tool outputs. If you want those mismatches to fail fast during development, enable strict feature validation on the OpenAI provider:
|
||||
When you route through Chat Completions, the SDK preserves compatibility by silently dropping Responses-only fields that Chat Completions cannot send, such as `previous_response_id`, `conversation_id`, the Responses API `prompt` field, or tool outputs that are not text-only. If you want those mismatches to fail fast during development, enable strict feature validation on the OpenAI provider:
|
||||
|
||||
```python
|
||||
from agents import Agent, OpenAIProvider, RunConfig, Runner
|
||||
@@ -659,7 +659,7 @@ You need to be aware of feature differences between model providers, or you may
|
||||
|
||||
## Third-party adapters
|
||||
|
||||
Reach for a third-party adapter only when the SDK's built-in provider integration points are not enough. If you are using OpenAI models only with this SDK, prefer the built-in [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel] path instead of Any-LLM or LiteLLM. Third-party adapters are for cases where you need to combine OpenAI models with non-OpenAI providers, or need adapter-managed provider coverage or routing that the built-in paths do not provide. Adapters add another compatibility layer between the SDK and the upstream model provider, so feature support and request semantics can vary by provider. The SDK currently includes Any-LLM and LiteLLM as best-effort, beta adapter integrations.
|
||||
Reach for a third-party adapter only when the SDK's built-in provider integration points are not enough. If you are using OpenAI models only with this SDK, prefer the built-in [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel] path instead of Any-LLM or LiteLLM. Third-party adapters are for cases where you need to combine OpenAI models with non-OpenAI providers, or need provider coverage or routing that only an adapter provides. Adapters add another compatibility layer between the SDK and the upstream model provider, so feature support and request semantics can vary by provider. The SDK currently includes Any-LLM and LiteLLM as best-effort, beta adapter integrations.
|
||||
|
||||
### Any-LLM
|
||||
|
||||
@@ -677,7 +677,7 @@ LiteLLM support is included on a best-effort, beta basis for cases where you nee
|
||||
|
||||
If you need LiteLLM, install `openai-agents[litellm]`, then start from [`examples/model_providers/litellm_auto.py`](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/litellm_auto.py) or [`examples/model_providers/litellm_provider.py`](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/litellm_provider.py). You can use `litellm/...` model names or instantiate [`LitellmModel`][agents.extensions.models.litellm_model.LitellmModel] directly.
|
||||
|
||||
Some LiteLLM-backed providers do not populate SDK usage metrics by default. If you need usage reporting, pass `ModelSettings(include_usage=True)` and validate the exact provider backend you plan to deploy if you depend on structured outputs, tool calling, usage reporting, or adapter-specific routing behavior.
|
||||
Some providers accessed through the LiteLLM adapter do not populate SDK usage metrics by default. If you need usage reporting, pass `ModelSettings(include_usage=True)` and validate the exact provider backend you plan to deploy if you depend on structured outputs, tool calling, usage reporting, or adapter-specific routing behavior.
|
||||
|
||||
If LiteLLM emits Pydantic serializer warnings for response objects, you can opt in to the SDK's compatibility patch before importing the LiteLLM adapter:
|
||||
|
||||
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
# Agent orchestration
|
||||
|
||||
Orchestration refers to the flow of agents in your app. Which agents run, in what order, and how do they decide what happens next? There are two main ways to orchestrate agents:
|
||||
Orchestration refers to the flow of agents in your app. Which agents run, in what order, and how is the next step decided? There are two main ways to orchestrate agents:
|
||||
|
||||
1. Allowing the LLM to make decisions: this uses the intelligence of an LLM to plan, reason, and decide on what steps to take based on that.
|
||||
2. Orchestrating via code: determining the flow of agents via your code.
|
||||
@@ -9,10 +9,10 @@ You can mix and match these patterns. Each has their own tradeoffs, described be
|
||||
|
||||
## Orchestrating via LLM
|
||||
|
||||
An agent is an LLM equipped with instructions, tools and handoffs. This means that given an open-ended task, the LLM can autonomously plan how it will tackle the task, using tools to take actions and acquire data, and using handoffs to delegate tasks to sub-agents. For example, a research agent could be equipped with tools like:
|
||||
An agent is an LLM equipped with instructions, tools and handoffs. This means that given an open-ended task, the LLM can autonomously plan how it will tackle the task, using tools to take actions and acquire data, and using handoffs to delegate tasks to sub-agents. For example, a research agent could be equipped with capabilities like:
|
||||
|
||||
- Web search to find information online
|
||||
- File search and retrieval to search through proprietary data and connections
|
||||
- File search and retrieval to search through proprietary data and connected data sources
|
||||
- Computer use to take actions on a computer
|
||||
- Code execution to do data analysis
|
||||
- Handoffs to specialized agents that are great at planning, report writing and more.
|
||||
@@ -23,16 +23,16 @@ In the Python SDK, two orchestration patterns come up most often:
|
||||
|
||||
| Pattern | How it works | Best when |
|
||||
| --- | --- | --- |
|
||||
| Agents as tools | A manager agent keeps control of the conversation and calls specialist agents through `Agent.as_tool()`. | You want one agent to own the final answer, combine outputs from multiple specialists, or enforce shared guardrails in one place. |
|
||||
| Handoffs | A triage agent routes the conversation to a specialist, and that specialist becomes the active agent for the rest of the turn. | You want the specialist to respond directly, keep prompts focused, or swap instructions without the manager narrating the result. |
|
||||
| Agents as tools | A manager agent keeps control of the conversation and calls specialist agents through `Agent.as_tool()`. | You want one agent to own the final answer, combine outputs from multiple specialists, or enforce shared SDK guardrails in one place. |
|
||||
| Handoffs | A triage agent routes the conversation to a specialist, and that specialist becomes the active agent for the rest of the turn. | You want the specialist to respond directly, keep prompts focused, or have the handoff switch the active instructions without requiring the manager to narrate the result. |
|
||||
|
||||
Use **agents as tools** when a specialist should help with a bounded subtask but should not take over the user-facing conversation. Use **handoffs** when routing itself is part of the workflow and you want the chosen specialist to own the next part of the interaction.
|
||||
Use **agents as tools** when a specialist should help with a bounded subtask but should not take over the user-facing conversation. Use **handoffs** when routing itself is part of the workflow and you want the chosen specialist to own the remainder of the current turn.
|
||||
|
||||
You can also combine the two. A triage agent might hand off to a specialist, and that specialist can still call other agents as tools for narrow subtasks.
|
||||
|
||||
This pattern is great when the task is open-ended and you want to rely on the intelligence of an LLM. The most important tactics here are:
|
||||
|
||||
1. Invest in good prompts. Make it clear what tools are available, how to use them, and what parameters it must operate within.
|
||||
1. Invest in good prompts. Make it clear what tools are available, how to use them, and what constraints the agent must follow.
|
||||
2. Monitor your app and iterate on it. See where things go wrong, and iterate on your prompts.
|
||||
3. Allow the agent to introspect and improve. For example, run it in a loop, and let it critique itself; or, provide error messages and let it improve.
|
||||
4. Have specialized agents that excel in one task, rather than having a general purpose agent that is expected to be good at anything.
|
||||
@@ -46,7 +46,7 @@ While orchestrating via LLM is powerful, orchestrating via code makes tasks more
|
||||
|
||||
- Using [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) to generate well formed data that you can inspect with your code. For example, you might ask an agent to classify the task into a few categories, and then pick the next agent based on the category.
|
||||
- Chaining multiple agents by transforming the output of one into the input of the next. You can decompose a task like writing a blog post into a series of steps - do research, write an outline, write the blog post, critique it, and then improve it.
|
||||
- Running the agent that performs the task in a `while` loop with an agent that evaluates and provides feedback, until the evaluator says the output passes certain criteria.
|
||||
- In each iteration of a `while` loop, run the task agent to produce an output, then run an evaluator agent to assess that output and provide feedback; stop when the evaluator says the output passes the required criteria.
|
||||
- Running multiple agents in parallel, e.g. via Python primitives like `asyncio.gather`. This is useful for speed when you have multiple tasks that don't depend on each other.
|
||||
|
||||
We have a number of examples in [`examples/agent_patterns`](https://github.com/openai/openai-agents-python/tree/main/examples/agent_patterns).
|
||||
|
||||
@@ -123,13 +123,13 @@ If server-side turn detection is disabled, you are responsible for marking turn
|
||||
await session.send_audio(audio_bytes, commit=True)
|
||||
```
|
||||
|
||||
If you need lower-level control, you can also send raw client events such as `input_audio_buffer.commit` through the underlying model transport.
|
||||
If you need lower-level control, you can also send Realtime API client events such as `input_audio_buffer.commit` directly through the underlying model transport.
|
||||
|
||||
### Manual response control
|
||||
|
||||
`session.send_message()` sends user input using the high-level path and starts a response for you. Raw audio buffering does **not** automatically do the same in every configuration.
|
||||
`session.send_message()` sends user input using the high-level path and starts a response for you. In some configurations, raw audio buffering does **not** automatically do the same.
|
||||
|
||||
At the Realtime API level, manual turn control means clearing `turn_detection` with a raw `session.update`, then sending `input_audio_buffer.commit` and `response.create` yourself.
|
||||
At the Realtime API level, manual turn control means sending a `session.update` event that sets `turn_detection` to `null`, then sending `input_audio_buffer.commit` and `response.create` yourself.
|
||||
|
||||
If you are managing turns manually, you can send raw client events through the model transport:
|
||||
|
||||
@@ -173,7 +173,7 @@ The most useful events for UI state are usually `history_added` and `history_upd
|
||||
|
||||
### Usage accounting
|
||||
|
||||
When a completed model response includes usage, the OpenAI realtime model emits a [`RealtimeModelUsageEvent`][agents.realtime.model_events.RealtimeModelUsageEvent] inside a `raw_model_event`. Its `usage` field contains the token counts for that response, while `input_tokens_details` and `output_tokens_details` provide optional modality breakdowns.
|
||||
When a completed model response includes usage, the SDK's OpenAI `RealtimeModel` transport emits a [`RealtimeModelUsageEvent`][agents.realtime.model_events.RealtimeModelUsageEvent] inside a `raw_model_event`. Its `usage` field contains the token counts for that response, while `input_tokens_details` and `output_tokens_details` provide optional modality breakdowns.
|
||||
|
||||
The session also adds each response's usage to the shared [`RunContextWrapper.usage`][agents.run_context.RunContextWrapper.usage]. Read it from `event.info.context.usage` on a subsequent high-level event such as `agent_end` to inspect cumulative usage for the live session.
|
||||
|
||||
@@ -199,7 +199,7 @@ Usage is reported only when the model provider includes it in the completed resp
|
||||
|
||||
When the user interrupts the assistant, the session emits `audio_interrupted` and updates history so the server-side conversation stays aligned with what the user actually heard.
|
||||
|
||||
In low-latency local playback, the default playback tracker is often enough. In remote or delayed playback scenarios, especially telephony, use [`RealtimePlaybackTracker`][agents.realtime.model.RealtimePlaybackTracker] so interruption truncation is based on actual playback progress rather than assuming all generated audio has already been heard.
|
||||
In low-latency local playback, the default playback tracker is often enough. In remote or delayed playback scenarios, especially telephony, use [`RealtimePlaybackTracker`][agents.realtime.model.RealtimePlaybackTracker] so the interrupted response is truncated at the actual playback position rather than assuming all generated audio has already been heard.
|
||||
|
||||
The Twilio example in [`examples/realtime/twilio/twilio_handler.py`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/twilio/twilio_handler.py) shows this pattern.
|
||||
|
||||
@@ -264,11 +264,11 @@ main_agent = RealtimeAgent(
|
||||
)
|
||||
```
|
||||
|
||||
Bare `RealtimeAgent` handoffs are auto-wrapped, and `realtime_handoff(...)` lets you customize names, descriptions, validation, callbacks, and availability. Realtime handoffs do **not** support the regular handoff `input_filter`.
|
||||
`RealtimeAgent` objects used directly as handoffs are auto-wrapped, and `realtime_handoff(...)` lets you customize names, descriptions, validation, callbacks, and availability. Realtime handoffs do **not** support the regular handoff `input_filter`.
|
||||
|
||||
### Guardrails
|
||||
|
||||
Realtime agents support output guardrails on agent responses and input guardrails on function-tool calls. Output guardrails run on debounced accumulation of output-text and audio-transcript deltas rather than on every partial delta, and they emit `guardrail_tripped` instead of raising an exception.
|
||||
Realtime agents support output guardrails on agent responses and input guardrails on function-tool calls. Output guardrail checks are debounced: each check runs on accumulated output-text and audio-transcript deltas rather than on every partial delta, and emits `guardrail_tripped` instead of raising an exception.
|
||||
|
||||
```python
|
||||
from agents.guardrail import GuardrailFunctionOutput, OutputGuardrail
|
||||
@@ -288,9 +288,9 @@ agent = RealtimeAgent(
|
||||
)
|
||||
```
|
||||
|
||||
When a realtime output guardrail trips on an audio transcript, the session interrupts the active response, forces `response.cancel`, emits `guardrail_tripped`, and sends a follow-up user message that names the triggered guardrail so the model can produce a replacement response. Your audio player should still listen for `audio_interrupted` and stop local playback immediately, because some audio may already be buffered when the tripwire fires. With the built-in OpenAI Realtime transports, if the guardrail finishes after its source response has ended, the session interrupts only that response's buffered playback and does not cancel a newer response. For text-only output, the session instead sends a response-scoped `response.cancel`; it does not emit `audio_interrupted` because there is no audio playback to stop. The same `guardrail_tripped` event and follow-up user message are emitted for the text-only path when using the built-in OpenAI Realtime models.
|
||||
When a realtime output guardrail trips on an audio transcript, the session interrupts the active response, forces `response.cancel`, emits `guardrail_tripped`, and sends a follow-up user message that names the triggered guardrail so the model can produce a replacement response. Your audio player should still listen for `audio_interrupted` and stop local playback immediately, because some audio may already be buffered when the tripwire fires. With the built-in OpenAI Realtime transports, if the guardrail check finishes after the response it is checking has ended, the session interrupts only that response's buffered playback and does not cancel any response that started later. For text-only output, the session instead sends a response-scoped `response.cancel`; it does not emit `audio_interrupted` because there is no audio playback to stop. The same `guardrail_tripped` event and follow-up user message are emitted for the text-only path when using the built-in OpenAI Realtime models.
|
||||
|
||||
Custom `RealtimeModel` transports must honor `RealtimeModelSendInterrupt.response_id` and `playback_only` to provide the same source-scoped audio interruption behavior. They must also override `RealtimeModel.send_event_if()` to support the text-only recovery message. The implementation must recheck or serialize the supplied condition at the transport's actual event commit boundary. The default implementation safely skips the recovery message because checking the condition before awaiting `send_event()` would allow a newer response to start before the message is committed; response cancellation and the `guardrail_tripped` event still occur.
|
||||
Custom `RealtimeModel` transports must honor `RealtimeModelSendInterrupt.response_id` and `playback_only` to provide the same source-scoped audio interruption behavior. They must also override `RealtimeModel.send_event_if()` to support the recovery message for the text-only output path. The implementation must either recheck the supplied condition at the transport's actual event commit boundary or serialize the condition check together with the event commit. The default implementation safely skips the recovery message because, if it checked the condition once and then sent the event separately, another response could start between that check and the event commit; response cancellation and the `guardrail_tripped` event still occur.
|
||||
|
||||
## SIP and telephony
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ Once the basic session works, the settings most people reach for next are:
|
||||
|
||||
The older flat aliases such as `input_audio_format`, `output_audio_format`, `input_audio_transcription`, and `turn_detection` still work, but nested `audio` settings are preferred for new code.
|
||||
|
||||
For manual turn control, use a raw `session.update` / `input_audio_buffer.commit` / `response.create` flow as described in the [Realtime agents guide](guide.md#manual-response-control).
|
||||
For manual turn control, use the low-level `session.update` / `input_audio_buffer.commit` / `response.create` flow described in the [Realtime agents guide](guide.md#manual-response-control).
|
||||
|
||||
For the full schema, see [`RealtimeRunConfig`][agents.realtime.config.RealtimeRunConfig] and [`RealtimeSessionModelSettings`][agents.realtime.config.RealtimeSessionModelSettings].
|
||||
|
||||
@@ -145,7 +145,7 @@ session = await runner.run(model_config={"api_key": "your-api-key"})
|
||||
|
||||
If you pass `headers` explicitly, the SDK will **not** inject an `Authorization` header for you.
|
||||
|
||||
When connecting to Azure OpenAI, pass a GA Realtime endpoint URL in `model_config["url"]` and explicit headers. Avoid the legacy beta path (`/openai/realtime?api-version=...`) with realtime agents. See the [Realtime agents guide](guide.md#low-level-access-and-custom-endpoints) for details.
|
||||
When connecting to Azure OpenAI, set `model_config["url"]` to a GA Realtime endpoint URL and pass headers explicitly. Avoid the legacy beta path (`/openai/realtime?api-version=...`) with realtime agents. See the [Realtime agents guide](guide.md#low-level-access-and-custom-endpoints) for details.
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ That means the standard Python topology looks like this:
|
||||
|
||||
1. Your Python service creates a `RealtimeRunner`.
|
||||
2. `await runner.run()` returns a `RealtimeSession`.
|
||||
3. Enter the session and send text, structured messages, or audio.
|
||||
3. Enter the `RealtimeSession` as an async context manager, then send text, structured messages, or audio.
|
||||
4. Consume `RealtimeSessionEvent` items and forward audio or transcripts to your application.
|
||||
|
||||
This is the topology used by the core demo app, the CLI example, and the Twilio Media Streams example:
|
||||
@@ -86,14 +86,14 @@ If your app's primary client is a browser using Realtime WebRTC:
|
||||
|
||||
- Treat it as outside the scope of the Python SDK docs in this repository.
|
||||
- Use the official [Realtime API with WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc/) and [Realtime conversations](https://developers.openai.com/api/docs/guides/realtime-conversations/) docs for the client-side flow and event model.
|
||||
- Use the official [Realtime server-side controls](https://developers.openai.com/api/docs/guides/realtime-server-controls/) guide if you need a sideband server connection on top of a browser WebRTC client.
|
||||
- Use the official [Realtime server-side controls](https://developers.openai.com/api/docs/guides/realtime-server-controls/) guide if, in addition to a browser WebRTC client, you need a sideband server connection.
|
||||
- Do not expect this repository to provide a browser-side `RTCPeerConnection` abstraction or a ready-made browser WebRTC sample.
|
||||
|
||||
This repository also does not currently ship a browser WebRTC plus Python sideband example.
|
||||
|
||||
## Custom endpoints and attach points
|
||||
|
||||
The transport configuration surface in [`RealtimeModelConfig`][agents.realtime.model.RealtimeModelConfig] lets you adapt the default paths:
|
||||
The transport configuration surface in [`RealtimeModelConfig`][agents.realtime.model.RealtimeModelConfig] lets you customize the default transport behavior:
|
||||
|
||||
- `url`: Override the WebSocket endpoint
|
||||
- `headers`: Provide explicit headers such as Azure auth headers
|
||||
|
||||
+14
-14
@@ -25,12 +25,12 @@ This minor release does **not** introduce a breaking change. The minor version b
|
||||
|
||||
Highlights:
|
||||
|
||||
- Added [`ProgrammaticToolCallingTool`][agents.tool.ProgrammaticToolCallingTool], which lets supported OpenAI Responses models generate JavaScript to coordinate eligible tools. It supports per-tool `allowed_callers`, structured function-tool outputs, and integration with Runner streaming, guardrails, approvals, sessions, and `RunState`. See [Programmatic Tool Calling](tools.md#programmatic-tool-calling) for setup and constraints.
|
||||
- Added the public `agents.decorators` module and the shorter `@tool` alias alongside the existing function and guardrail decorators. Function tools now also support async callable objects.
|
||||
- Added [`ProgrammaticToolCallingTool`][agents.tool.ProgrammaticToolCallingTool], which lets supported OpenAI Responses models generate JavaScript to coordinate tools eligible for Programmatic Tool Calling. It supports per-tool `allowed_callers`, structured outputs from `FunctionTool` instances, and integration with Runner streaming, guardrails, approvals, sessions, and `RunState`. See [Programmatic Tool Calling](tools.md#programmatic-tool-calling) for setup and constraints.
|
||||
- Added the public `agents.decorators` module and `@tool` as a shorter alias for the existing `@function_tool` decorator, alongside the existing guardrail decorators. `FunctionTool` instances now also support async callable objects.
|
||||
- SDK configuration now consistently accepts either typed settings objects or dictionaries across agents, runs, models, sessions, sandboxes, and voice pipelines, with validation for unknown settings.
|
||||
- Hardened error and diagnostic logging across models, tools, MCP, Realtime, sessions, sandboxes, and tracing to avoid exposing raw sensitive payloads while preserving useful debugging context.
|
||||
- Improved AnyLLM, LiteLLM, and Chat Completions compatibility, preserved session history across model retries, and added provider retry guidance for WebSocket overloads that occur before a response starts so opt-in Runner retry policies can act when replay is permitted.
|
||||
- Added [create-time-only S3 mounts for Vercel sandboxes](sandbox/clients.md#mounts-and-remote-storage) through `VercelCloudBucketMountStrategy`. Mounted sessions exclude bucket contents from workspace persistence and intentionally do not support dynamic mount changes or session resume.
|
||||
- Improved AnyLLM, LiteLLM, and Chat Completions compatibility, preserved session history across model retries, and added provider retry guidance for WebSocket overloads that occur before a response starts, so opt-in Runner retry policies can replay the failed attempt when permitted.
|
||||
- Added [S3 mounts that can be configured only when a Vercel sandbox is created](sandbox/clients.md#mounts-and-remote-storage) through `VercelCloudBucketMountStrategy`. Mounted sessions exclude bucket contents from workspace persistence and intentionally do not support dynamic mount changes or session resume.
|
||||
|
||||
### 0.18.0
|
||||
|
||||
@@ -111,7 +111,7 @@ This minor release does **not** introduce a breaking change, but it adds a major
|
||||
Highlights:
|
||||
|
||||
- Added a new beta sandbox runtime surface centered on `SandboxAgent`, `Manifest`, and `SandboxRunConfig`, letting agents work inside persistent isolated workspaces with files, directories, Git repos, mounts, snapshots, and resume support.
|
||||
- Added sandbox execution backends for local and containerized development via `UnixLocalSandboxClient` and `DockerSandboxClient`, plus hosted provider integrations for Blaxel, Cloudflare, Daytona, E2B, Modal, Runloop, and Vercel through optional extras.
|
||||
- Added sandbox execution backends for local and containerized development via `UnixLocalSandboxClient` and `DockerSandboxClient`, plus hosted provider integrations for Blaxel, Cloudflare, Daytona, E2B, Modal, Runloop, and Vercel through optional dependency extras in the Python package.
|
||||
- Added sandbox memory support so future runs can reuse lessons from prior runs, with progressive disclosure, multi-turn grouping, configurable isolation boundaries, and persisted-memory examples including S3-backed workflows.
|
||||
- Added a broader workspace and resume model, including local and synthetic workspace entries, remote storage mounts for S3/R2/GCS/Azure Blob Storage/S3 Files, portable snapshots, and resume flows via `RunState`, `SandboxSessionState`, or saved snapshots.
|
||||
- Added substantial sandbox examples and tutorials under `examples/sandbox/`, covering coding tasks with skills, handoffs, memory, provider-specific setups, and end-to-end workflows such as code review, dataroom QA, and website cloning.
|
||||
@@ -124,9 +124,9 @@ This minor release does **not** introduce a breaking change, but it includes a n
|
||||
Highlights:
|
||||
|
||||
- The default websocket Realtime model is now `gpt-realtime-1.5`, so new Realtime agent setups use the newer model without extra configuration.
|
||||
- `MCPServer` now exposes `list_resources()`, `list_resource_templates()`, and `read_resource()`, and `MCPServerStreamableHttp` now exposes `session_id` so streamable HTTP sessions can be resumed across reconnects or stateless workers.
|
||||
- Chat Completions integrations can now opt into reasoning-content replay via `should_replay_reasoning_content`, improving provider-specific reasoning/tool-call continuity for adapters such as LiteLLM/DeepSeek.
|
||||
- Fixed several runtime and session edge cases, including concurrent first writes in `SQLAlchemySession`, compaction requests with orphaned assistant message IDs after reasoning stripping, `remove_all_tools()` leaving MCP/reasoning items behind, and a race in the function-tool batch executor.
|
||||
- `MCPServer` now exposes `list_resources()`, `list_resource_templates()`, and `read_resource()`, and `MCPServerStreamableHttp` now exposes `session_id` so sessions using the MCP Streamable HTTP transport can be resumed across reconnects or stateless workers.
|
||||
- Chat Completions integrations can now opt into re-sending existing reasoning content via `should_replay_reasoning_content`, improving provider-specific reasoning/tool-call continuity for adapters such as LiteLLM/DeepSeek.
|
||||
- Fixed several runtime and session edge cases, including concurrent first writes in `SQLAlchemySession`, compaction requests with orphaned assistant message IDs after reasoning stripping, `remove_all_tools()` leaving MCP/reasoning items behind, and a race in the batch executor for `FunctionTool` instances.
|
||||
|
||||
### 0.12.0
|
||||
|
||||
@@ -156,7 +156,7 @@ Additionally, the type hint for the value returned from the `Agent#as_tool()` me
|
||||
|
||||
In this version, two runtime behavior changes may require migration work:
|
||||
|
||||
- Function tools wrapping **synchronous** Python callables now execute on worker threads via `asyncio.to_thread(...)` instead of running on the event loop thread. If your tool logic depends on thread-local state or thread-affine resources, migrate to an async tool implementation or make thread affinity explicit in your tool code.
|
||||
- `FunctionTool` instances wrapping **synchronous** Python callables now execute on worker threads via `asyncio.to_thread(...)` instead of running on the event loop thread. If your tool logic depends on thread-local state or thread-affine resources, migrate to an async tool implementation or make thread affinity explicit in your tool code.
|
||||
- Local MCP tool failure handling is now configurable, and the default behavior can return model-visible error output instead of failing the whole run. If you rely on fail-fast semantics, set `mcp_config={"failure_error_function": None}`. Server-level `failure_error_function` values override the agent-level setting, so set `failure_error_function=None` on each local MCP server that has an explicit handler.
|
||||
|
||||
### 0.7.0
|
||||
@@ -168,14 +168,14 @@ In this version, there were a few behavior changes that can affect existing appl
|
||||
|
||||
### 0.6.0
|
||||
|
||||
In this version, the default handoff history is now packaged into a single assistant message instead of exposing the raw user/assistant turns, giving downstream agents a concise, predictable recap
|
||||
- The existing single-message handoff transcript now by default starts with "For context, here is the conversation so far between the user and the previous agent:" before the `<CONVERSATION HISTORY>` block, so downstream agents get a clearly labeled recap
|
||||
In this version, the default handoff history is now packaged into a single assistant message rather than passing the user and assistant turns as separate messages, giving downstream agents a concise, predictable recap
|
||||
- The existing single-message handoff transcript now starts by default with the exact literal text `For context, here is the conversation so far between the user and the previous agent:` before the `<CONVERSATION HISTORY>` block, so downstream agents get a clearly labeled recap
|
||||
|
||||
### 0.5.0
|
||||
|
||||
This version doesn’t introduce any visible breaking changes, but it includes new features and a few significant updates under the hood:
|
||||
|
||||
- Added support for `RealtimeRunner` to handle [SIP protocol connections](https://platform.openai.com/docs/guides/realtime-sip)
|
||||
- Added support in `RealtimeRunner` for handling [SIP protocol connections](https://platform.openai.com/docs/guides/realtime-sip).
|
||||
- Significantly revised the internal logic of `Runner#run_sync` for Python 3.14 compatibility
|
||||
|
||||
### 0.4.0
|
||||
@@ -188,8 +188,8 @@ In this version, the Realtime API support migrates to gpt-realtime model and its
|
||||
|
||||
### 0.2.0
|
||||
|
||||
In this version, a few places that used to take `Agent` as an arg, now take `AgentBase` as an arg instead. For example, the `list_tools()` call in MCP servers. This is a purely typing change, you will still receive `Agent` objects. To update, just fix type errors by replacing `Agent` with `AgentBase`.
|
||||
In this version, a few places that used to take `Agent` as an arg, now take `AgentBase` as an arg instead. For example, this applies to the `list_tools()` method signature in MCP servers. This is a purely typing change, you will still receive `Agent` objects. To update, just fix type errors by replacing `Agent` with `AgentBase`.
|
||||
|
||||
### 0.1.0
|
||||
|
||||
In this version, [`MCPServer.list_tools()`][agents.mcp.server.MCPServer] has two new params: `run_context` and `agent`. You'll need to add these params to any classes that subclass `MCPServer`.
|
||||
In this version, [`MCPServer.list_tools()`][agents.mcp.server.MCPServer] has two new params: `run_context` and `agent`. You'll need to add these params to every overridden `MCPServer.list_tools()` method in subclasses of `MCPServer`.
|
||||
|
||||
+7
-7
@@ -59,9 +59,9 @@ In practice:
|
||||
|
||||
When SDK-default nested handoff history preserves a message item verbatim, Sessions, `RunState`, and `to_input_list()` track the exact owned occurrence rather than deduplicating by content. Identical messages that occurred separately remain separate; only the already-owned occurrence is kept from being appended a second time.
|
||||
|
||||
Unlike the JavaScript SDK, Python does not expose a separate `output` property for the model-shaped delta only. Use `new_items` when you need SDK metadata, or inspect `raw_responses` when you need the raw model payloads.
|
||||
Unlike the JavaScript SDK, Python does not expose a separate `output` property containing only the model-format items newly generated during the run. Use `new_items` when you need SDK metadata, or inspect `raw_responses` when you need the raw model payloads.
|
||||
|
||||
Computer-tool replay follows the raw Responses payload shape. Preview-model `computer_call` items preserve a single `action`, while `gpt-5.5` computer calls can preserve batched `actions[]`. [`to_input_list()`][agents.result.RunResultBase.to_input_list] and [`RunState`][agents.run_state.RunState] keep whichever shape the model produced, so manual replay, pause/resume flows, and stored transcripts continue to work across both preview and GA computer-tool calls. Local execution results still appear as `computer_call_output` items in `new_items`.
|
||||
Resubmitting computer-tool items as conversation input uses the raw Responses payload shape. Preview-model `computer_call` items preserve a single `action`, while `gpt-5.5` computer calls can preserve batched `actions[]`. [`to_input_list()`][agents.result.RunResultBase.to_input_list] and [`RunState`][agents.run_state.RunState] keep whichever shape the model produced, so manually resubmitting those items as conversation input, pause/resume flows, and stored transcripts continue to work across both preview and GA computer-tool calls. Local execution results still appear as `computer_call_output` items in `new_items`.
|
||||
|
||||
### New items
|
||||
|
||||
@@ -103,7 +103,7 @@ caller_id = (
|
||||
)
|
||||
```
|
||||
|
||||
For a program-owned child call, `caller` has type `program`, and `caller_id` identifies the parent program call.
|
||||
For a program-owned child call, the `type` field of `caller` is `program`, and `caller_id` identifies the parent program call.
|
||||
|
||||
## Continue or resume the conversation
|
||||
|
||||
@@ -142,7 +142,7 @@ If you already continue the conversation with `to_input_list()`, `session`, or `
|
||||
|
||||
## Agent-as-tool metadata
|
||||
|
||||
When a result comes from a nested [`Agent.as_tool()`][agents.agent.Agent.as_tool] run, [`agent_tool_invocation`][agents.result.RunResultBase.agent_tool_invocation] exposes immutable metadata about the outer tool call:
|
||||
When a result comes from a nested [`Agent.as_tool()`][agents.agent.Agent.as_tool] run, [`agent_tool_invocation`][agents.result.RunResultBase.agent_tool_invocation] exposes immutable metadata about the enclosing `Agent.as_tool()` call:
|
||||
|
||||
- `tool_name`
|
||||
- `tool_call_id`
|
||||
@@ -150,9 +150,9 @@ When a result comes from a nested [`Agent.as_tool()`][agents.agent.Agent.as_tool
|
||||
|
||||
For ordinary top-level runs, `agent_tool_invocation` is `None`.
|
||||
|
||||
This is especially useful inside `custom_output_extractor`, where you may need the outer tool name, call ID, or raw arguments while post-processing the nested result. See [Tools](tools.md) for the surrounding `Agent.as_tool()` patterns.
|
||||
This is especially useful inside `custom_output_extractor`, where you may need the enclosing `Agent.as_tool()` call's tool name, call ID, or raw arguments while post-processing the nested result. See [Tools](tools.md) for the surrounding `Agent.as_tool()` patterns.
|
||||
|
||||
If you also need the parsed structured input for that nested run, read `context_wrapper.tool_input`. That is the field [`RunState`][agents.run_state.RunState] serializes generically for nested tool input, while `agent_tool_invocation` is the live result accessor for the current nested invocation.
|
||||
If you also need the parsed structured input for that nested run, read `context_wrapper.tool_input`. That is the field [`RunState`][agents.run_state.RunState] serializes generically for nested tool input, while `agent_tool_invocation` exposes metadata for the current nested invocation directly on the result.
|
||||
|
||||
## Streaming lifecycle and diagnostics
|
||||
|
||||
@@ -167,7 +167,7 @@ Keep consuming `stream_events()` until the async iterator finishes. A streaming
|
||||
|
||||
If you call `cancel()`, continue consuming `stream_events()` so cancellation and cleanup can finish correctly.
|
||||
|
||||
Python does not expose a separate streamed `completed` promise or `error` property. Terminal streaming failures are surfaced by raising from `stream_events()`, and `is_complete` reflects whether the run has reached its terminal state.
|
||||
Python does not expose a separate streamed `completed` promise or `error` property. Streaming failures that terminate the run are raised by `stream_events()`, and `is_complete` reflects whether the run has reached its terminal state.
|
||||
|
||||
### Raw responses
|
||||
|
||||
|
||||
+13
-13
@@ -25,7 +25,7 @@ Read more in the [results guide](results.md).
|
||||
|
||||
### The agent loop
|
||||
|
||||
When you use the run method in `Runner`, you pass in a starting agent and input. The input can be:
|
||||
When you call any of the three `Runner` methods above, you pass in a starting agent and input. The input can be:
|
||||
|
||||
- a string (treated as a user message),
|
||||
- a list of input items in the OpenAI Responses API format, or
|
||||
@@ -35,8 +35,8 @@ The runner then runs a loop:
|
||||
|
||||
1. We call the LLM for the current agent, with the current input.
|
||||
2. The LLM produces its output.
|
||||
1. If the LLM returns a `final_output`, the loop ends and we return the result.
|
||||
2. If the LLM does a handoff, we update the current agent and input, and re-run the loop.
|
||||
1. If the runner classifies the LLM's output as final output, the loop ends and we return the result.
|
||||
2. If the LLM requests a handoff, we update the current agent and input, and re-run the loop.
|
||||
3. If the LLM produces tool calls, we run those tool calls, append the results, and re-run the loop.
|
||||
3. If we exceed the `max_turns` passed, we raise a [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] exception. Pass `max_turns=None` to disable this turn limit.
|
||||
|
||||
@@ -135,7 +135,7 @@ Use `RunConfig` to override behavior for a single run without changing each agen
|
||||
- [`model_provider`][agents.run.RunConfig.model_provider]: A model provider for looking up model names, which defaults to OpenAI.
|
||||
- [`model_settings`][agents.run.RunConfig.model_settings]: Overrides agent-specific settings. For example, you can set a global `temperature` or `top_p`.
|
||||
- [`session_settings`][agents.run.RunConfig.session_settings]: Overrides session-level defaults (for example, `SessionSettings(limit=...)`) when retrieving history during a run.
|
||||
- [`session_input_callback`][agents.run.RunConfig.session_input_callback]: Customize how new user input is merged with session history before each turn when using Sessions. The callback can be sync or async.
|
||||
- [`session_input_callback`][agents.run.RunConfig.session_input_callback]: Customize how new user input is merged with session history before each `Runner` run when using Sessions. The callback can be sync or async.
|
||||
|
||||
##### Guardrails, handoffs, and model input shaping
|
||||
|
||||
@@ -156,9 +156,9 @@ Use `RunConfig` to override behavior for a single run without changing each agen
|
||||
|
||||
##### Tool execution, approval, and tool error behavior
|
||||
|
||||
- [`tool_execution`][agents.run.RunConfig.tool_execution]: Configure SDK-side execution behavior for local tool calls, such as limiting how many function tools run at once.
|
||||
- [`tool_not_found_behavior`][agents.run.RunConfig.tool_not_found_behavior]: Configure how the runner handles unresolved function tool calls emitted by the model. The default raises `ModelBehaviorError`; opt in to return a model-visible error output instead.
|
||||
- [`tool_name_collision_policy`][agents.run.RunConfig.tool_name_collision_policy]: Configure how the runner handles bare function-tool and handoff names that collide. The default, `"warn"`, logs an actionable warning and exposes only the current dispatch winner; `"error"` raises `UserError` before the model is called. Strict validation for namespaced and deferred-loading tools is unchanged.
|
||||
- [`tool_execution`][agents.run.RunConfig.tool_execution]: Configure SDK-side execution behavior for local tool calls, such as limiting how many local function tool calls run at once.
|
||||
- [`tool_not_found_behavior`][agents.run.RunConfig.tool_not_found_behavior]: Configure how the runner handles model-emitted function tool calls whose tool name does not match any function tool available to the current agent. The default raises `ModelBehaviorError`; opt in to return a model-visible error output instead.
|
||||
- [`tool_name_collision_policy`][agents.run.RunConfig.tool_name_collision_policy]: Configure how the runner handles unnamespaced function-tool and handoff names that collide. The default, `"warn"`, logs an actionable warning and exposes only the current dispatch winner; `"error"` raises `UserError` before the model is called. Strict validation for namespaced and deferred-loading tools is unchanged.
|
||||
- [`tool_error_formatter`][agents.run.RunConfig.tool_error_formatter]: Customize model-visible tool error messages, such as approval rejections and opt-in tool-not-found outputs.
|
||||
|
||||
Nested handoffs are available as an opt-in beta. Enable ordered transcript compaction by passing `RunConfig(nest_handoff_history=True)` or set `handoff(..., nest_handoff_history=True)` to turn it on for a specific handoff. The built-in mapper places generated assistant summary segments around lossless message items instead of collapsing the whole transcript into one message. If you prefer to keep the raw transcript (the default), leave the flag unset or provide a `handoff_input_filter` (or `handoff_history_mapper`) that forwards the conversation exactly as you need. To change the wrapper text used in generated summary segments without writing a custom mapper, call [`set_conversation_history_wrappers`][agents.handoffs.set_conversation_history_wrappers] (and [`reset_conversation_history_wrappers`][agents.handoffs.reset_conversation_history_wrappers] to restore the defaults).
|
||||
@@ -186,7 +186,7 @@ result = await Runner.run(
|
||||
)
|
||||
```
|
||||
|
||||
`max_function_tool_concurrency=None` preserves the default behavior: when a model emits multiple function tool calls in a turn, the SDK starts all emitted local function tool calls. Set an integer value to cap how many of those local function tools run at once.
|
||||
`max_function_tool_concurrency=None` preserves the default behavior: when a model emits multiple function tool calls in a turn, the SDK starts all emitted local function tool calls. Set an integer value to cap how many of those local function tool calls run at once.
|
||||
|
||||
This is separate from provider-side [`ModelSettings.parallel_tool_calls`][agents.model_settings.ModelSettings.parallel_tool_calls]. `parallel_tool_calls` controls whether the model is allowed to emit multiple tool calls in a single response. `tool_execution.max_function_tool_concurrency` controls how the SDK executes local function tool calls after the model has emitted them.
|
||||
|
||||
@@ -210,7 +210,7 @@ result = await Runner.run(
|
||||
)
|
||||
```
|
||||
|
||||
This option currently applies to unresolved function tool calls only. Other invalid tool payloads continue to use their existing error behavior.
|
||||
This option currently applies only to function tool calls that fail tool-name lookup. Other invalid tool payloads continue to use their existing error behavior.
|
||||
|
||||
##### `tool_error_formatter`
|
||||
|
||||
@@ -569,7 +569,7 @@ For tool approval pause/resume patterns, start with the dedicated [Human-in-the-
|
||||
|
||||
### Dapr
|
||||
|
||||
You can use the Agents SDK [Dapr](https://dapr.io) Diagrid integration to run durable, long running agents that automatically recover from failures with human-in-the-loop support. Dapr is a vendor-neutral, [CNCF](https://cncf.io) workflow orchestrator. Get started with Dapr and OpenAI agents [here](https://docs.diagrid.io/getting-started/quickstarts/ai-agents/?agentframework=openai).
|
||||
You can use the Agents SDK [Dapr](https://dapr.io) Diagrid integration to run durable, long-running agents that automatically recover from failures and support human-in-the-loop workflows. Dapr is a vendor-neutral, [CNCF](https://cncf.io) workflow orchestrator. Get started with Dapr and OpenAI agents [here](https://docs.diagrid.io/getting-started/quickstarts/ai-agents/?agentframework=openai).
|
||||
|
||||
### Temporal
|
||||
|
||||
@@ -587,11 +587,11 @@ You can use the Agents SDK [DBOS](https://dbos.dev/) integration to run reliable
|
||||
|
||||
The SDK raises exceptions in certain cases. The full list is in [`agents.exceptions`][]. As an overview:
|
||||
|
||||
- [`AgentsException`][agents.exceptions.AgentsException]: This is the base class for all exceptions raised within the SDK. It serves as a generic type from which all other specific exceptions are derived.
|
||||
- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded]: This exception is raised when the agent's run exceeds the `max_turns` limit passed to the `Runner.run`, `Runner.run_sync`, or `Runner.run_streamed` methods. It indicates that the agent could not complete its task within the specified number of interaction turns. Set `max_turns=None` to disable the limit.
|
||||
- [`AgentsException`][agents.exceptions.AgentsException]: This is the base class for all exceptions that the SDK raises. It serves as a generic type from which all other specific exceptions are derived.
|
||||
- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded]: This exception is raised when the agent's run exceeds the `max_turns` limit passed to the `Runner.run`, `Runner.run_sync`, or `Runner.run_streamed` methods. It indicates that the agent could not complete its task within the specified number of agent-loop turns (LLM calls). Set `max_turns=None` to disable the limit.
|
||||
- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError]: This exception occurs when the underlying model (LLM) produces unexpected or invalid outputs. This can include:
|
||||
- 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.
|
||||
- Unexpected tool-related failures: When the model fails to use tools in an expected manner
|
||||
- [`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"`.
|
||||
- [`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.
|
||||
- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered]: This exception is raised when the conditions of an input guardrail or output guardrail are met, respectively. Input guardrails check incoming messages before processing, while output guardrails check the agent's final response before delivery.
|
||||
- [`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.
|
||||
|
||||
+10
-10
@@ -27,7 +27,7 @@ For most users, start with one of these two sandbox clients:
|
||||
| Client | Install | Choose it when | Example |
|
||||
| --- | --- | --- | --- |
|
||||
| `UnixLocalSandboxClient` | none | Fastest local iteration on macOS or Linux. Good default for local development. | [Unix-local starter](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/unix_local_runner.py) |
|
||||
| `DockerSandboxClient` | `openai-agents[docker]` | You want container isolation or a specific image for local parity. | [Docker starter](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/docker/docker_runner.py) |
|
||||
| `DockerSandboxClient` | `openai-agents[docker]` | You want container isolation or a specific image to reproduce a target environment locally. | [Docker starter](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/docker/docker_runner.py) |
|
||||
|
||||
</div>
|
||||
|
||||
@@ -52,7 +52,7 @@ run_config = RunConfig(
|
||||
)
|
||||
```
|
||||
|
||||
Use this when you want container isolation or image parity. See [examples/sandbox/docker/docker_runner.py](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/docker/docker_runner.py).
|
||||
Use this when you want container isolation or want the sandbox image to match the image used in another environment. See [examples/sandbox/docker/docker_runner.py](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/docker/docker_runner.py).
|
||||
|
||||
## Mounts and remote storage
|
||||
|
||||
@@ -76,7 +76,7 @@ Generic local/container strategies:
|
||||
| `InContainerMountStrategy(pattern=MountpointMountPattern(...))` | The image has `mount-s3` and you want Mountpoint-style S3 or S3-compatible access. | Supports `S3Mount` and `GCSMount`. |
|
||||
| `InContainerMountStrategy(pattern=FuseMountPattern(...))` | The image has `blobfuse2` and FUSE support. | Supports `AzureBlobMount`. |
|
||||
| `InContainerMountStrategy(pattern=S3FilesMountPattern(...))` | The image has `mount.s3files` and can reach an existing S3 Files mount target. | Supports `S3FilesMount`. |
|
||||
| `DockerVolumeMountStrategy(driver=...)` | Docker should attach a volume-driver-backed mount before the container starts. | Docker-only. S3, GCS, R2, Azure Blob, and Box support `rclone`; S3 and GCS also support `mountpoint`. |
|
||||
| `DockerVolumeMountStrategy(driver=...)` | Docker should attach a volume-driver-backed mount before the container starts. | Docker-only. S3, GCS, R2, Azure Blob, and Box can be mounted through `rclone`; S3 and GCS can also be mounted through `mountpoint`. |
|
||||
|
||||
</div>
|
||||
|
||||
@@ -109,13 +109,13 @@ Hosted sandbox clients expose provider-specific mount strategies. Choose the bac
|
||||
| Backend | Mount notes |
|
||||
| --- | --- |
|
||||
| Docker | Supports `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, `BoxMount`, and `S3FilesMount` with local strategies such as `InContainerMountStrategy` and `DockerVolumeMountStrategy`. |
|
||||
| `ModalSandboxClient` | Supports Modal cloud bucket mounts with `ModalCloudBucketMountStrategy` on `S3Mount`, `R2Mount`, and HMAC-authenticated `GCSMount`. You can use inline credentials or a named Modal Secret. |
|
||||
| `CloudflareSandboxClient` | Supports Cloudflare bucket mounts with `CloudflareBucketMountStrategy` on `S3Mount`, `R2Mount`, and HMAC-authenticated `GCSMount`. |
|
||||
| `BlaxelSandboxClient` | Supports cloud bucket mounts with `BlaxelCloudBucketMountStrategy` on `S3Mount`, `R2Mount`, and `GCSMount`. Also supports persistent Blaxel Drives with `BlaxelDriveMount` and `BlaxelDriveMountStrategy` from `agents.extensions.sandbox.blaxel`. |
|
||||
| `DaytonaSandboxClient` | Supports rclone-backed cloud storage mounts with `DaytonaCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
|
||||
| `E2BSandboxClient` | Supports rclone-backed cloud storage mounts with `E2BCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
|
||||
| `RunloopSandboxClient` | Supports rclone-backed cloud storage mounts with `RunloopCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
|
||||
| `VercelSandboxClient` | Supports create-time-only S3 and S3-compatible bucket mounts with `VercelCloudBucketMountStrategy` on `S3Mount`; mounted sessions cannot be resumed, and inline credentials require `allow_s3_credential_exposure=True`. |
|
||||
| `ModalSandboxClient` | Supports cloud bucket mounts by using `ModalCloudBucketMountStrategy` with `S3Mount`, `R2Mount`, and HMAC-authenticated `GCSMount`. You can use inline credentials or a named Modal Secret. |
|
||||
| `CloudflareSandboxClient` | Supports bucket mounts by using `CloudflareBucketMountStrategy` with `S3Mount`, `R2Mount`, and HMAC-authenticated `GCSMount`. |
|
||||
| `BlaxelSandboxClient` | Supports cloud bucket mounts by pairing `BlaxelCloudBucketMountStrategy` with an `S3Mount`, `R2Mount`, or `GCSMount` entry. Also supports persistent Blaxel Drives with `BlaxelDriveMount` and `BlaxelDriveMountStrategy`, both available from `agents.extensions.sandbox.blaxel`. |
|
||||
| `DaytonaSandboxClient` | Supports mounting cloud storage through `rclone` by using `DaytonaCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
|
||||
| `E2BSandboxClient` | Supports mounting cloud storage through `rclone` by using `E2BCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
|
||||
| `RunloopSandboxClient` | Supports mounting cloud storage through `rclone` by using `RunloopCloudBucketMountStrategy`; use it with `S3Mount`, `GCSMount`, `R2Mount`, `AzureBlobMount`, and `BoxMount`. |
|
||||
| `VercelSandboxClient` | Supports create-time-only S3 and S3-compatible bucket mounts by pairing `VercelCloudBucketMountStrategy` with an `S3Mount` entry; mounted sessions cannot be resumed, and inline credentials require `allow_s3_credential_exposure=True`. |
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
+10
-10
@@ -26,7 +26,7 @@ You define the workspace around the data the agent needs. It can start from GitH
|
||||
|
||||
Throughout this page, "sandbox session" means the live execution environment managed by a sandbox client. It is different from the SDK's conversational [`Session`][agents.memory.session.Session] interfaces described in [Sessions](../sessions/index.md).
|
||||
|
||||
The outer runtime still owns approvals, tracing, handoffs, and resume bookkeeping. The sandbox session owns commands, file changes, and environment isolation. That split is a core part of the model.
|
||||
The outer runtime still owns approvals, tracing, handoffs, and tracking the state needed to resume runs. The sandbox session owns commands, file changes, and environment isolation. That split is a core part of the model.
|
||||
|
||||
### How the pieces fit together
|
||||
|
||||
@@ -54,7 +54,7 @@ Think about the lifecycle in three phases:
|
||||
2. Execute a run by giving `Runner` a `SandboxRunConfig` that injects, resumes, or creates the sandbox session.
|
||||
3. Continue later from runner-managed `RunState`, explicit sandbox `session_state`, or a saved workspace snapshot.
|
||||
|
||||
If shell access is only one occasional tool, start with hosted shell in the [tools guide](../tools.md). Reach for sandbox agents when workspace isolation, sandbox client choice, or sandbox-session resume behavior are part of the design.
|
||||
If shell access is just one tool that you use occasionally, start with hosted shell in the [tools guide](../tools.md). Reach for sandbox agents when workspace isolation, sandbox client choice, or sandbox-session resume behavior are part of the design.
|
||||
|
||||
## When to use them
|
||||
|
||||
@@ -66,7 +66,7 @@ Sandbox agents are a good fit for workspace-centric workflows, for example:
|
||||
- isolated multi-agent patterns, for example giving each reviewer or coding sub-agent its own workspace
|
||||
- multi-step workspace tasks, for example fixing a bug in one run and adding a regression test later, or resuming from snapshot or sandbox session state
|
||||
|
||||
If you do not need access to files or a living filesystem, keep using `Agent`. If shell access is just one occasional capability, add hosted shell; if the workspace boundary itself is part of the feature, use sandbox agents.
|
||||
If you do not need access to files or a stateful, mutable filesystem, keep using `Agent`. If shell access is just one occasional capability, add hosted shell; if the workspace boundary itself is part of the feature, use sandbox agents.
|
||||
|
||||
## Choose a sandbox client
|
||||
|
||||
@@ -119,7 +119,7 @@ At run time, the runner turns that definition into a concrete sandbox-backed run
|
||||
4. It builds the final instructions in a fixed order: the SDK's default sandbox prompt, or `base_instructions` if you explicitly override it, then `instructions`, then capability instruction fragments, then any remote-mount policy text, then a rendered filesystem tree.
|
||||
5. It binds capability tools to the live sandbox session and runs the prepared agent through the normal `Runner` APIs.
|
||||
|
||||
Sandboxing does not change what a turn means. A turn is still a model step, not a single shell command or sandbox action. There is no fixed 1:1 mapping between sandbox-side operations and turns: some work may stay inside the sandbox execution layer, while other actions return tool results, approvals, or other state that requires another model step. As a practical rule, another turn is consumed only when the agent runtime needs another model response after sandbox work has happened.
|
||||
Sandboxing does not change what a turn means. A turn is still a model step, not a single shell command or sandbox action. There is no fixed 1:1 mapping between sandbox-side operations and turns: some work may stay inside the sandbox execution layer, while other actions return information that requires another model step, such as a tool result, an approval, or another kind of state. As a practical rule, another turn is consumed only when the agent runtime needs another model response after sandbox work has happened.
|
||||
|
||||
Those preparation steps are why `default_manifest`, `instructions`, `base_instructions`, `capabilities`, and `run_as` are the main sandbox-specific options to think about when designing a `SandboxAgent`.
|
||||
|
||||
@@ -188,7 +188,7 @@ Built-in capabilities include:
|
||||
| `Shell` | The agent needs shell access. | Adds `exec_command`, plus `write_stdin` when the sandbox client supports PTY interaction. |
|
||||
| `Filesystem` | The agent needs to edit files or inspect local images. | Adds `apply_patch` and `view_image`; patch paths are workspace-root-relative. |
|
||||
| `Skills` | You want skill discovery and materialization in the sandbox. | Prefer this over manually mounting `.agents` or `.agents/skills`; `Skills` indexes and materializes skills into the sandbox for you. |
|
||||
| `Memory` | Follow-on runs should read or generate memory artifacts. | Requires `Shell`; live updates also require `Filesystem`. |
|
||||
| `Memory` | Follow-on runs should read or generate memory artifacts. | Requires `Shell`; updating memory artifacts during a run also requires `Filesystem`. |
|
||||
| `Compaction` | Long-running flows need context trimming after compaction items. | Adjusts model sampling and input handling. |
|
||||
|
||||
</div>
|
||||
@@ -237,7 +237,7 @@ Mount entries describe what storage to expose; mount strategies describe how a s
|
||||
|
||||
Good manifest design usually means keeping the workspace contract narrow, putting long task recipes in workspace files such as `repo/task.md`, and using relative workspace paths in instructions, for example `repo/task.md` or `output/report.md`. If the agent edits files with the `Filesystem` capability's `apply_patch` tool, remember that patch paths are relative to the sandbox workspace root, not the shell `workdir`.
|
||||
|
||||
Use `extra_path_grants` only when the agent needs a concrete absolute path outside the workspace or the manifest needs to copy a trusted local source outside the SDK process working directory. Examples include `/tmp` for temporary tool output, `/opt/toolchain` for a read-only runtime, or a generated skills directory that should be materialized into the sandbox. A grant applies to local source materialization, SDK file APIs, and shell execution where the backend can enforce filesystem policy:
|
||||
Use `extra_path_grants` only when the agent needs a concrete absolute path outside the workspace or the manifest needs to copy a trusted local source outside the SDK process working directory. Examples include `/tmp` for temporary tool output, `/opt/toolchain` for a read-only runtime, or a generated skills directory that should be materialized into the sandbox. A grant applies to local source materialization and SDK file APIs. It also applies to shell execution when the backend can enforce filesystem policy:
|
||||
|
||||
```python
|
||||
from agents.sandbox import Manifest, SandboxPathGrant
|
||||
@@ -387,7 +387,7 @@ sequenceDiagram
|
||||
|
||||
</div>
|
||||
|
||||
Use SDK-owned lifecycle when the sandbox only needs to live for one run. Pass a `client`, optional `manifest`, optional `snapshot`, and client `options`; the runner creates or resumes the sandbox, starts it, runs the agent, persists snapshot-backed workspace state, shuts the sandbox down, and lets the client clean up runner-owned resources.
|
||||
Use SDK-owned lifecycle when the sandbox only needs to live for one run. Pass a `client`, optionally a `manifest` and `snapshot`, and any client `options` you need; the runner creates or resumes the sandbox, starts it, runs the agent, persists snapshot-backed workspace state, ends the sandbox session, and lets the client clean up runner-owned resources.
|
||||
|
||||
```python
|
||||
result = await Runner.run(
|
||||
@@ -447,7 +447,7 @@ These options decide whether the runner should reuse, resume, or create the sand
|
||||
| --- | --- | --- |
|
||||
| `client` | You want the runner to create, resume, and clean up sandbox sessions for you. | Required unless you provide a live sandbox `session`. |
|
||||
| `session` | You already created a live sandbox session yourself. | The caller owns lifecycle; the runner reuses that live sandbox session. |
|
||||
| `session_state` | You have serialized sandbox session state but not a live sandbox session object. | Requires `client`; the runner resumes from that explicit state as an owning session. |
|
||||
| `session_state` | You have serialized sandbox session state but not a live sandbox session object. | Requires `client`; the runner resumes from that explicit state and owns the resumed session's lifecycle. |
|
||||
|
||||
</div>
|
||||
|
||||
@@ -668,7 +668,7 @@ run_config = RunConfig(
|
||||
)
|
||||
```
|
||||
|
||||
Use this when a fresh run should start from saved workspace contents rather than only `agent.default_manifest`. See [examples/sandbox/memory.py](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/memory.py) for a local snapshot flow and [examples/sandbox/sandbox_agent_with_remote_snapshot.py](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/sandbox_agent_with_remote_snapshot.py) for a remote snapshot client.
|
||||
Use this when a run that creates a fresh sandbox session should start from saved workspace contents rather than only `agent.default_manifest`. See [examples/sandbox/memory.py](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/memory.py) for a local snapshot flow and [examples/sandbox/sandbox_agent_with_remote_snapshot.py](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/sandbox_agent_with_remote_snapshot.py) for a remote snapshot client.
|
||||
|
||||
### Load skills from Git
|
||||
|
||||
@@ -687,7 +687,7 @@ Use this when the skills bundle has its own release cadence or should be shared
|
||||
|
||||
### Expose as tools
|
||||
|
||||
Tool-agents can either get their own sandbox boundary or reuse a live sandbox from the parent run. Reuse is useful for a fast read-only explorer agent: it can inspect the exact workspace the parent is using without paying to create, hydrate, or snapshot another sandbox.
|
||||
Tool-agents can either get their own sandbox boundary or reuse a live sandbox from the parent run. Reuse is useful for a fast read-only explorer agent: it can inspect the exact workspace the parent run is using without paying to create, hydrate, or snapshot another sandbox.
|
||||
|
||||
```python
|
||||
from agents import Runner
|
||||
|
||||
@@ -42,7 +42,7 @@ If read is enabled, `Memory()` requires `Shell()`, which lets the agent read and
|
||||
|
||||
By default, memory artifacts are stored in the sandbox workspace under `memories/`. To reuse them in a later run, preserve and reuse the whole configured memories directory by keeping the same live sandbox session or resuming from a persisted session state or snapshot; a fresh empty sandbox starts with empty memory.
|
||||
|
||||
`Memory()` enables both reading and generating memories. Use `Memory(generate=None)` for agents that should read memory but should not generate new memories: for example, an internal agent, subagent, checker, or one-off tool agent whose run doesn't add much signal. Use `Memory(read=None)` when the run should generate memory for later, but the user doesn't want the run to be influenced by existing memory.
|
||||
`Memory()` enables both reading and generating memories. Use `Memory(generate=None)` for agents that should read memory but should not generate new memories—for example, when runs by internal agents, subagents, checkers, or one-off tool agents do not add much signal. Use `Memory(read=None)` when the run should generate memory for later, but the user doesn't want the run to be influenced by existing memory.
|
||||
|
||||
## Read memory
|
||||
|
||||
@@ -128,7 +128,7 @@ async with sandbox:
|
||||
)
|
||||
```
|
||||
|
||||
Both runs append to one memory conversation file because they pass the same SDK conversation session (`session=conversation_session`) and therefore share the same `session.session_id`. This is different from the sandbox (`sandbox`), which identifies the live workspace and is not used as the memory conversation ID. Phase 1 sees the accumulated conversation when the sandbox session closes, so it can extract memory from the whole exchange instead of two isolated turns.
|
||||
Both runs pass the same SDK conversation session (`session=conversation_session`) and therefore share the same `session.session_id`. As a result, both runs append to one memory conversation file. This is different from the sandbox (`sandbox`), which identifies the live workspace and is not used as the memory conversation ID. Phase 1 sees the accumulated conversation when the sandbox session closes, so it can extract memory from the whole exchange instead of two isolated turns.
|
||||
|
||||
If you want multiple `Runner.run(...)` calls to become one memory conversation, pass a stable identifier across those calls. When memory associates a run with a conversation, it resolves in this order:
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ pip install "openai-agents[docker]"
|
||||
|
||||
## Create a local sandbox agent
|
||||
|
||||
This example stages a local repo under `repo/`, loads local skills lazily, and lets the runner create a Unix-local sandbox session for the run.
|
||||
This example stages a local repo under `repo/`, loads local skills lazily, and has the runner create a Unix-local sandbox session for the run.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
@@ -99,8 +99,8 @@ Once the basic run works, the choices most people reach for next are:
|
||||
- `default_manifest`: the files, repos, directories, and mounts for fresh sandbox sessions
|
||||
- `instructions`: short workflow rules that should apply across prompts
|
||||
- `base_instructions`: an advanced escape hatch for replacing the SDK sandbox prompt
|
||||
- `capabilities`: sandbox-native tools such as filesystem editing/image inspection, shell, skills, memory, and compaction
|
||||
- `run_as`: the sandbox user identity for model-facing tools
|
||||
- `capabilities`: sandbox-native tools such as filesystem editing/image inspection, shell, skills, memory, and the SDK's compaction mechanism
|
||||
- `run_as`: the sandbox user account under which model-facing tools execute
|
||||
- `SandboxRunConfig.client`: the sandbox backend
|
||||
- `SandboxRunConfig.session`, `session_state`, or `snapshot`: how later runs reconnect to prior work
|
||||
|
||||
@@ -110,4 +110,4 @@ Once the basic run works, the choices most people reach for next are:
|
||||
- [Sandbox clients](sandbox/clients.md): choose Unix-local, Docker, hosted providers, and mount strategies.
|
||||
- [Agent memory](sandbox/memory.md): preserve and reuse lessons from previous sandbox runs.
|
||||
|
||||
If shell access is only one occasional tool, start with hosted shell in the [tools guide](tools.md). Reach for sandbox agents when workspace isolation, sandbox client choice, or sandbox-session resume behavior are part of the design.
|
||||
If shell access is just one tool that you use occasionally, start with hosted shell in the [tools guide](tools.md). Reach for sandbox agents when workspace isolation, sandbox client choice, or sandbox-session resume behavior are part of the design.
|
||||
|
||||
+253
-56
@@ -1,8 +1,10 @@
|
||||
# ruff: noqa
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
from openai import OpenAI
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
@@ -98,7 +100,6 @@ eng_to_non_eng_mapping = {
|
||||
"orchestrating multiple agents": "에이전트 오케스트레이션",
|
||||
"handoffs": "핸드오프",
|
||||
"function tools": "함수 도구",
|
||||
"function calling": "함수 호출",
|
||||
"tracing": "트레이싱",
|
||||
"code examples": "코드 예제",
|
||||
"vector store": "벡터 스토어",
|
||||
@@ -118,7 +119,6 @@ eng_to_non_eng_mapping = {
|
||||
"Human in the loop": "휴먼인더루프 (HITL)",
|
||||
"Hosted tool": "호스티드 툴",
|
||||
"Hosted MCP server tools": "호스티드 MCP 서버 도구",
|
||||
"raw": "원문",
|
||||
"Realtime Agents": "실시간 에이전트",
|
||||
"Build your first agent in minutes.": "단 몇 분 만에 첫 에이전트를 만들 수 있습니다",
|
||||
"Let's build": "시작하기",
|
||||
@@ -132,16 +132,13 @@ eng_to_non_eng_mapping = {
|
||||
"well formed data": "格式良好的数据",
|
||||
"guardrail": "安全防护措施",
|
||||
"handoffs": "任务转移",
|
||||
"function tools": "工具调用",
|
||||
"function tools": "函数工具",
|
||||
"tracing": "追踪",
|
||||
"code examples": "代码示例",
|
||||
"vector store": "向量存储",
|
||||
"deep research": "深度研究",
|
||||
"category": "目录",
|
||||
"user": "用户",
|
||||
"parameter": "参数",
|
||||
"processor": "进程",
|
||||
"server": "服务",
|
||||
"web search": "网络检索",
|
||||
"file search": "文件检索",
|
||||
"streaming": "流式传输",
|
||||
@@ -155,6 +152,9 @@ eng_to_non_eng_instructions = {
|
||||
"common": [
|
||||
"* The term 'examples' must be code examples when the page mentions the code examples in the repo, it can be translated as either 'code examples' or 'sample code'.",
|
||||
"* The term 'primitives' can be translated as basic components.",
|
||||
"* Prefer established technical usage in the target language. Do not invent an awkward localized alternative solely to avoid an English term when that English term is standard in developer documentation.",
|
||||
"* Preserve distinctions between SDK concepts. For example, a function tool is not a tool call, a processor is not a process, and a server is not automatically a service.",
|
||||
"* In Python packaging contexts, 'extras' means installable optional-dependency extras, not dependency groups. Keep 'extras' in English when a literal translation would be unfamiliar or ambiguous.",
|
||||
"* When the terms 'instructions' and 'tools' are mentioned as API parameter names, they must be kept as is.",
|
||||
"* The terms 'temperature', 'top_p', 'max_tokens', 'presence_penalty', 'frequency_penalty' as parameter names must be kept as is.",
|
||||
"* Keep the original structure like `* **The thing**: foo`; this needs to be translated as `* **(translation)**: (translation)`",
|
||||
@@ -168,6 +168,7 @@ eng_to_non_eng_instructions = {
|
||||
"ko": [
|
||||
"* 공손하고 중립적인 문체(합니다/입니다체)를 일관되게 사용하세요.",
|
||||
"* 개발자 문서이므로 자연스러운 의역을 허용하되 정확성을 유지하세요.",
|
||||
"* 기술 문맥의 'raw'는 가공되지 않은 저수준 데이터라는 뜻입니다. 문맥에 따라 자연스럽게 번역하거나 영어 'raw'를 유지하되, 원문(source text)이라는 뜻으로 번역하지 마세요.",
|
||||
"* 'instructions', 'tools' 같은 API 매개변수와 temperature, top_p, max_tokens, presence_penalty, frequency_penalty 등은 영문 그대로 유지하세요.",
|
||||
"* 문장이 아닌 불릿 항목 끝에는 마침표를 찍지 마세요.",
|
||||
],
|
||||
@@ -210,7 +211,7 @@ You must return **only** the translated markdown. Do not include any commentary,
|
||||
- Do not change the markdown data structure, including the indentations.
|
||||
- Section titles starting with # or ## must be a noun form rather than a sentence.
|
||||
- Section titles must be translated except for the Do-Not-Translate list.
|
||||
- Keep all placeholders such as `CODE_BLOCK_*` and `CODE_LINE_PREFIX` unchanged.
|
||||
- Keep all placeholders such as `CODE_BLOCK_*`, `INLINE_CODE_*`, and `CODE_LINE_PREFIX` unchanged.
|
||||
- Convert asset paths: `./assets/…` → `../assets/…`.
|
||||
*Example:* `` → ``
|
||||
- Treat the **Do‑Not‑Translate list** and **Term‑Specific list** as case‑insensitive; preserve the original casing you see.
|
||||
@@ -223,6 +224,7 @@ You must return **only** the translated markdown. Do not include any commentary,
|
||||
## HARD CONSTRAINTS ##
|
||||
#########################
|
||||
- Never insert spaces immediately inside emphasis markers. Use `**bold**`, not `** bold **`.
|
||||
- Preserve every source inline-code span exactly once. Do not add, remove, duplicate, split, merge, or translate inline-code spans. Keep each span with the text it describes, but move it when target-language grammar requires a different word order.
|
||||
- Preserve the number of emphasis markers from the source: if the source uses `**` or `__`, keep the same pair count.
|
||||
- Ensure one space after heading markers: `##Heading` -> `## Heading`.
|
||||
- Ensure one space after list markers: `-Item` -> `- Item`, `*Item` -> `* Item` (does not apply to `**`).
|
||||
@@ -292,77 +294,272 @@ Follow the following workflow to translate the given markdown text data:
|
||||
"""
|
||||
|
||||
|
||||
FENCE_OPENING_PATTERN = re.compile(r"^[ \t]*(?P<marker>`{3,}|~{3,})(?P<info>.*)$")
|
||||
|
||||
|
||||
def opening_fence(line: str) -> tuple[str, int] | None:
|
||||
match = FENCE_OPENING_PATTERN.match(line)
|
||||
if match is None:
|
||||
return None
|
||||
marker = match.group("marker")
|
||||
if marker[0] == "`" and "`" in match.group("info"):
|
||||
return None
|
||||
return marker[0], len(marker)
|
||||
|
||||
|
||||
def is_closing_fence(line: str, marker: str, minimum_length: int) -> bool:
|
||||
return re.fullmatch(rf"[ \t]*{re.escape(marker)}{{{minimum_length},}}[ \t]*", line) is not None
|
||||
|
||||
|
||||
def fenced_code_ranges(markdown: str) -> list[tuple[int, int]]:
|
||||
ranges: list[tuple[int, int]] = []
|
||||
open_fence: tuple[str, int] | None = None
|
||||
block_start = 0
|
||||
offset = 0
|
||||
for line_with_ending in markdown.splitlines(keepends=True):
|
||||
line = line_with_ending.rstrip("\r\n")
|
||||
line_end = offset + len(line)
|
||||
if open_fence is None:
|
||||
opening = opening_fence(line)
|
||||
if opening is not None:
|
||||
open_fence = opening
|
||||
block_start = offset
|
||||
elif is_closing_fence(line, *open_fence):
|
||||
ranges.append((block_start, line_end))
|
||||
open_fence = None
|
||||
offset += len(line_with_ending)
|
||||
if open_fence is not None:
|
||||
raise ValueError("Unclosed fenced code block")
|
||||
return ranges
|
||||
|
||||
|
||||
def fenced_code_blocks(markdown: str) -> list[str]:
|
||||
return [markdown[start:end] for start, end in fenced_code_ranges(markdown)]
|
||||
|
||||
|
||||
def remove_fenced_code_blocks(markdown: str) -> str:
|
||||
parts: list[str] = []
|
||||
cursor = 0
|
||||
for start, end in fenced_code_ranges(markdown):
|
||||
parts.append(markdown[cursor:start])
|
||||
cursor = end
|
||||
parts.append(markdown[cursor:])
|
||||
return "".join(parts)
|
||||
|
||||
|
||||
def protect_fenced_code(markdown: str, *, namespace: str) -> tuple[str, list[str]]:
|
||||
parts: list[str] = []
|
||||
code_blocks: list[str] = []
|
||||
cursor = 0
|
||||
for index, (start, end) in enumerate(fenced_code_ranges(markdown)):
|
||||
parts.append(markdown[cursor:start])
|
||||
parts.append(code_block_placeholder(namespace, index))
|
||||
code_blocks.append(markdown[start:end])
|
||||
cursor = end
|
||||
parts.append(markdown[cursor:])
|
||||
return "".join(parts), code_blocks
|
||||
|
||||
|
||||
def backtick_run_end(markdown: str, start: int) -> int:
|
||||
end = start + 1
|
||||
while end < len(markdown) and markdown[end] == "`":
|
||||
end += 1
|
||||
return end
|
||||
|
||||
|
||||
def inline_code_ranges(markdown: str) -> list[tuple[int, int]]:
|
||||
ranges: list[tuple[int, int]] = []
|
||||
cursor = 0
|
||||
while cursor < len(markdown):
|
||||
opener_start = markdown.find("`", cursor)
|
||||
if opener_start < 0:
|
||||
break
|
||||
opener_end = backtick_run_end(markdown, opener_start)
|
||||
delimiter_length = opener_end - opener_start
|
||||
search_from = opener_end
|
||||
matching_closer_end: int | None = None
|
||||
while search_from < len(markdown):
|
||||
closer_start = markdown.find("`", search_from)
|
||||
if closer_start < 0:
|
||||
break
|
||||
closer_end = backtick_run_end(markdown, closer_start)
|
||||
if closer_end - closer_start == delimiter_length:
|
||||
matching_closer_end = closer_end
|
||||
break
|
||||
search_from = closer_end
|
||||
if matching_closer_end is None:
|
||||
cursor = opener_end
|
||||
else:
|
||||
ranges.append((opener_start, matching_closer_end))
|
||||
cursor = matching_closer_end
|
||||
return ranges
|
||||
|
||||
|
||||
def inline_code_spans(markdown: str) -> list[str]:
|
||||
without_fences = remove_fenced_code_blocks(markdown)
|
||||
return [without_fences[start:end] for start, end in inline_code_ranges(without_fences)]
|
||||
|
||||
|
||||
def inline_code_spans_match(source: str, translated: str) -> bool:
|
||||
try:
|
||||
return Counter(inline_code_spans(source)) == Counter(inline_code_spans(translated))
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def fenced_code_blocks_match(source: str, translated: str) -> bool:
|
||||
try:
|
||||
return fenced_code_blocks(source) == fenced_code_blocks(translated)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def placeholder_namespace(markdown: str) -> str:
|
||||
namespace_index = 0
|
||||
while True:
|
||||
namespace = f"T{namespace_index}_"
|
||||
if f"CODE_BLOCK_{namespace}" not in markdown and f"INLINE_CODE_{namespace}" not in markdown:
|
||||
return namespace
|
||||
namespace_index += 1
|
||||
|
||||
|
||||
def code_block_placeholder(namespace: str, index: int) -> str:
|
||||
return f"CODE_BLOCK_{namespace}{index:03}"
|
||||
|
||||
|
||||
def inline_code_placeholder(namespace: str, index: int) -> str:
|
||||
return f"`INLINE_CODE_{namespace}{index:04}`"
|
||||
|
||||
|
||||
def restore_placeholders(markdown: str, replacements: dict[str, str]) -> str:
|
||||
if not replacements:
|
||||
return markdown
|
||||
placeholders = sorted(replacements, key=len, reverse=True)
|
||||
pattern = re.compile("|".join(re.escape(value) for value in placeholders))
|
||||
return pattern.sub(lambda match: replacements[match.group(0)], markdown)
|
||||
|
||||
|
||||
def placeholders_preserved(markdown: str, placeholders: list[str]) -> bool:
|
||||
return all(markdown.count(placeholder) == 1 for placeholder in placeholders)
|
||||
|
||||
|
||||
def protect_inline_code(
|
||||
markdown: str, *, namespace: str = "", start_index: int = 0
|
||||
) -> tuple[str, list[str]]:
|
||||
parts: list[str] = []
|
||||
inline_codes: list[str] = []
|
||||
cursor = 0
|
||||
for start, end in inline_code_ranges(markdown):
|
||||
parts.append(markdown[cursor:start])
|
||||
parts.append(inline_code_placeholder(namespace, start_index + len(inline_codes)))
|
||||
inline_codes.append(markdown[start:end])
|
||||
cursor = end
|
||||
parts.append(markdown[cursor:])
|
||||
return "".join(parts), inline_codes
|
||||
|
||||
|
||||
def restore_inline_code(markdown: str, inline_codes: list[str], *, namespace: str = "") -> str:
|
||||
replacements = {
|
||||
inline_code_placeholder(namespace, idx): inline_code
|
||||
for idx, inline_code in enumerate(inline_codes)
|
||||
}
|
||||
return restore_placeholders(markdown, replacements)
|
||||
|
||||
|
||||
def restore_code_blocks(markdown: str, code_blocks: list[str], *, namespace: str) -> str:
|
||||
replacements = {
|
||||
code_block_placeholder(namespace, idx): code_block
|
||||
for idx, code_block in enumerate(code_blocks)
|
||||
}
|
||||
return restore_placeholders(markdown, replacements)
|
||||
|
||||
|
||||
def translate_chunk(chunk: str, instructions: str) -> str:
|
||||
if OPENAI_MODEL.startswith("gpt-5"):
|
||||
response = openai_client.responses.create(
|
||||
model=OPENAI_MODEL,
|
||||
instructions=instructions,
|
||||
input=chunk,
|
||||
reasoning={"effort": "high"},
|
||||
text={"verbosity": "medium"},
|
||||
)
|
||||
elif OPENAI_MODEL.startswith("o"):
|
||||
response = openai_client.responses.create(
|
||||
model=OPENAI_MODEL,
|
||||
instructions=instructions,
|
||||
input=chunk,
|
||||
)
|
||||
else:
|
||||
response = openai_client.responses.create(
|
||||
model=OPENAI_MODEL,
|
||||
instructions=instructions,
|
||||
input=chunk,
|
||||
temperature=0.0,
|
||||
)
|
||||
return response.output_text
|
||||
|
||||
|
||||
# Function to translate and save files
|
||||
def translate_file(file_path: str, target_path: str, lang_code: str) -> None:
|
||||
print(f"Translating {file_path} into a different language: {lang_code}")
|
||||
with open(file_path, encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
namespace = placeholder_namespace(content)
|
||||
|
||||
if ENABLE_CODE_SNIPPET_EXCLUSION is True:
|
||||
protected_content, code_blocks = protect_fenced_code(content, namespace=namespace)
|
||||
else:
|
||||
protected_content = content
|
||||
code_blocks = []
|
||||
|
||||
# Split content into lines
|
||||
lines: list[str] = content.splitlines()
|
||||
lines: list[str] = protected_content.splitlines()
|
||||
chunks: list[str] = []
|
||||
current_chunk: list[str] = []
|
||||
|
||||
# Split content into chunks of up to 120 lines, ensuring splits occur before section titles
|
||||
in_code_block = False
|
||||
code_blocks: list[str] = []
|
||||
code_block_chunks: list[str] = []
|
||||
for line in lines:
|
||||
if (
|
||||
ENABLE_SMALL_CHUNK_TRANSLATION is True
|
||||
and len(current_chunk) >= 120 # required for gpt-4.5
|
||||
and not in_code_block
|
||||
and line.startswith("#")
|
||||
):
|
||||
chunks.append("\n".join(current_chunk))
|
||||
current_chunk = []
|
||||
if ENABLE_CODE_SNIPPET_EXCLUSION is True and line.strip().startswith("```"):
|
||||
code_block_chunks.append(line)
|
||||
if in_code_block is True:
|
||||
code_blocks.append("\n".join(code_block_chunks))
|
||||
current_chunk.append(f"CODE_BLOCK_{(len(code_blocks) - 1):03}")
|
||||
code_block_chunks.clear()
|
||||
in_code_block = not in_code_block
|
||||
continue
|
||||
if in_code_block is True:
|
||||
code_block_chunks.append(line)
|
||||
else:
|
||||
current_chunk.append(line)
|
||||
current_chunk.append(line)
|
||||
if current_chunk:
|
||||
chunks.append("\n".join(current_chunk))
|
||||
|
||||
# Translate each chunk separately and combine results
|
||||
translated_content: list[str] = []
|
||||
inline_codes: list[str] = []
|
||||
protected_chunks: list[str] = []
|
||||
for chunk in chunks:
|
||||
instructions = built_instructions(languages[lang_code], lang_code)
|
||||
if OPENAI_MODEL.startswith("gpt-5"):
|
||||
response = openai_client.responses.create(
|
||||
model=OPENAI_MODEL,
|
||||
instructions=instructions,
|
||||
input=chunk,
|
||||
reasoning={"effort": "high"},
|
||||
text={"verbosity": "medium"},
|
||||
)
|
||||
translated_content.append(response.output_text)
|
||||
elif OPENAI_MODEL.startswith("o"):
|
||||
response = openai_client.responses.create(
|
||||
model=OPENAI_MODEL,
|
||||
instructions=instructions,
|
||||
input=chunk,
|
||||
)
|
||||
translated_content.append(response.output_text)
|
||||
else:
|
||||
response = openai_client.responses.create(
|
||||
model=OPENAI_MODEL,
|
||||
instructions=instructions,
|
||||
input=chunk,
|
||||
temperature=0.0,
|
||||
)
|
||||
translated_content.append(response.output_text)
|
||||
protected_chunk, chunk_inline_codes = protect_inline_code(
|
||||
chunk, namespace=namespace, start_index=len(inline_codes)
|
||||
)
|
||||
protected_chunks.append(protected_chunk)
|
||||
inline_codes.extend(chunk_inline_codes)
|
||||
chunks = protected_chunks
|
||||
|
||||
translated_text = "\n".join(translated_content)
|
||||
for idx, code_block in enumerate(code_blocks):
|
||||
translated_text = translated_text.replace(f"CODE_BLOCK_{idx:03}", code_block)
|
||||
instructions = built_instructions(languages[lang_code], lang_code)
|
||||
translated_text = ""
|
||||
for _attempt in range(3):
|
||||
translated_text = "\n".join(translate_chunk(chunk, instructions) for chunk in chunks)
|
||||
placeholders = [
|
||||
*(code_block_placeholder(namespace, idx) for idx in range(len(code_blocks))),
|
||||
*(inline_code_placeholder(namespace, idx) for idx in range(len(inline_codes))),
|
||||
]
|
||||
if not placeholders_preserved(translated_text, placeholders):
|
||||
continue
|
||||
translated_text = restore_inline_code(translated_text, inline_codes, namespace=namespace)
|
||||
translated_text = restore_code_blocks(translated_text, code_blocks, namespace=namespace)
|
||||
if inline_code_spans_match(content, translated_text) and fenced_code_blocks_match(
|
||||
content, translated_text
|
||||
):
|
||||
break
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Protected Markdown changed after 3 translation attempts for {file_path} to {lang_code}"
|
||||
)
|
||||
|
||||
# FIXME: enable mkdocs search plugin to seamlessly work with i18n plugin
|
||||
translated_text = SEARCH_EXCLUSION + translated_text
|
||||
|
||||
@@ -81,7 +81,7 @@ session = AdvancedSQLiteSession(
|
||||
### 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
|
||||
- `db_path` (str | Path): Path to SQLite database file. Defaults to `:memory:`, which uses 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
|
||||
|
||||
@@ -245,7 +245,7 @@ for turn in matching_turns:
|
||||
|
||||
The session automatically tracks message structure including:
|
||||
|
||||
- Message types (user, assistant, tool_call, etc.)
|
||||
- Message type values (`user`, `assistant`, `tool_call`, etc.)
|
||||
- Tool names for tool calls
|
||||
- Turn numbers and sequence numbers
|
||||
- Branch associations
|
||||
@@ -284,7 +284,7 @@ CREATE TABLE branch_reservations (
|
||||
);
|
||||
```
|
||||
|
||||
This table atomically reserves branch IDs, including branches whose copied prefix is empty. Reservation rows are retained after branch deletion and session clearing so stale session instances cannot merge history into a later branch that reused the same ID.
|
||||
This table atomically reserves branch IDs, including branches whose copied prefix is empty. Reservation rows are retained both when a branch is deleted and when the session is cleared, so stale session instances cannot merge history into a later branch that reused the same ID.
|
||||
|
||||
### turn_usage table
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ The Agents SDK provides built-in session memory to automatically maintain conver
|
||||
|
||||
Sessions stores conversation history for a specific session, allowing agents to maintain context without requiring explicit manual memory management. This is particularly useful for building chat applications or multi-turn conversations where you want the agent to remember previous interactions.
|
||||
|
||||
Use sessions when you want the SDK to manage client-side memory for you. Sessions cannot be combined with `conversation_id`, `previous_response_id`, or `auto_previous_response_id` in the same run. If you want OpenAI server-managed continuation instead, choose one of those mechanisms rather than layering a session on top.
|
||||
Use sessions when you want the SDK to manage client-side memory for you. In the same run, a session cannot be combined with the run-level continuation options `conversation_id`, `previous_response_id`, or `auto_previous_response_id`. If you want OpenAI server-managed continuation instead, choose one of those mechanisms rather than layering a session on top.
|
||||
|
||||
## Quick start
|
||||
|
||||
@@ -47,7 +47,7 @@ print(result.final_output) # "Approximately 39 million"
|
||||
|
||||
## Resuming interrupted runs with the same session
|
||||
|
||||
If a run pauses for approval, resume it with the same session instance (or another session instance that points at the same backing store) so the resumed turn continues the same stored conversation history.
|
||||
If a run pauses for approval, resume it with the same session instance (or another instance configured with the same session ID and the same underlying storage backend) so the resumed turn continues the same stored conversation history.
|
||||
|
||||
```python
|
||||
result = await Runner.run(agent, "Delete temporary files that are no longer needed.", session=session)
|
||||
@@ -130,7 +130,7 @@ result = await Runner.run(
|
||||
)
|
||||
```
|
||||
|
||||
If your session implementation exposes default session settings, `RunConfig.session_settings` overrides any non-`None` values for that run. This is useful for long conversations where you want to cap retrieval size without changing the session's default behavior.
|
||||
If your session implementation exposes default session settings, each non-`None` value in `RunConfig.session_settings` overrides the corresponding default for that run. This is useful for long conversations where you want to cap retrieval size without changing the session's default behavior.
|
||||
|
||||
## Memory operations
|
||||
|
||||
@@ -274,9 +274,9 @@ result = await Runner.run(agent, "Hello", session=session)
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
By default, compaction runs after each turn once the candidate threshold is reached.
|
||||
By default, after each turn, the SDK checks whether the compaction candidate meets the threshold and compacts only if it does.
|
||||
|
||||
`compaction_mode="previous_response_id"` works best when you are already chaining turns with Responses API response IDs. `compaction_mode="input"` rebuilds the compaction request from the current session items instead, which is useful when the response chain is unavailable or you want the session contents to be the source of truth. The default `"auto"` chooses the safest available option.
|
||||
`compaction_mode="previous_response_id"` uses Responses API response IDs retained by the compaction session and works best while that response chain remains available. `compaction_mode="input"` rebuilds the compaction request from the current session items instead, which is useful when the response chain is unavailable or you want the session contents to be the source of truth. The default `"auto"` chooses the safest available option.
|
||||
|
||||
If your agent runs with `ModelSettings(store=False)`, the Responses API does not retain the last response for later lookup. In that stateless setup, the default `"auto"` mode falls back to input-based compaction instead of relying on `previous_response_id`. See [`examples/memory/compaction_session_stateless_example.py`](https://github.com/openai/openai-agents-python/tree/main/examples/memory/compaction_session_stateless_example.py) for a complete example.
|
||||
|
||||
@@ -390,7 +390,7 @@ See [SQLAlchemy Sessions](sqlalchemy_session.md) for detailed documentation.
|
||||
|
||||
### Dapr sessions
|
||||
|
||||
Use `DaprSession` when you already run Dapr sidecars or want session storage that can move across different state-store backends without changing your agent code.
|
||||
Use `DaprSession` when you already run Dapr sidecars or want to switch the configured state-store backend without changing your agent code.
|
||||
|
||||
```bash
|
||||
pip install openai-agents[dapr]
|
||||
@@ -415,7 +415,7 @@ Notes:
|
||||
|
||||
- `from_address(...)` creates and owns the Dapr client for you. If your app already manages one, construct `DaprSession(...)` directly with `dapr_client=...`.
|
||||
- Exiting the context or calling `close()` makes an owned-client session terminal; subsequent session operations raise `RuntimeError`, while repeated or concurrent `close()` calls are safe. With an injected client, `close()` is a no-op and the session remains usable.
|
||||
- Pass `ttl=...` to let the backing state store expire old session data automatically when the store supports TTL.
|
||||
- If the backing state store supports TTL, pass `ttl=...` so it automatically applies TTL expiration to the session data.
|
||||
- Pass `consistency=DAPR_CONSISTENCY_STRONG` when you need stronger read-after-write guarantees.
|
||||
- The Dapr Python SDK also checks the HTTP sidecar endpoint. In local development, start Dapr with `--dapr-http-port 3500` as well as the gRPC port used in `dapr_address`.
|
||||
- See [`examples/memory/dapr_session_example.py`](https://github.com/openai/openai-agents-python/tree/main/examples/memory/dapr_session_example.py) for a full setup walkthrough, including local components and troubleshooting.
|
||||
@@ -448,7 +448,7 @@ await session.close()
|
||||
|
||||
Notes:
|
||||
|
||||
- `from_uri(...)` creates and owns the `AsyncMongoClient` and closes it on `session.close()`. An owned-client session is terminal after `close()`, and subsequent session operations raise `RuntimeError`. If your application already manages a client, construct `MongoDBSession(...)` directly with `client=...`; in that case `session.close()` is a no-op, and lifecycle plus session usability stay with the caller.
|
||||
- `from_uri(...)` creates and owns the `AsyncMongoClient` and closes it on `session.close()`. An owned-client session is terminal after `close()`, and subsequent session operations raise `RuntimeError`. If your application already manages a client, construct `MongoDBSession(...)` directly with `client=...`; in that case, `session.close()` is a no-op, the caller retains responsibility for the client lifecycle, and the session remains usable.
|
||||
- Connect to [MongoDB Atlas](https://www.mongodb.com/products/platform) by passing an `mongodb+srv://user:password@cluster.example.mongodb.net` URI to `from_uri(...)` with no other changes.
|
||||
- Two collections are used and both names are configurable via `sessions_collection=` (default `agent_sessions`) and `messages_collection=` (default `agent_messages`). Indexes are created automatically on first use. Each non-empty `add_items()` call writes one logical-batch document whose monotonically increasing `seq` orders the batch by its final item; legacy per-item message documents remain readable. A logical batch must fit within MongoDB's single-document size limit; an oversized batch fails atomically without storing a partial batch.
|
||||
- Use `await session.ping()` to verify connectivity before your first run.
|
||||
@@ -526,7 +526,7 @@ Use meaningful session IDs that help you organize conversations:
|
||||
- Use Redis-backed sessions (`RedisSession.from_url("session_id", url="redis://...")`) for shared, low-latency session memory
|
||||
- Use SQLAlchemy-powered sessions (`SQLAlchemySession("session_id", engine=engine, create_tables=True)`) for production systems with existing databases supported by SQLAlchemy
|
||||
- Use MongoDB sessions (`MongoDBSession.from_uri("session_id", uri="mongodb://localhost:27017")`) for applications already using MongoDB or needing multi-process, horizontally-scalable session storage
|
||||
- Use Dapr state store sessions (`DaprSession.from_address("session_id", state_store_name="statestore", dapr_address="localhost:50001")`) for production cloud-native deployments with support for 30+ database backends with built-in telemetry, tracing, and data isolation
|
||||
- Use Dapr state store sessions (`DaprSession.from_address("session_id", state_store_name="statestore", dapr_address="localhost:50001")`) for production cloud-native deployments with built-in telemetry, tracing, and data isolation and support for 30+ database backends
|
||||
- Use OpenAI-hosted storage (`OpenAIConversationsSession()`) when you prefer to store history in the OpenAI Conversations API
|
||||
- Use encrypted sessions (`EncryptedSession(session_id, underlying_session, encryption_key)`) to wrap any session with transparent encryption and TTL-based expiration
|
||||
- Consider implementing custom session backends for other production systems (for example, Django) for more advanced use cases
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Installation
|
||||
|
||||
SQLAlchemy sessions require the `sqlalchemy` extra:
|
||||
SQLAlchemy sessions require the `sqlalchemy` optional-dependency extra from the `openai-agents` package:
|
||||
|
||||
```bash
|
||||
pip install openai-agents[sqlalchemy]
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ Keep consuming `result.stream_events()` until the async iterator finishes. A str
|
||||
|
||||
## Raw response events
|
||||
|
||||
[`RawResponsesStreamEvent`][agents.stream_events.RawResponsesStreamEvent] are raw events passed directly from the LLM. They are in OpenAI Responses API format, which means each event has a type (like `response.created`, `response.output_text.delta`, etc) and data. These events are useful if you want to stream response messages to the user as soon as they are generated.
|
||||
[`RawResponsesStreamEvent`][agents.stream_events.RawResponsesStreamEvent] objects wrap raw events passed directly from the LLM. Each object's `data` field contains an OpenAI Responses API event with a type such as `response.created` or `response.output_text.delta`. These events are useful if you want to stream response messages to the user as soon as they are generated.
|
||||
|
||||
Computer-tool raw events keep the same preview-vs-GA distinction as stored results. Preview flows stream `computer_call` items with one `action`, while `gpt-5.5` can stream `computer_call` items with batched `actions[]`. The higher-level [`RunItemStreamEvent`][agents.stream_events.RunItemStreamEvent] surface does not add a special computer-only event name for this: both shapes still surface as `tool_called`, and the screenshot result comes back as `tool_output` wrapping a `computer_call_output` item.
|
||||
|
||||
@@ -61,7 +61,7 @@ If you need to stop a streaming run in the middle, call [`result.cancel()`][agen
|
||||
|
||||
A streamed run is not complete until `result.stream_events()` finishes. The SDK may still be persisting session items, finalizing approval state, or compacting history after the last visible token.
|
||||
|
||||
If you are manually continuing from [`result.to_input_list(mode="normalized")`][agents.result.RunResultBase.to_input_list], and `cancel(mode="after_turn")` stops after a tool turn, continue that unfinished turn by rerunning `result.last_agent` with that normalized input instead of appending a fresh user turn right away.
|
||||
If you are manually continuing from [`result.to_input_list(mode="normalized")`][agents.result.RunResultBase.to_input_list], and `cancel(mode="after_turn")` stops after a tool turn, rerun `result.last_agent` with that normalized input to continue the unfinished existing user turn instead of appending a fresh user turn right away.
|
||||
- If a streamed run stopped for tool approval, do not treat that as a new turn. Finish draining the stream, inspect `result.interruptions`, and resume from `result.to_state()` instead.
|
||||
- Use [`RunConfig.session_input_callback`][agents.run.RunConfig.session_input_callback] to customize how retrieved session history and the new user input are merged before the next model call. If you rewrite new-turn items there, the rewritten version is what gets persisted for that turn.
|
||||
|
||||
@@ -91,7 +91,7 @@ A handoff call is emitted only as `handoff_requested`; it is not also emitted as
|
||||
|
||||
When you use hosted tool search, `tool_search_called` is emitted when the model issues a tool-search request and `tool_search_output_created` is emitted when the Responses API returns the loaded subset.
|
||||
|
||||
With Programmatic Tool Calling, `tool_called` is emitted for the generated `program` and for ordinary program-owned child tool calls. `tool_output` is emitted for child tool outputs and the matching `program_output`. Program-owned hosted MCP `mcp_approval_request` and `mcp_list_tools` items are exceptions: they are emitted as `mcp_approval_requested` and `mcp_list_tools`, wrapping [`MCPApprovalRequestItem`][agents.items.MCPApprovalRequestItem] and [`MCPListToolsItem`][agents.items.MCPListToolsItem], respectively. Inspect the raw item's `type` to distinguish the remaining items; program-owned child calls also carry a `caller` whose type is `program` and whose caller ID identifies the parent program.
|
||||
With Programmatic Tool Calling, `tool_called` is emitted for the generated `program` and for ordinary program-owned child tool calls. `tool_output` is emitted for child tool outputs and for the `program_output` that matches the generated `program`. Program-owned hosted MCP `mcp_approval_request` and `mcp_list_tools` items are exceptions: they are emitted as `mcp_approval_requested` and `mcp_list_tools`, wrapping [`MCPApprovalRequestItem`][agents.items.MCPApprovalRequestItem] and [`MCPListToolsItem`][agents.items.MCPListToolsItem], respectively. Inspect the raw item's `type` to distinguish the remaining items; program-owned child calls also carry a `caller` whose type is `program` and whose caller ID identifies the parent program.
|
||||
|
||||
For example, this will ignore raw events and stream updates to the user.
|
||||
|
||||
|
||||
+8
-8
@@ -2,9 +2,9 @@
|
||||
|
||||
Tools let agents take actions: things like fetching data, running code, calling external APIs, and even using a computer. The SDK supports five categories:
|
||||
|
||||
- Hosted OpenAI tools: run alongside the model on OpenAI servers.
|
||||
- Hosted OpenAI tools: execute for the model on OpenAI servers.
|
||||
- Local/runtime execution tools: `ComputerTool` and `ApplyPatchTool` always run in your environment, while `ShellTool` can run locally or in a hosted container.
|
||||
- Function calling: wrap any Python function as a tool.
|
||||
- `FunctionTool` instances: wrap any Python function as a tool.
|
||||
- Agents as tools: expose an agent as a callable tool without a full handoff.
|
||||
- Experimental: Codex tool: run workspace-scoped Codex tasks from a tool call.
|
||||
|
||||
@@ -167,7 +167,7 @@ What to know:
|
||||
- Add at most one `ProgrammaticToolCallingTool()` to an agent. The agent must also expose at least one programmatically callable tool, a `ToolSearchTool()` backed by a namespace, deferred function, or deferred hosted MCP server, or an opaque prompt-managed tool surface. A bare `ToolSearchTool()` without a searchable surface is rejected.
|
||||
- `allowed_callers` controls how a tool may be invoked. Omitting it allows direct model calls only. Use `["programmatic"]` for program-only access or `["direct", "programmatic"]` to allow both.
|
||||
- SDK tool types that can opt in are `FunctionTool`, `CustomTool`, `ShellTool`, `ApplyPatchTool`, `HostedMCPTool`, and `CodeInterpreterTool`. Function, custom, shell, and apply-patch tools expose `allowed_callers` directly. For hosted MCP and code interpreter, set `allowed_callers` inside `tool_config`.
|
||||
- For `@function_tool(allowed_callers=[...])`, a structured return annotation such as a Pydantic model, TypedDict, or dataclass automatically becomes a strict object output schema and is validated before the value is returned to the program. Use `output_type=...` when the function has no usable annotation, or the lower-level `output_json_schema={...}` escape hatch when you already have a strict object schema. `output_type` and `output_json_schema` are mutually exclusive. Plain `str`, `Any`, and `None` returns remain untyped. For a schema-backed program-owned call, the default failure formatter is disabled because its free-form text does not satisfy the output schema. A handler exception therefore propagates unless you provide a custom `failure_error_function` that returns schema-conforming JSON.
|
||||
- For `@function_tool(allowed_callers=[...])`, a structured return annotation such as a Pydantic model, TypedDict, or dataclass automatically becomes a strict object output schema, and the returned value is validated against that schema before it is returned to the program. Use `output_type=...` when the function has no usable annotation, or the lower-level `output_json_schema={...}` escape hatch when you already have a strict object schema. `output_type` and `output_json_schema` are mutually exclusive. Return annotations of `str`, `Any`, or `None` do not create an output schema. For a schema-backed program-owned call, the default failure formatter is disabled because its free-form text does not satisfy the output schema. A handler exception therefore propagates unless you provide a custom `failure_error_function` that returns schema-conforming JSON.
|
||||
- Program-owned SDK tools still use the normal Runner lifecycle. Tool input and output guardrails, hooks, timeouts, concurrency limits, approvals, sessions, and `RunState` pause/resume behavior continue to apply, and the SDK preserves each child call's program caller relationship.
|
||||
- Model-request retries use a stricter replay-safety boundary whenever `ProgrammaticToolCallingTool()` is present, even before a program executes. The SDK disables provider-managed retries and WebSocket pre-event retries for these requests. A Runner retry policy retries only when provider advice explicitly marks the replay safe; `retry_policies.network_error()` by itself does not override this boundary.
|
||||
- Approval-sensitive or high-impact tools are usually better kept as direct calls so a person can review each action before it becomes part of a larger program. If a program-owned call pauses for approval, resolve the interruption through `RunState` and resume the original run as usual.
|
||||
@@ -245,7 +245,7 @@ Shell action timeouts use positive integer milliseconds for a finite timeout. Th
|
||||
|
||||
`ComputerTool` is still a local harness: you provide a [`Computer`][agents.computer.Computer] or [`AsyncComputer`][agents.computer.AsyncComputer] implementation, and the SDK maps that harness onto the OpenAI Responses API computer surface.
|
||||
|
||||
For explicit [`gpt-5.5`](https://developers.openai.com/api/docs/models/gpt-5.5) requests, the SDK sends the GA built-in tool payload `{"type": "computer"}`. The older `computer-use-preview` model keeps the preview payload `{"type": "computer_use_preview", "environment": ..., "display_width": ..., "display_height": ...}`. This mirrors the platform migration described in OpenAI's [Computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use/):
|
||||
For explicit [`gpt-5.5`](https://developers.openai.com/api/docs/models/gpt-5.5) requests, the SDK sends the GA built-in tool payload `{"type": "computer"}`. For requests to the older `computer-use-preview` model, the SDK continues to send the preview payload `{"type": "computer_use_preview", "environment": ..., "display_width": ..., "display_height": ...}`. This mirrors the platform migration described in OpenAI's [Computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use/):
|
||||
|
||||
- Model: `computer-use-preview` -> `gpt-5.5`
|
||||
- Tool selector: `computer_use_preview` -> `computer`
|
||||
@@ -256,7 +256,7 @@ The SDK chooses that wire shape from the effective model on the actual Responses
|
||||
|
||||
When a [`ComputerTool`][agents.tool.ComputerTool] is present, `tool_choice="computer"`, `"computer_use"`, and `"computer_use_preview"` are all accepted and normalized to the built-in selector that matches the effective request model. Without a `ComputerTool`, those strings still behave like ordinary function names.
|
||||
|
||||
This distinction matters when `ComputerTool` is backed by a [`ComputerProvider`][agents.tool.ComputerProvider] factory. The GA `computer` payload does not need `environment` or dimensions at serialization time, so unresolved factories are fine. Preview-compatible serialization still needs a resolved `Computer` or `AsyncComputer` instance so the SDK can send `environment`, `display_width`, and `display_height`.
|
||||
This distinction matters when `ComputerTool` is backed by a [`ComputerProvider`][agents.tool.ComputerProvider] factory. The GA `computer` payload does not need `environment` or dimensions at serialization time, so serialization can occur before a factory has produced a `Computer` or `AsyncComputer` instance. Preview-compatible serialization still needs a resolved `Computer` or `AsyncComputer` instance so the SDK can send `environment`, `display_width`, and `display_height`.
|
||||
|
||||
At runtime, both paths still use the same local harness. Preview responses emit `computer_call` items with a single `action`; `gpt-5.5` can emit batched `actions[]`, and the SDK executes them in order before producing a `computer_call_output` screenshot item. See `examples/tools/computer_use.py` for a runnable Playwright-based harness.
|
||||
|
||||
@@ -368,7 +368,7 @@ for tool in agent.tools:
|
||||
|
||||
1. You can use any Python types as arguments to your functions, and the function can be sync or async.
|
||||
2. Docstrings, if present, are used to capture descriptions and argument descriptions
|
||||
3. Functions can optionally take the `context` (must be the first argument). You can also set overrides, like the name of the tool, description, which docstring style to use, etc.
|
||||
3. Functions can optionally take the run context as their first argument. You can also set overrides, like the name of the tool, description, which docstring style to use, etc.
|
||||
4. You can pass the decorated functions to the list of tools.
|
||||
|
||||
??? note "Expand to see output"
|
||||
@@ -653,7 +653,7 @@ if __name__ == "__main__":
|
||||
|
||||
### Customizing tool-agents
|
||||
|
||||
The `agent.as_tool` function is a convenience method to make it easy to turn an agent into a tool. It supports common runtime options such as `max_turns`, `run_config`, `hooks`, `previous_response_id`, `conversation_id`, `session`, and `needs_approval`. It also supports structured input with `parameters`, `input_builder`, and `include_input_schema`.
|
||||
`agent.as_tool` is a convenience method for turning an agent into a tool. It supports common runtime options such as `max_turns`, `run_config`, `hooks`, `previous_response_id`, `conversation_id`, `session`, and `needs_approval`. It also supports structured input with `parameters`, `input_builder`, and `include_input_schema`.
|
||||
|
||||
The state options configure the nested agent run started by the tool call; the parent run's conversation state is not inherited automatically. To share client-managed history between the parent and nested runs, explicitly pass the same `session` to both. As with `Runner.run`, choose one state strategy for the nested run: a client-managed `session`, or server-managed continuation through `previous_response_id` or `conversation_id`.
|
||||
|
||||
@@ -679,7 +679,7 @@ async def run_my_agent() -> str:
|
||||
|
||||
### Structured input for tool-agents
|
||||
|
||||
By default, `Agent.as_tool()` expects a single string input (`{"input": "..."}`), but you can expose a structured schema by passing `parameters` (a Pydantic model or dataclass type).
|
||||
By default, `Agent.as_tool()` expects an object with one string field, `input` (`{"input": "..."}`), but you can expose a structured schema by passing `parameters` (a Pydantic model type or a dataclass type).
|
||||
|
||||
Additional options:
|
||||
|
||||
|
||||
+8
-8
@@ -10,12 +10,12 @@ The Agents SDK includes built-in tracing, collecting a comprehensive record of e
|
||||
2. You can globally disable tracing in code with [`set_tracing_disabled(True)`][agents.set_tracing_disabled]
|
||||
3. You can disable tracing for a single run by setting [`agents.run.RunConfig.tracing_disabled`][] to `True`
|
||||
|
||||
***For organizations operating under a Zero Data Retention (ZDR) policy using OpenAI's APIs, tracing is unavailable.***
|
||||
***Tracing is unavailable for organizations that use OpenAI's APIs under a Zero Data Retention (ZDR) policy.***
|
||||
|
||||
## Traces and spans
|
||||
|
||||
- **Traces** represent a single end-to-end operation of a "workflow". They're composed of Spans. Traces have the following properties:
|
||||
- `workflow_name`: This is the logical workflow or app. For example "Code generation" or "Customer service".
|
||||
- `workflow_name`: This is the name of the logical workflow or app. For example "Code generation" or "Customer service".
|
||||
- `trace_id`: A unique ID for the trace. Automatically generated if you don't pass one. Must have the format `trace_<32_alphanumeric>`.
|
||||
- `group_id`: Optional group ID, to link multiple traces from the same conversation. For example, you might use a chat thread ID.
|
||||
- `disabled`: If True, the trace will not be recorded.
|
||||
@@ -40,9 +40,9 @@ By default, the SDK traces the following:
|
||||
- Handoffs are wrapped in `handoff_span()`
|
||||
- Audio inputs (speech-to-text) are wrapped in a `transcription_span()`
|
||||
- Audio outputs (text-to-speech) are wrapped in a `speech_span()`
|
||||
- Related audio spans may be parented under a `speech_group_span()`
|
||||
- The SDK may parent related audio spans under a `speech_group_span()`
|
||||
|
||||
By default, the trace is named "Agent workflow". You can set this name if you use `trace`, or you can configure the name and other properties with the [`RunConfig`][agents.run.RunConfig].
|
||||
By default, the trace name is the literal string `Agent workflow`. You can set this name if you use `trace`, or you can configure the name and other properties with the [`RunConfig`][agents.run.RunConfig].
|
||||
|
||||
If you want a more compact hierarchy, disable the automatic task and turn spans for a run. Agent, generation, function, guardrail, handoff, and custom spans are still recorded.
|
||||
|
||||
@@ -118,7 +118,7 @@ async def main():
|
||||
print(f"Rating: {second_result.final_output}")
|
||||
```
|
||||
|
||||
1. Because the two calls to `Runner.run` are wrapped in a `with trace()`, the individual runs will be part of the overall trace rather than creating two traces.
|
||||
1. Because the two calls to `Runner.run` are wrapped in a `with trace()`, both runs become part of one overall trace instead of each creating a separate trace.
|
||||
|
||||
## Creating traces
|
||||
|
||||
@@ -127,7 +127,7 @@ You can use the [`trace()`][agents.tracing.trace] function to create a trace. Tr
|
||||
1. **Recommended**: use the trace as a context manager, i.e. `with trace(...) as my_trace`. This will automatically start and end the trace at the right time.
|
||||
2. You can also manually call [`trace.start()`][agents.tracing.Trace.start] and [`trace.finish()`][agents.tracing.Trace.finish].
|
||||
|
||||
The current trace is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). This means that it works with concurrency automatically. If you manually start/end a trace, you'll need to pass `mark_as_current` and `reset_current` to `start()`/`finish()` to update the current trace.
|
||||
The current trace is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). This means that it works with concurrency automatically. If you manually start and finish a trace, pass `mark_as_current` to `start()` and `reset_current` to `finish()` to update the current trace.
|
||||
|
||||
## Creating spans
|
||||
|
||||
@@ -160,7 +160,7 @@ To customize this default setup, to send traces to alternative or additional bac
|
||||
|
||||
## Tracing with non-OpenAI models
|
||||
|
||||
You can use an OpenAI API key with non-OpenAI models to enable free tracing in the OpenAI Traces dashboard without needing to disable tracing. See the [Third-party adapters](models/index.md#third-party-adapters) section in the Models guide for adapter selection and setup caveats.
|
||||
When using non-OpenAI models, you can provide an OpenAI API key to the tracing exporter to enable free tracing in the OpenAI Traces dashboard without disabling tracing. See the [Third-party adapters](models/index.md#third-party-adapters) section in the Models guide for adapter selection and setup caveats.
|
||||
|
||||
```python
|
||||
import os
|
||||
@@ -199,7 +199,7 @@ await Runner.run(
|
||||
|
||||
## Ecosystem integrations
|
||||
|
||||
The following community and vendor integrations support the OpenAI Agents SDK tracing surface.
|
||||
The following community and vendor integrations support the tracing API surface of the OpenAI Agents SDK.
|
||||
|
||||
### External tracing processors list
|
||||
|
||||
|
||||
+4
-4
@@ -27,16 +27,16 @@ print("Output tokens:", usage.output_tokens)
|
||||
print("Total tokens:", usage.total_tokens)
|
||||
```
|
||||
|
||||
Usage is aggregated across all model calls during the run (including tool calls and handoffs).
|
||||
Usage is aggregated across all model calls during the run, including model calls that produce tool calls or handoffs.
|
||||
|
||||
### Enabling usage with third-party adapters
|
||||
|
||||
Usage reporting varies across third-party adapters and provider backends. If you rely on adapter-backed models and need accurate `result.context_wrapper.usage` values:
|
||||
Usage reporting varies across third-party adapters and provider backends. If you access models through third-party adapters and need accurate `result.context_wrapper.usage` values:
|
||||
|
||||
- With `AnyLLMModel`, usage is propagated automatically when the upstream provider returns it. For streamed Chat Completions backends, you may need `ModelSettings(include_usage=True)` before usage chunks are emitted.
|
||||
- With `AnyLLMModel`, usage is propagated automatically when the upstream provider returns it. When streaming responses from a Chat Completions backend, you may need `ModelSettings(include_usage=True)` for usage chunks to be emitted.
|
||||
- With `LitellmModel`, some provider backends do not report usage by default, so `ModelSettings(include_usage=True)` is often required.
|
||||
|
||||
Review the adapter-specific notes in the [Third-party adapters](models/index.md#third-party-adapters) section of the Models guide and validate the exact provider backend you plan to deploy.
|
||||
Review the adapter-specific notes in the [Third-party adapters](models/index.md#third-party-adapters) section of the Models guide and validate usage reporting on the exact provider backend you plan to deploy.
|
||||
|
||||
## Per-request usage tracking
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Agent visualization
|
||||
|
||||
Agent visualization allows you to generate a structured graphical representation of agents and their relationships using **Graphviz**. This is useful for understanding how agents, tools, and handoffs interact within an application.
|
||||
Agent visualization allows you to generate a structured graphical representation of agents and their connections to other agents, tools, and MCP servers using **Graphviz**. This is useful for understanding how agents, tools, and handoffs interact within an application.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -83,7 +83,7 @@ The generated graph includes:
|
||||
- **Dashed arrows** for MCP server invocations.
|
||||
- An **end node** (`__end__`) indicating where execution terminates.
|
||||
|
||||
**Note:** MCP servers are rendered in recent versions of the `agents` package (verified in **v0.2.8**). If you don’t see MCP boxes in your visualization, upgrade to the latest release.
|
||||
**Note:** MCP servers are rendered in recent versions of the `agents` package, including **v0.2.8**, where this behavior was verified. If you don’t see MCP boxes in your visualization, upgrade to the latest release.
|
||||
|
||||
## Customizing the graph
|
||||
|
||||
|
||||
@@ -74,4 +74,4 @@ async for event in result.stream():
|
||||
|
||||
### Interruptions
|
||||
|
||||
The Agents SDK currently does not provide any built-in interruption handling for [`StreamedAudioInput`][agents.voice.input.StreamedAudioInput]. Instead, every detected turn triggers a separate run of your workflow. If you want to handle interruptions inside your application, you can listen to the [`VoiceStreamEventLifecycle`][agents.voice.events.VoiceStreamEventLifecycle] events. `turn_started` indicates that a new turn was transcribed and processing is beginning. `turn_ended` triggers after all the audio was dispatched for a respective turn. You could use these events to mute the speaker's microphone when the model starts a turn and unmute it after you flush all the related audio for a turn.
|
||||
The Agents SDK currently does not provide any built-in interruption handling for [`StreamedAudioInput`][agents.voice.input.StreamedAudioInput]. Instead, every detected turn triggers a separate run of your workflow. If you want to handle interruptions inside your application, you can listen to the [`VoiceStreamEventLifecycle`][agents.voice.events.VoiceStreamEventLifecycle] events. `turn_started` indicates that a new turn was transcribed and processing is beginning. `turn_ended` triggers after all the audio was dispatched for a respective turn. You could use these events to mute the speaker's microphone when the model starts a turn and unmute it after your application finishes playing all audio related to that turn.
|
||||
|
||||
@@ -50,7 +50,7 @@ graph LR
|
||||
|
||||
## Agents
|
||||
|
||||
First, let's set up some Agents. This should feel familiar to you if you've built any agents with this SDK. We'll have a couple of Agents, a handoff, and a tool.
|
||||
First, let's set up some Agents. This should feel familiar to you if you've built any agents with this SDK. We'll have two Agents, a configured handoff, and a tool.
|
||||
|
||||
```python
|
||||
import random
|
||||
@@ -190,4 +190,4 @@ if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
If you run this example, the agent will speak to you! Check out the example in [examples/voice/static](https://github.com/openai/openai-agents-python/tree/main/examples/voice/static) to see a demo where you can speak to the agent yourself.
|
||||
If you run this example, the agent will produce spoken audio for you to hear! Check out the example in [examples/voice/static](https://github.com/openai/openai-agents-python/tree/main/examples/voice/static) to see a demo where you can speak to the agent yourself.
|
||||
|
||||
Reference in New Issue
Block a user