ira/execution/api_handlers/serper_handler.py

135 lines
4.4 KiB
Python

"""
Serper API handler.
Provides direct access to Serper's enhanced search capabilities.
"""
import os
import json
import requests
from typing import Dict, List, Any, Optional
from .base_handler import BaseSearchHandler
from config.config import get_config, get_api_key
class SerperSearchHandler(BaseSearchHandler):
"""Handler for Serper's enhanced search API."""
def __init__(self):
"""Initialize the Serper search handler."""
self.config = get_config()
self.api_key = get_api_key("serper")
self.base_url = "https://google.serper.dev/search"
self.available = self.api_key is not None
def search(self, query: str, num_results: int = 10, **kwargs) -> List[Dict[str, Any]]:
"""
Execute a search query using Serper's enhanced API.
Args:
query: The search query to execute
num_results: Number of results to return
**kwargs: Additional search parameters:
- search_type: Type of search ("web", "news", "images", "places")
- country: Country code (default: "us")
- language: Language code (default: "en")
- page: Page number (default: 1)
Returns:
List of search results with standardized format
"""
if not self.available:
raise ValueError("Serper API is not available. API key is missing.")
# Set up the request parameters
params = {
"q": query,
"num": num_results
}
# Add optional parameters
search_type = kwargs.get("search_type", "search")
params["type"] = search_type
if "country" in kwargs:
params["gl"] = kwargs["country"]
if "language" in kwargs:
params["hl"] = kwargs["language"]
if "page" in kwargs:
params["page"] = kwargs["page"]
# Set up the headers
headers = {
"X-API-KEY": self.api_key,
"Content-Type": "application/json"
}
try:
# Make the request
print(f"Making request to {self.base_url} with API key: {self.api_key[:5]}...")
print(f"Headers: {headers}")
print(f"Params: {params}")
response = requests.post(
self.base_url,
headers=headers,
json=params
)
print(f"Response status: {response.status_code}")
print(f"Response text: {response.text[:200]}")
response.raise_for_status()
# Parse the response
data = response.json()
# Process the results
results = []
# Process organic results
if "organic" in data:
for item in data["organic"]:
result = {
"title": item.get("title", ""),
"url": item.get("link", ""),
"snippet": item.get("snippet", ""),
"source": "serper"
}
results.append(result)
# Process knowledge graph if available
if "knowledgeGraph" in data:
kg = data["knowledgeGraph"]
if "title" in kg and "description" in kg:
result = {
"title": kg.get("title", ""),
"url": kg.get("website", ""),
"snippet": kg.get("description", ""),
"source": "serper_kg"
}
results.append(result)
return results
except requests.exceptions.RequestException as e:
print(f"Error executing Serper search: {e}")
return []
def get_name(self) -> str:
"""Get the name of the search handler."""
return "serper"
def is_available(self) -> bool:
"""Check if the Serper API is available."""
return self.available
def get_rate_limit_info(self) -> Dict[str, Any]:
"""Get information about the API's rate limits."""
# These are example values - adjust based on your Serper plan
return {
"requests_per_minute": 60,
"requests_per_day": 2500,
"current_usage": None # Serper doesn't provide usage info in responses
}