Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/agents/realtime/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ async def on_event(self, event: RealtimeModelEvent) -> None:

# If still missing and this is an assistant item, fall back to
# accumulated transcript deltas tracked during the turn.
if incoming_item.role == "assistant":
if not preserved and incoming_item.role == "assistant":
preserved = self._item_transcripts.get(incoming_item.item_id)

if preserved:
Expand Down
38 changes: 38 additions & 0 deletions tests/realtime/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -2447,3 +2447,41 @@ async def test_assistant_transcript_can_fallback_to_deltas(self, mock_model, moc
preserved_item = cast(AssistantMessageItem, session._history[0])
assert isinstance(preserved_item.content[0], AssistantAudio)
assert preserved_item.content[0].transcript == "partial transcript"

@pytest.mark.asyncio
async def test_existing_transcript_not_overwritten_by_stale_deltas(
self, mock_model, mock_agent
):
"""Existing transcripts must take precedence over leftover delta accumulators.

``_item_transcripts`` is keyed by item_id and persists across updates within a
turn. When the model retrieves an item without a transcript, the merge should
fall back to deltas only when no existing transcript is present – otherwise
the complete transcript already in history would be clobbered by partial
(or stale) delta state.
"""
session = RealtimeSession(mock_model, mock_agent, None)

# History already has the completed transcript for the item.
initial_item = AssistantMessageItem(
item_id="assist_3",
role="assistant",
content=[AssistantAudio(audio=None, transcript="Final complete transcript")],
)
session._history = [initial_item]

# Simulate stale/leftover delta state for the same item id.
session._item_transcripts["assist_3"] = "stale partial"

# Update arrives without transcript populated; merge must keep the existing
# complete transcript rather than reverting to the stale delta accumulator.
update_without_transcript = AssistantMessageItem(
item_id="assist_3",
role="assistant",
content=[AssistantAudio(audio=None, transcript=None)],
)
await session.on_event(RealtimeModelItemUpdatedEvent(item=update_without_transcript))

preserved_item = cast(AssistantMessageItem, session._history[0])
assert isinstance(preserved_item.content[0], AssistantAudio)
assert preserved_item.content[0].transcript == "Final complete transcript"
Loading