Skip to content

fix(agent): re-sync model identity when LM Studio swaps model mid-session - #58880

Open
ryanda9910 wants to merge 1 commit into
NousResearch:mainfrom
ryanda9910:fix/lmstudio-model-report-54454
Open

fix(agent): re-sync model identity when LM Studio swaps model mid-session#58880
ryanda9910 wants to merge 1 commit into
NousResearch:mainfrom
ryanda9910:fix/lmstudio-model-report-54454

Conversation

@ryanda9910

@ryanda9910 ryanda9910 commented Jul 5, 2026

Copy link
Copy Markdown

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 from agent.model and 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.model is 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 against agent.model; 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 already used for provider failover, so the stored prompt is untouched and the prefix cache stays coherent when the primary is restored. Scoped to the lmstudio provider so no other provider's identity is touched. LM Studio's native API returns models as publisher/slug; the comparison uses the existing _model_id_matches so a slug-vs-basename form of the same model is not treated as a drift.

Related Issue

Fixes #54454

Type of Change

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

Changes Made

  • agent/chat_completion_helpers.py: add sync_lmstudio_active_model(agent, response) — LM Studio-scoped, reuses rewrite_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

  1. Start LM Studio serving a model (e.g. gemma-3-4b); start a Hermes session and ask which model it is → reports gemma.
  2. Without restarting Hermes, swap the loaded model in LM Studio to e.g. qwen3-6b.
  3. Ask again which model it is → now reports qwen (previously still reported gemma until a new session).

Automated:

pytest tests/agent/test_failover_identity.py -q          # 14 passed (5 new)
pytest tests/hermes_cli/test_model_switch_context_display.py tests/agent/test_model_metadata.py -q   # 125 passed
ruff check agent/chat_completion_helpers.py agent/conversation_loop.py tests/agent/test_failover_identity.py   # clean

Checklist

Code

Documentation & Housekeeping

  • N/A — no docs/config/schema/tool changes
  • Cross-platform: pure Python string/attribute logic, no platform-specific code

Notes

The change is scoped to the lmstudio provider and reuses the existing rewrite_prompt_model_identity helper (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.py cover the divergence + adopt + publisher/slug matching, and the related test_model_switch_context_display.py / test_model_metadata.py suites 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.

…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
@ryanda9910
ryanda9910 marked this pull request as ready for review July 5, 2026 13:53
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/ollama Ollama / local models P3 Low — cosmetic, nice to have labels Jul 5, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:2270 in 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.model before the proposed update (agent/conversation_loop.py:2276 on current main).
  • The helper rewrites _cached_system_prompt but not the loop-local active_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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users area/sessions Session lifecycle, resume, persistence, history labels Jul 15, 2026
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/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have provider/ollama Ollama / local models sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) 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]: Incorrect reporting of inference model with LM Studio

4 participants