#!/usr/bin/env python3 """ Tavily AI Search API Client Usage: python tavily_search.py "your search query" [--max-results 5] [--depth basic|advanced] """ import os import sys import json import argparse from typing import List, Dict, Any, Optional def get_api_key(api_key: Optional[str] = None) -> str: """Get Tavily API key from environment variable or parameter.""" # Priority: parameter > environment variable if api_key: return api_key env_key = os.environ.get("TAVILY_API_KEY") if env_key: return env_key raise ValueError( "Tavily API key required. Set via:\n" " 1. Environment variable: export TAVILY_API_KEY=your_key\n" " (Add to ~/.bashrc or ~/.zshrc for persistence)\n" " 2. Direct parameter: pass api_key when calling the function\n" "\nGet your API key at: https://tavily.com" ) def tavily_search( query: str, max_results: int = 5, search_depth: str = "basic", include_answer: bool = False, include_images: bool = False, api_key: Optional[str] = None ) -> Dict[str, Any]: """ Search using Tavily AI Search API. Args: query: Search query string max_results: Number of results to return (1-20) search_depth: "basic" or "advanced" include_answer: Include AI-generated answer include_images: Include image URLs api_key: Tavily API key (optional, auto-detected from env/config) Returns: Dictionary containing search results """ api_key = get_api_key(api_key) try: import requests except ImportError: raise ImportError("requests package required. Install with: pip install requests") url = "https://api.tavily.com/search" payload = { "api_key": api_key, "query": query, "max_results": min(max(max_results, 1), 20), "search_depth": search_depth, "include_answer": include_answer, "include_images": include_images, } response = requests.post(url, json=payload, timeout=30) response.raise_for_status() return response.json() def format_results(results: Dict[str, Any]) -> str: """Format search results for display.""" output = [] if "answer" in results and results["answer"]: output.append("=" * 60) output.append("AI ANSWER") output.append("=" * 60) output.append(results["answer"]) output.append("") output.append("=" * 60) output.append("SEARCH RESULTS") output.append("=" * 60) output.append(f"Query: {results.get('query', 'N/A')}") output.append("") for i, result in enumerate(results.get("results", []), 1): output.append(f"{i}. {result.get('title', 'No title')}") output.append(f" URL: {result.get('url', 'N/A')}") if result.get('published_date'): output.append(f" Published: {result['published_date']}") if result.get('score'): output.append(f" Relevance: {result['score']:.2f}") content = result.get('content', '') if content: # Truncate long content if len(content) > 300: content = content[:297] + "..." output.append(f" {content}") output.append("") return "\n".join(output) def main(): parser = argparse.ArgumentParser(description="Tavily AI Search") parser.add_argument("query", help="Search query") parser.add_argument("--max-results", type=int, default=5, help="Number of results (1-20)") parser.add_argument("--depth", choices=["basic", "advanced"], default="basic", help="Search depth") parser.add_argument("--answer", action="store_true", help="Include AI-generated answer") parser.add_argument("--images", action="store_true", help="Include images") parser.add_argument("--json", action="store_true", help="Output raw JSON") args = parser.parse_args() try: results = tavily_search( query=args.query, max_results=args.max_results, search_depth=args.depth, include_answer=args.answer, include_images=args.images ) if args.json: print(json.dumps(results, indent=2, ensure_ascii=False)) else: print(format_results(results)) except ValueError as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) except Exception as e: print(f"Search failed: {e}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()