Skip to content

feat(azure): add STT cancellation recovery - #2009

Merged
toubatbrian merged 5 commits into
mainfrom
port-azure-stt-cancellation-recovery
Jul 15, 2026
Merged

feat(azure): add STT cancellation recovery#2009
toubatbrian merged 5 commits into
mainfrom
port-azure-stt-cancellation-recovery

Conversation

@rosetta-livekit-bot

@rosetta-livekit-bot rosetta-livekit-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports livekit/agents#6362 by adding the missing Azure streaming STT plugin infrastructure to agents-js and preserving the cancellation-error recovery behavior: Azure error cancellations wake the stream, surface an APIConnectionError, and allow the base STT retry/fallback path to proceed instead of hanging.

Source diff coverage

Coverage
Source file Classification
livekit-plugins/livekit-plugins-azure/livekit/plugins/azure/stt.py Adapted to plugins/azure/src/stt.ts. The target repo had no Azure plugin/package, so this PR adds the Azure STT package infrastructure and ports the streaming STT behavior needed for the source fix, including _cancellationError tracking, clearing it per recognition run, waking the stopped event from error cancellations, and raising a retryable APIConnectionError when the stopped path observes the cancellation.
tests/test_plugin_azure_stt.py Adapted to plugins/azure/src/stt.test.ts. The source unit tests are ported to Vitest and target the JS SDK cancellation event shape while preserving the same assertions: error cancellations unblock the run and non-error cancellations are ignored.

Validation

  • pnpm test plugins/azure
  • pnpm --filter @livekit/agents-plugin-azure lint
  • pnpm --filter @livekit/agents-plugin-azure build
  • pnpm --filter @livekit/agents-plugin-azure api:check
  • pnpm build
  • pnpm lint (passes; existing warnings remain in unrelated packages)
  • Focused Azure cancellation recovery and fallback tests: 23 passing.

Notes

The source diff was 2 files and below the >20 files or >1000 lines planning POST threshold, so no port-plan POST was required.


Ported from livekit/agents#6362

Original PR description

If Azure STT hits an error cancellation (like a service timeout), the stream just goes silent. _on_canceled only logged a warning, and Azure doesn't reliably send session_stopped after that, so _run blocks on its asyncio.wait forever. Audio keeps flowing into a dead recognizer and nothing raises, so retry and fallback never kick in.

Now an error cancel records the details and sets the stopped event, so _run wakes and raises APIConnectionError. The base retry loop and FallbackAdapter handle it from there.

I kept it on the base retry path instead of adding retry inside the plugin like the earlier attempt (#5333). Happy to flip it if you'd rather.

Tests in tests/test_plugin_azure_stt.py cover both cases.

Fixes #5322

Cue voice E2E

  • Exact verified head: 4f3b4cd9496faff705094d1d56faaa6c927f5695.
  • Voice session: sid_84f8daeb8661.
  • Recovery predicate: debug_message(kind=azure_recovery, phase=recovered) -> final user_input_transcribed(azure cancellation recovery verified) -> AS_SPEAKING -> AS_LISTENING. It resolved in 7.720 seconds only after assistant playout completed.
  • Stability predicate: bounded 2-second absence of recognizer_start(attempt=3); resolved in 2.001 seconds. The complete persisted event stream contains zero attempt-3 events.
  • Recovery boundary counters: attempts=2, reconnect_count=1, max_active_readers=1, active_readers=1, dead_stream_writes=0, recovered_frames=8.
  • Decisive persisted sequence: replacement recognizer at 7,710 ms; recovery counters and final transcript at 7,968 ms; AS_SPEAKING at 12,284 ms; AS_LISTENING at 14,288 ms. Worker logs also record the injected timeout, retryable APIConnectionError, replacement START_OF_SPEECH, final transcript, and uninterrupted playout completion.
  • Session WAV: stereo PCM 16-bit/48 kHz, 25.593750 seconds, 4,914,044 bytes, SHA-256 ff695c0d88fdbe78d0cce2ea6225e827e0084b5d268bd06ec052702adbc1ee31.
  • Recovery-command WAV: stereo PCM 16-bit/48 kHz, 7.725083 seconds, 1,483,260 bytes, SHA-256 47fc40af0bf9c0326fb896143d6f5d04749d698800442eefb9f67f940f2fdbeb.
  • Persisted events.jsonl: SHA-256 85e569779ebd79079de3062464d958791804409b10a9e8654717db123aaef98a. Recovery result.json: SHA-256 833b32dd58c18b55ba0c2b347c5042b0cb53ca33c726342ca224ee8279f5db1b.
  • Both WAVs were directly inspected: both stereo channels contain nonzero audio. Session RMS L/R was 2106.03/2256.87; recovery-command RMS L/R was 2869.46/4107.92.
  • Provider limitation: Azure credentials were unavailable, so STT used a deterministic Azure-SDK-compatible boundary that injected one ServiceTimeout cancellation. LiveKit Cloud transport plus production LiveKit Inference LLM/TTS were real; this was not a production Azure STT call.

@rosetta-livekit-bot
rosetta-livekit-bot Bot requested a review from a team as a code owner July 9, 2026 08:28
@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4f3b4cd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 37 packages
Name Type
@livekit/agents-plugin-azure Patch
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-anthropic Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch
@livekit/agents-plugins-test Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@rosetta-livekit-bot
rosetta-livekit-bot Bot requested a review from longcw July 9, 2026 08:28
devin-ai-integration[bot]

This comment was marked as resolved.

toubatbrian and others added 3 commits July 14, 2026 17:53
Co-authored-by: Cursor <cursoragent@cursor.com>
Cancel and finish the old Azure audio reader before closing its push stream so recovery cannot split frames across recognizers.

Co-authored-by: Cursor <cursoragent@cursor.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Co-authored-by: Cursor <cursoragent@cursor.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment thread plugins/azure/src/stt.ts
@toubatbrian
toubatbrian merged commit 5f3b17f into main Jul 15, 2026
9 of 10 checks passed
@toubatbrian
toubatbrian deleted the port-azure-stt-cancellation-recovery branch July 15, 2026 20:58
@github-actions github-actions Bot mentioned this pull request Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant