Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ The most common properties of an agent are:
| `reset_tool_choice` | no | Reset `tool_choice` after a tool call (default: `True`) to avoid tool-use loops. See [Forcing tool use](#forcing-tool-use). |

```python
from agents import Agent, function_tool
from agents import Agent
from agents.decorators import tool

@function_tool
@tool
def get_weather(city: str) -> str:
"""returns weather info for the specified city."""
return f"The weather in {city} is sunny"
Expand Down Expand Up @@ -330,9 +331,10 @@ Supplying a list of tools doesn't always mean the LLM will use a tool. You can f
When you are using OpenAI Responses tool search, named tool choices are more limited: you cannot target bare namespace names or deferred-only tools with `tool_choice`, and `tool_choice="tool_search"` does not target [`ToolSearchTool`][agents.tool.ToolSearchTool]. In those cases, prefer `auto` or `required`. See [Hosted tool search](tools.md#hosted-tool-search) for the Responses-specific constraints.

```python
from agents import Agent, function_tool, ModelSettings
from agents import Agent, ModelSettings
from agents.decorators import tool

@function_tool
@tool
def get_weather(city: str) -> str:
"""Returns weather info for the specified city."""
return f"The weather in {city} is sunny"
Expand All @@ -353,9 +355,10 @@ The `tool_use_behavior` parameter in the `Agent` configuration controls how tool
- `"stop_on_first_tool"`: The output of the first tool call is used as the final response, without further LLM processing.

```python
from agents import Agent, function_tool
from agents import Agent
from agents.decorators import tool

@function_tool
@tool
def get_weather(city: str) -> str:
"""Returns weather info for the specified city."""
return f"The weather in {city} is sunny"
Expand All @@ -371,15 +374,16 @@ agent = Agent(
- `StopAtTools(stop_at_tool_names=[...])`: Stops if any specified tool is called, using its output as the final response.

```python
from agents import Agent, function_tool
from agents import Agent
from agents.agent import StopAtTools
from agents.decorators import tool

@function_tool
@tool
def get_weather(city: str) -> str:
"""Returns weather info for the specified city."""
return f"The weather in {city} is sunny"

@function_tool
@tool
def sum_numbers(a: int, b: int) -> int:
"""Adds two numbers."""
return a + b
Expand All @@ -395,11 +399,12 @@ agent = Agent(
- `ToolsToFinalOutputFunction`: A custom function that processes tool results and decides whether to stop or continue with the LLM.

```python
from agents import Agent, function_tool, FunctionToolResult, RunContextWrapper
from agents import Agent, FunctionToolResult, RunContextWrapper
from agents.agent import ToolsToFinalOutputResult
from agents.decorators import tool
from typing import List, Any

@function_tool
@tool
def get_weather(city: str) -> str:
"""Returns weather info for the specified city."""
return f"The weather in {city} is sunny"
Expand Down
10 changes: 6 additions & 4 deletions docs/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,15 @@ Conversation state is a separate concern. Use `result.to_input_list()`, `session
import asyncio
from dataclasses import dataclass

from agents import Agent, RunContextWrapper, Runner, function_tool
from agents import Agent, RunContextWrapper, Runner
from agents.decorators import tool

@dataclass
class UserInfo: # (1)!
name: str
uid: int

@function_tool
@tool
async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)!
"""Fetch the age of the user. Call this function to get user's age information."""
return f"The user {wrapper.context.name} is 47 years old"
Expand Down Expand Up @@ -97,7 +98,8 @@ For this, you can use the [`ToolContext`][agents.tool_context.ToolContext] class
```python
from typing import Annotated
from pydantic import BaseModel, Field
from agents import Agent, function_tool
from agents import Agent
from agents.decorators import tool
from agents.tool_context import ToolContext

class WeatherContext(BaseModel):
Expand All @@ -108,7 +110,7 @@ class Weather(BaseModel):
temperature_range: str = Field(description="The temperature range in Celsius")
conditions: str = Field(description="The weather conditions")

@function_tool
@tool
def get_weather(ctx: ToolContext[WeatherContext], city: Annotated[str, "The city to get the weather for"]) -> Weather:
print(f"[debug] Tool context: (name: {ctx.tool_name}, call_id: {ctx.tool_call_id}, args: {ctx.tool_arguments})")
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")
Expand Down
1 change: 1 addition & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ Check out a variety of sample implementations of the SDK in the examples section
- 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`)
- 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`)
Expand Down
10 changes: 4 additions & 6 deletions docs/guardrails.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ from agents import (
RunContextWrapper,
Runner,
TResponseInputItem,
input_guardrail,
)
from agents.decorators import input_guardrail

class MathHomeworkOutput(BaseModel):
is_math_homework: bool
Expand Down Expand Up @@ -136,8 +136,8 @@ from agents import (
OutputGuardrailTripwireTriggered,
RunContextWrapper,
Runner,
output_guardrail,
)
from agents.decorators import output_guardrail
class MessageOutput(BaseModel): # (1)!
response: str

Expand Down Expand Up @@ -192,10 +192,8 @@ from agents import (
Agent,
Runner,
ToolGuardrailFunctionOutput,
function_tool,
tool_input_guardrail,
tool_output_guardrail,
)
from agents.decorators import tool, tool_input_guardrail, tool_output_guardrail

@tool_input_guardrail
def block_secrets(data):
Expand All @@ -215,7 +213,7 @@ def redact_output(data):
return ToolGuardrailFunctionOutput.allow()


@function_tool(
@tool(
tool_input_guardrails=[block_secrets],
tool_output_guardrails=[redact_output],
)
Expand Down
2 changes: 1 addition & 1 deletion docs/handoffs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 collapses the prior transcript into a single assistant summary message and wraps it in a `<CONVERSATION HISTORY>` block that keeps appending new turns when multiple handoffs happen during the same run. You can provide your own mapping function via [`RunConfig.handoff_history_mapper`][agents.run.RunConfig.handoff_history_mapper] to replace the generated message without writing a full `input_filter`. 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 the generated summary, 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 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.

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.

Expand Down
14 changes: 9 additions & 5 deletions docs/human_in_the_loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ 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.
Comment thread
seratch marked this conversation as resolved.

```python
from agents import Agent, function_tool
from agents import Agent
from agents.decorators import tool


@function_tool(needs_approval=True)
@tool(needs_approval=True)
async def cancel_order(order_id: int) -> str:
return f"Cancelled order {order_id}"

Expand All @@ -25,7 +28,7 @@ async def requires_review(_ctx, params, _call_id) -> bool:
return "refund" in params.get("subject", "").lower()


@function_tool(needs_approval=requires_review)
@tool(needs_approval=requires_review)
async def send_email(subject: str, body: str) -> str:
return f"Sent '{subject}'"

Expand Down Expand Up @@ -106,14 +109,15 @@ import asyncio
import json
from pathlib import Path

from agents import Agent, Runner, RunState, function_tool
from agents import Agent, Runner, RunState
from agents.decorators import tool


async def needs_oakland_approval(_ctx, params, _call_id) -> bool:
return "Oakland" in params.get("city", "")


@function_tool(needs_approval=needs_oakland_approval)
@tool(needs_approval=needs_oakland_approval)
async def get_temperature(city: str) -> str:
return f"The temperature in {city} is 20° Celsius"

Expand Down
Loading