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

Vapi Assistants

In deepeval, you can run a voice simulation against an assistant hosted on Vapi. The connector creates a call on Vapi's WebSocket transport, plays the simulated user's audio into it, and captures the spoken reply along with how long it took to start.

No phone number is involved. Vapi rejects phone parameters on this transport, so the simulator is the only caller and nothing routes through telephony. The connector speaks Vapi's API directly, so no Vapi SDK is required.

Setting Up Your API Key

Your key mints the call that the WebSocket carries, so it's required. DeepEval autoloads .env.local then .env at import time (process env -> .env.local -> .env).

Recommended (local dev):

# .env.local
VAPI_API_KEY=<your-vapi-api-key>

Alternative (Shell/CI):

export VAPI_API_KEY=<your-vapi-api-key>

Alternative (notebook):

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

%env VAPI_API_KEY=<your-vapi-api-key>

In Code

Pass a VapiConnector to VoiceConfig, naming the assistant to call:

from deepeval.voice import VapiConnector, VoiceConfig

connector = VapiConnector(assistant_id="your-assistant-id")
voice_config = VoiceConfig(
    connector=connector,
    ...,
)

There are ONE mandatory and THREE optional parameters when creating a VapiConnector, alongside the shared connector settings:

  • assistant_id: A string identifying the Vapi assistant to call.
  • [Optional] api_key: A string specifying your Vapi API key. Defaults to VAPI_API_KEY if not passed; raises at connect time if neither is set.
  • [Optional] assistant_overrides: A dictionary of assistant overrides for this call, such as variableValues, firstMessage, or model. Defaulted to None.
  • [Optional] base_url: A string specifying a custom endpoint to reach the Vapi API through. Defaulted to https://api.vapi.ai.
  • [Optional] turn_detection: A string preset β€” "eager", "balanced", or "patient" β€” controlling how long a pause has to last before your assistant is considered finished speaking. Defaulted to "balanced".

Personalizing a Call

assistant_overrides is sent per call, so one assistant can be simulated as many different callers β€” a different customer, tier, or opening line per golden:

connector = VapiConnector(
    assistant_id="your-assistant-id",
    assistant_overrides={
        "variableValues": {"customer_name": "Alice", "tier": "enterprise"},
        "firstMessage": "Hi, thanks for calling. How can I help?",
    },
)

Transcripts

Your assistant's own final transcripts arrive over the same socket and become the assistant Turn.content, so no transcription runs on turns where they land. Vapi sends one final per utterance rather than one per turn, and the connector joins them, so a reply made of several sentences is recorded whole.

Turns without a transcript still fall back to your speech-to-text model, which VoiceConfig resolves either way β€” so it still needs to be configured, credentials included.

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

On this page