Skip to content

Keep speculative turn revisions synchronized with Realtime client history #487

Description

@andimarafioti

Problem

The speculative VAD/STT pipeline can reopen and supersede revisions of one logical user turn. Internally, RealtimeService can replace the previous speculative user message with the latest cumulative transcript. That replacement is not currently represented equivalently on the Realtime wire.

A generic Realtime client only knows standard event identity:

  • transcription events are grouped by item_id;
  • conversation.item.input_audio_transcription.completed is terminal for that item;
  • a different item_id represents a different user item;
  • there is no standard supersedes, transcript-retraction, or in-place transcript-replacement event.

When speech resumes after an earlier revision has already emitted completed, the server can emit another cumulative completion under a new item ID while replacing the earlier message only in its internal chat history. A normal client then retains multiple independent user turns even though the LLM sees one revised turn.

For example, one utterance with pauses can appear on the wire as:

item_A completed: "The quick brown fox"
item_B completed: "The quick brown fox jumps over the lazy dog"
item_C completed: "The quick brown fox jumps over the lazy dog ... returns home"

The bundled/local renderers can visually mask this by replacing the current user display, but a protocol client keyed by item_id cannot infer that B supersedes A or that C supersedes B.

Protocol constraints

The OpenAI Realtime lifecycle associates input_audio_buffer.speech_started and speech_stopped with the user message item created from that speech segment. Incremental transcript deltas add text to one (item_id, content_index), and the completed event provides that committed item's final transcript.

There is no standard server event for revising an already completed input transcription in place. Deleting and recreating an item is possible only as an explicit conversation-history operation and also requires handling any response already based on the deleted item.

Scope

  • Define when a speculative turn revision becomes externally committed as a Realtime user item.
  • Keep speculative revisions internal until the server is willing to expose a permanent item, or explicitly synchronize any replacement through standard conversation lifecycle events.
  • Ensure the client-visible user-item history and the server/LLM chat history converge.
  • Define behavior when speech resumes:
    • before the current speculative revision has committed;
    • after an input item has emitted transcription completion;
    • after assistant generation has started or produced committed output.
  • Preserve cancellation and response ordering when a still-uncommitted revision is superseded.
  • Add protocol-level tests using only standard events and item IDs, without relying on bundled-client knowledge of internal turn revisions.

Acceptance criteria

  • A generic Realtime client that maintains history solely from standard events ends with the same committed user-turn history as the server/LLM.
  • Superseded, uncommitted revisions do not publish terminal user items, terminal transcription events, or accepted assistant output.
  • Once a user item emits conversation.item.input_audio_transcription.completed, its transcript is not silently replaced only in server-side chat state.
  • Continuing speech after an externally committed item is either represented as a genuine new item containing only the new turn/segment, or the earlier item is explicitly removed/replaced with a fully synchronized lifecycle.
  • One long utterance with several Smart Turn/VAD pauses is covered end to end, including a pause long enough for final STT and a later continuation.
  • Tests assert item identity, event ordering, client history, internal chat history, and assistant cancellation/commit behavior.

Non-goals

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions