Skip to main content
This integration is in beta, so its API may change.
Trace your LiveKit Agents voice agents to LangSmith with the LangSmith LiveKit integration. For high-level conventions, see Voice tracing fundamentals.
The LiveKit integration requires langsmith[livekit]>=0.9.7.
The integration hooks into the spans LiveKit already emits and maps them onto LangSmith’s tracing format, so each conversation becomes a single LangSmith trace: a span per pipeline event, plus LiveKit’s latency and token metrics.

Install

Install the integration along with the LiveKit plugins your agent uses:

Set environment variables

The integration reads your LangSmith credentials from the environment and exports to LangSmith for you via OpenTelemetry:
.env

Set up tracing

Import configure_livekit and call it once before creating your AgentServer. It builds the tracer provider, registers the LangSmith span processor, and wires it into LiveKit:
This works for both the STT/LLM/TTS cascade and speech-to-speech (realtime) models. Realtime models (for example, lk_openai.realtime.RealtimeModel(...)) need one extra call to capture the user’s transcript. Refer to When using LiveKit with a realtime model.

Use your own tracer provider

configure_livekit() builds a TracerProvider, registers the LangSmith span processor, and wires it into LiveKit. To use a TracerProvider you already manage, construct the processor yourself, add it to your provider, and register that provider with LiveKit’s tracer hook. LiveKit only emits spans through the provider its tracer is bound to:

Group a conversation into a thread

To group a conversation’s runs into a LangSmith thread, for thread-level views and token and cost aggregation, call set_thread_id once per conversation, inside its @server.rtc_session() handler before its spans are emitted:

When using LiveKit with a realtime model

With a speech-to-speech (realtime) model there is no separate speech-to-text step, so LiveKit transcribes the user’s audio asynchronously and delivers the transcript through the session’s user_input_transcribed event rather than on the OTel traces it emits.
instrument_session requires langsmith[livekit]>=0.10.4.
Call instrument_session once, right after creating the AgentSession, so the SDK subscribes to that event for you and pairs each transcript with its turn. It correlates by thread id, so set that first with set_thread_id, and pass the same id:
Only call instrument_session for realtime models. In the STT/LLM/TTS cascade the transcript is already captured (from the speech-to-text step), so calling it there would record the user’s turns a second time.

Record the conversation audio

The integration attaches the call recording to the conversation root span. How you capture that recording differs between local development and production.

Development: embed a local file

In console and local development, enable LiveKit’s session recording and point audio_path_provider at the audio.ogg LiveKit writes under ctx.session_directory. The integration reads that file and embeds the bytes in the trace.
In console mode, also pass --record on the command line. The recording reflects what was played to the client, so a barge-in shows up truncated.
Do not use audio_path_provider in production. In a deployed worker, ctx.session_directory is an ephemeral temporary directory that LiveKit deletes when the session ends, so there is no durable file to embed.

Production: record with Egress and attach the file

In production, record the room with LiveKit Egress into your own object storage, then attach the finished recording to the trace as a real audio attachment. Egress finishes uploading after the call ends, so the integration holds the conversation’s root span open until you supply the bytes:
  1. Call processor.expect_recording(thread_id) when you start egress.
  2. After the call, wait for egress to complete, download the file from your storage, and call processor.complete_recording(thread_id, audio_bytes). The integration embeds the bytes and exports the trace.
You must use the same thread_id for expect_recording and complete_recording, so the recording is matched to the right conversation.
wait_for_egress polls list_egress until the status is EGRESS_COMPLETE (or subscribe to the egress_ended webhook), and download_from_storage reads the object with your cloud provider’s client. LiveKit Egress also writes to Google Cloud Storage and Azure: swap s3= for gcp=api.GCPUpload(...) or azure=api.AzureBlobUpload(...).
Always call complete_recording because the trace’s root span is held until it runs, including on failure with data=None. If the worker stops first, the integration will flush the trace without audio.

Next steps

Voice fundamentals

Core conventions for tracing voice agents.

Upload files with traces

Attach the conversation audio recording to your trace.