Repository navigation
[Core] Add NIXL omni connector - #6093
Conversation
Adds a device-agnostic NixlConnector implementing OmniConnectorBase, its factory registration with optional-dependency guards, and the UCX/NIXL wheel build used by the XPU image. Signed-off-by: yuanwu <yuan.wu@intel.com>
A NIXL READ needs the producer's agent metadata and memory descriptors. put() returns them, but the generic stage payload path drops that value and calls get() with metadata=None, so NixlConnector was only usable from the diffusion path that forwards the handle itself. Serve the metadata from a ROUTER socket on the producer and let the consumer fetch it by key, mirroring MooncakeTransferEngineConnector. The handshake only starts when zmq_port is configured, so deployments that forward the metadata themselves keep their current behaviour and open no sockets. The consumer also reports completion, letting the producer deregister the buffer immediately instead of holding it until the lease expires. Signed-off-by: yuanwu <yuan.wu@intel.com>
_allocate_tensor_from_spec() fell back to the device string recorded by the producer, so a producer on cuda:3 made the consumer allocate on its own cuda:3 -- a card that may belong to a different stage. Keep only the device type and use this process's current device of that type. Also report the reclaimed size when a lease expires, and add unit tests for the handshake with NIXL stubbed out. Signed-off-by: yuanwu <yuan.wu@intel.com>
NixlConnector is the only stage-transfer backend that works on Intel XPU, but nothing in the docs said so or explained how to configure it. Add a page next to the other connector docs covering the two control-plane modes, the XPU installation requirement, and every knob the connector reads, then register it in the nav, the design index and the disaggregation overview so it is discoverable from the same places as its siblings. Signed-off-by: yuanwu <yuan.wu@intel.com>
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
open an RFC first |
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
This PR appears to belong to: docs/design/module/omni_connector.md. Module owners: @princepride @yuanheng-zhao @xuechendi @natureofnature @fake0fan Please take a look when you have a chance. If you would like an automated review, mention @vllm-omni-review-bot in a comment. |
Ok. Let me finish the integration with #5885. |
NixlConnector opens its own ZMQ handshake socket, so it needs the same endpoint derivation the other transfer-engine connectors get: a stage and rank offset on top of the deploy YAML's base port, an explicit sender/receiver role from the edge direction, and a default sender endpoint on the receiving side. Without it every rank of a tensor-parallel stage would bind the single port spelled out in the YAML. Signed-off-by: yuanwu <yuan.wu@intel.com>
A deploy YAML spells out one base ZMQ port per connector edge, and the orchestrator publishes kv_zmq_port(base, from_stage, 0) as the producer endpoint that receivers then offset by their own rank. Connectors built on the worker payload path skipped that derivation and bound the literal YAML port, so on a tensor-parallel sender every rank past the first died with EADDRINUSE and the port the orchestrator advertised belonged to nobody. Apply the same derivation when building the worker-side connector. Receivers only dial out and keep their configured port, and connectors that do not run a handshake of their own are left alone. Signed-off-by: yuanwu <yuan.wu@intel.com>
The sender port is only correct if it matches the formula the orchestrator uses to advertise the endpoint, and nothing at runtime reports a mismatch: the receiver simply dials a port nobody is listening on and times out. Assert the two stay in step, and that receivers and handshake-less connectors keep the port spelled out in the deploy YAML. Signed-off-by: yuanwu <yuan.wu@intel.com>
Done — filed as #6160. I've also filled in the PR description. Two things worth flagging for review:
|
H3 NIXL E2E retest after main integration (2026-09-09)The successful test used the tree now published as 0a54596 in #6264, stacked on #6093 head 7da3f90. The run used Results
An earlier attempt failed during text-encoder loading because an external managed model was automatically restarted onto GPU0. The successful retry above ran after that service was stopped. That failed attempt is not counted as a successful E2E. Update / author self-review#6264's nine feature commits were restacked onto the current #6093 head, retaining the upstream H3 conditioning contract and native paged-KV request plumbing. The final configuration fix preserves stage payload keys through the centralized diffusion projection, serialization, and runtime mapping, with topology defaults and explicit deploy/CLI precedence (including empty overrides). It also registers/documents the two existing NIXL environment variables and updates their inventory tests. These five-file fixes are in #6264; this comment does not claim they were separately added to #6093. The native paged-KV and full-payload protocols remain distinct. Reproduction commandsRun from a checkout of #6264 at export HF_CACHE=/path/to/huggingface/cache
export IMAGE=vllm-omni-pr6093:integration-20260908
mkdir -p h3-e2e-output
docker run -d --name h3-nixl-e2e \
--gpus '"device=0,1,3,4,5,6"' --network host --ipc host \
-v "$PWD:/workspace:ro" -v "$PWD/h3-e2e-output:/artifacts" \
-v "$HF_CACHE:/root/.cache/huggingface:ro" -w /workspace \
-e PYTHONPATH=/workspace -e PYTHONDONTWRITEBYTECODE=1 \
-e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5 \
-e HF_HOME=/root/.cache/huggingface -e VLLM_LOGGING_LEVEL=DEBUG \
-e UCX_RCACHE_MAX_UNRELEASED=1024 \
"$IMAGE" python -m vllm_omni.entrypoints.cli.main serve \
/root/.cache/huggingface/hub/models--MiniMaxAI--MiniMax-H3/snapshots/42ed227ee7df40d41602854ae760620d6eb651fe/FL2VA \
--omni --host 127.0.0.1 --port 8010 --disable-log-stats \
--deploy-config vllm_omni/deploy/minimax_h3_disaggregated.yaml \
--stage-overrides '{"1":{"tensor_parallel_size":4,"ulysses_degree":1}}'
trap 'docker stop --time 30 h3-nixl-e2e' EXIT
curl --noproxy '*' --fail --silent --show-error \
--retry 180 --retry-connrefused --retry-delay 10 \
--retry-max-time 1800 --max-time 5 http://127.0.0.1:8010/health
curl --noproxy '*' --fail --silent --show-error --max-time 1800 \
-D h3-e2e-output/orange-cat.headers -o h3-e2e-output/orange-cat.mp4 \
-w 'http_code=%{http_code}\ncontent_type=%{content_type}\nsize_download=%{size_download}\ntime_total=%{time_total}\n' \
http://127.0.0.1:8010/v1/videos/sync \
-F 'prompt=A clearly visible orange tabby cat walking through bright green grass in warm daylight, detailed fur, natural motion, vibrant colors, wide cinematic shot, with matching outdoor ambient sound.' \
-F 'width=1344' -F 'height=768' -F 'num_frames=107' -F 'fps=24' \
-F 'num_inference_steps=8' -F 'seed=42' \
-F 'extra_params={"task":"t2va","duration":4.0,"aspect_ratio":"16:9","flow_shift":12.0,"audio_flow_shift":3.0}'
docker exec h3-nixl-e2e ffprobe -v error \
-show_entries format=duration,size \
-show_entries stream=codec_type,codec_name,width,height,nb_frames,r_frame_rate \
-of json /artifacts/orange-cat.mp4
docker exec h3-nixl-e2e ffmpeg -v error -xerror \
-i /artifacts/orange-cat.mp4 -f null -
docker exec h3-nixl-e2e ffmpeg -v error -ss 2 \
-i /artifacts/orange-cat.mp4 -frames:v 1 /artifacts/preview.png
docker logs h3-nixl-e2e > h3-e2e-output/server.log 2>&1
grep -E 'NixlConnector (put|get).*video_sync' h3-e2e-output/server.log
sha256sum h3-e2e-output/orange-cat.mp4
docker stop --time 30 h3-nixl-e2e
trap - EXITThe test host additionally passed its HTTP(S) proxy through to the container; configure your own proxy if required. No custom deployment YAML is needed. Separate native smoke command (run when its two GPUs are free): docker run --rm --gpus '"device=1,3"' --network host --ipc host \
-v "$PWD:/workspace:ro" -w /workspace \
-e PYTHONPATH=/workspace -e PYTHONDONTWRITEBYTECODE=1 \
-e UCX_RCACHE_MAX_UNRELEASED=1024 \
"$IMAGE" python -m pytest -vv -s -rA -o addopts='' -p no:cacheprovider \
tests/distributed/omni_connectors/test_nixl_connector_native.pyScope: real same-host CUDA/NIXL transfer with heterogeneous producer/consumer TP. Inter-node transfer, Intel XPU, and real Mooncake RDMA E2E were not validated. Mooncake/paged-KV coexistence is covered by regression tests, not by this H3 media run. H3 exercises an AR producer and diffusion consumer; the separate diffusion-producer/inline-drop branch is unit-tested, not exercised by this two-stage E2E. Expanded regression commandRun separately in the same runtime, without GPU exposure. Redirecting the CI fixture helper's generated directory to docker run --rm \
-v "$PWD:/workspace:ro" -w /workspace \
-e PYTHONPATH=/workspace -e PYTHONDONTWRITEBYTECODE=1 \
-e CUDA_VISIBLE_DEVICES= \
"$IMAGE" python -c '
import sys
from pathlib import Path
import tests.helpers.stage_config as stage_config
import pytest
stage_config._CI_GENERATED_DIR = Path("/tmp/generated-ci")
raise SystemExit(pytest.main(sys.argv[1:]))
' -q -o addopts='' -p no:cacheprovider \
tests/distributed/omni_connectors \
--ignore=tests/distributed/omni_connectors/test_nixl_connector_native.py \
--ignore=tests/distributed/omni_connectors/test_mooncake_transfer_engine_rdma.py \
--ignore=tests/distributed/omni_connectors/test_bagel_mooncake_connector.py \
--ignore=tests/distributed/omni_connectors/test_bagel_shared_memory_connector.py \
--ignore=tests/distributed/omni_connectors/test_mooncake_tcp_completion.py \
tests/engine/test_orchestrator_kv_sender_info.py \
tests/worker/test_omni_connector_mixin.py \
tests/diffusion/test_diffusion_stage_payload.py \
tests/diffusion/test_inline_stage_diffusion_client.py \
tests/diffusion/test_stage_payload_kv_coexistence.py \
tests/diffusion/test_diffusion_request.py \
tests/diffusion/test_diffusion_config_fields.py \
tests/diffusion/diffusion_kv \
tests/model_executor/stage_input_processors/test_minimax_h3.py \
tests/configObserved result on the fixed tree: |
|
Review of P1: Intermediate stages cannot publish metadata for their outgoing payloadsFor a chain such as Thinker -> Talker -> Code2Wav, Please preserve both incoming and outgoing endpoint responsibilities for intermediate stages and add a three-stage regression test. Simply changing the role to sender would not preserve the incoming-edge contract. P1: Async-chunk prewarming bypasses producer endpoint propagationThe new Please attach the bound upstream producer's endpoint during prewarming and test the actual submitted request with unequal producer/consumer replica IDs. Testing an adapter request with manually populated sender info does not cover this entry point. P1: Producer lease expiry can release memory during an active remote READ
The receiver-side deferred-transfer handling protects destination resources, but not this producer allocation. Increasing the TTL reduces the probability without establishing memory-lifetime safety. Please distinguish unclaimed payload expiry from active-transfer ownership and cover expiry while a consumer transfer remains P2: Explicit
|
Signed-off-by: yuanwu <yuan.wu@intel.com>
Signed-off-by: yuanwu <yuan.wu@intel.com>
|
Addressed the new review in 4146ce7, then integrated main aff7d64 in 882026b. Both commits are signed off and pushed. Main's connector-mixin facade/split is preserved; the NIXL-specific changes are carried in the split runtime and payload-transport modules. @natureofnature — the four findings are addressed:
Also adopted @hsliuustc0106's Validation on the final integrated tree
Reproduction selections (inside the matching test environment, from repository root): export HTTP_PROXY=http://proxy-ir.intel.com:912 HTTPS_PROXY=http://proxy-ir.intel.com:912
export http_proxy="$HTTP_PROXY" https_proxy="$HTTPS_PROXY"
export NO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1
python3 -m pytest -o addopts='' -p no:cacheprovider -o faulthandler_timeout=120 \
tests/distributed/omni_connectors \
--ignore=tests/distributed/omni_connectors/test_nixl_connector_native.py \
--ignore=tests/distributed/omni_connectors/test_mooncake_transfer_engine_rdma.py \
--ignore=tests/distributed/omni_connectors/test_bagel_mooncake_connector.py \
--ignore=tests/distributed/omni_connectors/test_bagel_shared_memory_connector.py \
--ignore=tests/distributed/omni_connectors/test_mooncake_tcp_completion.py \
tests/engine/test_orchestrator_kv_sender_info.py \
tests/worker/test_omni_connector_mixin.py tests/config
# Separate run with two available GPUs:
UCX_RCACHE_MAX_UNRELEASED=1024 python3 -m pytest -o addopts='' -p no:cacheprovider -vv -s -rA \
tests/distributed/omni_connectors/test_nixl_connector_native.pyFor the actual read-only-container runs, an external guarded launcher redirects generated CI fixtures to |
Integrate PR vllm-project#6093 at 882026b, preserving the H3 encoder-disaggregation payload contract and resolving upstream runtime changes. Validation: 735 focused tests passed; expanded suite 1062 passed with two previously reproduced CPU-platform failures; native NIXL matrix 8 passed. Full H3 E2E remains incomplete: default 262K context exceeded KV cache budget, and the 32K CLI retry was blocked by unrelated GPU workloads. Further E2E testing paused at user request pending PR vllm-project#6093 CI investigation. Signed-off-by: yuanwu <yuan.wu@intel.com>
|
CI failed |
Model stage-pool request bindings in the bridge fixture. Retry successful discovery probes outside bounded metadata queries and isolate exact ownership claims from lost-reply timing, retaining endpoint and generation assertions. Validated with 401 passed, 1 skipped and 3 subtests; endpoint stress 100 passed and injected-first-miss 40 passed on CPU with vLLM 0.29. Hardware CI's stale vLLM 0.28 API mismatch is not addressed by this test-only change. Signed-off-by: yuanwu <yuan.wu@intel.com>
|
CI follow-up for NVIDIA failures addressed
Local CPU-only validation with Python 3.12.3 / vLLM 0.29.0: 401 passed, 1 skipped, 3 subtests passed; repeated endpoint cases 100 passed; injected first-query misses 40 passed. The original one-shot assumption fails under the same injection. Ruff check/format and diff checks passed. This is not a remote CI or native RDMA rerun. Remaining accelerator CI incompatibilities
These failures require aligning the accelerator runtime revisions and rerunning CI; this patch does not claim those platforms pass or establish a baseline exemption. The PR diff against its merged main parent does not change the Buildkite/container/dependency configuration. No compatibility shim or broader CI-image change was added to this connector PR. H3/#6264 E2E remains paused and incomplete; no GPU/native/E2E tests were launched for this fix. |
Merge main 01a2f93, including AMD vllm-project#7395, XPU vllm-project#7441 and NPU vllm-project#7433. Preserve both NIXL environment controls and adapt the upstream terminal-chunk test mock to connector metadata forwarding. CPU validation: 1069 passed, 2 skipped, 3 subtests passed; three additional failures reproduced identically on pinned main under the same no-device/offline environment. Hardware CI and H3 E2E remain unverified. Signed-off-by: yuanwu <yuan.wu@intel.com>
|
Follow-up: the accelerator version mismatch fixes are already available on newer main. Updated this branch in signed merge 3e746e8 with pinned main 01a2f93, including AMD #7395, Intel/XPU #7441 and NPU #7433. Their hardware Dockerfiles and Intel pipeline match that main revision. No custom compatibility shim was needed. Resolved the environment-inventory conflict preserving both NIXL controls, and adapted the new upstream terminal-chunk test mock to accept forwarded metadata. Both preceding CI test fixes remain intact. Local CPU validation: 1,069 passed, 2 skipped, plus 3 passing subtests across combined connector/orchestrator and config/AMD checks. Three additional no-device/offline config failures reproduce with identical node IDs/messages on exact main in the same runtime. Focused inventory/AMD/chunk rerun: 155 passed. Ruff and diff checks passed. No GPU/native/E2E or accelerator-image builds were run locally; updated remote CI must verify the hardware results. H3 E2E remains paused. |
Merge PR vllm-project#6093 at 3e746e8, including main 01a2f93. Resolve H3 pipeline and contract conflicts, preserving encoder disaggregation, worker-side media encoding, and separate reference audio budgets. Tested tree b99e97a: CPU 547 passed, H3 config 21 passed, native NIXL 8 passed. Real T2VA, FL2VA and Ref2VA returned HTTP 200 and passed media/decode checks. Overall E2E remains failing because request-correlated NIXL get log evidence is absent; do not interpret generation success as complete transport acceptance. Signed-off-by: yuanwu <yuan.wu@intel.com>
NIXL native E2E validation completed; H3 integration resultsThe native two-process NIXL transport E2E matrix passed all 8 cases (236.34 seconds). These are real native transfers, not the CPU fake-agent tests. Separately, the H3 integration generated valid video/audio for all three tasks, with an outstanding model-request log-evidence check described below. Tested stack / attribution
Native transport matrix — PASS
Native NIXL/UCX, separate producer and consumer processes, mixed CPU/CUDA structured tensors, including H3 text/visual/audio/layout fields and empty optional tensors. Logs record native transfer states, received payload values/types/shapes, source retention and final cleanup. This is same-host CUDA evidence, not cross-node or non-CUDA validation. The matrix ran in validation Real H3 application generation — media PASS, transport log acceptance incomplete
All outputs: H.264 1344×768, 107 frames at 24 fps, stereo 32-kHz AAC; full ffmpeg decode and preview extraction passed. Requests used 8 steps and seed42. Six physical GPUs0,1,3,4,5,6: Stage0 TP2/max_model_len32768; Stage1 TP4/Ulysses1/VAE4. Repository H3 YAML unchanged, with CLI overrides Important boundary: the application runner's request-correlated NIXL log check failed for all three requests: producer Runtime: Python3.12.3 / vLLM0.29.0 / Torch2.13cu130 / NIXL1.3.2; immutable image Detailed #6264 results and output hashes: #6264 (comment) . Local native logs/XML are retained under |
… ports Backport the receiver fix validated in PR6264. Remove shared incoming-edge local bind ports and apply sender info for payload-only receivers as well as KV receivers. Keep standalone explicit sender endpoints intact. Standalone PR6093 validation: 479 CPU tests passed, 1 skipped and 3 passing subtests; both generic native UCX cases passed through OmniKVTransferManager. Full H3 E2E was validated on the PR6264 integration, not this standalone branch. Signed-off-by: yuanwu <yuan.wu@intel.com>
Final NIXL receiving acceptance PASS — correction and follow-upPublished fixes:
Root cause: not merely absent DEBUG logsThe earlier 0625 run attempted to bind the receiver to the producer's occupied port. Receiver initialization failed and the application fell back to the inline prompt. Those videos were therefore not application-level NIXL receive evidence. The earlier matcher correctly failed; this supersedes the previous unresolved-log interpretation. The fix removes shared incoming-edge Fresh integration acceptance — all PASS, overall exit 0Completed 2026-09-13 07:36:01 UTC. 670 CPU passed (1 skipped), 21 H3 config passed, 8 native UCX passed (237.44s). Native tests now exercise manager-created receivers against an already-bound producer endpoint and retain exact mixed-device payload/ownership checks.
Exact keys recorded at both producer and consumer:
All outputs passed H.264 1344×768, 107 frames/24fps, stereo 32-kHz AAC, full ffmpeg decode and preview checks. Owned containers removed, six test GPUs released, unrelated GPU2 untouched. Exact tested stackFull-model tests used #6264 integration, based on #6093 Standalone #6093 backport validation: 479 CPU passed, 1 skipped, 3 passing subtests; 2/2 generic native UCX cases passed. These two cases are separate from the integration's eight. Runtime: Python3.12.3/vLLM0.29.0/Torch2.13cu130/NIXL1.3.2; image Cross-node, non-CUDA and Turbo checkpoints remain untested; remote CI and approval are separate. #6264's inherited unsigned commit remains unresolved. Local evidence: |
Base: main 6505577 (includes squashed vllm-project#6093), plus vllm-project#6939 7abb255. Reconstruct the unique vllm-project#6264 payload delta from a7ac1bd without replaying obsolete dependency history. Preserve complete conditioning, TP fanout, request routing and inline cleanup. Reconcile the refreshed encoder dependency with existing main FastH3, offload and worker MP4 APIs and separate embedded/standalone audio budgets. Keep main NIXL implementation and receiver initialization unchanged. Fresh validation pending; previous E2E evidence applies only to the old tree. Signed-off-by: yuanwu <yuan.wu@intel.com>
Signed-off-by: yuanwu <yuan.wu@intel.com> Signed-off-by: Yuan Wu <yuan.wu@intel.com>
Purpose
Adds
NixlConnector, an optionalOmniConnectorBaseimplementation for moving arbitrary stage payloads across nodes through NIXL.RFC: #6160
When to Choose It
Use
NixlConnectorfor intra-node or inter-node stage transfer when a deployment already uses NIXL for vLLM P/D, needs UCX transport coverage (RDMA, TCP, and accelerator-native paths), or targets Intel XPU through UCX Level Zero.SharedMemoryConnectorNixlConnectorNIXL is not a replacement for existing connectors. It remains an optional lazy import and is only constructed when a deployment YAML names it. It implements Omni
put/getfor arbitrary payloads; it cannot reuse vLLM P/D's NIXL connector because that interface operates on block-table, layer-wise KV semantics.Design and Deployment Constraints
ROUTER, consumerREQ. A consumer obtains metadata withGET_META{key}and reportsXFER_DONEafter successful consumption. Callers that forwardput()metadata directly do not need a handshake socket. Ordinary cross-stage payload requests carry the producer endpoint through the scheduler request path.READ. The producer registers source memory; the consumer allocates destination tensors on its own device and pulls data.zmq_portis a base port. The centralized resolver derives a unique endpoint from purpose (request_forwarding,kv_transfer, ororchestrator), producer stage, replica, and TP local rank. This prevents collisions between TP workers and connector purposes.lease_seconds, default 3600;VLLM_OMNI_NIXL_LEASE_S). A reaper releases abandoned registrations even on an idle producer.close()waits before releasing them. This prevents native DMA into deregistered memory.nixlpackage.docker/Dockerfile.xpuuses the upstream vLLM XPU image, which already ships a matched torch-xpu, UCX Level Zero, and NIXL runtime; this PR adds no custom UCX/NIXL wheel build.Validation Matrix
test_structured_payload_groups_descriptors_by_memory_typetest_idle_producer_lease_expires_without_another_putValidation run in the CUDA test image:
Native smoke:
1 passed; focused NIXL and affected transport suites included above. Ruff checks, Ruff format checks, andgit diff --checkpassed.Non-goals