From d3b14237ac1a1c67db7385f9838ea1a3867f89fa Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Sat, 22 Aug 2026 09:58:02 +0900 Subject: [PATCH] docs: document v0.22.1 behavior updates --- docs/config.md | 4 +++ docs/guardrails.md | 19 ++++++++++-- docs/human_in_the_loop.md | 2 +- docs/mcp.md | 36 +++++++++++++++++++++++ docs/models/index.md | 8 +++-- docs/realtime/guide.md | 2 +- docs/results.md | 8 +++++ docs/sandbox/clients.md | 35 ++++++++++++++++++++++ docs/tools.md | 14 +++++---- docs/tracing.md | 4 +++ docs/usage.md | 2 ++ docs/visualization.md | 1 + docs/voice/pipeline.md | 61 +++++++++++++++++++++++++++++++++++++++ 13 files changed, 184 insertions(+), 12 deletions(-) diff --git a/docs/config.md b/docs/config.md index 98de64ea0a..8fe7d89bcf 100644 --- a/docs/config.md +++ b/docs/config.md @@ -53,6 +53,10 @@ set_default_openai_client(custom_client) When you pass an explicit client to [`OpenAIProvider`][agents.models.openai_provider.OpenAIProvider], that client owns its connection and account settings. Do not also pass `api_key`, `base_url`, `websocket_base_url`, `organization`, or `project` to `OpenAIProvider`; combining `openai_client` with any of those arguments raises [`UserError`][agents.exceptions.UserError] instead of silently ignoring the duplicate value. Set the intended values when constructing `AsyncOpenAI`. +When `openai_client` is omitted, `OpenAIProvider` reuses the SDK-wide default client only if `api_key`, `base_url`, `websocket_base_url`, `organization`, and `project` are all `None`. Passing any of those options, including an empty string, makes the provider create its own client and gives the provider option precedence over the SDK-wide default client. Leave every provider option as `None` when the provider should inherit the client installed by `set_default_openai_client()`. + +[`OpenAIVoiceModelProvider`][agents.voice.models.openai_model_provider.OpenAIVoiceModelProvider] uses the same ownership and precedence rules for `api_key`, `base_url`, `organization`, and `project`. Its explicit `openai_client` cannot be combined with any of those four options. + ### Custom HTTP clients with `openai` v3 Version 0.21.0 requires `openai>=3.0.0,<4`. The default OpenAI provider uses HTTPX2, so most applications do not need to configure an HTTP client directly. If your application passes `http_client=` to `AsyncOpenAI`, use HTTPX2 types for the custom client and its transport-facing options: diff --git a/docs/guardrails.md b/docs/guardrails.md index 5ae8485628..9e145a05ba 100644 --- a/docs/guardrails.md +++ b/docs/guardrails.md @@ -13,7 +13,7 @@ Guardrails are attached to agents and tools, but they do not all run at the same - **Input guardrails** run only for the first agent in the chain. - **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. +- **Tool guardrails** run on every guarded function-tool invocation, including local MCP tools when their server configures guardrails, with input guardrails before execution and output guardrails after execution. 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. @@ -53,7 +53,20 @@ Output guardrails run in 3 steps: An output tripwire and an exception raised by the guardrail function have different session behavior. A tripwire rejects the candidate final output. When a tripwire fires, the runner asks the configured session to persist already-completed tool call and tool output items, together with any reasoning context required to replay those calls, while excluding the rejected candidate final output. The runner applies this tripwire rule to both streaming and non-streaming runs. When the guardrail function raises an exception instead of returning a tripwire result, the runner treats the verdict as unknown and asks the configured session to persist the completed final-turn items before surfacing the guardrail exception. If that session write also fails, the session write error takes precedence. Streaming runs use the same persistence ordering as non-streaming runs and raise the terminal exception from `stream_events()`. An immediate [`RunResultStreaming.cancel()`][agents.result.RunResultStreaming.cancel] call while the output guardrail is running cancels the in-flight guardrail and does not start a final-turn session write. -Terminal function-tool output needs additional handling because the tool has already run before the agent-level output guardrail checks the value. When [`Agent.tool_use_behavior`][agents.agent.Agent.tool_use_behavior] makes that tool result the final output and an output tripwire rejects it, the SDK retains a replay-valid function call/output pair only when it can rebuild the pair from validated fields. The retained `function_call_output` payload is replaced with the fixed text `"Output withheld by an output guardrail."`; the original tool-output payload is not retained in the session, `RunState`, streamed result state, or sandbox memory input. The SDK does retain validated function-call metadata required for replay, including the function arguments, so that metadata can contain data that also appeared in the rejected output. Current-response [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult] objects also replace `agent_output` with the fixed text and clear `output_info`. Current-response [`ToolOutputGuardrailResult`][agents.tool_guardrails.ToolOutputGuardrailResult] objects preserve the allow/reject behavior type but replace payload-bearing `output_info` and rejection messages with the same text. Earlier accepted turns and guardrail results remain unchanged. If the response contains reasoning or another shape that the SDK cannot sanitize safely, the SDK discards the complete current-response suffix instead of retaining the rejected output payload. A guardrail function that raises an exception has not returned a rejection verdict, so the completed terminal-tool turn follows the exception persistence behavior described above. +Terminal function-tool output needs additional handling because the tool has already run before the agent-level output guardrail checks the value. When [`Agent.tool_use_behavior`][agents.agent.Agent.tool_use_behavior] makes that tool result the final output and an output tripwire rejects it, the SDK retains a replay-valid function call/output pair only when it can rebuild the pair from validated fields. The retained `function_call_output` payload is replaced with the default text `"Output withheld by an output guardrail."`; the original tool-output payload is not retained in the session, `RunState`, streamed result state, or sandbox memory input. The SDK does retain validated function-call metadata required for replay, including the function arguments, so that metadata can contain data that also appeared in the rejected output. Current-response [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult] objects also replace `agent_output` with the resolved placeholder and clear `output_info`. Current-response [`ToolOutputGuardrailResult`][agents.tool_guardrails.ToolOutputGuardrailResult] objects preserve the allow/reject behavior type but replace payload-bearing `output_info` and rejection messages with the same placeholder. Earlier accepted turns and guardrail results remain unchanged. If the response contains reasoning or another shape that the SDK cannot sanitize safely, the SDK discards the complete current-response suffix instead of retaining the rejected output payload. A guardrail function that raises an exception has not returned a rejection verdict, so the completed terminal-tool turn follows the exception persistence behavior described above. + +Set [`RunConfig.output_guardrail_blocked_message`][agents.run.RunConfig.output_guardrail_blocked_message] to a non-empty string or a synchronous formatter when your application needs a different data-free placeholder. The formatter receives [`OutputGuardrailBlockedMessageArgs`][agents.run.OutputGuardrailBlockedMessageArgs] with the SDK default, the guardrail name, the agent, and the active run context. It never receives the rejected tool output or guardrail `output_info`. The returned text is persisted and replayed wherever the SDK retains the sanitized terminal-tool turn, so keep it free of sensitive data and do not copy secrets from the run context. If the formatter raises, returns `None`, returns an empty or non-string value, or produces an awaitable, the SDK uses the default placeholder. Async formatter functions are rejected when `RunConfig` is constructed. + +```python +from agents import OutputGuardrailBlockedMessageArgs, RunConfig + + +def blocked_message(args: OutputGuardrailBlockedMessageArgs[dict[str, str]]) -> str: + return f"Output blocked by policy: {args.guardrail_name}." + + +run_config = RunConfig(output_guardrail_blocked_message=blocked_message) +``` ## Tool guardrails @@ -62,7 +75,7 @@ Tool guardrails wrap **`FunctionTool` instances** and let you validate or block - 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. - If a function tool requires approval, input tool guardrails normally run after approval and immediately before execution. Set [`RunConfig.tool_execution`][agents.run.RunConfig.tool_execution] to [`ToolExecutionConfig(pre_approval_tool_input_guardrails=True)`][agents.run.ToolExecutionConfig] when you want those input checks to run before the pending approval interruption is emitted. Calls that pass this pre-approval check are still checked again after approval before the tool executes. -- Tool guardrails apply only to function tools created with [`function_tool`][agents.tool.function_tool]. Handoffs run through the SDK's handoff pipeline rather than the normal function-tool pipeline, so tool guardrails do not apply to the handoff call itself. Hosted tools (`WebSearchTool`, `FileSearchTool`, `HostedMCPTool`, `CodeInterpreterTool`, `ImageGenerationTool`) and built-in execution tools (`ComputerTool`, `ShellTool`, `ApplyPatchTool`, `LocalShellTool`) also do not use this guardrail pipeline, and [`Agent.as_tool()`][agents.agent.Agent.as_tool] does not currently expose tool-guardrail options directly. +- Tool guardrails use the `FunctionTool` execution pipeline. You can attach them directly to a custom tool created with `tool` or [`function_tool`][agents.tool.function_tool]. You can also set `tool_input_guardrails` and `tool_output_guardrails` on a local MCP server; the SDK attaches those lists to every tool exposed by that server. Handoffs run through the SDK's handoff pipeline rather than the function-tool pipeline, so tool guardrails do not apply to the handoff call itself. Hosted tools (`WebSearchTool`, `FileSearchTool`, `HostedMCPTool`, `CodeInterpreterTool`, `ImageGenerationTool`) and built-in execution tools (`ComputerTool`, `ShellTool`, `ApplyPatchTool`, `LocalShellTool`) do not use this guardrail pipeline, and [`Agent.as_tool()`][agents.agent.Agent.as_tool] does not currently expose tool-guardrail options directly. See [MCP server tool guardrails](mcp.md#tool-guardrails) for the local MCP configuration. See the code snippet below for details. diff --git a/docs/human_in_the_loop.md b/docs/human_in_the_loop.md index c153fe4b11..c3b071d0ee 100644 --- a/docs/human_in_the_loop.md +++ b/docs/human_in_the_loop.md @@ -12,7 +12,7 @@ This page focuses on the manual approval flow via `interruptions`. If your app c Set `needs_approval` to `True` to always require approval or provide an async function that decides per call. The callable receives the run context, parsed tool parameters, and the tool call ID. -Callable approval rules fail closed when the SDK cannot safely inspect the arguments. If the arguments are malformed JSON, are valid JSON but not an object (for example, `null` or a list), or contain non-standard constants such as `NaN`, `Infinity`, or `-Infinity`, the callable is not invoked and the call requires manual approval. This behavior is the same for Runner and Realtime tool calls. +Callable approval rules fail closed when the SDK cannot safely inspect the arguments. If the arguments are missing, empty, contain only whitespace, are malformed JSON, are valid JSON but not an object (for example, `null` or a list), or contain non-standard constants such as `NaN`, `Infinity`, or `-Infinity`, the callable is not invoked and the call requires manual approval. This behavior is the same for Runner and Realtime tool calls. ```python from agents import Agent diff --git a/docs/mcp.md b/docs/mcp.md index 3104f023eb..83a5502eb7 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -388,6 +388,7 @@ async with MCPServerManager(servers) as manager: Key behaviors: - `active_servers` includes only successfully connected servers when `drop_failed_servers=True` (the default). +- If the input iterable repeats the same server object, the manager owns that server once: `all_servers` and `active_servers` contain one entry, and connection and cleanup run once for that server. - Failures are tracked in `failed_servers` and `errors`. - Set `strict=True` to raise on the first connection failure. - Call `reconnect(failed_only=True)` to retry failed servers, or `reconnect(failed_only=False)` to restart all servers. @@ -452,6 +453,39 @@ async with MCPServerStdio( The filter context exposes the active `run_context`, the `agent` requesting the tools, and the `server_name`. +## Tool guardrails + +Local MCP server classes accept `tool_input_guardrails` and `tool_output_guardrails`. The SDK attaches these server-wide guardrails to every MCP tool that remains after filtering. Input guardrails can prevent the MCP server call and supply replacement content, while output guardrails inspect the converted MCP result before the SDK sends that result back to the model. These guardrails use the same function-tool execution pipeline, approval ordering, result tracking, and tripwire exceptions described in [Tool guardrails](guardrails.md#tool-guardrails). + +```python +import json + +from agents import ToolGuardrailFunctionOutput +from agents.decorators import tool_input_guardrail +from agents.mcp import MCPServerStdio + + +@tool_input_guardrail +def block_secret_arguments(data): + arguments = json.loads(data.context.tool_arguments or "{}") + if "secret" in arguments: + return ToolGuardrailFunctionOutput.reject_content( + "Remove secrets before calling this MCP tool." + ) + return ToolGuardrailFunctionOutput.allow() + + +filesystem_server = MCPServerStdio( + params={ + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "."], + }, + tool_input_guardrails=[block_secret_arguments], +) +``` + +This configuration applies only to tools exposed by local MCP server objects such as `MCPServerStdio`, `MCPServerSse`, and `MCPServerStreamableHttp`. It does not add client-side tool guardrails to [`HostedMCPTool`][agents.tool.HostedMCPTool], which the Responses API executes as a hosted tool. + ## Prompts MCP servers can also provide prompts that dynamically generate agent instructions. Servers that support prompts expose two @@ -486,6 +520,8 @@ Resources remain explicitly paginated. Pass the `nextCursor` from `list_resource Every agent run calls `list_tools()` on each MCP server. Remote servers can introduce noticeable latency, so all of the MCP server classes expose a `cache_tools_list` option. Set it to `True` only if you are confident that the tool definitions do not change frequently. To force a fresh list later, call `invalidate_tools_cache()` on the server instance. +When caching is enabled, each `list_tools()` result contains detached copies of the cached tool definitions, including nested input schemas. Dynamic tool-filter callbacks also inspect detached copies. Mutating a returned tool or a tool received by a filter therefore does not change the server's cached schema or later `list_tools()` results. + ## Tracing [Tracing](./tracing.md) automatically captures MCP activity, including: diff --git a/docs/models/index.md b/docs/models/index.md index 4c495e383d..4cdb7ae392 100644 --- a/docs/models/index.md +++ b/docs/models/index.md @@ -98,9 +98,9 @@ When using `context="all_turns"`, preserve the conversation through `previous_re #### 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. +If an agent includes [`ComputerTool`][agents.tool.ComputerTool], the effective model on the actual Responses request determines which computer-tool payload the SDK sends. When the agent does not set `model`, normal SDK model-selection precedence applies. The built-in SDK default, currently [`gpt-5.6-luna`](https://developers.openai.com/api/docs/models/gpt-5.6-luna), supports the GA built-in `computer` tool. If `OPENAI_DEFAULT_MODEL` or `RunConfig.model` overrides that default, select a model that supports computer use. Set `model` on the agent when you want to choose a different capability and cost profile for the computer-use workload; for example, `model="gpt-5.6"` uses the alias that OpenAI routes to GPT-5.6 Sol. Explicit `computer-use-preview` requests keep the older `computer_use_preview` payload. -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")`. +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 a supported GA model such as `model="gpt-5.6"` 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. @@ -644,6 +644,8 @@ If you use [`MultiProvider`][agents.MultiProvider], pass `openai_strict_feature_ The OpenAI Chat Completions API can return audio output, but [`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel] does not currently convert audio output into Agents SDK run items. If a non-streaming message or streaming delta contains audio output, the adapter raises `AgentsException("Audio is not currently supported")` instead of returning a partial or empty result. Use [Realtime agents](../realtime/guide.md) or [Voice agents](../voice/quickstart.md) for SDK-managed audio workflows. +If a streaming or non-streaming Chat Completions response ends with `finish_reason="length"` before producing assistant text, a tool call, or a refusal, the adapter raises [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError]. The SDK treats this empty result as token- or reasoning-budget exhaustion, not as a content-policy refusal, so model-refusal handlers do not run for it. + Some OpenAI-compatible Chat Completions providers stream tool-call deltas in chunks that are not reliable enough for incremental SDK processing. In that case, enable streamed tool-call buffering so the SDK emits tool calls only after the provider stream finishes: ```python @@ -689,6 +691,8 @@ Depending on the upstream provider path, Any-LLM may use the Responses API, Chat If you need Any-LLM, install `openai-agents[any-llm]`, then start from [`examples/model_providers/any_llm_auto.py`](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/any_llm_auto.py) or [`examples/model_providers/any_llm_provider.py`](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/any_llm_provider.py). You can use `any-llm/...` model names with [`MultiProvider`][agents.MultiProvider], instantiate `AnyLLMModel` directly, or use `AnyLLMProvider` at run scope. If you need to pin the model surface explicitly, pass `api="responses"` or `api="chat_completions"` when constructing `AnyLLMModel`. +On the Any-LLM Chat Completions path, [`ModelSettings.extra_body`][agents.model_settings.ModelSettings.extra_body] remains a nested `extra_body` argument. The Agents SDK does not merge that mapping into Any-LLM's top-level call arguments, so keep provider-specific request-body fields inside the `extra_body` mapping. + Any-LLM remains a third-party adapter layer, so provider dependencies and capability gaps are defined upstream by Any-LLM rather than by the SDK. Usage metrics are propagated automatically when the upstream provider returns them, but streamed Chat Completions backends may require `ModelSettings(include_usage=True)` before they emit usage chunks. Validate the exact provider backend you plan to deploy if you depend on structured outputs, tool calling, usage reporting, or Responses-specific behavior. ### LiteLLM diff --git a/docs/realtime/guide.md b/docs/realtime/guide.md index 99dacc53b1..02b98bebae 100644 --- a/docs/realtime/guide.md +++ b/docs/realtime/guide.md @@ -324,7 +324,7 @@ main_agent = RealtimeAgent( ### Guardrails -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. +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. A single delta schedules at most one check. If that delta crosses multiple `debounce_text_length` boundaries, the SDK advances the next boundary past all of them instead of scheduling catch-up checks after later small deltas. ```python from agents.guardrail import GuardrailFunctionOutput, OutputGuardrail diff --git a/docs/results.md b/docs/results.md index 5f9126b170..4345e1d0ed 100644 --- a/docs/results.md +++ b/docs/results.md @@ -59,6 +59,8 @@ 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. +When model output is converted into replayable input, `to_input_list()`, [`ModelResponse.to_input_items()`][agents.items.ModelResponse.to_input_items], and each [`RunItemBase.to_input_item()`][agents.items.RunItemBase.to_input_item] call remove provider output-only `created_by` metadata. This includes `created_by` on nested `shell_call_output` chunks. The conversion rebuilds the affected mappings and does not mutate the original raw item. + 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. 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`. @@ -135,6 +137,12 @@ if result.interruptions: result = await Runner.run(agent, state) ``` +#### Recover a failed resumed Session write + +A resumed run can complete approved tool work and then fail while writing the completed tool calls and outputs to a client-managed [`Session`][agents.memory.session.Session]. Keep the same [`RunState`][agents.run_state.RunState], or serialize and restore it, and retry `Runner.run(...)` or `Runner.run_streamed(...)` with the original Session backend and `session_id`. Before any later model call, the SDK reconciles the pending batch with the Session history. If the Session committed the complete batch but its acknowledgment failed, the SDK recognizes the exact history tail and does not append the batch again. If the write did not commit, the SDK retries the append. The SDK does not execute the completed tool work again. + +Recovery fails closed when the Session history is not an exact match. Use the original Session backend and `session_id`, and give the resumed run exclusive access to that history. If another writer changes the history tail, only part of the pending batch is present, or the history is otherwise ambiguous, the SDK raises [`UserError`][agents.exceptions.UserError] before another model call. Repair the original Session history before resuming; do not rerun the completed tool work. The pending batch survives `RunState` JSON and string round trips. After [`stream_events()`][agents.result.RunResultStreaming.stream_events] raises the Session write error, [`RunResultStreaming.to_state()`][agents.result.RunResultStreaming.to_state] also returns a detached state that retains the pending batch. + #### Add input before resuming Use [`RunState.add_input()`][agents.run_state.RunState.add_input] when new user input arrives after a run pauses or stops after a completed turn, but before the unfinished run reaches its next model call. A string becomes a user message, and multiple calls preserve insertion order. The staged input is part of serialized `RunState`, so it survives `to_json()` / `from_json()` and `to_string()` / `from_string()` round trips. diff --git a/docs/sandbox/clients.md b/docs/sandbox/clients.md index 6f52a8d7b7..60f2931ffc 100644 --- a/docs/sandbox/clients.md +++ b/docs/sandbox/clients.md @@ -35,6 +35,25 @@ Unix-local is the easiest way to start developing against a local filesystem. Mo `SandboxPathGrant.host_path` is Docker-only and maps a host path to a different POSIX path inside the container. Unix-local supports only same-path grants. See [Manifest path grants](guide.md#manifest) for details. +### Limit host environment inheritance for Unix-local sessions + +By default, `UnixLocalSandboxClient` starts every command environment from the complete host process environment. Set `inherit_host_environment=False` to pass only a conservative allowlist of host variables instead: + +```python +from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient + +client = UnixLocalSandboxClient( + inherit_host_environment=False, + host_environment_allowlist={"PATH", "LANG", "SSL_CERT_FILE"}, +) +``` + +When `inherit_host_environment=False` and `host_environment_allowlist` is omitted, the SDK allows `PATH`, `LANG`, `LC_ALL`, `LC_COLLATE`, `LC_CTYPE`, `LC_MESSAGES`, `LC_MONETARY`, `LC_NUMERIC`, `LC_TIME`, `TZ`, `TERM`, `TMPDIR`, `SSL_CERT_FILE`, `SSL_CERT_DIR`, `REQUESTS_CA_BUNDLE`, `NODE_EXTRA_CA_CERTS`, `UV_PYTHON`, `NO_COLOR`, `FORCE_COLOR`, and `CI`. Pass a custom collection to replace that default allowlist. A custom allowlist requires `inherit_host_environment=False`. + +Values from `Manifest.environment` are applied after host filtering and override inherited values. Unix-local commands always receive the workspace root as `HOME`. The inheritance policy belongs to the current client rather than serialized session state, so `create(...)` and `resume(...)` apply the policy of the client that performs that operation. + +This option filters inherited environment variables only. Unix-local commands still run as local host processes with local filesystem and network access. Use Docker or a hosted sandbox when the workload requires stronger isolation. + To switch from Unix-local to Docker, keep the agent definition the same and change only the run config: ```python @@ -67,6 +86,22 @@ options = DockerSandboxClientOptions( The only supported explicit network mode is `"none"`; omit `network_mode` to preserve Docker's default behavior. A network-disabled sandbox cannot expose ports, so combining `network_mode="none"` with a non-empty `exposed_ports` tuple fails during option validation. The setting is stored in sandbox session state and reapplied if the SDK must create a replacement container while resuming that state. +### Label Docker containers + +Set `labels` when an application needs to identify or manage the Docker containers created for sandbox sessions: + +```python +options = DockerSandboxClientOptions( + image="python:3.14-slim", + labels={ + "com.example.owner": "agents-sdk", + "com.example.environment": "development", + }, +) +``` + +The SDK passes these key-value pairs to Docker when it creates the container and stores them in [`DockerSandboxSessionState`][agents.sandbox.sandboxes.docker.DockerSandboxSessionState]. When a resumed session reconnects to an existing container, the SDK verifies that every persisted label still has the expected value and raises `ValueError` if the labels do not match. When the SDK creates a replacement container from the saved state, it reapplies the persisted labels. + ## Mounts and remote storage Mount entries describe what storage to expose; mount strategies describe how a sandbox backend attaches that storage. Import the built-in mount entries and generic strategies from `agents.sandbox.entries`. Hosted-provider strategies are available from `agents.extensions.sandbox` or the provider-specific extension package. diff --git a/docs/tools.md b/docs/tools.md index d506b58c5f..cf73b148a0 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -245,23 +245,25 @@ 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"}`. 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/): +When an [`Agent`][agents.agent.Agent] does not set `model`, normal SDK model-selection precedence applies. The built-in SDK default, currently [`gpt-5.6-luna`](https://developers.openai.com/api/docs/models/gpt-5.6-luna), supports computer use. If `OPENAI_DEFAULT_MODEL` or `RunConfig.model` overrides that default, select a model that supports computer use. Set `model` on the agent when you want to choose a different capability and cost profile for the computer-use workload. The example below uses the [`gpt-5.6`](https://developers.openai.com/api/docs/models/gpt-5.6) alias, which OpenAI routes to GPT-5.6 Sol; you can instead select another model that supports computer use, such as [GPT-5.6 Terra](https://developers.openai.com/api/docs/models/gpt-5.6-terra) or [GPT-5.6 Luna](https://developers.openai.com/api/docs/models/gpt-5.6-luna). + +For explicit requests to a model that supports the GA built-in computer tool, such as `gpt-5.6`, the SDK sends the 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` - Computer call shape: one `action` per `computer_call` -> batched `actions[]` on `computer_call` - Truncation: `ModelSettings(truncation="auto")` required on the preview path -> not required on the GA path -The SDK chooses that wire shape from the effective model on the actual Responses request. If you use a prompt template and the request omits `model` because the prompt owns it, the SDK keeps the preview-compatible computer payload unless you either keep `model="gpt-5.5"` explicit or force the GA selector with `ModelSettings(tool_choice="computer")` or `ModelSettings(tool_choice="computer_use")`. +The SDK chooses that wire shape from the effective model on the actual Responses request. If you use a prompt template and the request omits `model` because the prompt owns it, the SDK keeps the preview-compatible computer payload unless you either make a supported GA model such as `model="gpt-5.6"` explicit or force the GA selector with `ModelSettings(tool_choice="computer")` or `ModelSettings(tool_choice="computer_use")`. 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 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. +At runtime, both paths still use the same local harness. Preview responses emit `computer_call` items with a single `action`; GA responses 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. ```python -from agents import Agent, ApplyPatchTool, ShellTool +from agents import Agent, ApplyPatchTool, ComputerTool, ShellTool from agents.computer import AsyncComputer from agents.editor import ApplyPatchResult, ApplyPatchOperation, ApplyPatchEditor @@ -295,8 +297,10 @@ agent = Agent( tools=[ ShellTool(executor=run_shell), ApplyPatchTool(editor=NoopEditor()), - # ComputerTool expects a Computer/AsyncComputer implementation; omitted here for brevity. + ComputerTool(computer=NoopComputer()), ], + # Optional: omit this argument to use the configured or built-in default model. + model="gpt-5.6", ) ``` diff --git a/docs/tracing.md b/docs/tracing.md index 9bdbb25f40..39241e5b07 100644 --- a/docs/tracing.md +++ b/docs/tracing.md @@ -101,6 +101,8 @@ async def run(prompt: str, background_tasks: BackgroundTasks): [`flush_traces()`][agents.tracing.flush_traces] blocks until currently buffered traces and spans are exported, so call it after `trace()` closes to avoid flushing a partially built trace. You can skip this call when the default export latency is acceptable. +Disabling tracing prevents the default provider from creating new traces and spans, but it does not discard data that its processors already buffered. [`flush_traces()`][agents.tracing.flush_traces] continues to flush that buffered data after tracing has been disabled through `set_tracing_disabled(True)` or `OPENAI_AGENTS_DISABLE_TRACING=1`. + ## Higher level traces Sometimes, you might want multiple calls to `run()` to be part of a single trace. You can do this by wrapping the entire code in a `trace()`. @@ -145,6 +147,8 @@ Similarly, Audio spans include base64-encoded PCM data for input and output audi By default, `trace_include_sensitive_data` is `True`. You can set the default without code by exporting the `OPENAI_AGENTS_TRACE_INCLUDE_SENSITIVE_DATA` environment variable to `true/1` or `false/0` before running your app. +When `trace_include_sensitive_data` is `False`, Responses model spans omit the request input and response output. For calls to an official OpenAI endpoint, the spans still include the Responses API `response_id` as correlation metadata. The SDK omits that identifier from redacted spans for custom endpoints. + ## Custom tracing processors The high level architecture for tracing is: diff --git a/docs/usage.md b/docs/usage.md index f752dc0e49..a0700203dc 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -52,6 +52,8 @@ for i, request in enumerate(result.context_wrapper.usage.request_usage_entries): print(f"Request {i + 1}: {request.input_tokens} in, {request.output_tokens} out") ``` +When the SDK aggregates one [`Usage`][agents.usage.Usage] object into another, it copies the per-request entries and their nested input/output token details. Later mutation of the source usage object cannot change the aggregate's `request_usage_entries`, and mutation of the aggregate cannot change the source entries. + ## Preserving provider usage payloads The Agents SDK normalizes provider usage into [`Usage`][agents.usage.Usage] fields that provide consistent totals across model providers. Set [`ModelSettings.preserve_raw_usage`][agents.model_settings.ModelSettings.preserve_raw_usage] to `True` when an application must retain provider-specific usage fields or distinguish an omitted field from a provider-reported zero: diff --git a/docs/visualization.md b/docs/visualization.md index cf173a63ec..c2b68e2916 100644 --- a/docs/visualization.md +++ b/docs/visualization.md @@ -70,6 +70,7 @@ This generates a graph that visually represents the structure of the **triage ag `draw_graph()` recursively expands target agents supplied directly in `handoffs` or registered through `handoff(agent)`. In both forms, the graph includes each target's tools, MCP servers, and downstream handoffs. A custom `Handoff` without an available target `Agent` is rendered as a named destination only, so the graph cannot expand resources behind that destination. +Graph nodes are identified by the underlying agent, tool, MCP server, or custom handoff object rather than by the displayed name. Distinct objects that share the same name remain separate nodes with the same visible label, and each edge connects to the corresponding object. ## Understanding the visualization diff --git a/docs/voice/pipeline.md b/docs/voice/pipeline.md index 33658e2afe..3bb50a9d14 100644 --- a/docs/voice/pipeline.md +++ b/docs/voice/pipeline.md @@ -39,6 +39,67 @@ When you create a pipeline, you can set a few things: - Tracing, including whether to disable tracing, whether audio files are uploaded, the workflow name, trace IDs etc. - Settings on the TTS and STT models, such as the prompt, language, and data types used. +### Pass application context to a single-agent workflow + +Pass `context` to [`SingleAgentVoiceWorkflow`][agents.voice.workflow.SingleAgentVoiceWorkflow] when the voice Agent, its tools, or its lifecycle hooks need application state or dependencies: + +```python +from dataclasses import dataclass + +from agents import Agent +from agents.voice import SingleAgentVoiceWorkflow, VoicePipeline + + +@dataclass +class VoiceContext: + user_id: str + + +agent = Agent[VoiceContext](name="Voice assistant") +workflow = SingleAgentVoiceWorkflow( + agent, + context=VoiceContext(user_id="user-123"), +) +pipeline = VoicePipeline(workflow=workflow) +``` + +The workflow forwards the same context object to every agent run that it starts, including later transcription turns. Tools and lifecycle hooks receive it through [`RunContextWrapper.context`][agents.run_context.RunContextWrapper.context]. The context remains local to your application and is not sent to the model. See [Context management](../context.md) for typing and lifecycle guidance. + +### Configure OpenAI speech models + +Pass [`STTModelSettings`][agents.voice.model.STTModelSettings] and [`TTSModelSettings`][agents.voice.model.TTSModelSettings] through `VoicePipelineConfig` to configure the default OpenAI speech models: + +```python +from agents.voice import STTModelSettings, TTSModelSettings, VoicePipeline, VoicePipelineConfig + +config = VoicePipelineConfig( + stt_settings=STTModelSettings( + language="en", + prompt="A customer support call about product AC-42.", + ), + tts_settings=TTSModelSettings( + voice="marin", + ), +) +pipeline = VoicePipeline(workflow=workflow, config=config) +``` + +For complete audio input, `STTModelSettings.language` and `prompt` are passed to the transcription request. For [`StreamedAudioInput`][agents.voice.input.StreamedAudioInput], the OpenAI transcription session also receives both settings when the WebSocket session is configured. `gpt-transcribe` and `gpt-live-transcribe` receive the single SDK `language` value as a one-element `languages` list; other transcription models receive the singular `language` field. Use a language code accepted by the OpenAI transcription API, and use `prompt` to describe the recording or its setting rather than restating the transcription task. See the OpenAI [Realtime transcription context guide](https://developers.openai.com/api/docs/guides/realtime-transcription#add-transcription-context). + +The supported built-in `TTSModelSettings.voice` values are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. Voice availability depends on the selected text-to-speech model; see the OpenAI [voice options](https://developers.openai.com/api/docs/guides/text-to-speech#voice-options) for current model-specific availability. Organizations with access to OpenAI custom voices can instead pass a custom voice ID: + +```python +config = VoicePipelineConfig( + tts_settings=TTSModelSettings( + voice={"id": "voice_123abc"}, + ), +) +``` + +Custom voices are limited to eligible customers and must be created through the OpenAI API before use. See the OpenAI [custom voices guide](https://developers.openai.com/api/docs/guides/text-to-speech#custom-voices) for access, consent, and creation requirements. + +[`OpenAIVoiceModelProvider`][agents.voice.models.openai_model_provider.OpenAIVoiceModelProvider] uses its configured `AsyncOpenAI` client for non-streamed transcription requests, TTS requests, and streamed STT connections. The streamed STT WebSocket connection derives its endpoint, authentication and default headers, and default query parameters from that client. See [API keys and clients](../config.md#api-keys-and-clients) for provider ownership and precedence rules. + ## Running a pipeline You can run a pipeline via the [`run()`][agents.voice.pipeline.VoicePipeline.run] method, which lets you pass in audio input in two forms: