Skip to main content
The OpenAI Realtime API powers low-latency speech-to-speech voice agents. This guide shows how to trace a Realtime app to LangSmith. OpenAI Realtime is a speech-to-speech model: it processes audio natively and exchanges a continuous stream of typed JSON events with your application over a persistent WebSocket connection, rather than making discrete request/response calls. The following sections show those events and how to turn them into a LangSmith trace. For our high-level philosophy on getting the most out of your voice agent traces, see Voice tracing fundamentals. For a complete implementation, see the voice demo repository.

The event model

Every event has a discriminated type string that indicates what it represents: audio, a tool call, and so on. The client sends events to configure the session and stream audio. You do not trace these as spans; they are requests, and their effects come back as server events: The server sends back events:

How events map to LangSmith runs

We recommend tracing the whole conversation as a single trace, with one span per traced event in arrival order:
The noise rule: we recommend skipping every event type ending in .delta, because the matching .done event repeats the complete payload. Tracing both records everything twice. response.output_audio.delta in particular is the agent’s voice: hundreds of chunks per response that would bury the trace. Play it to the speaker, but never make it a span.

Installation

The demo also uses sounddevice and numpy for the mic/speaker and to build the WAV attachment.

Set up your environment

The following steps demonstrate how to trace using the LangSmith SDK. You can also trace using OpenTelemetry directly. See Trace with OpenTelemetry.

Quickstart

This guide focuses on the tracing layer. It assumes you already have a working Realtime app: the WebSocket connection, the session.update configuration, and your microphone and speaker I/O. For a complete, runnable implementation, see the voice demo repository. Enable input_audio_transcription (and the agent transcript) in your session.update, or the transcription events that make the trace readable never arrive.

Step 1: Open the conversation root at connect time

Use one RunTree per conversation:
A stable thread_id you generate per conversation (for example, a UUID) groups the trace into a LangSmith thread; ls_modality="audio" marks it as a voice conversation.

Step 2: Span each received event, skipping the noise

Define a small helper that opens a child run for one event, records the scrubbed payload as the run’s input, and closes it when the block exits:
Then loop over the events from your open Realtime connection, skipping the .delta noise and tracing the rest:

Step 3: Run tools nested under the announcing event

Attach the conversation audio

To listen to a conversation alongside its transcript, attach a single combined recording of the whole conversation to the root run. Record both the user and the agent in one file (for example, a stereo WAV with the user mic on one channel and the agent on the other), captured from what was played to the client so the recording reflects what was actually heard, including speech cut off by a barge-in. The Realtime API streams agent audio as response.output_audio.delta events: decode and write those bytes to your output device, and tap that same output to build the recording. For the underlying attachment API, see Upload files with traces. For the cross-provider rationale, see Record a single combined audio file. When the conversation ends, finalize the root run:

Next steps

Voice fundamentals

Core conventions for tracing voice agents.

Upload files with traces

Attach the conversation audio recording to your trace.