> ## 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](/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, such as ad-hoc lookups and exploration from your IDE.

[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](/integrations/sdk-integrations/opentelemetry) — Braintrust's OpenTelemetry support
  * [Trace application logic](/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="tracing-resources-python-auto">
    Tracing resources
  </h3>

  * [Trace LLM calls](/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](/integrations/sdk-integrations/opentelemetry) — Braintrust's OpenTelemetry support
  * [LiveKit Agents overview](https://docs.livekit.io/agents/) — LiveKit Agents documentation
</View>
