Skip to content

[Core] Composable parallel: Phase 1 strategy overlay (TP/DP/stage-replica) (RFC #4084) - #4281

Merged
alex-jw-brooks merged 14 commits into
vllm-project:mainfrom
kushanam:feat/composable-parallel-strategies
Jul 11, 2026
Merged

alex-jw-brooks merged 14 commits into
vllm-project:mainfrom
kushanam:feat/composable-parallel-strategies

Conversation

@kushanam

@kushanam kushanam commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Purpose

Implements Phase 1 of RFC #4084 ([RFC]: Composable Parallel Strategies for
vLLM-Omni), adding an opt-in, additive declarative front-end for vLLM-Omni
parallelism. Users describe per-role parallelism in a small strategy.yaml; the
orchestrator overlays the derived sizing onto the already-merged stage configs
(after merge_pipeline_deploy, before CLI overrides) and then leaves runtime
request routing to vLLM-Omni's existing load balancer. When no strategy is
supplied, behavior is unchanged.

This follows the design consensus reached in the RFC discussion:

  • Option B (strategy as a separate input, not embedded in deploy YAML):
    the strategy owns each axis's degree, the deploy YAML owns physical devices,
    and a validator enforces count(devices) == product(degrees).
  • Override-after-merge precedence: the spec is authoritative only for the
    axes it declares; it overrides defaults and errors only on a conflicting
    explicit
    value, so the additive rollout stays painless.
  • Engine DP vs omni stage replicas are distinct axes: dp is realized
    intra-engine by vLLM's DP load balancer; stage_replica spins up independent
    stage backends balanced by omni's StagePool — with a pre-flight check that a
    stage_replica plan can never imply cross-replica vLLM DP.
  • No silent downgrades: hash-affinity DP routing (which omni's balancer
    can't honor) is rejected with an actionable error rather than routed at random.

What landed:

  • New package vllm_omni/config/composable_parallel/ — pure, native types:
    spec.py, routing.py, aggregation.py, translator.py
    (StrategySpec → OmniParallelConfig), apply.py (override-after-merge +
    pre-spawn device check), strategy_yaml.py (loads a strategy.yaml).
  • Wiring (all opt-in): OrchestratorArgs.strategy_config, --strategy-config
    on vllm serve (head + headless) and the Qwen2.5-Omni example, threaded
    through load_and_resolve_stage_configs into StageConfigFactory, with a
    post-CLI reconciliation pass that warns when a CLI arg overrides a
    strategy-declared axis and re-runs the device check against the effective
    layout. A strategy-derived omni_lb_policy is applied at engine construction.

Axis coverage: tp, dp, pp, ep, and stage_replica are translated.
tp and stage_replica are validated by the live e2e below; dp/pp/ep
sizing is covered by CPU unit tests (Phase 1 per the RFC is DP/TP — the
remaining schemes' full semantics arrive in follow-up RFCs).

Test Plan

CPU unit tests (no GPU) — translation, override/conflict, device guard, YAML
parsing, closed-hierarchy guards, factory integration against the real bundled
deploy, post-CLI reconciliation, lb-policy precedence, and the serve CLI flag:

PYTHONPATH=$PWD python3 -m pytest \
  tests/config/composable_parallel/ \
  tests/engine/test_strategy_lb_policy.py \
  tests/entrypoints/test_serve.py \
  tests/engine/test_arg_utils.py -q

Live e2e (H100x8), Qwen2.5-Omni, from
examples/offline_inference/qwen2_5_omni/:

# Control: bundled deploy, no strategy (baseline)
python3 end2end.py --query-type text --num-prompts 1

# Strategy: replicate the thinker x2; lb-policy is NOT passed on the CLI,
# so the strategy-derived round_robin must be applied automatically
python3 end2end.py --query-type text --num-prompts 4 \
  --deploy-config deploy_qwen2_5_omni_replica.yaml \
  --strategy-config strategy_stage_replica.yaml

Test Result

CPU unit tests: all green.

tests/config/composable_parallel/ + test_strategy_lb_policy.py + test_arg_utils.py
  -> 69 passed
tests/entrypoints/test_serve.py
  -> 11 passed

Live e2e — control (no strategy): RC=0, produced text + audio, no errors.

Live e2e — strategy (stage_replica=2 on the thinker, 4 prompts): RC=0,
all 4 prompts produced text + audio, clean StagePool shutdown, no
errors. The new paths fired exactly as intended (no --omni-lb-policy passed)


Essential Elements of an Effective PR Description Checklist
  • The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)".
  • The test plan. Please provide the test scripts & test commands.
  • The test results. Please paste the results comparison before and after, or the e2e results.
  • (Optional) The necessary documentation update — adds runnable examples (strategy_stage_replica.yaml, deploy_qwen2_5_omni_replica.yaml, and --strategy-config/--deploy-config/--omni-lb-policy flags in the Qwen2.5-Omni example).
  • (Optional) Release notes update.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

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

This PR is substantial (>22 files). For such a large change affecting core parallelism infrastructure, please run the L3 tests locally and paste the results here.

Phase 1 strategy overlay implementation looks architecturally sound. The declarative front-end approach for composable parallelism is well-designed.

@kushanam

kushanam commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor Author

@hsliuustc0106, thanks again. I ran the L3 suite for the omni paths on an 8×H100 node and pulled the results together below.

============================= test session starts ==============================
platform linux -- Python 3.12.13, pytest-9.0.3, pluggy-1.6.0 -- /usr/bin/python3
rootdir: /workspace/sw3/test/vllm-omni
configfile: pyproject.toml
plugins: mock-3.15.1, asyncio-1.4.0, anyio-4.13.0
asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collecting ... collected 4918 items / 4804 deselected / 2 skipped / 114 selected

tests/diffusion/diffusion_backend/test_diffusers_backend.py::TestDiffusersBackendEndToEndExecution::test_t2i_random_weights[omni_server0] PASSED [  0%]
tests/diffusion/distributed/test_zimage_parallelism.py::test_zimage_tensor_parallel_tp2 PASSED [  1%]
tests/diffusion/distributed/test_zimage_parallelism.py::test_zimage_vae_patch_parallel_tp2 PASSED [  2%]
tests/diffusion/lora/test_diffusion_lora.py::test_diffusion_model[omni_runner0] PASSED [  3%]
tests/diffusion/lora/test_images_generations_lora.py::test_images_generations_per_request_lora_switching[zimage_turbo_lora] PASSED [  4%]
tests/diffusion/models/ovis_image/test_ovis_image.py::test_real_transformer_init_and_forward PASSED [  5%]
tests/distributed/omni_connectors/test_bagel_mooncake_connector.py::test_bagel_text2img_mooncake_connector PASSED [  6%]
tests/distributed/omni_connectors/test_bagel_shared_memory_connector.py::test_bagel_img2img_shared_memory_connector PASSED [  7%]
tests/distributed/omni_connectors/test_bagel_shared_memory_connector.py::test_bagel_text2img_shared_memory_connector PASSED [  7%]
tests/e2e/offline_inference/test_bagel.py::test_bagel_text2text[omni_runner0] PASSED [  8%]
tests/e2e/offline_inference/test_bagel.py::test_bagel_img2text[omni_runner0] PASSED [  9%]
tests/e2e/offline_inference/test_flux2_klein.py::test_flux2_klein_can_accept_text_inputs[omni_runner0] PASSED [ 10%]
tests/e2e/offline_inference/test_flux2_klein.py::test_flux2_klein_inpaint_basic[omni_runner0] PASSED [ 11%]
tests/e2e/offline_inference/test_flux2_klein.py::test_flux2_klein_inpaint_deterministic[omni_runner0] PASSED [ 12%]
tests/e2e/offline_inference/test_flux2_klein.py::test_flux2_klein_inpaint_different_seeds_different_output[omni_runner0] PASSED [ 13%]
tests/e2e/offline_inference/test_glm_tts.py::test_offline_voice_clone_zh[sync] FAILED [ 14%]
tests/e2e/offline_inference/test_glm_tts.py::test_offline_voice_clone_zh[async_chunk] FAILED [ 14%]
tests/e2e/offline_inference/test_higgs_audio_v2.py::test_higgs_audio_v2_offline_voice_clone[omni_runner_function0] PASSED [ 15%]
tests/e2e/offline_inference/test_hunyuanimage3.py::test_system_prompt_scores[none] SKIPPED [ 16%]
tests/e2e/offline_inference/test_hunyuanimage3.py::test_system_prompt_scores[dynamic] SKIPPED [ 17%]
tests/e2e/offline_inference/test_hunyuanimage3.py::test_system_prompt_scores[en_vanilla] SKIPPED [ 18%]
tests/e2e/offline_inference/test_hunyuanimage3.py::test_system_prompt_scores[en_recaption] SKIPPED [ 19%]
tests/e2e/offline_inference/test_hunyuanimage3.py::test_system_prompt_scores[en_think_recaption] SKIPPED [ 20%]
tests/e2e/offline_inference/test_hunyuanimage3.py::test_system_prompt_scores[en_unified] SKIPPED [ 21%]
tests/e2e/offline_inference/test_ming_flash_omni_expansion.py::test_text_to_audio[omni_runner0] PASSED [ 21%]
tests/e2e/offline_inference/test_ming_flash_omni_expansion.py::test_image_to_audio[omni_runner0] PASSED [ 22%]
tests/e2e/offline_inference/test_moss_tts_nano_expansion.py::test_moss_tts_nano_english[omni_runner0] PASSED [ 23%]
tests/e2e/offline_inference/test_moss_tts_nano_expansion.py::test_moss_tts_nano_chinese[omni_runner0] PASSED [ 24%]
tests/e2e/offline_inference/test_moss_tts_nano_expansion.py::test_moss_tts_nano_deterministic[omni_runner0] PASSED [ 25%]
tests/e2e/offline_inference/test_moss_tts_nano_expansion.py::test_moss_tts_nano_batch[omni_runner0] PASSED [ 26%]
tests/e2e/offline_inference/test_qwen2_5_omni_expansion.py::test_mix_to_audio[omni_runner0] PASSED [ 27%]
tests/e2e/offline_inference/test_qwen2_5_omni_expansion.py::test_text_to_text[omni_runner0] PASSED [ 28%]
tests/e2e/offline_inference/test_qwen3_omni.py::test_video_to_audio[omni_runner0] PASSED [ 28%]
tests/e2e/offline_inference/test_qwen3_tts_base.py::test_text_to_audio_001[no_cuda_graph] PASSED [ 29%]
tests/e2e/offline_inference/test_qwen3_tts_customvoice.py::test_text_to_audio_001[no_cuda_graph] PASSED [ 30%]
tests/e2e/offline_inference/test_sensenova_u1_img2img.py::test_sensenova_u1_img2img PASSED [ 31%]
tests/e2e/offline_inference/test_sensenova_u1_text2img.py::test_sensenova_u1_text2img FAILED [ 32%]
tests/e2e/offline_inference/test_voxcpm2.py::test_voxcpm2_zero_shot_001 ERROR [ 33%]
tests/e2e/offline_inference/test_voxcpm2.py::test_voxcpm2_voice_clone_002 ERROR [ 34%]
tests/e2e/offline_inference/test_voxcpm2.py::test_voxcpm2_prefill_decode_mixed_batch_003 ERROR [ 35%]
tests/e2e/offline_inference/test_voxtral_tts.py::test_voxtral_tts_offline_basic[omni_runner_function0] PASSED [ 35%]
tests/e2e/offline_inference/test_voxtral_tts.py::test_voxtral_tts_offline_streaming PASSED [ 36%]
tests/e2e/offline_inference/test_wan22_t2v.py::test_text_to_video_001[omni_runner0] PASSED [ 37%]
tests/e2e/offline_inference/test_z_image.py::test_zimage[omni_runner0] PASSED [ 38%]
tests/e2e/online_serving/test_bagel.py::test_bagel_text2img_online[omni_server0] PASSED [ 39%]
tests/e2e/online_serving/test_bagel.py::test_bagel_img2img_online[omni_server0] PASSED [ 40%]
tests/e2e/online_serving/test_ernie_image_expansion.py::test_ernie_image[tp_2_cpu_offload] PASSED [ 41%]
tests/e2e/online_serving/test_flux2_klein.py::test_text_to_image_001[default] PASSED [ 42%]
tests/e2e/online_serving/test_glm_tts.py::test_voice_clone_zh_sync[glm_tts_sync] PASSED [ 42%]
tests/e2e/online_serving/test_glm_tts.py::test_voice_clone_zh_async_chunk[glm_tts_async_chunk] PASSED [ 43%]
tests/e2e/online_serving/test_glm_tts.py::test_models_endpoint[glm_tts_sync] PASSED [ 44%]
tests/e2e/online_serving/test_higgs_audio_v2.py::TestHiggsAudioV2OnlineVoiceClone::test_voice_clone_basic[higgs_audio_v2_plain_text] PASSED [ 45%]
tests/e2e/online_serving/test_higgs_audio_v2.py::TestHiggsAudioV2OnlineVoiceClone::test_voice_clone_pcm_streaming[higgs_audio_v2_plain_text] PASSED [ 46%]
tests/e2e/online_serving/test_hunyuan_video_15.py::test_text_to_video_001[default] PASSED [ 47%]
tests/e2e/online_serving/test_lance.py::test_lance_text2img_online[omni_server0] ERROR [ 48%]
tests/e2e/online_serving/test_lance.py::test_lance_img2img_online[omni_server0] ERROR [ 49%]
tests/e2e/online_serving/test_longcat_image.py::test_longcat_image[default] PASSED [ 50%]
tests/e2e/online_serving/test_longcat_image_edit.py::test_longcat_image_edit[default] PASSED [ 50%]
tests/e2e/online_serving/test_mimo_audio.py::test_audio_to_text_audio_001[omni_server0] SKIPPED [ 51%]
tests/e2e/online_serving/test_mimo_audio.py::test_text_to_text_001[omni_server0] PASSED [ 52%]
tests/e2e/online_serving/test_qwen2_5_omni_expansion.py::test_mix_to_text_audio_001[omni_server0] PASSED [ 53%]
tests/e2e/online_serving/test_qwen2_5_omni_expansion.py::test_text_to_text_001[omni_server0] PASSED [ 54%]
tests/e2e/online_serving/test_qwen3_omni.py::test_mix_to_text_audio_001[default] PASSED [ 55%]
tests/e2e/online_serving/test_qwen3_omni.py::test_text_to_text_001[default] PASSED [ 56%]
tests/e2e/online_serving/test_qwen3_omni.py::test_thinker_prefix_caching_text_output[default] PASSED [ 57%]
tests/e2e/online_serving/test_qwen3_omni.py::test_thinker_prefix_caching_audio_output[default] PASSED [ 57%]
tests/e2e/online_serving/test_qwen3_tts_base.py::test_text_to_audio_001[async_chunk] PASSED [ 58%]
tests/e2e/online_serving/test_qwen3_tts_base.py::test_text_to_audio_002[async_chunk] PASSED [ 59%]
tests/e2e/online_serving/test_qwen3_tts_customvoice.py::test_text_to_audio_001[async_chunk] PASSED [ 60%]
tests/e2e/online_serving/test_qwen3_tts_customvoice.py::test_text_to_audio_002[async_chunk] PASSED [ 61%]
tests/e2e/online_serving/test_qwen_image.py::test_text_to_image_001[default] PASSED [ 62%]
tests/e2e/online_serving/test_qwen_image.py::test_batch_001[default] PASSED [ 63%]
tests/e2e/online_serving/test_qwen_image_edit.py::test_single_image_to_image_001[default] PASSED [ 64%]
tests/e2e/online_serving/test_qwen_image_edit.py::test_multi_images_to_image_001[default] PASSED [ 64%]
tests/e2e/online_serving/test_qwen_image_edit.py::test_different_sizes_001[default] PASSED [ 65%]
tests/e2e/online_serving/test_qwen_image_layered.py::test_single_image_to_image_001[default] PASSED [ 66%]
tests/e2e/online_serving/test_sd3.py::test_text_to_image_001[default] ERROR [ 67%]
tests/e2e/online_serving/test_voxcpm2_tts.py::test_text_to_audio_001[voxcpm2] ERROR [ 68%]
tests/e2e/online_serving/test_voxcpm2_tts.py::test_text_to_audio_002[voxcpm2] ERROR [ 69%]
tests/e2e/online_serving/test_voxtral_tts.py::TestVoxtralTTSFixedVoice::test_speech_different_voices[omni_server0] PASSED [ 70%]
tests/e2e/online_serving/test_wan22_t2v.py::test_text_to_video_001[default] PASSED [ 71%]
tests/e2e/online_serving/test_z_image.py::test_basic_001[default] PASSED [ 71%]
tests/e2e/online_serving/test_z_image.py::test_different_sizes_001[default] PASSED [ 72%]
tests/entrypoints/openai_api/test_openai_audio_list_endpoints.py::TestOpenAIAudioListEndpoints::test_list_voices_endpoint[omni_server0] PASSED [ 73%]
tests/entrypoints/openai_api/test_openai_audio_list_endpoints.py::TestOpenAIAudioListEndpoints::test_models_endpoint[omni_server0] PASSED [ 74%]
tests/entrypoints/openai_api/test_qwen3_omni_realtime_websocket.py::TestQwen3OmniRealtimeWebSocket::test_streaming_audio_input_pcm_output_async_chunk[async_chunk] PASSED [ 75%]
tests/entrypoints/openai_api/test_qwen3_omni_realtime_websocket.py::TestQwen3OmniRealtimeWebSocket::test_streaming_audio_input_pcm_output[sync] PASSED [ 76%]
tests/entrypoints/openai_api/test_qwen3_tts_speaker_embedding.py::TestSpeakerEmbeddingBase::test_speaker_embedding_produces_audio[qwen3-tts-0.6b-base] PASSED [ 77%]
tests/entrypoints/openai_api/test_qwen3_tts_speaker_embedding.py::TestSpeakerEmbeddingBase::test_speaker_embedding_pcm_not_silence[qwen3-tts-0.6b-base] PASSED [ 78%]
tests/entrypoints/openai_api/test_qwen3_tts_speaker_embedding.py::TestSpeakerEmbeddingBase::test_speaker_embedding_streaming[qwen3-tts-0.6b-base] PASSED [ 78%]
tests/entrypoints/openai_api/test_qwen3_tts_speaker_embedding.py::TestSpeakerEmbedding1_7B::test_2048_dim_embedding_produces_audio[qwen3-tts-1.7b-base] PASSED [ 79%]
tests/entrypoints/openai_api/test_qwen3_tts_websocket.py::TestQwen3TTSWebSocket::test_streaming_pcm_output[qwen3-tts-ws-customvoice] PASSED [ 80%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchE2E::test_batch_basic_two_items[qwen3-tts-batch-default] PASSED [ 81%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchE2E::test_batch_single_item[qwen3-tts-batch-default] PASSED [ 82%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchE2E::test_batch_per_item_voice_override[qwen3-tts-batch-default] PASSED [ 83%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchE2E::test_batch_multiple_languages[qwen3-tts-batch-default] PASSED [ 84%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchE2E::test_batch_whisper_transcription[qwen3-tts-batch-default] PASSED [ 85%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchE2E::test_batch_result_indices_ordered[qwen3-tts-batch-default] PASSED [ 85%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchValidation::test_batch_empty_items_rejected[qwen3-tts-batch-default] PASSED [ 86%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchValidation::test_batch_exceeds_max_items[qwen3-tts-batch-default] PASSED [ 87%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchSize2::test_batch2_produces_valid_audio[qwen3-tts-batch-max2] PASSED [ 88%]
tests/entrypoints/openai_api/test_serving_speech_batch.py::TestSpeechBatchSize2::test_batch2_vs_sequential_timing[qwen3-tts-batch-max2] PASSED [ 89%]
tests/entrypoints/test_omni_sleep_mode.py::TestOmniSleepMode::test_llm_sleep_ack PASSED [ 90%]
tests/entrypoints/test_omni_sleep_mode.py::TestOmniSleepMode::test_diffusion_sleep_handshake PASSED [ 91%]
tests/entrypoints/test_omni_sleep_mode.py::TestOmniSleepMode::test_cross_device_cleanup PASSED [ 92%]
tests/entrypoints/test_omni_sleep_mode.py::TestOmniSleepMode::test_diffusion_integrity_bit_level PASSED [ 92%]
tests/entrypoints/test_omni_sleep_mode.py::TestOmniSleepMode::test_coordinated_cross_device SKIPPED [ 93%]
tests/entrypoints/test_omni_sleep_mode.py::TestOmniSleepMode::test_diffusion_vram_lifecycle_audit PASSED [ 94%]
tests/entrypoints/test_omni_sleep_mode.py::test_diffusion_model_sleep_tp[1] PASSED [ 95%]
tests/entrypoints/test_omni_sleep_mode.py::test_multistage_sleep_h100[1] PASSED [ 96%]
tests/entrypoints/test_omni_sleep_mode.py::test_multistage_sleep_h100[2] PASSED [ 97%]
tests/entrypoints/test_omni_sleep_mode.py::test_pure_diffusion_scenario[1] PASSED [ 98%]
tests/entrypoints/test_omni_sleep_mode.py::test_pure_diffusion_scenario[2] PASSED [ 99%]
tests/examples/offline_inference/test_internvla_a1.py::test_internvla_a1_offline_open_loop SKIPPED [100%]

....

=========================== short test summary info ============================
SKIPPED [1] tests/diffusion/models/dreamid_omni/test_dreamid_omni_hsdp.py:4: could not import 'dreamid_omni.modules.model': No module named 'dreamid_omni.modules'
SKIPPED [1] tests/dreamzero/upstream/test_openpi_e2e_source_parity.py:41: could not import 'openpi_client.msgpack_numpy': No module named 'openpi_client'
SKIPPED [6] tests/e2e/offline_inference/test_hunyuanimage3.py:330: Could not load CLIP model from local cache (openai/clip-vit-base-patch32): We couldn't connect to 'https://huggingface.co' to load the files, and couldn't find them in the cached files.
Check your internet connection or see how to run the library in offline mode at 'https://huggingface.co/docs/transformers/installation#offline-mode'.
SKIPPED [1] tests/e2e/online_serving/test_mimo_audio.py:81: CI failed 8571
SKIPPED [1] tests/entrypoints/test_omni_sleep_mode.py:296: Flaky/CI: dual AsyncOmni can fail with RuntimeError: Orchestrator init failed, StageDiffusionProc died during handshake. Re-enable when stable (no OOM on coordinated talker+diffusion). Note: this test is fixture-free so skip does not init llm/diffusion engines.
SKIPPED [1] tests/examples/offline_inference/test_internvla_a1.py:28: INTERNVLA_A1_MODEL_DIR is not set
ERROR tests/e2e/offline_inference/test_voxcpm2.py::test_voxcpm2_zero_shot_001
ERROR tests/e2e/offline_inference/test_voxcpm2.py::test_voxcpm2_voice_clone_002
ERROR tests/e2e/offline_inference/test_voxcpm2.py::test_voxcpm2_prefill_decode_mixed_batch_003
ERROR tests/e2e/online_serving/test_lance.py::test_lance_text2img_online[omni_server0]
ERROR tests/e2e/online_serving/test_lance.py::test_lance_img2img_online[omni_server0]
ERROR tests/e2e/online_serving/test_sd3.py::test_text_to_image_001[default]
ERROR tests/e2e/online_serving/test_voxcpm2_tts.py::test_text_to_audio_001[voxcpm2]
ERROR tests/e2e/online_serving/test_voxcpm2_tts.py::test_text_to_audio_002[voxcpm2]
FAILED tests/e2e/offline_inference/test_glm_tts.py::test_offline_voice_clone_zh[sync]
FAILED tests/e2e/offline_inference/test_glm_tts.py::test_offline_voice_clone_zh[async_chunk]
FAILED tests/e2e/offline_inference/test_sensenova_u1_text2img.py::test_sensenova_u1_text2img
= 3 failed, 94 passed, 11 skipped, 4804 deselected, 417 warnings, 8 errors in 7247.03s (2:00:47) =

All 8 errors and a few of the failures turned out to be missing test-harness dependencies / gated-model access in the runner. Once those were installed/granted they go green:

5 opencc-dependent tests -> passed on rerun after pip install opencc
test_voxcpm2 (offline + online TTS) -> passed after pip install voxcpm
test_sd3 → passed after HF model access
The 3 residual failures are pre-existing and unrelated to this PR:

test_glm_tts voice-clone (×2): model-side dtype bug - float input vs BFloat16 bias in a conv1d layer.
test_sensenova_u1_text2img: strict golden-pixel assertion (one-pixel RGB mismatch), not a pipeline failure.
test_qwen2_5_omni_expansion::test_mix_to_text_audio_001: model-content/keyword check on synthesized audio.


On overhead: I benchmarked the adapter path (StrategySpec -> translation -> engine kwargs) against driving the engine directly (main), on the same model/config (Qwen, TP=1, H100, warmup discarded, 3 timed iters). The translated EngineCore config is byte-identical, and decode throughput matches within noise:

baseline (typed engine): 8086.1 tok/s (std 30.4)
adapter (translated): 8077.0 tok/s (std 31.5)
delta: 9 tok/s (0.11%), inside per-run variance
So the strategy layer is effectively a zero-overhead pass-through. Happy to attach the full logs if useful.

@kushanam
kushanam force-pushed the feat/composable-parallel-strategies branch from 557df8d to 00f6978 Compare June 10, 2026 01:00

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

Reviewing from a UX and default-YAML perspective. The core design (separate strategy intent from deploy layout) is solid — a few things that would make the feature more adoptable out of the box.

thinker:
- axis: stage_replica
size: 2
routing: round_robin

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 is the only strategy example and it only demonstrates stage_replica. The PR supports tp, dp, pp, ep axes but provides no example YAMLs for them.

Consider adding a strategy_tp2.yaml (e.g. thinker: [{axis: tp, size: 2}]) alongside a matching deploy_qwen2_5_omni_tp2.yaml. TP is the most common parallelism axis and an example would help users understand the mental model.

Alternatively, a single strategy_examples.yaml with commented-out sections for each axis would serve as a living reference.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TP example request Added a TP=2 pair: strategy_tp2.yaml + deploy_qwen2_5_omni_tp2.yaml. Verified e2e on Qwen2.5-Omni (the strategy alone sharded the thinker TP=2). Thanks!

# python3 end2end.py --query-type text --num-prompts 6 \
# --deploy-config deploy_qwen2_5_omni_replica.yaml \
# --strategy-config strategy_stage_replica.yaml --omni-lb-policy round-robin
async_chunk: false

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.

The header comment shows --omni-lb-policy round-robin passed on the CLI, but the PR's async_omni_engine._apply_strategy_lb_policy auto-derives the LB policy from the strategy's routing field. These two paths contradict each other.

The example should drop --omni-lb-policy and let the strategy drive it — that demonstrates the auto-derive feature. Add a comment like:

# omni-lb-policy is auto-derived from the strategy's routing field;
# only pass --omni-lb-policy if you need to override it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch! removed it and added a note that the LB policy auto-derives from the strategy's routing field

@@ -0,0 +1,63 @@
# Qwen2.5-Omni deploy laid out for stage replication (4-GPU box).

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 deploy YAML assumes exactly 4 GPUs (thinker on 0,1; talker on 2; code2wav on 2). Users on 2-GPU or 8-GPU boxes will have to figure this out themselves.

Consider either:

  1. Making the GPU count explicit in the filename (e.g. deploy_qwen2_5_omni_replica_4gpu.yaml), or
  2. Adding a prominent comment: # Requires >= 3 GPUs (thinker uses 2, talker+code2wav share 1)

A common pattern is to provide variants per GPU count, or at least document how to adapt the devices fields.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added # Requires >= 3 GPUs: thinker uses 2 (stage_replica=2), talker + code2wav share 1


MESH_AXIS_KINDS: tuple[MeshAxisKind, ...] = (
"tp",
"dp",

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.

11 axis kinds are declared but only tp, dp, stage_replica are translatable in Phase 1. The rest (pp, ep, sp_ulysses, sp_ring, cfg, vae_pp, hsdp, stage_pp, cp) are reserved.

Consider adding a docstring or comment listing which axes are currently wired up vs reserved, so users reading the spec don't try to use an unsupported axis and get a confusing error. Something like:

# Phase 1: tp, dp, stage_replica
# Reserved for future phases: pp, ep, sp_ulysses, sp_ring, cfg, vae_pp, hsdp, stage_pp, cp

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a docstring listing wired axes (tp/dp/pp/ep/stage_replica) vs reserved (sp_ulysses/sp_ring/cfg/vae_pp/hsdp/stage_pp/cp, which raise on translation)

omni_config_group.add_argument(
"--strategy-config",
type=str,
default=None,

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.

The --strategy-config help text says "opt-in; ignored on the legacy YAML path." This is important behavior that users might miss.

Consider either:

  1. Emitting a warning when --strategy-config is passed but the model uses the legacy YAML path (so the flag is silently ignored), or
  2. Updating the help text to be more explicit: "Only works with registry-based models (e.g. qwen2_5_omni). Silently ignored for models using legacy pipeline YAML."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

logs a warning when --strategy-config is passed but the model resolves via the legacy yaml path. Thanks!

@kushanam

Copy link
Copy Markdown
Contributor Author

Thanks @hsliuustc0106 for your review. I’ve addressed the comments. Please take a look when you have a chance. Thank you.

@@ -0,0 +1,69 @@
# Qwen2.5-Omni deploy laid out for stage replication.

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.

currently, the deploy yamls are placed under src config folder and there does not exist strategy yaml for users, what do you suggest to avoid change user behavior if possible? currently we have added a lot of recipes, this needs more consideration:)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point @hsliuustc0106, and agreed it needs care. The intent is to keep this fully additive:

  • So that nothing changes unless you opt in. All built-in deploy recipes work exactly as today; this PR doesn't touch any of them. Strategy only kicks in when you pass --strategy-config, otherwise the path is byte-identical to main.
  • We don't ship strategy YAMLs as recipes. They stay user authored, the only ones in the tree are under examples/ as references, not in vllm_omni/deploy/. So the recipe surface users rely on is unchanged.
  • The common case needs no new file. Plain --tensor-parallel-size etc. still work and get wrapped into the equivalent spec internally, so simple users never see a strategy file.

For the bigger question of where strategy files should live long term (and whether built-in recipes eventually pair with one), I'd defer that to #4021 so we don't migrate the recipe layout twice. For Phase 1: zero strategy recipes shipped, examples only. WDYT?

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.

LGTM, cc @alex-jw-brooks , we need to move it faster with this PR

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.

Hey, sorry for the delay @kushanam @hsliuustc0106 - I will review this PR first thing in the morning 🙏

@alex-jw-brooks alex-jw-brooks self-assigned this Jun 18, 2026
@hsliuustc0106 hsliuustc0106 added high priority high priority issue, needs to be done asap core related to core module: cache, scheduler, engine, worker, modelrunner labels Jun 22, 2026
@hsliuustc0106 hsliuustc0106 mentioned this pull request Jun 22, 2026
1 task done

@alex-jw-brooks alex-jw-brooks 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.

Thanks for your patience and work @kushanam! Some suggestions and discussion points.

Also please fix DCO when you get the chance!

raise DuplicateAxisKindError(f"axis kind {kind!r} appears more than once in the spec stack")

owner = _resolve_l1_owner(spec)
if kind == "dp":

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.

I know the spec types the kind to MeshAxisKind, but I think it would be better to use a str enum instead for some of these types that are widely used. Otherwise refactors etc may break these types of changes in the future

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. class MeshAxisKind(str, Enum) string comparisons still work

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.

I think it would be better to also compare against the enum members instead of the raw strings to prevent typos. I can also open a follow-up for this though - IMO it would be useful to add this before we get too far along, and also clean up load_and_resolve_stage_configs to not return a tuple, but we don't need to block this PR for it

Comment thread vllm_omni/config/composable_parallel/translator.py Outdated
Comment thread vllm_omni/config/composable_parallel/translator.py
Comment thread vllm_omni/config/composable_parallel/translator.py
Comment thread vllm_omni/config/composable_parallel/translator.py
descriptor = f"stage_id={key}"
else:
matches = [s for s in stages if getattr(s, "model_stage", None) == key]
descriptor = f"model_stage={key!r}"

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.

can you clarify why we match against both stage and stage id? This seems like it could cause confusing behaviors imo

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Since key type picks the field (str -> model_stage, int -> stage_id, bool rejected) I think it's unambiguous. Added a docstring spelling this out. Does it satisfy your concern @alex-jw-brooks?

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.

Thanks @kushanam! Is there a reason why we can't just use the model_stage given that it's not optional on stage configs? This still feels unnecessarily flexible to me. I think it's more practical to refer to stages by name rather than indices as keys, because in addition to being more readable, its more understandable if we have situations like either of the following:

  • An architecture supports multiple pipeline configs, and the user either picks one or its selected based on the HF config for what is actually supported. In this case, the index for a given stage may not be the same across pipeline config variations, but the name should be
  • A model's stage separations change (e.g., splitting one stage to two). This would mess up both names and indices, but with names we could do potential validation that may not be possible based purely on indices

Comment thread vllm_omni/config/composable_parallel/apply.py Outdated
Comment thread vllm_omni/config/composable_parallel/strategy_yaml.py Outdated
Comment thread vllm_omni/entrypoints/cli/serve.py Outdated
Comment thread vllm_omni/entrypoints/utils.py Outdated
kushanam pushed a commit to kushanam/vllm-omni that referenced this pull request Jun 22, 2026
@kushanam

Copy link
Copy Markdown
Contributor Author

Thanks for your patience and work @kushanam! Some suggestions and discussion points.

Also please fix DCO when you get the chance!

Thank you @alex-jw-brooks for your thorough review. I addressed the comments. Please have a look when you got a chance!

kushanam pushed a commit to kushanam/vllm-omni that referenced this pull request Jun 23, 2026
…ject#8 docstring)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
kushanam pushed a commit to kushanam/vllm-omni that referenced this pull request Jun 23, 2026
…ranslationError (taxonomy align)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
@kushanam
kushanam force-pushed the feat/composable-parallel-strategies branch from b550c95 to f48a24c Compare June 23, 2026 03:25
kushanam added a commit to kushanam/vllm-omni that referenced this pull request Jun 23, 2026
Signed-off-by: Kushan A. <kahmadian@nvidia.com>

@alex-jw-brooks alex-jw-brooks 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.

Thanks @kushanam! I think this is close to being ready to merge, but added some additional discussion points. The most important things are the public docs, the test skips, and the dual key stuff though (since it affects API a bit), I think once that is resolved we can merge + iterate on this design in the next phase.

Also please resolve conflicts when you get the chance!

raise DuplicateAxisKindError(f"axis kind {kind!r} appears more than once in the spec stack")

owner = _resolve_l1_owner(spec)
if kind == "dp":

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.

I think it would be better to also compare against the enum members instead of the raw strings to prevent typos. I can also open a follow-up for this though - IMO it would be useful to add this before we get too far along, and also clean up load_and_resolve_stage_configs to not return a tuple, but we don't need to block this PR for it

if not derived:
return
explicit = kwargs.get("omni_lb_policy")
user_set = explicit is not None and str(explicit) != "random"

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.

As a follow-up, I think we should make sure defaults/fallbacks for all of these are initialized in a way that is easy to follow (i.e., things like lora stuff, async chunk, and this are currently a bit scattered).

It should be a separate PR as this effort continues in case there are conflicts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, will do this as a separate follow-up after this PR lands. I'll open a tracking issue covering the lora, async-chunk, and omni_lb_policy defaults so they all get centralized in one pass

type=str,
default=None,
choices=["random", "round-robin", "least-queue-length"],
help="StagePool load-balancer policy for replicated stages (engine-level knob).",

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.

I think engine-level knob should actually be pipeline level or orchestrator level. Engine level is confusing because every stage has its own engine, so it sounds like this is either stage level or ambiguously configuring all stages of a specific engine type

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Renamed to "orchestrator-level knob" everywhere


def test_device_check_survives_cli_override():
if not _DEPLOY.exists():
pytest.skip("qwen2_5_omni deploy config not found")

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.

Can you remove these conditional skips? If the deploy config is moved, these tests should fail so that we know to update them rather than skip

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed. All three _DEPLOY.exists() skip guards in this file are gone so the tests now fail loudly if the deploy config moves

descriptor = f"stage_id={key}"
else:
matches = [s for s in stages if getattr(s, "model_stage", None) == key]
descriptor = f"model_stage={key!r}"

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.

Thanks @kushanam! Is there a reason why we can't just use the model_stage given that it's not optional on stage configs? This still feels unnecessarily flexible to me. I think it's more practical to refer to stages by name rather than indices as keys, because in addition to being more readable, its more understandable if we have situations like either of the following:

  • An architecture supports multiple pipeline configs, and the user either picks one or its selected based on the HF config for what is actually supported. In this case, the index for a given stage may not be the same across pipeline config variations, but the name should be
  • A model's stage separations change (e.g., splitting one stage to two). This would mess up both names and indices, but with names we could do potential validation that may not be possible based purely on indices


The output is a plain sizing struct keyed by the real ``OmniEngineArgs`` /
``EngineArgs`` field names, so this module runs on CPU and the deploy layer can
splat the result into a stage's engine args.

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.

@kushanam can you please add user facing docs too (not just in the examples)? I think something like this would be useful.

Doesn't need to be too extensive, I just think it would help a lot for discoverability to have something in the public docs + an example or two for how to use it

kushanam pushed a commit to kushanam/vllm-omni that referenced this pull request Jun 26, 2026
…ject#8 docstring)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
kushanam pushed a commit to kushanam/vllm-omni that referenced this pull request Jun 26, 2026
…ranslationError (taxonomy align)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
kushanam added a commit to kushanam/vllm-omni that referenced this pull request Jun 26, 2026
Signed-off-by: Kushan A. <kahmadian@nvidia.com>
kushanam added a commit to kushanam/vllm-omni that referenced this pull request Jul 10, 2026
- Narrow strategy role key to str (model_stage); drop int/bool branch
- Remove conditional pytest.skip on deploy-YAML path
- Rename "engine-level knob" -> "orchestrator-level knob" (omni_lb_policy)
- Add docs/configuration/composable_parallel.md

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
kushanam added a commit to kushanam/vllm-omni that referenced this pull request Jul 10, 2026
…ntegration

stage_config._PIPELINE_REGISTRY -> pipeline_registry.OMNI_PIPELINES;
StageConfigFactory now in config_factory. Caught by CPU test collection.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
kushanam added a commit to kushanam/vllm-omni that referenced this pull request Jul 10, 2026
…m_registry

_create_from_registry on main now takes pipeline_cfg as positional
arg 2 and returns (stages, omni_lb_policy). Update 2 tests in
test_factory_integration.py to pass pipeline_cfg and 1 test in
test_config_factory.py to unpack the tuple.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
kushanam added a commit to kushanam/vllm-omni that referenced this pull request Jul 10, 2026
…y move

The _reconcile_strategy_with_cli warning now emits from
config_factory.py; update test_cli_overrides_strategy_with_warning
to attach its handler to vllm_omni.config.config_factory.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
kushanam and others added 14 commits July 10, 2026 13:20
…n B)

Opt-in strategy.yaml that overlays per-role parallel sizing (TP, stage
replica, ...) onto merged stage configs after pipeline+deploy merge and
before CLI overrides. No-op when no strategy is supplied.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: kushanam <kushanam@users.noreply.github.com>
Apply strategy-derived omni_lb_policy at engine construction, expose
--strategy-config on vllm serve (and headless), validate dp routing policy,
enforce strategy sizing against the post-CLI layout, and harden strategy.yaml
parsing.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: kushanam <kushanam@users.noreply.github.com>
Modernize typing imports/annotations (UP006/UP035/UP045) and add the
Error suffix to composable-parallel exception classes (N818).

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: kushanam <kushanam@users.noreply.github.com>
Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
Signed-off-by: kushanam <kushanam@users.noreply.github.com>
TP=2 example pair (strategy_tp2 + deploy_qwen2_5_omni_tp2), spec
wired/reserved-axis docs, replica example lb-policy/GPU notes, and a
legacy-path warning + sharper help text for --strategy-config.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
…ject#8 docstring)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: kahmadian <kahmadian@users.noreply.github.com>
…ranslationError (taxonomy align)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
Signed-off-by: kahmadian <kahmadian@users.noreply.github.com>
Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
- Narrow strategy role key to str (model_stage); drop int/bool branch
- Remove conditional pytest.skip on deploy-YAML path
- Rename "engine-level knob" -> "orchestrator-level knob" (omni_lb_policy)
- Add docs/configuration/composable_parallel.md

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
…ntegration

stage_config._PIPELINE_REGISTRY -> pipeline_registry.OMNI_PIPELINES;
StageConfigFactory now in config_factory. Caught by CPU test collection.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
…m_registry

_create_from_registry on main now takes pipeline_cfg as positional
arg 2 and returns (stages, omni_lb_policy). Update 2 tests in
test_factory_integration.py to pass pipeline_cfg and 1 test in
test_config_factory.py to unpack the tuple.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
…y move

The _reconcile_strategy_with_cli warning now emits from
config_factory.py; update test_cli_overrides_strategy_with_warning
to attach its handler to vllm_omni.config.config_factory.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
Parse and pass stage_overrides into load_and_resolve_stage_configs from run_headless via a shared parse_stage_overrides helper, restoring parity with the standard engine path.

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
@kushanam
kushanam force-pushed the feat/composable-parallel-strategies branch from aa62af9 to 6869fd1 Compare July 10, 2026 20:22
@kushanam

Copy link
Copy Markdown
Contributor Author

Oops you guys are already planning to merge this PR. Sorry for the late catch up! 😁 I think my comments are primarily decorative

Thank you @fhfuih, I will address them in the following PR if you don't mind :)

Hi @kushanam, can you please resolve the conflicts? I think we can merge this PR now once it is updated 🙏

@alex-jw-brooks Sorry for the delay and thanks for you attention :) Rebased.

@hsliuustc0106 hsliuustc0106 added ready label to trigger buildkite CI and removed ready label to trigger buildkite CI labels Jul 10, 2026
@alex-jw-brooks

Copy link
Copy Markdown
Collaborator

Thanks @kushanam @fhfuih - I think let's go ahead and merge this PR since its pretty prone to conflicts with the ongoing OmniConfig work. We can iterate on it and collaborate in follow-ups 🙂

@alex-jw-brooks
alex-jw-brooks merged commit ff6f0da into vllm-project:main Jul 11, 2026
7 of 9 checks passed
wkutak pushed a commit to wkutak/vllm-omni-cosmos-genai-nim that referenced this pull request Jul 13, 2026
…lica) (RFC vllm-project#4084) (vllm-project#4281)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: kushanam <kushanam@users.noreply.github.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
Signed-off-by: kahmadian <kahmadian@users.noreply.github.com>
Co-authored-by: kushanam <kushanam@users.noreply.github.com>
Co-authored-by: kahmadian <kahmadian@users.noreply.github.com>
Signed-off-by: Wojciech Kutak <wkutak@nvidia.com>
bowieshi pushed a commit to bowieshi/vllm-omni that referenced this pull request Jul 14, 2026
…lica) (RFC vllm-project#4084) (vllm-project#4281)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: kushanam <kushanam@users.noreply.github.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
Signed-off-by: kahmadian <kahmadian@users.noreply.github.com>
Co-authored-by: kushanam <kushanam@users.noreply.github.com>
Co-authored-by: kahmadian <kahmadian@users.noreply.github.com>
khairulkabir1661 pushed a commit to khairulkabir1661/vllm-omni that referenced this pull request Sep 25, 2026
…lica) (RFC vllm-project#4084) (vllm-project#4281)

Signed-off-by: Kushan A. <kahmadian@nvidia.com>
Signed-off-by: kushanam <kushanam@users.noreply.github.com>
Signed-off-by: Kushan A <kahmadian@nvidia.com>
Signed-off-by: kahmadian <kahmadian@users.noreply.github.com>
Co-authored-by: kushanam <kushanam@users.noreply.github.com>
Co-authored-by: kahmadian <kahmadian@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core related to core module: cache, scheduler, engine, worker, modelrunner high priority high priority issue, needs to be done asap ready label to trigger buildkite CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants