Skip to content

[ROCm][Perf][MiniMax-M3.1] Four-wave decode top-k for the NVFP4 indexer - #65087

Merged
shen-shanshan merged 3 commits into
vllm-project:mainfrom
chao-xu-spec:m3-index-topk-fourwave
Nov 4, 2026
Merged

shen-shanshan merged 3 commits into
vllm-project:mainfrom
chao-xu-spec:m3-index-topk-fourwave

Conversation

@chao-xu-spec

@chao-xu-spec chao-xu-spec commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Overview

On gfx950, the MiniMax-M3.1 NVFP4 indexer picks the 16 best blocks of every decode row with one wave per row. This PR gives each decode row four waves and merges their top-16 lists exactly, so the ordered block ids are unchanged. No other kernel, flag or call site changes.

Claims

  • Decode top-k is 4.1x faster over the decode shapes of a served MiniMax-M3.1 AgentX run (18.9 -> 4.65 us per call, weighted by how often each shape occurs, real index scores); 4.07x in the live server (21.2 -> 5.2 us per call). With random scores the same shapes give 1.26x: the old kernel is much slower on real scores (see Details).
  • End to end (TP4, 2 runs per arm): c16 +1.34% and c32 +2.47% tok/s/GPU, TPOT p50 -12.2% / -12.0%, p90 -8.3% / -8.2% (interactivity p50 +13.9% / +13.6%, p90 +9.0% / +8.9%).
  • Same output as before: bit-for-bit equal to AITER's pa_sparse_block_topk and to the old kernel in 4,500 sweep cases (772,650 rows); eager and CUDA-graph replay agree. In a replay of the real served calls 1 of 2,816 decode rows differs, in a call with duplicate page ids where the old kernel's own output changes from run to run. GSM8K and spec-decode accept length are unchanged.
  • Costs: With random scores a call is up to 12% slower when every row is under about 213k tokens; with real scores that only shows for calls in which no row triggers the old kernel's slow path. See Limits.

Validation

Microbench. MI355X (gfx950), ROCm 10.0.0, torch 2.13.0+rocm10.0.0, vllm/vllm-openai-rocm:minimax-m31-flash with the two top-k files of this PR on top of main (7b77ed5; the files are unchanged on main at 76c6f3f, where the test file was re-run); stock = main's files. CUDA-graph replay of 16 back-to-back calls on distinct score buffers, 1 GiB memset (cold L2) before every replay, median of 20 replays per process, 6 fresh processes per arm; per-call us, median of the 6 (process-to-process range at most 0.07 us ours, 0.21 us stock). 1 index head, 4 rows per request, output equal in every process. Scores are the real output of the unchanged scorer on captured queries and index-K pages of a served run (grid cells: re-scored captured requests clipped to the cell's lengths; live lengths uniform in [max/2, max]). Rows = requests, columns = max sequence length (graph width). Not a repo benchmark (none exists for this op); the method is in the details block.

Stock / ours, us (speedup), real scores:

Requests 64k 128k 256k 512k
1 10.5 / 4.00 (2.6x) 11.4 / 4.00 (2.9x) 12.2 / 4.44 (2.8x) 20.8 / 4.71 (4.4x)
2 8.7 / 4.05 (2.1x) 15.0 / 4.05 (3.7x) 18.5 / 4.51 (4.1x) 20.6 / 4.64 (4.4x)
4 16.5 / 4.10 (4.0x) 17.4 / 4.10 (4.3x) 18.3 / 4.54 (4.0x) 35.3 / 4.68 (7.6x)
8 18.3 / 4.11 (4.4x) 18.3 / 4.12 (4.4x) 20.4 / 4.64 (4.4x) 35.9 / 4.74 (7.6x)
16 18.3 / 4.15 (4.4x) 18.4 / 4.23 (4.3x) 20.1 / 4.69 (4.3x) 36.5 / 4.86 (7.5x)
32 18.4 / 4.23 (4.3x) 18.5 / 4.34 (4.3x) 20.6 / 4.84 (4.2x) 36.8 / 5.18 (7.1x)
64 18.5 / 4.35 (4.2x) 18.6 / 4.45 (4.2x) 20.7 / 5.16 (4.0x) 36.8 / 5.83 (6.3x)
100 18.6 / 4.77 (3.9x) 18.8 / 4.95 (3.8x) 21.0 / 5.81 (3.6x) 37.4 / 6.90 (5.4x)

Served shapes (20 decode graph calls of a TP4 AgentX c16 run: real lengths, padded batch, max length 1M, 1-14 requests), weighted by call count: stock 18.91 us (18.91-18.92), ours 4.65 us (4.65-4.66) per call, 4.06x (per process 4.06-4.07x); per call 1.04x to 7.4x. The same shapes with random scores: 5.79 / 4.61 us, 1.255x (table and per-call numbers in the details block).

E2E. amd/MiniMax-M3.1-Flash-Quark-MXFP4 (MXFP4 experts, MXFP8 dense linears), TP4 EP1 on 4x MI355X, nvfp4 KV and indexer cache, DSpark with 3 draft tokens (synthetic acceptance 2.88), DBO, max-model-len 1M, --max-num-seqs 32 (c16) / 64 (c32). CI image plus an overlay of the two changed files (the four files of the decode top-k and its scorer are byte-identical to main's); stock = the image as is. InferenceX AgentX replay (agentx-harness 1.0.12, cc-traces-weka-062126), 3600 s per run, the CI client flags; 2 runs per arm in ABBA order (stock, ours, ours, stock), server restarted per run; ranges are min-max of the 2 runs. Noise floor = the larger of the stock-vs-stock range and the spread of 5 (c16) / 2 (c32) earlier stock runs; a change counts if it exceeds 2x the floor, has the same sign in both adjacent ABBA pairs and the runs do not overlap.

Concurrency Stock (tok/s/GPU) Ours (tok/s/GPU) Change Stock TPOT p50 / p90 (ms) Ours TPOT p50 / p90 (ms) Change p50 / p90 Noise floor (tok/s)
16 168.70 (168.48-168.91) 170.95 (170.92-170.98) +1.34% 5.285 (5.270-5.300) / 7.295 (7.290-7.300) 4.640 (4.630-4.650) / 6.690 (6.650-6.730) -12.2% / -8.3% +-0.26%
32 304.06 (304.03-304.08) 311.57 (311.56-311.57) +2.47% 7.390 (7.390-7.390) / 10.535 (10.520-10.550) 6.505 (6.490-6.520) / 9.675 (9.600-9.750) -12.0% / -8.2% +-0.09%
Concurrency Metric Stock Ours Change
16 interactivity p50 / p90 189.2 (188.6-189.8) / 137.08 (136.94-137.23) 215.5 (215.2-215.8) / 149.46 (148.49-150.44) +13.9% / +9.0%
32 interactivity p50 / p90 135.3 (135.3-135.4) / 94.93 (94.80-95.07) 153.7 (153.4-154.1) / 103.36 (102.61-104.12) +13.6% / +8.9%

Throughput, TPOT and interactivity gains (p50 and p90) are above the floor at both concurrencies (both ours runs above both stock runs). Mean output length is 975 (c16) / 1027 (c32); tok/s per mean output length gives +1.48% / +2.04%. In a kernel trace of the c16 server (profiler on, 12 windows of 64 steps per arm, rank 0) the decode top-k takes 21.2 us per call stock and 5.2 us ours (60 calls per decode step, 1.27 -> 0.31 ms of an 18 ms step); the scorer is unchanged (17.0 us per call at matched context in both). Dispatch: on all 4 ranks the ours runs launch m3_index_topk_decode_indices_w16 and never _w26; the stock runs launch only _w26.

Accuracy. GSM8K, 1319 questions, 5-shot, temperature 0, chat completions, max_tokens 12288, concurrency 64; CI conc-64 recipe with real verification (block rejection sampling; the synthetic acceptance of the benchmark recipe degrades text). 2 servers per arm in ABBA order. vLLM's tests/evals/gsm8k/gsm8k_eval.py (2 passes per server, 4 values per arm) and the CI harness (lm-eval, the CI-pinned version, 1 pass per server).

Stock Ours
Accuracy, tests/evals/gsm8k 0.9689 (0.9674-0.9697) 0.9687 (0.9666-0.9704)
Accuracy, lm-eval strict / flexible 0.9644 / 0.9636 (0.9629-0.9659) 0.9659 / 0.9651 (0.9651-0.9666)
Accept length (spec decode) 3.116 (3.114-3.119) 3.123 (3.114-3.132)

The CI published 0.9666 strict for this recipe. Long-context check (my own script, since GSM8K stays under 131k tokens and never reaches the multi-wave path): a 5-digit key at a given depth in unique filler, 147k-971k prompt tokens, 98 of 98 correct on both arms.

Exactness and tests (kernel level, MI355X):

Check Stock Ours
Repo test file (decode and prefill, 9 score kinds, ordered ids vs AITER; this PR adds 3 decode shapes: 100 requests, and the two lengths where waves meet) 221 passed 221 passed
Sweep: 1-100 requests x 9 kinds x 5 max lengths, 4,500 cases, 772,650 rows; differing rows vs AITER 0 0
Sweep: output of ours vs stock identical in all 4,500 cases
Replay of the 41 captured served calls with captured and 3 random value sets (2,816 decode rows) 1 differing row, in a call with duplicate page ids whose stock output also changes from run to run (1 of 8 runs)
Eager vs CUDA-graph replay, in-place new lengths, 66 cases 0 mismatches 0 mismatches
Reproduce and more microbench numbers
# tests (MI355X)
python -m pytest tests/kernels/attention/test_minimax_m3_index_topk_decode.py \
    tests/kernels/attention/test_minimax_m3_nvfp4_index.py
# server (c16; c32 uses --max-num-seqs 64; the GSM8K runs use --max-num-seqs 128 and "rejection_sample_method":"block")
vllm serve amd/MiniMax-M3.1-Flash-Quark-MXFP4 --tensor-parallel-size 4 --attention-config '{"indexer_kv_dtype":"nvfp4","indexer_decode_cp":true}' \
  --block-size 128 --kv-cache-dtype nvfp4 --enable-dbo --dbo-decode-token-threshold 32768 --dbo-prefill-token-threshold 16384 \
  --max-model-len 1048576 --max-num-batched-tokens 32768 --max-num-seqs 32 --moe-backend aiter --enable-prefix-caching \
  --disable-hybrid-kv-cache-manager --reasoning-parser minimax_m3 --tool-call-parser minimax_m3 --enable-auto-tool-choice \
  --language-model-only --trust-remote-code --gpu-memory-utilization 0.9 \
  --speculative-config '{"method":"dspark","num_speculative_tokens":3,"attention_backend":"TRITON_ATTN","kv_cache_dtype":"fp8","draft_sample_method":"probabilistic","rejection_sample_method":"synthetic","synthetic_acceptance_length":2.88}'
# env: VLLM_ROCM_USE_AITER=1 VLLM_ROCM_USE_AITER_MOE=1 VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS=0 VLLM_USE_V2_MODEL_RUNNER=1
# client (InferenceX agentx-harness 1.0.12)
agentx profile --url http://127.0.0.1:<port> --model amd/MiniMax-M3.1-Flash-Quark-MXFP4 --tokenizer <model path> --concurrency 16 \
  --benchmark-duration 3600 --tokenizer-trust-remote-code --server-metrics http://127.0.0.1:<port>/metrics \
  --output-artifact-dir <dir> --public-dataset semianalysis_cc_traces_weka_062126
# GSM8K
python tests/evals/gsm8k/gsm8k_eval.py --port <port> --use-chat-completions --num-questions 1319 --num-shots 5 \
  --max-tokens 12288 --temperature 0 --max-concurrency 64 --request-timeout-seconds 1800
# microbench (random scores, below; run on this PR and on main, 6 fresh processes each). For real scores, run the scorer
# (minimax_m3_nvfp4_index_score_decode) on a captured decode step and feed its output to the same timing loop (the captured data is not in this PR)
python bench_topk.py out.json
# bench_topk.py (random scores)
import sys, json, math, statistics, torch
from vllm.models.minimax_m3.amd.ops.index_topk_decode.host import minimax_m3_index_topk_decode_indices as topk
K, QL = 16, 4
width = lambda msl: (1 << (math.ceil(math.ceil(msl / 128) / 64) - 1).bit_length()) * 64
flush = torch.empty(1 << 30, dtype=torch.uint8, device="cuda"); res = {}
for msl in [65536, 131072, 262144, 524288, 1 << 20]:
    for nr in [1, 2, 3, 4, 6, 8, 12, 16, 32, 64, 100]:
        gen = torch.Generator().manual_seed(nr * 1000003 + msl)
        seq = torch.randint(msl // 2, msl + 1, (nr,), generator=gen).int().cuda()
        scores = []
        for _ in range(K):
            s = torch.randn(1, nr * QL, width(msl), generator=gen)
            s = torch.where(torch.rand(s.shape, generator=gen) < 4.0 / s.shape[-1], s + 6, s)
            s[:, :, 0] = 1e30; scores.append(s.cuda())
        outs = [torch.empty(1, nr * QL, 16, dtype=torch.int32, device="cuda") for _ in range(K)]
        def fn():
            for i in range(K): topk(scores[i], outs[i], seq, max_seq_len=msl, query_len=QL)
        fn(); torch.accelerator.synchronize()
        g = torch.cuda.CUDAGraph()
        with torch.cuda.graph(g): fn()
        ts = []
        for _ in range(20):
            flush.zero_(); a, b = torch.cuda.Event(enable_timing=True), torch.cuda.Event(enable_timing=True)
            a.record(); g.replay(); b.record(); torch.accelerator.synchronize(); ts.append(a.elapsed_time(b) * 1000 / K)
        res[f"{nr}x{msl}"] = statistics.median(ts)
json.dump(res, open(sys.argv[1], "w"))

Random scores (N(0,1), a few outliers), stock / ours, us (speedup), same method:

Requests 64k 128k 256k 512k 1024k
1 4.07 / 4.01 (1.01x) 4.05 / 3.97 (1.02x) 4.07 / 4.42 (0.92x) 7.92 / 4.65 (1.71x) 9.98 / 6.51 (1.53x)
2 4.17 / 4.06 (1.03x) 4.16 / 4.06 (1.02x) 5.91 / 4.49 (1.32x) 6.15 / 4.63 (1.33x) 12.00 / 6.66 (1.80x)
3 4.18 / 4.09 (1.02x) 4.18 / 4.09 (1.02x) 5.81 / 4.52 (1.29x) 7.91 / 4.62 (1.71x) 11.94 / 6.66 (1.79x)
4 4.16 / 4.09 (1.02x) 4.17 / 4.09 (1.02x) 4.16 / 4.52 (0.92x) 8.01 / 4.66 (1.72x) 11.97 / 6.67 (1.79x)
6 4.18 / 4.10 (1.02x) 4.17 / 4.13 (1.01x) 5.93 / 4.57 (1.30x) 8.02 / 4.68 (1.71x) 11.97 / 6.69 (1.79x)
8 4.17 / 4.11 (1.01x) 4.17 / 4.14 (1.01x) 5.98 / 4.58 (1.31x) 8.13 / 4.68 (1.74x) 12.06 / 6.73 (1.79x)
12 4.16 / 4.13 (1.01x) 4.19 / 4.19 (1.00x) 5.99 / 4.63 (1.29x) 8.15 / 4.73 (1.72x) 12.14 / 6.84 (1.78x)
16 4.17 / 4.16 (1.00x) 4.22 / 4.22 (1.00x) 6.00 / 4.65 (1.29x) 8.20 / 4.78 (1.72x) 12.04 / 6.86 (1.76x)
32 4.21 / 4.26 (0.99x) 4.23 / 4.31 (0.98x) 6.17 / 4.77 (1.29x) 8.37 / 5.12 (1.64x) 12.30 / 7.24 (1.70x)
64 4.22 / 4.33 (0.97x) 4.29 / 4.44 (0.97x) 6.37 / 5.13 (1.24x) 8.59 / 5.75 (1.50x) 12.74 / 8.25 (1.54x)
100 4.32 / 4.77 (0.91x) 4.52 / 4.92 (0.92x) 6.74 / 5.79 (1.16x) 9.19 / 6.85 (1.34x) 13.56 / 9.76 (1.39x)

The old kernel does a row of up to 213k tokens in one window and needs only a few dozen candidates per window on random scores, so this table is its best case. Real scores, served shapes (call = requests, padded rows, mean context; weight = share of calls; rows over CAP = rows whose old-kernel candidate stage overflows 64 candidates, emulated):

call (requests, padded rows, mean ctx) weight rows over CAP stock real ours real speedup real stock / ours random
1, 4, 92k 7.7% 0/4 4.23 (4.18-4.23) 4.07 (4.06-4.07) 1.04x 4.09 / 4.10
1, 4, 211k 8.3% 1/4 18.14 (18.11-18.19) 4.51 (4.49-4.52) 4.02x 4.12 / 4.46
2, 8, 92k 9.1% 3/8 18.50 (18.49-18.51) 4.22 (4.20-4.25) 4.39x 4.18 / 4.17
2, 8, 227k 11.0% 5/8 20.49 (20.47-20.51) 4.61 (4.56-4.63) 4.45x 5.97 / 4.57
3, 16, 160k 11.2% 4/12 18.39 (18.38-18.40) 4.63 (4.62-4.64) 3.98x 4.05 / 4.59
3, 16, 219k 9.2% 5/12 20.30 (20.27-20.31) 4.67 (4.67-4.70) 4.34x 5.84 / 4.61
4, 16, 159k 7.6% 0/16 6.04 (6.03-6.05) 4.66 (4.65-4.67) 1.30x 5.87 / 4.60
4, 16, 320k 8.5% 6/16 34.49 (34.45-34.62) 4.68 (4.66-4.70) 7.38x 7.93 / 4.63
5, 24, 159k 5.2% 0/20 6.38 (6.33-6.40) 4.66 (4.65-4.66) 1.37x 5.88 / 4.61
5, 24, 224k 6.4% 4/20 34.53 (34.49-34.55) 4.67 (4.66-4.68) 7.39x 6.04 / 4.65
6, 24, 161k 2.8% 22/24 20.32 (20.29-20.34) 4.71 (4.70-4.72) 4.32x 5.94 / 4.61
6, 24, 226k 4.1% 10/24 22.20 (22.14-22.23) 4.69 (4.68-4.71) 4.73x 7.91 / 4.63
7, 32, 227k 4.0% 22/28 22.16 (22.15-22.19) 4.81 (4.81-4.82) 4.61x 7.92 / 4.64
8, 32, 208k 2.4% 8/32 18.35 (18.33-18.37) 6.65 (6.64-6.67) 2.76x 9.89 / 6.56
9, 40, 227k 1.3% 0/36 9.75 (9.72-9.77) 6.40 (6.39-6.42) 1.52x 9.87 / 6.55
10, 40, 230k 0.6% 10/40 38.90 (38.82-39.03) 6.58 (6.57-6.59) 5.91x 9.96 / 6.56
11, 48, 154k 0.3% 13/44 25.44 (25.40-25.46) 6.64 (6.63-6.65) 3.83x 9.88 / 6.60
12, 48, 156k 0.2% 0/48 8.02 (8.01-8.03) 4.74 (4.72-4.74) 1.69x 7.95 / 4.73
13, 56, 165k 0.0% 15/52 39.36 (39.21-39.37) 6.72 (6.71-6.72) 5.86x 9.86 / 6.59
14, 56, 159k 0.0% 37/56 39.24 (39.21-39.27) 6.81 (6.80-6.81) 5.76x 9.88 / 6.63

Details

Each decode row is one workgroup that walks the row's block scores in windows, keeps a running top 16 and ranks the candidates. The old kernel did this with a single wave over windows of 1,664 blocks (213k tokens); rows longer than that serialize on the one wave. Now the workgroup has four waves, each walking every fourth window of 1,024 blocks with its own candidates and running top 16, and the lists are merged by an exact rank over (score, block id), which reproduces AITER's order including ties, NaN and infinities. A wave with no live window pads its list with dead entries, and a row of at most one window skips the merge. The lane-bound threshold uses 16 bits instead of 12 and falls back to all 32 bits when too many keys pass. Prefill compiles to the same code as before. The kernel name for decode changes from ..._w26 to ..._w16.

Why real scores matter: the old kernel keeps the keys above a bound and ranks at most 64 of them per window; when more pass, it rebuilds the candidates, and with many near-ties it runs an exact bit search on one wave. Random scores never reach that. Real index scores sit in a narrow range, so the 12-bit bound lets more than 64 keys through in about a third of the rows (emulated on the captured rows: 33% of rows with the old bound and window size, 6% with the 16-bit bound), and a call takes as long as its slowest row. Calls in which no row overflows run at the random-score time. I did not build a 16-bit-bound-only or four-wave-only variant, so I cannot say how much of the 4x comes from each.

Limits:

  • Slower in some calls: when every row is under about 213k tokens and no row overflows the old kernel's candidate buffer, a call is up to 12% slower (about 0.5 us on a 4 us call; random-score table, and the 0.88x-0.92x served calls there) and 8-9% slower at 100 requests under 131k tokens. In my real-score runs no cell (20 served calls, 32 grid cells, 6 cells of 131k-213k rows) is slower than stock (lowest 1.04x), but about 30% of single-request calls with 131k-213k rows have no overflowing row (emulation). The launch only sees the graph's max_seq_len (1M in serving), not the row lengths, so the host cannot choose between the two kernels.
  • The real-score grid cells are re-scored captured requests, not replays of served calls; the max length 1M column is random-score only.
  • E2E was run on the published ROCm image with the two files overlaid, not on a build of current main: the CI recipe (DBO with this multimodal model) does not start on main under Model Runner V2, and I did not change the recipe. The kernel files are identical to main's; other kernels' shares differ on main (for example the MXFP8 fusions), so the gain on main may differ.
  • c16 and c32 only (c1-c8 do not use this indexer path; c64 not run); one model, TP4, DSpark, DBO on; 2 runs per arm.
  • Validation is gfx950 only, and no Buildkite step runs the test file on gfx950 (I ran it on MI355X). The microbench is not in benchmarks/kernels/.
  • A 2-way merge for rows up to 2,048 blocks would halve the 131k-213k regression (+8 lines); it is not in this PR.

Not a duplicate: no open PR changes the top-k files or their test file (checked against main at 76c6f3f). #64198 (MXFP4 K/V and index cache) does not touch them. #64264 (NVFP4 kernel warmup at startup) and #64273 (monokernel decode on M3.1) are closed drafts that only carried the same bytes of these files as main. #64448 (decode CP for DSpark 7) is open and edits the indexer module and the CP push kernel, not the top-k kernel, so it is complementary.

AI assistance: AI assistance was used in this PR, for the kernel change, the added tests and benchmark scripts, the measurements and this description; the models are listed as Co-authored-by on the commit. The numbers in this description come from the runs described above.


Pull Request Checklist
  • I used vLLM's /pr-checklist skill. (Mandatory for agents, optional for humans).

  • AI assistance was used during the creation of this PR.

  • Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.

  • Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.

  • Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.

  • Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.

The decode top-k walked each row with one wave. Run four waves per row,
each over every fourth window with the next window already loading, and
merge their sorted top-16 lists by an exact rank over (key, index), which
keeps AITER's order including ties, NaN and infinities. Prefill is
unchanged.

Extend the decode tests with 100 requests and the window edges where the
waves meet.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: chao-xu-spec <chaox@amd.com>
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment /ci run for upstream CI or /amd-ci run for AMD CI only whenever CI signals are needed.

Once the PR is approved or has the ready label, the PR author can also use the corresponding /ci run, /ci retry, and /ci cancel commands, or their /amd-ci variants. New commits do not start upstream CI automatically.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban.

🚀

@mergify mergify Bot added minimax rocm Related to AMD ROCm labels Nov 2, 2026
@github-project-automation github-project-automation Bot moved this to Todo in AMD Nov 2, 2026
@chao-xu-spec
chao-xu-spec marked this pull request as ready for review November 3, 2026 07:53
@chao-xu-spec
chao-xu-spec requested a review from tjtanaa as a code owner November 3, 2026 07:53

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@Fangzhou-Ai

Copy link
Copy Markdown
Collaborator

/ci run

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

✅ Triggered Buildkite CI #99595 for commit 769b0247045d.

@Fangzhou-Ai

Copy link
Copy Markdown
Collaborator

/ci run

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

✅ @chao-xu-spec, CI is now available for this PR.

  • /ci run starts upstream CI; /amd-ci run starts AMD CI only.
  • Run commands test your branch with every commit currently on its upstream target branch. If it is behind, the bot merges the target branch into it first (pull before pushing again). Append --allow-stale to a run command to test an outdated branch as is, at your own risk.
  • /ci retry retries failed jobs in the CI build for the current PR head. If the current head has no CI build, it starts a new CI build for the current head containing only jobs that failed in the latest earlier CI build for this PR.
  • /amd-ci retry retries failed jobs in AMD CI for the current PR head. Use /amd-ci run when the current head has no AMD CI build.
  • /ci cancel cancels scheduled or running CI builds for this PR branch; /amd-ci cancel does the same for AMD CI only.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

✅ Triggered Buildkite CI #99665 for commit 1c1e301254d4.

@shen-shanshan
shen-shanshan merged commit c0c8373 into vllm-project:main Nov 4, 2026
85 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in AMD Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minimax ready ONLY add when PR is ready to merge/full CI is needed rocm Related to AMD ROCm

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants