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
2 changes: 1 addition & 1 deletion docs/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ OpenAI offers a few built-in tools when using the [`OpenAIResponsesModel`][agent
Advanced hosted search options:

- `FileSearchTool` supports `filters`, `ranking_options`, and `include_search_results` in addition to `vector_store_ids` and `max_num_results`. Set `max_num_results` to an integer from 1 through 50; `None` or zero uses the provider default.
- `WebSearchTool` supports `filters`, `user_location`, and `search_context_size`.
- `WebSearchTool` supports `filters`, `user_location`, `search_context_size`, `external_web_access`, and `search_content_types` with `image_settings` for image results.
Comment thread
seratch marked this conversation as resolved.

```python
from agents import Agent, FileSearchTool, Runner, WebSearchTool
Expand Down
11 changes: 10 additions & 1 deletion src/agents/models/openai_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2183,9 +2183,18 @@ def _convert_tool(
}
if tool.external_web_access is not None:
web_search_tool["external_web_access"] = tool.external_web_access
if tool.search_content_types is not None:
web_search_tool["search_content_types"] = list(tool.search_content_types)
if tool.image_settings is not None:
web_search_tool["image_settings"] = dict(tool.image_settings)
web_search_include: ResponseIncludable | None = (
"web_search_call.results"
if tool.search_content_types is not None and "image" in tool.search_content_types
else None
)
return (
_require_responses_tool_param(web_search_tool),
None,
web_search_include,
)
elif isinstance(tool, FileSearchTool):
file_search_tool_param: FileSearchToolParam = {
Expand Down
22 changes: 22 additions & 0 deletions src/agents/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,16 @@ def name(self):
return "file_search"


class WebSearchToolImageSettings(TypedDict, total=False):
Comment thread
seratch marked this conversation as resolved.
"""Image result settings for `WebSearchTool` when `search_content_types` includes `"image"`."""

max_results: int
"""The number of image results to return."""

caption: bool
"""Whether to include a short caption with each image when one is available."""


@dataclass
class WebSearchTool:
"""A hosted tool that lets the LLM search the web. Currently only supported with OpenAI models,
Expand All @@ -817,6 +827,16 @@ class WebSearchTool:
indexed-only behavior where supported.
"""

search_content_types: list[Literal["text", "image"]] | None = None
"""The kinds of results the search may return.

When omitted, the API default (text only) is used. Include `"image"` to
receive image results; pair it with `image_settings`.
"""

image_settings: WebSearchToolImageSettings | None = None
"""Settings for image results when `search_content_types` includes `"image"`."""

if TYPE_CHECKING:

def __init__(
Expand All @@ -825,6 +845,8 @@ def __init__(
filters: WebSearchToolFilters | dict[str, Any] | None = None,
search_context_size: Literal["low", "medium", "high"] = "medium",
external_web_access: bool | None = None,
search_content_types: list[Literal["text", "image"]] | None = None,
image_settings: WebSearchToolImageSettings | None = None,
) -> None: ...

def __post_init__(self) -> None:
Expand Down
33 changes: 33 additions & 0 deletions tests/models/test_openai_responses_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ def test_convert_tools_basic_types_and_includes():
assert web_params.get("user_location") == web_tool.user_location
assert web_params.get("search_context_size") == web_tool.search_context_size
assert "external_web_access" not in web_params
assert "search_content_types" not in web_params
assert "image_settings" not in web_params
# Verify computer tool uses the GA built-in tool payload.
comp_params = next(ct for ct in converted.tools if ct["type"] == "computer")
assert comp_params == {"type": "computer"}
Expand Down Expand Up @@ -493,6 +495,37 @@ def test_convert_file_search_tool_rejects_unsupported_result_limits(
Converter.convert_tools([tool], handoffs=[])


def test_convert_tools_includes_web_search_content_types_and_image_settings() -> None:
web_tool = WebSearchTool(
search_content_types=["text", "image"],
image_settings={"max_results": 3, "caption": True},
)

converted = Converter.convert_tools([web_tool], handoffs=[], model="gpt-5.6")

# Image results arrive through the web_search_call.results include.
assert converted.includes == ["web_search_call.results"]
assert converted.tools == [
{
"type": "web_search",
"filters": None,
"user_location": None,
"search_context_size": "medium",
"search_content_types": ["text", "image"],
"image_settings": {"max_results": 3, "caption": True},
}
]


def test_convert_tools_text_only_content_types_adds_no_include() -> None:
web_tool = WebSearchTool(search_content_types=["text"])

converted = Converter.convert_tools([web_tool], handoffs=[], model="gpt-5.6")

assert converted.includes == []
assert converted.tools[0].get("search_content_types") == ["text"]


def test_convert_tools_includes_explicit_false_external_web_access() -> None:
web_tool = WebSearchTool(external_web_access=False)

Expand Down
Loading