> ## Documentation Index
> Fetch the complete documentation index at: https://braintrust.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# LiveKit Agents

> Trace real-time voice AI pipelines built with LiveKit Agents, including LLM turns, speech-to-text, text-to-speech, and function tool calls

If you are a coding agent, prefer the Braintrust [`bt` CLI](/docs/reference/cli/quickstart) for repeatable, scriptable work: running evals, instrumenting code, querying logs, syncing data, managing functions, and configuring coding agents. Use the MCP server for reasoning over Braintrust data in conversation, and for capabilities the CLI doesn't cover, such as monitor views, alerts, and authoring evaluators, preprocessors, and facets.

[LiveKit Agents](https://livekit.io/) is a framework for building real-time voice and video AI applications. Braintrust traces LiveKit Agents applications to capture voice interactions, agent sessions, and realtime model usage.

<View title="TypeScript" icon="https://img.logo.dev/typescriptlang.org?token=pk_BdcHD9e5SCW3j1rnJkNyMQ">
  To trace LiveKit Agents with Braintrust's TypeScript SDK, use OpenTelemetry.

  <h2 id="opentelemetry-typescript">
    OpenTelemetry
  </h2>

  Braintrust attaches to LiveKit's built-in OpenTelemetry pipeline by registering a span processor with LiveKit's tracer provider.

  <h3 id="setup-typescript-otel">
    Setup
  </h3>

  <Steps>
    <Step title="Install dependencies">
      <CodeGroup>
        ```bash pnpm theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
        pnpm add @braintrust/otel @livekit/agents @livekit/agents-plugin-openai "@opentelemetry/sdk-trace-node@^1.30" "@opentelemetry/exporter-trace-otlp-http@^0.57" "@opentelemetry/resources@^1.30" "@opentelemetry/semantic-conventions@^1.28" dotenv
        ```

        ```bash npm theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
        npm install @braintrust/otel @livekit/agents @livekit/agents-plugin-openai "@opentelemetry/sdk-trace-node@^1.30" "@opentelemetry/exporter-trace-otlp-http@^0.57" "@opentelemetry/resources@^1.30" "@opentelemetry/semantic-conventions@^1.28" dotenv
        ```
      </CodeGroup>

      <Note>
        `@livekit/agents` is built against OpenTelemetry JS 1.x. Keep every OpenTelemetry package on its 1.x-compatible version, as pinned above. Upgrading any of them to 2.x breaks LiveKit at build or run time. The OTLP exporter packages use a separate `0.x` version sequence that moves in step with the stable 1.x packages, so `@opentelemetry/exporter-trace-otlp-http` pins to `^0.57` rather than `^1`.
      </Note>
    </Step>

    <Step title="Set your environment variables">
      ```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      BRAINTRUST_API_KEY=your-api-key
      BRAINTRUST_PARENT=project_name:livekit-demo
      OPENAI_API_KEY=your-openai-api-key
      ```
    </Step>
  </Steps>

  <h3 id="configure-typescript-otel">
    Configure tracing
  </h3>

  Configure Braintrust's span processor and set it as LiveKit's tracer provider.

  ```typescript title="livekit_agent.ts" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  import "dotenv/config.js";
  import { fileURLToPath } from "url";
  import { BraintrustSpanProcessor, setupOtelCompat } from "@braintrust/otel";
  import {
    cli,
    defineAgent,
    JobContext,
    ServerOptions,
    telemetry,
    voice,
  } from "@livekit/agents";
  import * as openai from "@livekit/agents-plugin-openai";
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
  import { Resource } from "@opentelemetry/resources";
  import {
    ATTR_SERVICE_NAME,
    ATTR_SERVICE_VERSION,
  } from "@opentelemetry/semantic-conventions";

  function setupBraintrustTelemetry() {
    // Setup OTEL compatibility for bidirectional interoperability
    setupOtelCompat();

    // Create BraintrustSpanProcessor with configuration
    const spanProcessor = new BraintrustSpanProcessor({
      apiKey: process.env.BRAINTRUST_API_KEY,
      apiUrl: process.env.BRAINTRUST_API_URL,
      parent: process.env.BRAINTRUST_PARENT,
      // Keep this false: LiveKit's voice spans use lk.* attributes, so AI-span
      // filtering would drop the session, user/agent speaking, and turn spans.
      filterAISpans: false,
    });

    const provider = new NodeTracerProvider({
      resource: new Resource({
        [ATTR_SERVICE_NAME]: "livekit-agent",
        [ATTR_SERVICE_VERSION]: "1.0.0",
      }),
      spanProcessors: [spanProcessor as any],
    });

    // Register the provider with OpenTelemetry's global API
    provider.register();

    // Configure LiveKit to use our tracer provider
    telemetry.setTracerProvider(provider, {
      metadata: {
        component: "livekit-agent",
      },
    });
  }

  export default defineAgent({
    entry: async (ctx: JobContext): Promise<void> => {
      // Setup telemetry
      setupBraintrustTelemetry();

      const session = new voice.AgentSession({
        llm: new openai.realtime.RealtimeModel({
          voice: "coral",
          temperature: 0.8,
        }),
      });

      await session.start({
        agent: new voice.Agent({
          instructions: `You are a helpful and friendly voice AI assistant`,
        }),
        room: ctx.room,
      });

      // Greet participant when they join
      const greetParticipant = async () => {
        session.say("Hello! I'm your voice assistant. How can I help you today?");
      };

      let greeted = ctx.room.remoteParticipants.size > 0;
      if (greeted) greetParticipant();

      ctx.room.once("participantConnected", () => {
        if (!greeted) {
          greeted = true;
          greetParticipant();
        }
      });

      // Wait for room to disconnect
      function waitForRoomDisconnect(room: typeof ctx.room) {
        return new Promise<void>((resolve) => {
          room.once("disconnected", () => resolve());
        });
      }

      await waitForRoomDisconnect(ctx.room);
    },
  });

  // Run the agent
  cli.runApp(
    new ServerOptions({
      agent: fileURLToPath(import.meta.url),
    })
  );
  ```

  <h3 id="what-traced-typescript-otel">
    What Braintrust traces
  </h3>

  These spans come from LiveKit's native OpenTelemetry instrumentation. With the OpenAI realtime model, the LLM, speech-to-text, and text-to-speech work happens inside a single realtime model call rather than as separate spans.

  * Session spans (`agent_session`), the root span covering the full agent session
  * Agent turn spans (`agent_turn`), with the model request, token usage, and realtime model metrics (`gen_ai.*`, `lk.realtime_model_metrics`)
  * User speaking spans (`user_speaking`), with participant and speech detection details
  * Agent speaking spans (`agent_speaking`), covering the agent's spoken response
  * Activity lifecycle spans (`start_agent_activity`, `on_enter`, `on_exit`, `drain_agent_activity`)

  <h3 id="tracing-resources-typescript-otel">
    Tracing resources
  </h3>

  * [OpenTelemetry Integration](/docs/integrations/sdk-integrations/opentelemetry) — Braintrust's OpenTelemetry support
  * [Trace application logic](/docs/instrument/trace-application-logic) — Add custom spans and attributes to your traces
  * [LiveKit Agents overview](https://docs.livekit.io/agents/) — LiveKit Agents documentation
</View>

<View title="Python" icon="https://img.logo.dev/python.org?token=pk_BdcHD9e5SCW3j1rnJkNyMQ">
  To trace LiveKit Agents with Braintrust's Python SDK, use auto-instrumentation or OpenTelemetry. Auto-instrumentation is the recommended path for most users.

  <h2 id="auto-instrumentation-python">
    Auto-instrumentation
  </h2>

  Braintrust patches the internals of LiveKit Agents at startup to emit spans directly through the Braintrust SDK, without requiring an OpenTelemetry pipeline.

  <h3 id="setup-python-auto">
    Setup
  </h3>

  <Steps>
    <Step title="Install the SDK">
      ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      pip install braintrust livekit-agents livekit-plugins-openai
      ```
    </Step>

    <Step title="Set your environment variables">
      ```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      BRAINTRUST_API_KEY=your-api-key
      BRAINTRUST_PARENT=project_name:livekit-demo
      OPENAI_API_KEY=your-openai-api-key
      ```
    </Step>
  </Steps>

  <h3 id="trace-python-auto">
    Trace your agent
  </h3>

  Call `auto_instrument()` once at startup, before any LiveKit imports or client creation:

  ```python title="agent.py" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  import braintrust

  # Set up Braintrust before importing LiveKit so all agent sessions are traced
  braintrust.auto_instrument()
  braintrust.init_logger(project="my-livekit-project")  # Replace with your project name

  from livekit import agents
  from livekit.agents import Agent, AgentSession, RoomInputOptions
  from livekit.plugins import openai

  class Assistant(Agent):
      def __init__(self) -> None:
          super().__init__(instructions="You are a helpful voice AI assistant.")

  async def entrypoint(ctx: agents.JobContext):
      session = AgentSession(llm=openai.realtime.RealtimeModel(voice="coral"))
      await session.start(
          room=ctx.room,
          agent=Assistant(),
          room_input_options=RoomInputOptions(),
      )

  if __name__ == "__main__":
      agents.cli.run_app(agents.WorkerOptions(entrypoint_fnc=entrypoint))
  ```

  <Accordion title="Set up manually">
    To instrument only specific sessions rather than patching globally, use `setup_livekit_agents()` instead of `auto_instrument()`:

    ```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    from braintrust.integrations.livekit_agents import setup_livekit_agents

    setup_livekit_agents()
    ```
  </Accordion>

  <Accordion title="Opt out of LiveKit Agents tracing">
    If you use `auto_instrument()` for other libraries but want to exclude LiveKit Agents, pass `livekit_agents=False`:

    ```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    braintrust.auto_instrument(livekit_agents=False)
    ```
  </Accordion>

  <h3 id="what-traced-python-auto">
    What Braintrust traces
  </h3>

  The example above uses the OpenAI realtime model, which handles the LLM, speech-to-text, and text-to-speech work inside a single model call. A component [STT-LLM-TTS pipeline](https://docs.livekit.io/agents/models/pipelines) runs those as separate steps, each producing its own spans.

  * Session spans (`livekit_agent_session`), with the full voice turn lifecycle
  * User speaking spans (`user_speaking`), with speech detection events
  * Agent speaking spans (`agent_speaking`), covering the agent's spoken response audio.
  * Tool spans (`function_tool`), with tool name, arguments, and output (when your agent defines tools).
  * Additional spans when you use a [STT-LLM-TTS pipeline](https://docs.livekit.io/agents/models/pipelines) instead of a realtime model:
    * LLM spans (`llm_request_run`), with model inputs, outputs, and token usage.
    * Text-to-speech spans (`tts_request`), with input text and latency metrics.
    * Speech-to-text spans (`stt_processing`), with transcript and latency metrics.
    * VAD spans (`vad_endpointing`), with silence and speech duration metrics.
    * End-of-utterance spans (`eou_detection`), with speech and silence timing.

  <h3 id="audio-attachments-python-auto">
    Audio attachments
  </h3>

  Audio attachments are disabled by default. Two environment variables control whether audio frames are attached to spans:

  * Set `BRAINTRUST_CAPTURE_AGENT_AUDIO_ATTACHMENTS=true` to attach the agent's spoken response audio to `agent_speaking` spans.
  * Set `BRAINTRUST_CAPTURE_USER_AUDIO_ATTACHMENTS=true` to attach user microphone audio to `user_speaking` spans.

  <Warning>
    As of v0.32.0, agent audio attachments are disabled by default. If you captured audio before v0.32.0, set `BRAINTRUST_CAPTURE_AGENT_AUDIO_ATTACHMENTS=true` to restore audio capture on `agent_speaking` spans.
  </Warning>

  <h3 id="tracing-resources-python-auto">
    Tracing resources
  </h3>

  * [Trace LLM calls](/docs/instrument/trace-llm-calls) — Auto-instrumentation setup and supported libraries
  * [LiveKit Agents overview](https://docs.livekit.io/agents/) — LiveKit Agents documentation

  <h2 id="opentelemetry-python">
    OpenTelemetry
  </h2>

  Braintrust attaches to LiveKit's built-in OpenTelemetry pipeline by registering a span processor with LiveKit's tracer provider.

  <h3 id="setup-python-otel">
    Setup
  </h3>

  <Steps>
    <Step title="Install dependencies">
      ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      pip install "braintrust[otel]" livekit-agents livekit-plugins-openai livekit-plugins-noise-cancellation opentelemetry-sdk
      ```
    </Step>

    <Step title="Set your environment variables">
      ```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      BRAINTRUST_API_KEY=your-api-key
      BRAINTRUST_PARENT=project_name:livekit-demo
      OPENAI_API_KEY=your-openai-api-key
      ```
    </Step>
  </Steps>

  <h3 id="configure-python-otel">
    Configure tracing
  </h3>

  Configure Braintrust's span processor and set it as LiveKit's tracer provider.

  ```python title="livekit_agent.py" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  from braintrust.otel import BraintrustSpanProcessor
  from livekit import agents
  from livekit.agents import Agent, AgentSession, RoomInputOptions
  from livekit.agents.telemetry import set_tracer_provider
  from livekit.plugins import noise_cancellation, openai
  from opentelemetry.sdk.trace import TracerProvider

  def setup_braintrust_telemetry():
      """Setup Braintrust OTEL telemetry for agent monitoring"""
      trace_provider = TracerProvider()
      trace_provider.add_span_processor(BraintrustSpanProcessor())
      set_tracer_provider(trace_provider)

  class Assistant(Agent):
      def __init__(self) -> None:
          super().__init__(instructions="You are a helpful voice AI assistant.")

  async def entrypoint(ctx: agents.JobContext):
      # Setup telemetry
      setup_braintrust_telemetry()

      # Create agent session with OpenAI realtime model
      session = AgentSession(llm=openai.realtime.RealtimeModel(voice="coral"))

      # Start session with assistant agent
      await session.start(
          room=ctx.room,
          agent=Assistant(),
          room_input_options=RoomInputOptions(
              noise_cancellation=noise_cancellation.BVC(),
          ),
      )

  # Run script locally with `python livekit_agent.py console`
  if __name__ == "__main__":
      agents.cli.run_app(agents.WorkerOptions(entrypoint_fnc=entrypoint))
  ```

  You can add attributes to spans using `span.set_attribute()` to enrich your traces with custom metadata.

  <h3 id="what-traced-python-otel">
    What Braintrust traces
  </h3>

  These spans come from LiveKit's native OpenTelemetry instrumentation. With the OpenAI realtime model, the LLM, speech-to-text, and text-to-speech work happens inside a single realtime model call rather than as separate spans.

  * Session spans (`agent_session`), the root span covering the full agent session
  * Agent turn spans (`agent_turn`), with the model request, token usage, and realtime model metrics (`gen_ai.*`, `lk.realtime_model_metrics`)
  * User speaking spans (`user_speaking`), with participant and speech detection details
  * Agent speaking spans (`agent_speaking`), covering the agent's spoken response
  * Activity lifecycle spans (`start_agent_activity`, `on_enter`, `on_exit`, `drain_agent_activity`)

  <h3 id="tracing-resources-python-otel">
    Tracing resources
  </h3>

  * [OpenTelemetry Integration](/docs/integrations/sdk-integrations/opentelemetry) — Braintrust's OpenTelemetry support
  * [LiveKit Agents overview](https://docs.livekit.io/agents/) — LiveKit Agents documentation
</View>
