Skip to content

fix(gateway): sync /model and /personality with CLI pattern - #150

Merged
teknium1 merged 1 commit into
NousResearch:mainfrom
VencentSoliman:fix/gateway-model-personality-commands
Feb 28, 2026
Merged

fix(gateway): sync /model and /personality with CLI pattern#150
teknium1 merged 1 commit into
NousResearch:mainfrom
VencentSoliman:fix/gateway-model-personality-commands

Conversation

@VencentSoliman

@VencentSoliman VencentSoliman commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

Issue

When a user runs /model on Discord (or any gateway platform), it
displays "anthropic/claude-opus-4.6" the hardcoded fallback (or nothing on accounts of other users) even
though the agent is actually using the model from config.yaml (e.g.
minimax-m2.5). Running /model reports success, but the next
message still uses whatever config.yaml says. Similarly, /personality
reports success but the personality never changes.

To reproduce:

  1. Set model.default to any model in ~/.hermes/config.yaml
  2. Start the gateway, run /model, shows the wrong model
  3. Run /model some-other-model, send a message , old model still used
  4. Run /personality pirate, send a message , no personality change

Fix

Align gateway /model and /personality command handlers with how
the CLI chat (hermes chat) already handles them. reading from and
writing to config.yaml instead of environment variables.

builds on the patterns established by @teknium1 in:

  • bc76a03 - where save_config_value and the CLI /model +
    /personality handlers were introduced with config.yaml as
    the source of truth
  • 55a0178 where the gateway's agent init was updated to re-read
    config.yaml per message.

The gateway command handlers were the last piece that hadn't caught
up to this pattern as they still wrote to env vars that the agent init
immediately overrides from config.yaml.

Changes

Not trying to intro new pattern/style, uses the same yaml read-modify-write
approach already present in the gateway's _handle_set_home_command.

  • /model display now resolves from config.yaml using the same chain
    as the agent init, instead of the HERMES_MODEL env var fallback
  • /model <name> persists to model.default in config.yaml (matching
    CLI's save_config_value("model.default", ...) at cli.py:1656)
  • /personality <name> writes to agent.system_prompt in config.yaml
    and updates the in-memory ephemeral prompt (matching CLI's
    save_config_value("agent.system_prompt", ...) at cli.py:1386)
  • Removes the orphaned HERMES_PERSONALITY env var write (zero
    consumers in the codebase)

Test plan

  • Make sure error is actually reproducible across different users
  • /model (no args) → should show model from config.yaml
  • /model other-model/model again → should show other-model
  • Send a message → verify new model is used
  • /personality pirate/new → send a message → pirate-style response
  • Check ~/.hermes/config.yaml after each → values persisted

Tested on: Linux (WSL2), Discord

@VencentSoliman
VencentSoliman force-pushed the fix/gateway-model-personality-commands branch from c95bd84 to 07fcb94 Compare February 27, 2026 22:43
@VencentSoliman

VencentSoliman commented Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

Rebased on main to stay current with the latest changes.

Saw the model display resolution in e09ef6b that config.yaml fallback is the right call. This PR picks up where that left off and covers the two remaining pieces:

Table 👇

Command Behavior on main What this PR adds
/model (no args) Reads config.yaml when env is empty ✅ (e09ef6b) already fixed
/model provider/name Writes to env var → overridden by config.yaml on next message ❌ Writes to config.yaml so the change persists ✅
/personality <name> Writes to HERMES_PERSONALITY env var → nothing reads it ❌ Writes to agent.system_prompt in config.yaml + updates in-memory prompt ✅

/model write side: The display is fixed, but /model provider/name still only sets os.environ["HERMES_MODEL"]. The agent init re-reads config.yaml on every message and overrides the env var, so the change silently reverts. This adds a config.yaml write using the same yaml read-modify-write pattern from the CLI's save_config_value.

/personality config persistence: Currently writes to os.environ["HERMES_PERSONALITY"], which nothing in the codebase reads. The CLI equivalent writes to agent.system_prompt in config.yaml and updates the in-memory prompt. This mirrors that exactly.

just bringing the gateway commands in line with how the CLI already handles these operations.

Do not merge if the intention for the /model command on gateways was for it to be a temp session-level override! 🙅‍♂️

@teknium1
teknium1 merged commit 306c670 into NousResearch:main Feb 28, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks!

@VencentSoliman
VencentSoliman deleted the fix/gateway-model-personality-commands branch February 28, 2026 10:11
angelburgosrosado pushed a commit to angelburgosrosado/hermes-agent that referenced this pull request Apr 27, 2026
…odel-personality-commands

fix(gateway): sync /model and /personality with CLI pattern
gizdusum pushed a commit to gizdusum/hermes-agent that referenced this pull request May 17, 2026
…odel-personality-commands

fix(gateway): sync /model and /personality with CLI pattern
teddyjfpender added a commit to teddyjfpender/superforecasting-agent that referenced this pull request Jun 24, 2026
… — error bars + agent nudge

Vote-share forecasts now express + render per-candidate uncertainty. The parallel
form (chosen for consistency with the live data, which already stores it): the payload
stays {candidate: share} (machine-scoreable as-is), and per-candidate 90% intervals
live in metadata.candidate_share_intervals_pp = {candidate: {p05, median, p95}}.

- dashboard._candidate_intervals normalizes that metadata to {candidate: {lo, mid, hi}}
  on the workspace item (None when absent/malformed; inverted lo>hi dropped).
- HistogramBar gains an optional `interval`; histogram() appends ` [lo–hi]` so each
  candidate bar shows its spread next to the point share. distributionBars attaches
  the interval per candidate by label.
- protocol nudge: a vote-share forecast should carry candidate_share_intervals_pp so
  the Desk draws an error bar per candidate — quantify each candidate's spread, not
  just a point share.

Tests: _candidate_intervals normalization (+ malformed -> None), distributionBars
interval attachment, histogram [lo–hi] suffix. TUI type-check + 98 chart/workspace
tests green; bundle rebuilt; forecasting dashboard/protocol regression green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
teddyjfpender added a commit to teddyjfpender/superforecasting-agent that referenced this pull request Jun 24, 2026
…high + 10 med + lows)

