Conversation
_runtime_model_config merges the agent's current identity onto the row's existing model_config JSON. For model and provider it only SET the key when the agent attribute was truthy, while base_url/api_mode/ reasoning_config/service_tier already deleted stale values when falsy. When an agent rebuilt with an empty provider (inheriting the profile default) was persisted, the previous provider/endpoint survived in model_config while _persist_live_session_runtime updated the model column separately. Resume then read the fresh model from the column but the STALE provider from model_config, silently routing the resumed chat to the wrong endpoint (e.g. a VeniceAI/empero route under a model that should run on the profile default). Apply the same delete-on-falsy rule to model and provider, mirroring the or-None deletion the CLI path (_persist_model_switch_to_session) already uses, so a stale session state can never survive into a resume override. Existing desynced rows self-heal on the next live metadata persist. Adds regression tests: merge drops stale provider/model when the agent attribute is falsy, a truthy provider overwrites the stale value, resume overrides fall back to the billing provider instead of the stale endpoint, a real-DB round trip heals an already-desynced row, and a first write (existing=None) reflects only the agent's current identity.
e2bbf25 to
ea15f7f
Compare
|
Author's note — post-revision scope verification (single-commit state) After trimming the branch to the surgical fix (registry/helper removed), I re-verified the exact behavioral scope. Three things worth flagging for maintainers: 1. The The Corollary: 2. Unknown 3. The nested Verified: 128 passed (17 in the persistence file; |
QA review summary (Team6 QA pass on this PR)Reviewed against Substance: correct and well-scoped.
Tests: good behavior contracts, not snapshots.
Verification I ran:
One housekeeping note (already corrected in the PR body):
Scope is tight (bugfix only; the bulk-reset CLI is correctly kept in a separate issue #96745). No speculative infrastructure, no new env vars, no cache-breaking changes. Mergeable as-is. |
Adopt reviewed ideas from upstream PRs NousResearch#96251, NousResearch#96498, NousResearch#96748, NousResearch#96884, and NousResearch#96885 with local integration tests. Co-authored-by: loulanyue <260355617@qq.com> Co-authored-by: 686f6c61 <github@00b.tech> Co-authored-by: ahrazzle <ahraz.arifuddin@gmail.com> Co-authored-by: Agi-Asi <Agi-Asi@users.noreply.github.com>
|
Merged via PR #97066 (rebase-merge) — your commit landed on main with your authorship preserved in git log. The gateway metadata writer now drops stale model/provider keys with the same or-None semantics as the CLI path, so a falsy agent identity can no longer leave a wrong-but-routable provider in the session row. Your regression tests (including the real-DB desync heal) came along intact. Thanks for catching the write-side half of this bug class — the resume-side fix alone (#97008) would have left it reachable. |
What
_runtime_model_config(TUI/desktop gateway persist path) merges the agent's current identity onto the row's existingmodel_configJSON. Formodelandproviderit only SET the key when the agent attribute was truthy — whilebase_url/api_mode/reasoning_config/service_tieralready deleted stale values when falsy. When an agent rebuilt with an empty provider (inheriting the profile default) was persisted, the previous provider/endpoint survived inmodel_configwhile_persist_live_session_runtimeupdated themodelcolumn separately.On resume,
_stored_session_runtime_overridesreads the model from the column but the provider/endpoint frommodel_config— so the chat silently routed to the stale provider (e.g. a VeniceAI/empero endpoint) under a model that should run on the profile default.This is the same stale-key bug class the CLI path already fixed with or-None deletion in
_persist_model_switch_to_session(seetests/cli/test_resume_model_restore.py::test_persist_model_switch_clears_stale_route_keys); the gateway writer never got the symmetric treatment. The fix applies delete-on-falsy tomodelandprovidertoo, matching the existing handling of the other identity keys.Evidence (real incident)
During a bulk provider rotation across 8 profiles / 174 chat sessions, the audit found 4 live rows where
sessions.modelwas alreadydeepseek/deepseek-v4-flash-0731butmodel_configstill carriedprovider: stealth-ox-alpha(VeniceAI) /emperowithbase_url+api_mode. Config.yaml was correct; the desync was purely in the session rows, and would have routed those resumes to the wrong endpoint.Reproduction
Tests
New
TestRuntimeModelConfigDropsStaleKeysintests/tui_gateway/test_custom_provider_session_persistence.py:existing=None) reflects only the agent's current identity.Verified green on the touched surface:
17 passedin the target file, plustests/cli/test_resume_model_restore.py(22) andtests/state/(89) — this change introduces no new failures. The target and sibling suites exercise the real resume path (realSessionDB, tempHERMES_HOME), not mocks.Existing desynced rows
Self-heal on the next live metadata persist (any
/modelswitch or reconnect writes through_runtime_model_config). No migration needed.Related: #96745 (CLI for bulk session model audit/reset — the tooling gap this incident exposed).