Skip to content

fix: avoid blocking the event loop when listing ElevenLabs voices - #15928

Open
olegsuhoparov wants to merge 1 commit into
langflow-ai:release-1.14.0from
olegsuhoparov:fix/elevenlabs-voice-list-event-loop
Open

olegsuhoparov wants to merge 1 commit into
langflow-ai:release-1.14.0from
olegsuhoparov:fix/elevenlabs-voice-list-event-loop

Conversation

@olegsuhoparov

@olegsuhoparov olegsuhoparov commented Oct 22, 2026 •

Copy link
Copy Markdown

Description

The async /api/v1/voice/elevenlabs/voice_ids endpoint calls the synchronous ElevenLabs voices.get_all() method. While that request waits for the provider, it blocks other work on the worker's event loop.

Run only that SDK call with asyncio.to_thread. Keep authorization, per-user credential lookup, the existing client lifecycle, response mapping, and error behavior unchanged. This lets concurrent requests progress while ElevenLabs responds; it does not reduce provider response time.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Added an event-gated synchronous fake provider: a second user's voice catalog request completes while the first remains blocked, and each request returns its own catalog. Timeouts only guard against deadlock.
  • The concurrency regression fails against the original implementation and passes with the fix.
  • Existing per-user credential, authorization-ordering, and transaction-release tests pass, with additional coverage for existing error responses and SDK error propagation.
  • Focused voice tests: 29 passed, 4 skipped. The four audio tests are skipped because webrtcvad is not installed.
  • Repository-wide Ruff check, formatting of changed files, all applicable pre-commit hooks, and git diff --check pass. Pre-commit used existing cached hook environments.
  • Tests use fakes and local SQLite; no live ElevenLabs requests were made. Locked backend development dependencies synced from the local cache. Full backend suite not run. Local pytest commands disable the unrelated codeflash-benchmark and rerunfailures plugins to avoid sandbox-only configuration/socket errors.

Checklist

  • The change is limited to the voice catalog request and regression coverage.
  • Necessary technical comments and docstrings are in English.
  • No dependencies or user-shared cache were added.

Summary by CodeRabbit

  • Bug Fixes
    • Voice catalog requests no longer block the event loop while waiting for the voice provider, allowing other requests to continue processing. Returned voice lists and error handling remain unchanged.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 38f354b and 8bb0fcf.

📒 Files selected for processing: 2
  • src/backend/base/langflow/api/v1/voice_mode.py
  • src/backend/tests/unit/test_voice_mode_client_scoping.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

Walkthrough

The voice ID endpoint now runs the synchronous ElevenLabs voices.get_all call in a worker thread. Tests cover concurrent user requests and provider exception handling.

Changes

Voice catalog request

Layer / File(s) Summary
Threaded provider call and regression coverage
src/backend/base/langflow/api/v1/voice_mode.py, src/backend/tests/unit/test_voice_mode_client_scoping.py
The endpoint awaits voices.get_all in a worker thread. Tests check concurrent requests return user-specific voice results, ordinary provider exceptions return error dictionaries, and ElevenLabs ApiError exceptions propagate unchanged.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: phact, erichare

Merge Risk: ⚪ Minimal · up to 8bb0f

The voice-listing change appears mergeable after normal checks; no actionable risk is established by the supplied evidence.

Pre-merge checks: Passed 8 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 8 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: moving the synchronous ElevenLabs voice-listing call off the event loop.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Test Coverage For New Implementations Passed The PR includes targeted regression coverage in src/backend/tests/unit/test_voice_mode_client_scoping.py, which follows the backend test_*.py naming convention. The concurrency test verifies that …
Test Quality And Coverage Passed Tests adequately cover the implementation. The async regression test verifies that voices.get_all runs off the event-loop thread, allows another user request to complete, and preserves per-user resu…
Test File Naming And Structure Passed The changed backend test file uses the required test_*.py pattern and is correctly located under src/backend/tests/unit. It uses pytest async tests, fixtures, parametrization, assertions, and clea…
Excessive Mock Usage Warning Passed The added tests do not use excessive mocks. AsyncMock replaces only async boundaries such as permission checks, credential lookup, and the database transaction; SimpleNamespace and local fake prov…
Full details: Test Coverage For New Implementations

Explanation

The PR includes targeted regression coverage in src/backend/tests/unit/test_voice_mode_client_scoping.py, which follows the backend test_*.py naming convention. The concurrency test verifies that voices.get_all() runs off the event-loop thread, allows another user's request to complete, and preserves per-user results. Additional tests cover the existing mapped errors and unchanged ApiError propagation. The implementation change is the corresponding asyncio.to_thread call in get_elevenlabs_voice_ids.

Full details: Test Quality And Coverage

Explanation

Tests adequately cover the implementation. The async regression test verifies that voices.get_all runs off the event-loop thread, allows another user request to complete, and preserves per-user results. Success mapping and authorization/transaction ordering are also checked. Parametrized tests cover the endpoint's error-response branches, and an ApiError test verifies unchanged propagation. All new async tests use pytest.mark.asyncio, matching the backend pytest pattern.

Full details: Test File Naming And Structure

Explanation

The changed backend test file uses the required test_*.py pattern and is correctly located under src/backend/tests/unit. It uses pytest async tests, fixtures, parametrization, assertions, and cleanup in a finally block. Test names describe concurrency, error handling, authorization ordering, and per-user scoping. The added coverage includes successful concurrent requests, provider exceptions, SDK errors, and negative authorization behavior. No frontend or integration test changes require additional naming or directory checks.

Full details: Excessive Mock Usage Warning

Explanation

The added tests do not use excessive mocks. AsyncMock replaces only async boundaries such as permission checks, credential lookup, and the database transaction; SimpleNamespace and local fake provider functions model the ElevenLabs response. The concurrency test executes the real endpoint and asyncio.to_thread path with a blocking synchronous provider, and the error tests exercise real exception mapping. The mocks are limited, asserted where interactions matter, and do not replace the endpoint logic under test.

✨ Finishing Touches: 💡 1
🧪 Generate unit tests (beta)
  • Create a new PR
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@olegsuhoparov
olegsuhoparov marked this pull request as ready for review October 22, 2026 14:16
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Oct 22, 2026
@olegsuhoparov

Copy link
Copy Markdown
Author

Could a maintainer please rerun the failed Docker verification job? The first attempt and automatic retry both logged a runner shutdown signal followed by cancellation. The final CI summary lists Docker Tests as its only failed dependency.

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

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant