mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
fix(gateway): clear session-scoped model overrides on /resume
/resume is a conversation boundary, but unlike /new it did not clear the chat-keyed _session_model_overrides / _pending_model_notes. A /model switch made in the previous session under the same chat session_key leaked into the resumed conversation, running it on the wrong model. Clear both maps for the session_key after the switch (mirroring /new), scoped to that key so other chats' overrides are untouched. The cached-agent eviction this leak also implied already landed via #6672. Closes #10702.
This commit is contained in:
parent
476875acb9
commit
61a4526ac7
2 changed files with 48 additions and 0 deletions
|
|
@ -3233,6 +3233,20 @@ class GatewaySlashCommandsMixin:
|
|||
return t("gateway.resume.switch_failed")
|
||||
self._clear_session_boundary_security_state(session_key)
|
||||
|
||||
# Clear session-scoped model/reasoning overrides so the resumed
|
||||
# conversation picks up configured defaults instead of a /model
|
||||
# switch made in the previous session under the same chat
|
||||
# session_key. /resume is a conversation boundary just like /new
|
||||
# (which clears these too); without this, a stale override leaks
|
||||
# across the switch. See #10702.
|
||||
_overrides = getattr(self, "_session_model_overrides", None)
|
||||
if isinstance(_overrides, dict):
|
||||
_overrides.pop(session_key, None)
|
||||
self._set_session_reasoning_override(session_key, None)
|
||||
_pending_notes = getattr(self, "_pending_model_notes", None)
|
||||
if isinstance(_pending_notes, dict):
|
||||
_pending_notes.pop(session_key, None)
|
||||
|
||||
# Evict any cached agent for this session so the next message
|
||||
# rebuilds with the correct session_id end-to-end — mirrors
|
||||
# /branch and /reset. Without this, the cached AIAgent (and its
|
||||
|
|
|
|||
|
|
@ -173,6 +173,40 @@ class TestHandleResumeCommand:
|
|||
assert call_args[0][1] == "old_session_abc"
|
||||
db.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resume_clears_session_model_overrides(self, tmp_path):
|
||||
"""Resume must not carry a previous session's /model override into the
|
||||
restored conversation, while leaving other chats' overrides intact (#10702)."""
|
||||
from hermes_state import SessionDB
|
||||
db = SessionDB(db_path=tmp_path / "state.db")
|
||||
db.create_session("old_session_abc", "telegram")
|
||||
db.set_session_title("old_session_abc", "My Project")
|
||||
db.create_session("current_session_001", "telegram")
|
||||
|
||||
event = _make_event(text="/resume My Project")
|
||||
runner = _make_runner(session_db=db, current_session_id="current_session_001",
|
||||
event=event)
|
||||
key = _session_key_for_event(event)
|
||||
runner._session_model_overrides = {
|
||||
key: {"model": "gpt-5", "provider": "openai"},
|
||||
"agent:main:telegram:dm:other": {"model": "keep-me"},
|
||||
}
|
||||
runner._pending_model_notes = {
|
||||
key: "[Note: switched to gpt-5]",
|
||||
"agent:main:telegram:dm:other": "[Note: keep-me]",
|
||||
}
|
||||
|
||||
result = await runner._handle_resume_command(event)
|
||||
|
||||
assert "Resumed" in result
|
||||
# The resumed chat's override + pending note are cleared...
|
||||
assert key not in runner._session_model_overrides
|
||||
assert key not in runner._pending_model_notes
|
||||
# ...but an unrelated chat's state is untouched.
|
||||
assert runner._session_model_overrides["agent:main:telegram:dm:other"] == {"model": "keep-me"}
|
||||
assert runner._pending_model_notes["agent:main:telegram:dm:other"] == "[Note: keep-me]"
|
||||
db.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resume_nonexistent_name(self, tmp_path):
|
||||
"""Returns error for unknown session name."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue