Hermes Integration

June 5, 2026 · View on GitHub

Status: supported in v1.0.0+ · single-config-block · no Perseus code changes ever required after initial setup Audience: anyone running Hermes Agent who wants Perseus's LLM-augmented surfaces (Pythia oracle, Mnēmē narrative compaction, Daedalus drift detection) to route through Hermes instead of Ollama or a raw OpenAI endpoint.


TL;DR — 30 seconds

# 1. Start Hermes's OpenAI-compatible server (any port, this example uses 8080)
hermes proxy start --port 8080      # routes through your configured Hermes provider

# 2. Tell Perseus to use it (~/.perseus/config.yaml)
cat >> ~/.perseus/config.yaml <<'YAML'
llm:
  provider: hermes
  hermes_url: http://localhost:8080
  hermes_model: claude-sonnet-4.6   # or whatever Hermes is serving
  timeout_s: 60
YAML

# 3. Verify
perseus llm ping
# ✓ hermes · model=claude-sonnet-4.6 · http://localhost:8080 · 312 ms · 'pong'

That's the whole integration. From here, every --llm hermes flag on any Perseus command will route through Hermes.


Why this works without a Hermes-specific provider

Hermes Agent ships an OpenAI-compatible /v1/chat/completions server (Hermes README). Perseus's openai-compat provider already speaks that protocol. The hermes provider name is a friendly alias that:

  1. Reads from dedicated hermes_url / hermes_model config keys (so Hermes config can coexist with other openai-compat endpoints — e.g. a llamacpp box for cheap tasks).
  2. Defaults to http://localhost:8080 (Hermes's documented default port) instead of http://localhost:11434 (Ollama's port).
  3. Reserves the namespace for a future Hermes-native provider if/when Hermes adds non-standard extensions (auth headers, model picker over the wire, tool-gateway routing).

Today the alias is a thin shim. Tomorrow it can grow without breaking anyone's config.


Config reference

The full llm: block, with Hermes-specific keys highlighted:

llm:
  provider: hermes              # or: ollama, openai-compat, llamacpp, daedalus
  timeout_s: 60                 # default 30; bump for slow models

  # Hermes-specific (used when provider == hermes)
  hermes_url: http://localhost:8080     # base URL, NO /v1 suffix
  hermes_model: claude-sonnet-4.6       # any model Hermes is configured to serve

  # Fallback / shared keys (used by openai-compat, llamacpp, and as fallback for hermes)
  url: http://localhost:11434
  model: mistral

  # Daedalus (Perseus's fine-tuned local model, ollama-backed)
  daedalus_url: http://localhost:11434
  daedalus_model: perseus-daedalus

Gotcha: the url field is the base URL. Perseus appends /v1/chat/completions itself. If you set hermes_url: http://localhost:8080/v1 the request will hit http://localhost:8080/v1/v1/chat/completions and fail. Strip the /v1 suffix.


Which Perseus surfaces actually use the LLM?

Most of Perseus is deterministic and doesn't need an LLM at all. The LLM-augmented surfaces are:

SurfaceCommandLLM use
Pythia oracleperseus suggest "<task>" --llm hermesGenerates the recommendation when the deterministic rules can't pick a clear winner
Mnēmē compactperseus memory compact --llm hermesRewrites the narrative into tighter prose (deterministic falls back if LLM unavailable)
Mnēmē updateperseus memory update --llm hermesOptional polish over the deterministic distillation
Mnēmē queryperseus memory query "<question>" --llm hermesAnswers questions over the narrative (deterministic grep fallback otherwise)
Daedalus driftperseus oracle drift --llm hermesReserved for future LLM explanation; current drift reporting is deterministic/JSON-first

Everything else — rendering, checkpoints, federation, health, inbox, serve, agora — runs without ever touching an LLM. You can use Perseus end-to-end with no LLM configured at all.


perseus llm ping — the diagnostic command

perseus llm ping
perseus llm ping --provider hermes
perseus llm ping --provider hermes --url http://other-host:8080
perseus llm ping --provider hermes --model claude-opus-4.6

Sends a one-word prompt ("Reply with the single word: pong.") through the configured provider. Reports:

  • Success: ✓ <provider> · model=<model> · <url> · <ms> ms · '<preview>' (exit 0)
  • Transport failure: ✗ <provider> · <url> · <ms> ms · <error> (exit 2)
  • Unsupported provider: ✗ unsupported provider: <name> (exit 2)

Use it in scripts, CI, or @health checks. Use it before opening an issue — if ping fails, the bug is in your environment; if ping succeeds but suggest doesn't, the bug is in Perseus.


