init v0.23.0

Signed-off-by: Sun Ruoxi <sunruoxi@4paradigm.com>
This commit is contained in:
2026-08-27 15:11:51 +08:00
parent b582a8e7d1
commit 7f8a1b1f7a
2849 changed files with 712887 additions and 22001 deletions

View File

@@ -0,0 +1,49 @@
#
# Copyright (c) 2026 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
from tests.e2e.conftest import VllmRunner
def test_qwen3_dense_tp2_fp16():
example_prompts = [
"Hello, my name is",
]
max_tokens = 5
with VllmRunner(
"Qwen/Qwen3-8B",
tensor_parallel_size=2,
enforce_eager=True,
dtype="float16",
max_model_len=16384,
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)
def test_qwen3_dense_tp4_w8a8():
example_prompts = [
"Hello, my name is",
]
max_tokens = 5
with VllmRunner(
"vllm-ascend/Qwen3-32B-W8A8",
tensor_parallel_size=4,
enforce_eager=True,
dtype="float16",
quantization="ascend",
max_model_len=16384,
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)

View File

@@ -0,0 +1,74 @@
#
# Copyright (c) 2026 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
import pytest
from tests.e2e.conftest import VllmRunner
def test_qwen3_moe_tp4_fp16():
example_prompts = [
"Hello, my name is",
]
max_tokens = 5
with VllmRunner(
"Qwen/Qwen3-30B-A3B",
tensor_parallel_size=4,
enforce_eager=True,
dtype="float16",
max_model_len=16384,
max_num_batched_tokens=2048,
max_num_seqs=256,
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)
def test_qwen3_moe_tp2_w8a8():
example_prompts = [
"Hello, my name is",
]
max_tokens = 5
with VllmRunner(
"vllm-ascend/Qwen3-30B-A3B-W8A8",
tensor_parallel_size=2,
enforce_eager=True,
dtype="float16",
quantization="ascend",
max_model_len=16384,
max_num_batched_tokens=2048,
max_num_seqs=256,
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)
@pytest.mark.skip("Probabilistic failure, need fix")
def test_qwen3_5_moe_tp4_fp16():
example_prompts = [
"Hello, my name is",
]
max_tokens = 5
with VllmRunner(
"Qwen/Qwen3.5-35B-A3B",
tensor_parallel_size=4,
enforce_eager=True,
dtype="float16",
max_model_len=16384,
max_num_batched_tokens=2048,
max_num_seqs=256,
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)

View File

@@ -0,0 +1,34 @@
#
# Copyright (c) 2026 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
import os
import sys
from tests.e2e.conftest import wait_until_npu_memory_free
current_dir = os.path.dirname(os.path.abspath(__file__))
full_dir = os.path.dirname(os.path.dirname(current_dir))
sys.path.insert(0, full_dir)
# ruff: noqa: E402
from tests.e2e.pull_request.utils_310p import run_vl_model_test
@wait_until_npu_memory_free(target_free_percentage=0.7)
def test_qwen3_vl_8b_tp2_fp16():
"""Qwen3-VL-8B dual-card FP16 test"""
run_vl_model_test(model_name="Qwen/Qwen3-VL-8B-Instruct", tensor_parallel_size=2, max_tokens=5)

View File

@@ -0,0 +1,493 @@
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""PCP/DCP long-sequence accuracy guards.
Run `pytest tests/e2e/pull_request/four_card/context_parallel/test_accuracy.py`.
"""
import os
from collections.abc import Sequence
from dataclasses import dataclass
from pathlib import Path
from typing import Any, cast
from unittest.mock import patch
import pytest
from PIL import Image
from vllm import SamplingParams
from tests.e2e.conftest import DPVllmRunner, VllmRunner, wait_until_npu_memory_free
DEEPSEEK_V2_LITE = "vllm-ascend/DeepSeek-V2-Lite-W8A8"
DEEPSEEK_MTP = "wemaster/deepseek_mtp_main_random_bf16"
MAX_NUM_SEQS = 4
E2E_ROOT = Path(__file__).resolve().parents[3]
QWEN_IMAGE_PATH = E2E_ROOT / "prompts" / "qwen.png"
FULL_DECODE_GRAPH = {
"cudagraph_mode": "FULL_DECODE_ONLY",
"cudagraph_capture_sizes": [MAX_NUM_SEQS],
}
COMMON_PROMPTS = [
"The capital of France is",
"Hello, my name is Tom, I am",
"The president of United States is",
]
DSV2_PROMPTS = [
"The president of the United States is",
"The capital of France is",
]
DSV2_PCP_GOLDEN = [
"The president of the United States is a man who is not only a liar, but",
"The capital of France is Paris.\nThe currency of France is the Euro",
]
DSV2_DCP_GOLDEN = [
"The president of the United States is a man who is not only a liar, but",
"The capital of France is Paris.\nThe currency of France is the Euro",
]
QWEN3_GOLDEN = [
"The capital of France is Paris. The capital of",
"Hello, my name is Tom, I am 12 years old",
"The president of United States is the head of state and",
]
QWEN3_NEXT_GOLDEN = [
"The capital of France is Paris. The capital of",
"Hello, my name is Tom, I am 12 years old",
"The president of United States is the head of state and",
]
DSV3_2_GOLDEN = [
"The capital of France isbearerhaloce梗",
"Hello, my name is Tom, I am" + "ERIC slicpacelike Chop",
"The president of United States is平行astra unbehetroni",
]
DSV3_2_GOLDEN_BACKUPS = (
[
"The capital of France isbearerhaloce梗",
"Hello, my name is Tom, I am" + "ERIC slicpacelike Chop",
"The president of United States isoint054 Rund959arki",
],
[
"The capital of France isbearerdenomorthal",
"Hello, my name is Tom, I am" + "ERIC slicpacelike Chop",
"The president of United States is平行astra unbehetroni",
],
[
"The capital of France isbearerdenomorthal",
"Hello, my name is Tom, I am" + "ERIC slicpacelike Chop",
"The president of United States isoint054 Rund959arki",
],
)
DSV3_2_DCP_GOLDEN = [
"The capital of France isoint054 Rund959arki",
"Hello, my name is Tom, I am" + "ERIC slicpacelike挂",
"The president of United States isoint054 Rund959arki",
]
DEEPSEEK_MTP3_GOLDEN = [
"The capital of France is Salmonella团团 elsewhereッγκ",
"Hello, my name is Tom, I amEiSlowukt Analysis sprouts",
"The president of United States is Salmonella团团 elsewhereッγκ",
]
DEEPSEEK_V4_PROMPTS = [
"Hello, my name is",
"What is the meaning of life?",
]
DEEPSEEK_V4_GOLDEN = ["Hello, my name is {name} and I", 'What is the meaning of life?",\n "What is']
@dataclass(frozen=True)
class AccuracyCase:
name: str
model: str
prompts: Sequence[str]
expected_outputs: Sequence[str] | Sequence[Sequence[str]]
max_tokens: int
runner_kwargs: dict[str, Any]
def _run_accuracy_case(case: AccuracyCase) -> None:
runner_cls = DPVllmRunner if case.runner_kwargs.get("data_parallel_size", 1) > 1 else VllmRunner
with runner_cls(case.model, **case.runner_kwargs) as runner:
outputs = runner.generate_greedy(list(case.prompts), case.max_tokens)
if isinstance(case.expected_outputs[0], str):
expected_outputs = cast(Sequence[str], case.expected_outputs)
match_outputs_with_goldens(outputs, expected_outputs)
else:
# If multiple expected output sets are provided, the output is considered correct if it matches any of the sets.
multi_expected_outputs = cast(Sequence[Sequence[str]], case.expected_outputs)
tries = []
for expected in multi_expected_outputs:
try:
match_outputs_with_goldens(outputs, expected)
except AssertionError as exc:
tries.append(f"Output did not match expected set:\n{exc}")
else:
break
if len(tries) == len(multi_expected_outputs):
failure_details = "\n\n".join(tries)
raise AssertionError(f"Output did not match any of the expected output sets:\n{failure_details}")
def match_outputs_with_goldens(outputs: list[tuple[list[int], str]], goldens: Sequence[str]) -> None:
"""Helper function to compare output with golden output, ignoring whitespace differences."""
outputs_str: Sequence[str] = [output[1] for output in outputs]
assert len(outputs_str) == len(goldens)
for index, (output, golden) in enumerate(zip(outputs_str, goldens)):
assert isinstance(output, str) and isinstance(golden, str), "Both output and golden must be strings"
assert output and golden, "Output and golden should not be empty"
assert output.strip() == golden.strip()
@patch.dict(
os.environ,
{
"HCCL_BUFFSIZE": "768",
"VLLM_WORKER_MULTIPROC_METHOD": "spawn",
"OMP_NUM_THREADS": "1",
"OMP_PROC_BIND": "false",
"PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
},
)
@wait_until_npu_memory_free(target_free_percentage=0.8)
def test_qwen3_vl_multimodal_pcp_accuracy_guard() -> None:
image = Image.open(QWEN_IMAGE_PATH).convert("RGB")
single_image_prompt = (
"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n"
"<|im_start|>user\n"
"<|vision_start|><|image_pad|><|vision_end|>"
"Describe this image in detail.<|im_end|>\n"
"<|im_start|>assistant\n"
)
multi_image_prompt = (
"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n"
"<|im_start|>user\n"
"<|vision_start|><|image_pad|><|vision_end|>"
"<|vision_start|><|image_pad|><|vision_end|>"
"Compare these two images and describe similarities.<|im_end|>\n"
"<|im_start|>assistant\n"
)
inputs = [
{
"prompt": single_image_prompt,
"multi_modal_data": {"image": image},
},
{
"prompt": multi_image_prompt,
"multi_modal_data": {"image": [image, image.copy()]},
},
]
sampling_params = SamplingParams(max_tokens=16, temperature=0.0)
with VllmRunner(
"Qwen/Qwen3-VL-8B-Instruct",
enforce_eager=False,
max_model_len=4096,
tensor_parallel_size=2,
prefill_context_parallel_size=2,
decode_context_parallel_size=1,
max_num_batched_tokens=1024,
block_size=128,
limit_mm_per_prompt={"image": 2},
mm_processor_kwargs={
"min_pixels": 28 * 28,
"max_pixels": 1280 * 28 * 28,
"fps": 1,
},
compilation_config={
"cudagraph_mode": "FULL_DECODE_ONLY",
"cudagraph_capture_sizes": [MAX_NUM_SEQS],
},
) as runner:
outputs = runner.model.generate(inputs, sampling_params=sampling_params)
assert len(outputs) == len(inputs)
for output in outputs:
assert output.outputs and output.outputs[0].text.strip()
DSV2_COMMON_KWARGS: dict[str, Any] = {
"max_model_len": 1024,
"max_num_seqs": MAX_NUM_SEQS,
"max_num_batched_tokens": 1024,
"enable_expert_parallel": True,
"enable_chunked_prefill": True,
"enable_prefix_caching": True,
"block_size": 128,
"quantization": "ascend",
"compilation_config": FULL_DECODE_GRAPH,
"additional_config": {"enable_flashcomm1": True},
}
DSV2_PARALLEL_CASES = [
AccuracyCase(
name="dsv2_pcp_dcp_full_features",
model=DEEPSEEK_V2_LITE,
prompts=DSV2_PROMPTS,
expected_outputs=DSV2_PCP_GOLDEN,
max_tokens=10,
runner_kwargs={
**DSV2_COMMON_KWARGS,
"tensor_parallel_size": 2,
"prefill_context_parallel_size": 2,
"decode_context_parallel_size": 2,
"cp_kv_cache_interleave_size": 128,
"long_prefill_token_threshold": 4,
},
),
AccuracyCase(
name="dsv2_pcp_only_full_features",
model=DEEPSEEK_V2_LITE,
prompts=DSV2_PROMPTS,
expected_outputs=DSV2_PCP_GOLDEN,
max_tokens=10,
runner_kwargs={
**DSV2_COMMON_KWARGS,
"tensor_parallel_size": 2,
"prefill_context_parallel_size": 2,
"decode_context_parallel_size": 1,
"cp_kv_cache_interleave_size": 128,
"long_prefill_token_threshold": 4,
},
),
AccuracyCase(
name="dsv2_dcp_only_full_features",
model=DEEPSEEK_V2_LITE,
prompts=DSV2_PROMPTS,
expected_outputs=DSV2_DCP_GOLDEN,
max_tokens=10,
runner_kwargs={
**DSV2_COMMON_KWARGS,
"tensor_parallel_size": 2,
"prefill_context_parallel_size": 1,
"decode_context_parallel_size": 2,
"long_prefill_token_threshold": 64,
"compilation_config": {
**FULL_DECODE_GRAPH,
"pass_config": {"enable_sp": True},
},
},
),
]
FULL_FEATURE_MODEL_CASES = [
AccuracyCase(
name="qwen3_pcp_dcp_full_features",
model="vllm-ascend/Qwen3-30B-A3B-W8A8",
prompts=COMMON_PROMPTS,
expected_outputs=QWEN3_GOLDEN,
max_tokens=5,
runner_kwargs={
"max_model_len": 1024,
"max_num_seqs": MAX_NUM_SEQS,
"max_num_batched_tokens": 1024,
"tensor_parallel_size": 2,
"prefill_context_parallel_size": 2,
"decode_context_parallel_size": 1,
"enable_expert_parallel": True,
"enable_chunked_prefill": True,
"enable_prefix_caching": True,
"block_size": 128,
"quantization": "ascend",
"long_prefill_token_threshold": 4,
"compilation_config": FULL_DECODE_GRAPH,
"additional_config": {"enable_flashcomm1": True},
},
),
AccuracyCase(
name="qwen3_next_pcp_dcp_full_features",
model="Qwen/Qwen3-Next-80B-A3B-Instruct",
prompts=COMMON_PROMPTS,
expected_outputs=QWEN3_NEXT_GOLDEN,
max_tokens=5,
runner_kwargs={
"enforce_eager": True,
"max_model_len": 1024,
"tensor_parallel_size": 2,
"prefill_context_parallel_size": 2,
"decode_context_parallel_size": 1,
"max_num_batched_tokens": 1024,
"enable_expert_parallel": True,
# TODO(qcs): We should set `long_prefill_token_threshold` to 4
# when chunked prefill with PCP is stable.
"long_prefill_token_threshold": 128,
"gpu_memory_utilization": 0.8,
"block_size": 128,
# FlashComm1 is disabled for qwen3_next until the PCP decode path is fixed.
"additional_config": {"enable_flashcomm1": False},
},
),
AccuracyCase(
name="dsv3_2_pcp_dcp_full_features",
model="vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
prompts=COMMON_PROMPTS,
# TODO(qcs): Remove multi-expected_outputs after the first request output is stable.
expected_outputs=(DSV3_2_GOLDEN, *DSV3_2_GOLDEN_BACKUPS),
max_tokens=5,
runner_kwargs={
"max_model_len": 1024,
"max_num_seqs": MAX_NUM_SEQS,
"max_num_batched_tokens": 1024,
"tensor_parallel_size": 2,
"prefill_context_parallel_size": 2,
"decode_context_parallel_size": 2,
"enable_expert_parallel": True,
"enable_chunked_prefill": True,
"enable_prefix_caching": True,
"gpu_memory_utilization": 0.2,
"cp_kv_cache_interleave_size": 128,
"block_size": 128,
"quantization": "ascend",
# TODO(qcs): We should set `long_prefill_token_threshold` to 4
# when chunked prefill with PCP is stable.
"long_prefill_token_threshold": 128,
"compilation_config": FULL_DECODE_GRAPH,
"additional_config": {"enable_flashcomm1": True},
# graph_mode is disabled for dsv32 until the PCP gatherv3 out of index issue is fixed.
"enforce_eager": True,
},
),
AccuracyCase(
name="dsv3_2_sfa_dcp_replicated_indexer",
model="vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
prompts=COMMON_PROMPTS,
expected_outputs=DSV3_2_DCP_GOLDEN,
max_tokens=5,
runner_kwargs={
"max_model_len": 1024,
"max_num_seqs": MAX_NUM_SEQS,
"max_num_batched_tokens": 1024,
"data_parallel_size": 2,
"tensor_parallel_size": 2,
"prefill_context_parallel_size": 1,
"decode_context_parallel_size": 2,
"enable_expert_parallel": True,
"enable_chunked_prefill": True,
"enable_prefix_caching": True,
"gpu_memory_utilization": 0.4,
"cp_kv_cache_interleave_size": 1,
"block_size": 128,
"quantization": "ascend",
"long_prefill_token_threshold": 128,
"compilation_config": FULL_DECODE_GRAPH,
"additional_config": {
"enable_flashcomm1": True,
"enable_dsa_cp": True,
"enable_sparse_sfa_c8": True,
"enable_sparse_li_c8": True,
},
"speculative_config": {
"method": "mtp",
"num_speculative_tokens": 3,
},
},
),
pytest.param(
AccuracyCase(
name="deepseek_mtp3_pcp_dcp_full_features",
model=DEEPSEEK_MTP,
prompts=COMMON_PROMPTS,
expected_outputs=DEEPSEEK_MTP3_GOLDEN,
max_tokens=5,
runner_kwargs={
"max_model_len": 1024,
"max_num_seqs": MAX_NUM_SEQS,
"max_num_batched_tokens": 1024,
"tensor_parallel_size": 2,
"prefill_context_parallel_size": 2,
"decode_context_parallel_size": 2,
"enable_expert_parallel": True,
"enable_chunked_prefill": True,
"enable_prefix_caching": True,
"block_size": 128,
"long_prefill_token_threshold": 4,
"speculative_config": {
"method": "mtp",
"num_speculative_tokens": 3,
},
"compilation_config": FULL_DECODE_GRAPH,
"additional_config": {"enable_flashcomm1": True},
},
),
marks=pytest.mark.skip(reason="Temporarily skip MTP with PCP/DCP until the token layout issue is fixed."),
),
AccuracyCase(
name="deepseek_v4_w4a8_dsa_cp_full_features",
model="gdydems/DeepSeek-V4-Flash-w4a8-mtp",
prompts=DEEPSEEK_V4_PROMPTS,
expected_outputs=DEEPSEEK_V4_GOLDEN,
max_tokens=5,
runner_kwargs={
"max_model_len": 8192,
"max_num_seqs": 16,
"max_num_batched_tokens": 4096,
"dtype": "auto",
"tensor_parallel_size": 4,
"prefill_context_parallel_size": 1,
"decode_context_parallel_size": 1,
"enable_expert_parallel": True,
"gpu_memory_utilization": 0.9,
"quantization": "ascend",
"tokenizer_mode": "deepseek_v4",
"block_size": 128,
"compilation_config": {
"cudagraph_mode": "FULL_DECODE_ONLY",
},
"additional_config": {
"enable_flashcomm1": True,
"enable_dsa_cp": True,
},
},
),
]
@patch.dict(
os.environ,
{
"HCCL_BUFFSIZE": "768",
},
)
@wait_until_npu_memory_free(target_free_percentage=0.8)
@pytest.mark.parametrize("case", DSV2_PARALLEL_CASES, ids=lambda case: case.name)
def test_dsv2_lite_parallel_config_accuracy(case: AccuracyCase) -> None:
_run_accuracy_case(case)
@patch.dict(
os.environ,
{
"HCCL_BUFFSIZE": "768",
"PYTORCH_NPU_ALLOC_CONF": "expandable_segments:True",
},
)
@wait_until_npu_memory_free(target_free_percentage=0.8)
@pytest.mark.parametrize("case", FULL_FEATURE_MODEL_CASES, ids=lambda case: case.name)
def test_models_pcp_dcp_full_feature_accuracy(case: AccuracyCase) -> None:
_run_accuracy_case(case)

View File

@@ -0,0 +1,76 @@
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
#
"""DeepSeek-V2-Lite prefix-cache CP guard.
Run `pytest tests/e2e/pull_request/four_card/context_parallel/test_prefix_caching_cp.py`.
"""
import os
from unittest.mock import patch
from tests.e2e.conftest import VllmRunner
# TODO(qcs): We should use Qwen3.5 for this test when it's fixed and available,
# so that we can test the hybrid kv cache with both full attention and linear attention.
MODEL = "vllm-ascend/DeepSeek-V2-Lite-W8A8"
MAX_NUM_SEQS = 2
THREAD_ENV = {
"OMP_NUM_THREADS": "1",
"MKL_NUM_THREADS": "1",
"OPENBLAS_NUM_THREADS": "1",
"NUMEXPR_NUM_THREADS": "1",
}
DSV2_LITE_PREFIX_PROMPT = (
"You are reading a compact synthetic operations ledger. "
"Use only the rows below when answering the final question.\n"
+ "\n".join(
f"Row {i}: route R{i:03d} moves cargo from zone {i % 11} to zone {(i * 7) % 13}; priority is {i % 5}."
for i in range(64)
)
+ "\n"
)
INPUT_PROMPTS = [
DSV2_LITE_PREFIX_PROMPT + "Question: What route is listed in row 17? Answer briefly.",
DSV2_LITE_PREFIX_PROMPT + "Question: What priority is listed in row 42? Answer briefly.",
]
@patch.dict(os.environ, THREAD_ENV)
def test_dsv2_lite_prefix_cache_with_pcp() -> None:
with VllmRunner(
MODEL,
block_size=128,
max_model_len=2048,
max_num_seqs=MAX_NUM_SEQS,
max_num_batched_tokens=2048,
tensor_parallel_size=2,
prefill_context_parallel_size=2,
decode_context_parallel_size=1,
enforce_eager=True,
enable_expert_parallel=True,
enable_prefix_caching=True,
quantization="ascend",
) as vllm_model:
prefix_cache_outputs = vllm_model.generate_greedy(INPUT_PROMPTS, 8)
assert len(prefix_cache_outputs) == len(INPUT_PROMPTS)
for output_ids, output_text in prefix_cache_outputs:
assert output_ids
assert output_text

View File

@@ -0,0 +1,85 @@
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
# Adapted from vllm/tests/basic_correctness/test_basic_correctness.py
#
"""Compare the short outputs of HF and vLLM when using greedy sampling.
Run `pytest tests/e2e/pull_request/four_card/spec_decode/test_mtp_qwen3_next.py`.
"""
import os
import pytest
from vllm.config import CompilationConfig
from vllm.v1.metrics.reader import Counter, Vector
from tests.e2e.conftest import VllmRunner, cleanup_dist_env_and_memory
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
MODELS = ["Qwen/Qwen3-Next-80B-A3B-Instruct"]
@pytest.mark.parametrize("model_name", MODELS)
def test_qwen3_next_mtp_acceptance_tp4(model_name):
golden = [0.85, 0.46, 0.19]
example_prompts = [
"Hello, my name is",
"The president of the United States is",
"The capital of France is",
"The future of AI is",
]
max_tokens = 1024
with VllmRunner(
model_name,
tensor_parallel_size=4,
max_model_len=4096,
gpu_memory_utilization=0.8,
distributed_executor_backend="mp",
disable_log_stats=False,
speculative_config={
"method": "qwen3_next_mtp",
"num_speculative_tokens": 3,
},
compilation_config=CompilationConfig(cudagraph_mode="FULL_DECODE_ONLY", cudagraph_capture_sizes=[20]),
) as spec_vllm_model:
_ = spec_vllm_model.generate_greedy(example_prompts, max_tokens)
metrics = spec_vllm_model.model.get_metrics()
num_drafts = 0
num_accepted_tokens_per_pos = [0] * 3
for metric in metrics:
if metric.name == "vllm:spec_decode_num_drafts":
assert isinstance(metric, Counter)
num_drafts += metric.value
elif metric.name == "vllm:spec_decode_num_accepted_tokens_per_pos":
assert isinstance(metric, Vector)
for pos in range(len(metric.values)):
num_accepted_tokens_per_pos[pos] += metric.values[pos]
acceptance_per_pos = [num_accepted_tokens / num_drafts for num_accepted_tokens in num_accepted_tokens_per_pos]
match = all((a >= b) or (b - a < 0.06) for a, b in zip(acceptance_per_pos, golden))
if not match:
print(f"acceptance_per_pos: {acceptance_per_pos}")
print(f"golden: {golden}")
assert match
cleanup_dist_env_and_memory()

View File

@@ -0,0 +1,49 @@
import os
import subprocess
import sys
from pathlib import Path
from unittest.mock import patch
import pytest
from tests.e2e.conftest import wait_until_npu_memory_free
MODELS = ["Qwen/Qwen3-30B-A3B"]
REPO_ROOT = Path(__file__).resolve().parents[4]
DATA_PARALLEL_SCRIPT = REPO_ROOT / "examples" / "offline_data_parallel.py"
@pytest.mark.skip(reason="broken, fix me")
@pytest.mark.parametrize("model", MODELS)
@pytest.mark.parametrize("max_tokens", [32])
@patch.dict(os.environ, {"ASCEND_RT_VISIBLE_DEVICES": "0,1,2,3"})
@wait_until_npu_memory_free(target_free_percentage=0.7)
def test_qwen3_inference_dp2_tp2(model, max_tokens):
env = os.environ.copy()
cmd = [
sys.executable,
str(DATA_PARALLEL_SCRIPT),
"--model",
model,
"--dp-size",
"2",
"--tp-size",
"2",
"--node-size",
"1",
"--node-rank",
"0",
"--trust-remote-code",
]
print(f"Running subprocess: {' '.join(cmd)}")
proc = subprocess.run(cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=600)
output = proc.stdout.decode(errors="ignore")
print(output)
assert "DP rank 0 needs to process" in output
assert "DP rank 1 needs to process" in output
assert "Generated text:" in output
assert proc.returncode == 0

View File

@@ -0,0 +1,164 @@
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
#
import json
import os
from unittest.mock import patch
import requests
from vllm.utils.network_utils import get_open_port
from tests.e2e.conftest import DisaggPDProxy, RemotePDServer, VllmRunner, wait_until_npu_memory_free
@patch.dict(os.environ, {"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1"})
@wait_until_npu_memory_free()
def test_moe_w8a8_tp_pp_ep_full_decode_only():
"""Verify W8A8 MoE generation with TP, PP, EP, and full decode only."""
model = "vllm-ascend/DeepSeek-V3.2-W8A8-Pruning"
prompts = ["Hello, my name is"]
with VllmRunner(
model,
enable_expert_parallel=True,
quantization="ascend",
max_model_len=1024,
tensor_parallel_size=2,
pipeline_parallel_size=2,
gpu_memory_utilization=0.8,
compilation_config={"cudagraph_capture_sizes": [2, 4, 6, 8, 10, 12], "cudagraph_mode": "FULL_DECODE_ONLY"},
) as vllm_model:
outputs = vllm_model.generate_greedy(prompts, max_tokens=500)
assert len(outputs) == len(prompts)
assert len(outputs[0][1]) > len(prompts[0])
@wait_until_npu_memory_free()
def test_pd_disaggregation_w8a8_sfa_dsa_full_decode_only():
"""Verify W8A8 1P1D PD disaggregation with full decode only."""
prefiller_port = [get_open_port()]
decoder_port = [get_open_port()]
proxy_port = get_open_port()
ld_library_path = os.environ.get("LD_LIBRARY_PATH", "")
env_dict = {
"LD_LIBRARY_PATH": f"/usr/local/lib:{ld_library_path}",
}
vllm_server_args = [
[
"--port",
str(prefiller_port[0]),
"--model",
"vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
"--trust-remote-code",
"--enable-request-id-headers",
"--no-enable-prefix-caching",
"--enable-expert-parallel",
"--quantization",
"ascend",
"--max-model-len",
"1024",
"--max-num-batched-tokens",
"1024",
"--max-num-seqs",
"4",
"--tensor-parallel-size",
"2",
"--gpu-memory-utilization",
"0.9",
"--kv-transfer-config",
json.dumps(
{
"kv_connector": "MooncakeConnectorV1",
"kv_role": "kv_producer",
"kv_port": "30000",
"kv_connector_extra_config": {
"prefill": {"dp_size": 1, "tp_size": 2},
"decode": {"dp_size": 1, "tp_size": 2},
},
}
),
"--enforce-eager",
],
[
"--port",
str(decoder_port[0]),
"--model",
"vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
"--trust-remote-code",
"--enable-request-id-headers",
"--no-enable-prefix-caching",
"--enable-expert-parallel",
"--quantization",
"ascend",
"--max-model-len",
"1024",
"--max-num-batched-tokens",
"1024",
"--max-num-seqs",
"4",
"--tensor-parallel-size",
"2",
"--gpu-memory-utilization",
"0.9",
"--kv-transfer-config",
json.dumps(
{
"kv_connector": "MooncakeConnectorV1",
"kv_role": "kv_consumer",
"kv_port": "30200",
"kv_connector_extra_config": {
"prefill": {"dp_size": 1, "tp_size": 2},
"decode": {"dp_size": 1, "tp_size": 2},
},
}
),
"--compilation-config",
json.dumps(
{
"cudagraph_mode": "FULL_DECODE_ONLY",
"cudagraph_capture_sizes": [1, 2, 4, 8],
}
),
],
]
with (
RemotePDServer(vllm_server_args, env_dict=env_dict),
DisaggPDProxy(
port=proxy_port,
prefiller_ports=prefiller_port,
decoder_ports=decoder_port,
) as proxy,
):
response = requests.post(
proxy.url_for("v1", "completions"),
json={
"model": "vllm-ascend/DeepSeek-V3.2-W8A8-Pruning",
"prompt": "Hello, my name is",
"max_tokens": 5,
"temperature": 0.0,
},
timeout=600,
)
response.raise_for_status()
output = response.json()
assert output["choices"][0]["text"]

View File

@@ -0,0 +1,117 @@
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
# Adapted from vllm/tests/basic_correctness/test_basic_correctness.py
#
import os
from unittest.mock import patch
from tests.e2e.conftest import VllmRunner, wait_until_npu_memory_free
os.environ["PYTORCH_NPU_ALLOC_CONF"] = "expandable_segments:True"
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
@patch.dict(
os.environ,
{
"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1",
},
)
@wait_until_npu_memory_free()
def test_deepseek_v4_w4a8_tp4_basic_greedy():
"""Verify DeepSeek V4 W4A8 basic greedy generation with TP4 and EP."""
example_prompts = [
"Hello, my name is",
"What is the meaning of life?",
]
max_tokens = 5
with VllmRunner(
"gdydems/DeepSeek-V4-Flash-w4a8-mtp",
max_model_len=8192,
max_num_seqs=16,
max_num_batched_tokens=4096,
dtype="auto",
tensor_parallel_size=4,
enable_expert_parallel=True,
gpu_memory_utilization=0.9,
quantization="ascend",
tokenizer_mode="deepseek_v4",
block_size=128,
compilation_config={
"cudagraph_mode": "FULL_DECODE_ONLY",
},
speculative_config={"num_speculative_tokens": 1, "method": "mtp"},
) as vllm_model:
outputs = vllm_model.generate_greedy(example_prompts, max_tokens)
expected_token_ids = [
[19923, 14, 1026, 2329, 344, 680, 2852, 95, 305, 342],
[3085, 344, 270, 5281, 294, 1988, 33, 3955, 361, 582, 3085, 344],
]
assert len(outputs) == len(example_prompts)
for i, (output_ids, output_str) in enumerate(outputs):
assert len(output_str) > 0
assert len(output_ids) > 0
assert output_ids == expected_token_ids[i]
@patch.dict(
os.environ,
{
"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1",
},
)
@wait_until_npu_memory_free()
def test_deepseek_v4_w4a8_tp4_index_cache_freq4():
"""IndexCache freq=4 must produce non-empty greedy outputs identical in
shape to the baseline test, verifying skip_topk/topk_indices_buffer
plumbing (DSAModules → AscendDSAImpl) is wired correctly across both
serial and dual-stream paths.
"""
example_prompts = [
"Hello, my name is",
"The capital of France is",
"What is the meaning of life?",
]
max_tokens = 5
with VllmRunner(
"gdydems/DeepSeek-V4-Flash-w4a8-mtp",
max_model_len=8192,
max_num_seqs=16,
max_num_batched_tokens=4096,
dtype="auto",
tensor_parallel_size=4,
enable_expert_parallel=True,
gpu_memory_utilization=0.9,
quantization="ascend",
tokenizer_mode="deepseek_v4",
block_size=128,
compilation_config={
"cudagraph_mode": "FULL_DECODE_ONLY",
},
hf_overrides={
"use_index_cache": True,
"index_topk_freq": 4,
},
) as vllm_model:
outputs = vllm_model.generate_greedy(example_prompts, max_tokens)
assert len(outputs) == len(example_prompts)
for output_ids, output_str in outputs:
assert len(output_str) > 0
assert len(output_ids) > 0

View File

@@ -0,0 +1,673 @@
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import contextlib
import multiprocessing
import os
import queue
from typing import Any
from unittest.mock import patch
import pytest
import torch
from vllm import LLM, SamplingParams
from vllm.utils.network_utils import get_open_port
from tests.e2e.conftest import cleanup_dist_env_and_memory, wait_until_npu_memory_free
from tests.e2e.pull_request.utils import PROMPTS_LONG, PROMPTS_SHORT
QWEN3 = "Qwen/Qwen3-0.6B"
DEEPSEEK_V2_LITE = "vllm-ascend/DeepSeek-V2-Lite-W8A8"
QWEN3_PROMPTS_SHORT_BASELINE = [
{
"token_ids": [444, 2210, 13, 358, 2776, 264, 220, 17, 17, 4666, 6284, 5458, 504, 5616, 13, 358],
"logprobs": [
-4.082355976104736,
-1.8239692449569702,
-0.7117747664451599,
-0.19997872412204742,
-0.9261042475700378,
-1.156473994255066,
-1.6240551471710205,
-0.4462895393371582,
-1.7280645370483398,
-0.03872159123420715,
-0.006691192742437124,
-1.729214072227478,
-1.4082962274551392,
-1.2914694547653198,
-0.30865585803985596,
-0.19352123141288757,
],
},
{
"token_ids": [279, 1852, 438, 279, 4767, 315, 279, 3639, 19140, 13, 1096, 374, 1576, 279, 4767, 315],
"logprobs": [
-1.329056978225708,
-1.9254618883132935,
-0.16430772840976715,
-0.07656584680080414,
-0.20014727115631104,
-0.009215084835886955,
-0.6130291223526001,
-0.7763887047767639,
-0.7629368901252747,
-0.7878186702728271,
-1.5039337873458862,
-0.34936416149139404,
-1.1888322830200195,
-0.7042690515518188,
-0.7195678949356079,
-0.5817108154296875,
],
},
{
"token_ids": [12095, 13, 576, 6722, 315, 15344, 374, 21718, 13, 576, 6722, 315, 17689, 374, 24081, 13],
"logprobs": [
-0.4164676070213318,
-0.650071918964386,
-1.2497835159301758,
-0.41373029351234436,
-0.10615493357181549,
-1.7584394216537476,
-0.014708652161061764,
-0.09583187848329544,
-0.05152897536754608,
-0.5075417757034302,
-0.04731765389442444,
-0.004497650545090437,
-0.734763503074646,
-0.006638852413743734,
-0.03197822347283363,
-0.02921150252223015,
],
},
{
"token_ids": [537, 1101, 264, 29016, 8645, 714, 264, 27155, 17991, 315, 1246, 582, 3887, 11, 975, 11],
"logprobs": [
-2.253798246383667,
-0.6618977785110474,
-1.1329351663589478,
-1.5547151565551758,
-2.502274751663208,
-0.6954820156097412,
-0.16636334359645844,
-0.9172338247299194,
-0.8822890520095825,
-0.6346368789672852,
-1.1293692588806152,
-0.355191707611084,
-0.23319871723651886,
-0.3448694050312042,
-0.015147067606449127,
-0.023382289335131645,
],
},
]
QWEN3_PROMPTS_LONG_BASELINE = [
{
"token_ids": [4710, 1249, 11625, 419, 3491, 11, 582, 1184, 311, 990, 279, 7506, 315, 328, 1543, 323],
"logprobs": [
-1.1807091236114502,
-0.7548902034759521,
-0.06596270948648453,
-0.21140751242637634,
-0.17375628650188446,
-0.012642711400985718,
-0.8002182841300964,
-0.5415360331535339,
-0.005715575534850359,
-1.0228453874588013,
-0.49259668588638306,
-0.5897566080093384,
-0.0001705739414319396,
-0.008742272853851318,
-0.0008293526479974389,
-0.20622815191745758,
],
},
{
"token_ids": [4710, 1249, 11625, 419, 3491, 11, 582, 646, 990, 279, 2701, 5486, 25, 6771, 400, 47],
"logprobs": [
-0.6663188934326172,
-0.31078171730041504,
-0.1298152357339859,
-0.17452840507030487,
-0.14723201096057892,
-0.011572140268981457,
-0.5467657446861267,
-1.1380974054336548,
-0.8147172927856445,
-0.2714247703552246,
-1.2143880128860474,
-0.84015291929245,
-0.7829729318618774,
-1.5653221607208252,
-0.631846010684967,
-1.7602534294128418,
],
},
{
"token_ids": [4710, 1249, 11625, 419, 3491, 11, 582, 646, 990, 279, 2701, 5486, 25, 6771, 400, 1124],
"logprobs": [
-0.979120671749115,
-0.74333256483078,
-0.22203724086284637,
-0.31942218542099,
-0.2570137679576874,
-0.012989195063710213,
-0.939915657043457,
-1.038073182106018,
-0.4173763394355774,
-0.17641115188598633,
-1.119675874710083,
-1.2035267353057861,
-0.7646281719207764,
-1.5680660009384155,
-0.5041356086730957,
-1.0269478559494019,
],
},
]
DEEPSEEK_V2_LITE_PROMPTS_SHORT_BASELINE = [
{
"token_ids": [185, 40, 608, 245, 207, 17, 15, 1012, 1712, 12608, 11, 285, 304, 463, 803, 14079],
"logprobs": [
-4.621323108673096,
-3.077335834503174,
-1.3346226215362549,
-0.9667544364929199,
-2.628643274307251,
-1.0353751182556152,
-1.9108309745788574,
-0.9746326208114624,
-0.07258293032646179,
-2.375293016433716,
-1.7306381464004517,
-1.7152436971664429,
-0.5231161713600159,
-1.2002876996994019,
-0.945522665977478,
-2.263141393661499,
],
},
{
"token_ids": [245, 668, 779, 317, 441, 889, 245, 69524, 11, 548, 245, 42357, 11, 245, 39925, 11],
"logprobs": [
-1.9601335525512695,
-2.9202699661254883,
-0.935673713684082,
-1.9938912391662598,
-2.5300979614257812,
-2.1406760215759277,
-2.01202130317688,
-2.9169065952301025,
-0.5751016736030579,
-0.5648833513259888,
-1.2851604223251343,
-2.817136764526367,
-0.826416015625,
-0.8048403263092041,
-1.2173839807510376,
-0.32657456398010254,
],
},
{
"token_ids": [8913, 13, 185, 549, 19305, 280, 7239, 317, 254, 28071, 13, 185, 549, 13829, 13451, 279],
"logprobs": [
-0.39279282093048096,
-0.8086707592010498,
-0.7125738859176636,
-1.6490240097045898,
-1.9644602537155151,
-0.4904576539993286,
-0.0832064300775528,
-0.00726190535351634,
-0.38277116417884827,
-0.2940319776535034,
-0.7399694323539734,
-0.07564151287078857,
-0.7905924320220947,
-1.8350766897201538,
-0.4076140522956848,
-0.20184002816677094,
],
},
{
"token_ids": [6464, 11, 285, 359, 487, 82, 889, 1872, 276, 752, 34993, 13, 1733, 20838, 11106, 276],
"logprobs": [
-2.4984869956970215,
-0.7851194739341736,
-0.6003906726837158,
-1.4917504787445068,
-0.9962607026100159,
-0.004179196432232857,
-1.5068310499191284,
-0.4656505584716797,
-0.0027805021964013577,
-0.11925199627876282,
-0.35579147934913635,
-0.35492807626724243,
-1.605405330657959,
-1.38535737991333,
-0.930091142654419,
-0.004122450482100248,
],
},
]
DEEPSEEK_V2_LITE_PROMPTS_LONG_BASELINE = [
{
"token_ids": [185, 185, 1679, 26430, 279, 16145, 285, 8204, 185, 185, 13483, 9890, 16982, 457, 17693, 829],
"logprobs": [
-0.24134111404418945,
-0.06949862092733383,
-2.196256637573242,
-0.3235849142074585,
-5.745722592109814e-05,
-7.152554530875932e-07,
-9.381330892210826e-05,
-3.4570632578834193e-06,
-0.0006878394051454961,
-6.079655122448457e-06,
-7.152554530875932e-07,
-8.356221951544285e-05,
-1.8358061424805783e-05,
-1.2636104656849056e-05,
-1.5497195136049413e-06,
-2.0146166207268834e-05,
],
},
{
"token_ids": [185, 185, 1679, 26430, 279, 16145, 285, 8204, 185, 185, 13483, 9890, 16982, 457, 17693, 829],
"logprobs": [
-0.27419501543045044,
-0.08238636702299118,
-2.0612075328826904,
-1.0054221153259277,
-4.947062916471623e-05,
-4.768370445162873e-07,
-2.1457441107486375e-05,
-3.933898824470816e-06,
-0.00018630675913300365,
-1.1920920996999484e-06,
-4.172316494077677e-06,
-0.00010048838157672435,
-1.0847986231965479e-05,
-1.1920928244535389e-07,
-1.1920920996999484e-06,
-1.4185804502631072e-05,
],
},
{
"token_ids": [185, 185, 1679, 26430, 279, 16145, 285, 8204, 185, 185, 13483, 9890, 16982, 457, 17693, 829],
"logprobs": [
-0.14973750710487366,
-0.09690935909748077,
-1.8772021532058716,
-1.6962311267852783,
-0.0001429217227268964,
-8.344646289515367e-07,
-7.617183291586116e-05,
-3.814689989667386e-06,
-0.0010556369088590145,
-6.318072337307967e-06,
-1.1920920996999484e-06,
-0.00035553809721022844,
-2.729855441430118e-05,
-2.3841855067985307e-07,
-1.4305104514278355e-06,
-4.410733708937187e-06,
],
},
]
CASE_QWEN_ACLGRAPH = {
"model": QWEN3,
"quantization": None,
"prompts": {"short": PROMPTS_SHORT, "long": PROMPTS_LONG},
"compilation_config": {"max_cudagraph_capture_size": 24, "cudagraph_mode": "FULL"},
"tensor_parallel_size": 4,
"data_parallel_size": 1,
"enable_expert_parallel": False,
"golden_answers": {"short": QWEN3_PROMPTS_SHORT_BASELINE, "long": QWEN3_PROMPTS_LONG_BASELINE},
# TODO: it increases after profile graph memory is disabled, invetigate later
"baseline_capture_mem": 0.30,
"capture_mem_tolerance": 1.3,
}
CASE_DS_ACLGRAPH = {
"model": DEEPSEEK_V2_LITE,
"quantization": "ascend",
"prompts": {"short": PROMPTS_SHORT, "long": PROMPTS_LONG},
"compilation_config": {"max_cudagraph_capture_size": 24, "cudagraph_mode": "FULL_AND_PIECEWISE"},
"tensor_parallel_size": 2,
"data_parallel_size": 2,
"enable_expert_parallel": True,
"golden_answers": {
"short": DEEPSEEK_V2_LITE_PROMPTS_SHORT_BASELINE,
"long": DEEPSEEK_V2_LITE_PROMPTS_LONG_BASELINE,
},
"baseline_capture_mem": 0.68,
"capture_mem_tolerance": 1.5,
}
CASE_DS_ACLGRAPH_ENPU = {
**CASE_DS_ACLGRAPH,
"env_vars": {"ENPU_ENABLE": "true"},
}
# inherit from tests/e2e/pull_request/utils.py::compare_logprobs
ATOL = 0.0689
_SAMPLING_PARAMS = SamplingParams(
max_tokens=16,
temperature=0.0,
top_p=1.0,
top_k=0,
logprobs=20,
)
def _install_spies(metrics: dict[str, Any] | None):
"""Installs thread-safe spies on NPU methods to track invocation counts."""
if metrics is None:
return contextlib.nullcontext()
from vllm_ascend.worker.model_runner_v1 import NPUModelRunner
def make_spy(cls, method_name, capture_mem):
original = getattr(cls, method_name)
def spy(self, *args, **kwargs):
mem_before = torch.npu.mem_get_info()[0] # free memory
result = original(self, *args, **kwargs)
mem_after = torch.npu.mem_get_info()[0]
with capture_mem["call_counts"].get_lock():
capture_mem["call_counts"].value += 1
capture_mem["mem_before_capture"].value += mem_before
capture_mem["mem_after_capture"].value += mem_after
return result
return spy
stack = contextlib.ExitStack()
hooks = [
(NPUModelRunner, "capture_model", metrics["capture_mem"]),
]
for cls, method, metric in hooks:
stack.enter_context(patch.object(cls, method, make_spy(cls, method, metric)))
return stack
def _check_prefill_token(baseline, comp_ids, comp_logprobs, prompt_idx: int, atol: float) -> None:
"""Token 0 is produced by the prefill pass; both models see identical input,
so the chosen token *must* be the same and its logprob must match within atol."""
base_token_id = baseline["token_ids"][0]
comp_token_id = comp_ids[0]
assert base_token_id == comp_token_id, (
f"Prefill token mismatch at prompt {prompt_idx}: baseline={base_token_id}, compiled={comp_token_id}"
)
base_logprob = baseline["logprobs"][0]
comp_logprob = comp_logprobs[0][comp_token_id]
assert abs(base_logprob - comp_logprob) <= atol, (
f"Prefill logprob mismatch at prompt {prompt_idx}: "
f"baseline={base_logprob:.4f}, compiled={comp_logprob:.4f}, "
f"diff={abs(base_logprob - comp_logprob):.4f} > atol={atol}"
)
def _check_decode_token(baseline, comp_ids, comp_logprobs, token_idx: int, prompt_idx: int, decode_atol: float) -> None:
"""Tokens 1-2 come from decode passes. When the two models pick different
tokens the context has already diverged, so we cannot compare logprobs of
the chosen tokens directly. Instead we do a cross-lookup: find the
baseline's chosen token inside compiled's top-K distribution (and vice
versa) and assert that the assigned log-probability is close. This
confirms that the compiled model's distribution is numerically consistent
with the baseline's even when the argmax differs by a tiny margin.
"""
base_token_id = baseline["token_ids"][token_idx]
comp_token_id = comp_ids[token_idx]
base_logprob = baseline["logprobs"][token_idx]
comp_topk = comp_logprobs[token_idx]
if base_token_id == comp_token_id:
# Happy path: same token, direct logprob comparison.
diff = abs(base_logprob - comp_topk[comp_token_id])
assert diff <= decode_atol, (
f"Decode logprob mismatch at prompt {prompt_idx}, token {token_idx}: "
f"baseline={base_logprob:.4f}, "
f"compiled={comp_topk[comp_token_id]:.4f}, "
f"diff={diff:.4f} > decode_atol={decode_atol}"
)
return
# Tokens differ – cross-lookup in each model's top-K distribution.
comp_logprob = comp_topk[comp_token_id]
# Check: what log-probability did compiled assign to baseline's token?
assert base_token_id in comp_topk, (
f"Decode token mismatch at prompt {prompt_idx}, token {token_idx}: "
f"baseline chose token {base_token_id} (logprob={base_logprob:.4f}) but "
f"compiled chose token {comp_token_id} (logprob={comp_logprob:.4f}) and "
f"baseline's token does not appear in compiled's top-{_SAMPLING_PARAMS.logprobs} distribution"
)
comp_logprob_of_base_token = comp_topk[base_token_id]
diff = abs(base_logprob - comp_logprob_of_base_token)
assert diff <= decode_atol, (
f"Decode distribution mismatch at prompt {prompt_idx}, token {token_idx}: "
f"baseline chose token {base_token_id} with logprob={base_logprob:.4f}; "
f"compiled assigned logprob={comp_logprob_of_base_token:.4f} to that token, "
f"diff={diff:.4f} > decode_atol={decode_atol} "
f"(compiled chose token {comp_token_id} with logprob={comp_logprob:.4f})"
)
def _run_worker_process(
rank: int,
local_rank: int,
world_size: int,
cur_case: dict,
master_ip: str,
master_port: int,
result_queue: multiprocessing.Queue,
metrics: dict[str, Any] | None = None,
):
"""Main entry point for the worker process."""
os.environ.update(
{
"VLLM_DP_RANK": str(rank),
"VLLM_DP_RANK_LOCAL": str(local_rank),
"VLLM_DP_SIZE": str(world_size),
"VLLM_DP_MASTER_IP": master_ip,
"VLLM_DP_MASTER_PORT": str(master_port),
}
)
for key, value in cur_case.get("env_vars", {}).items():
os.environ[key] = str(value)
# Apply hooks and run inference
with _install_spies(metrics):
short_prompts = cur_case["prompts"]["short"]
chunk_size = len(short_prompts) // world_size
short_start_idx = rank * chunk_size
short_end_idx = short_start_idx + chunk_size if rank < world_size - 1 else len(short_prompts)
local_short_prompts = short_prompts[short_start_idx:short_end_idx]
long_prompts = cur_case["prompts"]["long"]
chunk_size = len(long_prompts) // world_size
long_start_idx = rank * chunk_size
long_end_idx = long_start_idx + chunk_size if rank < world_size - 1 else len(long_prompts)
local_long_prompts = long_prompts[long_start_idx:long_end_idx]
llm = LLM(
model=cur_case["model"],
max_model_len=1024,
compilation_config=cur_case["compilation_config"],
quantization=cur_case["quantization"],
tensor_parallel_size=cur_case["tensor_parallel_size"],
enable_expert_parallel=cur_case["enable_expert_parallel"],
trust_remote_code=True,
)
compiled_outputs_short = llm.generate(local_short_prompts, _SAMPLING_PARAMS)
compiled_outputs_long = llm.generate(local_long_prompts, _SAMPLING_PARAMS)
def extract_outputs(outputs):
extracted = []
for out in outputs:
gen = out.outputs[0]
extracted.append(
{
"text": gen.text,
"token_ids": list(gen.token_ids),
"logprobs": [
{token_id: lp.logprob for token_id, lp in step_logprobs.items()}
for step_logprobs in gen.logprobs
]
if gen.logprobs
else None,
}
)
return extracted
result_data = {
"rank": rank,
"short": {"prompt_idx": short_start_idx, "outputs": extract_outputs(compiled_outputs_short)},
"long": {"prompt_idx": long_start_idx, "outputs": extract_outputs(compiled_outputs_long)},
}
result_queue.put(result_data)
def _exit():
from vllm_ascend.ascend_config import clear_ascend_config
clear_ascend_config()
cleanup_dist_env_and_memory()
def check_accuracy(baselines, result, atol, decode_atol):
for idx, comp_out in enumerate(result["outputs"]):
prompt_idx = result["prompt_idx"] + idx
baseline = baselines[prompt_idx]
comp_ids = comp_out["token_ids"]
comp_logprobs = comp_out["logprobs"]
assert comp_logprobs is not None, f"logprobs not returned for prompt {prompt_idx}"
assert len(baseline["token_ids"]) == len(comp_ids) == _SAMPLING_PARAMS.max_tokens, (
f"Expected {_SAMPLING_PARAMS.max_tokens} tokens for prompt {prompt_idx}, "
f"got baseline={len(baseline['token_ids'])}, compiled={len(comp_ids)}"
)
_check_prefill_token(baseline, comp_ids, comp_logprobs, prompt_idx, atol)
for token_idx in range(1, _SAMPLING_PARAMS.max_tokens):
_check_decode_token(baseline, comp_ids, comp_logprobs, token_idx, prompt_idx, decode_atol)
def check_capture_mem(capture_mem, baseline_capture_mem=0.2, capture_mem_tolerance=1.3):
assert capture_mem["call_counts"].value != 0, (
f"capture_model was not called during test. capture_called({capture_mem['call_counts'].value})"
)
print("capture_called =", capture_mem["call_counts"].value)
print("capture_mem_before =", capture_mem["mem_before_capture"].value)
print("capture_mem_after =", capture_mem["mem_after_capture"].value)
mem_used_by_capture = (
capture_mem["mem_before_capture"].value - capture_mem["mem_after_capture"].value
) / capture_mem["call_counts"].value
# Empirical observation: capturing ACL graphs for Qwen3-0.6B uses ~0.20 GiB of NPU memory.
# DeepSeek-V2-Lite-W8A8 uses ~0.68 GiB of NPU memory
# a 1.3x tolerance is applied to account for runtime variance.
max_capture_mem_gib = baseline_capture_mem * capture_mem_tolerance
max_mem_expected = max_capture_mem_gib * (1024**3)
assert mem_used_by_capture < max_mem_expected, (
f"capture_model used more memory than expected. "
f"Used: {mem_used_by_capture / (1024**3):.2f} GiB, "
f"Expected: < {max_capture_mem_gib:.2f} GiB"
)
@wait_until_npu_memory_free(0.7)
@pytest.mark.parametrize("cur_case", [CASE_QWEN_ACLGRAPH, CASE_DS_ACLGRAPH, CASE_DS_ACLGRAPH_ENPU])
def test_aclgraph(cur_case: dict, monkeypatch: pytest.MonkeyPatch):
# Counter doesn't work in default "spawn" mode
metrics = None
if "DeepSeek-V2-Lite-W8A8" in cur_case["model"]:
# TODO(shihan-lin168): remove this env after set_device issue is resolved
monkeypatch.setenv("VLLM_WORKER_MULTIPROC_METHOD", "spawn")
else:
monkeypatch.setenv("OMP_NUM_THREADS", "1")
monkeypatch.delenv("VLLM_WORKER_MULTIPROC_METHOD", raising=False)
metrics = {
"capture_mem": {
# Monitor key metrics of mem
"call_counts": multiprocessing.Value("i", 0),
"mem_before_capture": multiprocessing.Value("q", 0),
"mem_after_capture": multiprocessing.Value("q", 0),
},
}
port = get_open_port()
# Create a queue to collect results from both processes
result_queue: multiprocessing.Queue[dict] = multiprocessing.Queue()
# Launch workers
workers = []
for rank in range(cur_case["data_parallel_size"]):
p = multiprocessing.Process(
target=_run_worker_process,
args=(rank, rank, cur_case["data_parallel_size"], cur_case, "127.0.0.1", port, result_queue, metrics),
)
p.start()
workers.append(p)
all_dp_results = []
# get results
for _ in range(cur_case["data_parallel_size"]):
try:
result = result_queue.get(timeout=180)
all_dp_results.append(result)
except queue.Empty:
print("Error: Timeout waiting for worker results. A worker might have crashed.")
break
# Supervision loop
for p in workers:
p.join(timeout=30)
if p.exitcode != 0:
for k in workers:
if k.is_alive():
k.kill()
p.join(timeout=5)
raise RuntimeError(f"Worker {p.pid} failed with exit code {p.exitcode}")
_exit()
assert len(all_dp_results) == cur_case["data_parallel_size"], f"Expected 2 results, got {len(all_dp_results)}"
# check graph memory
if metrics is not None:
check_capture_mem(metrics["capture_mem"], cur_case["baseline_capture_mem"], cur_case["capture_mem_tolerance"])
# check accuracy
decode_atol = 2 * ATOL
for result in all_dp_results:
check_accuracy(cur_case["golden_answers"]["short"], result["short"], ATOL, decode_atol)
check_accuracy(cur_case["golden_answers"]["long"], result["long"], ATOL, decode_atol)

View File

@@ -0,0 +1,146 @@
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
#
import pytest
from tests.e2e.conftest import DPVllmRunner, VllmRunner, wait_until_npu_memory_free
from tests.e2e.model_utils import check_outputs_equal
DS3 = "deepseek-ai/DeepSeek-V2-Lite-Chat"
MODELS = [
DS3,
]
MOE_MODELS = [
DS3,
]
DATA_PARALLELS = [2]
TENSOR_PARALLELS = [1]
PIPELINE_PARALLELS = [2]
DIST_EXECUTOR_BACKEND = ["mp", "ray"]
prompts = [
"Hello, my name is",
"The future of AI is",
]
GOLDEN = [
(
[
17464,
11,
601,
1210,
317,
459,
6946,
29,
32,
1568,
32092,
535,
6946,
29,
285,
304,
6,
76,
245,
459,
6946,
],
"Hello, my name is <strong>Alessandro</strong> and I'm a <strong",
),
(
[
549,
3680,
280,
20838,
317,
6464,
11,
285,
359,
487,
82,
1872,
276,
330,
245,
2624,
12,
73309,
279,
254,
1843,
],
"The future of AI is bright, and it’s going to be a game-changer in the world",
),
]
@pytest.mark.parametrize("model", MODELS)
@pytest.mark.parametrize("tp_size", TENSOR_PARALLELS)
@pytest.mark.parametrize("pp_size", PIPELINE_PARALLELS)
@pytest.mark.parametrize("distributed_executor_backend", DIST_EXECUTOR_BACKEND)
@wait_until_npu_memory_free(target_free_percentage=0.6)
def test_models_pp2_tp2(model: str, tp_size: int, pp_size: int, distributed_executor_backend: str) -> None:
with VllmRunner(
model,
tensor_parallel_size=tp_size,
pipeline_parallel_size=pp_size,
compilation_config={
"cudagraph_mode": "PIECEWISE",
"cudagraph_capture_sizes": [1, 2, 4],
},
distributed_executor_backend=distributed_executor_backend,
gpu_memory_utilization=0.7,
enable_expert_parallel=model in MOE_MODELS,
) as vllm_model:
outputs = vllm_model.generate_greedy(prompts, 16)
check_outputs_equal(
outputs_0_lst=outputs,
outputs_1_lst=GOLDEN,
name_0=f"{model}-tp{tp_size}pp{pp_size}",
name_1="GOLDEN",
)
@pytest.mark.parametrize("model", MODELS)
@pytest.mark.parametrize("dp_size", DATA_PARALLELS)
@pytest.mark.parametrize("pp_size", PIPELINE_PARALLELS)
@pytest.mark.parametrize("distributed_executor_backend", DIST_EXECUTOR_BACKEND)
@wait_until_npu_memory_free(target_free_percentage=0.6)
def test_models_pp2_dp2(model: str, dp_size: int, pp_size: int, distributed_executor_backend: str) -> None:
with DPVllmRunner(
model,
data_parallel_size=dp_size,
pipeline_parallel_size=pp_size,
compilation_config={
"cudagraph_mode": "PIECEWISE",
"cudagraph_capture_sizes": [1, 2, 4],
},
distributed_executor_backend=distributed_executor_backend,
gpu_memory_utilization=0.7,
enable_expert_parallel=model in MOE_MODELS,
) as vllm_model:
outputs = vllm_model.generate_greedy(prompts, 16)
check_outputs_equal(
outputs_0_lst=outputs,
outputs_1_lst=GOLDEN,
name_0=f"{model}-dp{dp_size}pp{pp_size}",
name_1="GOLDEN",
)

View File

@@ -0,0 +1,97 @@
"""Performance guard for profiling-based dynamic chunk sizing (PP scenario).
Measures Time-To-First-Token (TTFT) on 64k-token prefill requests with
profiling_chunk_config enabled. The test runs against
DeepSeek-V2-Lite-Chat served with PP=2, TP=2 (4 NPU cards total).
Test flow:
1. Create an LLM engine with profiling_chunk_config enabled.
2. Run NUM_WARMUP sequential requests (64k tokens, max_tokens=1) to warm
up both the NPU and the profiling predictor.
3. Run NUM_TEST sequential requests, recording TTFT for each.
4. Assert that the median TTFT does not exceed BASELINE_TTFT_S seconds.
"""
import os
import statistics
import time
from unittest.mock import patch
from tests.e2e.conftest import VllmRunner
MODEL = "Qwen/Qwen3-30B-A3B"
# ~64k tokens
_WORD = "hello "
INPUT_64K_TOKENS = _WORD * (384_000 // len(_WORD))
NUM_WARMUP = 5
NUM_TEST = 5
# NOTE: Any changes to this baseline must be approved by team members.
# Measured on Qwen3-30B-A3B, PP=2, TP=2, 64k prefill, profiling_chunk enabled.
BASELINE_TTFT_S = 5.2
@patch.dict(
os.environ,
{
"VLLM_WORKER_MULTIPROC_METHOD": "spawn",
"VLLM_ALLOW_LONG_MAX_MODEL_LEN": "1",
},
)
def test_profiling_chunk_ttft_performance() -> None:
with VllmRunner(
MODEL,
max_model_len=70000,
tensor_parallel_size=2,
pipeline_parallel_size=2,
block_size=128,
enable_expert_parallel=True,
enable_prefix_caching=False,
gpu_memory_utilization=0.9,
max_num_batched_tokens=12288,
distributed_executor_backend="mp",
enforce_eager=True,
async_scheduling=False,
additional_config={
"profiling_chunk_config": {"enabled": True, "smooth_factor": 0.9},
"enable_cpu_binding": False,
"enable_flashcomm1": True,
},
hf_overrides={
"rope_parameters": {
"rope_type": "yarn",
"rope_theta": 1000,
"factor": 5,
"original_max_position_embeddings": 262144,
}
},
) as vllm_model:
# With max_tokens=1, total latency ≈ prefill time ≈ TTFT
prompts = [INPUT_64K_TOKENS]
# ── Warmup ──────────────────────────────────────────────────────────
for _ in range(NUM_WARMUP):
vllm_model.generate_greedy(prompts, max_tokens=1)
# ── Measurement ─────────────────────────────────────────────────────
ttfts: list[float] = []
for _ in range(NUM_TEST):
start = time.perf_counter()
vllm_model.generate_greedy(prompts, max_tokens=1)
ttfts.append(time.perf_counter() - start)
median_ttft = statistics.median(ttfts)
ttft_str = ", ".join(f"{t:.2f}s" for t in ttfts)
print(
f"\n[profiling_chunk perf] TTFT per request: [{ttft_str}]"
f"\n[profiling_chunk perf] Median TTFT: {median_ttft:.2f}s "
f"(baseline: {BASELINE_TTFT_S}s)"
)
assert median_ttft <= BASELINE_TTFT_S, (
f"TTFT performance regression: median TTFT {median_ttft:.2f}s "
f"exceeds baseline {BASELINE_TTFT_S}s. "
f"Individual TTFTs: [{ttft_str}]"
)

View File

@@ -0,0 +1,70 @@
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
# Adapted from vllm/tests/basic_correctness/test_basic_correctness.py
#
import os
from unittest.mock import patch
from tests.e2e.conftest import DPVllmRunner, VllmRunner
def test_qwen3_5_27b_distributed_mp_tp4():
example_prompts = [
"Hello, my name is",
] * 4
max_tokens = 5
with VllmRunner(
"Qwen/Qwen3.5-27B",
tensor_parallel_size=4,
cudagraph_capture_sizes=[1, 2, 4, 8],
max_model_len=4096,
gpu_memory_utilization=0.90,
distributed_executor_backend="mp",
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)
del vllm_model
@patch.dict(os.environ, {"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1"})
def test_qwen3_5_35b_distributed_mp_tp4_full_decode_only_mtp3_flashcomm():
example_prompts = [
"Hello, my name is",
"The president of the United States is",
"The capital of France is",
"The future of AI is",
]
max_tokens = 20
with DPVllmRunner(
"Qwen/Qwen3.5-35B-A3B",
data_parallel_size=2,
tensor_parallel_size=2,
enable_expert_parallel=True,
max_model_len=4096,
gpu_memory_utilization=0.90,
distributed_executor_backend="mp",
compilation_config={
"cudagraph_mode": "FULL_DECODE_ONLY",
"cudagraph_capture_sizes": [4, 8, 12, 16],
},
speculative_config={
"method": "qwen3_5_mtp",
"num_speculative_tokens": 3,
},
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)
del vllm_model

View File

@@ -0,0 +1,80 @@
#
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
# Copyright 2023 The vLLM team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
# Adapted from vllm/tests/basic_correctness/test_basic_correctness.py
#
import os
from unittest.mock import patch
from tests.e2e.conftest import VllmRunner
def test_qwen3_next_distributed_mp_full_decode_only_tp4():
example_prompts = [
"Hello, my name is",
] * 4
max_tokens = 5
with VllmRunner(
"Qwen/Qwen3-Next-80B-A3B-Instruct",
tensor_parallel_size=4,
max_model_len=4096,
gpu_memory_utilization=0.8,
distributed_executor_backend="mp",
compilation_config={"cudagraph_mode": "FULL_DECODE_ONLY", "cudagraph_capture_sizes": [1, 2, 4]},
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)
del vllm_model
@patch.dict(os.environ, {"VLLM_ASCEND_ENABLE_FLASHCOMM1": "1"})
@patch.dict(os.environ, {"HCCL_BUFFSIZE": "1024"})
def test_qwen3_next_w8a8dynamic_distributed_mp_flash_comm_tp4():
example_prompts = [
"Hello, my name is",
] * 4
max_tokens = 5
with VllmRunner(
"vllm-ascend/Qwen3-Next-80B-A3B-Instruct-W8A8",
tensor_parallel_size=4,
max_model_len=4096,
gpu_memory_utilization=0.7,
distributed_executor_backend="mp",
enable_expert_parallel=True,
enforce_eager=True,
quantization="ascend",
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)
del vllm_model
@patch.dict(os.environ, {"HCCL_BUFFSIZE": "1024"})
def test_qwen3_next_distributed_mp_graph_mode_tp4():
example_prompts = [
"Hello, my name is",
] * 4
max_tokens = 5
with VllmRunner(
"Qwen/Qwen3-Next-80B-A3B-Instruct",
tensor_parallel_size=4,
max_model_len=4096,
gpu_memory_utilization=0.8,
distributed_executor_backend="mp",
enable_expert_parallel=True,
cudagraph_capture_sizes=[1, 2, 4],
enforce_eager=False,
) as vllm_model:
vllm_model.generate_greedy(example_prompts, max_tokens)
del vllm_model