azettl's picture
add new research tools
ce0bf87
"""
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 # Longer delay for web searches
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:
# Simplified prompt for better reliability
agent_prompt = f"Search the web for current information about: {query}. Provide a comprehensive summary of the most relevant and recent findings."
# Run the agent
result = self.agent.run(agent_prompt)
# Clean and validate the result
if result and isinstance(result, str) and len(result.strip()) > 0:
# Remove any code-like syntax that might cause parsing errors
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:
# More robust fallback
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:
# Look for news-specific patterns
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