Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d4d0f73
Support Qwen3.8-Flash-Next
peakcrosser7 Aug 26, 2026
02f2b4c
Merge branch 'main' into release/qwen38next
peakcrosser7 Aug 26, 2026
da1d8c2
fix tests of preprocess_mamba
peakcrosser7 Aug 26, 2026
52a8bc1
fix pre-commit error of block_fp8
peakcrosser7 Aug 26, 2026
d7f50c5
fix interface of qsa
peakcrosser7 Aug 26, 2026
c92e9d6
fix weight loader mappings
peakcrosser7 Aug 26, 2026
060089f
fix qsa kv-cache layout
peakcrosser7 Aug 26, 2026
ea5b457
fix PLE profile warmup
peakcrosser7 Aug 27, 2026
d9f64bd
ad CI for Qwen3.8-Flash-Next-Fp8
peakcrosser7 Aug 27, 2026
a6bbc58
add get_mamba_state_copy_funcs interface
peakcrosser7 Aug 27, 2026
3824eb3
fix tests
peakcrosser7 Aug 27, 2026
d69520d
Merge branch 'main' into release/qwen38next
peakcrosser7 Aug 27, 2026
234d1c6
fix _populate_metadata
peakcrosser7 Aug 27, 2026
2a4cd64
fix csa-linear packed tensor stride
peakcrosser7 Aug 27, 2026
bd182cc
[ROCm] Support Qwen3.8 Quark MXFP4 TP and TEP
andyluo7 Aug 27, 2026
9477556
[Bugfix] Fix NVFP4 MoE gate-up padding
Aug 27, 2026
8226242
Merge pull request #7 from andyluo7/codex/qwen38-mxfp4-gfx950
peakcrosser7 Aug 28, 2026
42d7490
Merge pull request #8 from lishunyang12/fix-qwen38-nvfp4-tp4
peakcrosser7 Aug 28, 2026
71360c2
Merge branch 'main' into release/qwen38next
peakcrosser7 Aug 28, 2026
8f931ef
Merge remote-tracking branch 'hhy/release/qwen38next' into release/qw…
peakcrosser7 Aug 28, 2026
e52da75
Update vllm/models/qwen4_exp/nvidia/ple_layer.py
peakcrosser7 Aug 28, 2026
d5999fb
revert mamba_get_block_table_tensor_triton
gau-nernst Aug 28, 2026
e858648
Merge branch 'main' into release/qwen38next
peakcrosser7 Aug 28, 2026
23fdcf2
Remove FP8 MoE block-scale refinement (moved to fp8-moe-block-scale-r…
Isotr0py Aug 28, 2026
0a42d22
Revert "Remove FP8 MoE block-scale refinement (moved to fp8-moe-block…
Isotr0py Aug 28, 2026
052f406
fix import
peakcrosser7 Aug 28, 2026
1c609b0
address fp8 comments
Isotr0py Aug 28, 2026
265a68c
clean up
peakcrosser7 Aug 28, 2026
98a05a9
[BugFix] fix PLE n-gram IDs for piecewise CUDA graphs
peakcrosser7 Aug 28, 2026
85a3e47
ad CI for qwen4exp
peakcrosser7 Aug 28, 2026
2e231aa
update CI
peakcrosser7 Aug 28, 2026
948ea3c
rm unnecessary tests
peakcrosser7 Aug 28, 2026
4701f0d
refactor NGram helpers
peakcrosser7 Aug 29, 2026
47f8a81
Merge branch 'main' into release/qwen38next
peakcrosser7 Aug 29, 2026
183915b
fix ci
peakcrosser7 Aug 30, 2026
adc200a
Simplify packed KV cache grouping
LucasWilkinson Aug 27, 2026
8527347
[KV cache] Give TP-replicated and odd-sized Mamba states their own group
ivanium Aug 30, 2026
78f86b5
[KV Connector][NIXL] Unwrap UniformTypeKVCacheSpecs group specs
ivanium Aug 30, 2026
d40be4a
[KV Connector][NIXL] Track scratch-cache regions explicitly
ivanium Aug 30, 2026
20e720d
[Bugfix][KV cache] Skip block truncation for non-prefix-cacheable groups
ivanium Aug 30, 2026
cfd0261
[Bugfix][Qwen4Exp] Fix pipeline parallelism with and without PLE
ivanium Aug 30, 2026
a61864d
Merge branch 'main' into release/qwen38next
ZJY0516 Aug 30, 2026
bdb03ff
Limit Qwen3.8-Flash-Next to model runner V2
ZJY0516 Aug 30, 2026
b5472f1
Remove Qwen3.8-specific MTP proposer
ZJY0516 Aug 30, 2026
1eb66d0
Clarify Qwen4Exp Mamba page sizing
ZJY0516 Aug 30, 2026
d794cd5
Merge branch 'main' into release/qwen38next
ZJY0516 Aug 30, 2026
6f8a507
Fix hybrid cache regressions across model runners
ZJY0516 Aug 30, 2026
4ba528c
Keep hidden-state cache separate in packed grouping
ivanium Aug 30, 2026
90e6e23
Route PLE shard loads through parameter loaders
ivanium Aug 30, 2026
c279484
Merge branch 'main' into release/qwen38next
ivanium Aug 31, 2026
b55fb55
Merge branch 'main' into release/qwen38next
ZJY0516 Aug 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .buildkite/test_areas/lm_eval.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,42 @@ steps:
- uv pip install --system 'gpt-oss[eval]==0.0.5'
- pytest -s -v evals/gpt_oss/test_gpqa_correctness.py --config-list-file=configs/models-b200.txt

- label: ":nvidia: (B200) Qwen3.8-Flash-Next-FP8 Accuracy Eval"
key: accuracy-eval-qwen3-8-flash-next-fp8-b200
timeout_in_minutes: 60
device: b200-k8s
optional: true
num_devices: 4
source_file_dependencies:
- csrc/libtorch_stable/gdn/
- tests/evals/qwen4_exp/
- vllm/model_executor/layers/mamba/
- vllm/models/qwen4_exp/
- vllm/transformers_utils/configs/qwen4_exp.py
- vllm/v1/attention/backends/short_conv_attn.py
- vllm/v1/spec_decode/qwen4_exp.py
commands:
- uv pip install --system 'evalscope==1.10.0'
- pytest -s -v evals/qwen4_exp/test_accuracy.py --config-list-file=configs/models-b200.txt

- label: ":nvidia: (H200) Qwen3.8-Flash-Next-FP8 Accuracy Eval"
key: accuracy-eval-qwen3-8-flash-next-fp8-h200
timeout_in_minutes: 60
device: h200
optional: true
num_devices: 4
source_file_dependencies:
- csrc/libtorch_stable/gdn/
- tests/evals/qwen4_exp/
- vllm/model_executor/layers/mamba/
- vllm/models/qwen4_exp/
- vllm/transformers_utils/configs/qwen4_exp.py
- vllm/v1/attention/backends/short_conv_attn.py
- vllm/v1/spec_decode/qwen4_exp.py
commands:
- uv pip install --system 'evalscope==1.10.0'
- pytest -s -v evals/qwen4_exp/test_accuracy.py --config-list-file=configs/models-h200.txt

- label: ":nvidia: (DGX) Spark GPQA Eval (GPT-OSS)"
key: gpqa-eval-gpt-oss-spark
timeout_in_minutes: 35
Expand Down
37 changes: 37 additions & 0 deletions .buildkite/test_areas/models_basic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,43 @@ steps:
# The native NVIDIA Kimi K3 kernels require the SM100 family.
- pytest -v -s models/kimi_k3 kernels/attention/test_kimi_k3_mla_fused_epilogue.py kernels/test_bf16_skinny_gemm.py

- label: ":nvidia: (H200) Qwen4 Exp"
key: qwen4-exp-unit-tests
timeout_in_minutes: 40
device: h200_35gb
source_file_dependencies:
- vllm/models/qwen4_exp/
- vllm/transformers_utils/configs/qwen4_exp.py
- vllm/config/compilation.py
- vllm/v1/attention/backends/short_conv_attn.py
- tests/models/qwen4_exp/
commands:
- pytest -v -s models/qwen4_exp/test_hc_ops.py models/qwen4_exp/test_qsa_pre_indexer.py models/qwen4_exp/test_qsa_reference.py
mirror:
amd:
label: ":amd: (MI300) Qwen4 Exp"
dind: false
device: mi300_1
depends_on:
- image-build-amd
commands:
- pytest -v -s models/qwen4_exp/test_qsa_amd.py

- label: ":computer: (CPU) Qwen4 Exp"
key: qwen4-exp-unit-tests-cpu
depends_on:
- image-build-cpu
timeout_in_minutes: 20
device: cpu-small
source_file_dependencies:
- vllm/models/qwen4_exp/
- vllm/transformers_utils/configs/qwen4_exp.py
- vllm/config/compilation.py
- vllm/v1/attention/backends/short_conv_attn.py
- tests/models/qwen4_exp/
commands:
- pytest -v -s models/qwen4_exp/test_config.py models/qwen4_exp/test_ple.py

- label: ":computer: (CPU) Basic Models Other"
key: basic-models-test-other-cpu
depends_on:
Expand Down
1 change: 1 addition & 0 deletions .buildkite/test_areas/spec_decode.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ steps:
- vllm/distributed/
- vllm/inputs/
- vllm/model_executor/
- vllm/models/qwen4_exp/
- vllm/platforms/
- vllm/sampling_params.py
- vllm/transformers_utils/
Expand Down
36 changes: 26 additions & 10 deletions csrc/libtorch_stable/gdn/fused_gdn_decode_kernel.cu
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

#include <cstdint>
#include <string>
#include <cuda_bf16.h>
#include <cuda_fp16.h>
#include <cuda_runtime.h>
Expand Down Expand Up @@ -146,7 +147,7 @@ __device__ __forceinline__ Sum2 warp_reduce_sum_pair(float x, float y) {
return {x, y};
}

template <typename StateT, int ValueHeadsPerKeyHead>
template <typename StateT, int ValueHeadsPerKeyHead, bool SigmoidGate>
__global__ __launch_bounds__(kThreads, 2) void gdn_decode_post_conv_mtp_kernel(
const __nv_bfloat16* __restrict__ mixed_qkv,
const __nv_bfloat16* __restrict__ a, const __nv_bfloat16* __restrict__ b,
Expand Down Expand Up @@ -354,9 +355,11 @@ __global__ __launch_bounds__(kThreads, 2) void gdn_decode_post_conv_mtp_kernel(
#pragma unroll
for (int i = 0; i < 4; ++i) {
const int value = lane + i * 32;
const float gate = silu_fast(__bfloat162float(
const float gate_input = __bfloat162float(
output_gate[static_cast<int64_t>(token) * strides.gate_row +
value_head * kDimV + value]));
value_head * kDimV + value]);
const float gate =
SigmoidGate ? sigmoid_fast(gate_input) : silu_fast(gate_input);
const float weight =
norm_weight_is_bf16
? __bfloat162float(
Expand All @@ -370,7 +373,7 @@ __global__ __launch_bounds__(kThreads, 2) void gdn_decode_post_conv_mtp_kernel(
}
}

template <typename StateT, int ValueHeadsPerKeyHead>
template <typename StateT, int ValueHeadsPerKeyHead, bool SigmoidGate>
void launch_gdn_decode_post_conv_mtp(
torch::stable::Tensor const& mixed_qkv, torch::stable::Tensor const& a_log,
torch::stable::Tensor const& dt_bias,
Expand All @@ -395,7 +398,7 @@ void launch_gdn_decode_post_conv_mtp(
get_current_cuda_stream(mixed_qkv.get_device_index());
const int num_requests = static_cast<int>(state_indices.size(0));
const dim3 grid(num_requests, num_value_heads);
gdn_decode_post_conv_mtp_kernel<StateT, ValueHeadsPerKeyHead>
gdn_decode_post_conv_mtp_kernel<StateT, ValueHeadsPerKeyHead, SigmoidGate>
<<<grid, kThreads, 0, stream>>>(
static_cast<const __nv_bfloat16*>(mixed_qkv.data_ptr()), a, b,
static_cast<const float*>(a_log.data_ptr()), dt_bias.data_ptr(),
Expand Down Expand Up @@ -425,7 +428,7 @@ void fused_gdn_decode_post_conv_mtp(
torch::stable::Tensor const& num_accepted_tokens,
torch::stable::Tensor& state, torch::stable::Tensor const& output_gate,
torch::stable::Tensor const& norm_weight, torch::stable::Tensor& out,
double scale, double norm_eps) {
double scale, double norm_eps, const std::string& output_gate_activation) {
using torch::headeronly::ScalarType;

STD_TORCH_CHECK(
Expand Down Expand Up @@ -466,6 +469,9 @@ void fused_gdn_decode_post_conv_mtp(
"norm_weight must be a CUDA float32 or bfloat16 tensor");
STD_TORCH_CHECK(out.is_cuda() && out.scalar_type() == ScalarType::BFloat16,
"out must be a CUDA bfloat16 tensor");
STD_TORCH_CHECK(
output_gate_activation == "silu" || output_gate_activation == "sigmoid",
"output_gate_activation must be 'silu' or 'sigmoid'");

STD_TORCH_CHECK(mixed_qkv.dim() == 2,
"mixed_qkv must have shape [L, 2 * H * 128 + HV * 128]");
Expand Down Expand Up @@ -547,18 +553,28 @@ void fused_gdn_decode_post_conv_mtp(
const auto* b_ptr = static_cast<const __nv_bfloat16*>(b.data_ptr());
const auto* output_gate_ptr =
static_cast<const __nv_bfloat16*>(output_gate.data_ptr());
const auto launch = [&]<typename StateT, int ValueHeadsPerKeyHead>() {
launch_gdn_decode_post_conv_mtp<StateT, ValueHeadsPerKeyHead>(
const auto launch = [&]<typename StateT, int ValueHeadsPerKeyHead,
bool SigmoidGate>() {
launch_gdn_decode_post_conv_mtp<StateT, ValueHeadsPerKeyHead, SigmoidGate>(
mixed_qkv, a_log, dt_bias, state_indices, cu_seqlens,
num_accepted_tokens, state, norm_weight, out, a_ptr, b_ptr,
output_gate_ptr, num_key_heads, num_value_heads, scale, norm_eps,
strides);
};
const auto dispatch_state_type = [&]<int ValueHeadsPerKeyHead>() {
if (state_scalar_type == ScalarType::Float) {
launch.template operator()<float, ValueHeadsPerKeyHead>();
if (output_gate_activation == "sigmoid") {
launch.template operator()<float, ValueHeadsPerKeyHead, true>();
} else {
launch.template operator()<float, ValueHeadsPerKeyHead, false>();
}
} else {
launch.template operator()<__nv_bfloat16, ValueHeadsPerKeyHead>();
if (output_gate_activation == "sigmoid") {
launch.template operator()<__nv_bfloat16, ValueHeadsPerKeyHead, true>();
} else {
launch
.template operator()<__nv_bfloat16, ValueHeadsPerKeyHead, false>();
}
}
};
switch (value_heads_per_key_head) {
Expand Down
2 changes: 1 addition & 1 deletion csrc/libtorch_stable/ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ void fused_gdn_decode_post_conv_mtp(
torch::stable::Tensor const& num_accepted_tokens,
torch::stable::Tensor& state, torch::stable::Tensor const& output_gate,
torch::stable::Tensor const& norm_weight, torch::stable::Tensor& out,
double scale, double norm_eps);
double scale, double norm_eps, const std::string& output_gate_activation);

#endif

Expand Down
3 changes: 2 additions & 1 deletion csrc/libtorch_stable/torch_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@ STABLE_TORCH_LIBRARY_FRAGMENT(_C, ops) {
"Tensor mixed_qkv, Tensor a, Tensor b, Tensor A_log, Tensor dt_bias, "
"Tensor state_indices, Tensor cu_seqlens, Tensor num_accepted_tokens, "
"Tensor! state, Tensor output_gate, Tensor norm_weight, Tensor! out, "
"float scale, float norm_eps=1e-5) -> ()");
"float scale, float norm_eps=1e-5, "
"str output_gate_activation='silu') -> ()");
#endif

#ifdef VLLM_ENABLE_KIMI_K3_ATTN_RES
Expand Down
19 changes: 19 additions & 0 deletions tests/config/test_config_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pytest

from vllm.config.cache import CacheConfig
from vllm.config.scheduler import SchedulerConfig
from vllm.config.utils import get_hash_factors, hash_factors, normalize_value

# Helpers
Expand Down Expand Up @@ -216,6 +217,24 @@ def test_cache_config_hash_ignores_kv_cache_sizing_knobs():
assert CacheConfig(gpu_memory_utilization=0.5).compute_hash() == base_hash


def test_scheduler_config_hash_includes_max_num_seqs():
"""Per-request workspace sizes must invalidate compiled graphs."""
base_hash = SchedulerConfig(
max_model_len=8192,
is_encoder_decoder=False,
max_num_batched_tokens=8192,
max_num_seqs=128,
).compute_hash()
larger_batch_hash = SchedulerConfig(
max_model_len=8192,
is_encoder_decoder=False,
max_num_batched_tokens=8192,
max_num_seqs=1024,
).compute_hash()

assert larger_batch_hash != base_hash


def test_cache_config_hash_ignores_prefix_cache_retention_interval():
base_hash = CacheConfig().compute_hash()
assert CacheConfig(prefix_cache_retention_interval=64).compute_hash() == base_hash
Expand Down
53 changes: 52 additions & 1 deletion tests/config/test_speculative_draft_hf_overrides.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Tests for SpeculativeConfig.compose_draft_hf_overrides.
"""Tests for draft config overrides used by SpeculativeConfig.

Callable ``hf_overrides`` on the target model config (e.g. the
``dummy_hf_overrides`` shrink used by ``tests/models/test_initialization.py``)
Expand All @@ -12,10 +12,12 @@
"""

import functools
from unittest.mock import MagicMock, patch

import pytest
from transformers import PretrainedConfig

from vllm.config.parallel import ParallelConfig
from vllm.config.speculative import SpeculativeConfig


Expand Down Expand Up @@ -134,3 +136,52 @@ def test_composed_override_is_picklable():

out = composed(_make_hf_config())
assert out.num_hidden_layers == 1


def _make_mtp_speculative_config(
override: bool | None,
checkpoint_value: bool,
) -> SpeculativeConfig:
draft_hf_config = _make_hf_config(
architectures=["Qwen4ExpMTP"],
model_type="qwen4_exp_mtp",
n_predict=1,
index_share_for_mtp_iteration=checkpoint_value,
)
draft_model_config = MagicMock(
model="draft",
hf_config=draft_hf_config,
architectures=draft_hf_config.architectures,
max_model_len=128,
)
target_model_config = MagicMock(
model="target",
max_model_len=128,
quantization=None,
hf_overrides={},
)

with patch("vllm.config.speculative.ModelConfig", return_value=draft_model_config):
return SpeculativeConfig(
model="draft",
method="mtp",
num_speculative_tokens=1,
index_share_for_mtp_iteration=override,
target_model_config=target_model_config,
target_parallel_config=ParallelConfig(),
)


@pytest.mark.cpu_test
@pytest.mark.parametrize(
("override", "checkpoint_value", "expected"),
[(None, True, True), (False, True, False), (True, False, True)],
)
def test_mtp_index_share_override(
override: bool | None, checkpoint_value: bool, expected: bool
):
speculative_config = _make_mtp_speculative_config(override, checkpoint_value)
assert (
speculative_config.draft_model_config.hf_config.index_share_for_mtp_iteration
is expected
)
23 changes: 23 additions & 0 deletions tests/distributed/test_custom_all_reduce.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@
test_sizes[i] -= v % 8


@pytest.mark.parametrize(
("dtype", "expected"),
[
(torch.float32, True),
(torch.float16, True),
(torch.bfloat16, True),
(torch.int8, False),
(torch.float8_e4m3fn, False),
],
)
def test_custom_allreduce_filters_dtype(
dtype: torch.dtype,
expected: bool,
) -> None:
communicator = car.CustomAllreduce.__new__(car.CustomAllreduce)
communicator.disabled = False
communicator.world_size = 2
communicator.max_size = 1024
communicator._ptr = 0

assert communicator.should_custom_ar(torch.empty(16, dtype=dtype)) is expected


@pytest.mark.parametrize(
("major", "local_multicast", "expected"),
[
Expand Down
14 changes: 14 additions & 0 deletions tests/evals/qwen4_exp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Qwen4Exp accuracy evaluation

This suite starts a Qwen3.8-Flash-Next-FP8 OpenAI-compatible server once and
uses EvalScope to evaluate GSM8K and AIME25.

```bash
# B200
pytest -s -v tests/evals/qwen4_exp/test_accuracy.py \
--config-list-file=configs/models-b200.txt

# H200
pytest -s -v tests/evals/qwen4_exp/test_accuracy.py \
--config-list-file=configs/models-h200.txt
```
3 changes: 3 additions & 0 deletions tests/evals/qwen4_exp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project

26 changes: 26 additions & 0 deletions tests/evals/qwen4_exp/configs/Qwen3.8-Flash-Next-FP8.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
model_name: "Qwen/Qwen3.8-Flash-Next-FP8"
datasets:
gsm8k:
metric_threshold: 0.98
tolerance: 0.02
aime25:
metric_threshold: 0.90
tolerance: 0.05
eval_batch_size: 32
startup_max_wait_seconds: 1800
generation_config:
seed: 1236
do_sample: true
temperature: 0.7
top_p: 0.8
top_k: 20
repetition_penalty: 1.0
presence_penalty: 1.5
max_tokens: 32768
stream: true
extra_body:
chat_template_kwargs:
enable_thinking: true
server_args: "--tensor-parallel-size 4"
Loading
Loading