Spaces:
Sleeping
Sleeping
""" | |
Search tools for finding song lyrics and related information. | |
""" | |
import os | |
import random | |
import time | |
from typing import Any, Dict, List | |
import requests | |
from loguru import logger | |
from smolagents import DuckDuckGoSearchTool, Tool | |
class BraveSearchTool(Tool): | |
""" | |
A tool for performing web searches using the Brave Search API. | |
This tool requires a Brave Search API key to be set in the environment | |
variable BRAVE_API_KEY or passed directly to the constructor. | |
Documentation: https://api.search.brave.com/app/documentation | |
""" | |
def __init__(self, max_results: int = 10, **kwargs): | |
""" | |
Initialize the Brave Search tool. | |
Args: | |
max_results: Maximum number of results to return (default: 10) | |
""" | |
super().__init__(**kwargs) | |
self.api_key = os.environ.get("BRAVE_API_KEY") | |
if not self.api_key: | |
logger.warning("No Brave API key found. Set BRAVE_API_KEY environment variable or pass api_key parameter.") | |
self.max_results = max_results | |
self.name = "brave_search" | |
self.description = "Search the web using Brave Search API" | |
self.inputs = {"query": {"type": "string", "description": "The search query string"}} | |
self.output_type = "string" | |
logger.info(f"Initialized BraveSearchTool with max_results={max_results}") | |
def forward(self, query: str) -> List[Dict[str, Any]]: | |
""" | |
Execute a search using the Brave Search API. | |
Args: | |
query: The search query string | |
Returns: | |
List of search results in the format: | |
[{"title": str, "href": str, "body": str}, ...] | |
""" | |
if not self.api_key: | |
logger.error("Brave Search API key is not set") | |
return [{"title": "API Key Error", "href": "", "body": "Brave Search API key is not set"}] | |
url = "https://api.search.brave.com/res/v1/web/search" | |
headers = {"Accept": "application/json", "X-Subscription-Token": self.api_key} | |
params = {"q": query, "count": self.max_results} | |
try: | |
logger.info(f"Performing Brave search for query: '{query}'") | |
response = requests.get(url, headers=headers, params=params) | |
response.raise_for_status() | |
data = response.json() | |
results = [] | |
if "web" in data and "results" in data["web"]: | |
for result in data["web"]["results"]: | |
results.append({ | |
"title": result.get("title", ""), | |
"href": result.get("url", ""), | |
"body": result.get("description", "") | |
}) | |
logger.info(f"Found {len(results)} results for query: '{query}'") | |
return results | |
except Exception as e: | |
logger.error(f"Error in Brave search: {str(e)}") | |
return [{"title": "Search error", "href": "", "body": f"Error performing search: {str(e)}"}] | |
class ThrottledDuckDuckGoSearchTool(DuckDuckGoSearchTool): | |
""" | |
A wrapper around DuckDuckGoSearchTool that adds a delay between requests | |
to avoid rate limiting issues. | |
This tool implements a delay mechanism to prevent hitting DuckDuckGo's rate limits. | |
Each search request will be followed by a random delay within the specified range. | |
""" | |
def __init__(self, min_delay: float = 7.0, max_delay: float = 15.0, **kwargs): | |
""" | |
Initialize the throttled search tool with delay parameters. | |
Args: | |
min_delay: Minimum delay in seconds between requests (default: 5.0) | |
max_delay: Maximum delay in seconds between requests (default: 5.0) | |
**kwargs: Additional arguments to pass to DuckDuckGoSearchTool | |
""" | |
super().__init__(**kwargs) | |
self.min_delay = min_delay | |
self.max_delay = max_delay | |
self.name = "search" # Keep the same name as the parent class | |
logger.info(f"Initialized ThrottledDuckDuckGoSearchTool with delay range: {min_delay}-{max_delay}s") | |
def forward(self, query: str) -> List[Dict[str, Any]]: | |
""" | |
Execute a search with a delay to avoid rate limiting. | |
Args: | |
query: The search query string | |
Returns: | |
List of search results | |
""" | |
# Add a random delay before the search to avoid rate limiting | |
delay = random.uniform(self.min_delay, self.max_delay) | |
logger.info(f"Throttling DuckDuckGo search for {delay:.2f} seconds before query: '{query}'") | |
time.sleep(delay) | |
# Call the parent class implementation | |
try: | |
results = super().forward(query) | |
# Add another delay after the search to ensure spacing between requests | |
time.sleep(random.uniform(self.min_delay / 2, self.max_delay / 2)) | |
return results | |
except Exception as e: | |
logger.error(f"Error in DuckDuckGo search: {str(e)}") | |
# Return empty results on error to allow the agent to continue | |
return [{"title": "Search error", "href": "", "body": f"Error performing search: {str(e)}"}] | |