import json from langchain_community.tools import DuckDuckGoSearchResults from langchain_core.tools import StructuredTool from pydantic import BaseModel, ConfigDict, Field from app.config import get_settings class WebSearchInput(BaseModel): model_config = ConfigDict(extra="allow") query: str = Field(default="", description="Search query for external/current information.") cursor: int | None = Field(default=None, description="Optional cursor from model/tool planning. Ignored.") id: str | int | None = Field(default=None, description="Optional id from model/tool planning. Ignored.") def _build_tavily_tool(api_key: str): try: from tavily import TavilyClient except Exception: return None client = TavilyClient(api_key=api_key) def tavily_search(query: str = "", cursor: int | None = None, id: str | int | None = None, **kwargs) -> str: query_text = query.strip() _ = (cursor, id, kwargs) if not query_text: return "Web search was requested without a query." try: result = client.search(query=query_text, search_depth="advanced") except Exception as exc: return f"Tavily search failed: {exc}" rows = result.get("results", []) if isinstance(result, dict) else [] if not rows: return "No web results found." lines = ["Web search results (cite website URLs used):"] for index, row in enumerate(rows[:5], start=1): title = str(row.get("title") or "Untitled result") url = str(row.get("url") or "").strip() snippet = str(row.get("content") or "").strip().replace("\n", " ") snippet = snippet[:500] lines.append(f"{index}. title: {title}") lines.append(f" url: {url or 'N/A'}") if snippet: lines.append(f" snippet: {snippet}") lines.append("\nRaw response:") lines.append(json.dumps(result, ensure_ascii=True)) return "\n".join(lines) return StructuredTool.from_function( func=tavily_search, name="web_search", description="Search the web for current/external information using Tavily.", args_schema=WebSearchInput, ) def build_web_search_tool(): settings = get_settings() provider = settings.web_search_provider.lower() if provider == "tavily": if not settings.tavily_api_key: return None return _build_tavily_tool(settings.tavily_api_key) try: return DuckDuckGoSearchResults(num_results=5) except Exception: return None