Skip to content

fix(bot-mode): bot sessions always follow the profile's current config - #90343

Closed
curator8888 wants to merge 2 commits into
NousResearch:mainfrom
curator8888:fix/room-plumbing-follows-profile-config
Closed

fix(bot-mode): bot sessions always follow the profile's current config#90343
curator8888 wants to merge 2 commits into
NousResearch:mainfrom
curator8888:fix/room-plumbing-follows-profile-config

Conversation

@curator8888

@curator8888 curator8888 commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Fixes the #89497 bug class: bot sessions stuck on a stale provider pin after a profile switch.

Summary

Bot-Mode sessions (room plumbing AND the canonical per-bot DM chat) are plugin-owned scratch conversations. They are now created with explicit contracts — room_plumbing for group-chat member sessions, follow_profile_config for the canonical Bot Chat — persisted in the session row's model_config, so session.resume rebuilds from the member profile's CURRENT config instead of restoring the stored model/provider pin from an old row.

That stale pin is what left bots stuck on a dead provider (e.g. "out of Nous credits" after the profile was switched to ollama-cloud) while the same bot worked fine elsewhere. Normal 1:1 user chats keep the stored-runtime restore: opening an older chat must show the model it actually used.

How to Test

  1. Configure a bot profile with model A (e.g. a paid Nous model), open its Bot Chat DM, send a message.
  2. Switch the profile to model B (e.g. ollama-cloud/glm-5.1).
  3. Reopen the DM (and/or the room). Before this fix: resume restores the stored model A pin → "out of Nous credits" / HTTP 404. After: resume uses the profile's current model B.

Verification

  • Backend: _stored_session_runtime_overrides returns {} for rows carrying either marker (dict and JSON model_config shapes); unmarked rows still restore stored runtime; _ensure_session_db_row persists each marker only when the contract is set. Legacy hidden + "Group:" title rows keep the room guard.
  • Desktop: createCanonicalChat sends follow_profile_config: true; ensureGroupChatSession sends both room_plumbing: true and follow_profile_config: true on session.create.
  • Tests: 29/29 test_custom_provider_session_persistence.py + test_session_resume_db_ownership.py, 42/42 desktop group-chat tests.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective
  • New and existing unit tests pass locally

…rent config

Room member sessions in Bot Mode are per-member scratch conversations
inside a group chat. session.resume restored their stored model/provider
pin from the row's model_config, so a room bot stayed stuck on whatever
provider was pinned when the row was first written — even after the
profile was switched. Every room message then failed on the stale
provider (e.g. 'out of Nous credits' after switching a profile from
Nous to ollama-cloud) while the same bot worked fine in DMs.

Add an explicit room_plumbing contract:
- session.create accepts room_plumbing: true, persisted in model_config
- _stored_session_runtime_overrides() returns {} for marked rows, so a
  room session always rebuilds from the member profile's CURRENT config
- hidden + 'Group:' title shape is kept as a legacy fallback for rows
  created by older desktop builds that never sent the marker; hidden
  non-room chats keep the stored-runtime restore
- Desktop Bot Mode sends room_plumbing: true when creating the hidden
  per-member room sessions

Fixes NousResearch#89497
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 19, 2026
…config

Bot-Mode canonical chats (the ONE forever DM per bot) and room plumbing
sessions are plugin-owned scratch conversations. They are now created with
an explicit follow_profile_config contract, persisted in the session row's
model_config, so session.resume rebuilds from the member profile's CURRENT
config instead of restoring the stored model/provider pin from an old row.

That stale pin is what left bot DMs stuck on a dead provider (e.g. 'out of
Nous credits' after the profile was switched to ollama-cloud) while the
same bot worked fine in rooms — the mirror image of the room-plumbing bug
(NousResearch#89497 class). Normal 1:1 user chats keep the stored-runtime restore:
opening an older chat must show the model it actually used.

- tui_gateway/methods_session.py: accept follow_profile_config on session.create
- tui_gateway/server.py: persist the marker in the row; skip stored-runtime
  overrides on resume when present
- apps/desktop/src/plugins/hermes-bots/plugin.js: send the contract from
  createCanonicalChat and ensureGroupChatSession
- tests: backend override + row-persist coverage; desktop source-contract
  coverage for both session kinds
@curator8888 curator8888 changed the title fix(bot-mode): room plumbing sessions always follow the profile's current config fix(bot-mode): bot sessions always follow the profile's current config Aug 20, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference; please use your judgment.

Reviewed by reviewer-e (AI automated review).

Right contract design: replacing a presentation heuristic's scope creep with an explicit, persisted follow_profile_config/room_plumbing marker (set by session.create consumers, stamped into model_config by _ensure_session_db_row, honored by _stored_session_runtime_overrides) fixes the "bot DMs stuck on Nous credits" class at the identity level instead of guessing from titles. Keeping the hidden+"Group:" shape as a legacy fallback only, and leaving normal 1:1 stored-runtime restore untouched (including hidden non-room chats), is exactly the narrow behavior you want — and the test matrix covers marker-in-dict/marker-in-JSON/legacy-shape/unmarked-row/stamp-and-omit for both contracts.

Nit (non-blocking): tui_gateway/server.py:4154-4207 — the room_plumbing and follow_profile_config checks are two ~20-line near-identical copies of "parse model_config (dict or JSON string), read flag" plus duplicated comments; a tiny _model_config_flag(row, key) -> bool helper would collapse them, remove the double parse of the same JSON blob, and keep comment drift from reintroducing subtle differences between the two contracts.

@KeyArgo

KeyArgo commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Fresh user report validating this exact fix: #94818 (2026-08-25) reports that switching the model for a Bot Chat in settings has no effect — the chat keeps using the model pinned at session creation ("Model used during chat creation is model that is continued to use"). That's precisely the stale model/provider pin your follow_profile_config contract addresses on session.resume. Flagging as corroboration; this fix looks like the right home for that report.

teknium1 added a commit that referenced this pull request Aug 28, 2026
…Bot Chats

Bot Chats created before the follow_profile_config marker existed carry no
contract in model_config, so they would stay pinned to a stale stored
provider until deleted — the exact shape of the live reports (#89497,
#94818). Mirror the plugin's own identity rule (the profile's session
titled exactly 'Bot Chat') as a legacy fallback in
_stored_session_runtime_overrides, matching the room-plumbing legacy
'Group:' title fallback.

Follow-up to the salvaged #90343 (@curator8888) and #96111 (@lorzl).
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #97008 (rebase-merge) — both of your commits landed on main with your authorship preserved in git log. We added one follow-up on top: a legacy backfill so canonical Bot Chats created BEFORE the follow_profile_config marker existed (rows titled exactly "Bot Chat") also follow the profile's current config — that covers users already stuck in the field. Thanks for the clean explicit-contract design.

@teknium1 teknium1 closed this Aug 28, 2026
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…Bot Chats

Bot Chats created before the follow_profile_config marker existed carry no
contract in model_config, so they would stay pinned to a stale stored
provider until deleted — the exact shape of the live reports (NousResearch#89497,
NousResearch#94818). Mirror the plugin's own identity rule (the profile's session
titled exactly 'Bot Chat') as a legacy fallback in
_stored_session_runtime_overrides, matching the room-plumbing legacy
'Group:' title fallback.

Follow-up to the salvaged NousResearch#90343 (@curator8888) and NousResearch#96111 (@lorzl).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants