""" Web Search Tool Uses DuckDuckGo for free web search (no API key needed). """ from typing import Dict, Any, Optional from duckduckgo_search import DDGS from loguru import logger from tools.base import BaseTool, ToolResult class WebSearchTool(BaseTool): """Web search using DuckDuckGo.""" @property def name(self) -> str: return "web_search" @property def description(self) -> str: return "Search the web for current information. Use this for recent events, news, or topics not in your training data." @property def parameters(self) -> Dict[str, Any]: return { "type": "object", "properties": { "query": { "type": "string", "description": "The search query" }, "max_results": { "type": "integer", "description": "Maximum number of results to return (default: 5)", "default": 5 } }, "required": ["query"] } async def execute(self, query: str, max_results: int = 5, **kwargs) -> ToolResult: """Execute web search.""" self._log_execution({"query": query, "max_results": max_results}) try: with DDGS() as ddgs: results = list(ddgs.text(query, max_results=max_results)) if not results: return ToolResult( success=True, data="No search results found." ) # Format results formatted_results = [] for i, result in enumerate(results, 1): formatted_results.append( f"{i}. {result.get('title', 'No title')}\n" f" {result.get('body', 'No description')}\n" f" Source: {result.get('href', 'No URL')}" ) output = f"Web search results for '{query}':\n\n" + "\n\n".join(formatted_results) self._log_success(output[:100]) return ToolResult(success=True, data=output) except Exception as e: self._log_error(str(e)) return ToolResult(success=False, error=str(e))