Skip to content

Speed up local vLLM ASR client requests#1487

Open
DongjiGao wants to merge 7 commits into
mainfrom
skills-httpcore-default
Open

Speed up local vLLM ASR client requests#1487
DongjiGao wants to merge 7 commits into
mainfrom
skills-httpcore-default

Conversation

@DongjiGao

@DongjiGao DongjiGao commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

@/tmp/tmp.HobmUsFmEc

Summary by CodeRabbit

Release Notes

New Features

  • Added audio_as_path option to vLLM multimodal models for more efficient handling of local audio files using URL references instead of inline encoding
  • New utility function for creating audio URL content blocks for API compatibility
  • Comprehensive validation for audio formats with intelligent configuration defaults and automatic fallbacks

DongjiGao added 2 commits June 9, 2026 15:35
Patch httpcore's async connection-pool assignment hot path by default so high-concurrency OpenAI-compatible clients do not spend the event loop rebuilding connection-state lists.

Signed-off-by: Dongji Gao <dongjig@nvidia.com>
Default local vLLM multimodal audio requests to file:// URLs instead of inline base64, while retaining the base64 path for external APIs and as an explicit fallback.

Signed-off-by: Dongji Gao <dongjig@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 621bfb40-3f23-4117-91ec-d9a71d027d88

📥 Commits

Reviewing files that changed from the base of the PR and between 18f9e9d and b0fa50a.

📒 Files selected for processing (2)
  • nemo_skills/inference/generate.py
  • tests/test_httpcore_patch.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/test_httpcore_patch.py
  • nemo_skills/inference/generate.py

📝 Walkthrough

Walkthrough

Adds an httpcore AsyncConnectionPool monkeypatch that replaces the multi-pass request assignment with a single-pass O(N+M) algorithm, applied once at async_loop startup. Separately, VLLMMultimodalModel gains an audio_as_path option that sends audio as file:// URLs for local vLLM requests instead of base64-inlined payloads, with validation and comprehensive tests.

Changes

httpcore Connection Pool Monkeypatch

Layer / File(s) Summary
httpcore monkeypatch function and call site
nemo_skills/inference/generate.py
Imports importlib, defines _patch_httpcore_connection_pool_assignment() to conditionally monkeypatch AsyncConnectionPool._assign_requests_to_connections with a single-pass assignment algorithm for non-HTTP/2 pools when httpcore version is 1.0.3–1.0.9 and not already patched, and calls it once at the start of GenerationTask.async_loop.
httpcore patch tests
tests/test_httpcore_patch.py
Validates that the patch is skipped for unvalidated httpcore versions and when __version__ is missing, and applied with _nemo_skills_fast_assign marker for versions 1.0.3–1.0.9; includes fake httpcore module injection helper for isolated testing and a functional test exercising the patched assignment logic against expired connections.

VLLMMultimodalModel audio_as_path Feature

Layer / File(s) Summary
make_audio_url_content_block helper and export
nemo_skills/inference/model/audio_utils.py, nemo_skills/inference/model/__init__.py, nemo_skills/inference/model/vllm_multimodal.py
Adds make_audio_url_content_block(audio_url) returning an {"type": "audio_url", ...} dict, exports it from the model package, imports it and Path into vllm_multimodal.py for file URI conversion.
VLLMMultimodalModel initialization and audio_as_path validation
nemo_skills/inference/model/vllm_multimodal.py
Adds audio_as_path: bool | None = None parameter, defaults it from local vs. external API mode and audio_format, and validates that it is only allowed for local vLLM with audio_url format, raising ValueError for unsupported combinations.
content_text_to_list refactor with _make_audio_input_block
nemo_skills/inference/model/vllm_multimodal.py
Refactors audio block construction to dispatch both audio and audios through _make_audio_input_block helper that selects file:// URLs when audio_as_path is enabled or base64-derived blocks otherwise, and updates docstring to describe audio content conversion.
Tests for audio_as_path defaults, validation, and content output
tests/test_vllm_audio.py
Updates _is_valid_audio_content to accept file:// URLs, sets audio_as_path on fixtures, adds _stub_vllm_base() context manager to isolate init logic from heavy base initialization, and adds tests covering defaults for local vs. external API, fallback to base64 when audio_as_path=False, reserved character handling in file paths, and rejection of unsupported audio_as_path/audio_format combinations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 summarizes the main change: optimizing local vLLM ASR client request performance through httpcore connection pool improvements and audio URL handling.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch skills-httpcore-default

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

Replace the connection-pool assignment hot path with a single-pass
O(N+M) algorithm ported from encode/httpcore#1035 (still unmerged in
1.0.9). Categorize connections once, assign queued requests from the
available bucket (popping each so a connection is never handed to two
requests in one pass), then enforce keepalive. HTTP/2 pools fall back to
the original since they need per-connection stream-capacity tracking.

This removes the short-circuit scan-depth dependence of the prior patch
(cost varied ~2x with where a free connection sat in the list), trading a
slightly slower best case for consistent, lower worst-case cost.

Signed-off-by: Dongji Gao <dongjig@nvidia.com>
Add direct VLLMMultimodalModel.__init__ tests (stubbing only the heavy
VLLMModel base) for audio_as_path defaulting from mode/format and the
validation that rejects path mode for external APIs / non-audio_url
formats, plus unsupported-format rejection. Addresses PR review feedback
that the existing fixtures bypass the constructor.

