Skip to content

feat: add Exa AI-powered search tool#264

Open
Teo (tgonzalezc5) wants to merge 1 commit into
langchain-ai:mainfrom
tgonzalezc5:feat/exa-search
Open

feat: add Exa AI-powered search tool#264
Teo (tgonzalezc5) wants to merge 1 commit into
langchain-ai:mainfrom
tgonzalezc5:feat/exa-search

Conversation

@tgonzalezc5

Copy link
Copy Markdown

Summary

Adds Exa as a selectable search provider alongside Tavily and the native OpenAI/Anthropic web-search tools.

  • New SearchAPI.EXA enum value plus a UI option in Configuration.search_api.
  • exa_search tool and exa_search_async wrapper in utils.py, mirroring the Tavily shape so the existing dedup-and-summarize pipeline applies unchanged.
  • Exposes Exa's key features as injected tool args: search_type (auto/neural/fast), category, include_domains / exclude_domains, and start_published_date / end_published_date.
  • Uses the official exa-py SDK; sync calls are dispatched via asyncio.to_thread so the event loop stays free, and queries run in parallel via asyncio.gather.
  • Reads EXA_API_KEY from the environment or from configurable.apiKeys when GET_API_KEYS_FROM_CONFIG=true, matching the existing Tavily pattern.
  • Bumps exa-py floor to >=2.0.0 and adds EXA_API_KEY= to .env.example.

Usage

from open_deep_research.configuration import Configuration, SearchAPI

config = Configuration(search_api=SearchAPI.EXA)
# or via env: EXA_API_KEY=... and selecting "Exa" in the LangGraph Studio UI

The agent will then call exa_search with a list of queries; the helper retrieves results with text + AI summary + highlights and feeds them into the same summarization step the Tavily path uses.

Files changed

  • src/open_deep_research/configuration.py — register SearchAPI.EXA + UI option.
  • src/open_deep_research/utils.pyexa_search tool, exa_search_async, _format_exa_response, get_exa_api_key, dispatch in get_search_tool.
  • pyproject.tomlexa-py>=2.0.0.
  • .env.example — add EXA_API_KEY=.
  • tests/test_exa_search.py — 15 unit tests.

Test plan

  • python -m ruff check src/open_deep_research/utils.py tests/test_exa_search.py — no new findings.
  • python -m pytest tests/test_exa_search.py -v --asyncio-mode=auto — 15 passed:
    • response normalization for typed and dict result objects
    • content / raw_content fallbacks (text-only, summary-only, highlights-only, none)
    • URL deduplication and skipping of result entries with no URL
    • get_exa_api_key env vs. apiKeys config lookup
    • exa_search_async raises when key is missing or domain filters conflict
    • SDK invocation parameters and x-exa-integration tracking header
    • get_search_tool(SearchAPI.EXA) returns the wired Exa tool
  • End-to-end run against the LangGraph Studio UI with search_api=exa (requires a live EXA_API_KEY; not exercised here).

Adds Exa as a selectable search provider alongside Tavily and the
native OpenAI/Anthropic web search tools.

- Wire up SearchAPI.EXA in configuration with a UI option
- Implement exa_search tool and async wrapper, mirroring the Tavily
  shape so the existing summarization and dedup pipeline applies
  unchanged
- Expose key Exa features: search type, category, domain include/exclude,
  and published-date range filters
- Use the exa_py SDK via asyncio.to_thread to keep the event loop free
- Read EXA_API_KEY from env or apiKeys config (matching the existing
  Tavily pattern)
- Bump exa-py floor to >=2.0.0 and document EXA_API_KEY in .env.example
- Add unit tests for response parsing, content fallbacks, API key
  resolution, async wrapper validation, and tool registration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant