Skip to content

fix(session): persist provider on model switch and use it on resume - #79604

Closed
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:fix/79536-session-provider-persistence
Closed

fix(session): persist provider on model switch and use it on resume#79604
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:fix/79536-session-provider-persistence

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Summary

When a session falls back from the primary model to a fallback on a different provider, and the user then switches to that fallback model with /model, the session persisted only the model name in state.db — never its provider. On resume, the runtime recombined the persisted model with the primary provider from config.yaml (which doesn't serve that model), producing a guaranteed 404 on every turn.

Root Cause

update_session_model() (in hermes_state.py) wrote the model name to the sessions.model column but never wrote model/provider keys into the model_config JSON blob — the provider/model pair was lost across restarts.

Change

  • Write side (Option A): update_session_model(session_id, model, provider=None) now merges {"model": ..., "provider": ...} into the model_config JSON when a provider is passed (json_set/json_remove SQL preserving lineage markers and the browser_model_lock behavior). Both /model call sites in gateway/slash_commands.py pass the resolved target_provider.
  • Read side (Option B, defensive): in tui_gateway/server.py::_stored_session_runtime_overrides, when model_config has no top-level provider, fall back to model_config.gateway_runtime.provider (written by _sync_session_model_from_agent on the last turn) instead of the config.yaml primary provider. Precedence: top-level → gateway_runtimebilling_provider → default.

Verification

  • tests/test_hermes_state.py + tests/test_tui_gateway_server.py: 698 passed (pre-existing + new coverage for provider persistence and defensive resume).

Closes #79536

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/sessions Session lifecycle, resume, persistence, history P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 5, 2026
@webtecnica

Copy link
Copy Markdown
Contributor Author

Addressed the merge note from the #79811 review (thanks @teknium1 for flagging it): WHEN model_config IS NULL THEN NULL no longer drops the provider for the common NULL-blob case. Both call sites in update_session_model now create a fresh json_object('model', ?, 'provider', ?) (or json_object('model', ?) without provider) when the blob is NULL, so every session keeps its provider across the switch — including the common case. Committed as 0ea74480f. Ready to land.

kshitijk4poor pushed a commit that referenced this pull request Aug 14, 2026
Salvage of #79604 (webtecnica) + #85721 (pierrenode), combined and
rebased onto current main with simplify-code findings folded in.

#79604: update_session_model() wrote the model name to sessions.model
but never persisted the provider into model_config. On resume, the
runtime recombined the persisted model with the config.yaml primary
provider (which may not serve that model), producing auth errors.
Fix: add optional provider parameter to update_session_model, merged
into model_config via the shared _merge_model_config_json helper (not
hand-rolled SQL). Wire both gateway /model call sites to pass
result.target_provider.

#85721: session_gateway_runtime() had no billing_provider fallback.
A CLI session that never ran /model has no gateway_runtime or
top-level provider in model_config — billing_provider (written on
every session's first accounted API call) is the only durable record.
Fix: add billing_provider as the last-resort fallback in
session_gateway_runtime(), filtering bare billing buckets (auto/custom)
that are not routable identities.

Simplify-code findings addressed:
- Use _merge_model_config_json instead of 40 lines of branched SQL
- Share _BARE_BILLING_PROVIDERS from hermes_state.py (was duplicated
  as a set in tui_gateway/server.py)
- Merge None-filtering from #85920 with the billing_provider fallback
  into one coherent return path

Co-authored-by: pierrenode <298902573+pierrenode@users.noreply.github.com>
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Salvaged and merged in #85977 (combined with #85721, rebased onto current main, simplify-code findings folded in). Thank you for the original contribution!

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

Labels

area/sessions Session lifecycle, resume, persistence, history comp/gateway Gateway runner, session dispatch, delivery 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.

bug(persistence): update_session_model() doesn't persist provider in model_config → cross-provider fallback sessions 404 on resume

3 participants