[Rebase] Rebase to vllm v0.21.0 (#3530)
Signed-off-by: tzhouam <tzhouam@connect.ust.hk> Signed-off-by: Zhou Taichang <tzhouam@connect.ust.hk> Signed-off-by: NumberWan <wantszkin2003@gmail.com> Signed-off-by: Divyansh Singhvi <divyanshsinghvi@gmail.com> Signed-off-by: dsinghvi <divyanshsinghvi@gmail.com> Signed-off-by: Dnoob <dxpouo@gmail.com> Signed-off-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com> Signed-off-by: knlnguyen1802 <knlnguyen1802@gmail.com> Signed-off-by: rein yang <ruiruyang2@gmail.com> Signed-off-by: Nick Cao <ncao@redhat.com> Signed-off-by: zhumingjue <zhumingjue@huawei.com> Signed-off-by: Ricardo Noriega De Soto <rnoriega@redhat.com> Signed-off-by: lyj-jjj <liuyingjun5@huawei.com> Signed-off-by: gcanlin <canlinguosdu@gmail.com> Signed-off-by: wangyu <410167048@qq.com> Signed-off-by: weizhoublue <weizhoublue@github.com> Signed-off-by: weizhou.lan@daocloud.io <weizhou.lan@daocloud.io> Signed-off-by: dengyunyang <584797741@qq.com> Signed-off-by: 汪志鹏 <wangzhipeng628@gmail.com> Signed-off-by: David Chen <530634352@qq.com> Signed-off-by: Jie Liu <33612777+keeper-jie@users.noreply.github.com> Signed-off-by: Yueqian Lin <linyueqian@outlook.com> Signed-off-by: princepride <wangzhipeng628@gmail.com> Signed-off-by: natureofnature <wzliu@connect.hku.hk> Signed-off-by: bjf-frz <frz123db@gmail.com> Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com> Signed-off-by: KexiongYu <yukexiong1@huawei.com> Signed-off-by: Huang, Zeyu <11222265+fhfuih@users.noreply.github.com> Signed-off-by: Zeyu Huang | 黃澤宇 <11222265+fhfuih@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: NumberWan <wantszkin2003@gmail.com> Co-authored-by: dsinghvi <divyanshsinghvi@gmail.com> Co-authored-by: Dnoob <dxpouo@gmail.com> Co-authored-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com> Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com> Co-authored-by: knlnguyen1802 <knlnguyen1802@gmail.com> Co-authored-by: Samit <285365963@qq.com> Co-authored-by: rein yang <73573651+R2-Y@users.noreply.github.com> Co-authored-by: Nick Cao <ncao@redhat.com> Co-authored-by: zhumingjue138 <zhumingjue@huawei.com> Co-authored-by: Ricardo Noriega <rnoriega@redhat.com> Co-authored-by: lyj-jjj <liuyingjun5@huawei.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: gcanlin <canlinguosdu@gmail.com> Co-authored-by: wangyu <53896905+yenuo26@users.noreply.github.com> Co-authored-by: weizhoublue <45163302+weizhoublue@users.noreply.github.com> Co-authored-by: weizhoublue <weizhoublue@github.com> Co-authored-by: dengyunyang <584797741@qq.com> Co-authored-by: 汪志鹏 <wangzhipeng628@gmail.com> Co-authored-by: WeiQing Chen <40507679+david6666666@users.noreply.github.com> Co-authored-by: Jie Liu <33612777+keeper-jie@users.noreply.github.com> Co-authored-by: Yueqian Lin <linyueqian@outlook.com> Co-authored-by: NATURE <wzliu@connect.hku.hk> Co-authored-by: bjf-frz <frz123db@gmail.com> Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com> Co-authored-by: Y. Fisher <yukexiong1@huawei.com> Co-authored-by: Zeyu Huang | 黃澤宇 <11222265+fhfuih@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
+20









Zhou Taichang
Claude Opus 4.7
NumberWan
dsinghvi
Dnoob
Yueqian Lin
Hongsheng Liu
knlnguyen1802
Samit
rein yang
Nick Cao
zhumingjue138
Ricardo Noriega
lyj-jjj
Cursor
gcanlin
wangyu
weizhoublue
weizhoublue
dengyunyang
汪志鹏
WeiQing Chen
Jie Liu
Yueqian Lin
NATURE
bjf-frz
amy-why-3459
Y. Fisher
Zeyu Huang | 黃澤宇
Copilot Autofix powered by AI authored and
GitHub committed 2026-05-16 18:18:43 +08:00
1 parent
5e1986206f
commit
eb4e60ee64
46 files changed
+589
-258
No files matched your search
@@ -1,5 +1,6 @@
|
||||
env:
|
||||
VLLM_WORKER_MULTIPROC_METHOD: spawn
|
||||
VLLM_USE_FLASHINFER_MOE_FP16: 0
|
||||
HF_HUB_DOWNLOAD_TIMEOUT: 300
|
||||
HF_HUB_ETAG_TIMEOUT: 60
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
env:
|
||||
VLLM_WORKER_MULTIPROC_METHOD: spawn
|
||||
VLLM_USE_FLASHINFER_MOE_FP16: 0
|
||||
HF_HUB_DOWNLOAD_TIMEOUT: 300
|
||||
HF_HUB_ETAG_TIMEOUT: 60
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
env:
|
||||
VLLM_WORKER_MULTIPROC_METHOD: spawn
|
||||
VLLM_USE_FLASHINFER_MOE_FP16: 0
|
||||
HF_HUB_DOWNLOAD_TIMEOUT: 300
|
||||
HF_HUB_ETAG_TIMEOUT: 60
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
env:
|
||||
VLLM_WORKER_MULTIPROC_METHOD: spawn
|
||||
VLLM_USE_FLASHINFER_MOE_FP16: 0
|
||||
HF_HUB_DOWNLOAD_TIMEOUT: 300
|
||||
HF_HUB_ETAG_TIMEOUT: 60
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ARG VLLM_BASE_IMAGE=vllm/vllm-openai
|
||||
ARG VLLM_BASE_TAG=v0.20.0
|
||||
ARG VLLM_BASE_TAG=v0.21.0
|
||||
FROM ${VLLM_BASE_IMAGE}:${VLLM_BASE_TAG}
|
||||
ARG APP_DIR=/workspace/vllm-omni
|
||||
WORKDIR ${APP_DIR}
|
||||
|
||||
@@ -34,7 +34,7 @@ vLLM-Omni is a Python library that supports the following GPU variants. The libr
|
||||
|
||||
### Pre-built wheels
|
||||
|
||||
Note: Pre-built wheels are currently available for vLLM-Omni 0.11.0rc1, 0.12.0rc1, 0.14.0rc1, 0.14.0, 0.16.0, 0.18.0, and 0.20.0. If you need a newer unreleased revision, please [build from source](https://docs.vllm.ai/projects/vllm-omni/en/latest/getting_started/installation/gpu/#build-wheel-from-source).
|
||||
Note: Pre-built wheels are currently available for vLLM-Omni 0.11.0rc1, 0.12.0rc1, 0.14.0rc1, 0.14.0, 0.16.0, 0.18.0, 0.20.0, and 0.21.0. If you need a newer unreleased revision, please [build from source](https://docs.vllm.ai/projects/vllm-omni/en/latest/getting_started/installation/gpu/#build-wheel-from-source).
|
||||
|
||||
=== "NVIDIA CUDA"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Therefore, it is recommended to install vLLM and vLLM-Omni with a **fresh new**
|
||||
|
||||
vLLM-Omni is built based on vLLM. Please install it with command below.
|
||||
```bash
|
||||
uv pip install vllm==0.20.0 --torch-backend=auto
|
||||
uv pip install vllm==0.21.0 --torch-backend=auto
|
||||
```
|
||||
|
||||
#### Installation of vLLM-Omni
|
||||
@@ -39,13 +39,13 @@ uv pip install 'vllm-omni[demo]'
|
||||
# --8<-- [start:build-wheel-from-source]
|
||||
|
||||
#### Installation of vLLM
|
||||
If you do not need to modify source code of vLLM, you can directly install the stable 0.20.0 release version of the library
|
||||
If you do not need to modify source code of vLLM, you can directly install the stable 0.21.0 release version of the library
|
||||
|
||||
```bash
|
||||
uv pip install vllm==0.20.0 --torch-backend=auto
|
||||
uv pip install vllm==0.21.0 --torch-backend=auto
|
||||
```
|
||||
|
||||
The 0.20.0 release of vLLM ships CUDA 13.0-compatible binaries by default. If you need a different CUDA variant or want to reuse an existing PyTorch installation, build vLLM from source instead.
|
||||
The 0.21.0 release of vLLM ships CUDA 13.0-compatible binaries by default. If you need a different CUDA variant or want to reuse an existing PyTorch installation, build vLLM from source instead.
|
||||
|
||||
#### Installation of vLLM-Omni
|
||||
Since vllm-omni is rapidly evolving, it's recommended to install it from source
|
||||
@@ -66,12 +66,12 @@ If you want to check, modify or debug with source code of vLLM, install the libr
|
||||
```bash
|
||||
git clone https://github.com/vllm-project/vllm.git
|
||||
cd vllm
|
||||
git checkout v0.20.0
|
||||
git checkout v0.21.0
|
||||
```
|
||||
Set up environment variables to get pre-built wheels. If there are internet problems, just download the whl file manually. And set `VLLM_PRECOMPILED_WHEEL_LOCATION` as your local absolute path of whl file.
|
||||
```bash
|
||||
#For CUDA 13.0 (the default for v0.20.0; the wheel filename has no `+cu130` suffix)
|
||||
export VLLM_PRECOMPILED_WHEEL_LOCATION=https://github.com/vllm-project/vllm/releases/download/v0.20.0/vllm-0.20.0-cp38-abi3-manylinux_2_35_x86_64.whl
|
||||
#For CUDA 13.0 (the default for v0.21.0; the wheel filename has no `+cu130` suffix)
|
||||
export VLLM_PRECOMPILED_WHEEL_LOCATION=https://github.com/vllm-project/vllm/releases/download/v0.21.0/vllm-0.21.0-cp38-abi3-manylinux_2_35_x86_64.whl
|
||||
```
|
||||
Install vllm with command below (If you have no existing PyTorch).
|
||||
```bash
|
||||
@@ -102,7 +102,7 @@ docker run --runtime nvidia --gpus 2 \
|
||||
--env "HF_TOKEN=$HF_TOKEN" \
|
||||
-p 8091:8091 \
|
||||
--ipc=host \
|
||||
vllm/vllm-omni:v0.20.0 \
|
||||
vllm/vllm-omni:v0.21.0 \
|
||||
--model Qwen/Qwen3-Omni-30B-A3B-Instruct --port 8091
|
||||
```
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ vLLM-Omni current recommends the steps in under setup through Docker Images.
|
||||
|
||||
vLLM-Omni is built based on vLLM. Please install it with command below.
|
||||
```bash
|
||||
uv pip install vllm==0.20.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.20.0/rocm721
|
||||
uv pip install vllm==0.21.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.21.0/rocm721
|
||||
```
|
||||
|
||||
#### Installation of vLLM-Omni
|
||||
@@ -34,13 +34,13 @@ uv pip install onnxruntime-rocm
|
||||
# --8<-- [start:build-wheel-from-source]
|
||||
|
||||
#### Installation of vLLM
|
||||
If you do not need to modify source code of vLLM, you can directly install the stable 0.20.0 release version of the library
|
||||
If you do not need to modify source code of vLLM, you can directly install the stable 0.21.0 release version of the library
|
||||
|
||||
```bash
|
||||
uv pip install vllm==0.20.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.20.0/rocm721
|
||||
uv pip install vllm==0.21.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.21.0/rocm721
|
||||
```
|
||||
|
||||
The pre-built 0.20.0 vLLM wheel targets ROCm 7.2.1. If you need a different ROCm stack or want to reuse an existing PyTorch installation, build vLLM from source instead.
|
||||
The pre-built 0.21.0 vLLM wheel targets ROCm 7.2.1. If you need a different ROCm stack or want to reuse an existing PyTorch installation, build vLLM from source instead.
|
||||
|
||||
#### Installation of vLLM-Omni
|
||||
Since vllm-omni is rapidly evolving, it's recommended to install it from source
|
||||
@@ -58,7 +58,7 @@ If you want to check, modify or debug with source code of vLLM, install the libr
|
||||
```bash
|
||||
git clone https://github.com/vllm-project/vllm.git
|
||||
cd vllm
|
||||
git checkout v0.20.0
|
||||
git checkout v0.21.0
|
||||
python3 -m pip install -r requirements/rocm.txt
|
||||
python3 setup.py develop
|
||||
```
|
||||
@@ -130,7 +130,7 @@ docker run --rm \
|
||||
-v ~/.cache/huggingface:/root/.cache/huggingface \
|
||||
--env "HF_TOKEN=$HF_TOKEN" \
|
||||
-p 8091:8091 \
|
||||
vllm/vllm-omni-rocm:v0.20.0 \
|
||||
vllm/vllm-omni-rocm:v0.21.0 \
|
||||
--model Qwen/Qwen3-Omni-30B-A3B-Instruct --omni --port 8091
|
||||
```
|
||||
|
||||
@@ -149,7 +149,7 @@ docker run --rm -it \
|
||||
-v ~/.cache/huggingface:/root/.cache/huggingface \
|
||||
--env "HF_TOKEN=$HF_TOKEN" \
|
||||
--entrypoint bash \
|
||||
vllm/vllm-omni-rocm:v0.20.0
|
||||
vllm/vllm-omni-rocm:v0.21.0
|
||||
```
|
||||
|
||||
# --8<-- [end:pre-built-images]
|
||||
@@ -19,10 +19,10 @@ uv venv --python 3.12 --seed
|
||||
source .venv/bin/activate
|
||||
|
||||
# On CUDA
|
||||
uv pip install vllm==0.20.0 --torch-backend=auto
|
||||
uv pip install vllm==0.21.0 --torch-backend=auto
|
||||
|
||||
# On ROCm
|
||||
uv pip install vllm==0.20.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.20.0/rocm721
|
||||
uv pip install vllm==0.21.0+rocm721 --extra-index-url https://wheels.vllm.ai/rocm/0.21.0/rocm721
|
||||
|
||||
git clone https://github.com/vllm-project/vllm-omni.git
|
||||
cd vllm-omni
|
||||
@@ -35,7 +35,7 @@ For additional installation methods — please see the [installation guide](inst
|
||||
!!! note
|
||||
It is important to install the same major & minor version of vLLM and vLLM Omni, otherwise things may not work as expected. If the versions are misaligned, you will see a warning when you import vLLM Omni.
|
||||
|
||||
If you are seeing strange behavior with the `vllm` command not handling the `--omni` flag correctly, you most likely have a version mismatch with vLLM < `0.20.0` and vLLM Omni `0.20.0`, as vLLM Omni no longer hijacks the vLLM entrypoint. Updating vLLM should resolve this issue.
|
||||
If you are seeing strange behavior with the `vllm` command not handling the `--omni` flag correctly, you most likely have a version mismatch with vLLM < `0.21.0` and vLLM Omni `0.21.0`, as vLLM Omni no longer hijacks the vLLM entrypoint. Updating vLLM should resolve this issue.
|
||||
|
||||
## Offline Inference
|
||||
|
||||
|
||||
@@ -394,6 +394,9 @@ class TestAttentionInitUsesCurrentDiffusionConfig:
|
||||
per_role={"cross": AttentionSpec(backend="TORCH_SDPA", extra={"block_size": 128})},
|
||||
),
|
||||
parallel_config=SimpleNamespace(ring_degree=2),
|
||||
diffusion_kv_cache_dtype=None,
|
||||
diffusion_kv_cache_skip_step_indices=None,
|
||||
diffusion_kv_cache_skip_layer_indices=None,
|
||||
)
|
||||
|
||||
with set_current_diffusion_config(od_config):
|
||||
|
||||
@@ -5,6 +5,7 @@ import pytest
|
||||
import torch
|
||||
from vllm.distributed.parallel_state import cleanup_dist_env_and_memory
|
||||
|
||||
from tests.helpers import skip_if_gated_repo_inaccessible
|
||||
from tests.helpers.env import DeviceMemoryMonitor
|
||||
from tests.helpers.mark import hardware_test
|
||||
from tests.helpers.runtime import OmniRunner
|
||||
@@ -21,6 +22,11 @@ IMAGE_VIDEO_MODELS = {
|
||||
|
||||
MODELS = {**AUDIO_MODEL, **IMAGE_VIDEO_MODELS}
|
||||
|
||||
_GATED_MODELS = {"stabilityai/stable-audio-open-1.0"}
|
||||
|
||||
# Aliased for backward compatibility (imported by test_diffusion_layerwise_offload.py).
|
||||
_skip_if_gated_repo_inaccessible = skip_if_gated_repo_inaccessible
|
||||
|
||||
AUDIO_MODEL_PARAMS = {
|
||||
"runner_params": {},
|
||||
"sampler_params": {},
|
||||
@@ -94,10 +100,23 @@ def check_audio_determinism(audio1, audio2, atol=1e-2):
|
||||
@hardware_test(res={"cuda": "L4", "rocm": "MI325"})
|
||||
@pytest.mark.parametrize("model_name", list(MODELS.keys()))
|
||||
def test_cpu_offload_diffusion_model(model_name: str):
|
||||
if model_name in _GATED_MODELS:
|
||||
_skip_if_gated_repo_inaccessible(model_name)
|
||||
try:
|
||||
offload_peak_memory, output_offload = inference(model_name, offload=True)
|
||||
cleanup_dist_env_and_memory()
|
||||
no_offload_peak_memory, output_no_offload = inference(model_name, offload=False)
|
||||
except ValueError as exc:
|
||||
# omni_snapshot_download wraps GatedRepoError in a ValueError.
|
||||
# If the pre-flight guard above did not catch it (e.g. partial
|
||||
# HF_TOKEN where config.json is accessible but weight shards are
|
||||
# blocked), skip instead of failing.
|
||||
if "Access to model" in str(exc) and "is restricted" in str(exc):
|
||||
pytest.skip(
|
||||
f"Skipping: gated HF repo {model_name!r} inaccessible "
|
||||
f"({exc}). See docs/contributing/ci/hf_credentials.md."
|
||||
)
|
||||
pytest.fail(f"Inference failed: {exc}")
|
||||
except Exception:
|
||||
pytest.fail("Inference failed")
|
||||
print(f"Offload peak memory: {offload_peak_memory} MB")
|
||||
|
||||
@@ -3,6 +3,10 @@ import pytest
|
||||
import torch
|
||||
from vllm.distributed.parallel_state import cleanup_dist_env_and_memory
|
||||
|
||||
from tests.diffusion.offloader.test_diffusion_cpu_offload import (
|
||||
_GATED_MODELS,
|
||||
_skip_if_gated_repo_inaccessible,
|
||||
)
|
||||
from tests.helpers.env import DeviceMemoryMonitor
|
||||
from tests.helpers.runtime import OmniRunner
|
||||
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
|
||||
@@ -96,6 +100,8 @@ def test_layerwise_offload_diffusion_model(model_name: str):
|
||||
offloader keeps only a single transformer block on GPU at a time, with
|
||||
prefetching for compute-memory overlap.
|
||||
"""
|
||||
if model_name in _GATED_MODELS:
|
||||
_skip_if_gated_repo_inaccessible(model_name)
|
||||
try:
|
||||
# Run without layerwise offloading (baseline)
|
||||
no_offload_peak_memory, output_no_offload = run_inference(model_name, layerwise_offload=False)
|
||||
@@ -104,6 +110,17 @@ def test_layerwise_offload_diffusion_model(model_name: str):
|
||||
# Run with layerwise offloading (1 layer on device)
|
||||
layerwise_offload_peak_memory, output_offload = run_inference(model_name, layerwise_offload=True)
|
||||
cleanup_dist_env_and_memory()
|
||||
except ValueError as exc:
|
||||
# omni_snapshot_download wraps GatedRepoError in a ValueError.
|
||||
# If the pre-flight guard above did not catch it (e.g. partial
|
||||
# HF_TOKEN where config.json is accessible but weight shards are
|
||||
# blocked), skip instead of failing.
|
||||
if "Access to model" in str(exc) and "is restricted" in str(exc):
|
||||
pytest.skip(
|
||||
f"Skipping: gated HF repo {model_name!r} inaccessible "
|
||||
f"({exc}). See docs/contributing/ci/hf_credentials.md."
|
||||
)
|
||||
pytest.fail(f"Inference failed: {exc}")
|
||||
except Exception:
|
||||
pytest.fail("Inference failed")
|
||||
|
||||
|
||||
@@ -540,6 +540,7 @@ def test_generation_scheduler_calls_cleanup_on_finished(monkeypatch, mocker: Moc
|
||||
kv_connector_output=None,
|
||||
cudagraph_stats=None,
|
||||
req_id_to_index={"req-s1": 0},
|
||||
routed_experts_dict=None,
|
||||
)
|
||||
|
||||
OmniGenerationScheduler.update_from_output(scheduler, scheduler_output, model_runner_output)
|
||||
@@ -625,6 +626,7 @@ def test_ar_scheduler_defers_cleanup_and_queues_save_on_finished(mocker: MockerF
|
||||
cudagraph_stats=None,
|
||||
req_id_to_index={"req-ar": 0},
|
||||
kv_extracted_req_ids=None,
|
||||
routed_experts_dict=None,
|
||||
)
|
||||
|
||||
OmniARScheduler.update_from_output(scheduler, scheduler_output, model_runner_output)
|
||||
|
||||
@@ -25,7 +25,11 @@ from vllm_omni.distributed.omni_connectors.connectors.mooncake_transfer_engine_c
|
||||
)
|
||||
|
||||
# All tests in this file require Mooncake TransferEngine and an RDMA environment.
|
||||
pytestmark = [pytest.mark.parallel, pytest.mark.core_model]
|
||||
pytestmark = [
|
||||
pytest.mark.parallel,
|
||||
pytest.mark.core_model,
|
||||
pytest.mark.skipif(TransferEngine is None, reason="Mooncake TransferEngine not installed"),
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Shared helpers
|
||||
|
||||
@@ -106,18 +106,21 @@ def _build_request(
|
||||
|
||||
def _collect_audio(omni: Omni, request: dict) -> tuple[torch.Tensor, int]:
|
||||
"""Run a single request and return (waveform, sample_rate)."""
|
||||
# Omni.generate returns list[OmniRequestOutput] by default (py_generator=False).
|
||||
for omni_out in omni.generate(request, DEFAULT_SAMPLING):
|
||||
mm = omni_out.multimodal_output
|
||||
assert mm is not None, "Expected multimodal_output to be non-None"
|
||||
audio = mm.get("audio")
|
||||
sr = mm.get("sr")
|
||||
assert audio is not None, "Expected 'audio' key in multimodal_output"
|
||||
assert isinstance(audio, torch.Tensor), f"audio should be Tensor, got {type(audio)}"
|
||||
return audio.cpu(), int(sr.item()) if sr is not None else SAMPLE_RATE
|
||||
for stage_outputs in omni.generate(request, DEFAULT_SAMPLING):
|
||||
req_output = stage_outputs.request_output
|
||||
if req_output is not None:
|
||||
mm = req_output.outputs[0].multimodal_output
|
||||
assert mm is not None, "Expected multimodal_output to be non-None"
|
||||
audio = mm.get("audio")
|
||||
sr = mm.get("sr")
|
||||
assert audio is not None, "Expected 'audio' key in multimodal_output"
|
||||
assert isinstance(audio, torch.Tensor), f"audio should be Tensor, got {type(audio)}"
|
||||
return audio.cpu(), int(sr.item()) if sr is not None else SAMPLE_RATE
|
||||
raise AssertionError("No stage outputs received")
|
||||
|
||||
|
||||
@pytest.mark.advanced_model
|
||||
@pytest.mark.omni
|
||||
@hardware_test(res={"cuda": "L4"})
|
||||
def test_moss_tts_nano_english(omni_runner: OmniRunner, ref_audio_path) -> None:
|
||||
"""English TTS produces non-empty 48 kHz stereo audio."""
|
||||
@@ -129,6 +132,8 @@ def test_moss_tts_nano_english(omni_runner: OmniRunner, ref_audio_path) -> None:
|
||||
assert not torch.all(audio == 0), "Audio should not be all-zeros (silence)"
|
||||
|
||||
|
||||
@pytest.mark.advanced_model
|
||||
@pytest.mark.omni
|
||||
@hardware_test(res={"cuda": "L4"})
|
||||
def test_moss_tts_nano_chinese(omni_runner: OmniRunner, ref_audio_path) -> None:
|
||||
"""Chinese TTS produces non-empty audio."""
|
||||
@@ -140,6 +145,8 @@ def test_moss_tts_nano_chinese(omni_runner: OmniRunner, ref_audio_path) -> None:
|
||||
assert not torch.all(audio == 0)
|
||||
|
||||
|
||||
@pytest.mark.advanced_model
|
||||
@pytest.mark.omni
|
||||
@hardware_test(res={"cuda": "L4"})
|
||||
def test_moss_tts_nano_deterministic(omni_runner: OmniRunner, ref_audio_path) -> None:
|
||||
"""Same seed produces identical waveforms."""
|
||||
@@ -151,6 +158,8 @@ def test_moss_tts_nano_deterministic(omni_runner: OmniRunner, ref_audio_path) ->
|
||||
assert torch.allclose(audio1, audio2, atol=1e-4), "Waveforms should match with same seed"
|
||||
|
||||
|
||||
@pytest.mark.advanced_model
|
||||
@pytest.mark.omni
|
||||
@hardware_test(res={"cuda": "L4"})
|
||||
def test_moss_tts_nano_batch(omni_runner: OmniRunner, ref_audio_path) -> None:
|
||||
"""Batch of two requests returns audio for each."""
|
||||
@@ -159,11 +168,13 @@ def test_moss_tts_nano_batch(omni_runner: OmniRunner, ref_audio_path) -> None:
|
||||
_build_request("Second request.", ref_audio_path),
|
||||
]
|
||||
results = []
|
||||
# Single-stage pipeline: one SamplingParams object is broadcast to all prompts.
|
||||
for omni_out in omni_runner.omni.generate(requests, DEFAULT_SAMPLING):
|
||||
mm = omni_out.multimodal_output
|
||||
assert mm is not None
|
||||
results.append(mm["audio"].cpu())
|
||||
# Single-stage model (num_stages=1): one sampling param for all requests.
|
||||
for stage_outputs in omni_runner.omni.generate(requests, [DEFAULT_SAMPLING]):
|
||||
req_output = stage_outputs.request_output
|
||||
if req_output is not None:
|
||||
mm = req_output.outputs[0].multimodal_output
|
||||
assert mm is not None
|
||||
results.append(mm["audio"].cpu())
|
||||
|
||||
assert len(results) == 2, f"Expected 2 outputs, got {len(results)}"
|
||||
for i, audio in enumerate(results):
|
||||
|
||||
@@ -15,6 +15,7 @@ import numpy as np
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from tests.helpers import skip_if_gated_repo_inaccessible
|
||||
from tests.helpers.assertions import assert_audio_valid
|
||||
from tests.helpers.mark import hardware_test
|
||||
from vllm_omni import Omni
|
||||
@@ -24,6 +25,8 @@ from vllm_omni.platforms import current_omni_platform
|
||||
|
||||
pytestmark = [pytest.mark.full_model, pytest.mark.diffusion]
|
||||
|
||||
_MODEL_REPO = "stabilityai/stable-audio-open-1.0"
|
||||
|
||||
_SAMPLE_RATE = 44100
|
||||
_CLIP_DURATION_S = 2.0
|
||||
|
||||
@@ -77,6 +80,7 @@ def test_stable_audio_quantization_and_teacache() -> None:
|
||||
|
||||
CI should provide ``HF_TOKEN`` if the checkpoint is gated.
|
||||
"""
|
||||
skip_if_gated_repo_inaccessible(_MODEL_REPO)
|
||||
# ``model_class_name`` must be passed explicitly: the default-stage-cfg
|
||||
# factory in ``async_omni_engine.py`` reads it out of ``kwargs`` when
|
||||
# deciding ``final_output_type`` (#2077), and at construction time the
|
||||
|
||||
@@ -79,7 +79,7 @@ class FakeStageClient:
|
||||
self._diffusion_outputs = queue.Queue()
|
||||
|
||||
# Orchestrator-facing interface.
|
||||
async def add_request_async(self, *args, **_kwargs) -> None:
|
||||
async def add_request_async(self, *args, **kwargs) -> None:
|
||||
self.add_request_calls.append(args)
|
||||
|
||||
async def get_output_async(self):
|
||||
@@ -448,7 +448,7 @@ def test_stage_engine_core_client_shutdown_cleans_children_if_base_shutdown_fail
|
||||
assert recursive
|
||||
return [fake_child]
|
||||
|
||||
def fail_base_shutdown(self):
|
||||
def fail_base_shutdown(self, **kwargs):
|
||||
raise RuntimeError("base shutdown failed")
|
||||
|
||||
monkeypatch.setattr(psutil, "Process", FakePsutilProcess)
|
||||
@@ -480,7 +480,7 @@ def test_stage_engine_core_client_shutdown_kills_stubborn_children(monkeypatch):
|
||||
|
||||
monkeypatch.setattr(psutil, "Process", FakePsutilProcess)
|
||||
monkeypatch.setattr(psutil, "wait_procs", lambda procs, timeout: ([], list(procs)))
|
||||
monkeypatch.setattr(AsyncMPClient, "shutdown", lambda self: None)
|
||||
monkeypatch.setattr(AsyncMPClient, "shutdown", lambda self, **kwargs: None)
|
||||
|
||||
client = object.__new__(StageEngineCoreClient)
|
||||
client._proc = fake_proc
|
||||
@@ -994,7 +994,7 @@ async def test_handle_streaming_update_passes_prompt_text_to_stage_pool() -> Non
|
||||
@pytest.mark.asyncio
|
||||
async def test_stage_pool_submit_initial_rolls_back_output_processor_when_client_submit_fails() -> None:
|
||||
class FailingStageClient(FakeStageClient):
|
||||
async def add_request_async(self, *args, **_kwargs) -> None:
|
||||
async def add_request_async(self, *args, **kwargs) -> None:
|
||||
raise RuntimeError("submit failed")
|
||||
|
||||
class TrackingOutputProcessor(FakeOutputProcessor):
|
||||
|
||||
@@ -389,7 +389,7 @@ class TestErrorHandling:
|
||||
|
||||
async def empty_gen(*args, **kwargs):
|
||||
return
|
||||
yield # noqa: unreachable – makes this an async generator
|
||||
yield # unreachable – makes this an async generator
|
||||
|
||||
engine.generate = MagicMock(side_effect=empty_gen)
|
||||
server = _make_server(engine_client=engine)
|
||||
@@ -448,7 +448,7 @@ class TestErrorHandling:
|
||||
|
||||
async def gen_value_error(*args, **kwargs):
|
||||
raise ValueError("bad value")
|
||||
yield # noqa: unreachable
|
||||
yield # unreachable
|
||||
|
||||
engine.generate = MagicMock(side_effect=gen_value_error)
|
||||
server = _make_server(engine_client=engine)
|
||||
@@ -463,7 +463,7 @@ class TestErrorHandling:
|
||||
|
||||
async def gen_runtime_error(*args, **kwargs):
|
||||
raise RuntimeError("something went wrong")
|
||||
yield # noqa: unreachable
|
||||
yield # unreachable
|
||||
|
||||
engine.generate = MagicMock(side_effect=gen_runtime_error)
|
||||
server = _make_server(engine_client=engine)
|
||||
|
||||
@@ -22,6 +22,7 @@ def serving_chat():
|
||||
|
||||
instance = object.__new__(OmniOpenAIServingChat)
|
||||
instance._supported_speakers = None
|
||||
instance.has_kv_connector = False
|
||||
return instance
|
||||
|
||||
|
||||
|
||||
@@ -6,3 +6,32 @@ refactor only inside the old monolithic ``conftest``; a greedy ``__init__``
|
||||
changes import order and can affect in-process Omni (``OmniRunner`` / offline
|
||||
e2e) vs subprocess-based ``OmniServer`` tests.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def skip_if_gated_repo_inaccessible(repo_id: str) -> None:
|
||||
"""Skip the test if a gated HuggingFace repo is not accessible.
|
||||
|
||||
Tries to download the model's config.json via ``hf_hub_download``,
|
||||
which performs an actual file-access check (unlike ``HfApi().model_info()``
|
||||
that only checks metadata). If the token has metadata access but not
|
||||
file-download access, ``hf_hub_download`` will raise ``GatedRepoError``
|
||||
and we skip cleanly.
|
||||
"""
|
||||
try:
|
||||
from huggingface_hub import hf_hub_download
|
||||
from huggingface_hub.errors import GatedRepoError, RepositoryNotFoundError
|
||||
except Exception:
|
||||
return
|
||||
try:
|
||||
hf_hub_download(repo_id=repo_id, filename="config.json")
|
||||
except GatedRepoError as exc:
|
||||
pytest.skip(
|
||||
f"Skipping: gated HF repo {repo_id!r} inaccessible to the current "
|
||||
f"HF_TOKEN ({exc}). See docs/contributing/ci/hf_credentials.md."
|
||||
)
|
||||
except RepositoryNotFoundError as exc:
|
||||
pytest.skip(f"Skipping: HF repo {repo_id!r} not found ({exc}).")
|
||||
except Exception:
|
||||
return
|
||||
@@ -48,6 +48,7 @@ def _build_mock_modules(mocker: MockerFixture) -> dict[str, object]:
|
||||
"""Build the dict of modules to inject into sys.modules."""
|
||||
platforms_mock = mocker.MagicMock()
|
||||
platforms_mock.current_omni_platform.supports_torch_inductor.return_value = False
|
||||
platforms_mock.current_omni_platform.is_npu.return_value = False
|
||||
|
||||
logger_mock = mocker.MagicMock()
|
||||
logger_mock.init_logger = lambda name: mocker.MagicMock()
|
||||
@@ -211,6 +212,10 @@ class TestCodePredictorDtypeAlignment:
|
||||
predictor._model_dtype = torch.float16
|
||||
predictor._compiled_model_fwd = predictor.model.forward
|
||||
|
||||
# Ensure NPU path is not taken on non-NPU hardware
|
||||
common_mod = sys.modules["vllm_omni.model_executor.models.common.qwen3_code_predictor"]
|
||||
mocker.patch.object(common_mod.current_omni_platform, "is_npu", return_value=False)
|
||||
|
||||
# _warmup_buckets should fix the dtype mismatch
|
||||
predictor._warmup_buckets()
|
||||
|
||||
@@ -230,6 +235,9 @@ class TestCodePredictorDtypeAlignment:
|
||||
predictor = predictor.to(torch.float16)
|
||||
|
||||
assert predictor._model_dtype is None
|
||||
# Ensure NPU path is not taken on non-NPU hardware
|
||||
common_mod = sys.modules["vllm_omni.model_executor.models.common.qwen3_code_predictor"]
|
||||
mocker.patch.object(common_mod.current_omni_platform, "is_npu", return_value=False)
|
||||
predictor._setup_compile()
|
||||
assert predictor._model_dtype == torch.float16
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ def _make_runner(multimodal_outputs):
|
||||
runner.device = torch.device("cpu")
|
||||
runner.supports_mm_inputs = False
|
||||
runner.speculative_config = None
|
||||
runner.routed_experts_initialized = False
|
||||
return runner
|
||||
|
||||
|
||||
|
||||
@@ -203,6 +203,14 @@ class OmniARScheduler(OmniSchedulerMixin, VLLMScheduler):
|
||||
return False
|
||||
|
||||
def schedule(self) -> SchedulerOutput: # type: ignore[override]
|
||||
# Remove FINISHED_ABORTED requests before the upstream scheduler sees
|
||||
# them. Upstream vllm raises RuntimeError on this status; omni allows
|
||||
# async abort (e.g. client disconnect during TTS streaming) to leave
|
||||
# requests in the waiting/running queues temporarily.
|
||||
for queue in (self.waiting, self.running):
|
||||
for req in list(queue):
|
||||
if getattr(req, "status", None) == RequestStatus.FINISHED_ABORTED:
|
||||
queue.remove(req)
|
||||
connector_output = self._latest_omni_connector_output
|
||||
self._latest_omni_connector_output = None
|
||||
if self.input_coordinator:
|
||||
@@ -250,6 +258,7 @@ class OmniARScheduler(OmniSchedulerMixin, VLLMScheduler):
|
||||
lora_request=nr.lora_request,
|
||||
# Enrich with omni payloads from the live request object
|
||||
prompt_embeds=(getattr(request, "prompt_embeds", None) if request else None),
|
||||
prompt_is_token_ids=nr.prompt_is_token_ids,
|
||||
additional_information=(getattr(request, "additional_information", None) if request else None),
|
||||
)
|
||||
new_list.append(omni_nr)
|
||||
@@ -419,7 +428,11 @@ class OmniARScheduler(OmniSchedulerMixin, VLLMScheduler):
|
||||
stopped = True
|
||||
|
||||
if stopped:
|
||||
routed_experts = self._get_routed_experts(request)
|
||||
if (
|
||||
model_runner_output.routed_experts_dict is not None
|
||||
and req_id in model_runner_output.routed_experts_dict
|
||||
):
|
||||
routed_experts = model_runner_output.routed_experts_dict[req_id]
|
||||
|
||||
# Capture finish_reason BEFORE _handle_stopped_request, which may
|
||||
# reset the status to WAITING for streaming requests that continue.
|
||||
|
||||
@@ -337,6 +337,7 @@ class OmniGenerationScheduler(OmniSchedulerMixin, VLLMScheduler):
|
||||
lora_request=nr.lora_request,
|
||||
# Enrich with omni payloads from the live request object
|
||||
prompt_embeds=(getattr(request, "prompt_embeds", None) if request else None),
|
||||
prompt_is_token_ids=nr.prompt_is_token_ids,
|
||||
additional_information=(getattr(request, "additional_information", None) if request else None),
|
||||
)
|
||||
new_list.append(omni_nr)
|
||||
@@ -520,7 +521,11 @@ class OmniGenerationScheduler(OmniSchedulerMixin, VLLMScheduler):
|
||||
stopped = True
|
||||
|
||||
if stopped:
|
||||
routed_experts = self._get_routed_experts(request)
|
||||
if (
|
||||
model_runner_output.routed_experts_dict is not None
|
||||
and req_id in model_runner_output.routed_experts_dict
|
||||
):
|
||||
routed_experts = model_runner_output.routed_experts_dict[req_id]
|
||||
finish_reason = request.get_finished_reason()
|
||||
finished = self._handle_stopped_request(request)
|
||||
if finished:
|
||||
|
||||
@@ -54,6 +54,7 @@ class OmniNewRequestData(NewRequestData):
|
||||
num_computed_tokens=request.num_computed_tokens,
|
||||
lora_request=request.lora_request,
|
||||
prompt_embeds=getattr(request, "prompt_embeds", None),
|
||||
prompt_is_token_ids=getattr(request, "prompt_is_token_ids", None),
|
||||
prefill_token_ids=prefill_token_ids,
|
||||
additional_information=getattr(request, "additional_information", None),
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ connectors:
|
||||
stages:
|
||||
- stage_id: 0
|
||||
max_num_seqs: 1
|
||||
gpu_memory_utilization: 0.3
|
||||
gpu_memory_utilization: 0.5
|
||||
enforce_eager: true
|
||||
trust_remote_code: true
|
||||
enable_prefix_caching: false
|
||||
@@ -42,7 +42,7 @@ stages:
|
||||
|
||||
- stage_id: 1
|
||||
max_num_seqs: 1
|
||||
gpu_memory_utilization: 0.2
|
||||
gpu_memory_utilization: 0.3
|
||||
enforce_eager: true
|
||||
trust_remote_code: true
|
||||
enable_prefix_caching: false
|
||||
|
||||
@@ -391,6 +391,8 @@ class StageDiffusionClient(StageClientBase):
|
||||
return self._output_queue.get_nowait()
|
||||
except asyncio.QueueEmpty:
|
||||
if self._engine_dead:
|
||||
if self._shutting_down:
|
||||
return None
|
||||
raise EngineDeadError()
|
||||
if not self._shutting_down and self._owns_process and self._proc is not None and not self._proc.is_alive():
|
||||
self._engine_dead = True
|
||||
|
||||
@@ -97,7 +97,7 @@ def _make_diffusion_vllm_model_config(od_config: OmniDiffusionConfig) -> _Diffus
|
||||
|
||||
@contextmanager
|
||||
def _force_cutlass_fp8_linear_kernel(quant_config: object | None) -> Iterator[None]:
|
||||
from vllm.model_executor.layers.quantization import modelopt as vllm_modelopt
|
||||
import vllm.model_executor.layers.quantization.modelopt as vllm_modelopt
|
||||
|
||||
linear_method_cls = getattr(quant_config, "LinearMethodCls", None)
|
||||
if linear_method_cls in {
|
||||
|
||||
@@ -94,6 +94,7 @@ class OmniEngineCoreRequest(EngineCoreRequest):
|
||||
return cls(
|
||||
request_id=request.request_id,
|
||||
prompt_token_ids=request.prompt_token_ids,
|
||||
prompt_is_token_ids=request.prompt_is_token_ids,
|
||||
mm_features=request.mm_features,
|
||||
sampling_params=request.sampling_params,
|
||||
pooling_params=request.pooling_params,
|
||||
@@ -109,6 +110,8 @@ class OmniEngineCoreRequest(EngineCoreRequest):
|
||||
resumable=request.resumable,
|
||||
external_req_id=request.external_req_id,
|
||||
reasoning_ended=request.reasoning_ended,
|
||||
reasoning_parser_kwargs=request.reasoning_parser_kwargs,
|
||||
abort_immediately=request.abort_immediately,
|
||||
additional_information=additional_information,
|
||||
)
|
||||
|
||||
|
||||
+105
-137
@@ -32,7 +32,6 @@ from vllm_omni.engine.messages import (
|
||||
EngineQueueMessage,
|
||||
ErrorMessage,
|
||||
OutputMessage,
|
||||
ShutdownRequestMessage,
|
||||
StageMetricsMessage,
|
||||
StageSubmissionMessage,
|
||||
)
|
||||
@@ -128,8 +127,8 @@ class Orchestrator:
|
||||
def __init__(
|
||||
self,
|
||||
request_async_queue: janus.AsyncQueue[EngineQueueMessage],
|
||||
output_async_queue: janus.AsyncQueue[EngineQueueMessage],
|
||||
rpc_async_queue: janus.AsyncQueue[EngineQueueMessage],
|
||||
output_async_queue: janus.AsyncQueue[dict[str, Any]],
|
||||
rpc_async_queue: janus.AsyncQueue[dict[str, Any]],
|
||||
stage_pools: list[StagePool],
|
||||
*,
|
||||
async_chunk: bool = False,
|
||||
@@ -174,6 +173,13 @@ class Orchestrator:
|
||||
await asyncio.gather(request_task, output_task)
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except EngineDeadError as e:
|
||||
# EngineDeadError from _orchestration_loop means the diffusion
|
||||
# engine died. All pending requests were already notified and
|
||||
# _shutdown_event was already set by the loop's handler.
|
||||
# During teardown this is expected; the finally block handles
|
||||
# proper cleanup. Do not re-raise.
|
||||
logger.info("[Orchestrator] Engine dead during shutdown: %s", e)
|
||||
except Exception:
|
||||
logger.exception("[Orchestrator] Fatal error in orchestrator tasks")
|
||||
raise
|
||||
@@ -208,27 +214,32 @@ class Orchestrator:
|
||||
"""Read messages from the main thread via request_async_queue."""
|
||||
while True:
|
||||
msg = await self.request_async_queue.get()
|
||||
msg_type = msg.type
|
||||
|
||||
if isinstance(msg, StageSubmissionMessage):
|
||||
if msg.type == "add_request":
|
||||
await self._handle_add_request(msg)
|
||||
elif msg.type == "streaming_update":
|
||||
await self._handle_streaming_update(msg)
|
||||
else:
|
||||
logger.warning("[Orchestrator] Unknown stage submission type: %s", msg.type)
|
||||
elif isinstance(msg, AddCompanionRequestMessage):
|
||||
if msg_type == "add_request":
|
||||
await self._handle_add_request(msg)
|
||||
elif msg_type == "streaming_update":
|
||||
await self._handle_streaming_update(msg)
|
||||
elif msg_type == "add_companion_request":
|
||||
await self._handle_add_companion(msg)
|
||||
elif isinstance(msg, AbortRequestMessage):
|
||||
elif msg_type == "abort":
|
||||
await self._handle_abort(msg)
|
||||
elif isinstance(msg, CollectiveRPCRequestMessage):
|
||||
elif msg_type == "collective_rpc":
|
||||
await self._handle_collective_rpc(msg)
|
||||
elif isinstance(msg, ShutdownRequestMessage):
|
||||
elif msg_type == "shutdown":
|
||||
logger.info("[Orchestrator] Received shutdown signal")
|
||||
self._shutdown_event.set()
|
||||
# Pre-mark stage clients as shutting down to prevent
|
||||
# proc_monitor daemon threads from flagging normal
|
||||
# process exit as EngineDeadError during teardown.
|
||||
for pool in self.stage_pools:
|
||||
for client in pool.clients:
|
||||
if hasattr(client, "_shutting_down"):
|
||||
client._shutting_down = True
|
||||
self._shutdown_stages()
|
||||
break
|
||||
else:
|
||||
logger.warning("[Orchestrator] Unknown message type: %s", type(msg).__name__)
|
||||
logger.warning("[Orchestrator] Unknown message type: %s", msg_type)
|
||||
|
||||
async def _handle_add_request(self, msg: StageSubmissionMessage) -> None:
|
||||
"""Handle an add_request message from the main thread."""
|
||||
@@ -387,8 +398,8 @@ class Orchestrator:
|
||||
rpc_id = msg.rpc_id
|
||||
method = msg.method
|
||||
timeout = msg.timeout
|
||||
args = msg.args
|
||||
kwargs = dict(msg.kwargs)
|
||||
args = tuple(msg.args)
|
||||
kwargs = dict(msg.kwargs or {})
|
||||
requested_stage_ids = msg.stage_ids
|
||||
|
||||
target_pools: list[StagePool] = []
|
||||
@@ -793,120 +804,78 @@ class Orchestrator:
|
||||
"""Forward output from the current logical stage to the next one."""
|
||||
next_logical = src_stage_id + 1
|
||||
next_pool = self.stage_pools[next_logical]
|
||||
|
||||
if next_pool.stage_type == "diffusion":
|
||||
await self._forward_to_diffusion_stage(
|
||||
req_id,
|
||||
src_stage_id,
|
||||
output,
|
||||
req_state,
|
||||
)
|
||||
else:
|
||||
await self._forward_to_llm_stage(
|
||||
req_id,
|
||||
src_stage_id,
|
||||
output,
|
||||
req_state,
|
||||
is_streaming_session=is_streaming_session,
|
||||
is_final_update=is_final_update,
|
||||
)
|
||||
|
||||
async def _forward_to_diffusion_stage(
|
||||
self,
|
||||
req_id: str,
|
||||
src_stage_id: int,
|
||||
output: Any,
|
||||
req_state: OrchestratorRequestState,
|
||||
) -> None:
|
||||
"""Forward output from an LLM stage to the next diffusion stage."""
|
||||
next_logical = src_stage_id + 1
|
||||
next_pool = self.stage_pools[next_logical]
|
||||
params = req_state.sampling_params_list[next_logical]
|
||||
source_outputs = [output]
|
||||
already_submitted = self._next_stage_already_submitted(src_stage_id, req_state)
|
||||
|
||||
diffusion_client = next_pool.stage_client
|
||||
if diffusion_client.custom_process_input_func is not None:
|
||||
_t_ar2d = _time.perf_counter()
|
||||
diffusion_prompt = diffusion_client.custom_process_input_func(
|
||||
source_outputs,
|
||||
req_state.prompt,
|
||||
getattr(diffusion_client, "requires_multimodal_data", False),
|
||||
)
|
||||
_dt_ar2d = (_time.perf_counter() - _t_ar2d) * 1000
|
||||
req_state.pipeline_timings["ar2diffusion_ms"] = _dt_ar2d
|
||||
logger.info(
|
||||
"[Orchestrator] ar2diffusion req=%s wall_time=%.3fms stage=%d->%d",
|
||||
req_id,
|
||||
_dt_ar2d,
|
||||
src_stage_id,
|
||||
next_logical,
|
||||
)
|
||||
if isinstance(diffusion_prompt, list):
|
||||
if not diffusion_prompt:
|
||||
error_output = OmniRequestOutput.from_error(
|
||||
req_id,
|
||||
f"Stage-{src_stage_id} produced no valid inputs for diffusion stage-{next_logical}",
|
||||
)
|
||||
logger.warning(
|
||||
"[Orchestrator] req=%s stage=%d produced empty diffusion inputs for stage=%d; "
|
||||
"routing terminal error output",
|
||||
req_id,
|
||||
src_stage_id,
|
||||
next_logical,
|
||||
)
|
||||
await self.output_async_queue.put(
|
||||
OutputMessage(
|
||||
request_id=req_id,
|
||||
stage_id=next_logical,
|
||||
engine_outputs=error_output,
|
||||
metrics=None,
|
||||
finished=True,
|
||||
)
|
||||
)
|
||||
await self._cleanup_request_ids(
|
||||
[req_id, *self._cfg_tracker.cleanup_parent(req_id)],
|
||||
)
|
||||
return
|
||||
if already_submitted and len(diffusion_prompt) == 1:
|
||||
diffusion_prompt = diffusion_prompt[0]
|
||||
else:
|
||||
diffusion_prompt = req_state.prompt
|
||||
|
||||
if already_submitted:
|
||||
await next_pool.submit_update(req_id, req_state, diffusion_prompt)
|
||||
else:
|
||||
await next_pool.submit_initial(
|
||||
req_id,
|
||||
req_state,
|
||||
diffusion_prompt,
|
||||
submit_kwargs={
|
||||
"kv_sender_info": self._build_kv_sender_info(
|
||||
list(getattr(diffusion_client, "engine_input_source", None) or [src_stage_id]),
|
||||
request_id=req_id,
|
||||
)
|
||||
},
|
||||
params_override=self._maybe_clone_diffusion_params_for_cfg(req_id, params),
|
||||
)
|
||||
req_state.stage_submit_ts[next_logical] = _time.time()
|
||||
|
||||
async def _forward_to_llm_stage(
|
||||
self,
|
||||
req_id: str,
|
||||
src_stage_id: int,
|
||||
output: Any,
|
||||
req_state: OrchestratorRequestState,
|
||||
*,
|
||||
is_streaming_session: bool = False,
|
||||
is_final_update: bool = False,
|
||||
) -> None:
|
||||
"""Forward output from the current stage to the next LLM stage."""
|
||||
next_logical = src_stage_id + 1
|
||||
next_pool = self.stage_pools[next_logical]
|
||||
next_client = next_pool.stage_client
|
||||
params = req_state.sampling_params_list[next_logical]
|
||||
source_outputs = [output]
|
||||
next_stage_resumable = is_streaming_session and not is_final_update
|
||||
already_submitted = self._next_stage_already_submitted(src_stage_id, req_state)
|
||||
requires_multimodal_data = getattr(next_client, "requires_multimodal_data", False)
|
||||
|
||||
if next_pool.stage_type == "diffusion":
|
||||
if next_client.custom_process_input_func is not None:
|
||||
_t_ar2d = _time.perf_counter()
|
||||
diffusion_prompt = next_client.custom_process_input_func(
|
||||
source_outputs,
|
||||
req_state.prompt,
|
||||
requires_multimodal_data,
|
||||
)
|
||||
_dt_ar2d = (_time.perf_counter() - _t_ar2d) * 1000
|
||||
req_state.pipeline_timings["ar2diffusion_ms"] = _dt_ar2d
|
||||
logger.info(
|
||||
"[Orchestrator] ar2diffusion req=%s wall_time=%.3fms stage=%d->%d",
|
||||
req_id,
|
||||
_dt_ar2d,
|
||||
src_stage_id,
|
||||
next_logical,
|
||||
)
|
||||
if isinstance(diffusion_prompt, list):
|
||||
if not diffusion_prompt:
|
||||
error_output = OmniRequestOutput.from_error(
|
||||
req_id,
|
||||
f"Stage-{src_stage_id} produced no valid inputs for diffusion stage-{next_logical}",
|
||||
)
|
||||
logger.warning(
|
||||
"[Orchestrator] req=%s stage=%d produced empty diffusion inputs for stage=%d; "
|
||||
"routing terminal error output",
|
||||
req_id,
|
||||
src_stage_id,
|
||||
next_logical,
|
||||
)
|
||||
await self.output_async_queue.put(
|
||||
OutputMessage(
|
||||
request_id=req_id,
|
||||
stage_id=next_logical,
|
||||
engine_outputs=error_output,
|
||||
metrics=None,
|
||||
finished=True,
|
||||
)
|
||||
)
|
||||
await self._cleanup_request_ids(
|
||||
[req_id, *self._cfg_tracker.cleanup_parent(req_id)],
|
||||
)
|
||||
return
|
||||
if already_submitted and len(diffusion_prompt) == 1:
|
||||
diffusion_prompt = diffusion_prompt[0]
|
||||
else:
|
||||
diffusion_prompt = req_state.prompt
|
||||
|
||||
if already_submitted:
|
||||
await next_pool.submit_update(req_id, req_state, diffusion_prompt)
|
||||
else:
|
||||
await next_pool.submit_initial(
|
||||
req_id,
|
||||
req_state,
|
||||
diffusion_prompt,
|
||||
submit_kwargs={
|
||||
"kv_sender_info": self._build_kv_sender_info(
|
||||
list(getattr(next_client, "engine_input_source", None) or [src_stage_id]),
|
||||
request_id=req_id,
|
||||
)
|
||||
},
|
||||
params_override=self._maybe_clone_diffusion_params_for_cfg(req_id, params),
|
||||
)
|
||||
req_state.stage_submit_ts[next_logical] = _time.time()
|
||||
return
|
||||
|
||||
# PD disaggregation: prefill → decode routing uses original prompt + KV transfer params
|
||||
if self._pd_pair is not None and (src_stage_id, next_logical) == self._pd_pair:
|
||||
@@ -953,7 +922,6 @@ class Orchestrator:
|
||||
{},
|
||||
)[req_id] = req_state.pd_prefill_multimodal_output
|
||||
|
||||
next_client = next_pool.llm_stage_client
|
||||
try:
|
||||
next_inputs = next_client.process_engine_inputs(
|
||||
source_outputs,
|
||||
@@ -972,7 +940,7 @@ class Orchestrator:
|
||||
for next_input in next_inputs:
|
||||
# Only AR thinker stages consume encoder mm_features; downstream
|
||||
# (talker/code2wav/…) must not see them (avoids encoder-cache misses).
|
||||
model_stage = next_client.model_stage
|
||||
model_stage = getattr(next_client, "model_stage", None)
|
||||
mm_features = req_state.mm_features if model_stage == "thinker" else None
|
||||
request = build_engine_core_request_from_tokens(
|
||||
request_id=req_id,
|
||||
@@ -1022,7 +990,7 @@ class Orchestrator:
|
||||
req_state.prompt,
|
||||
submit_kwargs={
|
||||
"kv_sender_info": self._build_kv_sender_info(
|
||||
list(next_pool.stage_client.engine_input_source or [next_stage_id - 1]),
|
||||
list(getattr(next_pool.stage_client, "engine_input_source", None) or [next_stage_id - 1]),
|
||||
request_id=request_id,
|
||||
)
|
||||
},
|
||||
@@ -1069,14 +1037,14 @@ class Orchestrator:
|
||||
continue
|
||||
|
||||
sender_pool = self.stage_pools[sender_stage_id]
|
||||
if sender_pool.stage_type == "diffusion":
|
||||
sender_stage = sender_pool.get_bound_client(request_id) if request_id is not None else None
|
||||
if sender_stage is None:
|
||||
sender_stage = sender_pool.stage_client
|
||||
get_sender_info = getattr(sender_stage, "get_kv_sender_info", None)
|
||||
if not callable(get_sender_info):
|
||||
continue
|
||||
|
||||
sender_stage = sender_pool.get_bound_llm_client(request_id) if request_id is not None else None
|
||||
if sender_stage is None:
|
||||
sender_stage = sender_pool.llm_stage_client
|
||||
|
||||
sender_info = sender_stage.get_kv_sender_info()
|
||||
sender_info = get_sender_info()
|
||||
if not sender_info:
|
||||
logger.warning(
|
||||
"[Orchestrator] Stage-%s has no KV sender info available",
|
||||
@@ -1110,7 +1078,7 @@ class Orchestrator:
|
||||
msg = self.request_async_queue.get_nowait()
|
||||
except Exception:
|
||||
break
|
||||
if isinstance(msg, StageSubmissionMessage) and msg.type == "add_request":
|
||||
if msg.type == "add_request":
|
||||
req_id = msg.request_id
|
||||
await self.output_async_queue.put(
|
||||
ErrorMessage(
|
||||
|
||||
@@ -3,6 +3,9 @@ from typing import Any
|
||||
import numpy as np
|
||||
import torch
|
||||
from vllm.logger import init_logger
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import (
|
||||
split_routed_experts,
|
||||
)
|
||||
from vllm.outputs import PoolingRequestOutput
|
||||
from vllm.sampling_params import RequestOutputKind
|
||||
from vllm.tokenizers import TokenizerLike
|
||||
@@ -230,7 +233,17 @@ class OmniRequestState(RequestState):
|
||||
self.sent_tokens_offset = self.detokenizer.num_output_tokens()
|
||||
|
||||
external_req_id = self.external_req_id
|
||||
output = self._new_completion_output(new_token_ids, finish_reason, stop_reason, routed_experts)
|
||||
|
||||
# Split routing data into prompt and generation portions, matching
|
||||
# upstream make_request_output behaviour.
|
||||
prompt_routed_experts = None
|
||||
gen_routed_experts = None
|
||||
if routed_experts is not None:
|
||||
prompt_len = len(self.prompt_token_ids) if self.prompt_token_ids else 0
|
||||
num_gen = self.detokenizer.num_output_tokens() if self.detokenizer is not None else None
|
||||
prompt_routed_experts, gen_routed_experts = split_routed_experts(routed_experts, prompt_len, num_gen)
|
||||
|
||||
output = self._new_completion_output(new_token_ids, finish_reason, stop_reason, gen_routed_experts)
|
||||
|
||||
if self.parent_req is None:
|
||||
outputs = [output]
|
||||
@@ -240,7 +253,13 @@ class OmniRequestState(RequestState):
|
||||
return None
|
||||
external_req_id = self.parent_req.external_req_id
|
||||
|
||||
return self._new_request_output(external_req_id, outputs, finished, kv_transfer_params)
|
||||
return self._new_request_output(
|
||||
external_req_id,
|
||||
outputs,
|
||||
finished,
|
||||
kv_transfer_params,
|
||||
prompt_routed_experts,
|
||||
)
|
||||
|
||||
def _new_completion_output(
|
||||
self,
|
||||
|
||||
@@ -80,7 +80,8 @@ class StageEngineCoreClientBase(StageClientBase):
|
||||
def make_async_mp_client(
|
||||
vllm_config: Any,
|
||||
executor_class: type,
|
||||
metadata: StageMetadata,
|
||||
log_stats: bool = False,
|
||||
metadata: StageMetadata | None = None,
|
||||
client_addresses: dict[str, str] | None = None,
|
||||
proc: Any = None,
|
||||
engine_manager: Any = None,
|
||||
@@ -93,6 +94,7 @@ class StageEngineCoreClientBase(StageClientBase):
|
||||
client_args = dict(
|
||||
vllm_config=vllm_config,
|
||||
executor_class=executor_class,
|
||||
log_stats=log_stats,
|
||||
metadata=metadata,
|
||||
client_addresses=client_addresses,
|
||||
proc=proc,
|
||||
@@ -441,7 +443,7 @@ class StageEngineCoreClientBase(StageClientBase):
|
||||
kwargs=kwargs,
|
||||
)
|
||||
|
||||
def shutdown(self) -> None:
|
||||
def shutdown(self, timeout: float | None = None) -> None:
|
||||
"""Shutdown managed resources and any externally spawned subprocess."""
|
||||
child_procs: list[psutil.Process] = []
|
||||
if self._proc is not None and self._proc.pid is not None:
|
||||
@@ -451,7 +453,7 @@ class StageEngineCoreClientBase(StageClientBase):
|
||||
child_procs = []
|
||||
|
||||
try:
|
||||
super().shutdown()
|
||||
super().shutdown(timeout=timeout)
|
||||
finally:
|
||||
if self._proc is not None and self._proc.is_alive():
|
||||
self._proc.terminate()
|
||||
|
||||
@@ -232,6 +232,7 @@ class AsyncOmni(EngineClient, OmniBase):
|
||||
priority: int = 0,
|
||||
data_parallel_rank: int | None = None,
|
||||
reasoning_ended: bool | None = None,
|
||||
reasoning_parser_kwargs: dict[str, Any] | None = None,
|
||||
) -> AsyncGenerator[OmniRequestOutput, None]:
|
||||
"""Generate outputs for the given prompt(s) asynchronously.
|
||||
|
||||
@@ -938,6 +939,37 @@ class AsyncOmni(EngineClient, OmniBase):
|
||||
"""Check if tracing is enabled."""
|
||||
return False
|
||||
|
||||
async def notify_kv_transfer_request_rejected(
|
||||
self,
|
||||
request_id: str,
|
||||
kv_transfer_params: dict[str, Any],
|
||||
*,
|
||||
data_parallel_rank: int | None = None,
|
||||
) -> None:
|
||||
"""Notify engine that a KV-transfer request was rejected before admission.
|
||||
|
||||
Omni does not currently use KV-transfer pre-admission resources,
|
||||
so this is a no-op.
|
||||
"""
|
||||
logger.debug(
|
||||
"KV-transfer request rejected (no-op in omni): request_id=%s",
|
||||
request_id,
|
||||
)
|
||||
|
||||
async def start_weight_update(self, is_checkpoint_format: bool = True) -> None:
|
||||
"""Start a new weight update.
|
||||
|
||||
Omni does not currently support weight transfer, so this is a no-op.
|
||||
"""
|
||||
logger.debug("Weight update start requested (no-op in omni)")
|
||||
|
||||
async def finish_weight_update(self) -> None:
|
||||
"""Finish the current weight update.
|
||||
|
||||
Omni does not currently support weight transfer, so this is a no-op.
|
||||
"""
|
||||
logger.debug("Weight update finish requested (no-op in omni)")
|
||||
|
||||
async def do_log_stats(self) -> None:
|
||||
"""Log statistics.
|
||||
|
||||
|
||||
@@ -148,6 +148,8 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
|
||||
instance._diffusion_mode = True
|
||||
instance._diffusion_engine = diffusion_engine
|
||||
instance._diffusion_model_name = model_name
|
||||
instance.engine_client = None
|
||||
instance.has_kv_connector = False
|
||||
return instance
|
||||
|
||||
def _get_diffusion_extra_body_params(self) -> frozenset[str]:
|
||||
@@ -216,6 +218,15 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
|
||||
For diffusion models, this generates images and returns them
|
||||
in a chat completion response format.
|
||||
"""
|
||||
return await self._with_kv_transfer_rejection_cleanup(
|
||||
self._create_chat_completion(request, raw_request), request, raw_request
|
||||
)
|
||||
|
||||
async def _create_chat_completion(
|
||||
self,
|
||||
request: ChatCompletionRequest,
|
||||
raw_request: Request | None = None,
|
||||
) -> AsyncGenerator[str, None] | ChatCompletionResponse | ErrorResponse:
|
||||
# Handle diffusion mode
|
||||
if self._diffusion_mode:
|
||||
return await self._create_diffusion_chat_completion(request, raw_request)
|
||||
@@ -996,6 +1007,9 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
|
||||
# when handling audio/image responses
|
||||
role = self.get_chat_request_role(request)
|
||||
|
||||
# Compute prompt_text once at first iteration (upstream #42052)
|
||||
prompt_text = getattr(res, "prompt", None) if getattr(request, "return_prompt_text", None) else None
|
||||
|
||||
# We need to do it here, because if there are exceptions in
|
||||
# the result_generator, it needs to be sent as the FIRST
|
||||
# response (by the try...catch).
|
||||
@@ -1022,6 +1036,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
|
||||
choices=[choice_data],
|
||||
model=model_name,
|
||||
prompt_token_ids=(res.prompt_token_ids if request.return_token_ids else None),
|
||||
prompt_text=prompt_text,
|
||||
modality=final_output_type,
|
||||
)
|
||||
|
||||
@@ -1740,6 +1755,13 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
|
||||
response_metrics.update(extra)
|
||||
choices.extend(choices_data)
|
||||
|
||||
# Compute prompt_text for non-streaming response (upstream #42052)
|
||||
prompt_text = (
|
||||
getattr(final_res, "prompt", None)
|
||||
if final_res is not None and getattr(request, "return_prompt_text", None)
|
||||
else None
|
||||
)
|
||||
|
||||
response = OmniChatCompletionResponse(
|
||||
id=request_id,
|
||||
created=created_time,
|
||||
@@ -1748,6 +1770,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
|
||||
usage=usage,
|
||||
prompt_logprobs=prompt_logprobs,
|
||||
prompt_token_ids=prompt_token_ids,
|
||||
prompt_text=prompt_text,
|
||||
kv_transfer_params=kv_transfer_params,
|
||||
metrics=response_metrics,
|
||||
)
|
||||
@@ -2034,13 +2057,17 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
|
||||
mm_output = final_res.outputs[0].multimodal_output
|
||||
audio_data = mm_output.get("audio")
|
||||
if isinstance(audio_data, list):
|
||||
if stream:
|
||||
if not audio_data:
|
||||
audio_tensor = None
|
||||
elif stream:
|
||||
audio_tensor = audio_data[-1]
|
||||
else:
|
||||
audio_tensor = torch.cat(audio_data, dim=-1)
|
||||
else:
|
||||
audio_tensor = audio_data
|
||||
audio_tensor = audio_tensor.float().detach().cpu().numpy()
|
||||
if audio_tensor is None:
|
||||
return self._create_error_response("Audio generation completed but no audio was produced.")
|
||||
audio_tensor = audio_tensor.detach().cpu().float().numpy()
|
||||
|
||||
# Ensure audio is 1D (flatten if needed)
|
||||
if audio_tensor.ndim > 1:
|
||||
|
||||
@@ -2103,6 +2103,13 @@ class OmniOpenAIServingSpeech(OpenAIServing, AudioMixin):
|
||||
voice_lower = request.voice.lower()
|
||||
tts_params["voice_name"] = [voice_lower]
|
||||
tts_params["voice_created_at"] = [self._voice_created_at(voice_lower)]
|
||||
# Propagate seed from sampling params for deterministic generation.
|
||||
# MOSS-TTS-Nano uses its own internal sampling inside
|
||||
# inference_stream(), which reads seed from additional_information
|
||||
# (not SamplingParams). Without this the model uses ambient RNG
|
||||
# state and produces non-deterministic output.
|
||||
if sampling_params_list and getattr(sampling_params_list[0], "seed", None) is not None:
|
||||
tts_params["seed"] = [sampling_params_list[0].seed]
|
||||
prompt = tokens_input(prompt_token_ids=[1])
|
||||
prompt["additional_information"] = tts_params
|
||||
else:
|
||||
|
||||
@@ -351,8 +351,10 @@ class CodePredictorBaseModel(nn.Module):
|
||||
# native bf16 support (Turing, Volta). The RMSNorm and RoPE
|
||||
# layers already upcast internally; this extends the same
|
||||
# treatment to attention and MLP.
|
||||
# autocast to float32 is unsupported on CPU; skip fp32 upcast there
|
||||
# (CPU uses full-precision intermediates internally).
|
||||
input_dtype = inputs_embeds.dtype
|
||||
use_fp32 = input_dtype == torch.float16
|
||||
use_fp32 = input_dtype == torch.float16 and inputs_embeds.device.type != "cpu"
|
||||
if use_fp32:
|
||||
inputs_embeds = inputs_embeds.float()
|
||||
hidden_states = inputs_embeds
|
||||
|
||||
@@ -61,8 +61,9 @@ class SnakeBeta(nn.Module):
|
||||
x = tl.load(x_ptr + bid * stride_b + cid * stride_c + t_off, mask=mask, other=0.0)
|
||||
ea = tl.load(exp_alpha_ptr + cid)
|
||||
ib = tl.load(inv_beta_ptr + cid)
|
||||
sin_val = tl.sin(x * ea)
|
||||
result = x + ib * sin_val * sin_val
|
||||
x_float = x.to(tl.float32)
|
||||
sin_val = tl.sin(x_float * ea)
|
||||
result = x + (ib * sin_val * sin_val).to(x.dtype)
|
||||
|
||||
tl.store(out_ptr + bid * stride_b + cid * stride_c + t_off, result, mask=mask)
|
||||
|
||||
|
||||
@@ -27,17 +27,8 @@ from vllm.distributed import (
|
||||
)
|
||||
from vllm.inputs import MultiModalDataDict
|
||||
from vllm.logger import init_logger
|
||||
from vllm.model_executor.layers.fused_moe import FusedMoE as SharedFusedMoE
|
||||
from vllm.model_executor.layers.fused_moe import fused_moe_make_expert_params_mapping
|
||||
|
||||
try:
|
||||
from vllm.model_executor.layers.fused_moe.shared_fused_moe import SharedFusedMoE
|
||||
except ImportError:
|
||||
# PyPI vllm 0.20.x neither exports `SharedFusedMoE` from the package top-level
|
||||
# nor ships a `shared_fused_moe.py` submodule. The functionality lives on
|
||||
# `FusedMoE` directly (which gained a `shared_experts` parameter), so alias
|
||||
# the symbol — call sites only use the classmethod `make_expert_params_mapping`
|
||||
# and `__init__(shared_experts=..., ...)` which are present on `FusedMoE`.
|
||||
from vllm.model_executor.layers.fused_moe import FusedMoE as SharedFusedMoE
|
||||
from vllm.model_executor.layers.linear import (
|
||||
ColumnParallelLinear,
|
||||
ReplicatedLinear,
|
||||
|
||||
@@ -160,7 +160,7 @@ class MossTTSNanoForGeneration(nn.Module):
|
||||
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
|
||||
with self._lock:
|
||||
if self._lm is not None:
|
||||
return set()
|
||||
return None
|
||||
_patch_torchaudio_load()
|
||||
|
||||
try:
|
||||
@@ -233,7 +233,7 @@ class MossTTSNanoForGeneration(nn.Module):
|
||||
# iterator so the underlying stream is closed cleanly.
|
||||
for _ in weights:
|
||||
pass
|
||||
return set()
|
||||
return None
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Dummy run support
|
||||
|
||||
+4
@@ -534,6 +534,10 @@ class Qwen3TTSTokenizerV2DecoderTransformerModel(Qwen3TTSTokenizerV2DecoderPreTr
|
||||
cache_position=None,
|
||||
**kwargs,
|
||||
) -> BaseModelOutputWithPast:
|
||||
r"""
|
||||
cache_position (`torch.LongTensor` of shape `(sequence_length)`, *optional*):
|
||||
Indices depicting the position of the input sequence tokens in the sequence. Used to update the cache.
|
||||
"""
|
||||
if input_ids is not None:
|
||||
raise ValueError("input_ids is not expected")
|
||||
if (input_ids is None) ^ (inputs_embeds is not None):
|
||||
|
||||
@@ -16,7 +16,11 @@ from vllm.distributed.kv_transfer import get_kv_transfer_group, has_kv_transfer_
|
||||
from vllm.distributed.parallel_state import get_pp_group, get_tp_group
|
||||
from vllm.forward_context import BatchDescriptor
|
||||
from vllm.logger import logger
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import RoutedExpertsCapturer
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import (
|
||||
extract_routed_experts_for_current_batch,
|
||||
get_global_experts_capturer,
|
||||
issue_routing_d2h_copy,
|
||||
)
|
||||
from vllm.sequence import IntermediateTensors
|
||||
from vllm.v1.core.sched.output import GrammarOutput, SchedulerOutput
|
||||
from vllm.v1.outputs import (
|
||||
@@ -28,8 +32,8 @@ from vllm.v1.outputs import (
|
||||
from vllm.v1.spec_decode.metadata import SpecDecodeMetadata
|
||||
from vllm.v1.structured_output.utils import apply_grammar_bitmask
|
||||
from vllm.v1.utils import record_function_or_nullcontext
|
||||
from vllm.v1.worker import mamba_utils
|
||||
from vllm.v1.worker.gpu_model_runner import AsyncGPUModelRunnerOutput, PerLayerAttnMetadata
|
||||
from vllm.v1.worker.mamba_utils import preprocess_mamba
|
||||
from vllm.v1.worker.ubatch_utils import maybe_create_ubatch_slices
|
||||
from vllm_ascend.ascend_forward_context import set_ascend_forward_context
|
||||
from vllm_ascend.attention.utils import AscendCommonAttentionMetadata
|
||||
@@ -246,11 +250,9 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
intermediate_tensors: IntermediateTensors | None = None,
|
||||
) -> OmniModelRunnerOutput | IntermediateTensors | None:
|
||||
if self.vllm_config.model_config.enable_return_routed_experts:
|
||||
capturer = RoutedExpertsCapturer.get_instance()
|
||||
if capturer is not None:
|
||||
capturer.clear_buffer()
|
||||
else:
|
||||
logger.warning("RoutedExpertsCapturer is not initialized.")
|
||||
capturer = get_global_experts_capturer()
|
||||
if capturer is not None and hasattr(capturer, "finalize_pending_copy"):
|
||||
capturer.finalize_pending_copy()
|
||||
if self.ascend_config.profiling_chunk_config.enabled:
|
||||
self._sync_device()
|
||||
self._execution_start_time = time.perf_counter()
|
||||
@@ -299,7 +301,9 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
# TODO(Ronald1995): deepcopy is expensive when there is a large
|
||||
# number of requests, optimize it later.
|
||||
if ((
|
||||
self.use_async_scheduling and self.num_spec_tokens and self._draft_token_ids is None # type: ignore[has-type]
|
||||
self.use_async_scheduling
|
||||
and self.num_spec_tokens
|
||||
and self._draft_token_ids is None # type: ignore[has-type]
|
||||
) or (
|
||||
# NOTE: This branch specifically triggers a deepcopy during the prefill phase
|
||||
# only for PCP (Parallel Context Processing) + Multi-Modal (MM) scenarios.
|
||||
@@ -455,7 +459,7 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
deferred_state_corrections_fn = None
|
||||
mamba_utils.preprocess_mamba(
|
||||
preprocess_mamba(
|
||||
scheduler_output,
|
||||
self.kv_cache_config,
|
||||
self.cache_config,
|
||||
@@ -715,6 +719,15 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
# previous model forward without breaking async scheduling.
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
|
||||
if self.vllm_config.model_config.enable_return_routed_experts and hasattr(self, "_positions_cpu"):
|
||||
issue_routing_d2h_copy(
|
||||
input_batch_req_ids=self.input_batch.req_ids,
|
||||
num_scheduled_tokens=scheduler_output.num_scheduled_tokens,
|
||||
positions=self.positions,
|
||||
positions_cpu=self._positions_cpu,
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
@torch.inference_mode()
|
||||
@@ -858,12 +871,17 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
if self.speculative_config is not None:
|
||||
self.finalize_kv_connector()
|
||||
|
||||
routed_experts_dict = None
|
||||
if self.model_config.enable_return_routed_experts:
|
||||
capturer = RoutedExpertsCapturer.get_instance()
|
||||
if capturer is not None:
|
||||
capturer.save_captured_experts(indices=self.cpu_slot_mapping)
|
||||
else:
|
||||
logger.warning("RoutedExpertsCapturer is not initialized.")
|
||||
capturer = get_global_experts_capturer()
|
||||
if capturer is not None and hasattr(self.input_batch, "num_tokens_no_spec"):
|
||||
routed_experts_dict = extract_routed_experts_for_current_batch(
|
||||
req_ids=req_ids_output_copy,
|
||||
requests=self.requests,
|
||||
req_id_to_index=self.input_batch.req_id_to_index,
|
||||
num_tokens_no_spec=self.input_batch.num_tokens_no_spec,
|
||||
max_model_len=self.max_model_len,
|
||||
)
|
||||
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
engine_output_type, downstream_req_ids = self._resolve_pooler_payload_req_ids(req_ids_output_copy)
|
||||
@@ -984,6 +1002,7 @@ class NPUARModelRunner(OmniNPUModelRunner):
|
||||
kv_connector_output=kv_connector_output,
|
||||
ec_connector_output=ec_connector_output if self.supports_mm_inputs else None,
|
||||
cudagraph_stats=cudagraph_stats,
|
||||
routed_experts_dict=routed_experts_dict,
|
||||
)
|
||||
model_runner_output.kv_extracted_req_ids = kv_extracted_req_ids
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
@@ -15,14 +15,18 @@ from vllm.distributed.ec_transfer import get_ec_transfer, has_ec_transfer
|
||||
from vllm.distributed.kv_transfer import get_kv_transfer_group, has_kv_transfer_group
|
||||
from vllm.distributed.parallel_state import get_pp_group
|
||||
from vllm.logger import logger
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import RoutedExpertsCapturer
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import (
|
||||
extract_routed_experts_for_current_batch,
|
||||
get_global_experts_capturer,
|
||||
issue_routing_d2h_copy,
|
||||
)
|
||||
from vllm.sequence import IntermediateTensors
|
||||
from vllm.utils.math_utils import cdiv
|
||||
from vllm.v1.core.sched.output import GrammarOutput, SchedulerOutput
|
||||
from vllm.v1.outputs import EMPTY_MODEL_RUNNER_OUTPUT, AsyncModelRunnerOutput, make_empty_encoder_model_runner_output
|
||||
from vllm.v1.utils import record_function_or_nullcontext
|
||||
from vllm.v1.worker import mamba_utils
|
||||
from vllm.v1.worker.gpu_model_runner import AsyncGPUModelRunnerOutput, PerLayerAttnMetadata
|
||||
from vllm.v1.worker.mamba_utils import preprocess_mamba
|
||||
from vllm.v1.worker.ubatch_utils import maybe_create_ubatch_slices
|
||||
from vllm.v1.worker.utils import sanity_check_mm_encoder_outputs
|
||||
from vllm_ascend.ascend_forward_context import set_ascend_forward_context
|
||||
@@ -74,11 +78,9 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
intermediate_tensors: IntermediateTensors | None = None,
|
||||
) -> OmniModelRunnerOutput | IntermediateTensors | None:
|
||||
if self.vllm_config.model_config.enable_return_routed_experts:
|
||||
capturer = RoutedExpertsCapturer.get_instance()
|
||||
if capturer is not None:
|
||||
capturer.clear_buffer()
|
||||
else:
|
||||
logger.warning("RoutedExpertsCapturer is not initialized.")
|
||||
capturer = get_global_experts_capturer()
|
||||
if capturer is not None and hasattr(capturer, "finalize_pending_copy"):
|
||||
capturer.finalize_pending_copy()
|
||||
if self.ascend_config.profiling_chunk_config.enabled:
|
||||
self._sync_device()
|
||||
self._execution_start_time = time.perf_counter()
|
||||
@@ -227,7 +229,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
deferred_state_corrections_fn = None
|
||||
mamba_utils.preprocess_mamba(
|
||||
preprocess_mamba(
|
||||
scheduler_output,
|
||||
self.kv_cache_config,
|
||||
self.cache_config,
|
||||
@@ -383,6 +385,15 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
# previous model forward without breaking async scheduling.
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
|
||||
if self.vllm_config.model_config.enable_return_routed_experts and hasattr(self, "_positions_cpu"):
|
||||
issue_routing_d2h_copy(
|
||||
input_batch_req_ids=self.input_batch.req_ids,
|
||||
num_scheduled_tokens=scheduler_output.num_scheduled_tokens,
|
||||
positions=self.positions,
|
||||
positions_cpu=self._positions_cpu,
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
@torch.inference_mode()
|
||||
@@ -468,6 +479,17 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
# [Omni] Copy req_id mappings to avoid async scheduling mutation.
|
||||
req_ids_output_copy = self.input_batch.req_ids.copy()
|
||||
req_id_to_index_output_copy = self.input_batch.req_id_to_index.copy()
|
||||
routed_experts_dict = None
|
||||
if self.vllm_config.model_config.enable_return_routed_experts and hasattr(
|
||||
self.input_batch, "num_tokens_no_spec"
|
||||
):
|
||||
routed_experts_dict = extract_routed_experts_for_current_batch(
|
||||
req_ids=req_ids_output_copy,
|
||||
requests=self.requests,
|
||||
req_id_to_index=self.input_batch.req_id_to_index,
|
||||
num_tokens_no_spec=self.input_batch.num_tokens_no_spec,
|
||||
max_model_len=self.max_model_len,
|
||||
)
|
||||
output = OmniModelRunnerOutput(
|
||||
req_ids=req_ids_output_copy,
|
||||
req_id_to_index=req_id_to_index_output_copy,
|
||||
@@ -478,6 +500,7 @@ class NPUGenerationModelRunner(OmniNPUModelRunner):
|
||||
kv_connector_output=kv_connector_output,
|
||||
ec_connector_output=ec_connector_output if self.supports_mm_inputs else None,
|
||||
cudagraph_stats=cudagraph_stats,
|
||||
routed_experts_dict=routed_experts_dict,
|
||||
)
|
||||
# -------------------------------------- Omni-new -------------------------------------------------
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ class OmniRequest(Request):
|
||||
client_index=request.client_index,
|
||||
prompt_token_ids=request.prompt_token_ids,
|
||||
prompt_embeds=request.prompt_embeds,
|
||||
prompt_is_token_ids=request.prompt_is_token_ids,
|
||||
mm_features=request.mm_features,
|
||||
sampling_params=request.sampling_params,
|
||||
pooling_params=request.pooling_params,
|
||||
@@ -94,6 +95,8 @@ class OmniRequest(Request):
|
||||
additional_information=request.additional_information,
|
||||
resumable=request.resumable,
|
||||
reasoning_ended=request.reasoning_ended,
|
||||
reasoning_parser_kwargs=request.reasoning_parser_kwargs,
|
||||
abort_immediately=request.abort_immediately,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ from vllm.distributed.parallel_state import get_pp_group, get_tp_group
|
||||
from vllm.forward_context import set_forward_context
|
||||
from vllm.logger import init_logger
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import (
|
||||
RoutedExpertsCapturer,
|
||||
extract_routed_experts_for_current_batch,
|
||||
get_global_experts_capturer,
|
||||
issue_routing_d2h_copy,
|
||||
)
|
||||
from vllm.v1.core.sched.output import GrammarOutput, SchedulerOutput
|
||||
from vllm.v1.outputs import AsyncModelRunnerOutput, make_empty_encoder_model_runner_output
|
||||
@@ -28,6 +30,7 @@ from vllm.v1.spec_decode.dflash import DFlashProposer
|
||||
from vllm.v1.spec_decode.draft_model import DraftModelProposer
|
||||
from vllm.v1.spec_decode.eagle import EagleProposer
|
||||
from vllm.v1.spec_decode.extract_hidden_states import ExtractHiddenStatesProposer
|
||||
from vllm.v1.spec_decode.gemma4 import Gemma4Proposer
|
||||
from vllm.v1.structured_output.utils import apply_grammar_bitmask
|
||||
from vllm.v1.utils import record_function_or_nullcontext
|
||||
from vllm.v1.worker.gpu_model_runner import (
|
||||
@@ -358,11 +361,21 @@ class GPUARModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin):
|
||||
self.flush_full_payload_outputs(flush_ids)
|
||||
|
||||
if self.routed_experts_initialized:
|
||||
capturer = RoutedExpertsCapturer.get_instance()
|
||||
if capturer is not None:
|
||||
capturer.clear_buffer() # noqa
|
||||
else:
|
||||
logger.error("RoutedExpertsCapturer not initialized.")
|
||||
capturer = get_global_experts_capturer()
|
||||
if capturer is not None and hasattr(capturer, "finalize_pending_copy"):
|
||||
capturer.finalize_pending_copy()
|
||||
|
||||
# If ngram_gpu is used, we need to copy the scheduler_output to avoid
|
||||
# the modification has influence on the scheduler_output in engine core process.
|
||||
# The replace is much faster than deepcopy.
|
||||
if self.speculative_config is not None and self.speculative_config.use_ngram_gpu():
|
||||
num_scheduled_tokens_copy = scheduler_output.num_scheduled_tokens.copy()
|
||||
spec_decode_tokens_copy = scheduler_output.scheduled_spec_decode_tokens.copy()
|
||||
scheduler_output = replace(
|
||||
scheduler_output,
|
||||
num_scheduled_tokens=num_scheduled_tokens_copy,
|
||||
scheduled_spec_decode_tokens=spec_decode_tokens_copy,
|
||||
)
|
||||
|
||||
if has_kv_transfer_group():
|
||||
kv_connector_metadata = scheduler_output.kv_connector_metadata
|
||||
@@ -678,6 +691,14 @@ class GPUARModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin):
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
|
||||
if self.routed_experts_initialized and hasattr(self, "_positions_cpu"):
|
||||
issue_routing_d2h_copy(
|
||||
input_batch_req_ids=self.input_batch.req_ids,
|
||||
num_scheduled_tokens=scheduler_output.num_scheduled_tokens,
|
||||
positions=self.positions,
|
||||
positions_cpu=self._positions_cpu,
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def _sample(
|
||||
@@ -829,7 +850,7 @@ class GPUARModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin):
|
||||
if use_gpu_toks:
|
||||
assert isinstance(
|
||||
self.drafter,
|
||||
EagleProposer | DFlashProposer | DraftModelProposer | ExtractHiddenStatesProposer,
|
||||
EagleProposer | DFlashProposer | DraftModelProposer | ExtractHiddenStatesProposer | Gemma4Proposer,
|
||||
)
|
||||
sampled_token_ids = sampler_output.sampled_token_ids
|
||||
if input_fits_in_drafter:
|
||||
@@ -1013,12 +1034,15 @@ class GPUARModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin):
|
||||
self.accumulate_full_payload_output(rid, pooler_output[i], req_state)
|
||||
|
||||
with record_function_or_nullcontext("gpu_model_runner: ModelRunnerOutput"):
|
||||
routed_experts_dict = None
|
||||
if self.routed_experts_initialized:
|
||||
capturer = RoutedExpertsCapturer.get_instance()
|
||||
if capturer is not None:
|
||||
capturer.save_captured_experts(indices=self.slot_mapping) # noqa
|
||||
else:
|
||||
logger.error("RoutedExpertsCapturer not initialized.")
|
||||
routed_experts_dict = extract_routed_experts_for_current_batch(
|
||||
req_ids=req_ids_output_copy,
|
||||
requests=self.requests,
|
||||
req_id_to_index=self.input_batch.req_id_to_index,
|
||||
num_tokens_no_spec=self.input_batch.num_tokens_no_spec,
|
||||
max_model_len=self.max_model_len,
|
||||
)
|
||||
output = OmniModelRunnerOutput(
|
||||
req_ids=req_ids_output_copy,
|
||||
req_id_to_index=req_id_to_index_output_copy,
|
||||
@@ -1030,6 +1054,7 @@ class GPUARModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin):
|
||||
ec_connector_output=ec_connector_output if self.supports_mm_inputs else None,
|
||||
num_nans_in_logits=num_nans_in_logits,
|
||||
cudagraph_stats=cudagraph_stats,
|
||||
routed_experts_dict=routed_experts_dict,
|
||||
)
|
||||
output.kv_extracted_req_ids = kv_extracted_req_ids
|
||||
output.omni_connector_output = self.get_omni_connector_output()
|
||||
|
||||
@@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
import gc
|
||||
import logging
|
||||
from copy import copy
|
||||
from dataclasses import replace
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
@@ -18,7 +19,9 @@ from vllm.distributed.kv_transfer import get_kv_transfer_group, has_kv_transfer_
|
||||
from vllm.distributed.parallel_state import get_pp_group
|
||||
from vllm.forward_context import set_forward_context
|
||||
from vllm.model_executor.layers.fused_moe.routed_experts_capturer import (
|
||||
RoutedExpertsCapturer,
|
||||
extract_routed_experts_for_current_batch,
|
||||
get_global_experts_capturer,
|
||||
issue_routing_d2h_copy,
|
||||
)
|
||||
from vllm.utils.math_utils import cdiv
|
||||
from vllm.v1.core.sched.output import GrammarOutput, SchedulerOutput
|
||||
@@ -27,6 +30,7 @@ from vllm.v1.spec_decode.dflash import DFlashProposer
|
||||
from vllm.v1.spec_decode.draft_model import DraftModelProposer
|
||||
from vllm.v1.spec_decode.eagle import EagleProposer
|
||||
from vllm.v1.spec_decode.extract_hidden_states import ExtractHiddenStatesProposer
|
||||
from vllm.v1.spec_decode.gemma4 import Gemma4Proposer
|
||||
from vllm.v1.utils import record_function_or_nullcontext
|
||||
from vllm.v1.worker.gpu_model_runner import (
|
||||
EMPTY_MODEL_RUNNER_OUTPUT,
|
||||
@@ -111,11 +115,21 @@ class GPUGenerationModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin
|
||||
self.flush_full_payload_outputs(flush_ids)
|
||||
|
||||
if self.routed_experts_initialized:
|
||||
capturer = RoutedExpertsCapturer.get_instance()
|
||||
if capturer is not None:
|
||||
capturer.clear_buffer() # noqa
|
||||
else:
|
||||
logger.error("RoutedExpertsCapturer not initialized.")
|
||||
capturer = get_global_experts_capturer()
|
||||
if capturer is not None and hasattr(capturer, "finalize_pending_copy"):
|
||||
capturer.finalize_pending_copy()
|
||||
|
||||
# If ngram_gpu is used, we need to copy the scheduler_output to avoid
|
||||
# the modification has influence on the scheduler_output in engine core process.
|
||||
# The replace is much faster than deepcopy.
|
||||
if self.speculative_config is not None and self.speculative_config.use_ngram_gpu():
|
||||
num_scheduled_tokens_copy = scheduler_output.num_scheduled_tokens.copy()
|
||||
spec_decode_tokens_copy = scheduler_output.scheduled_spec_decode_tokens.copy()
|
||||
scheduler_output = replace(
|
||||
scheduler_output,
|
||||
num_scheduled_tokens=num_scheduled_tokens_copy,
|
||||
scheduled_spec_decode_tokens=spec_decode_tokens_copy,
|
||||
)
|
||||
|
||||
if has_kv_transfer_group():
|
||||
kv_connector_metadata = scheduler_output.kv_connector_metadata
|
||||
@@ -339,6 +353,14 @@ class GPUGenerationModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin
|
||||
if deferred_state_corrections_fn:
|
||||
deferred_state_corrections_fn()
|
||||
|
||||
if self.routed_experts_initialized and hasattr(self, "_positions_cpu"):
|
||||
issue_routing_d2h_copy(
|
||||
input_batch_req_ids=self.input_batch.req_ids,
|
||||
num_scheduled_tokens=scheduler_output.num_scheduled_tokens,
|
||||
positions=self.positions,
|
||||
positions_cpu=self._positions_cpu,
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
@torch.inference_mode()
|
||||
@@ -425,6 +447,15 @@ class GPUGenerationModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin
|
||||
# [Omni] Copy req_id mappings to avoid async scheduling mutation.
|
||||
req_ids_output_copy = self.input_batch.req_ids.copy()
|
||||
req_id_to_index_output_copy = self.input_batch.req_id_to_index.copy()
|
||||
routed_experts_dict = None
|
||||
if self.routed_experts_initialized:
|
||||
routed_experts_dict = extract_routed_experts_for_current_batch(
|
||||
req_ids=req_ids_output_copy,
|
||||
requests=self.requests,
|
||||
req_id_to_index=self.input_batch.req_id_to_index,
|
||||
num_tokens_no_spec=self.input_batch.num_tokens_no_spec,
|
||||
max_model_len=self.max_model_len,
|
||||
)
|
||||
if self._should_accumulate_full_payload_output():
|
||||
for i, rid in enumerate(req_ids_output_copy):
|
||||
req_state = self.requests.get(rid)
|
||||
@@ -442,6 +473,7 @@ class GPUGenerationModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin
|
||||
num_nans_in_logits={},
|
||||
cudagraph_stats=cudagraph_stats,
|
||||
ec_connector_output=ec_connector_output if self.supports_mm_inputs else None,
|
||||
routed_experts_dict=routed_experts_dict,
|
||||
)
|
||||
output.omni_connector_output = self.get_omni_connector_output()
|
||||
|
||||
@@ -751,7 +783,7 @@ class GPUGenerationModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin
|
||||
device=self.device,
|
||||
)
|
||||
|
||||
intermediate_tensors = self.sync_and_slice_intermediate_tensors(num_tokens_padded, None, False)
|
||||
intermediate_tensors = self.sync_and_gather_intermediate_tensors(num_tokens_padded, None, False)
|
||||
|
||||
if ubatch_slices_padded is not None:
|
||||
# Adjust values to reflect a single ubatch.
|
||||
@@ -794,7 +826,7 @@ class GPUGenerationModelRunner(OmniGPUModelRunner, OmniConnectorModelRunnerMixin
|
||||
):
|
||||
assert isinstance(
|
||||
self.drafter,
|
||||
EagleProposer | DFlashProposer | DraftModelProposer | ExtractHiddenStatesProposer,
|
||||
EagleProposer | DFlashProposer | DraftModelProposer | ExtractHiddenStatesProposer | Gemma4Proposer,
|
||||
)
|
||||
assert self.speculative_config is not None
|
||||
# Eagle currently only supports PIECEWISE cudagraphs.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import contextlib
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
import numpy as np
|
||||
@@ -18,6 +19,11 @@ from vllm.v1.spec_decode.dflash import DFlashProposer
|
||||
from vllm.v1.spec_decode.draft_model import DraftModelProposer
|
||||
from vllm.v1.spec_decode.eagle import EagleProposer
|
||||
from vllm.v1.spec_decode.extract_hidden_states import ExtractHiddenStatesProposer
|
||||
from vllm.v1.spec_decode.gemma4 import Gemma4Proposer
|
||||
from vllm.v1.spec_decode.ngram_proposer_gpu import (
|
||||
update_ngram_gpu_tensors_incremental,
|
||||
update_scheduler_for_invalid_drafts,
|
||||
)
|
||||
from vllm.v1.worker.gpu_input_batch import CachedRequestState
|
||||
from vllm.v1.worker.gpu_model_runner import GPUModelRunner, IntermediateTensors, PerLayerAttnMetadata
|
||||
from vllm.v1.worker.ubatch_utils import maybe_create_ubatch_slices
|
||||
@@ -250,7 +256,7 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
|
||||
mrope_pos_ptr += completion_part_len
|
||||
|
||||
def _update_states(self, scheduler_output: "SchedulerOutput"):
|
||||
def _update_states(self, scheduler_output: "SchedulerOutput") -> Callable | None:
|
||||
"""Update the cached states and the persistent batch with the scheduler
|
||||
output.
|
||||
|
||||
@@ -284,8 +290,7 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
if cleanup_finished_request is not None:
|
||||
cleanup_finished_request(req_id)
|
||||
|
||||
if hasattr(self, "late_interaction_runner"):
|
||||
self.late_interaction_runner.on_requests_finished(scheduler_output.finished_req_ids)
|
||||
self.late_interaction_runner.on_requests_finished(scheduler_output.finished_req_ids)
|
||||
# Remove the finished requests from the persistent batch.
|
||||
# NOTE(woosuk): There could be an edge case where finished_req_ids and
|
||||
# scheduled_req_ids overlap. This happens when a request is aborted and
|
||||
@@ -326,8 +331,9 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
for req_id in unscheduled_req_ids:
|
||||
self.input_batch.remove_request(req_id)
|
||||
|
||||
if self.use_async_spec_decode:
|
||||
self.prev_num_draft_tokens.np.fill(0)
|
||||
is_ngram_gpu = self.speculative_config is not None and self.speculative_config.use_ngram_gpu()
|
||||
if is_ngram_gpu:
|
||||
ngram_gpu_new_reqs: list[CachedRequestState] = []
|
||||
|
||||
reqs_to_add: list[CachedRequestState] = []
|
||||
deferred_spec_decode_corrections = []
|
||||
@@ -379,8 +385,7 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
lora_request=new_req_data.lora_request,
|
||||
)
|
||||
self.requests[req_id] = req_state
|
||||
if hasattr(self, "late_interaction_runner"):
|
||||
self.late_interaction_runner.register_request(req_id, pooling_params)
|
||||
self.late_interaction_runner.register_request(req_id, pooling_params)
|
||||
|
||||
# If prompt embeddings are provided, decode and attach to inter_data
|
||||
try:
|
||||
@@ -429,12 +434,30 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
self._init_xdrope_positions(req_state)
|
||||
|
||||
reqs_to_add.append(self.requests[req_id])
|
||||
# Track new requests for ngram_gpu full tensor copy
|
||||
if is_ngram_gpu:
|
||||
ngram_gpu_new_reqs.append(req_state)
|
||||
|
||||
# Update the states of the running/resumed requests.
|
||||
is_last_rank = get_pp_group().is_last_rank
|
||||
req_data = scheduler_output.scheduled_cached_reqs
|
||||
scheduled_spec_tokens = scheduler_output.scheduled_spec_decode_tokens
|
||||
|
||||
# Save scheduler-allocated spec lengths before trimming so
|
||||
# prev_num_draft_len keeps the optimistic count for rejection correction.
|
||||
original_num_spec_per_req: dict[str, int] = {}
|
||||
if self.speculative_config is not None and self.speculative_config.use_ngram_gpu():
|
||||
for req_id, toks in scheduled_spec_tokens.items():
|
||||
original_num_spec_per_req[req_id] = len(toks)
|
||||
update_scheduler_for_invalid_drafts(
|
||||
self._num_valid_draft_tokens_event,
|
||||
self._num_valid_draft_tokens_cpu,
|
||||
scheduler_output,
|
||||
self.input_batch.req_id_to_index,
|
||||
)
|
||||
if self.use_async_spec_decode:
|
||||
self.prev_num_draft_tokens.np.fill(0)
|
||||
|
||||
for i, req_id in enumerate(req_data.req_ids):
|
||||
req_state = self.requests[req_id]
|
||||
num_computed_tokens = req_data.num_computed_tokens[i]
|
||||
@@ -473,6 +496,9 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
if prev_req_index is not None:
|
||||
self.prev_num_draft_tokens.np[prev_req_index] = optimistic_num_accepted
|
||||
|
||||
if is_ngram_gpu and optimistic_num_accepted > 0:
|
||||
self.input_batch.num_tokens_no_spec[req_index] += optimistic_num_accepted
|
||||
|
||||
# Update the cached states.
|
||||
req_state.num_computed_tokens = num_computed_tokens
|
||||
|
||||
@@ -521,6 +547,9 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
req_state.output_token_ids = resumed_token_ids[-num_output_tokens:]
|
||||
|
||||
reqs_to_add.append(req_state)
|
||||
# Track resumed requests for ngram_gpu full tensor copy
|
||||
if is_ngram_gpu:
|
||||
ngram_gpu_new_reqs.append(req_state)
|
||||
continue
|
||||
|
||||
# Update the persistent batch.
|
||||
@@ -539,6 +568,11 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
|
||||
# Add spec_token_ids to token_ids_cpu.
|
||||
self.input_batch.update_req_spec_token_ids(req_state, scheduled_spec_tokens)
|
||||
# Restore scheduler-side draft count after ngram trimming.
|
||||
if original_num_spec_per_req:
|
||||
orig = original_num_spec_per_req.get(req_id, 0)
|
||||
if orig != req_state.prev_num_draft_len:
|
||||
req_state.prev_num_draft_len = orig
|
||||
|
||||
# Add the new or resumed requests to the persistent batch.
|
||||
# The smaller empty indices are filled first.
|
||||
@@ -553,6 +587,18 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
# Refresh batch metadata with any pending updates.
|
||||
self.input_batch.refresh_metadata()
|
||||
|
||||
# Incrementally update ngram_gpu tensors after batch is stable
|
||||
if is_ngram_gpu:
|
||||
update_ngram_gpu_tensors_incremental(
|
||||
self.input_batch,
|
||||
self.token_ids_gpu_tensor,
|
||||
self.num_tokens_no_spec_gpu,
|
||||
ngram_gpu_new_reqs,
|
||||
self.device,
|
||||
_pinned_idx_buf=self._ngram_pinned_idx_buf,
|
||||
_pinned_val_buf=self._ngram_pinned_val_buf,
|
||||
)
|
||||
|
||||
if deferred_spec_decode_corrections:
|
||||
|
||||
def correct_spec_decode_token_counts():
|
||||
@@ -577,6 +623,9 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
if cur_req_index is None:
|
||||
continue
|
||||
self.input_batch.num_computed_tokens_cpu[cur_req_index] -= correction
|
||||
if is_ngram_gpu and correction > 0:
|
||||
self.input_batch.num_tokens_no_spec[cur_req_index] -= correction
|
||||
self.num_tokens_no_spec_gpu[cur_req_index] -= correction
|
||||
|
||||
return correct_spec_decode_token_counts
|
||||
else:
|
||||
@@ -846,7 +895,7 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
device=self.device,
|
||||
)
|
||||
|
||||
intermediate_tensors = self.sync_and_slice_intermediate_tensors(num_tokens_padded, None, False)
|
||||
intermediate_tensors = self.sync_and_gather_intermediate_tensors(num_tokens_padded, None, False)
|
||||
|
||||
if ubatch_slices_padded is not None:
|
||||
# Adjust values to reflect a single ubatch.
|
||||
@@ -900,7 +949,7 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
):
|
||||
assert isinstance(
|
||||
self.drafter,
|
||||
EagleProposer | DFlashProposer | DraftModelProposer | ExtractHiddenStatesProposer,
|
||||
EagleProposer | DFlashProposer | DraftModelProposer | ExtractHiddenStatesProposer | Gemma4Proposer,
|
||||
)
|
||||
assert self.speculative_config is not None
|
||||
# Eagle currently only supports PIECEWISE cudagraphs.
|
||||
@@ -1300,7 +1349,7 @@ class OmniGPUModelRunner(GPUModelRunner):
|
||||
intermediate_tensors = None
|
||||
else:
|
||||
assert intermediate_tensors is not None
|
||||
intermediate_tensors = self.sync_and_slice_intermediate_tensors(
|
||||
intermediate_tensors = self.sync_and_gather_intermediate_tensors(
|
||||
num_input_tokens, intermediate_tensors, True
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user