refactor(compression): dedupe the actionable-user row test, skip an unread scan

Follow-up to the oversized-turn exception merged in #116181.

- `_find_last_user_message_idx` and `_real_user_indices_desc` each spelled out
  the same actionable-and-not-synthetic predicate; both now call one
  `_is_real_user_turn`. No behaviour change — same two classmethods, same rows.
- The newest-user index is only read by the split exception, which rolling
  micro-compaction disables, so the scan is skipped on that pass instead of
  running and being discarded.

`_is_actionable_user_turn` / `_is_synthetic_compression_user_turn` are pure, so
the dedupe is equivalence by construction; the row set each scan returns is
unchanged.
This commit is contained in:
kshitij
2026-09-19 21:56:23 +05:30
parent 00570550f3
commit 0b9a9a0f5c

View File

@@ -4236,23 +4236,25 @@ Write only the summary body. Do not include any preamble or prefix."""
return check
return idx
@classmethod
def _is_real_user_turn(cls, message: Dict[str, Any]) -> bool:
"""Actionable user turn that is not synthetic scaffolding — the shared row test for both index scans."""
return cls._is_actionable_user_turn(message) and not cls._is_synthetic_compression_user_turn(message)
@classmethod
def _real_user_indices_desc(cls, messages: List[Dict[str, Any]], head_end: int) -> list[int]:
"""Newest-first indices of actionable, non-synthetic user turns at or after *head_end* (no handoffs/blank echoes)."""
return [
i for i in range(len(messages) - 1, head_end - 1, -1)
if cls._is_actionable_user_turn(messages[i])
and not cls._is_synthetic_compression_user_turn(messages[i])
if cls._is_real_user_turn(messages[i])
]
def _find_last_user_message_idx(self, messages: List[Dict[str, Any]], head_end: int) -> int:
"""Return the latest actionable user turn at or after *head_end*, or -1."""
# Early-exit generator: only the newest hit is needed, and this runs on every boundary
# computation — collecting every index (``_real_user_indices_desc``) costs a full scan.
# Early-exit generator: callers want the newest hit only, and collecting every index
# (``_real_user_indices_desc``) costs a full backward scan per call.
return next(
(i for i in range(len(messages) - 1, head_end - 1, -1)
if self._is_actionable_user_turn(messages[i])
and not self._is_synthetic_compression_user_turn(messages[i])),
(i for i in range(len(messages) - 1, head_end - 1, -1) if self._is_real_user_turn(messages[i])),
-1,
)
@@ -4570,7 +4572,9 @@ Write only the summary body. Do not include any preamble or prefix."""
# soft ceiling, anchoring its opening request retains the whole turn and blows the budget by
# design — then the clean tool-group boundary above wins and that request rides the handoff
# (#80449). The N-user promise (#70250) is never relaxed.
last_user_idx = self._find_last_user_message_idx(messages, head_end)
# Only batch/manual compaction can take the exception below, and only that path reads the
# newest user index, so the scan is not paid on the rolling micro-compaction pass.
last_user_idx = self._find_last_user_message_idx(messages, head_end) if allow_split_turn else -1
user_anchored_cut = self._ensure_last_user_message_in_tail(messages, cut_idx, head_end)
split_oversized_turn = False
# ``user_anchored_cut < cut_idx`` means the anchor found a real user turn strictly inside the