Skip to content

AMD: opt-in to process carrier early-media audio (183 Session Progress + SDP) instead of always deferring to sip.callStatus == "active" #6895

Description

@SFGMorales

Feature Type

Would make my life easier

Feature Description

AMD (livekit.agents.AMD) deliberately defers all audio processing — the no-speech timer (#5848) and, as of 1.6.9, the detection timeout too (#6187 / #6580) — until sip.callStatus == "active". Both fixes treat all pre-answer audio uniformly as "ringback / carrier early media / dialtone" noise that should never influence classification, which is correct when early media is just a ringback tone.

We run outbound voice AI calls through several Latin American mobile carriers, and we've confirmed that at least a couple of them (seen so far in Peru) send the ringing tone, system announcements, or the voicemail greeting itself via early media — SIP 183 Session Progress with an SDP body — rather than only after a final 200 OK. For calls that reach voicemail on these carriers, the 200 OK either arrives well after the greeting has already played, or the leg never produces a clean "answered" transition the way AMD expects. Deferring to active in that situation doesn't protect the classifier from noise — it discards the only audio that was ever going to be available, so the call falls through as uncertain/no-answer instead of being correctly classified as machine-vm.

This is different from what #6187 fixed: that issue was about timer accounting (a detection_timeout armed at track-subscription burning down during a long ring before a late active answer arrives — the useful audio there was always post-answer). In our case the useful audio is the pre-answer early media itself.

We're not asking to change the default behavior — the ringback-only case #5848/#6187 fixed is the common case and shouldn't regress. We'd like an opt-in, e.g. a process_early_media: bool (default False) on AMD.__init__ or DetectionOptions, that — when set — starts start_listening() (timers + transcript processing) from track subscription instead of gating behind sip.callStatus == "active" via _wait_for_sip_answer. This can't be auto-detected safely (no way to tell "informative early media" from generic ringback without listening to it), so it needs to be an integrator-set flag per call/trunk rather than a new default heuristic — trunk/carrier knowledge of which ones do this lives on our side, not the SDK's.

Workarounds / Alternatives

Checked whether upgrading fixes this: it doesn't. Confirmed the same defer-to-active behavior is present through 1.6.9 (latest with release notes as of this report) — #6580 actually reinforces it further (anchors the detection timeout to active too, on top of the no-speech timer from #5848).

The only workaround we can see today is bypassing AMD entirely and reimplementing the classifier/timer orchestration ourselves outside the SDK so we can start listening from track subscription for specific trunks — a much larger lift than an opt-in flag, and it means losing the SDK's built-in AMD maintenance/improvements going forward.

Additional Context

  • SIP mechanism: RFC 3261 183 Session Progress responses can include an SDP body and start early media before the call is answered — see LiveKit's own SIP handshake reference, step 2.2.
  • livekit-agents version: 1.6.4 in production; confirmed same behavior through 1.6.9.
  • Setup: outbound SIP calls via a self-hosted LiveKit server with a SIP proxy in front of direct carrier SBCs.
  • Confirmed so far in Peru; we suspect the pattern isn't unique to one country/carrier given how common early-media announcements are across LATAM mobile networks, but we don't have hard confirmation elsewhere yet.
  • Happy to provide a redacted SIP trace (signaling only, no RTP/audio) showing the 183+SDP → late/absent 200 OK timing if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions