Skip to content

feat(vllm): add external encoder handoff - #13293

Merged
furionw merged 8 commits into
mainfrom
qiwa/vllm-workflow-components
Sep 23, 2026
Merged

furionw merged 8 commits into
mainfrom
qiwa/vllm-workflow-components

Conversation

@furionw

@furionw furionw commented Aug 15, 2026 •

Copy link
Copy Markdown
Contributor

Why

Dynamo already maintains the VisionEncoderBackend framework, but applications that place the encoder outside the aggregated vLLM worker still had to reimplement image extraction, encoder driving, tensor packing, request sanitization, and the decoder-side prompt reconstruction contract.

This PR makes that handoff a vLLM-owned contract while keeping GenerateRequest.encoder_result opaque in dynamo.common.

User contract

handoff = ExternalEncoderHandoff(backend)
handoff.load(model_id)
prepared = await handoff.prepare_request(request, target_model=generator_model)
handoff.shutdown()

Users implement VisionEncoderBackend; Dynamo owns the rest of the external handoff.

What changed

  • Add public ExternalEncoderHandoff lifecycle and request-preparation API.
  • Move the concrete versioned wire format under the vLLM custom-encoder component.
  • Share one linear prompt builder between inline and external encoder paths.
  • Sanitize source-side multimodal state before forwarding to stock aggregated dynamo.vllm.
  • Keep decoder-side conflict validation and off-event-loop tensor reconstruction.

Version 0 remains intentionally narrow: image inputs, ordered two-dimensional CPU linear embeddings, MsgPack request-plane transport, token-in/token-out, and a text-only aggregated vLLM worker with --enable-prompt-embeds.

The thin application-owned example is #14859.

Test plan

  • Unit coverage for payload validation and round trips, artifact ordering, request sanitization, lifecycle, malformed inputs, and inline/external prompt parity.
  • Existing handler coverage for aggregated, text-mode rejection, and conditional-bypass behavior.
  • Pre-commit and Python syntax compilation pass locally.

Full PR sequence

  1. #13293 — encoder handoff (merged)
  2. #15195 — unary endpoint helpers (merged)
  3. #15552 — model and client utilities
  4. #14859 — application-owned orchestrator example
  5. #15200 — draft end-to-end verification

The three open PRs form GitHub stack #15572. The first two remain in completed stack #15197.

Summary by CodeRabbit

  • New Features
    • Added support for passing image embeddings from an external encoder to an aggregated worker, which reconstructs the prompt for generation.
    • External encoder results are supported in token-in/token-out mode on aggregated workers.
  • Bug Fixes
    • Requests with incompatible multimodal inputs or invalid external encoder results now return errors instead of proceeding.
  • Documentation
    • Clarified when applications and request routing can provide external encoder results.

@datadog-official

This comment has been minimized.

