Skip to content
Merged
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
48 changes: 48 additions & 0 deletions tests/tools/test_web_tools_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,54 @@ def test_singleton_returns_same_instance(self):
assert client1 is client2


class TestWebSearchSchema:
"""Test suite for web_search tool schema and handler wiring."""

def test_schema_exposes_optional_limit(self):
import tools.web_tools

limit_schema = tools.web_tools.WEB_SEARCH_SCHEMA["parameters"]["properties"]["limit"]

assert limit_schema["type"] == "integer"
assert limit_schema["minimum"] == 1
assert limit_schema["maximum"] == 100
assert limit_schema["default"] == 5
assert "limit" not in tools.web_tools.WEB_SEARCH_SCHEMA["parameters"]["required"]

def test_registered_handler_passes_limit(self):
import tools.web_tools

entry = tools.web_tools.registry.get_entry("web_search")
with patch("tools.web_tools.web_search_tool", return_value='{"success": true}') as mock_search:
result = entry.handler({"query": "site:example.com docs", "limit": 12})

assert result == '{"success": true}'
mock_search.assert_called_once_with("site:example.com docs", limit=12)

def test_registered_handler_defaults_limit_to_five(self):
import tools.web_tools

entry = tools.web_tools.registry.get_entry("web_search")
with patch("tools.web_tools.web_search_tool", return_value='{"success": true}') as mock_search:
result = entry.handler({"query": "docs"})

assert result == '{"success": true}'
mock_search.assert_called_once_with("docs", limit=5)

def test_web_search_clamps_limit_before_backend_call(self):
import tools.web_tools

with patch("tools.web_tools._get_backend", return_value="parallel"), \
patch("tools.web_tools._parallel_search", return_value={"success": True, "data": {"web": []}}) as mock_search, \
patch("tools.interrupt.is_interrupted", return_value=False), \
patch.object(tools.web_tools._debug, "log_call"), \
patch.object(tools.web_tools._debug, "save"):
result = json.loads(tools.web_tools.web_search_tool("docs", limit=500))

assert result == {"success": True, "data": {"web": []}}
mock_search.assert_called_once_with("docs", 100)


class TestWebSearchErrorHandling:
"""Test suite for web_search_tool() error responses."""

Expand Down
19 changes: 16 additions & 3 deletions tools/web_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,12 @@ def web_search_tool(query: str, limit: int = 5) -> str:
Raises:
Exception: If search fails or API key is not set
"""
try:
limit = int(limit)
except (TypeError, ValueError):
limit = 5
limit = min(max(limit, 1), 100)

debug_call_data = {
"parameters": {
"query": query,
Expand Down Expand Up @@ -2047,13 +2053,20 @@ def check_auxiliary_model() -> bool:

WEB_SEARCH_SCHEMA = {
"name": "web_search",
"description": "Search the web for information on any topic. Returns up to 5 relevant results with titles, URLs, and descriptions.",
"description": "Search the web for information. Returns up to 5 results by default with titles, URLs, and descriptions. The query is passed through to the configured backend, so operators such as site:domain, filetype:pdf, intitle:word, -term, and \"exact phrase\" may work when the backend supports them.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query to look up on the web"
"description": "The search query to look up on the web. You may include backend-supported operators such as site:example.com, filetype:pdf, intitle:word, -term, or \"exact phrase\"."
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return. Defaults to 5.",
"minimum": 1,
"maximum": 100,
"default": 5
}
},
"required": ["query"]
Expand Down Expand Up @@ -2081,7 +2094,7 @@ def check_auxiliary_model() -> bool:
name="web_search",
toolset="web",
schema=WEB_SEARCH_SCHEMA,
handler=lambda args, **kw: web_search_tool(args.get("query", ""), limit=5),
handler=lambda args, **kw: web_search_tool(args.get("query", ""), limit=args.get("limit", 5)),
check_fn=check_web_api_key,
requires_env=_web_requires_env(),
emoji="🔍",
Expand Down
2 changes: 1 addition & 1 deletion website/docs/reference/tools-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Scoped to the Feishu document-comment handler. Drives comment read/write operati

| Tool | Description | Requires environment |
|------|-------------|----------------------|
| `web_search` | Search the web for information on any topic. Returns up to 5 relevant results with titles, URLs, and descriptions. | EXA_API_KEY or PARALLEL_API_KEY or FIRECRAWL_API_KEY or TAVILY_API_KEY |
| `web_search` | Search the web for information. Returns up to 5 results by default with titles, URLs, and descriptions. Accepts an optional `limit` (1-100, default 5). The query is passed through to the configured backend, so operators such as `site:domain`, `filetype:pdf`, `intitle:word`, `-term`, and `"exact phrase"` may work when the backend supports them. | EXA_API_KEY or PARALLEL_API_KEY or FIRECRAWL_API_KEY or TAVILY_API_KEY |
| `web_extract` | Extract content from web page URLs. Returns page content in markdown format. Also works with PDF URLs — pass the PDF link directly and it converts to markdown text. Pages under 5000 chars return full markdown; larger pages are LLM-summarized. | EXA_API_KEY or PARALLEL_API_KEY or FIRECRAWL_API_KEY or TAVILY_API_KEY |

## `tts` toolset
Expand Down
Loading