From f9e3d7d7195244b73601666cbaa26187c331282f Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Fri, 7 Aug 2026 13:31:57 -0700 Subject: [PATCH] fix(bedrock/realtime): clear tool ids on TOOL contentEnd TOOL contentEnd left minted response/item ids active, so the next ASSISTANT contentStart reused them and broke post-tool correlation. Clear those ids when a TOOL block ends and assert the next assistant response mints distinct ids --- .../llms/bedrock/realtime/transformation.py | 7 +++- .../test_bedrock_realtime_transformation.py | 39 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/litellm/llms/bedrock/realtime/transformation.py b/litellm/llms/bedrock/realtime/transformation.py index 4a475389f70..578a2aa11c5 100644 --- a/litellm/llms/bedrock/realtime/transformation.py +++ b/litellm/llms/bedrock/realtime/transformation.py @@ -1211,6 +1211,7 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): returned_messages.extend(events) elif "contentEnd" in event: + content_end: Final = event["contentEnd"] events, current_delta_chunks = self.transform_content_end_event( event, current_output_item_id, @@ -1219,7 +1220,11 @@ class BedrockRealtimeConfig(BaseRealtimeConfig): current_delta_chunks, ) returned_messages.extend(events) - if BedrockContentEnd.model_validate(event["contentEnd"]).stopReason == "END_TURN": + if content_end.get("type") == "TOOL": + current_output_item_id = None + current_response_id = None + current_delta_type = None + if BedrockContentEnd.model_validate(content_end).stopReason == "END_TURN": ( done_events, current_output_item_id, diff --git a/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_transformation.py b/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_transformation.py index 9b7456c5155..798bc86023d 100644 --- a/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_transformation.py +++ b/tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_transformation.py @@ -763,11 +763,40 @@ class TestBedrockRealtimeResponseTransformation: "current_delta_type": result["current_delta_type"], }, ) - assert follow_up["current_response_id"] == result["current_response_id"] - assert follow_up["current_output_item_id"] == result["current_output_item_id"] + assert follow_up["current_response_id"] is None + assert follow_up["current_output_item_id"] is None + assert follow_up["current_delta_type"] is None assert follow_up["response"] == [] assert all(msg["type"] != "response.output_item.done" for msg in follow_up["response"]) + post_tool_state = { + "session_configuration_request": follow_up["session_configuration_request"], + "current_output_item_id": follow_up["current_output_item_id"], + "current_response_id": follow_up["current_response_id"], + "current_conversation_id": follow_up["current_conversation_id"], + "current_delta_chunks": follow_up["current_delta_chunks"], + "current_item_chunks": follow_up["current_item_chunks"], + "current_delta_type": follow_up["current_delta_type"], + } + assistant_start = config.transform_realtime_response( + json.dumps({"event": {"contentStart": {"role": "ASSISTANT", "type": "TEXT"}}}), + "amazon.nova-2-sonic-v1:0", + logging_obj, + realtime_response_transform_input=post_tool_state, + ) + tool_response_id = result["current_response_id"] + tool_item_id = result["current_output_item_id"] + assert assistant_start["current_response_id"] is not None + assert assistant_start["current_output_item_id"] is not None + assert assistant_start["current_response_id"] != tool_response_id + assert assistant_start["current_output_item_id"] != tool_item_id + created = [msg for msg in assistant_start["response"] if msg["type"] == "response.created"][0] + added = [msg for msg in assistant_start["response"] if msg["type"] == "response.output_item.added"][0] + assert created["response"]["id"] == assistant_start["current_response_id"] + assert added["item"]["id"] == assistant_start["current_output_item_id"] + assert created["response"]["id"] != function_call["response_id"] + assert added["item"]["id"] != function_call["item_id"] + def test_tool_content_end_does_not_emit_message_output_item_done(self): """Minted tool ids must not unlock unpaired message output_item.done on TOOL contentEnd""" config = BedrockRealtimeConfig() @@ -798,9 +827,9 @@ class TestBedrockRealtimeResponseTransformation: ) assert result["response"] == [] - assert result["current_response_id"] == "resp_minted_for_tool" - assert result["current_output_item_id"] == "item_minted_for_tool" - assert result["current_delta_type"] == "text" + assert result["current_response_id"] is None + assert result["current_output_item_id"] is None + assert result["current_delta_type"] is None def test_transform_content_end_text(self): """Test contentEnd for text response"""