Skip to content

Commit 63cc2db

Browse files
fix: linting and typechecks
1 parent 424e3dc commit 63cc2db

File tree

2 files changed

+49
-22
lines changed

2 files changed

+49
-22
lines changed

dreadnode/agent/hooks/summarize.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,12 @@ def _find_tool_aware_boundary(
7676
has_orphan = False
7777
for msg in messages[boundary:]:
7878
if msg.role == "tool" and hasattr(msg, "tool_call_id"):
79-
call_idx = tool_call_map.get(msg.tool_call_id)
80-
if call_idx is not None and call_idx < boundary:
81-
has_orphan = True
82-
break
79+
tool_call_id = getattr(msg, "tool_call_id", None)
80+
if tool_call_id is not None:
81+
call_idx = tool_call_map.get(tool_call_id)
82+
if call_idx is not None and call_idx < boundary:
83+
has_orphan = True
84+
break
8385

8486
if not has_orphan:
8587
return boundary
@@ -93,6 +95,7 @@ def summarize_when_long(
9395
max_tokens: int = 100_000,
9496
min_messages_to_keep: int = 5,
9597
guidance: str = "",
98+
*,
9699
preserve_tool_pairs: bool = True,
97100
) -> "Hook":
98101
"""

tests/test_preserve_tool_pairs.py

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
"""Tests for preserve_tool_pairs functionality in summarize_when_long hook."""
22

33
import rigging as rg
4+
45
from dreadnode.agent.hooks.summarize import _find_tool_aware_boundary
56

67

78
def 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

2741
def 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

6684
def 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

87112
if __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

Comments
 (0)