Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion agent/context_compressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ def on_session_reset(self) -> None:
self._last_compression_savings_pct = 100.0
self._ineffective_compression_count = 0
self._summary_failure_cooldown_until = 0.0 # transient errors must not block a fresh session
self.summary_model = self._configured_summary_model # restore in case fallback cleared it
self._summary_model_fallen_back = False # allow the one-shot fallback to fire again

def update_model(
self,
Expand Down Expand Up @@ -464,7 +466,8 @@ def __init__(
self.last_prompt_tokens = 0
self.last_completion_tokens = 0

self.summary_model = summary_model_override or ""
self._configured_summary_model: str = summary_model_override or ""
self.summary_model = self._configured_summary_model

# Stores the previous compaction summary for iterative updates
self._previous_summary: Optional[str] = None
Expand Down
47 changes: 47 additions & 0 deletions tests/agent/test_context_compressor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1631,3 +1631,50 @@ def test_pass3_emits_valid_json_for_downstream_provider(self):
parsed = _json.loads(shrunk)
assert parsed["path"] == "~/.hermes/skills/shopping/browser-setup-notes.md"
assert parsed["content"].endswith("...[truncated]")


class TestOnSessionResetSummaryModel:
"""on_session_reset() must restore summary_model to the configured value.

When the aux summary model fails mid-session, the fallback logic clears
self.summary_model = "" so subsequent compressions use the main model.
On /reset the compressor is reused (not recreated), so without an explicit
restore the new session silently degrades to the main model even if the
aux model is back online. See issue companion to #15548.
"""

def _make_compressor(self, summary_model: str):
with patch("agent.context_compressor.get_model_context_length", return_value=100_000):
return ContextCompressor(
model="main/model",
threshold_percent=0.85,
quiet_mode=True,
summary_model_override=summary_model,
)

def test_configured_summary_model_restored_after_fallback(self):
c = self._make_compressor("gpt-4o-mini")
assert c.summary_model == "gpt-4o-mini"

# Simulate the fallback clearing summary_model
c.summary_model = ""
c._summary_model_fallen_back = True

c.on_session_reset()

assert c.summary_model == "gpt-4o-mini", (
"on_session_reset() must restore summary_model to the configured value"
)

def test_summary_model_fallen_back_cleared_on_reset(self):
c = self._make_compressor("gpt-4o-mini")
c._summary_model_fallen_back = True

c.on_session_reset()

assert c._summary_model_fallen_back is False

def test_empty_summary_model_stays_empty_after_reset(self):
c = self._make_compressor("")
c.on_session_reset()
assert c.summary_model == ""
Loading