fix(agent): re-sync model identity when LM Studio swaps model mid-session - #58880
fix(agent): re-sync model identity when LM Studio swaps model mid-session#58880ryanda9910 wants to merge 1 commit into
Conversation
…sion The system prompt's Model:/Provider: identity lines are built once at session start and cached. For LM Studio the loaded model can change server-side (the user swaps it in the LM Studio app), but nothing re-syncs the cached identity, so the agent keeps reporting the original model when asked about its inference engine. Only a new session picks up the change. Every chat-completion response echoes the model that actually served it, so after a successful LM Studio response, compare response.model against agent.model and, when it genuinely diverges, adopt the live name and rewrite the cached identity via the existing rewrite_prompt_model_identity helper (the same in-place, non-persisted rewrite used for provider failover). Scoped to the lmstudio provider so no other provider's identity is touched. Fixes NousResearch#54454
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Fix resyncs model identity when LM Studio swaps models mid-session. Prevents stale model identity from causing downstream issues. Single-file change (107 additions, 1 deletion), well-scoped.
Reviewed by Hermes Agent
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the focused LM Studio report and the existing slug-match coverage. The underlying stale-identity problem is still present on current main: agent/system_prompt.py:507-509 embeds the runtime identity, and agent/turn_context.py:368-371 reuses that cached prompt.
Problems
- The new call is after the completed API response (
agent/conversation_loop.py:2270in this diff). That response was already generated from the stale system prompt, so the first identity question after a swap is not corrected. - The call also follows token persistence, which records
model=agent.modelbefore the proposed update (agent/conversation_loop.py:2276on current main). - The helper rewrites
_cached_system_promptbut not the loop-localactive_system_prompt. Current failover explicitly synchronizes both through_sync_failover_system_message(agent/conversation_loop.py:511-534), which is needed for tool-loop follow-ups.
Suggested changes
- Rework this around a pre-request active-model discovery path, or explicitly scope the contract to subsequent requests.
- Add an integration test covering the response ordering and a tool-call continuation, rather than only direct helper tests.
Automated hermes-sweeper review.
| # session is open; the response echoes the live model, so re-sync | ||
| # the cached identity when it drifts (#54454). | ||
| if agent.provider == "lmstudio": | ||
| try: |
There was a problem hiding this comment.
This runs after the response has already been generated, so it cannot correct the first “what model are you?” answer after an LM Studio swap. It also follows the token-persistence block, which has already recorded the completed response under the previous agent.model. Please move discovery/synchronization to a pre-request path (or narrow the behavior contract) and cover that ordering with a loop-level test.
What does this PR do?
With LM Studio (a local OpenAI-compatible provider), the loaded model can be swapped from the LM Studio app while a Hermes session stays open. When that happens, asking the agent which inference engine it is running keeps returning the model that was loaded at session start. Only a brand-new session picks up the change.
Root cause. The system prompt's
Model:/Provider:identity lines are built once at session start fromagent.modeland cached for the life of the session (for prefix-cache stability). When the user answers "what model are you", the model reads those cached lines.agent.modelis resolved from LM Studio's loaded model at startup, which is why a new session reports correctly, but nothing re-syncs the cached identity when LM Studio swaps the model underneath a live session.Fix. Every chat-completion response echoes the model that actually served it (
response.model). After a successful LM Studio response, compare that live name againstagent.model; when it genuinely diverges, adopt the live name and rewrite the cached identity via the existingrewrite_prompt_model_identityhelper — the same in-place, non-persisted rewrite already used for provider failover, so the stored prompt is untouched and the prefix cache stays coherent when the primary is restored. Scoped to thelmstudioprovider so no other provider's identity is touched. LM Studio's native API returns models aspublisher/slug; the comparison uses the existing_model_id_matchesso a slug-vs-basename form of the same model is not treated as a drift.Related Issue
Fixes #54454
Type of Change
Changes Made
agent/chat_completion_helpers.py: addsync_lmstudio_active_model(agent, response)— LM Studio-scoped, reusesrewrite_prompt_model_identity+_model_id_matches.agent/conversation_loop.py: call it once per successful API response on the LM Studio path (guarded, best-effort).tests/agent/test_failover_identity.py: 5 new cases (adopt-on-swap, no-op unchanged, no-op on slug-vs-basename, no-op for non-lmstudio provider, no-op when response has no model).How to Test
gemma-3-4b); start a Hermes session and ask which model it is → reports gemma.qwen3-6b.Automated:
Checklist
Code
pytest tests/agent/test_failover_identity.py -q)Documentation & Housekeeping
Notes
The change is scoped to the
lmstudioprovider and reuses the existingrewrite_prompt_model_identityhelper (the same in-place, non-persisted rewrite already used for provider failover), so the stored prompt and prefix cache stay coherent.One honest caveat: I verified this with unit tests and by tracing the code path, not with a live LM Studio model swap on my machine, so the interactive step under "How to Test" is written from the code rather than a captured session. The 5 new cases in
test_failover_identity.pycover the divergence + adopt +publisher/slugmatching, and the relatedtest_model_switch_context_display.py/test_model_metadata.pysuites still pass (125 total), so the behaviour is exercised end-to-end at the unit level.Happy to adjust the hook point (currently once per successful response on the lmstudio path) or the comparison per your preferences.