Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,11 @@ def list_messages(
return []

def _filter_restored_tool_context(self, messages: list[SessionMessage]) -> list[SessionMessage]:
"""Strip historical toolUse/toolResult context from restored messages."""
"""Strip toolUse/toolResult context, merging same-role turns into a Converse-valid sequence.

The trailing turn is left as-is: the runtime appends the next user turn before the model
call, so forcing a user tail here would create two adjacent user turns.
"""
filtered_messages: list[SessionMessage] = []
for session_message in messages:
message = session_message.to_message()
Expand All @@ -810,6 +814,9 @@ def _filter_restored_tool_context(self, messages: list[SessionMessage]) -> list[
if not filtered_content:
continue

if filtered_messages and filtered_messages[-1].to_message()["role"] == message["role"]:
filtered_content = filtered_messages.pop().to_message()["content"] + filtered_content

filtered_message: Message = {"role": message["role"], "content": filtered_content}
filtered_messages.append(
SessionMessage(
Expand All @@ -821,6 +828,9 @@ def _filter_restored_tool_context(self, messages: list[SessionMessage]) -> list[
)
)

while filtered_messages and filtered_messages[0].to_message()["role"] != "user":
filtered_messages.pop(0)

return filtered_messages

# endregion SessionRepository interface implementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,72 @@ def test_list_messages_returns_values_in_correct_reverse_order(self, session_man
assert messages[1].message["role"] == "user"
assert messages[0].message["role"] == "assistant"

def test_filter_restored_drops_tool_only_turn_and_merges_assistants(self, session_manager):
"""A toolResult-only user turn is dropped, so the surrounding assistant turns merge."""
messages = [
SessionMessage(message={"role": "user", "content": [{"text": "hi"}]}, message_id=0),
SessionMessage(
message={
"role": "assistant",
"content": [{"text": "calling"}, {"toolUse": {"toolUseId": "t1", "name": "f", "input": {}}}],
},
message_id=1,
),
SessionMessage(
message={
"role": "user",
"content": [{"toolResult": {"toolUseId": "t1", "status": "success", "content": [{"text": "ok"}]}}],
},
message_id=2,
),
SessionMessage(message={"role": "assistant", "content": [{"text": "done"}]}, message_id=3),
]

result = session_manager._filter_restored_tool_context(messages)

assert [m.message for m in result] == [
{"role": "user", "content": [{"text": "hi"}]},
{"role": "assistant", "content": [{"text": "calling"}, {"text": "done"}]},
]

def test_filter_restored_drops_leading_non_user_turns(self, session_manager):
"""Restored history must start on a user turn."""
messages = [
SessionMessage(message={"role": "assistant", "content": [{"text": "orphan"}]}, message_id=0),
SessionMessage(message={"role": "user", "content": [{"text": "hi"}]}, message_id=1),
SessionMessage(message={"role": "assistant", "content": [{"text": "answer"}]}, message_id=2),
]

result = session_manager._filter_restored_tool_context(messages)

assert [m.message for m in result] == [
{"role": "user", "content": [{"text": "hi"}]},
{"role": "assistant", "content": [{"text": "answer"}]},
]

def test_filter_restored_preserves_trailing_assistant_turn(self, session_manager):
"""The trailing assistant turn is kept; the runtime appends the next user turn."""
messages = [
SessionMessage(message={"role": "user", "content": [{"text": "hi"}]}, message_id=0),
SessionMessage(message={"role": "assistant", "content": [{"text": "answer"}]}, message_id=1),
]

result = session_manager._filter_restored_tool_context(messages)

assert [m.message for m in result] == [
{"role": "user", "content": [{"text": "hi"}]},
{"role": "assistant", "content": [{"text": "answer"}]},
]

def test_filter_restored_all_assistant_history_becomes_empty(self, session_manager):
"""History with no user turn is dropped entirely."""
messages = [
SessionMessage(message={"role": "assistant", "content": [{"text": "a"}]}, message_id=0),
SessionMessage(message={"role": "assistant", "content": [{"text": "b"}]}, message_id=1),
]

assert session_manager._filter_restored_tool_context(messages) == []

def test_events_to_messages_empty_payload(self, session_manager):
"""Test converting Bedrock events with empty payload."""
events = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,5 @@ def test_list_messages_filters_restored_tool_context():

assert [m.message for m in messages] == [
{"role": "user", "content": [{"text": "hello"}]},
{"role": "assistant", "content": [{"text": "calling tool"}]},
{"role": "assistant", "content": [{"text": "done"}]},
{"role": "assistant", "content": [{"text": "calling tool"}, {"text": "done"}]},
]