Skip to content

feat(hermes): Hermes-only mode — run with no Open WebUI server#522

Draft
cogwheel0 wants to merge 3 commits into
feat/hermes-agentfrom
feat/hermes-only-mode
Draft

feat(hermes): Hermes-only mode — run with no Open WebUI server#522
cogwheel0 wants to merge 3 commits into
feat/hermes-agentfrom
feat/hermes-only-mode

Conversation

@cogwheel0

@cogwheel0 cogwheel0 commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

Makes Open WebUI optional so a user can install the app and use a self-hosted Hermes Agent exclusively — no OWUI server at all. The mode is derived from what's configured and is switchable anytime; first run shows a backend chooser.

Stacked on feat/hermes-agent (#521) — review that one first; this PR's diff is the Hermes-only delta.

Design — two signals

  • preferredBackendProvider (lib/core/providers/backend_mode_providers.dart) — a synchronous, persisted Notifier<PreferredBackend> (unset | owui | hermes). Read by the router only, for boot determinism — mirrors reviewerModeProvider's synchronous role. On cold boot the router runs redirect() while activeServerProvider is still loading and Hermes secrets are still loading, so a purely-derived gate would bounce a Hermes-only user to server-connection. The flag avoids that; real gating stays derived.
  • hermesOnlyModeProvider (derived, in hermes_providers.dart) — !reviewerMode && hermesConfig.isUsable && activeServer == null. Drives UI gating; widgets rebuild as it settles.

What's included

  • Onboarding: new BackendChooserPage (Connect to Open WebUI / Use a Hermes Agent) + Routes.backendChooser. Reuses HermesSettingsPage(isOnboarding: true) with a "Finish setup" button that validates isUsable, sets preferredBackend = hermes, and enters the app.
  • Router: Hermes-only short-circuits straight to chat before any OWUI server/auth branch; holds splash (not server-connection) while Hermes secrets load; first run → chooser. Disabled-Hermes safety net avoids a stuck splash.
  • Models: Models.build / defaultModelProvider surface + auto-select the synthetic Hermes model when there's no OWUI API. Send guard relaxed (extracted to a pure isSendBlocked() helper) to allow api == null for Hermes models.
  • UI gating: hide chats/notes/terminal/channels across the 3 synchronized tab sites — Hermes becomes the home tab. preferredBackend = owui is set on successful OWUI connect.
  • Composer gating: when a Hermes model is active, hide the OWUI affordances — the "+" overflow button, quick pills, and the iOS keyboard-accessory actions (Hermes has no OWUI tools/web-search/image-gen/attachments and uses its own / skills).
  • iOS native settings sheet (100% data-driven from Dart, no Swift changes): gate the OWUI account sections (profile, AI memory, data connection, password + profile detail sheets, sign-out) on Hermes-only; render a Hermes-branded profile header (name "Hermes Agent", agent host, hermes_agent.png avatar); the About detail skips the server lookup so it no longer errors with no server.
  • Bidirectional switching: a "Connect to Open WebUI" entry in both the native sheet (routed via a control event in main.dart) and the Flutter profile page; the router reaches the OWUI connect/auth routes for a Hermes-only user; preferredBackend resets to unset when a Hermes-only backend is disabled.

Verification

  • flutter analyze clean; flutter test (2218) passing — incl. unit tests for preferredBackend parse/persistence, hermesOnlyMode derivation (usable/no-server, server-present, disabled, incomplete, reviewer precedence), isSendBlocked (the send-guard relaxation), and modelsProvider/defaultModelProvider Hermes surfacing + auto-select when api == null.
  • On-device E2E (erased iOS simulator, fresh install, live Hermes server): backend chooser → "Use a Hermes Agent" → enter URL/key (capabilities + toolsets load) → Finish → lands in Hermes-only chat (header "Hermes Agent", no model dropdown) → sidebar shows only the Hermes tab → kill/relaunch boots straight into Hermes chat (boot determinism) → send created a new server-side session with no OWUI backend.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eb70e6a2-8138-4f4a-836a-9b31e71200aa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/hermes-only-mode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cogwheel0 cogwheel0 force-pushed the feat/hermes-only-mode branch from d909c37 to 42aac43 Compare June 20, 2026 14:21
Make Open WebUI optional so a user can install the app and use a self-hosted
Hermes Agent exclusively. The mode is derived from what's configured and is
switchable; first run shows a backend chooser.

- Mode signals: a synchronous persisted `preferredBackend` (unset|owui|hermes)
  for boot-deterministic routing, plus a derived `hermesOnlyMode` for UI gating
  (mirrors the reviewer-mode precedent).
- Router: Hermes-only short-circuits to chat with no OWUI server/auth; first run
  routes to a new backend chooser; holds splash while Hermes secrets load.
- Onboarding: BackendChooserPage + HermesSettingsPage(isOnboarding) with a
  Finish button that sets preferredBackend=hermes and enters the app.
- Models/defaultModel surface + auto-select the synthetic Hermes model when
  there's no OWUI API; send guard relaxed for Hermes models.
- UI gating: hide chats/notes/terminal/channels (3 synced sites); Hermes is the
  home tab. preferredBackend=owui set on OWUI connect.

Verified end-to-end on an erased iOS simulator (fresh install): chooser →
Hermes setup → Finish → Hermes-only chat → only the Hermes tab → relaunch boots
straight to chat → send creates a server session with no OWUI.
flutter analyze clean; flutter test (2200) passing.
…, tests

Address the Hermes-only follow-ups:

- Composer: hide OWUI affordances (the "+" overflow button, quick pills, and the
  iOS keyboard-accessory actions) when a Hermes model is selected — Hermes has no
  OWUI tools/web-search/image-gen/attachments and uses its own `/` skills.
- iOS native settings sheet: gate the OWUI account sections (profile header,
  AI memory, data connection, password + profile detail sheets, sign-out) on
  Hermes-only, and make the About detail skip the server lookup so it no longer
  errors with no server.
- Bidirectional switching: add a "Connect to Open WebUI" entry in both the native
  sheet (routed via a control event in main.dart) and the Flutter profile page;
  let the router reach the OWUI connect/auth routes for a Hermes-only user; reset
  preferredBackend to unset when a Hermes-only backend is disabled.
- Tests: preferredBackend parse/persistence round-trip and hermesOnlyMode
  derivation (usable/no-server, server-present, disabled, incomplete, reviewer
  precedence).

flutter analyze clean; flutter test (2211) passing.
…tests

Close out the last Hermes-only follow-ups:

- iOS native settings sheet: render a Hermes-branded profile header in
  Hermes-only mode ("Hermes Agent" + the agent host + the hermes_agent.png
  avatar). Fully data-driven from Dart — no Swift change; the avatar bytes are
  pre-loaded (cached) before the sync config builder runs.
- Extract the inline send/regenerate guard into a pure isSendBlocked() helper
  used at both sites, removing the duplicated condition and making the
  Hermes-only relaxation unit-testable.
- Tests: isSendBlocked (null model, OWUI-no-api, api present, reviewer, Hermes
  relaxation); modelsProvider surfaces only the synthetic Hermes model when
  unauthed + usable Hermes; defaultModelProvider auto-selects + writes through
  the Hermes model when api == null.

flutter analyze clean; flutter test (2218) passing.
@cogwheel0 cogwheel0 force-pushed the feat/hermes-only-mode branch from 42aac43 to 9695628 Compare June 20, 2026 15:09
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.

1 participant