Skip to content

fix(llm): cancel recovery probes when the fallback adapter closes - #7932

Open
BlueX888 wants to merge 1 commit into
livekit:mainfrom
BlueX888:fix/fallback-adapter-aclose-probes
Open

BlueX888 wants to merge 1 commit into
livekit:mainfrom
BlueX888:fix/fallback-adapter-aclose-probes

Conversation

@BlueX888

Copy link
Copy Markdown
Contributor

fix(llm): cancel recovery probes when the fallback adapter closes

What breaks

FallbackAdapter.aclose() only unsubscribes the metrics collector. Recovery probes — the background tasks FallbackLLMStream._try_recovery() detaches into _LLMStatus.recovering_task — keep running after close: they can flip available=True and emit llm_availability_changed after the adapter is closed, mutate state post-shutdown, and a stream that fails after aclose() can spawn a brand-new probe (the probe machinery has no closed check).

Fix

  • _closed flag, set first in aclose().
  • aclose() then aio.cancel_and_waits every in-flight recovering_task, then unsubscribes metrics.
  • _try_recovery() returns immediately when _closed, so no probe can start after close.

Mirrors the existing TTS/STT fallback-adapter close style. Pre-close behavior is unchanged (the fallback/retry paths are untouched; the full test_llm_fallback.py suite sees identical results apart from the two new tests).

Tests

tests/test_llm_fallback.py (deterministic event gating, no sleeps):

  • test_aclose_cancels_in_flight_recovery_probe — a recovery probe is in flight when aclose() runs; asserts it is finished when aclose() returns.
  • test_no_recovery_probe_after_close — a gated failure that would start a probe after close; asserts no probe is created.

On base: both fail — assert ... <Task pending ... _recover_llm_task() ...> (a probe outlived aclose()). With the fix: 2 passed, repeated 20× without flakes (also under -W error::RuntimeWarning); test_llm_fallback.py 106 passed, LLM+STT+TTS fallback suites 130, coverage spans 16; ruff clean. Independent review verified cancellation cannot hang (probes are unshielded, CancelledError propagates through the stream's unwind) and that _closed is set before the sweep so no probe can slip through.

FallbackAdapter.aclose() only unsubscribed from metrics, leaving the
background probes that FallbackLLMStream._try_recovery() starts for
unavailable providers running. A probe could outlive the adapter: its
provider request kept going, and on completion it flipped _status back to
available and emitted llm_availability_changed after close.

Set a closed flag before sweeping, cancel and await every recovery task,
and refuse to start new probes once closed, matching the STT and TTS
fallback adapters.

Add regression tests covering an in-flight probe at aclose() and a
stream that fails after aclose().
@BlueX888
BlueX888 requested a review from a team as a code owner October 18, 2026 19:23

@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: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant