From 1bf3338896b4a4a4fd32da705ae517d90f22dc55 Mon Sep 17 00:00:00 2001 From: Yuning Wang Date: Wed, 20 May 2026 17:04:02 +0000 Subject: [PATCH] feat: add image search content types to WebSearchTool --- src/agents/models/openai_responses.py | 9 ++++- src/agents/tool.py | 5 ++- .../models/test_openai_responses_converter.py | 35 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/agents/models/openai_responses.py b/src/agents/models/openai_responses.py index 3af75481bf..9e0174b93c 100644 --- a/src/agents/models/openai_responses.py +++ b/src/agents/models/openai_responses.py @@ -2021,9 +2021,16 @@ 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) + 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 = { diff --git a/src/agents/tool.py b/src/agents/tool.py index 6dcfc5a60f..ab07e31915 100644 --- a/src/agents/tool.py +++ b/src/agents/tool.py @@ -8,7 +8,7 @@ import json import math import weakref -from collections.abc import Awaitable, Callable, Mapping +from collections.abc import Awaitable, Callable, Mapping, Sequence from dataclasses import dataclass, field from enum import Enum from types import UnionType @@ -600,6 +600,9 @@ class WebSearchTool: indexed-only behavior where supported. """ + search_content_types: Sequence[Literal["text", "image"]] | None = None + """The content types to include in the search results. Omitting this uses the API default.""" + @property def name(self): return "web_search" diff --git a/tests/models/test_openai_responses_converter.py b/tests/models/test_openai_responses_converter.py index e1c8069ec9..d793025c29 100644 --- a/tests/models/test_openai_responses_converter.py +++ b/tests/models/test_openai_responses_converter.py @@ -438,6 +438,7 @@ 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 # 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"} @@ -468,6 +469,40 @@ def test_convert_tools_includes_explicit_false_external_web_access() -> None: ] +def test_convert_tools_forwards_web_search_content_types() -> None: + web_tool = WebSearchTool(search_content_types=["text", "image"]) + + converted = Converter.convert_tools([web_tool], handoffs=[], model="gpt-5.4") + + 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"], + } + ] + + +def test_convert_tools_includes_results_for_image_only_web_search() -> None: + web_tool = WebSearchTool(search_content_types=["image"]) + + converted = Converter.convert_tools([web_tool], handoffs=[], model="gpt-5.4") + + 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": ["image"], + } + ] + + def test_convert_tools_uses_preview_computer_payload_for_preview_model() -> None: comp_tool = ComputerTool(computer=DummyComputer())