feat: add Exa as web_search provider#31310
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f39bef2311
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| suppressToolErrorWarnings: z.boolean().optional(), | ||
| lightContext: z.boolean().optional(), | ||
| }) |
There was a problem hiding this comment.
Restore heartbeat lightContext in config schema
Removing lightContext from HeartbeatSchema breaks existing configs that set defaults.heartbeat.lightContext, because this schema is .strict() and validateConfigObject* rejects unknown keys. The runtime still consumes heartbeat.lightContext (for lightweight heartbeat context), so this change turns a previously valid and used setting into a validation error and can block config loading for users already relying on it.
Useful? React with 👍 / 👎.
docs/tools/web.md
Outdated
| 2. **Gemini** — `GEMINI_API_KEY` env var or `search.gemini.apiKey` config | ||
| 3. **Perplexity** — `PERPLEXITY_API_KEY` / `OPENROUTER_API_KEY` env var or `search.perplexity.apiKey` config | ||
| 4. **Grok** — `XAI_API_KEY` env var or `search.grok.apiKey` config | ||
| 5. **Exa** — `EXA_API_KEY` env var or `search.exa.apiKey` config |
There was a problem hiding this comment.
Correct Exa auto-detection behavior in docs
This section now states Exa is auto-detected from EXA_API_KEY, but the implementation and tests explicitly require provider: "exa" (auto-detection falls back through other providers and then Brave). Users following this doc can set only EXA_API_KEY and still not get Exa, which leads to confusing setup failures; the doc should match the actual resolver behavior.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR adds Exa as a 6th web search provider with explicit opt-in configuration. The implementation is well-structured and follows established patterns from other providers (Brave, Perplexity, Gemini, Grok, Kimi). Key changes:
Issue found:
Confidence Score: 4/5
Last reviewed commit: f39bef2 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1f886eb67
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/agents/tools/web-search.ts
Outdated
| numResults: params.exaNumResults ?? DEFAULT_EXA_NUM_RESULTS, | ||
| highlightsMaxChars: params.exaHighlightsMaxChars ?? DEFAULT_EXA_HIGHLIGHTS_MAX_CHARS, |
There was a problem hiding this comment.
Honor count argument when using Exa provider
The Exa branch ignores the tool-level count input (and the derived search.maxResults fallback) by always sending numResults from exaNumResults/default 5. In practice, web_search calls like count: 1 or count: 10 with provider: "exa" will still return the configured/default Exa count, which breaks the existing web_search parameter contract for callers that expect per-request result sizing.
Useful? React with 👍 / 👎.
04bfb21 to
3286b5f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3286b5f8a6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 801725f441
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Adds Exa as a 6th web_search provider (explicit opt-in only via provider: "exa" config). Includes config types, Zod schema, highlights- based content retrieval, env var support (EXA_API_KEY), and tests. No changes to existing providers or default behavior.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…Schema for exa.apiKey
801725f to
78bfed1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78bfed1dec
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| } | ||
|
|
||
| function resolveExaApiKey(exa?: ExaSearchConfig): string | undefined { | ||
| const fromConfig = normalizeApiKey(exa?.apiKey); |
There was a problem hiding this comment.
Resolve Exa SecretRefs before reading apiKey
resolveExaApiKey normalizes exa.apiKey as a plain string, so a valid SecretRef value gets coerced to empty and the Exa provider fails with missing_exa_api_key. Fresh evidence for re-raising this area: Exa is still absent from the web-search secret resolver paths in src/secrets/runtime-config-collectors-core.ts:284-290 and from the target registry in src/secrets/target-registry-data.ts:666-719, so tools.web.search.exa.apiKey is never resolved before this code executes.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7e5bce9e41
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const paths = [ | ||
| "apiKey", | ||
| "exa.apiKey", |
There was a problem hiding this comment.
Include exa in provider gating for secret resolution
Adding "exa.apiKey" to this path list without updating the selectedProvider whitelist above means provider: "exa" falls into the selectedProvider === undefined branch, so all web-search provider secret refs are treated as active. In that configuration, stale refs like tools.web.search.grok.apiKey or tools.web.search.kimi.apiKey are now resolved (instead of ignored as inactive), and missing/invalid refs can fail config secret resolution even though only Exa is selected.
Useful? React with 👍 / 👎.
|
Nice work surfacing this early. I'm closing this as a duplicate of #52617. The Exa provider is now merged through the current bundled-plugin architecture, so this earlier branch is fully superseded by the landed path. If that overlap looks wrong from your side, tell me what differs and I can re-check it quickly. |
Summary
provider: "exa"config), with highlights-based content retrieval, config types, Zod schema, env var support (EXA_API_KEY), and tests.web_fetch, no auto-detection — Exa is only activated when explicitly configured.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
"exa"option fortools.web.search.providertools.web.search.exa.apiKey,tools.web.search.exa.numResults(default: 5),tools.web.search.exa.highlightsMaxChars(default: 8000)EXA_API_KEY"Search the web using Exa search. Returns structured results with titles, URLs, and highlights."Security Impact (required)
EXA_API_KEYenv var /exa.apiKeyconfig, follows same pattern as existing providershttps://api.exa.ai/searchwithx-exa-integration: "openclaw"headerprovider: "exa". Uses existingwithTrustedWebSearchEndpointwrapper with timeout and abort support.Repro + Verification
Environment
{ tools: { web: { search: { provider: "exa", exa: { apiKey: "exa-..." } } } } }Steps
EXA_API_KEYor configuretools.web.search.exa.apiKeytools.web.search.provider: "exa"web_searchtool with a queryExpected
numResults(default 5) with highlights limited tohighlightsMaxChars(default 8000)Actual
Evidence
47 tests passing in
web-search.test.ts, 18 inconfig.web-search-provider.test.ts.Human Verification (required)
apiKey,numResults,highlightsMaxChars), config validation, auto-detection exclusion, Exa not in provider priority chainnumResultsandhighlightsMaxChars, missing API key fallback to env varCompatibility / Migration
EXA_API_KEYenv var andtools.web.search.exaconfig blockFailure Recovery (if this breaks)
provider: "exa"from config — falls back to default Brave providerEXA_API_KEYis invalid, the tool returns an API error message (same pattern as other providers)Risks and Mitigations