Spaces:
Running
Running
File size: 4,529 Bytes
d9162ac e3c2163 d9162ac e3c2163 d9162ac e3c2163 d9162ac e3c2163 d9162ac e3c2163 b4f9ff5 e3c2163 b4f9ff5 e3c2163 d9162ac |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
"""Factory for creating web search tools based on configuration."""
import structlog
from src.tools.base import SearchTool
from src.tools.searchxng_web_search import SearchXNGWebSearchTool
from src.tools.serper_web_search import SerperWebSearchTool
from src.tools.web_search import WebSearchTool
from src.utils.config import settings
from src.utils.exceptions import ConfigurationError
logger = structlog.get_logger()
def create_web_search_tool(provider: str | None = None) -> SearchTool | None:
"""Create a web search tool based on configuration.
Args:
provider: Override provider selection. If None, uses settings.web_search_provider.
Returns:
SearchTool instance, or None if not available/configured
The tool is selected based on provider (or settings.web_search_provider if None):
- "serper": SerperWebSearchTool (requires SERPER_API_KEY)
- "searchxng": SearchXNGWebSearchTool (requires SEARCHXNG_HOST)
- "duckduckgo": WebSearchTool (always available, no API key)
- "brave" or "tavily": Not yet implemented, returns None
- "auto": Auto-detect best available provider (prefers Serper > SearchXNG > DuckDuckGo)
Auto-detection logic (when provider is "auto" or not explicitly set):
1. Try Serper if SERPER_API_KEY is available (best quality - Google search + full content scraping)
2. Try SearchXNG if SEARCHXNG_HOST is available
3. Fall back to DuckDuckGo (always available, but lower quality - snippets only)
"""
provider = provider or settings.web_search_provider
# Auto-detect best available provider if "auto" or if provider is duckduckgo but better options exist
if provider == "auto" or (provider == "duckduckgo" and settings.serper_api_key):
# Prefer Serper if API key is available (better quality)
if settings.serper_api_key:
try:
logger.info(
"Auto-detected Serper web search (SERPER_API_KEY found)",
provider="serper",
)
return SerperWebSearchTool()
except Exception as e:
logger.warning(
"Failed to initialize Serper, falling back",
error=str(e),
)
# Try SearchXNG as second choice
if settings.searchxng_host:
try:
logger.info(
"Auto-detected SearchXNG web search (SEARCHXNG_HOST found)",
provider="searchxng",
)
return SearchXNGWebSearchTool()
except Exception as e:
logger.warning(
"Failed to initialize SearchXNG, falling back",
error=str(e),
)
# Fall back to DuckDuckGo
if provider == "auto":
logger.info(
"Auto-detected DuckDuckGo web search (no API keys found)",
provider="duckduckgo",
)
return WebSearchTool()
try:
if provider == "serper":
if not settings.serper_api_key:
logger.warning(
"Serper provider selected but no API key found",
hint="Set SERPER_API_KEY environment variable",
)
return None
return SerperWebSearchTool()
elif provider == "searchxng":
if not settings.searchxng_host:
logger.warning(
"SearchXNG provider selected but no host found",
hint="Set SEARCHXNG_HOST environment variable",
)
return None
return SearchXNGWebSearchTool()
elif provider == "duckduckgo":
# DuckDuckGo is always available (no API key required)
return WebSearchTool()
elif provider in ("brave", "tavily"):
logger.warning(
f"Web search provider '{provider}' not yet implemented",
hint="Use 'serper', 'searchxng', or 'duckduckgo'",
)
return None
else:
logger.warning(f"Unknown web search provider '{provider}', falling back to DuckDuckGo")
return WebSearchTool()
except ConfigurationError as e:
logger.error("Failed to create web search tool", error=str(e), provider=provider)
return None
except Exception as e:
logger.error("Unexpected error creating web search tool", error=str(e), provider=provider)
return None
|