Skip to content

feat: add Exa AI-powered search tool#1

Open
tgonzalezc5 wants to merge 1 commit into
waitdeadai:mainfrom
tgonzalezc5:feat/exa-search-modernize
Open

feat: add Exa AI-powered search tool#1
tgonzalezc5 wants to merge 1 commit into
waitdeadai:mainfrom
tgonzalezc5:feat/exa-search-modernize

Conversation

@tgonzalezc5
Copy link
Copy Markdown

Summary

The Exa search provider in forgegod/tools/web.py was sending a /search request without a contents block, so the response had no text/highlights/summary fields and r.get(\"text\", \"\") was always empty. In practice the Exa fallback was returning [{url, title, snippet: \"\"}] — useless to the researcher.

This PR aligns the implementation with the current Exa API and adds attribution.

  • Request contents.highlights (3 sentences) and contents.text (1000 chars) so snippets are actually populated.
  • Drop the legacy useAutoprompt field; type: \"auto\" already covers it.
  • Add x-exa-integration: forgegod header for usage attribution.
  • Cascade snippet extraction across highlights → summary → text via a small _exa_snippet() helper.

Usage

No config or API surface change. Existing ReconConfig.exa_api_key and the web_search(provider=\"exa\", ...) entrypoint behave the same — they just return non-empty snippets now.

from forgegod.tools.web import web_search

# Returns JSON list of {url, title, snippet}, with snippets populated
# from Exa highlights (falling back to summary/text).
results = await web_search(\"transformer architecture\", provider=\"exa\", exa_api_key=\"...\")

Files changed

  • forgegod/tools/web.py — fix _search_exa request body, add integration header, add _exa_snippet() helper.
  • tests/test_tools.py — add 7 unit tests covering the request shape, header, snippet fallback, and error paths.

Test plan

  • pytest tests/test_tools.py — 22 passed (7 new + 15 existing)
  • ruff check forgegod/ tests/ — clean
  • No-API-key short-circuit returns []
  • Request body includes contents.highlights + contents.text and headers include x-exa-integration: forgegod
  • Snippet falls back across highlights → summary → text
  • HTTP 5xx is caught and returns [] (no exception bubbled to caller)

The Exa search provider was sending a request without a `contents` block,
so each result came back without `text`/`highlights`/`summary`. Snippets
read `r.get("text", "")`, which was always empty in practice — the
fallback chain returned `[{url, title, snippet: ""}]` to the agent.

Changes:
- Request `contents.highlights` (3 sentences) and `contents.text`
  (1000 chars) so snippets are populated.
- Drop the legacy `useAutoprompt` field; `type: "auto"` already covers it.
- Add `x-exa-integration: forgegod` header for usage attribution.
- Cascade snippet extraction across highlights → summary → text via a
  small `_exa_snippet()` helper.

Adds tests covering: empty key short-circuit, request shape (header +
contents), HTTP error path, and the snippet fallback ladder.
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