Skip to content

feat(config): render web.backend/search_backend/extract_backend as Config Form selects (#71929) - #71958

Open
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:feat/config-form-web-backend-selects
Open

feat(config): render web.backend/search_backend/extract_backend as Config Form selects (#71929)#71958
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:feat/config-form-web-backend-selects

Conversation

@PRATHAMESH75

Copy link
Copy Markdown
Contributor

What & why

Fixes #71929.

The Config page's Form view (/config → Web section) rendered web.backend, web.search_backend, and web.extract_backend as bare text <input>s. Users had to know the exact backend string (e.g. brave-free, not brave) with no hint of valid values and no validation — a silent-failure trap, while sibling fields (terminal.backend, tts.provider, display.skin, …) have long been dropdowns.

Change

Add three _SCHEMA_OVERRIDES entries in hermes_cli/web_server.py turning all three fields into selects, following the existing terminal.backend pattern. The schema feeds every surface that reads /api/config/schema (desktop, CLI, dashboard), so all three get the dropdown.

Option lists are mirrored from the runtime, not hand-copied from the issue:

  • web.backend / web.search_backend → the built-in _LEGACY_WEB_BACKENDS set in tools/web_tools.py (firecrawl, searxng, brave-free, ddgs, tavily, exa, parallel, xai). A test asserts the options equal that set so the two never drift.
  • web.extract_backend → only the extract-capable subset (firecrawl, tavily, exa, parallel). Search-only providers (searxng, brave-free, ddgs, xai) are rejected at runtime with a "search-only backend" error, so they're excluded here.
  • A leading "" keeps the shared-backend / auto-detect fallback selectable.

Plugin-backend safety

Web backends are plugin-extensible (agent/web_search_registry), and the dashboard renders a select as a closed gate (unlike the free-input voice/model fields). A naive static select would silently drop a configured plugin/custom backend and let the next save clobber it. To avoid that regression, _schema_with_dynamic_provider_options() now preserves a configured value that falls outside the built-in list — the same current-value preservation already used for tts/stt/memory providers. The common case (built-in or blank) returns the frozen import-time entry untouched.

Tests

tests/hermes_cli/test_web_server.py:

  • the three fields render as selects with a leading "";
  • search options equal _LEGACY_WEB_BACKENDS (drift guard);
  • extract options are exactly the extract-capable subset and exclude search-only providers;
  • a configured plugin/custom backend outside the list stays selectable via the dynamic merge, and the module-level schema is copied not mutated;
  • a built-in/blank value produces no overlay (identity with the frozen schema).

uv run pytest tests/hermes_cli/test_web_server.py — all schema tests green.


Note: the local footgun preflight flags 10 pre-existing bare write_text() calls elsewhere in test_web_server.py; none are in this change, and the CI Windows footguns check scans production packages only (not tests/), so it passes clean. Those belong to the separate #71014 read_text/write_text campaign, not this PR.

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard tool/web Web search and extraction area/config Config system, migrations, profiles sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 26, 2026
@PRATHAMESH75
PRATHAMESH75 force-pushed the feat/config-form-web-backend-selects branch from 4b30a1d to b3dbc04 Compare July 28, 2026 18:36

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing a verified Config Form gap: current main still infers the three web.*_backend fields as strings through hermes_cli/web_server.py:1044-1083.

Problems

  • hermes_cli/web_server.py:1245-1248 preserves only an already-configured unknown provider. Web providers are registered and enumerable through agent/web_search_registry.py:78-82, while the Form select renders only supplied options (web/src/components/AutoField.tsx:116-128). A newly installed, unconfigured plugin therefore cannot be selected from this UI.
  • tests/hermes_cli/test_web_server.py:5327 and :5339 freeze exact provider sets. AGENTS.md:1333-1380 requires behavior/invariant tests rather than change-detector snapshots.

Suggested changes

  • Populate per-capability options from the web registry, filter by supports_search() / supports_extract(), and separately retain stale configured values. Cover an unconfigured custom provider.
  • Replace exact-set assertions with capability-based invariants.

Automated hermes-sweeper review.

Comment thread hermes_cli/web_server.py
current-value preservation used for tts/stt/memory providers. Returns the
``base`` list unchanged when nothing needs appending.
"""
current = str(configured or "").strip()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only retains a custom backend after it is already configured. agent.web_search_registry.list_providers() exposes installed providers, but a closed select cannot choose an unconfigured one unless it is added here. Enumerate registry providers per capability and preserve configured-but-undiscoverable names separately.

from hermes_cli.web_server import CONFIG_SCHEMA

offered = {o for o in CONFIG_SCHEMA["web.extract_backend"]["options"] if o}
assert offered == {"firecrawl", "tavily", "exa", "parallel"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid freezing this current provider enumeration. Test the relation to provider capability flags instead, including a registered custom provider; AGENTS.md prohibits change-detector snapshots for evolving provider lists.

@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 30, 2026
@PRATHAMESH75
PRATHAMESH75 force-pushed the feat/config-form-web-backend-selects branch from b3dbc04 to 08b2d61 Compare July 31, 2026 15:48
…nfig Form selects (NousResearch#71929)

The Config page's Form view rendered web.backend, web.search_backend and
web.extract_backend as bare text inputs, forcing users to know exact backend
strings (e.g. 'brave-free', not 'brave') with no hint of valid values and no
validation.

Add _SCHEMA_OVERRIDES entries turning all three into selects, following the
existing terminal.backend/tts.provider pattern. Option lists mirror the
runtime: search/backend offer the built-in _LEGACY_WEB_BACKENDS set from
tools/web_tools.py; extract_backend offers only the extract-capable subset
(firecrawl, tavily, exa, parallel) since search-only providers are rejected
by the runtime. A leading '' keeps the shared-backend/auto-detect fallback
selectable.

Web backends are plugin-extensible and the dashboard renders a select as a
closed gate, so preserve a configured value outside the built-in list via
_schema_with_dynamic_provider_options (same current-value preservation used
for tts/stt/memory providers) — a plugin/custom backend never silently
vanishes from the dropdown.
@PRATHAMESH75
PRATHAMESH75 force-pushed the feat/config-form-web-backend-selects branch from 08b2d61 to daca098 Compare August 2, 2026 11:53
@PRATHAMESH75

Copy link
Copy Markdown
Contributor Author

Both points addressed in daca098a6.

1. Enumerate the registry, don't just retain the configured value. You're right — the old helper only preserved a configured backend, so a closed select could never reach an installed-but-unconfigured provider. _web_backend_schema_options now enumerates agent.web_search_registry.list_providers() per capability via a new _registry_web_backend_names(capability):

  • web.backend"any" (supports_search or supports_extract)
  • web.search_backend"search" (supports_search)
  • web.extract_backend"extract" (supports_extract)

Order is base built-ins → newly-discovered registry names → the configured-but-undiscoverable fallback (still preserved so switching away never drops it). The registry read is wrapped fail-safe: any import/registry error returns [] (built-in base stays the floor), and a single provider that raises on introspection is skipped, not fatal.

2. Test capability flags, not a frozen list. Replaced the snapshot approach with relation tests: test_dynamic_merge_enumerates_registry_web_backends_by_capability registers a search-only and an extract-capable fake provider and asserts the search-only reaches web.backend/web.search_backend but is kept out of web.extract_backend, while the extract-capable reaches web.backend/web.extract_backend. test_registry_web_backend_names_filters_by_capability drives the real helper end-to-end against a temporarily-register_provider'd backend (restored in finally). The no-overlay test now stubs enumeration to [] to isolate the "nothing new registered" invariant deterministically.

The two static tests (_web_backend_options_match_runtime_backends, _web_extract_backend_omits_search_only) I left as-is: they guard the import-time base list against tools/web_tools._LEGACY_WEB_BACKENDS drift — a real invariant, not a provider-enumeration snapshot, and unaffected by the dynamic registry merge. Full file green (121 passed), ruff clean.

@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

One PR addresses issue #71929. #71958 converts all three reported web backend fields from unrestricted text inputs to selects, adds capability-based discovery of installed registry providers, and preserves configured-but-undiscoverable values, but its tests still include the exact provider-set assertions challenged in review.

Related pull requests

Suggested consolidation

Keep #71958 open with a salvage path: retain the select overrides, capability-filtered registry enumeration, custom-provider discovery, and configured-value fallback, but replace the remaining exact built-in provider-set assertions with behavior/invariant coverage and obtain review clearance. There are no duplicate PRs to close.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I71929(["issue #71929 (open)"])
    P71958["PR #71958 (open)"]
    P71958 -->|best fix| I71929
    class I71929 open
    class P71958 open
    class P71958 best
    class P71958 target
    click I71929 "https://github.com/NousResearch/hermes-agent/issues/71929"
    click P71958 "https://github.com/NousResearch/hermes-agent/pull/71958"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 1 pull request and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 16 kB of PR diffs, 5 kB of issue/PR text, 5 kB of discussion (5 comments), 3 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades tool/web Web search and extraction type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: make web.backend / search_backend / extract_backend dropdown selects in Config Form view

4 participants