diff --git a/src/agents/realtime/openai_realtime.py b/src/agents/realtime/openai_realtime.py index 02cb55a1a6..2322604141 100644 --- a/src/agents/realtime/openai_realtime.py +++ b/src/agents/realtime/openai_realtime.py @@ -2011,7 +2011,7 @@ def conversation_item_to_realtime_message_item( "type": item.type, "role": item.role, "content": content, - "status": "in_progress", + "status": item.status, }, ) diff --git a/src/agents/realtime/session.py b/src/agents/realtime/session.py index ef5f18cdfd..2bdd000814 100644 --- a/src/agents/realtime/session.py +++ b/src/agents/realtime/session.py @@ -523,6 +523,14 @@ async def on_event(self, event: RealtimeModelEvent) -> None: and existing_item.type == "message" and incoming_item.type == "message" ): + if ( + isinstance(existing_item, AssistantMessageItem) + and isinstance(incoming_item, AssistantMessageItem) + and incoming_item.status is None + ): + incoming_item = incoming_item.model_copy( + update={"status": existing_item.status} + ) try: # Merge transcripts for matching content indices existing_content = existing_item.content diff --git a/tests/realtime/test_item_parsing.py b/tests/realtime/test_item_parsing.py index e8484a58f6..3cd90a1789 100644 --- a/tests/realtime/test_item_parsing.py +++ b/tests/realtime/test_item_parsing.py @@ -50,11 +50,12 @@ def test_user_message_conversion() -> None: assert isinstance(converted, UserMessageItem) -def test_assistant_message_conversion() -> None: +def test_assistant_message_conversion_preserves_status() -> None: item = RealtimeConversationItemAssistantMessage( id="123", type="message", role="assistant", + status="completed", content=[AssistantMessageContent(type="output_text", text=None)], ) @@ -63,6 +64,22 @@ def test_assistant_message_conversion() -> None: ) assert isinstance(converted, AssistantMessageItem) + assert converted.status == "completed" + + +def test_assistant_message_conversion_preserves_missing_status() -> None: + item = RealtimeConversationItemAssistantMessage( + id="123", + type="message", + role="assistant", + status=None, + content=[AssistantMessageContent(type="output_text", text=None)], + ) + + converted = _ConversionHelper.conversation_item_to_realtime_message_item(item, None) + + assert isinstance(converted, AssistantMessageItem) + assert converted.status is None def test_system_message_conversion() -> None: diff --git a/tests/realtime/test_session.py b/tests/realtime/test_session.py index 178d37fa8e..00fb2da6cb 100644 --- a/tests/realtime/test_session.py +++ b/tests/realtime/test_session.py @@ -6181,6 +6181,36 @@ async def test_update_agent_validation_failure_keeps_current_agent(self, mock_mo class TestTranscriptPreservation: """Tests ensuring assistant transcripts are preserved across updates.""" + @pytest.mark.asyncio + @pytest.mark.parametrize("incoming_status", ["completed", "incomplete", None]) + async def test_assistant_status_preserved_or_updated_on_item_update( + self, mock_model, mock_agent, incoming_status + ): + session = RealtimeSession(mock_model, mock_agent, None) + session._history = [ + AssistantMessageItem( + item_id="assist_status", + status="completed", + role="assistant", + content=[AssistantAudio(audio=None, transcript="Hello there")], + ) + ] + + await session.on_event( + RealtimeModelItemUpdatedEvent( + item=AssistantMessageItem( + item_id="assist_status", + status=incoming_status, + role="assistant", + content=[AssistantAudio(audio=None, transcript=None)], + ) + ) + ) + + updated_item = cast(AssistantMessageItem, session._history[0]) + assert updated_item.status == (incoming_status or "completed") + assert updated_item.content[0].transcript == "Hello there" + @pytest.mark.asyncio async def test_assistant_transcript_preserved_on_item_update(self, mock_model, mock_agent): session = RealtimeSession(mock_model, mock_agent, None)