11"""Tests for preserve_tool_pairs functionality in summarize_when_long hook."""
22
33import rigging as rg
4+
45from dreadnode .agent .hooks .summarize import _find_tool_aware_boundary
56
67
78def test_preserves_tool_pairs ():
8- """Tool call and response stay together when split."""
9+ """Tool call and response stay together when boundary would split them ."""
910 messages = [
1011 rg .Message ("user" , "Hello" ),
1112 rg .Message (
1213 "assistant" ,
1314 "Let me check" ,
14- tool_calls = [{"id" : "call_1" , "type" : "function" , "function" : {"name" : "check" , "arguments" : "{}" }}],
15+ tool_calls = [
16+ {
17+ "id" : "call_1" ,
18+ "type" : "function" ,
19+ "function" : {"name" : "check" , "arguments" : "{}" },
20+ }
21+ ],
1522 ),
1623 rg .Message ("tool" , "Result" , tool_call_id = "call_1" ),
1724 rg .Message ("assistant" , "Done" ),
1825 rg .Message ("user" , "Thanks" ),
1926 ]
2027
21- boundary = _find_tool_aware_boundary (messages , min_messages_to_keep = 2 )
28+ # With min=3, naive boundary would be at index 2, keeping [2,3,4]
29+ # But that would orphan the tool response at index 2 (call at index 1)
30+ # So boundary should move back to index 1 to keep the pair together
31+ boundary = _find_tool_aware_boundary (messages , min_messages_to_keep = 3 )
32+ assert boundary == 1 , f"Expected boundary 1 to keep tool pair together, got { boundary } "
2233
23- # Should keep tool pair together by moving boundary earlier
24- assert boundary <= 1 , "Boundary should preserve tool call/response pair"
34+ # Verify the kept messages include the complete tool pair
35+ kept = messages [boundary :]
36+ assert len (kept ) == 4
37+ assert kept [0 ].role == "assistant"
38+ assert kept [1 ].role == "tool"
2539
2640
2741def test_no_tools ():
@@ -44,49 +58,59 @@ def test_multiple_tool_pairs():
4458 rg .Message (
4559 "assistant" ,
4660 "Running A" ,
47- tool_calls = [{"id" : "a" , "type" : "function" , "function" : {"name" : "run_a" , "arguments" : "{}" }}],
61+ tool_calls = [
62+ {"id" : "a" , "type" : "function" , "function" : {"name" : "run_a" , "arguments" : "{}" }}
63+ ],
4864 ),
4965 rg .Message ("tool" , "A done" , tool_call_id = "a" ),
5066 rg .Message (
5167 "assistant" ,
5268 "Running B" ,
53- tool_calls = [{"id" : "b" , "type" : "function" , "function" : {"name" : "run_b" , "arguments" : "{}" }}],
69+ tool_calls = [
70+ {"id" : "b" , "type" : "function" , "function" : {"name" : "run_b" , "arguments" : "{}" }}
71+ ],
5472 ),
5573 rg .Message ("tool" , "B done" , tool_call_id = "b" ),
5674 rg .Message ("user" , "Thanks" ),
5775 ]
5876
59- boundary = _find_tool_aware_boundary ( messages , min_messages_to_keep = 2 )
60-
61- # Should not split between any tool pairs
62- kept = messages [ boundary :]
63- assert len ( kept ) >= 2 , "Should keep minimum messages "
77+ # With min=3, naive boundary at index 3 would keep [3,4,5]
78+ # But index 4 (tool response "b") references call at index 3
79+ # So boundary should move back to index 3 to keep the second pair
80+ boundary = _find_tool_aware_boundary ( messages , min_messages_to_keep = 3 )
81+ assert boundary == 3 , f"Expected boundary 3 to preserve second tool pair, got { boundary } "
6482
6583
6684def test_no_valid_boundary ():
67- """Returns 0 when entire conversation is tool chain ."""
85+ """Returns 0 when min_messages would force splitting all tool pairs ."""
6886 messages = [
6987 rg .Message (
7088 "assistant" ,
7189 "Start" ,
72- tool_calls = [{"id" : "1" , "type" : "function" , "function" : {"name" : "start" , "arguments" : "{}" }}],
90+ tool_calls = [
91+ {"id" : "1" , "type" : "function" , "function" : {"name" : "start" , "arguments" : "{}" }}
92+ ],
7393 ),
7494 rg .Message ("tool" , "Result 1" , tool_call_id = "1" ),
7595 rg .Message (
7696 "assistant" ,
7797 "Continue" ,
78- tool_calls = [{"id" : "2" , "type" : "function" , "function" : {"name" : "continue" , "arguments" : "{}" }}],
98+ tool_calls = [
99+ {"id" : "2" , "type" : "function" , "function" : {"name" : "continue" , "arguments" : "{}" }}
100+ ],
79101 ),
80102 rg .Message ("tool" , "Result 2" , tool_call_id = "2" ),
81103 ]
82104
83- boundary = _find_tool_aware_boundary (messages , min_messages_to_keep = 2 )
84- assert boundary == 0 , "Should keep everything when no valid split exists"
105+ # With min=3, we'd need to keep last 3 messages
106+ # Any boundary would orphan at least one tool response
107+ # So should return 0 to keep everything
108+ boundary = _find_tool_aware_boundary (messages , min_messages_to_keep = 3 )
109+ assert boundary == 0 , f"Should keep everything when no valid split exists, got { boundary } "
85110
86111
87112if __name__ == "__main__" :
88113 test_preserves_tool_pairs ()
89114 test_no_tools ()
90115 test_multiple_tool_pairs ()
91116 test_no_valid_boundary ()
92- print ("All tests passed" )
0 commit comments