open-swe/agent/tools/web_search.py
amoussa1229 25d4886b9a feat: port durable dispatch hardening and startup latency improvements
Port five upstream PRs onto dev:

- #1621 / #1658: durable dispatch with loopback webhook defense,
  create_durable_run helper, _config_with_prepare_run_id, degradation
  to None for relative/loopback completion webhook URLs
- #1696: run-level completion webhook deduplication (replace
  claim-then-post with post-then-flag per run_id), DeferredErrorModel
  for graph-factory resilience, ToolRetryMiddleware for task subagents,
  TimeoutWrapupMiddleware for all three graphs
- #1697: lazy-load __init__.py for agent.middleware, agent.tools,
  agent.dashboard (PEP 562); defer heavy imports (exa_py in web_search,
  agent.webapp in request_pr_review, deepagents in sandbox.py); add
  ttl_cache.py with stale-while-revalidate for tool loaders

Refs: #137
2026-07-09 17:29:17 +00:00

63 lines
1.9 KiB
Python

import asyncio
import logging
import os
from typing import Any
logger = logging.getLogger(__name__)
async def web_search(
query: str,
num_results: int = 5,
include_contents: bool = True,
) -> dict[str, Any]:
"""Search the web using Exa to find relevant information.
Use this tool when you need to find documentation, code examples, GitHub repos,
news, or research papers to help complete a task.
Args:
query: The search query
num_results: Number of results to return (default: 5)
include_contents: Whether to include full page contents (default: True)
Returns:
Dictionary containing:
- success: Whether the search succeeded
- results: Search results from Exa
- error: Error message if something failed
"""
api_key = os.environ.get("EXA_API_KEY")
if not api_key:
logger.warning("exa_api_key_missing")
return {
"success": False,
"error": "EXA_API_KEY is not configured. Please add it to your environment variables.",
}
async def _search() -> dict[str, Any]:
from exa_py import Exa # deferred: heavy import
client = Exa(api_key=api_key)
if include_contents:
result = await asyncio.to_thread(
client.search_and_contents,
query,
text=True,
num_results=num_results,
type="auto",
)
else:
result = await asyncio.to_thread(
client.search,
query,
num_results=num_results,
type="auto",
)
return {"success": True, "results": str(result), "error": None}
try:
return await _search()
except Exception as e:
logger.exception("web_search failed")
return {"success": False, "results": None, "error": f"{type(e).__name__}: {e}"}