Skip to content

Interruptions

This recipe shows how to script a user interrupting the agent mid-reply, and explains how the SDK handles the interrupt signal differently depending on whether the adapter supports native barge-in or falls back to VAD-driven detection.

Pattern

Two equivalent forms produce identical runtime behaviour.

Unrolled form

python
scenario.user("Tell me about my billing"),
scenario.agent(wait=False),          # start agent reply in background
scenario.user("Wait — I meant account support"),  # interrupt when agent begins speaking
scenario.agent(),                    # let the agent finish the recovery turn

agent(wait=False) / agent({ wait: false }) starts the adapter's reply in the background without blocking the script. The following steps run while the agent keeps speaking; the next user() turn fires the interrupt and sends the replacement user audio. (In TypeScript, scenario.voiceAgent({ wait: false }) is an exported alias of the same step.)

Sugar form

python
scenario.user("Tell me about every product feature you offer"),
scenario.interrupt("Sorry — what are your business hours?"),
scenario.agent(),

scenario.interrupt(...) is shorthand for the unrolled form above. Prefer it when you only need to express a single interrupt. In TypeScript, interrupt takes an options object with three trigger modes:

scenario.interrupt({ after: 2, content: "Wait, that's wrong!" });        // time-based
scenario.interrupt({ afterWords: 5, content: "No, that's not it" });     // word-count (needs streaming transcripts)
scenario.interrupt({ content: "Hold on—" });                            // first-chunk barge-in

afterWords requires an adapter with streaming transcripts; otherwise it raises voice.UnsupportedCapabilityError suggesting interrupt({ content }) instead.

Native barge-in vs VAD-driven barge-in

When user() fires the interrupt, the SDK checks whether the adapter reports native interrupt support (the interruption column in the capability matrix).

ModeHow it works
Native barge-inThe adapter sends a provider-side cancel signal (response.cancel for OpenAI Realtime, clear for Twilio, etc.). The agent's TTS stops immediately and the user turn is queued.
VAD barge-inNo provider cancel signal is available. The user audio overlaps with the agent TTS and the SUT's Voice Activity Detection detects barge-in and stops the agent. Requires native_vad support on the adapter side.

Check the adapter row in the capability matrix to see which mode applies to your deployment.

Random interruptions across a proceed loop

The patterns above script a single deterministic barge-in. For multi-turn probabilistic interruptions (where any agent turn may be cut off with a given probability), set interruptProbability on the user simulator. This is the parity-clean knob shared by both SDKs: it drives barge-ins on any plain scenario.proceed() loop, and the SDK builds the interruption config internally.

typescript
scenario.userSimulatorAgent({
  voice: "openai/nova",
  interruptProbability: 0.5,   // interrupt ~50% of agent turns
})

Then let the conversation run with a plain proceed(); barge-ins fire automatically per interruptProbability:

typescript
script: [
  scenario.agent(),                        // capture the opening greeting
  scenario.user("I need help with my account"),
  scenario.proceed(5),                     // barge-ins fire per interruptProbability
  scenario.judge(),
]

interruptProbability controls how often a turn is interrupted. The interruption's timing (a short delay after the agent starts speaking) and its phrasing (a canned interjection) use built-in defaults; they are not separately configurable from the public API. InterruptionConfig remains exported under the voice namespace for inspection, but it is constructed internally.

Worked example

Python:

interruption_recovery.py — demonstrates both unrolled and sugar forms back-to-back in a single scenario, with a JudgeAgent verifying the agent recovered from both interruptions.

Adapter-specific variants:

TypeScript:

See also