@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from 6b0b89f to bcd4d65 Compare August 15, 2026 02:51
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from bcd4d65 to 0653bf1 Compare August 15, 2026 03:06
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from 0653bf1 to 51b27ed Compare August 15, 2026 03:43
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from f42d16b to 8e8332e Compare August 17, 2026 23:30
@github-actions github-actions Bot added documentation Improvements or additions to documentation frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` labels Aug 17, 2026
@furionw
furionw changed the base branch from qiwa/vllm-external-encoder to qiwa/workflow-generate-binding August 17, 2026 23:31
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from 8e8332e to 24b3084 Compare August 18, 2026 02:49
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from 24b3084 to c4dbac5 Compare August 20, 2026 08:05
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from c4dbac5 to 7f5b03c Compare August 21, 2026 04:44
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from 7f5b03c to 1a0f101 Compare August 21, 2026 04:54
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from b64bc29 to e656d71 Compare August 28, 2026 21:28
Signed-off-by: furionw <qiwa@nvidia.com>
@furionw
furionw force-pushed the qiwa/vllm-workflow-components branch from e656d71 to 01332e4 Compare September 14, 2026 22:32
Signed-off-by: furionw <qiwa@nvidia.com>
@furionw furionw changed the title feat(vllm): accept remote custom encoder results feat(vllm): add external encoder handoff Sep 22, 2026
@furionw
furionw marked this pull request as ready for review September 22, 2026 21:54
@furionw
furionw requested review from a team as code owners September 22, 2026 21:54
@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Walkthrough

The change adds an external vision encoder handoff for aggregated vLLM requests. It serializes image embeddings into encoder_result and adds request validation and prompt reconstruction in the decode handler. The shared prompt builder supports both inline adapter use and external results.

Changes

External Encoder Handoff

Layer / File(s) Summary
Shared prompt construction
components/src/dynamo/vllm/multimodal_utils/custom_encoder/adapter/linear.py, components/src/dynamo/vllm/multimodal_utils/custom_encoder/backend/base.py, components/src/dynamo/common/backend/engine.py
Adds LinearEmbedsPromptBuilder and delegates prompt construction from LinearEmbedsAdapter. Updates documentation for encoder_result and encoder placement.
External result handoff
components/src/dynamo/vllm/multimodal_utils/custom_encoder/handoff.py, components/src/dynamo/vllm/multimodal_utils/custom_encoder/__init__.py, components/src/dynamo/vllm/tests/multimodal_utils/custom_encoder/test_vllm_external_handoff.py
Adds tensor serialization, versioned encoder results, and request preparation that encodes image URLs and removes source-side multimodal data. Tests cover serialization, validation, and downstream request contents.
Aggregated worker prompt assembly
components/src/dynamo/vllm/multimodal_utils/custom_encoder/external.py, components/src/dynamo/vllm/handlers.py, components/src/dynamo/vllm/tests/test_vllm_external_encoder.py
Adds prompt reconstruction from external results and handler support for aggregated token-mode requests. Tests cover reconstruction, validation errors, and mode selection.

Priority: ⬆️ High

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to bc735

The external encoder flow lacks an end-to-end assertion for forwarding its reconstructed prompt to generation. Add that focused integration test before relying on this coverage for future changes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.94% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 68 functions across 9 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'feat(vllm): add external encoder handoff' directly corresponds to the main changeset objective. The title clearly identifies the primary feature addition: a new external encode…
Description check ✅ Passed The pull request description covers the required template sections with substantial detail. The 'Overview' section is implicit in the 'Why' heading, which explains the motivation: Dynamo maintains `Vi…
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.
  • Fix all pre-merge checks with AI

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

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: ai-dynamo/dynamo/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b99cd151-207c-4d05-b606-932f43329d98

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab7d48 and bc73513.

📒 Files selected for processing (9)
  • components/src/dynamo/common/backend/engine.py
  • components/src/dynamo/vllm/handlers.py
  • components/src/dynamo/vllm/multimodal_utils/custom_encoder/__init__.py
  • components/src/dynamo/vllm/multimodal_utils/custom_encoder/adapter/linear.py
  • components/src/dynamo/vllm/multimodal_utils/custom_encoder/backend/base.py
  • components/src/dynamo/vllm/multimodal_utils/custom_encoder/external.py
  • components/src/dynamo/vllm/multimodal_utils/custom_encoder/handoff.py
  • components/src/dynamo/vllm/tests/multimodal_utils/custom_encoder/test_vllm_external_handoff.py
  • components/src/dynamo/vllm/tests/test_vllm_external_encoder.py

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.

Comment thread components/src/dynamo/vllm/tests/test_vllm_handoff_consumer.py
@furionw
furionw added this pull request to stack #15197 September 22, 2026 23:36
Signed-off-by: furionw <qiwa@nvidia.com>
@furionw
furionw merged commit 7472c23 into main Sep 23, 2026
121 of 123 checks passed
@furionw
furionw deleted the qiwa/vllm-workflow-components branch September 23, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

actions backend::sglang Relates to the sglang backend backend::trtllm Relates to the trtllm backend backend::vllm Relates to the vllm backend container deployment::k8s Relates to dynamo deployment in kubernetes documentation Improvements or additions to documentation feat frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` multimodal planner router Relates to routing, KV-aware routing, etc. size/XXL xpu

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants