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 — use voiceProceed with an InterruptionConfig:

typescript
import scenario, { voice } from "@langwatch/scenario";
 
const { InterruptionConfig } = voice;
 
script: [
  scenario.agent(),                        // capture the opening greeting
  scenario.user("I need help with my account"),
  scenario.voiceProceed({
    turns: 5,
    interruptions: new InterruptionConfig({
      probability: 0.5,          // interrupt ~50% of agent turns
      delayRange: [0.5, 2.0],   // barge-in 0.5–2s after agent starts speaking
      strategy: "random_phrase", // pick from the built-in canned-phrase pool
    }),
  }),
  scenario.judge(),
]

InterruptionConfig options:

OptionTypeDefaultDescription
probabilitynumber0.3Fraction of agent turns to interrupt.
delayRange[number, number][0.5, 3.0]Seconds to wait after agent starts speaking before firing the barge-in.
strategy"random_phrase" | "contextual""random_phrase""random_phrase" picks from the canned phrase pool; "contextual" generates a realistic interjection via an LLM from the running conversation.
phrasesstring[]built-in poolPhrase pool for "random_phrase" (and few-shot examples for "contextual").

Per-simulator interrupt probability

As an alternative to voiceProceed({ interruptions }), set interruptProbability directly on the user simulator. This drives barge-ins on any plain scenario.proceed() loop without needing an explicit InterruptionConfig:

typescript
scenario.userSimulatorAgent({
  voice: "openai/nova",
  interruptProbability: 0.3,   // 30% of agent turns get a barge-in
})

When both are configured, the voiceProceed({ interruptions }) config wins.

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