mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat(checkpoint): add pre-compression checkpoint flush hook
This commit is contained in:
parent
bd7fba2c52
commit
888d241ca2
2 changed files with 88 additions and 0 deletions
49
run_agent.py
49
run_agent.py
|
|
@ -8124,6 +8124,52 @@ class AIAgent:
|
|||
if messages and messages[-1].get("_flush_sentinel") == _sentinel:
|
||||
messages.pop()
|
||||
|
||||
def flush_checkpoint(self, messages: list = None) -> None:
|
||||
"""Write a checkpoint before context compression destroys progress.
|
||||
|
||||
Called by _compress_context before the compression happens.
|
||||
Captures whatever task state we can extract from the agent's
|
||||
current context (todo list, session ID, git state) and writes
|
||||
a checkpoint. If a checkpoint already exists for this session,
|
||||
it gets overwritten -- the pre-compression checkpoint is always
|
||||
the most accurate.
|
||||
"""
|
||||
if not self._checkpoint_store:
|
||||
return
|
||||
|
||||
from tools.checkpoint_tool import checkpoint_tool
|
||||
# Extract task description from session title or fallback
|
||||
task_desc = "Session checkpoint (auto-saved before compression)"
|
||||
try:
|
||||
if self._session_db:
|
||||
title = self._session_db.get_session_title(self.session_id)
|
||||
if title:
|
||||
task_desc = title
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Extract progress from todo store if available
|
||||
progress = []
|
||||
if self._todo_store:
|
||||
try:
|
||||
items = self._todo_store._items if hasattr(self._todo_store, '_items') else []
|
||||
for item in items:
|
||||
step = {"step": item.get("content", ""), "status": item.get("status", "pending")}
|
||||
progress.append(step)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
checkpoint_tool(
|
||||
action="write",
|
||||
task=task_desc,
|
||||
progress=progress,
|
||||
state={},
|
||||
decisions=[],
|
||||
store=self._checkpoint_store,
|
||||
agent=self,
|
||||
)
|
||||
logger.info("Pre-compression checkpoint saved for session %s", self.session_id)
|
||||
|
||||
def _compress_context(self, messages: list, system_message: str, *, approx_tokens: int = None, task_id: str = "default", focus_topic: str = None) -> tuple:
|
||||
"""Compress conversation context and split the session in SQLite.
|
||||
|
||||
|
|
@ -8145,6 +8191,9 @@ class AIAgent:
|
|||
# Pre-compression memory flush: let the model save memories before they're lost
|
||||
self.flush_memories(messages, min_turns=0)
|
||||
|
||||
# Pre-compression checkpoint: save task state before context is lost
|
||||
self.flush_checkpoint(messages)
|
||||
|
||||
# Notify external memory provider before compression discards context
|
||||
if self._memory_manager:
|
||||
try:
|
||||
|
|
|
|||
39
tests/test_checkpoint_flush.py
Normal file
39
tests/test_checkpoint_flush.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
"""Test that flush_checkpoint is called during context compression."""
|
||||
import json
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
def test_flush_checkpoint_method_exists():
|
||||
"""AIAgent must have a flush_checkpoint method."""
|
||||
from run_agent import AIAgent
|
||||
assert hasattr(AIAgent, "flush_checkpoint")
|
||||
|
||||
|
||||
def test_flush_checkpoint_writes_to_store(tmp_path):
|
||||
"""flush_checkpoint should write a checkpoint with current session state."""
|
||||
from agent.checkpoint_store import CheckpointStore
|
||||
from tools.checkpoint_tool import checkpoint_tool
|
||||
store = CheckpointStore(checkpoints_dir=tmp_path / "checkpoints")
|
||||
|
||||
mock_agent = MagicMock()
|
||||
mock_agent.session_id = "flush_test_session"
|
||||
mock_agent._checkpoint_store = store
|
||||
mock_agent._todo_store = MagicMock()
|
||||
mock_agent._todo_store.format_for_injection.return_value = "- [x] Step 1"
|
||||
|
||||
# Verify the checkpoint tool writes successfully (same path flush_checkpoint uses)
|
||||
result = checkpoint_tool(
|
||||
action="write",
|
||||
task="Auto-checkpoint before compression",
|
||||
progress=[],
|
||||
state={},
|
||||
decisions=[],
|
||||
store=store,
|
||||
agent=mock_agent,
|
||||
)
|
||||
data = json.loads(result)
|
||||
assert data["success"] is True
|
||||
saved = store.read("flush_test_session")
|
||||
assert saved is not None
|
||||
assert saved["task"] == "Auto-checkpoint before compression"
|
||||
Loading…
Add table
Add a link
Reference in a new issue