Spaces:
Sleeping
Sleeping
| 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." | |
| ) | |