A multi-agent review of NousResearch#150/NousResearch#138/NousResearch#137/NousResearch#139 confirmed 21 findings. Fixed:

HIGH (NousResearch#139): forecast.theses ran inline on the gateway dispatch thread while doing
per-thesis N+1 work — it can stall interrupt/approval RPCs on a large book. Added it to
_LONG_HANDLERS (thread pool) + a routing regression test. Also: one shared ForecastLedger
per call (was two), error code 5008 (was a colliding 5021), _num excludes bool, and the
CLI dashboard now renders factors too (parity with the RPC/tool; matches its docstring).

NousResearch#150 (vote-share intervals): intervals silently dropped on the fraction-scale path
(the PMF branch never attached them) — now attached on both branches. Interval lookup
now tolerates case/whitespace divergence from the scorer/hook key normalization. And the
intervals (always percentage-points) are rescaled to the payload's scale, so a
fraction-scale share renders `0.70 [0.50-0.85]` not `0.70 [50-85]`.

NousResearch#138 (readiness benchmarks): evaluating with the default --last window or a --dataset
filter could hide the freshly-run suite (a closed gap looked reopened / zero closed) —
the improve path now evaluates over ALL runs, unfiltered. Dropped `naive` (can't beat
baselines, so it can't close the edge gap). The tool mirror wraps the run in try/except.

NousResearch#137 (cycle --agent): selection now filters on alert.scope_type == "question" (a
domain/topic/portfolio alert's scope_ref is not a question; this also keeps a
question-scoped domain_error_profile_applies trigger). --max-questions now counts
processed (expensive) LLM runs, not just commits, so it actually caps. --max-iterations
0 is honored (was coerced to 12).

Deferred (noted): a per-candidate central-within-interval COMMIT gate (a new backend
invariant mirroring central_within) — warrants its own pass. New tests cover every fix
above. Full tests/forecasting + tests/tools + gateway regression green (6834 passed);
TUI type-check + 99 chart/workspace tests green; bundle rebuilt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…odel-personality-commands

fix(gateway): sync /model and /personality with CLI pattern
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
… (DAN-2503) (NousResearch#150)

v0.20 regression from the fork sync: _select_under_lock() called
self._select_unlocked(model=model), but `model` is undefined in that
scope and _select_unlocked(self, *, refresh=True) does not accept a
`model` kwarg at all. pool.select() therefore raised
NameError: name 'model' is not defined.

Call chain: cron run_job -> resolve_runtime_provider -> pool.select()
-> _select_under_lock -> NameError. Every cron job that resolves a
runtime provider failed (fusion-watchdog, Signal Scanner Unified, KB
Research Unified, Config Integrity Watchdog, ...) — the whole cron fleet
was down post-v0.20-deploy.

Fix: return self._select_unlocked() (drop the dead kwarg). The model= arg
was leftover from an incomplete refactor in the upstream sync; no code path
supplies model here and _select_unlocked has no model parameter.

Co-authored-by: Claude <noreply@anthropic.com>
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
…-2503) (NousResearch#151)

Second v0.20 regression in the same incomplete "model lockouts" feature as
NousResearch#150. _available_entries() referenced `if model_lockouts_active: continue`
(line 1968) but model_lockouts_active is never defined anywhere in the
codebase — like the `model` kwarg in NousResearch#150, it was leftover from an
incomplete refactor that never wired model-aware selection through
select -> _select_under_lock -> _select_unlocked -> _available_entries
(none of which take a model param).

After NousResearch#150 fixed the first NameError (model), pool.select() advanced one
step and hit this one: NameError: name 'model_lockouts_active' is not
defined. Same root cause, same fix class: drop the dead check. Restores
pre-feature behavior (entries available; the lockout feature never worked,
so nothing is lost).

Verified on mfc1: hot-patched deployed checkout + restarted + triggered
fusion-watchdog -> "Ran now: succeeded." (cron fleet unblocked).

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants