Skip to content

fix: make _is_backend_available() plugin-aware - #36987

Closed
h121b wants to merge 1 commit into
NousResearch:mainfrom
h121b:fix/plugin-aware-backend-availability
Closed

fix: make _is_backend_available() plugin-aware#36987
h121b wants to merge 1 commit into
NousResearch:mainfrom
h121b:fix/plugin-aware-backend-availability

Conversation

@h121b

@h121b h121b commented Jun 1, 2026

Copy link
Copy Markdown

Background

I wanted to use Firecrawl and Crawl4AI together — Crawl4AI has better markdown support and I can use custom LLM extraction (via Hermes auxiliary provider). I asked Hermes to build a custom plugin for me. After some hiccups, the plugin works as a terminal call but fails when registered as a web.extract_backend — because Hermes core code is hardcoded to look for a select few plugins only.

Broader question: Can such type of fix be allowed for all plugins — allowing user-created plugins to be registered and used, overriding hardcoding if any? At the moment, I have to daily patch this code in my local installation (as I run hermes update --yes daily via cron and it overwrites my patch).

Disclaimer: Rest of this PR is AI generated. I am vibe coding. This suggestion to the code was made by AI, and I have patched the code locally to verify it works (tested by Hermes' own tool calls).

Problem

_is_backend_available() in tools/web_tools.py has a hardcoded list of backend names. User-installed web plugins register via agent.web_search_registry.register_provider() but the function never checks the registry — only recognized names return True.

When a user sets web.extract_backend: firecrawl-crawl4ai (a plugin-registered backend), the function returns False, causing _get_capability_backend() to fall back to web.backend (typically searxng), which is search-only.

Why This Matters

The plugin system is the extensibility mechanism for Hermes. If plugin-registered backends can't be selected via config, the plugin system's web provider interface is effectively broken for dispatch purposes. The plugin registers successfully (plugins.enabled check passes, register(ctx) runs, provider appears in the registry), but the tool dispatcher ignores it.

This affects any user who:

  1. Installs a custom web extract plugin
  2. Sets web.extract_backend to the plugin's name
  3. Expects web_extract to use their plugin

Fix

Add a registry fallback at the end of _is_backend_available():

# Plugin-provided backends: check the registry
try:
    from agent.web_search_registry import get_provider as _wsp_get_provider
    p = _wsp_get_provider(backend)
    if p is not None and p.is_available():
        return True
except ImportError:
    pass

Safety

  • get_provider() is a simple dict lookup — no network calls, no side effects
  • p.is_available() is the provider's own check (env var, importable dep)
  • try/except ImportError handles contexts where the registry isn't loaded
  • Existing hardcoded checks run first — no performance change for built-in backends
  • Only triggers for unknown backend names (the return False fallthrough case)

Alternatives Considered

Alternative Why rejected
Hardcode plugin names Defeats the purpose of plugins — every new plugin needs a core change
Make plugins patch _is_backend_available Brittle — patching core functions from plugins is fragile and update-unfriendly
Only support built-in backends Breaks the plugin system's web provider contract
Let get_active_extract_provider() handle it It already does — but _get_capability_backend() filters before reaching it

Testing

Verified with firecrawl-crawl4ai plugin:

  • Plugin registers successfully and appears in the web provider registry
  • web_extract(urls=["https://example.com"]) routes to the plugin instead of falling back to SearXNG
  • 54/54 plugin tests passing (49 unit + 5 integration)

…ackends

User-installed web plugins register via agent.web_search_registry but
_is_backend_available() has a hardcoded list that doesn't include them.
This causes web.extract_backend to fall back to the wrong backend.

Add a registry fallback at the end of _is_backend_available() that
checks get_provider() for any unrecognized backend name. Only triggers
for unknown backends — no change for built-in backends.

Co-authored-by: h121b <h121b@users.noreply.github.com>
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for identifying a real plugin-resolution gap. This is now implemented on main; this is an automated hermes-sweeper review.

  • 0a9d42ce402cc1a4e12dee18a313c1db2e0a02e3 (fix(web_tools): delegate backend availability to provider registry) routes non-legacy registered backends through their provider is_available() result at tools/web_tools.py:323-327.
  • The per-capability selector consequently honors the configured backend at tools/web_tools.py:304-308, including web.extract_backend.
  • Current regression coverage verifies custom-provider availability and configured extract-backend routing at tests/tools/test_web_tools_config.py:784-800.
  • The fix shipped in v2026.7.7 (Hermes Agent v0.18.1).

@teknium1 teknium1 closed this Jul 13, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:implemented-on-main Sweeper: behavior already present on current main tool/web Web search and extraction type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants