from __future__ import annotations from typing import Any, Optional from smolagents.tools import Tool class WebSearchTool(Tool): name = "web_search" description = "Search the web with DuckDuckGo and return a short list of results." inputs = { "query": {"type": "string", "description": "Search query."}, "max_results": { "type": "integer", "description": "Maximum number of results to return (1-10). Defaults to 5.", "nullable": True, }, } output_type = "any" def forward(self, query: str, max_results: Optional[int] = 5) -> Any: if not isinstance(query, str) or not query.strip(): raise ValueError("`query` must be a non-empty string.") limit = 5 if max_results is None else int(max_results) limit = max(1, min(10, limit)) try: from ddgs import DDGS # type: ignore except Exception as e: # pragma: no cover raise ModuleNotFoundError("Missing dependency: `ddgs` (pip install ddgs).") from e results: list[dict[str, str]] = [] with DDGS() as ddgs: for r in ddgs.text(query, max_results=limit): title = (r.get("title") or "").strip() url = (r.get("href") or r.get("url") or "").strip() snippet = (r.get("body") or r.get("snippet") or "").strip() if not (title or url or snippet): continue results.append({"title": title, "url": url, "snippet": snippet}) return results