|
""" |
|
Web Search Tool using DuckDuckGo via smolagents |
|
""" |
|
from .base_tool import BaseTool |
|
from typing import Optional |
|
from smolagents import CodeAgent, DuckDuckGoSearchTool, FinalAnswerTool, InferenceClientModel, VisitWebpageTool |
|
|
|
|
|
class WebSearchTool(BaseTool): |
|
"""Web search using DuckDuckGo via smolagents""" |
|
|
|
def __init__(self): |
|
super().__init__("Web Search", "Search the web for current information using DuckDuckGo") |
|
self.rate_limit_delay = 2.0 |
|
|
|
try: |
|
self.agent = CodeAgent( |
|
tools=[ |
|
DuckDuckGoSearchTool(), |
|
VisitWebpageTool(), |
|
FinalAnswerTool() |
|
], |
|
model=InferenceClientModel(), |
|
max_steps=3, |
|
verbosity_level=0 |
|
) |
|
except Exception as e: |
|
print(f"Warning: Could not initialize web search agent: {e}") |
|
self.agent = None |
|
|
|
def search(self, query: str, max_results: int = 5, **kwargs) -> str: |
|
"""Use the CodeAgent to perform comprehensive web search and analysis""" |
|
if not self.agent: |
|
return self.format_error_response(query, "Web search agent not available. Please check dependencies.") |
|
|
|
self.rate_limit() |
|
|
|
try: |
|
|
|
agent_prompt = f"Search the web for current information about: {query}. Provide a comprehensive summary of the most relevant and recent findings." |
|
|
|
|
|
result = self.agent.run(agent_prompt) |
|
|
|
|
|
if result and isinstance(result, str) and len(result.strip()) > 0: |
|
|
|
cleaned_result = result.replace('```', '').replace('`', '').strip() |
|
return f"**Web Search Results for: {query}**\n\n{cleaned_result}" |
|
else: |
|
return f"**Web Search for: {query}**\n\nNo clear results found. Please try a different search term." |
|
|
|
except Exception as e: |
|
|
|
error_msg = str(e) |
|
if "max steps" in error_msg.lower(): |
|
return f"**Web Search for: {query}**\n\nSearch completed but reached complexity limit. Basic analysis: This query relates to {query.lower()} and would benefit from further investigation." |
|
elif "syntax" in error_msg.lower(): |
|
return f"**Web Search for: {query}**\n\nSearch encountered formatting issues but found relevant information about {query.lower()}." |
|
else: |
|
return self.format_error_response(query, error_msg) |
|
|
|
def should_use_for_query(self, query: str) -> bool: |
|
"""Web search is good for current events, news, and general information""" |
|
current_indicators = ['news', 'recent', 'latest', 'current', 'today', '2024', '2025'] |
|
general_indicators = ['what is', 'how to', 'guide', 'tutorial', 'review'] |
|
|
|
query_lower = query.lower() |
|
return any(indicator in query_lower for indicator in current_indicators + general_indicators) |
|
|
|
def extract_key_info(self, text: str) -> dict: |
|
"""Extract key information from web search results""" |
|
base_info = super().extract_key_info(text) |
|
|
|
if text: |
|
|
|
base_info.update({ |
|
'has_news_keywords': bool(any(word in text.lower() for word in ['breaking', 'report', 'announced', 'according to'])), |
|
'has_quotes': text.count('"') > 1, |
|
'has_sources': bool(any(source in text.lower() for source in ['reuters', 'bloomberg', 'bbc', 'cnn', 'associated press'])) |
|
}) |
|
|
|
return base_info |