Per-invocation overrides

Every Perseus command that takes --llm also takes --model and --model-url for ad-hoc routing without editing config:

# One-off route to a different Hermes box
perseus suggest "debug failing test" \
  --llm hermes \
  --model-url http://gpu-box.lan:8080 \
  --model deepseek-coder-v3

# Route Mnēmē compaction to a heavier model than your default
perseus memory compact --llm hermes --model claude-opus-4.6

These flags do not persist; for permanent changes, edit ~/.perseus/config.yaml.


Auth (or lack thereof)

Today: Perseus does not send an Authorization header. This matches Hermes's localhost-only default posture: Hermes binds to 127.0.0.1 with no auth, and Perseus on the same box reaches it with no auth.

If you put Hermes behind a reverse proxy with auth: Perseus needs a code change (one field added to run_llm's Request(...) construction). File an issue tagged provider-hermes-auth and it'll get scoped as a small task. The hook is intentionally trivial — auth header support is ~10 LoC + one test.


Cross-host topology

The example assumes Perseus and Hermes on the same machine. They don't have to be:

TopologyHermes sidePerseus side
Same boxhermes proxy start --port 8080hermes_url: http://localhost:8080
Mac → Linux box on LANhermes proxy start --host 0.0.0.0 --port 8080hermes_url: http://hermes-box.lan:8080
Mac → Linux via SSH tunnelhermes proxy start --port 8080 (on remote) + ssh -L 8080:localhost:8080 user@remotehermes_url: http://localhost:8080
Mac → Linux via Tailscale/WireGuardhermes proxy start --host 0.0.0.0 --port 8080hermes_url: http://<tailscale-ip>:8080

The SSH-tunnel option is the simplest secure default — Hermes stays bound to localhost on the remote, the tunnel handles authn, and Perseus's config doesn't need to know where Hermes actually lives.


Federation + Hermes

@memory federation and perseus memory federation * are LLM-free. Subscriptions read narrative files directly from disk; no model is invoked. If you want LLM-summarized federation digests, treat that as generator/curator work and file an issue tagged federation-llm-digest.


Failure modes

SymptomLikely causeFix
ping returns connection refusedHermes not running, wrong porthermes proxy start --port 8080 and check lsof -i :8080
ping returns 404 Not Foundhermes_url has /v1 suffixStrip it — Perseus appends the path itself
ping returns Model not foundhermes_model not configured in Hermeshermes model to see what's available
ping returns timeoutModel is slow (large prompts, GPU offload)Bump llm.timeout_s to 120 or higher
ping returns empty/> ⚠ LLM returned no response.Hermes returned 200 but no choices[0].message.contentCheck Hermes logs; usually a provider misconfig on Hermes's end
suggest --llm hermes falls back to deterministicSame as above — but Pythia is graceful by designRun ping to isolate; check ~/.perseus/pythia_log.jsonl

Operational notes

  • Mnēmē auto-update at checkpoint time is deterministic-only by design. The LLM path runs only when explicitly requested via --llm or when memory.llm_provider is set in config. This means perseus checkpoint never blocks on a network round-trip even if Hermes is misconfigured.
  • Pythia log entries created with --llm hermes record provider: hermes and model: <resolved-model> in the log, so retrospective dataset exports (perseus oracle export) can filter by provider for training-data curation.
  • Mixing providers per command is fine. perseus suggest ... --llm hermes and perseus memory compact --llm ollama in the same shell coexist without state issues.

Roadmap touchpoints

  • Daedalus v2perseus oracle drift is implemented as a deterministic/JSON-first surface. Future LLM explanations can use perseus llm ping as a precondition.
  • LSP — the LSP server (perseus serve --lsp) is implemented. Surfacing llm.provider and recent ping state in editor UI remains an optional editor enhancement.
  • Future team mode — if/when Perseus grows a server mode for shared federation, Hermes will likely be one of the first inference paths supported via the same alias, with auth headers added.

See also

  • Hermes Agent — README
  • Perseus Deployment Guide — full ecosystem setup (Bastra, LLM proxy, cron jobs)
  • Unraid Notes — Unraid-specific operational constraints (no crontab, no systemd)
  • Perseus spec/components.md § 4 (Mnēmē) and § 6 (Pythia) for the LLM-augmented surfaces
  • Perseus README.md § "Configuration" for the full llm: block

Context-file wiring (the @perseusAGENTS.md render pipeline that feeds into Hermes Agent) is documented in the Setup & Configuration Guide, not here. This file covers only LLM proxy routing.