""" 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 }