Skip to content

feat(vllm): OpenAI embeddings dimensions truncation (DIS-2093 vLLM side) - #9751

Merged
tzulingk merged 2 commits into
mainfrom
feat/vllm-embedding-dimensions
May 21, 2026
Merged

feat(vllm): OpenAI embeddings dimensions truncation (DIS-2093 vLLM side)#9751
tzulingk merged 2 commits into
mainfrom
feat/vllm-embedding-dimensions

Conversation

@tzulingk

@tzulingk tzulingk commented May 19, 2026

Copy link
Copy Markdown
Contributor

Overview:

Adds OpenAI /v1/embeddings dimensions (Matryoshka truncation) support to the vLLM embedding worker. Mirror of PR #9722 which did the same for SGLang.

Scope: Only changes the vLLM EmbeddingWorkerHandler from #9713. Once #9713 and #9722 both land, this PR closes out the vLLM half of DIS-2093 (the dimensions half).

Details:

In EmbeddingWorkerHandler.generate, the handler now reads request.get("dimensions") once before the per-input loop, validates it (must be a positive int), then per embedding asserts dimensions <= len(embedding) and slices to embedding[:dimensions]. Out-of-range values raise ValueError which the Rust frontend surfaces as HTTP 400.

Test: embedding_agg config in tests/serve/test_vllm.py gains a fourth payload — dimensions=128 against Qwen3-Embedding-0.6B (hidden dim 1024). Built inline using EmbeddingPayload(...) directly because the embedding_payload(..., extra_body=...) helper from PR #9722 isn't on this branch's base.

Not included — encoding_format=base64

Same as the SGLang half of DIS-2093: the Rust frontend's response type is Vec<f32> (inherited from the upstream async_openai::types::embeddings::* re-export) and rejects base64 strings. End-to-end support requires owning the embedding response type in lib/protocols/. Tracked in DIS-2099.

Stacking

Based on feat/vllm-embedding-worker-mvp (PR #9713). Will rebase to main once #9713 lands.

Reviewer focus

  1. components/src/dynamo/vllm/handlers.py::EmbeddingWorkerHandler.generate — the new validation + slicing.
  2. tests/serve/test_vllm.py — one new dimensions=128 payload.

Refs DIS-2093.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added embedding worker mode to serve embedding models with new --embedding-worker configuration flag
    • Implemented OpenAI-compatible /v1/embeddings endpoint supporting text, token, and batched input formats
    • Added dimension truncation support for embeddings
    • New aggregated embedding model launch script provided
  • Tests

    • Added comprehensive test coverage for embedding worker configuration, validation, and API behavior

Review Change Stack

@github-actions github-actions Bot added feat backend::vllm Relates to the vllm backend and removed feat labels May 19, 2026
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-worker-mvp branch from dc4f5d2 to d5a2053 Compare May 19, 2026 19:44
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from f1a64d9 to f532e6e Compare May 19, 2026 19:46
@github-actions github-actions Bot added the feat label May 19, 2026
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-worker-mvp branch 2 times, most recently from c12c8be to 295335e Compare May 19, 2026 20:54
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from f532e6e to a0fa5a4 Compare May 19, 2026 20:55
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from a0fa5a4 to 71fbe88 Compare May 20, 2026 14:54
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from 71fbe88 to 06f181e Compare May 20, 2026 15:06
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from 1618ad7 to ac3242b Compare May 20, 2026 15:16
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-worker-mvp branch from 8ad9834 to 3d974aa Compare May 20, 2026 15:23
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from ac3242b to fb74245 Compare May 20, 2026 15:24
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-worker-mvp branch from 3d974aa to 5be8483 Compare May 20, 2026 20:22
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from fb74245 to f4c8f4f Compare May 20, 2026 20:22
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-worker-mvp branch from 5be8483 to 894e159 Compare May 20, 2026 20:52
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from f4c8f4f to 0edca9a Compare May 20, 2026 20:52
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from 932f5e4 to 2a231ed Compare May 21, 2026 14:49
Base automatically changed from feat/vllm-embedding-worker-mvp to main May 21, 2026 16:23
@tzulingk
tzulingk marked this pull request as ready for review May 21, 2026 17:13
@tzulingk
tzulingk requested a review from a team as a code owner May 21, 2026 17:13
@tzulingk
tzulingk requested a review from a team May 21, 2026 17:13
@tzulingk
tzulingk requested review from a team as code owners May 21, 2026 17:13
Adds OpenAI `/v1/embeddings` `dimensions` (Matryoshka truncation)
support to the vLLM embedding worker. Mirrors PR #9722 which did the
same for SGLang.

In `EmbeddingWorkerHandler.generate`, the handler now reads
`request.get("dimensions")` once before the per-input loop, validates
it (must be a positive int), then per embedding asserts
`dimensions <= len(embedding)` and slices to `embedding[:dimensions]`.
Out-of-range values raise `ValueError` which the Rust frontend
surfaces as HTTP 400.

`encoding_format=base64` is not yet supported end-to-end because the
Rust frontend's response type is `Vec<f32>` and rejects base64
strings; that requires owning the embedding response type in
`lib/protocols/` and is tracked as a separate follow-up.

Test: `embedding_agg` config in `tests/serve/test_vllm.py` gains a
fourth payload — `dimensions=128` against Qwen3-Embedding-0.6B
(hidden dim 1024). Built inline using `EmbeddingPayload(...)` directly
because the `embedding_payload(..., extra_body=...)` helper added in
PR #9722 isn't on this branch's base.

Signed-off-by: Tzu-Ling <tzulingk@nvidia.com>
@tzulingk
tzulingk force-pushed the feat/vllm-embedding-dimensions branch from 2a231ed to 79234a7 Compare May 21, 2026 17:16
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR adds support for dedicated embedding worker mode in the Dynamo vLLM wrapper, enabling OpenAI-compatible embedding serving in aggregated disaggregation mode. It introduces CLI configuration with strict validation, a specialized handler for embedding requests, factory-level routing, and end-to-end launch and integration testing.

Changes

Embedding Worker Feature

Layer / File(s) Summary
Config flag and validation rules
components/src/dynamo/vllm/backend_args.py, components/src/dynamo/vllm/tests/test_backend_args.py, components/src/dynamo/vllm/tests/test_vllm_unit.py
Added --embedding-worker CLI flag and embedding_worker config field to DynamoVllmConfig. Validation enforces embedding worker is only allowed in agg disaggregation mode, mutually exclusive with multimodal roles and enable_multimodal, and incompatible with benchmark_mode.
Embedding input classification
components/src/dynamo/vllm/tests/test_vllm_worker_handler.py
Test suite validates _classify_embedding_input for OpenAI-spec embedding input shapes: single string, list of strings, token-id lists, and batched prompts, with strict rejection of mixed-type and invalid inputs.
Worker factory embedding routing
components/src/dynamo/vllm/worker_factory.py, components/src/dynamo/vllm/tests/test_vllm_worker_factory.py
Factory imports EmbeddingWorkerHandler, checks config.embedding_worker, and routes to _create_embedding_worker coroutine to initialize vLLM engine, construct handler, serve endpoint, and register embedding model with guaranteed cleanup.
Launch script and integration test
examples/backends/vllm/launch/agg_embed.sh, tests/serve/test_vllm.py
agg_embed.sh launches aggregated embedding serving with frontend and vLLM worker processes; integration test adds embedding_agg configuration covering default, single, batch, and dimension-truncation embedding payload variants.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.74% 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
Title check ✅ Passed The title clearly and specifically identifies the main change: adding OpenAI embeddings dimensions truncation support to vLLM, with precise issue reference (DIS-2093).
Description check ✅ Passed The PR description follows the template structure with clear Overview, Details, and Related Issues sections; it comprehensively explains the change, scope, implementation, limitations, and stacking context.
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
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/vllm-embedding-dimensions

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 and usage tips.

@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)
components/src/dynamo/vllm/handlers.py (1)

2887-2889: ⚡ Quick win

Move PoolingParams to module scope.

This function-local import violates the repo Python rule and adds import work on every embeddings request. If the lazy-load concern is real, the embedding handler should live in its own module instead of importing inside generate().

♻️ Proposed fix
+from vllm import PoolingParams
 from vllm.config import ModelConfig, VllmConfig
 from vllm.inputs import EmbedsPrompt, TextPrompt, TokensPrompt
@@
-        # Lazy import to avoid pulling PoolingParams into handlers.py at module
-        # load time for non-embedding workers.
-        from vllm import PoolingParams
-
         model_name = request.get("model") or self.config.served_model_name or ""
As per coding guidelines, "Keep all imports at module scope (flag any import inside functions/classes)."
🤖 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 `@components/src/dynamo/vllm/handlers.py` around lines 2887 - 2889, The local
import of PoolingParams inside generate() should be moved to module scope to
comply with repo rules and avoid per-request import overhead: remove the
in-function "from vllm import PoolingParams" and add "from vllm import
PoolingParams" at the top of this handlers.py module; if the original lazy-load
rationale is still required, extract the embedding handler into a dedicated
module (e.g., vllm_embedding_handler) and place the PoolingParams import at that
module's top so generate() no longer contains inline imports.
🤖 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 `@components/src/dynamo/vllm/handlers.py`:
- Around line 2887-2889: The local import of PoolingParams inside generate()
should be moved to module scope to comply with repo rules and avoid per-request
import overhead: remove the in-function "from vllm import PoolingParams" and add
"from vllm import PoolingParams" at the top of this handlers.py module; if the
original lazy-load rationale is still required, extract the embedding handler
into a dedicated module (e.g., vllm_embedding_handler) and place the
PoolingParams import at that module's top so generate() no longer contains
inline imports.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3e14961f-ffff-475d-bc9e-db07488f7023

📥 Commits

Reviewing files that changed from the base of the PR and between 066a7a5 and 2a231ed.

📒 Files selected for processing (9)
  • components/src/dynamo/vllm/backend_args.py
  • components/src/dynamo/vllm/handlers.py
  • components/src/dynamo/vllm/tests/test_backend_args.py
  • components/src/dynamo/vllm/tests/test_vllm_unit.py
  • components/src/dynamo/vllm/tests/test_vllm_worker_factory.py
  • components/src/dynamo/vllm/tests/test_vllm_worker_handler.py
  • components/src/dynamo/vllm/worker_factory.py
  • examples/backends/vllm/launch/agg_embed.sh
  • tests/serve/test_vllm.py

@tzulingk
tzulingk requested review from biswapanda and nnshah1 May 21, 2026 17:21
Signed-off-by: Tzu-Ling <tzulingk@nvidia.com>
@tzulingk
tzulingk enabled auto-merge (squash) May 21, 2026 17:27
@tzulingk
tzulingk merged commit d7f3341 into main May 21, 2026
96 checks passed
@tzulingk
tzulingk deleted the feat/vllm-embedding-dimensions branch May 21, 2026 19:25
Jont828 pushed a commit to Jont828/dynamo that referenced this pull request May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend::vllm Relates to the vllm backend feat size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants