Skip to content

Pin the provider/model to each session and restore it on switch - #321

Merged
plombeer31 merged 1 commit into
mainfrom
feat/per-session-model
Sep 3, 2026
Merged

Pin the provider/model to each session and restore it on switch#321
plombeer31 merged 1 commit into
mainfrom
feat/per-session-model

Conversation

@plombeer31

Copy link
Copy Markdown
Collaborator

Problem

The active text provider and its chat model are a single global setting, so every session silently follows whatever was last picked. Run one project on openrouter/z-ai/glm-5.2 and another on local-llama, switch between their sessions — and both quietly use the same model. A session has no memory of what it was driven with.

Change

Each session now records the provider/model it runs on, and switching into a session re-applies it:

  • Stamp: executeTurn writes metadata.llm = { providerId, chatModel } with the post-turn save, resolved from the live config at turn start (deliberately not the fallback chain's emergency substitute — the session should remember the operator's choice, not the outage). Every origin funnels through executeTurn, so TUI/scheduler/Telegram/HTTP turns all stamp.
  • Stamp on pick: the TUI also stamps the open session the moment a model is chosen in the LLM panel (providers_select_chat_model / providers_set_active_text), so a choice made between turns survives switching away before the next message.
  • Restore: switchSession compares the target session's stamp against the active config (planModelRestore, a pure function) and, when they differ, re-applies it by emitting the LLM panel's own bus actions — config persistence, provider reload and panel refresh all happen in the code path that already owns them. A transcript line says what was restored.
  • Degradation: a stamped provider that has since been removed changes nothing and says so; sessions without a stamp (pre-existing rows) keep the current model; malformed metadata reads as "no stamp", never a crash.

Scope note: model resolution stays global at runtime — the restore happens when you enter a session. A scheduled/background turn on another session still uses the currently active model; per-turn model plumbing through the engine would be a much larger change and is intentionally out of scope here.

Tests

  • readSessionLlmStamp: well-formed / model-less / malformed metadata
  • planModelRestore: no stamp, already active, cross-provider select, legacy model field, model-less activate, deleted provider
  • runtime integration: a turn stamps the stored session with the active provider

The active text provider + chat model are one global config setting, so
every session silently followed whatever the operator last picked —
switch from an OpenRouter/glm thread into a local-llama thread and the
OpenRouter model kept serving it.

- executeTurn stamps metadata.llm = { providerId, chatModel } (resolved
  from the live config at turn start, not the fallback chain's
  substitute) onto the session with its post-turn save.
- The TUI also stamps the open session the moment the operator picks a
  model, so a choice made between turns survives switching away.
- switchSession re-applies the target session's stamp when it differs
  from the active model, through the LLM panel's own bus actions
  (providers_select_chat_model / providers_set_active_text) so config
  persistence + provider reload + panel refresh stay in the one place
  that owns them. A stamped provider that was removed changes nothing
  and says so; sessions without a stamp keep the current model.
- planModelRestore/readSessionLlmStamp are pure and unit-tested;
  malformed metadata degrades to "no stamp", never a crash.
@plombeer31
plombeer31 merged commit fc6f917 into main Sep 3, 2026
2 checks passed
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