Skip to content

feat: add Serply as a WebSearchTool engine - #197

Merged
EvilFreelancer merged 3 commits into
vamplabAI:mainfrom
googio:feat/serply-search
Sep 16, 2026
Merged

EvilFreelancer merged 3 commits into
vamplabAI:mainfrom
googio:feat/serply-search

Conversation

@googio

@googio googio commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

What and why

Adds serply as a fourth web_search_tool engine next to tavily, brave and perplexity. Serply (https://serply.io) is a Google SERP API; teams that already hold a Serply key can point their research agents at it without a Tavily account. Tavily stays the default engine, nothing changes for existing configs.

The change follows the engine-handler pattern introduced in #177:

  • _convert_serply_response / _search_serply registered in _ENGINE_HANDLERS, default URL in _ENGINE_DEFAULT_URLS, engine Literal extended.
  • GET https://api.serply.io/v1/search/ with the key in the X-Api-Key header, params q and num. The API serves at most 10 results per page, so num is capped at 10 and offset is passed natively as start (same shape as the Brave handler, rather than Tavily's over-fetch and slice).
  • Only httpx is used, which is already a core dependency, so no new packages. This also fits the lite-core split proposed in Make heavy integrations optional dependencies (lite core install) #196, where brave and perplexity stay in the core install.
  • Errors use the same log-and-reraise handling as the Brave and Perplexity handlers.

Docs: the engine lists in config.yaml.example (root and the 3 examples), docs/en|ru/framework/configuration.md and docs/en|ru/framework/tools.md now include serply, and tools.md gets a short paragraph on the engine with links to https://serply.io and the API reference at https://serply.io/docs. I left docs/*/framework/tools/web-search-tool.md alone: it still documents the old Tavily-only search: block and #196 is already rewriting it.

Verification

  • pytest: 591 passed. The one failure, tests/test_acp_bridge.py::test_acp_new_session_advertises_agent_and_model_options, fails identically on main (ACP option category), unrelated to this change.
  • New TestSerplySearchHandler in tests/test_search_providers.py: response conversion (skips results without a link), request shape (URL, header, params), offset mapped to start with the page-size cap, and engine registration.
  • pre-commit run --files ... clean (ruff, ruff-format, docformatter).
  • Live-tested against the real API: first page, offset=10 returns the second page, tool-level dispatch through WebSearchTool fills context.sources with renumbered citations, an invalid key raises HTTPStatusError (401).

Disclosure

I work with Serply. Happy to adjust naming, docs placement, or drop the tools.md paragraph if you prefer the docs to stay provider-neutral.

googio and others added 3 commits August 27, 2026 10:38
Register a fourth web_search_tool engine, "serply", next to tavily, brave
and perplexity. The handler follows the pattern from vamplabAI#177: a
_convert_serply_response / _search_serply pair in _ENGINE_HANDLERS with
its default URL in _ENGINE_DEFAULT_URLS. It calls the Serply Search API
over httpx (already a core dependency) with the key in X-Api-Key, caps
the page size at the API's maximum of 10 and passes offset natively as
start. Tavily remains the default engine.

Docs and config examples list the new engine; docs/*/framework/tools.md
gets a short paragraph on the serply engine with links to the provider
and its API reference.
vamplabAI#196 documents brave and perplexity as the engines that work without the
[search] extra; serply also only needs httpx, so add it to those lists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hijera

hijera commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the contribution — the handler follows the Brave pattern cleanly and the tests cover conversion, request shape, offset -> start and registration. Nothing blocking.

Pushed to this branch (maintainer edit):

Questions / non-blocking remarks:

  1. num capped at 10 (_search_serply). WebSearchTool.max_results allows up to 20, and the cap silently drops results above 10. The Serply docs show an example with num=100, which suggests a page can hold more than 10. Could you confirm the real per-page limit? If the API accepts larger num, I'd drop the cap (or raise it to match the documented limit).
  2. URL form. The docs describe the query as a path segment (GET /v1/search/q=...&num=...), while the handler sends it as a query string (/v1/search/?q=...&num=...). Without a key both forms return 401 API key missing rather than 404, and you live-tested the query-string form, so this works. Still, using the documented form would be safer against future routing changes. Your call.
  3. Serply paragraph in tools.md. Brave and Perplexity have no equivalent paragraph with vendor links. It is short and useful, so I'm fine keeping it, but flagging it for the maintainers given the disclosure.

Not for this PR: docs/*/framework/tools/web-search-tool.md still documents the old Tavily-only search: block, as you noted.

@googio

googio commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @hijera for the merge of main and for adding serply to the core-install engine lists, that is the right place for it.

On the three points:

  1. num cap. The 10 is deliberate and measured, not a guess. Against the live API, num=20 returns the same page as num=10, and below 10 the value is honoured exactly (num=3 gives 3). At 10 the row count follows whatever the SERP page holds, so a query can come back with 9 or 11 rows. The num=100 example in the docs is aspirational for this endpoint. I kept the cap so the request never asks for something the API will not honour, and the handler trims the response to max_results client-side. If Serply raises the per-page ceiling later, lifting the cap is a one-line change. I am happy to add a short comment on the cap explaining this if you want it recorded in code.

  2. URL form. The query-string form is deliberate too. The path form works when the query is fully percent-encoded, but a literal % in the query gets a Cloudflare 400 and a literal ? or # gets decoded server-side and re-parsed, which truncates the query and silently drops num back to the default. The query-string form handled every case I threw at it (&, +, /, ?, #, %, non-ASCII, 300-character queries). Both forms are routed by the same handler today, so this is about tolerance to un-encoded input rather than one form being unsupported. I would rather keep the form that survives a bad character.

  3. Paragraph in tools.md. Fine either way. If you would prefer parity with Brave and Perplexity, I can trim it to a one-line pointer to the docs and drop the rest.

Nothing else pending from my side. Let me know if you want either of the optional edits above in this PR.

@EvilFreelancer
EvilFreelancer merged commit 8c0aa53 into vamplabAI:main Sep 16, 2026
3 checks passed
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.

3 participants