Skip to content

Commit bab08db

Browse files
authored
fix(conversation-manager): handle window_size=0 and reject negative values (#2208)
1 parent 009374f commit bab08db

2 files changed

Lines changed: 62 additions & 2 deletions

File tree

src/strands/agent/conversation_manager/sliding_window_conversation_manager.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def __init__(
4242
4343
Args:
4444
window_size: Maximum number of messages to keep in the agent's history.
45-
Defaults to 40 messages.
45+
Use 0 to clear all messages on every reduction. Defaults to 40 messages.
4646
should_truncate_results: Truncate tool results when a message is too large for the model's context window
4747
per_turn: Controls when to apply message management during agent execution.
4848
- False (default): Only apply management at the end (default behavior)
@@ -56,8 +56,10 @@ def __init__(
5656
for performance tuning.
5757
5858
Raises:
59-
ValueError: If per_turn is 0 or a negative integer.
59+
ValueError: If window_size is negative, or if per_turn is 0 or a negative integer.
6060
"""
61+
if not isinstance(window_size, bool) and window_size < 0:
62+
raise ValueError(f"window_size must be a non-negative integer, got {window_size}")
6163
if isinstance(per_turn, int) and not isinstance(per_turn, bool) and per_turn <= 0:
6264
raise ValueError(f"per_turn must be a positive integer, True, or False, got {per_turn}")
6365

@@ -173,6 +175,12 @@ def reduce_context(self, agent: "Agent", e: Exception | None = None, **kwargs: A
173175
"""
174176
messages = agent.messages
175177

178+
# window_size=0 means "remove all messages" (matches TypeScript SDK behaviour)
179+
if self.window_size == 0:
180+
self.removed_message_count += len(messages)
181+
messages[:] = []
182+
return
183+
176184
# Try to truncate the tool result first
177185
oldest_message_idx_with_tool_results = self._find_oldest_message_with_tool_results(messages)
178186
if oldest_message_idx_with_tool_results is not None and self.should_truncate_results:

tests/strands/agent/test_conversation_manager.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,3 +703,55 @@ def test_boundary_text_in_tool_result_not_truncated():
703703

704704
assert not changed
705705
assert messages[0]["content"][0]["toolResult"]["content"][0]["text"] == boundary_text
706+
707+
708+
# ==============================================================================
709+
# window_size=0 and negative window_size validation tests
710+
# ==============================================================================
711+
712+
713+
def test_window_size_negative_raises_value_error():
714+
with pytest.raises(ValueError, match="window_size"):
715+
SlidingWindowConversationManager(window_size=-1)
716+
717+
718+
def test_window_size_zero_clears_all_messages_on_apply_management():
719+
"""window_size=0 should remove all messages, matching TypeScript SDK behaviour (issue #2205)."""
720+
manager = SlidingWindowConversationManager(window_size=0, should_truncate_results=False)
721+
messages = [
722+
{"role": "user", "content": [{"text": "Hello"}]},
723+
{"role": "assistant", "content": [{"text": "Hi there"}]},
724+
]
725+
test_agent = Agent(messages=messages)
726+
manager.apply_management(test_agent)
727+
728+
assert messages == []
729+
assert manager.removed_message_count == 2
730+
731+
732+
def test_window_size_zero_clears_all_messages_on_reduce_context():
733+
"""reduce_context with window_size=0 should clear all messages even without overflow."""
734+
manager = SlidingWindowConversationManager(window_size=0, should_truncate_results=False)
735+
messages = [
736+
{"role": "user", "content": [{"text": "First"}]},
737+
{"role": "assistant", "content": [{"text": "Second"}]},
738+
{"role": "user", "content": [{"text": "Third"}]},
739+
]
740+
test_agent = Agent(messages=messages)
741+
manager.reduce_context(test_agent)
742+
743+
assert messages == []
744+
assert manager.removed_message_count == 3
745+
746+
747+
def test_window_size_zero_clears_on_overflow():
748+
"""reduce_context with window_size=0 should clear messages even when called with an overflow exception."""
749+
manager = SlidingWindowConversationManager(window_size=0, should_truncate_results=False)
750+
messages = [
751+
{"role": "user", "content": [{"text": "Hello"}]},
752+
{"role": "assistant", "content": [{"text": "Hi"}]},
753+
]
754+
test_agent = Agent(messages=messages)
755+
manager.reduce_context(test_agent, e=Exception("overflow"))
756+
757+
assert messages == []

0 commit comments

Comments
 (0)