File size: 1,575 Bytes
20f10c1
 
fd18d93
20f10c1
8fe992b
 
20f10c1
 
8fe992b
20f10c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8fe992b
 
20f10c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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