πŸ’₯ BREAKING CHANGE: All metric scores are now HIGHER THE BETTER. Read changelog β†’
Voice Agent Connectors

LiveKit Agents

In deepeval, you can run a voice simulation against a LiveKit agent. deepeval joins your agent's room as another participant and trades audio with it over WebRTC, so the agent has to be deployed and able to join a room.

Setting Up Your Credentials

A LiveKit agent has no id you can dial β€” it's a worker dispatched into a room. So instead of naming the agent, you give deepeval credentials for your LiveKit project and it mints a token and creates a room. DeepEval autoloads .env.local then .env at import time (process env -> .env.local -> .env).

Recommended (local dev):

# .env.local
LIVEKIT_URL=wss://your-project.livekit.cloud
LIVEKIT_API_KEY=<your-livekit-api-key>
LIVEKIT_API_SECRET=<your-livekit-api-secret>

Alternative (Shell/CI):

export LIVEKIT_URL=wss://your-project.livekit.cloud
export LIVEKIT_API_KEY=<your-livekit-api-key>
export LIVEKIT_API_SECRET=<your-livekit-api-secret>

Alternative (notebook):

If you're working in a notebook environment (Jupyter or Colab), set your credentials in a cell:

%env LIVEKIT_URL=wss://your-project.livekit.cloud
%env LIVEKIT_API_KEY=<your-livekit-api-key>
%env LIVEKIT_API_SECRET=<your-livekit-api-secret>

In Code

Pass a LiveKitConnector to VoiceConfig, naming the agent to dispatch into the room:

from deepeval.voice import LiveKitConnector, VoiceConfig

connector = LiveKitConnector(
    url="wss://your-project.livekit.cloud",
    api_key="your-api-key",
    api_secret="your-api-secret",
    agent_name="restaurant-agent",
)
voice_config = VoiceConfig(
    connector=connector,
    ...,
)

There are ZERO mandatory and ELEVEN optional parameters when creating a LiveKitConnector.

  • [Optional] url: A string LiveKit server URL. Defaults to LIVEKIT_URL, and is required unless room is already connected.
  • [Optional] api_key: A string LiveKit API key. Defaults to LIVEKIT_API_KEY, and is required to mint a token.
  • [Optional] api_secret: A string LiveKit API secret. Defaults to LIVEKIT_API_SECRET, and is required to mint a token.
  • [Optional] room: An rtc.Room to use instead of creating one, connected or not. Defaulted to None.
  • [Optional] token: A LiveKit access token you minted yourself, used instead of api_key and api_secret. Defaulted to None.
  • [Optional] room_name: A string naming the room to join. Defaulted to None, which creates a room for the session.
  • [Optional] identity: A string participant identity for the simulator. Defaulted to "deepeval-test".
  • [Optional] agent_name: A string naming the agent to dispatch into the room. Defaulted to None, which leaves your existing routing to place the agent there.
  • [Optional] turn_detection: A string preset β€” "eager", "balanced", or "patient" β€” controlling how long a pause has to last before your agent is considered finished speaking. Defaulted to "balanced".
  • [Optional] connect_timeout_s: A float number of seconds to wait when establishing the room connection. Defaulted to 15.0.
  • [Optional] transcript_grace_s: A float number of seconds to wait after your agent stops speaking for its transcript to arrive. Defaulted to 1.5.

room_name and agent_name are what actually find your agent. Omit room_name and the connector creates a room; set agent_name and LiveKit dispatches that agent into it.

Bringing Your Own Room

If your test setup already joined a room, hand that over instead and skip the credentials:

from livekit import rtc
from deepeval.voice import LiveKitConnector, VoiceConfig

room = rtc.Room()
await room.connect("wss://your-project.livekit.cloud", your_token)

voice_config = VoiceConfig(
    connector=LiveKitConnector(room=room),
    ...,
)

Concurrent conversations each get their own session, cloned from the connector you passed. A connector holding a room you connected yourself can't be cloned, so give VoiceConfig a zero-argument callable that builds a fresh connector instead of the connector itself.

deepeval publishes a microphone track, listens for the agent, and leaves the room as it found it β€” a room you connected stays connected afterwards, with just the simulated microphone removed. An unconnected room gets connected using the parameters above.

For how connectors fit into the simulation loop, and for turn detection in full, see Voice Connectors.

On this page