@@ -759,3 +759,78 @@ def test_full_conversation(self):
759759 assert len (result ) == 2
760760 assert result [0 ].parts [0 ].text == 'Great'
761761 assert result [1 ].parts [0 ].text == 'Tell me more'
762+
763+
764+ class TestResponseInteractionDeduplication :
765+ """Tests for response interaction api deduplication."""
766+
767+ def test_is_final_response_requires_turn_complete_true (self ):
768+ """Verify is_final_response() returns False when turn_complete=False."""
769+ from google .adk .events .event import Event
770+
771+ # Case 1: partial=False but turn_complete=False -> should be False
772+ event = Event (
773+ author = 'agent' ,
774+ content = types .Content (role = 'model' , parts = [types .Part (text = 'Hello' )]),
775+ partial = False ,
776+ turn_complete = False , # Turn not complete
777+ )
778+ assert event .is_final_response () is False
779+
780+ # Case 2: partial=False and turn_complete=True -> should be True
781+ event_final = Event (
782+ author = 'agent' ,
783+ content = types .Content (role = 'model' , parts = [types .Part (text = 'Hello' )]),
784+ partial = False ,
785+ turn_complete = True , # Turn complete
786+ )
787+ assert event_final .is_final_response () is True
788+
789+ def test_no_duplicate_final_response_in_streaming (self ):
790+ """Verify streaming events don't duplicate the final response."""
791+ from unittest .mock import MagicMock
792+
793+ # Simulate the streaming flow
794+ aggregated_parts = []
795+
796+ # Event 1: content.delta (streaming text)
797+ delta_event = MagicMock ()
798+ delta_event .event_type = 'content.delta'
799+ delta_event .delta = MagicMock ()
800+ delta_event .delta .type = 'text'
801+ delta_event .delta .text = 'Hello'
802+
803+ response1 = interactions_utils .convert_interaction_event_to_llm_response (
804+ delta_event , aggregated_parts , 'interaction_123'
805+ )
806+ assert response1 .partial is True
807+ assert response1 .turn_complete is False
808+
809+ # Event 2: content.stop (content complete but turn not complete)
810+ stop_event = MagicMock ()
811+ stop_event .event_type = 'content.stop'
812+
813+ response2 = interactions_utils .convert_interaction_event_to_llm_response (
814+ stop_event , aggregated_parts , 'interaction_123'
815+ )
816+ assert response2 .partial is False
817+ assert response2 .turn_complete is False # Not final yet
818+
819+ # Event 3: interaction.status_update with completed (final response)
820+ status_event = MagicMock ()
821+ status_event .event_type = 'interaction.status_update'
822+ status_event .status = 'completed'
823+
824+ response3 = interactions_utils .convert_interaction_event_to_llm_response (
825+ status_event , aggregated_parts , 'interaction_123'
826+ )
827+ assert response3 .partial is False
828+ assert response3 .turn_complete is True # This is the final response
829+
830+ # Verify: Only response3 should have turn_complete=True
831+ # This proves no duplication - there's only ONE final response
832+ final_responses = [
833+ r for r in [response1 , response2 , response3 ] if r .turn_complete
834+ ]
835+ assert len (final_responses ) == 1
836+ assert final_responses [0 ] == response3
0 commit comments