mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-19 16:12:30 +00:00
Revert "fix(agent): persist _last_summary across restarts with used sentinel"
This reverts commit e5a1416a37b423de95b0fa279e9473110a678112.
This commit is contained in:
parent
e5a1416a37
commit
9252f4d826
@ -89,7 +89,6 @@ class AutoCompact:
|
|||||||
if summary and summary != "(nothing)":
|
if summary and summary != "(nothing)":
|
||||||
self._summaries[key] = (summary, last_active)
|
self._summaries[key] = (summary, last_active)
|
||||||
session.metadata["_last_summary"] = {"text": summary, "last_active": last_active.isoformat()}
|
session.metadata["_last_summary"] = {"text": summary, "last_active": last_active.isoformat()}
|
||||||
session.metadata.pop("_last_summary_used", None)
|
|
||||||
session.messages = kept_msgs
|
session.messages = kept_msgs
|
||||||
session.last_consolidated = 0
|
session.last_consolidated = 0
|
||||||
session.updated_at = datetime.now()
|
session.updated_at = datetime.now()
|
||||||
@ -112,15 +111,13 @@ class AutoCompact:
|
|||||||
logger.info("Auto-compact: reloading session {} (archiving={})", key, key in self._archiving)
|
logger.info("Auto-compact: reloading session {} (archiving={})", key, key in self._archiving)
|
||||||
session = self.sessions.get_or_create(key)
|
session = self.sessions.get_or_create(key)
|
||||||
# Hot path: summary from in-memory dict (process hasn't restarted).
|
# Hot path: summary from in-memory dict (process hasn't restarted).
|
||||||
|
# Also clean metadata copy so stale _last_summary never leaks to disk.
|
||||||
entry = self._summaries.pop(key, None)
|
entry = self._summaries.pop(key, None)
|
||||||
if entry:
|
if entry:
|
||||||
session.metadata["_last_summary_used"] = True
|
session.metadata.pop("_last_summary", None)
|
||||||
return session, self._format_summary(entry[0], entry[1])
|
return session, self._format_summary(entry[0], entry[1])
|
||||||
# Cold path: summary persisted in session metadata (process restarted).
|
if "_last_summary" in session.metadata:
|
||||||
# Keep it in metadata so it survives restarts; only inject once per
|
meta = session.metadata.pop("_last_summary")
|
||||||
# turn via the _last_summary_used sentinel.
|
self.sessions.save(session)
|
||||||
meta = session.metadata.get("_last_summary")
|
|
||||||
if meta and not session.metadata.get("_last_summary_used"):
|
|
||||||
session.metadata["_last_summary_used"] = True
|
|
||||||
return session, self._format_summary(meta["text"], datetime.fromisoformat(meta["last_active"]))
|
return session, self._format_summary(meta["text"], datetime.fromisoformat(meta["last_active"]))
|
||||||
return session, None
|
return session, None
|
||||||
|
|||||||
@ -585,7 +585,6 @@ class Consolidator:
|
|||||||
"text": summary,
|
"text": summary,
|
||||||
"last_active": session.updated_at.isoformat(),
|
"last_active": session.updated_at.isoformat(),
|
||||||
}
|
}
|
||||||
session.metadata.pop("_last_summary_used", None)
|
|
||||||
self.sessions.save(session)
|
self.sessions.save(session)
|
||||||
|
|
||||||
def estimate_session_prompt_tokens(
|
def estimate_session_prompt_tokens(
|
||||||
|
|||||||
@ -1021,15 +1021,13 @@ class TestSummaryPersistence:
|
|||||||
assert summary is not None
|
assert summary is not None
|
||||||
assert "User said hello." in summary
|
assert "User said hello." in summary
|
||||||
assert "Inactive for" in summary
|
assert "Inactive for" in summary
|
||||||
# Metadata persists so the summary survives restarts; _last_summary_used
|
# Metadata should be cleaned up after consumption
|
||||||
# sentinel prevents duplicate injection within the same turn.
|
assert "_last_summary" not in reloaded.metadata
|
||||||
assert "_last_summary" in reloaded.metadata
|
|
||||||
assert reloaded.metadata.get("_last_summary_used") is True
|
|
||||||
await loop.close_mcp()
|
await loop.close_mcp()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_metadata_cleanup_no_leak(self, tmp_path):
|
async def test_metadata_cleanup_no_leak(self, tmp_path):
|
||||||
"""_last_summary persists in metadata for restart survival; _last_summary_used sentinel prevents duplicate injection."""
|
"""_last_summary should be removed from metadata after being consumed."""
|
||||||
loop = _make_loop(tmp_path, session_ttl_minutes=15)
|
loop = _make_loop(tmp_path, session_ttl_minutes=15)
|
||||||
session = loop.sessions.get_or_create("cli:test")
|
session = loop.sessions.get_or_create("cli:test")
|
||||||
_add_turns(session, 6, prefix="hello")
|
_add_turns(session, 6, prefix="hello")
|
||||||
@ -1052,13 +1050,10 @@ class TestSummaryPersistence:
|
|||||||
_, summary = loop.auto_compact.prepare_session(reloaded, "cli:test")
|
_, summary = loop.auto_compact.prepare_session(reloaded, "cli:test")
|
||||||
assert summary is not None
|
assert summary is not None
|
||||||
|
|
||||||
# Second call: no summary (already consumed this turn)
|
# Second call: no summary (already consumed)
|
||||||
_, summary2 = loop.auto_compact.prepare_session(reloaded, "cli:test")
|
_, summary2 = loop.auto_compact.prepare_session(reloaded, "cli:test")
|
||||||
assert summary2 is None
|
assert summary2 is None
|
||||||
# _last_summary stays in metadata for restart survival;
|
assert "_last_summary" not in reloaded.metadata
|
||||||
# _last_summary_used sentinel prevents duplicate injection.
|
|
||||||
assert "_last_summary" in reloaded.metadata
|
|
||||||
assert reloaded.metadata.get("_last_summary_used") is True
|
|
||||||
await loop.close_mcp()
|
await loop.close_mcp()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@ -1086,8 +1081,6 @@ class TestSummaryPersistence:
|
|||||||
# In-memory path is taken (no restart)
|
# In-memory path is taken (no restart)
|
||||||
_, summary = loop.auto_compact.prepare_session(reloaded, "cli:test")
|
_, summary = loop.auto_compact.prepare_session(reloaded, "cli:test")
|
||||||
assert summary is not None
|
assert summary is not None
|
||||||
# _last_summary stays in metadata for restart survival;
|
# Metadata should also be cleaned up
|
||||||
# _last_summary_used sentinel prevents duplicate injection.
|
assert "_last_summary" not in reloaded.metadata
|
||||||
assert "_last_summary" in reloaded.metadata
|
|
||||||
assert reloaded.metadata.get("_last_summary_used") is True
|
|
||||||
await loop.close_mcp()
|
await loop.close_mcp()
|
||||||
|
|||||||
@ -190,10 +190,7 @@ async def test_consolidation_persists_summary_for_next_prepare_session(tmp_path,
|
|||||||
reloaded, pending = loop.auto_compact.prepare_session(reloaded, "cli:test")
|
reloaded, pending = loop.auto_compact.prepare_session(reloaded, "cli:test")
|
||||||
assert pending is not None
|
assert pending is not None
|
||||||
assert "User discussed project status." in pending
|
assert "User discussed project status." in pending
|
||||||
# _last_summary persists for restart survival; _last_summary_used prevents
|
assert "_last_summary" not in reloaded.metadata
|
||||||
# duplicate injection within the same turn.
|
|
||||||
assert "_last_summary" in reloaded.metadata
|
|
||||||
assert reloaded.metadata.get("_last_summary_used") is True
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user