Skip to content

feat(web-search): add an opt-in Keenable search provider - #68

Open
ilya-bogin-keenable wants to merge 1 commit into
ApodexAI:mainfrom
keenableai:feat/keenable-search
Open

ilya-bogin-keenable wants to merge 1 commit into
ApodexAI:mainfrom
keenableai:feat/keenable-search

Conversation

@ilya-bogin-keenable

Copy link
Copy Markdown

What changed

Adds Keenable as a third opt-in backend for the existing web_search provider switch. Setting WEB_SEARCH_PROVIDER=keenable sends web_search to the Keenable search API. It works without a key; an optional KEENABLE_API_KEY only raises the rate limits. Both the original and aligned native search implementations use it.

  • plugins/tools/_keenable_search.py: one REST call per query, run concurrently, mapped to the same Serper-shaped organic rows the Parallel path returns. 429 and 5xx responses are retried twice with backoff; other failures return a clear message and never fall back to Serper.
  • web_search.py / web_search_aligned.py: the Parallel branch also serves keenable, so domain filtering, URL deduplication and the per-query limit after filtering are shared. Parallel output is unchanged.
  • _PROVIDER_NAMES and the config preflight accept keenable.
  • Docs: .env.example, README.md, apodex/README.md, docs/install/global-install.md.
  • Tests: tests/test_keenable_search.py (offline, httpx.MockTransport) and one preflight case.

Serper remains the implicit provider and its missing-key behavior is unchanged. Custom region, language and time filters (and the aligned tool's location, page and autocorrect options) return a message to use Serper, as they do for Parallel. Each query asks for 10 to 50 results so the existing filters have room before the limit applies. Keenable returns page text rather than a short snippet, so each snippet is collapsed to one line and capped at 1,000 characters. web_fetch keeps its existing provider.

Sent to Keenable: the query and result count, with X-Keenable-Title: FrontierAgent, plus X-API-Key only when KEENABLE_API_KEY is set.

Setup

Set WEB_SEARCH_PROVIDER=keenable in the project .env or the user env file, optionally with KEENABLE_API_KEY. The API reference describes the endpoint. Setting the value back to serper restores the current provider.

Validation

  • Live and keyless (no KEENABLE_API_KEY, no SERPER_API_KEY) through web_search.ainvoke and web_search_aligned.ainvoke with WEB_SEARCH_PROVIDER=keenable: one query returned 5 of 5 results in 0.82 s, a two-query list returned 10 results in 0.48 s, and the aligned tool returned 8 of 8 in 0.46 s. All 4 requests went to /v1/search/public with X-Keenable-Title: FrontierAgent and no key header.
  • The keyed path is covered by an offline test only.
  • uv run pytest tests/test_keenable_search.py tests/test_parallel_search.py apodex/tests/test_config_preflight.py apodex/tests/test_userenv.py -q: 81 passed.
  • uv run pytest -q: 4,820 passed, 10 skipped. The 7 setup errors in apodex/tests/test_global_install.py also occur on unmodified main on macOS (the installed launcher's shebang is /bin/sh there).
  • uv run ruff check frontier_agent/ apodex/ benchmarks/ workflows/ plugins/ deploy/ tools/ scripts/: passed.
  • uv run pyright: 0 errors in the touched modules; the full run only reports imports from the eval and hf-space extras, which were not installed.
  • tools/import_smoke.py --stage 1, tools/check_symbols.py, tools/check_lazy_exports.py and git diff --check: passed.

I did not run a full agent session, since that needs model credentials; the live checks call the same tool objects the profiles bind.

Affiliation

Disclosure: I work at Keenable, which runs this API.

WEB_SEARCH_PROVIDER=keenable routes both native web_search implementations
to the Keenable search API. It works without a key; an optional
KEENABLE_API_KEY only raises the rate limits. Serper stays the default and
Parallel is unchanged.

This branch has not been deployed

No deployments
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