Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/adaptive-interruption-send-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@livekit/agents': patch
---

Adaptive interruption: stop dropping audio at the send boundary, and make the verdict diagnosable.

The WebSocket transport re-checked `overlapSpeechStarted` immediately before writing to the socket, after awaiting any in-flight reconnect. Because `overlap-speech-ended`, `agent-speech-ended` and `bargein_detected` can all clear that flag inside the await window, audio the pipeline had already committed to sending could be discarded and never counted in `numRequests`. The buffering stage upstream is the only place that decides whether a slice belongs to an overlap, which matches the Python implementation, whose send task gates on nothing.

Slices now carry the overlap they were cut for, so a send that lands after that overlap closed is no longer counted against whichever overlap happens to be open when the socket accepts it. Without this, a later overlap could report a `numRequests` it never made.

An overlap that ends without any usable inference result now logs at `warn` (previously `debug`) with the overlap duration, `numRequests`, buffered samples and agent-speech state, so a fallback backchannel verdict is no longer indistinguishable from a genuine low-probability one. Every verdict also logs `probability`, `isInterruption` and `numRequests` at `debug`. `OverlappingSpeechEvent` is now exported by name for typing `overlapping_speech` handlers.
15 changes: 15 additions & 0 deletions .changeset/fix-recorder-capture-finish-race.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@livekit/agents': patch
---

Fix a deadlock where a recorder-wrapped audio output could leave `waitForPlayout` stranded when
an interrupt arrived before the recorder had registered its segment. `RecorderAudioOutput` now
registers its own segment before forwarding a frame downstream, and attributes each playback
finish to the segment it belongs to instead of relying on a global counter.

A recorded segment is also timestamped when it opens rather than when the wrapped output accepts
its first frame, so a finish that lands while that frame is parked no longer clamps the segment's
playback position to zero and drop the audio the sink reported as played. And a segment whose
downstream capture throws now releases the capture latch on both the recorder and the wrapped
output, so a caller that retries after a transient rejection is no longer rejected forever with
`recorder capture has no active segment`.
Loading
Loading