fix: harden compaction pair handling

This commit is contained in:
gui-yue
2026-06-07 12:35:08 +08:00
parent 7891c25c12
commit 99a1e52b36
9 changed files with 238 additions and 32 deletions

View File

@@ -46,10 +46,10 @@ def snip_compact(messages, max_messages=50):
if len(messages) <= max_messages:
return messages
head_end, tail_start = 3, len(messages) - (max_messages - 3)
if has_tool_use(messages[head_end - 1]):
while head_end < len(messages) and is_tool_result_message(messages[head_end]):
if _message_has_tool_use(messages[head_end - 1]):
while head_end < len(messages) and _is_tool_result_message(messages[head_end]):
head_end += 1
if is_tool_result_message(messages[tail_start]) and has_tool_use(messages[tail_start - 1]):
if _is_tool_result_message(messages[tail_start]) and _message_has_tool_use(messages[tail_start - 1]):
tail_start -= 1
snipped = tail_start - head_end
placeholder = {"role": "user", "content": f"[snipped {snipped} messages from conversation middle]"}
@@ -141,7 +141,7 @@ def reactive_compact(messages):
transcript = write_transcript(messages)
summary = summarize_history(messages)
tail_start = max(0, len(messages) - 5)
if is_tool_result_message(messages[tail_start]) and has_tool_use(messages[tail_start - 1]):
if _is_tool_result_message(messages[tail_start]) and _message_has_tool_use(messages[tail_start - 1]):
tail_start -= 1
return [{"role": "user",
"content": f"[Reactive compact]\n\n{summary}"}, *messages[tail_start:]]

View File

@@ -46,10 +46,10 @@ def snip_compact(messages, max_messages=50):
if len(messages) <= max_messages:
return messages
head_end, tail_start = 3, len(messages) - (max_messages - 3)
if has_tool_use(messages[head_end - 1]):
while head_end < len(messages) and is_tool_result_message(messages[head_end]):
if _message_has_tool_use(messages[head_end - 1]):
while head_end < len(messages) and _is_tool_result_message(messages[head_end]):
head_end += 1
if is_tool_result_message(messages[tail_start]) and has_tool_use(messages[tail_start - 1]):
if _is_tool_result_message(messages[tail_start]) and _message_has_tool_use(messages[tail_start - 1]):
tail_start -= 1
snipped = tail_start - head_end
placeholder = {"role": "user", "content": f"[snipped {snipped} messages from conversation middle]"}
@@ -141,7 +141,7 @@ def reactive_compact(messages):
transcript = write_transcript(messages)
summary = summarize_history(messages)
tail_start = max(0, len(messages) - 5)
if is_tool_result_message(messages[tail_start]) and has_tool_use(messages[tail_start - 1]):
if _is_tool_result_message(messages[tail_start]) and _message_has_tool_use(messages[tail_start - 1]):
tail_start -= 1
return [{"role": "user",
"content": f"[Reactive compact]\n\n{summary}"}, *messages[tail_start:]]

View File

@@ -46,10 +46,10 @@ def snip_compact(messages, max_messages=50):
if len(messages) <= max_messages:
return messages
head_end, tail_start = 3, len(messages) - (max_messages - 3)
if has_tool_use(messages[head_end - 1]):
while head_end < len(messages) and is_tool_result_message(messages[head_end]):
if _message_has_tool_use(messages[head_end - 1]):
while head_end < len(messages) and _is_tool_result_message(messages[head_end]):
head_end += 1
if is_tool_result_message(messages[tail_start]) and has_tool_use(messages[tail_start - 1]):
if _is_tool_result_message(messages[tail_start]) and _message_has_tool_use(messages[tail_start - 1]):
tail_start -= 1
snipped = tail_start - head_end
placeholder = {"role": "user", "content": f"[snipped {snipped} messages from conversation middle]"}
@@ -141,7 +141,7 @@ def reactive_compact(messages):
transcript = write_transcript(messages)
summary = summarize_history(messages)
tail_start = max(0, len(messages) - 5)
if is_tool_result_message(messages[tail_start]) and has_tool_use(messages[tail_start - 1]):
if _is_tool_result_message(messages[tail_start]) and _message_has_tool_use(messages[tail_start - 1]):
tail_start -= 1
return [{"role": "user",
"content": f"[Reactive compact]\n\n{summary}"}, *messages[tail_start:]]

View File

@@ -269,9 +269,10 @@ PERSIST_THRESHOLD = 30000
def estimate_size(msgs): return len(str(msgs))
def _block_type(block):
return getattr(block, "type", None) if not isinstance(block, dict) else block.get("type")
return block.get("type") if isinstance(block, dict) else getattr(block, "type", None)
def _has_tool_use(msg):
def _message_has_tool_use(msg):
if msg.get("role") != "assistant":
return False
content = msg.get("content")
@@ -279,13 +280,15 @@ def _has_tool_use(msg):
return False
return any(_block_type(block) == "tool_use" for block in content)
def _is_tool_result_message(msg):
if msg.get("role") != "user":
return False
content = msg.get("content")
if not isinstance(content, list):
return False
return any(isinstance(block, dict) and block.get("type") == "tool_result" for block in content)
return any(isinstance(block, dict) and block.get("type") == "tool_result"
for block in content)
# L1: snipCompact — trim middle messages
@@ -293,10 +296,12 @@ def snip_compact(messages, max_messages=50):
if len(messages) <= max_messages: return messages
keep_head, keep_tail = 3, max_messages - 3
head_end, tail_start = keep_head, len(messages) - keep_tail
if head_end > 0 and _has_tool_use(messages[head_end - 1]):
if head_end > 0 and _message_has_tool_use(messages[head_end - 1]):
while head_end < len(messages) and _is_tool_result_message(messages[head_end]):
head_end += 1
if tail_start > 0 and tail_start < len(messages) and _is_tool_result_message(messages[tail_start]) and _has_tool_use(messages[tail_start - 1]):
if (tail_start > 0 and tail_start < len(messages)
and _is_tool_result_message(messages[tail_start])
and _message_has_tool_use(messages[tail_start - 1])):
tail_start -= 1
if head_end >= tail_start:
return messages
@@ -379,7 +384,9 @@ def reactive_compact(messages):
transcript = write_transcript(messages)
summary = summarize_history(messages)
tail_start = max(0, len(messages) - 5)
if tail_start > 0 and tail_start < len(messages) and _is_tool_result_message(messages[tail_start]) and _has_tool_use(messages[tail_start - 1]):
if (tail_start > 0 and tail_start < len(messages)
and _is_tool_result_message(messages[tail_start])
and _message_has_tool_use(messages[tail_start - 1])):
tail_start -= 1
return [{"role": "user", "content": f"[Reactive compact]\n\n{summary}"}, *messages[tail_start:]]