Signed-off-by: Dongji Gao <dongjig@nvidia.com>
@DongjiGao
DongjiGao force-pushed the skills-httpcore-default branch from 701cbf0 to bddb482 Compare June 15, 2026 18:56
@DongjiGao
DongjiGao requested a review from KunalDhawan June 15, 2026 19:30
@Slyne

Slyne commented Jun 15, 2026

Copy link
Copy Markdown
  • comparison with vllm benchmark script
Screenshot 2026-06-15 at 12 17 58 PM

@KunalDhawan KunalDhawan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Great work @DongjiGao! Added some comments below, other than that LGTM

raise ValueError(f"Unsupported audio_format '{audio_format}'. Use 'audio_url' or 'input_audio'.")
self.audio_format = audio_format
if audio_as_path is None:
audio_as_path = not self._external_api_mode and self.audio_format == "audio_url"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This defaults audio_as_path=True for all local audio_url requests, but vLLM rejects file:// media unless the server is launched with --allowed-local-media-path (it's disabled by default for security). serve_vllm.py (the command at L44–53) doesn't pass that flag, and it doesn't appear anywhere in the repo, so the default path here will fail at request time on a stock server, the same local-vLLM ASR path this PR is named for. The green e2e run likely had a server with the flag set manually, which hides this.
Could we either (a) have serve_vllm.py add --allowed-local-media-path (pointed at data_dir) when audio is in use, or (b) flip the default to audio_as_path=False so it's strictly opt-in, with the server requirement documented? Right now turning it on by default puts the burden on every user to also reconfigure the server.

LOG.debug("httpcore is not installed; skipping async connection-pool patch")
return

original_assign = async_connection_pool.AsyncConnectionPool._assign_requests_to_connections

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This function depends on httpcore 1.0.9 internals (_connections, _requests, _http2, _max_connections, _max_keepalive_connections, pool_request.is_queued(), connection.can_handle_request()). The only safeguard is the _nemo_skills_fast_assign double-patch marker, but there's no version check. If httpcore bumps, this will either silently change behavior or raise inside the event loop. Could we gate the patch on httpcore.__version__ (patch only the validated version, log-and-skip otherwise) or add some other guards?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks @KunalDhawan , both points make sense.

  • For the httpcore patch, I gated it to the validated version only (httpcore==1.0.9) and log/skip otherwise, so we don't patch unknown private internals after a dependency bump. I also added a small unit test covering both the apply and skip cases.

  • For audio_as_path, agreed that defaulting to file:// is unsafe without --allowed-local-media-path. I flipped the default to False, kept it opt-in for local audio_url, and updated the docstring/tests to call out the server requirement.

Keep file:// audio opt-in unless vLLM is explicitly configured for local media, and gate the private httpcore pool patch to the validated 1.0.9 internals.

Signed-off-by: Dongji Gao <dongjig@nvidia.com>

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
tests/test_httpcore_patch.py (1)

54-62: ⚡ Quick win

Consider adding a test for double-patch prevention.

The patch function includes explicit logic to skip patching if _nemo_skills_fast_assign is already set. A test that calls _patch_httpcore_connection_pool_assignment() twice on the same fake module would validate this guard works correctly.

🧪 Example test implementation
def test_httpcore_patch_prevents_double_patching(monkeypatch):
    pool_cls = _install_fake_httpcore(monkeypatch, "1.0.9")
    
    _patch_httpcore_connection_pool_assignment()
    first_patched = pool_cls._assign_requests_to_connections
    
    _patch_httpcore_connection_pool_assignment()
    second_patched = pool_cls._assign_requests_to_connections
    
    assert first_patched is second_patched
    assert getattr(first_patched, "_nemo_skills_fast_assign", False) is True
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_httpcore_patch.py` around lines 54 - 62, Add a new test function
called test_httpcore_patch_prevents_double_patching that verifies the patch
idempotency guard works correctly. The test should call _install_fake_httpcore
to set up a fake module, call _patch_httpcore_connection_pool_assignment twice
on the same pool class, and then assert that the patched
_assign_requests_to_connections method returned after the first call is
identical to the one returned after the second call, confirming that the second
call did not re-patch the method. Also verify that the _nemo_skills_fast_assign
attribute remains set to True.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/test_httpcore_patch.py`:
- Around line 54-62: Add a new test function called
test_httpcore_patch_prevents_double_patching that verifies the patch idempotency
guard works correctly. The test should call _install_fake_httpcore to set up a
fake module, call _patch_httpcore_connection_pool_assignment twice on the same
pool class, and then assert that the patched _assign_requests_to_connections
method returned after the first call is identical to the one returned after the
second call, confirming that the second call did not re-patch the method. Also
verify that the _nemo_skills_fast_assign attribute remains set to True.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c278ce7b-ac96-4d63-bc19-dcd2ac204575

📥 Commits

Reviewing files that changed from the base of the PR and between bddb482 and 843cc1c.

📒 Files selected for processing (4)
  • nemo_skills/inference/generate.py
  • nemo_skills/inference/model/vllm_multimodal.py
  • tests/test_httpcore_patch.py
  • tests/test_vllm_audio.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • nemo_skills/inference/generate.py
  • tests/test_vllm_audio.py

Preserve httpcore's ability to replace expired connections in the same assignment pass, tolerate missing httpcore version metadata, and emit escaped file:// URIs for local audio paths.

Signed-off-by: Dongji Gao <dongjig@nvidia.com>
Apply the fast assignment monkeypatch to the inspected 1.0.3-1.0.9 internals instead of only 1.0.9, while keeping older/different internals unpatched.

Signed-off-by: Dongji Gao <dongjig@nvidia.com>
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.

3 participants