fix(session): preserve user turns in replay history

This commit is contained in:
Xubin Ren
2026-06-18 00:03:22 +08:00
parent 472c67722e
commit 09962895fb
7 changed files with 91 additions and 17 deletions
+2
View File
@@ -1172,6 +1172,7 @@ class AgentLoop:
"max_messages": self._max_messages,
"max_tokens": self._replay_token_budget(),
"include_timestamps": True,
"extend_to_user": True,
}
history = session.get_history(**_hist_kwargs)
current_role = "assistant" if is_subagent else "user"
@@ -1447,6 +1448,7 @@ class AgentLoop:
"max_messages": self._max_messages,
"max_tokens": self._replay_token_budget(),
"include_timestamps": True,
"extend_to_user": True,
}
ctx.history = ctx.session.get_history(**_hist_kwargs)
self._runtime_events().record_turn_runtime(
+8 -1
View File
@@ -22,6 +22,7 @@ from nanobot.utils.helpers import (
estimate_message_tokens,
estimate_prompt_tokens_chain,
find_legal_message_start,
recent_message_start_index,
strip_think,
truncate_text,
truncate_text_to_tokens,
@@ -717,7 +718,13 @@ class Consolidator:
if len(tail) <= replay_max_messages:
return None
sliced = tail[-replay_max_messages:]
tail_messages = [message for _idx, message in tail]
start_idx = recent_message_start_index(
tail_messages,
replay_max_messages,
extend_to_user=True,
)
sliced = tail[start_idx:]
for i, (_idx, message) in enumerate(sliced):
if message.get("role") == "user":
start = i
+8 -1
View File
@@ -19,6 +19,7 @@ from nanobot.utils.helpers import (
estimate_message_tokens,
find_legal_message_start,
image_placeholder_text,
recent_message_start_index,
safe_filename,
strip_think,
)
@@ -153,6 +154,7 @@ class Session:
*,
max_tokens: int = 0,
include_timestamps: bool = False,
extend_to_user: bool = False,
) -> list[dict[str, Any]]:
"""Return unconsolidated messages for LLM input.
@@ -161,7 +163,12 @@ class Session:
"""
unconsolidated = self.messages[self.last_consolidated:]
max_messages = max_messages if max_messages > 0 else 120
sliced = unconsolidated[-max_messages:]
start_idx = recent_message_start_index(
unconsolidated,
max_messages,
extend_to_user=extend_to_user,
)
sliced = unconsolidated[start_idx:]
# Avoid starting mid-turn when possible, except for proactive
# assistant deliveries that the user may be replying to.
+24
View File
@@ -270,6 +270,30 @@ def truncate_text_to_tokens(text: str, max_tokens: int) -> str:
return truncate_text(text, max_chars - suffix_chars)
def recent_message_start_index(
messages: list[dict[str, Any]],
max_messages: int,
*,
extend_to_user: bool = False,
) -> int:
"""Return the start index for a recent replay window."""
if max_messages <= 0:
return len(messages)
start_idx = max(0, len(messages) - max_messages)
if not extend_to_user or len(messages) <= max_messages:
return start_idx
recovered_user = next(
(i for i in range(start_idx, -1, -1) if messages[i].get("role") == "user"),
None,
)
if recovered_user is None:
return start_idx
if recovered_user > 0 and messages[recovered_user - 1].get("_channel_delivery"):
return recovered_user - 1
return recovered_user
def find_legal_message_start(messages: list[dict[str, Any]]) -> int:
"""Find the first index whose tool results have matching assistant calls."""
declared: set[str] = set()