from typing import Any, Optional from smolagents.tools import Tool import duckduckgo_search import random import time class DuckDuckGoSearchTool(Tool): name = "web_search" description = "Performs a duckduckgo web search based on your query (think a Google search) then returns the top search results." inputs = {'query': {'type': 'string', 'description': 'The search query to perform.'}} output_type = "string" def __init__( self, max_results=10, backends=None, retries=2, min_delay=0.2, max_delay=0.8, **kwargs, ): super().__init__() self.max_results = max_results self.backends = backends or ["auto", "lite", "html", "api"] self.retries = retries self.min_delay = min_delay self.max_delay = max_delay try: from duckduckgo_search import DDGS except ImportError as e: raise ImportError( "You must install package `duckduckgo_search` to run this tool: for instance run `pip install duckduckgo-search`." ) from e self.ddgs = DDGS(**kwargs) def _format_results(self, results) -> str: postprocessed_results = [] for result in results: title = result.get("title") or "Untitled" href = result.get("href") or result.get("url") or "" body = result.get("body") or result.get("snippet") or "" postprocessed_results.append(f"[{title}]({href})\n{body}".strip()) return "## Search Results\n\n" + "\n\n".join(postprocessed_results) def forward(self, query: str) -> str: last_error = None for backend in self.backends: for attempt in range(self.retries + 1): try: results = list( self.ddgs.text(query, max_results=self.max_results, backend=backend) ) except Exception as e: last_error = e # Jittered backoff to reduce rate-limit issues. sleep_for = min( self.max_delay, self.min_delay * (2 ** attempt) + random.random() * 0.1, ) time.sleep(sleep_for) continue if results: return self._format_results(results) # Empty results can happen; try next backend. break if last_error is not None: return ( "Web search failed. This can happen if outbound network access is blocked or the " f"DuckDuckGo API is rate-limiting requests. Error: {last_error}" ) return ( "No results found. This may indicate network restrictions or a temporary " "DuckDuckGo issue. Try a shorter query or retry later." )