refactor(memory): clarify idle compact archive inputs

maintainer edit: rename the idle compact archive inputs so the code distinguishes messages being removed from messages being summarized. This keeps the #4264 behavior unchanged while making the retained-suffix summary rule easier to read.
This commit is contained in:
chengyongru
2026-06-13 22:24:30 +08:00
committed by Xubin Ren
parent 0863e6e5ab
commit be6419b289
+21 -26
View File
@@ -799,23 +799,22 @@ class Consolidator:
messages: list[dict], messages: list[dict],
*, *,
session_key: str | None = None, session_key: str | None = None,
summary_context: list[dict] | None = None, summary_messages: list[dict] | None = None,
) -> str | None: ) -> str | None:
"""Summarize messages via LLM and append to history.jsonl. """Summarize messages via LLM and append to history.jsonl.
``messages`` are the messages being archived (removed from the live ``messages`` are the messages being archived (removed from the live
session); they are what gets raw-dumped if the LLM call fails. session); they are what gets raw-dumped if the LLM call fails.
``summary_context``, when given, is fed to the summarizer in place of ``summary_messages``, when given, lets callers include retained
``messages`` so a caller can summarize over a wider window (e.g. the messages in the summary without archiving them.
full conversation tail) while still only archiving ``messages``.
Returns the summary text on success, None if nothing to archive. Returns the summary text on success, None if nothing to archive.
""" """
if not messages: if not messages:
return None return None
context = summary_context if summary_context is not None else messages messages_to_summarize = summary_messages if summary_messages is not None else messages
try: try:
formatted = MemoryStore._format_messages(context) formatted = MemoryStore._format_messages(messages_to_summarize)
formatted = self._truncate_to_token_budget(formatted) formatted = self._truncate_to_token_budget(formatted)
response = await self.provider.chat_with_retry( response = await self.provider.chat_with_retry(
model=self.model, model=self.model,
@@ -972,42 +971,38 @@ class Consolidator:
self.sessions.invalidate(session_key) self.sessions.invalidate(session_key)
session = self.sessions.get_or_create(session_key) session = self.sessions.get_or_create(session_key)
tail = list(session.messages[session.last_consolidated:]) messages_to_summarize = list(session.messages[session.last_consolidated:])
if not tail: if not messages_to_summarize:
session.updated_at = datetime.now() session.updated_at = datetime.now()
self.sessions.save(session) self.sessions.save(session)
return "" return ""
probe = Session( probe = Session(
key=session.key, key=session.key,
messages=tail.copy(), messages=messages_to_summarize.copy(),
created_at=session.created_at, created_at=session.created_at,
updated_at=session.updated_at, updated_at=session.updated_at,
metadata={}, metadata={},
last_consolidated=0, last_consolidated=0,
) )
dropped, already_consolidated = probe.retain_recent_legal_suffix(max_suffix) dropped, already_consolidated = probe.retain_recent_legal_suffix(max_suffix)
kept = probe.messages messages_to_keep = probe.messages
archive_msgs = dropped[already_consolidated:] messages_to_remove = dropped[already_consolidated:]
if not archive_msgs and not kept: if not messages_to_remove and not messages_to_keep:
session.updated_at = datetime.now() session.updated_at = datetime.now()
self.sessions.save(session) self.sessions.save(session)
return "" return ""
last_active = session.updated_at last_active = session.updated_at
summary: str | None = "" summary: str | None = ""
if archive_msgs: if messages_to_remove:
# Summarize over the full unconsolidated tail — including the # Summarize the retained suffix too, but only remove/raw-dump
# recent suffix we retain — not just the dropped prefix. Idle # the messages that are no longer kept in the live session.
# compaction usually runs on a finished conversation, so a late
# user correction or final result that landed in the kept suffix
# must still reach the persisted summary; otherwise history keeps
# the stale pre-correction conclusion that never gets fixed
# (#4264). Only archive_msgs are removed/raw-dumped; kept stays
# in the session.
summary = await self.archive( summary = await self.archive(
archive_msgs, session_key=session_key, summary_context=tail messages_to_remove,
session_key=session_key,
summary_messages=messages_to_summarize,
) )
if summary and summary != "(nothing)": if summary and summary != "(nothing)":
@@ -1016,17 +1011,17 @@ class Consolidator:
"last_active": last_active.isoformat(), "last_active": last_active.isoformat(),
} }
session.messages = kept session.messages = messages_to_keep
session.last_consolidated = 0 session.last_consolidated = 0
session.updated_at = datetime.now() session.updated_at = datetime.now()
self.sessions.save(session) self.sessions.save(session)
if archive_msgs: if messages_to_remove:
logger.info( logger.info(
"Idle-session compact for {}: archived={}, kept={}, summary={}", "Idle-session compact for {}: archived={}, kept={}, summary={}",
session_key, session_key,
len(archive_msgs), len(messages_to_remove),
len(kept), len(messages_to_keep),
bool(summary), bool(summary),
) )