|
|
"""Web search tool using DuckDuckGo.""" |
|
|
|
|
|
import asyncio |
|
|
|
|
|
import structlog |
|
|
from duckduckgo_search import DDGS |
|
|
|
|
|
from src.utils.models import Citation, Evidence, SearchResult |
|
|
|
|
|
logger = structlog.get_logger() |
|
|
|
|
|
|
|
|
class WebSearchTool: |
|
|
"""Tool for searching the web using DuckDuckGo.""" |
|
|
|
|
|
def __init__(self) -> None: |
|
|
self._ddgs = DDGS() |
|
|
|
|
|
async def search(self, query: str, max_results: int = 10) -> SearchResult: |
|
|
"""Execute a web search.""" |
|
|
try: |
|
|
loop = asyncio.get_running_loop() |
|
|
|
|
|
def _do_search() -> list[dict[str, str]]: |
|
|
|
|
|
return list(self._ddgs.text(query, max_results=max_results)) |
|
|
|
|
|
raw_results = await loop.run_in_executor(None, _do_search) |
|
|
|
|
|
evidence = [] |
|
|
for r in raw_results: |
|
|
ev = Evidence( |
|
|
content=r.get("body", ""), |
|
|
citation=Citation( |
|
|
title=r.get("title", "No Title"), |
|
|
url=r.get("href", ""), |
|
|
source="web", |
|
|
date="Unknown", |
|
|
authors=[], |
|
|
), |
|
|
relevance=0.0, |
|
|
) |
|
|
evidence.append(ev) |
|
|
|
|
|
return SearchResult( |
|
|
query=query, evidence=evidence, sources_searched=["web"], total_found=len(evidence) |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error("Web search failed", error=str(e)) |
|
|
return SearchResult( |
|
|
query=query, evidence=[], sources_searched=["web"], total_found=0, errors=[str(e)] |
|
|
) |
|
|
|