mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
fix(tui): keep the pending model shown at turn end instead of blipping back (#74766)
A model picked mid-turn is applied at the next turn start, but the end-of-turn session.info (_emit_settled_session_info) reads the still-live agent — the OLD model — and clobbered the optimistic paint, so the UI showed the new model, then snapped back to the old one when the turn ended, then switched for real on the next turn. Report the queued pick's model/provider in _session_info while a switch is pending (it IS the model the next turn runs), so the display stays on the user's choice through the settle. Cleared once the switch applies.
This commit is contained in:
parent
206eda50a5
commit
d8a9c17dae
2 changed files with 45 additions and 3 deletions
|
|
@ -8439,6 +8439,30 @@ def test_session_info_includes_session_title(monkeypatch):
|
|||
assert info["title"] == "Dashboard title"
|
||||
|
||||
|
||||
def test_session_info_reports_pending_model_switch(monkeypatch):
|
||||
"""A model queued mid-turn shows as the session's model in session.info, so
|
||||
the end-of-turn settle doesn't blip the UI back to the still-live old model
|
||||
before the switch applies at the next turn start."""
|
||||
agent = types.SimpleNamespace(tools=[], model="old/model", provider="openai")
|
||||
session = {
|
||||
"session_key": "",
|
||||
"history": [],
|
||||
"pending_model_switch": {
|
||||
"raw": "new/model --provider anthropic",
|
||||
"display_model": "new/model",
|
||||
"display_provider": "anthropic",
|
||||
},
|
||||
}
|
||||
|
||||
info = server._session_info(agent, session)
|
||||
assert info["model"] == "new/model"
|
||||
assert info["provider"] == "anthropic"
|
||||
|
||||
# With nothing queued the live agent model wins, as before.
|
||||
session.pop("pending_model_switch")
|
||||
assert server._session_info(agent, session)["model"] == "old/model"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# History-mutating commands must reject while session.running is True.
|
||||
# Without these guards, prompt.submit's post-run history write either
|
||||
|
|
|
|||
|
|
@ -4768,9 +4768,18 @@ def _session_info(agent, session: dict | None = None) -> dict:
|
|||
yolo = bool(_YOLO_MODE_FROZEN) or session_yolo or approval_mode == "off"
|
||||
except Exception:
|
||||
yolo = False
|
||||
# A model switch queued mid-turn (pending_model_switch) applies at the next
|
||||
# turn start, so agent.model still reads the OLD model until then. Report the
|
||||
# pending pick instead — it's the model the next turn will run, and it stops
|
||||
# the end-of-turn settle from blipping the UI back to the old model before
|
||||
# the switch lands. Cleared once _apply_pending_model_switch consumes it.
|
||||
pending_switch = (session or {}).get("pending_model_switch") or {}
|
||||
pending_model = str(pending_switch.get("display_model") or "").strip()
|
||||
pending_provider = str(pending_switch.get("display_provider") or "").strip()
|
||||
info: dict = {
|
||||
"model": mirror.get("model", getattr(agent, "model", "")),
|
||||
"provider": mirror.get("provider", getattr(agent, "provider", "")),
|
||||
"model": pending_model or mirror.get("model", getattr(agent, "model", "")),
|
||||
"provider": pending_provider
|
||||
or mirror.get("provider", getattr(agent, "provider", "")),
|
||||
"reasoning_effort": reasoning_effort,
|
||||
"service_tier": service_tier,
|
||||
"fast": service_tier == "priority",
|
||||
|
|
@ -10012,8 +10021,9 @@ def _(rid, params: dict) -> dict:
|
|||
# The user gets to pick, keep typing, and send the next turn on
|
||||
# the new model without waiting for the swap or interrupting.
|
||||
if session.get("running"):
|
||||
parsed = parse_model_switch_args(value)
|
||||
try:
|
||||
pending_model = parse_model_switch_args(value).model_input
|
||||
pending_model = parsed.model_input
|
||||
except Exception:
|
||||
pending_model = str(value)
|
||||
session["pending_model_switch"] = {
|
||||
|
|
@ -10021,6 +10031,14 @@ def _(rid, params: dict) -> dict:
|
|||
"confirm_expensive_model": bool(
|
||||
params.get("confirm_expensive_model", False)
|
||||
),
|
||||
# The resolved model/provider the next turn will run on.
|
||||
# _session_info reports these while the switch is pending
|
||||
# so the end-of-turn settle keeps showing the user's pick
|
||||
# instead of blipping back to the still-live old model.
|
||||
"display_model": pending_model,
|
||||
"display_provider": (
|
||||
getattr(parsed, "explicit_provider", "") or ""
|
||||
).strip(),
|
||||
}
|
||||
return _ok(
|
||||
rid,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue