feat(tools): hermes tools web reorder — interactive search provider priority - #53152
Closed
Icather wants to merge 3 commits into
Closed
feat(tools): hermes tools web reorder — interactive search provider priority#53152Icather wants to merge 3 commits into
Icather wants to merge 3 commits into
Conversation
…u-baidu, serpapi, jina, google-cse, sogou, 360-search) v0.17 removed the Parallel free MCP fallback (NousResearch#46350), sharply reducing free search options. This adds 9 new providers as plugins following the existing brave-free/ddgs pattern, registered in backend_candidates in composite quality-score order after the existing backends. New functional providers (search-only): - serper — Google SERP, 2,500 free queries, no credit card - baidu — Baidu AI Search, 100/day free, native Chinese content - bocha — Chinese market leader, 1,000 free starter queries - qiniu-baidu — Qiniu Cloud Baidu, 3M tokens for new users - serpapi — Multi-engine, 100/month free - jina — Full-page extraction, 10M tokens free (blocked in China) - google-cse — Google Custom Search, 100/day (blocked in China) Registry-only entries (no public API, listed for completeness): - sogou, 360-search Two prior attempts (NousResearch#41015 Serper by ViezeVingertjes, NousResearch#35690 fallback by jonathanwxh-cell) were self-closed without review. This salages their approach with a complete provider surface covering both Western and Chinese search engines. Changes: - plugins/web/*/ — 27 new files (provider.py, plugin.yaml, __init__.py per backend) - tools/web_tools.py — +9 entries in _KNOWN_WEB_BACKENDS set, +9 in backend_candidates tuple, +_check_provider_available() helper, generic plugin probe in _is_backend_available()
…r for web_search_tool When a search provider fails, Hermes previously returned an error immediately — no retry with another backend. This adds: - search_engine parameter to web_search_tool() — model can specify "baidu", "serper", "ddgs", or "auto" to walk the fallback chain. Valid values are dynamically sourced from list_provider_names(), so adding a new provider automatically extends the enum — zero changes. - web.fallback_backends config key — user-defined ordered list of providers to try. If unset, all registered providers are tried in registration order (paid first, free last). - _search_with_fallback() — walks the chain, skipping backends that are not found, not available, return errors, or return 0 results. Stops at first success. Returns error trace on total failure. This PR depends on feat/add-free-web-search-providers (PR NousResearch#53149) which adds the 9 new providers that this chain can fall back through.
…ider priority New subcommand: hermes tools web reorder Shows the current web.fallback_backends list with numbered indices. User enters a space-separated reorder (e.g. '3 1 2 4 5'). Validated for completeness (must include each number exactly once). Writes result back to config.yaml as web.fallback_backends. This gives users a simple way to prioritize search engines after adding new providers via feat/add-free-web-search-providers (PR NousResearch#53149).
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a new interactive CLI flow to reorder the web search provider fallback priority (hermes tools web reorder), and it also adds/extends the web search provider surface and fallback-chain logic in tools/web_tools.py alongside several new provider plugins.
Changes:
- Add
hermes tools web reordersubcommand to interactively reorderweb.fallback_backendsand persist it toconfig.yaml. - Extend
tools/web_tools.pywith additional provider names, generic plugin availability probing, and an"auto"fallback-chain search mode viasearch_engine. - Add new bundled web provider plugins (Serper, Baidu, Bocha, Qiniu-Baidu, SerpAPI, Jina, Google CSE) plus registry-only “unavailable” entries (Sogou, 360 Search).
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/web_tools.py | Adds provider candidates, plugin availability probing, and fallback-chain search behavior (search_engine="auto"). |
| hermes_cli/tools_config.py | Adds interactive reorder flow for web.fallback_backends and a display-name helper. |
| hermes_cli/subcommands/tools.py | Registers hermes tools web reorder argparse subcommand. |
| plugins/web/serper/provider.py | New Serper search provider implementation. |
| plugins/web/serper/plugin.yaml | Registers Serper plugin metadata and provided provider name. |
| plugins/web/serper/init.py | Plugin package marker for Serper. |
| plugins/web/baidu/provider.py | New Baidu search provider implementation. |
| plugins/web/baidu/plugin.yaml | Registers Baidu plugin metadata and provided provider name. |
| plugins/web/baidu/init.py | Plugin package marker for Baidu. |
| plugins/web/bocha/provider.py | New Bocha search provider implementation. |
| plugins/web/bocha/plugin.yaml | Registers Bocha plugin metadata and provided provider name. |
| plugins/web/bocha/init.py | Plugin package marker for Bocha. |
| plugins/web/qiniu-baidu/provider.py | New Qiniu-Baidu search provider implementation. |
| plugins/web/qiniu-baidu/plugin.yaml | Registers Qiniu-Baidu plugin metadata and provided provider name. |
| plugins/web/qiniu-baidu/init.py | Plugin package marker for Qiniu-Baidu. |
| plugins/web/serpapi/provider.py | New SerpAPI search provider implementation. |
| plugins/web/serpapi/plugin.yaml | Registers SerpAPI plugin metadata and provided provider name. |
| plugins/web/serpapi/init.py | Plugin package marker for SerpAPI. |
| plugins/web/jina/provider.py | New Jina search provider implementation. |
| plugins/web/jina/plugin.yaml | Registers Jina plugin metadata and provided provider name. |
| plugins/web/jina/init.py | Plugin package marker for Jina. |
| plugins/web/google-cse/provider.py | New Google CSE search provider implementation. |
| plugins/web/google-cse/plugin.yaml | Registers Google CSE plugin metadata and provided provider name. |
| plugins/web/google-cse/init.py | Plugin package marker for Google CSE. |
| plugins/web/sogou/provider.py | New registry-only Sogou provider marked always-unavailable. |
| plugins/web/sogou/plugin.yaml | Registers Sogou plugin metadata and provided provider name. |
| plugins/web/sogou/init.py | Plugin package marker for Sogou. |
| plugins/web/_360_search/provider.py | New registry-only 360 Search provider marked always-unavailable. |
| plugins/web/_360_search/plugin.yaml | Registers 360 Search plugin metadata and provided provider name. |
| plugins/web/_360_search/init.py | Plugin package marker for 360 Search. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+926
to
930
| def web_search_tool(query: str, limit: int = 5, search_engine: str = "auto") -> str: | ||
| """ | ||
| Search the web for information using available search API backend. | ||
| Search the web for information using available search API backends. | ||
|
|
||
| This function provides a generic interface for web search that can work |
Comment on lines
+306
to
+322
| cfg = _load_web_config() | ||
|
|
||
| # Build unique-ordered chain. Explicit backend goes first, then | ||
| # user-configured fallback list, then auto-discovered remainder. | ||
| chain: list[str] = [] | ||
|
|
||
| primary = (cfg.get("backend") or "").lower().strip() | ||
| if primary: | ||
| chain.append(primary) | ||
|
|
||
| user_fbs = cfg.get("fallback_backends", []) | ||
| if isinstance(user_fbs, str): | ||
| user_fbs = [b.strip() for b in user_fbs.split(",") if b.strip()] | ||
| for b in user_fbs: | ||
| if b not in chain: | ||
| chain.append(b) | ||
|
|
Comment on lines
+369
to
+377
| if not result.get("success"): | ||
| errors.append(f"{name}: {result.get('error', 'unknown error')}") | ||
| continue | ||
| if not len(result.get("data", {}).get("web", [])): | ||
| errors.append(f"{name}: returned 0 results") | ||
| continue | ||
|
|
||
| # Success — return immediately | ||
| return result, errors |
Comment on lines
+3431
to
+3436
| if set(indices) != set(range(1, len(current) + 1)): | ||
| print(color( | ||
| f" ✗ Must include each number 1–{len(current)} exactly once.", | ||
| Colors.RED, | ||
| )) | ||
| return |
Comment on lines
+42
to
+45
| r.raise_for_status() | ||
| data = r.json() | ||
| raw = list(((data or {}).get("data", []) or {}).get("results", []))[:limit] | ||
| results = [ |
Comment on lines
+282
to
+285
| # ─── Fallback Chain & Multi-Source Search ──────────────────────────────────── | ||
|
|
||
| def _get_valid_engine_names() -> set[str]: | ||
| """Return the set of currently-registered search provider names. |
Comment on lines
+1015
to
+1020
| # --- Auto mode: fallback chain --- | ||
| chain = _get_fallback_chain() | ||
| response_data, errors = _search_with_fallback(query, limit, chain) | ||
|
|
||
| if response_data is None: | ||
| summary = "; ".join(errors[-3:]) if errors else "All backends exhausted" |
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
New users adding search providers via #53149 get all backends in a hardcoded order with no way to re-prioritize without editing config.yaml by hand. This adds
hermes tools web reorder— an interactive command that lists the current fallback order, accepts a space-separated reorder from the user, validates it, and writes back toweb.fallback_backendsin config.Depends on: #53149 (providers) and #53151 (fallback chain)
Type of Change
Changes Made
hermes_cli/tools_config.py—_tools_web_reorder()function (~90 lines): interactive prompt, validation, config write;_provider_display_name()helper for human-readable labelshermes_cli/subcommands/tools.py—hermes tools web reordersub-subparser registrationHow to Test
hermes tools web reorder3 1 2to reorder — should validate and saveChecklist