Skip to content

feat(tools): add Serply as a web search backend - #412

Open
googio wants to merge 1 commit into
Kuberwastaken:mainfrom
googio:feat/serply-search
Open

googio wants to merge 1 commit into
Kuberwastaken:mainfrom
googio:feat/serply-search

Conversation

@googio

@googio googio commented Sep 13, 2026

Copy link
Copy Markdown

What

Adds Serply as a fourth backend for WebSearchTool, between Brave Search and the DuckDuckGo fallback.

crates/tools/src/web_search.rs

  • one else if in execute(), gated on a non-empty SERPLY_API_KEY
  • search_serply + format_serply_results, mirroring search_brave / format_brave_results line for line
  • a #[cfg(test)] mod tests with three network-free formatter tests

docs/tools.md

  • one list item in the backend priority order, plus the neither to none fix now that the list has three configurable entries

No new dependencies, no schema change, no change to any existing code path.

Why

The backend chain today is SearXNG, Brave, DuckDuckGo. SearXNG needs a self-hosted instance, Brave needs a paid key, and DuckDuckGo's Instant Answer endpoint is not a web search: it returns an abstract and related topics, so a query with no instant answer comes back empty. That leaves no keyed option for someone who does not want to run a SearXNG instance and does not have Brave.

Serply returns ranked web results from a single GET with an X-Api-Key header, so it drops into the existing shape without new plumbing. Response body is results[] with title, link, description, which maps onto the same three fields the Brave and SearXNG formatters already read.

Placement is deliberate: after Brave so nobody's current configuration changes behaviour, before DuckDuckGo so a configured key beats the no-results fallback.

Notes

  • Serply caps a page at 10 results. num_results is already clamped to 1..=10 at the top of execute(), so the two ceilings agree and no extra guard was needed. The comment in search_serply records this.
  • I matched the formatting of search_brave rather than running cargo fmt. The workspace is not currently rustfmt-clean (cargo fmt --all -- --check reports ~2900 hunks on main), and formatting only my additions would have made them read differently from the identical Brave code three functions above. Happy to run the formatter over the file if you would rather.
  • spec/13_rust_codebase.md documents WebSearchTool as Brave plus DuckDuckGo only and does not mention SearXNG, so it predates the current chain. I left it alone rather than adding a fourth entry to a list that is already two behind. Glad to bring that section up to date in this PR or a separate one if it is useful.

Testing

Backend registration and the whole claurst-tools suite:

$ cargo clippy --package claurst-tools --all-targets -- -D warnings
    Checking claurst-tools v0.1.7 (src-rust/crates/tools)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.33s

$ cargo test --package claurst-tools -- web_search
running 3 tests
test web_search::tests::serply_reports_no_results_when_the_array_is_empty_or_absent ... ok
test web_search::tests::serply_results_match_the_other_backends_format ... ok
test web_search::tests::serply_results_stop_at_the_requested_count ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 105 filtered out

$ cargo test --package claurst-tools
test result: ok. 108 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.05s

The test fixture is a trimmed copy of a real response. Confirming the field names the formatter reads, against the live endpoint the code calls:

$ curl -s -H "X-Api-Key: $SERPLY_API_KEY" \
    "https://api.serply.io/v1/search/?q=rust%20programming%20language&num=3"

  title      : Rust Programming Language
  link       : https://www.rust-lang.org/en-US
  description: A language empowering everyone to build reliable and efficient software...

  title      : Rust (programming language) - Wikipedia
  link       : https://en.wikipedia.org/wiki/Rust_(programming_language)
  description: Rust is a general-purpose programming language that emphasizes performance...

  title      : Rust - A Living Hell - The Perspective From A Programmer ...
  link       : https://www.reddit.com/r/learnrust/comments/1binxlv/rust_a_living_hell...
  description: Rust is a uniquely useful language but it's also unfortunately the language...

results returned: 3

The tests are network-free; the curl above is just evidence that the fixture reflects the real body.

Serply stays entirely optional. With SERPLY_API_KEY unset the chain is exactly what it is today.

Disclosure: I work with Serply. Happy to adjust scope, naming, or drop this entirely if it isn't a direction you want for the project.

WebSearchTool picks its backend from the environment. Adds a Serply
branch between Brave Search and the DuckDuckGo fallback, so it runs
only when SERPLY_API_KEY is set and nothing higher in the chain is
configured. Existing SearXNG and Brave users see no change.

search_serply mirrors search_brave: same client, same error strings,
same numbered output shape. Serply returns results[] with title, link
and description, and caps a page at 10, which matches the tool's own
num_results clamp.

Tests cover the formatter against a trimmed real response body, the
max cutoff, and the empty and missing array cases. No network.
@Kuberwastaken

Copy link
Copy Markdown
Owner

this looks good, will follow up this weekend !

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.

2 participants