Skip to content

Commit 83ad822

Browse files
committed
fix: all tool uses now must be replaced
1 parent 4b5c5a7 commit 83ad822

File tree

2 files changed

+25
-25
lines changed

2 files changed

+25
-25
lines changed

src/strands/event_loop/_recover_message_on_max_tokens_reached.py

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,44 @@
1414

1515

1616
def recover_message_on_max_tokens_reached(message: Message) -> Message:
17-
"""Recover and clean up incomplete messages when max token limits are reached.
17+
"""Recover and clean up messages when max token limits are reached.
1818
19-
When a model response is truncated due to maximum token limits, tool use blocks may be
20-
incomplete or malformed. This function inspects the message content and:
19+
When a model response is truncated due to maximum token limits, all tool use blocks
20+
should be replaced with informative error messages since they may be incomplete or
21+
unreliable. This function inspects the message content and:
2122
22-
1. Identifies incomplete tool use blocks (missing name, input, or toolUseId)
23-
2. Replaces incomplete tool uses with informative error messages
24-
3. Preserves all valid content blocks (text and complete tool uses)
23+
1. Identifies all tool use blocks (regardless of validity)
24+
2. Replaces all tool uses with informative error messages
25+
3. Preserves all non-tool content blocks (text, images, etc.)
2526
4. Returns a cleaned message suitable for conversation history
2627
2728
This recovery mechanism ensures that the conversation can continue gracefully even when
28-
model responses are truncated, providing clear feedback about what happened.
29+
model responses are truncated, providing clear feedback about what happened and preventing
30+
potentially incomplete or corrupted tool executions.
31+
32+
TODO: after https://github.com/strands-agents/sdk-python/issues/561 is completed, only the verifiable
33+
invalid tool_use content blocks need to be replaced.
2934
3035
Args:
3136
message: The potentially incomplete message from the model that was truncated
3237
due to max token limits.
3338
3439
Returns:
35-
A cleaned Message with incomplete tool uses replaced by explanatory text content.
40+
A cleaned Message with all tool uses replaced by explanatory text content.
3641
The returned message maintains the same role as the input message.
3742
3843
Example:
39-
If a message contains an incomplete tool use like:
44+
If a message contains any tool use (complete or incomplete):
4045
```
41-
{"toolUse": {"name": "calculator"}} # missing input and toolUseId
46+
{"toolUse": {"name": "calculator", "input": {"expression": "2+2"}, "toolUseId": "123"}}
4247
```
4348
4449
It will be replaced with:
4550
```
4651
{"text": "The selected tool calculator's tool use was incomplete due to maximum token limits being reached."}
4752
```
4853
"""
49-
logger.info("handling max_tokens stop reason - inspecting incomplete message for invalid tool uses")
54+
logger.info("handling max_tokens stop reason - replacing all tool uses with error messages")
5055

5156
valid_content: list[ContentBlock] = []
5257
for content in message["content"] or []:
@@ -55,15 +60,8 @@ def recover_message_on_max_tokens_reached(message: Message) -> Message:
5560
valid_content.append(content)
5661
continue
5762

58-
# Check if tool use is incomplete (missing or empty required fields)
59-
tool_name = tool_use.get("name")
60-
if tool_name and tool_use.get("input") and tool_use.get("toolUseId"):
61-
# As far as we can tell, tool use is valid if this condition is true
62-
valid_content.append(content)
63-
continue
64-
65-
# Tool use is incomplete due to max_tokens truncation
66-
display_name = tool_name if tool_name else "<unknown>"
63+
# Replace all tool uses with error messages when max_tokens is reached
64+
display_name = tool_use.get("name", "<unknown>")
6765
logger.warning("tool_name=<%s> | replacing with error message due to max_tokens truncation.", display_name)
6866

6967
valid_content.append(

tests/strands/event_loop/test_recover_message_on_max_tokens_reached.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def test_recover_message_on_max_tokens_reached_with_missing_tool_use_id():
9595

9696

9797
def test_recover_message_on_max_tokens_reached_with_valid_tool_use():
98-
"""Test that valid tool uses are preserved unchanged."""
98+
"""Test that even valid tool uses are replaced with error messages."""
9999
complete_message: Message = {
100100
"role": "assistant",
101101
"content": [
@@ -106,13 +106,15 @@ def test_recover_message_on_max_tokens_reached_with_valid_tool_use():
106106

107107
result = recover_message_on_max_tokens_reached(complete_message)
108108

109-
# Should preserve the message exactly as-is
109+
# Should replace even valid tool uses with error messages
110110
assert result["role"] == "assistant"
111111
assert len(result["content"]) == 2
112112
assert result["content"][0] == {"text": "I'll help you with that."}
113-
assert result["content"][1] == {
114-
"toolUse": {"name": "calculator", "input": {"expression": "2+2"}, "toolUseId": "123"}
115-
}
113+
114+
# Valid tool use should also be replaced with error message
115+
assert "text" in result["content"][1]
116+
assert "calculator" in result["content"][1]["text"]
117+
assert "incomplete due to maximum token limits" in result["content"][1]["text"]
116118

117119

118120
def test_recover_message_on_max_tokens_reached_with_empty_content():

0 commit comments

Comments
 (0)