[Refactor] Remove dead tests utils (#58803)

Signed-off-by: yewentao256 <zhyanwentao@126.com>

Wentao Ye committed Sep 26, 2026 at 09:16 UTC bf93e1f92c40673454c938b277a0a99d49662657
16 files changed +4 -1337
tests/conftest.py
-8
@@ -317,14 +317,6 @@ def example_system_message() -> str:
317 return f.read()
318
319
320 -class DecoderPromptType(Enum):
321 - """For encoder/decoder models only."""
322 -
323 - CUSTOM = 1
324 - NONE = 2
325 - EMPTY_STR = 3
326 -
327 -
320 @pytest.fixture
321 def example_long_prompts() -> list[str]:
322 return [prompt for filename in _LONG_PROMPTS for prompt in _read_prompts(filename)]
tests/entrypoints/serve/dev/rlhf/conftest.py
-86
@@ -15,7 +15,6 @@ import subprocess
15 import sys
16 import threading
17 import time
18 -from collections.abc import Callable
18 from contextlib import contextmanager
19 from dataclasses import dataclass, field
20 from typing import Any
@@ -138,36 +137,6 @@ def server(
137 proc.kill()
138
139
141 -# ---------------------------------------------------------------------------
142 -# Polling helper (200-lie workaround)
143 -# ---------------------------------------------------------------------------
144 -
145 -
146 -def poll_until(
147 - predicate: Callable[[], bool],
148 - timeout: float = 10.0,
149 - interval: float = 0.5,
150 -) -> bool:
151 - """Poll predicate() until it returns True or timeout expires.
152 -
153 - Workaround for the vLLM sleep/wake "200-lie" — the HTTP endpoints may
154 - return 200 before the underlying operation is complete, so callers that
155 - need to verify state *after* an operation can use this helper instead of
156 - assuming the 200 means completion.
157 -
158 - Returns True if predicate became true within timeout, False otherwise.
159 - """
160 - deadline = time.time() + timeout
161 - while time.time() < deadline:
162 - try:
163 - if predicate():
164 - return True
165 - except Exception:
166 - pass
167 - time.sleep(interval)
168 - return False
169 -
170 -
140 # ---------------------------------------------------------------------------
141 # HTTP helpers — generation
142 # ---------------------------------------------------------------------------
@@ -191,27 +160,6 @@ def gen(url, prompt="The capital of France is", max_tokens=8, timeout=30):
160 return None
161
162
194 -def gen_with_logprobs(
195 - url, prompt="The capital of France is", max_tokens=8, logprobs=5, timeout=30
196 -):
197 - """Fire a /v1/completions request with logprobs; return JSON or None."""
198 - try:
199 - r = requests.post(
200 - f"{url}/v1/completions",
201 - json={
202 - "model": "m",
203 - "prompt": prompt,
204 - "max_tokens": max_tokens,
205 - "temperature": 0,
206 - "logprobs": logprobs,
207 - },
208 - timeout=timeout,
209 - )
210 - return r.json()
211 - except Exception:
212 - return None
213 -
214 -
163 def ok(resp) -> bool:
164 """True iff resp is a successful completion (has choices, no error key)."""
165 return (
@@ -399,37 +347,3 @@ def get_world_size(url, include_dp=True):
347 params={"include_dp": include_dp},
348 timeout=5,
349 )
402 -
403 -
404 -# ---------------------------------------------------------------------------
405 -# GPU / metrics helpers
406 -# ---------------------------------------------------------------------------
407 -
408 -
409 -def gpu_free_bytes(device: int = 0) -> int:
410 - """Read GPU free bytes via subprocess to avoid import-time torch init."""
411 - out = subprocess.check_output(
412 - [
413 - sys.executable,
414 - "-c",
415 - f"import torch; f,_=torch.accelerator.get_memory_info({device}); print(f)",
416 - ],
417 - timeout=10,
418 - )
419 - return int(out.strip())
420 -
421 -
422 -def sleep_metrics(url):
423 - """Return (awake, weights_offloaded, discard_all) from /metrics."""
424 - try:
425 - from prometheus_client.parser import text_string_to_metric_families
426 - except ImportError:
427 - return None, None, None
428 -
429 - r = requests.get(f"{url}/metrics", timeout=5)
430 - vals: dict = {}
431 - for family in text_string_to_metric_families(r.text):
432 - if family.name == "vllm:engine_sleep_state":
433 - for s in family.samples:
434 - vals[s.labels.get("sleep_state", "")] = s.value
435 - return vals.get("awake"), vals.get("weights_offloaded"), vals.get("discard_all")
tests/kernels/attention/test_attention.py
-40
@@ -309,46 +309,6 @@ def test_paged_attention(
309 torch.testing.assert_close(output, ref_output, atol=atol, rtol=rtol)
310
311
312 -def ref_multi_query_kv_attention(
313 - cu_seq_lens: list[int],
314 - query: torch.Tensor,
315 - key: torch.Tensor,
316 - value: torch.Tensor,
317 - scale: float,
318 - alibi_bias: list[torch.Tensor] | None,
319 - dtype: torch.dtype,
320 -) -> torch.Tensor:
321 - num_seqs = len(cu_seq_lens) - 1
322 - ref_outputs: list[torch.Tensor] = []
323 - if alibi_bias:
324 - assert len(alibi_bias) == num_seqs
325 - for i in range(num_seqs):
326 - start_idx = cu_seq_lens[i]
327 - end_idx = cu_seq_lens[i + 1]
328 - seq_len = end_idx - start_idx
329 -
330 - # Create attention mask. ALiBi already includes a tril causal mask.
331 - if alibi_bias:
332 - attn_mask = alibi_bias[i]
333 - else:
334 - attn_mask = torch.triu(
335 - torch.ones(seq_len, seq_len, dtype=dtype), diagonal=1
336 - )
337 - attn_mask = attn_mask * torch.finfo(dtype).min
338 - attn_mask = attn_mask.to(dtype=dtype)
339 -
340 - ref_output = ref_masked_attention(
341 - query[start_idx:end_idx],
342 - key[start_idx:end_idx],
343 - value[start_idx:end_idx],
344 - scale,
345 - attn_mask=attn_mask,
346 - )
347 - ref_outputs.append(ref_output)
348 -
349 - return torch.cat(ref_outputs, dim=0)
350 -
351 -
312 @pytest.mark.parametrize("attention_cls", [Attention, MMEncoderAttention])
313 def test_num_heads_not_divisible_by_num_kv_heads(attention_cls: type) -> None:
314 head_size = 64
tests/kernels/mamba/test_causal_conv1d.py
-31
@@ -120,37 +120,6 @@ def causal_conv1d_update_ref(
120 return (out if activation is None else F.silu(out)).to(dtype=dtype_in)
121
122
123 -@pytest.mark.parametrize("itype", [torch.bfloat16, torch.float])
124 -@pytest.mark.parametrize("silu_activation", [True])
125 -@pytest.mark.parametrize("has_bias", [True])
126 -def causal_conv1d_opcheck_fn(
127 - x: torch.Tensor,
128 - weight: torch.Tensor,
129 - bias: torch.Tensor | None = None,
130 - cu_seq_len: torch.Tensor | None = None,
131 - cache_indices: torch.Tensor | None = None,
132 - has_initial_state: torch.Tensor | None = None,
133 - conv_states: torch.Tensor | None = None,
134 - activation: str | None = "silu",
135 - null_block_id: int = NULL_BLOCK_ID,
136 -):
137 - """x: (batch, dim, seqlen)
138 - weight: (dim, width)
139 - bias: (dim,)
140 - seq_idx: (batch, seqlen)
141 - initial_states: (batch, dim, width - 1)
142 - final_states_out: (batch, dim, width - 1), to be written to
143 - activation: either None or "silu" or "swish"
144 -
145 - out: (batch, dim, seqlen)
146 - """
147 - if activation not in [None, "silu", "swish"]:
148 - raise NotImplementedError("activation must be None, silu, or swish")
149 - if x.stride(-1) != 1:
150 - x = x.contiguous()
151 - bias = bias.contiguous() if bias is not None else None
152 -
153 -
123 @pytest.mark.parametrize("itype", [torch.bfloat16])
124 @pytest.mark.parametrize("silu_activation", [False, True])
125 @pytest.mark.parametrize("has_bias", [False, True])
tests/kernels/moe/modular_kernel_tools/mk_objects.py
-18
@@ -448,24 +448,6 @@ if cutlass_fp4_supported() or has_flashinfer_cutlass_fused_moe():
448 ]
449
450
451 -def _slice(rank: int, num_local_experts: int, t: torch.Tensor) -> torch.Tensor:
452 - s = rank * num_local_experts
453 - e = s + num_local_experts
454 - return t[s:e]
455 -
456 -
457 -def make_cutlass_strides(
458 - e: int,
459 - n: int,
460 - k: int,
461 -) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
462 - ab_strides1 = torch.full((e,), k, device="cuda", dtype=torch.int64)
463 - ab_strides2 = torch.full((e,), n, device="cuda", dtype=torch.int64)
464 - c_strides1 = torch.full((e,), 2 * n, device="cuda", dtype=torch.int64)
465 - c_strides2 = torch.full((e,), k, device="cuda", dtype=torch.int64)
466 - return ab_strides1, ab_strides2, c_strides1, c_strides2
467 -
468 -
451 def make_fused_experts(
452 fused_experts_type: mk.FusedMoEExpertsModular,
453 moe: FusedMoEConfig,
tests/kernels/moe/test_moe_layer.py
-51
@@ -275,11 +275,6 @@ def maybe_roundup_layer_hidden_size(
275 return hidden_size
276
277
278 -def rank_chunk(num: int, r: int, w: int) -> int:
279 - rem = num % w
280 - return (num // w) + (1 if r < rem else 0)
281 -
282 -
278 def chunk_by_rank(
279 t: torch.Tensor,
280 r: int,
@@ -294,19 +289,6 @@ def chunk_by_rank(
289 return t
290
291
297 -def maybe_chunk_by_rank(
298 - t: torch.Tensor | None,
299 - r: int,
300 - w: int,
301 - dim: int = 0,
302 - device: torch.device | None = None,
303 -) -> torch.Tensor | None:
304 - if t is not None:
305 - return chunk_by_rank(t, r, w, dim, device)
306 - else:
307 - return t
308 -
309 -
292 def tp_chunk_gate_up(
293 w: torch.Tensor,
294 tp_rank: int,
@@ -676,26 +658,6 @@ def is_valid_config(config: MoETestConfig) -> tuple[bool, str | None]:
658 return True, None
659
660
679 -def chunk_scales_by_rank(
680 - t: torch.Tensor | None,
681 - r: int,
682 - w: int,
683 - device: torch.device | None = None,
684 -) -> torch.Tensor | None:
685 - if t is not None and t.numel() > 1:
686 - # Calculate start index by summing chunk sizes for all previous ranks
687 - # start = sum(rank_chunk(t.shape[0], i, w) for i in range(r))
688 - # chunk = rank_chunk(t.shape[0], r, w)
689 - # t = t[start:(start + chunk)]
690 - chunk = rank_chunk(t.shape[0], r, w)
691 - t = t[(r * chunk) : max(t.shape[0], (r + 1) * chunk)]
692 -
693 - if t is not None and device is not None:
694 - t = t.to(device)
695 -
696 - return t
697 -
698 -
661 def chunk_scales(
662 t: torch.Tensor | None,
663 start: int,
@@ -771,19 +733,6 @@ def _quantize_fp8_halves(
733 )
734
735
774 -def quantization_to_quant_dtype(
775 - quantization: str | None,
776 -) -> torch.dtype | str | None:
777 - if quantization is None:
778 - return None
779 - elif quantization in ["fp8", "fp8_blocked", "modelopt_fp8"]:
780 - return fp8_dtype
781 - elif quantization in ["modelopt_fp4"]:
782 - return "nvfp4"
783 - else:
784 - raise NotImplementedError(f"Unsupported quantization: {quantization}")
785 -
786 -
736 def make_quant_config(
737 quantization: str | None,
738 w1: torch.Tensor,
tests/kernels/moe/utils.py
-29
@@ -552,16 +552,6 @@ class TestMLP(torch.nn.Module):
552 return x
553
554
555 -def make_naive_shared_experts(
556 - N: int,
557 - K: int,
558 - in_dtype: torch.dtype = torch.bfloat16,
559 -) -> torch.nn.Module:
560 - w1 = torch.randn((K, N * 2), device=DEVICE, dtype=in_dtype) / 15
561 - w2 = torch.randn((N, K), device=DEVICE, dtype=in_dtype) / 15
562 - return TestMLP(w1, w2, out_dtype=in_dtype)
563 -
564 -
555 class RealMLP(torch.nn.Module):
556 def __init__(
557 self,
@@ -668,25 +658,6 @@ def modular_triton_fused_moe(
658 )
659
660
671 -def make_shared_experts(
672 - N: int,
673 - K: int,
674 - in_dtype: torch.dtype = torch.bfloat16,
675 - quant_dtype: torch.dtype | str | None = None,
676 -) -> torch.nn.Module:
677 - (_, w1, w1_s, _), (_, w2, w2_s, _) = make_test_weights(
678 - 1,
679 - N,
680 - K,
681 - in_dtype=in_dtype,
682 - quant_dtype=quant_dtype,
683 - )
684 -
685 - return make_shared_experts_with_weights(
686 - N, K, in_dtype, w1, w2, w1_s=w1_s, w2_s=w2_s, quant_dtype=quant_dtype
687 - )
688 -
689 -
661 def check_accuracy(a, b, atol, rtol, percent):
662 """Allow a mismatch percentage of 1 - percent."""
663 if torch.any(torch.isnan(a)):
tests/kernels/quant_utils.py
-19
@@ -253,25 +253,6 @@ def dequant(
253 return t.to(out_dtype)
254
255
256 -def batched_dequant(
257 - t: torch.Tensor,
258 - scale: torch.Tensor | None,
259 - block_shape: list[int] | None,
260 - per_act_token_quant: bool,
261 - out_dtype: torch.dtype | None = torch.float32,
262 -) -> torch.Tensor:
263 - if scale is not None:
264 - assert t.shape[0] == scale.shape[0]
265 - out = torch.empty_like(t, dtype=out_dtype)
266 - for e in range(t.shape[0]):
267 - out[e] = dequant(
268 - t[e], scale[e], block_shape, per_act_token_quant, out_dtype
269 - )
270 - return out
271 -
272 - return t.to(out_dtype)
273 -
274 -
256 def native_batched_masked_quant_matmul(
257 A: torch.Tensor,
258 B: torch.Tensor,
tests/kernels/utils.py
+1 -721
@@ -2,11 +2,8 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Kernel test utils."""
4
5 -import itertools
6 -import random
5 from collections.abc import Sequence
8 -from numbers import Number
9 -from typing import Any, NamedTuple
6 +from typing import Any
7 from unittest.mock import patch
8
9 import torch
@@ -17,8 +14,6 @@ from vllm.model_executor.custom_op import op_registry
14 from vllm.model_executor.layers.activation import SiluAndMul
15 from vllm.model_executor.layers.fused_moe.activation import MoEActivation
16 from vllm.model_executor.layers.fused_moe.utils import moe_kernel_quantize_input
20 -from vllm.utils.torch_utils import make_tensor_with_pad
21 -from vllm.v1.attention.backend import AttentionType
17
18 # For now, disable "test_aot_dispatch_dynamic" since there are some
19 # bugs related to this test in PyTorch 2.4.
@@ -93,721 +88,6 @@ def _assert_deterministic(
88 )
89
90
96 -class QKVInputs(NamedTuple):
97 - """Data structure for representing unpacked attention inputs,
98 - query/key/values and their sequence lengths.
99 -
100 - Attributes:
101 - * {query,key,value}: unpacked (batch_size x padded_seq_len x
102 - num_heads x head_size) attention inputs
103 - * q_seq_lens: query sequence lengths list
104 - * kv_seq_lens: shared key/value sequence lengths list
105 -
106 - """
107 -
108 - query: torch.Tensor
109 - key: torch.Tensor
110 - value: torch.Tensor
111 - q_seq_lens: list[int]
112 - kv_seq_lens: list[int]
113 -
114 -
115 -class QKVO(NamedTuple):
116 - """Data structure for representing unpacked attention inputs,
117 - alongside unpacked known-correct attention output
118 -
119 - Attributes:
120 - * qkv: unpacked (batch_size x padded_seq_len x
121 - num_heads x head_size) attention inputs
122 - * ideal_output: unpacked (batch_size x padded_seq_len x
123 - num_heads x head_size) known-correct attention output
124 -
125 - """
126 -
127 - qkv: QKVInputs
128 - ideal_output: torch.Tensor
129 -
130 -
131 -class PackedQKVInputs(NamedTuple):
132 - """Data structure for representing packed attention inputs.
133 -
134 - Attributes:
135 - * {query,key,value}: packed (number_of_tokens x num_heads
136 - x head_size) attention inputs
137 - * q_start_loc_list: list of query start locations within packed tensor
138 - * kv_start_loc_list: shared list of key/value start locations within
139 - packed tensor
140 - * q_seq_lens: query sequence lengths list
141 - * kv_seq_lens: shared key/value sequence lengths list
142 -
143 - """
144 -
145 - query: torch.Tensor
146 - key: torch.Tensor
147 - value: torch.Tensor
148 - q_start_loc_list: list[int] | None
149 - kv_start_loc_list: list[int] | None
150 - q_seq_lens: list[int] | None
151 - kv_seq_lens: list[int] | None
152 -
153 -
154 -class PackedQKVO(NamedTuple):
155 - """Data structure for representing packed attention inputs,
156 - alongside packed known-correct attention output
157 -
158 - Attributes:
159 - * packed_qkv: packed (number_of_tokens x num_heads
160 - x head_size) attention inputs
161 - * ideal_output: packed (number_of_tokens x num_heads
162 - x head_size) known-correct attention output
163 -
164 - """
165 -
166 - packed_qkv: PackedQKVInputs | None
167 - ideal_output: torch.Tensor
168 -
169 -
170 -class KVMemoryMap(NamedTuple):
171 - """Data structure for encapsulating KV cache memory mapping.
172 -
173 - Attributes:
174 - * block_tables: KV cache block tables
175 - * slot_mapping: mapping of sequence offset to physical address
176 -
177 - """
178 -
179 - block_tables: torch.Tensor
180 - slot_mapping: torch.Tensor
181 -
182 -
183 -class PhaseTestParameters(NamedTuple):
184 - """Data structure for encapsulating the test parameters
185 - for a given test "phase" (prefill or decode phase) and attention
186 - scenario (encoder, decoder-self, encoder/decoder-cross)
187 -
188 - Attributes:
189 - * packed_qkvo: packed (number_of_tokens x num_heads
190 - x head_size) attention inputs & known-correct
191 - output
192 - * kv_mmap: KV cache memory mapping, specific to this test phase &
193 - attention scenario
194 -
195 - """
196 -
197 - packed_qkvo: PackedQKVO
198 - kv_mmap: KVMemoryMap | None
199 -
200 -
201 -def maybe_make_int_tensor(
202 - _list: list[int] | None,
203 - device: torch.device | str,
204 -) -> torch.Tensor:
205 - """Convert Python int list to a 1D int torch.Tensor on `device`.
206 -
207 - Returns:
208 - If _list is not None: 1D int torch.Tensor on `device`, `None` otherwise.
209 -
210 - """
211 - return (
212 - None if _list is None else torch.tensor(_list, dtype=torch.int, device=device)
213 - )
214 -
215 -
216 -def maybe_make_long_tensor(
217 - _list: list[int] | None,
218 - device: torch.device | str,
219 -) -> torch.Tensor:
220 - """Convert Python int list to a 1D long torch.Tensor on `device`.
221 -
222 - Returns:
223 - If _list is not None: 1D long torch.Tensor on `device`, `None` otherwise.
224 -
225 - """
226 - return (
227 - None if _list is None else torch.tensor(_list, dtype=torch.long, device=device)
228 - )
229 -
230 -
231 -def maybe_max(_list: list | None) -> Number | None:
232 - """Compute the maximum of a list, if it exists.
233 -
234 - Returns:
235 - If _list is not None: max(_list), `None` otherwise.
236 -
237 - """
238 - return None if _list is None else max(_list)
239 -
240 -
241 -def make_causal_mask(
242 - q_max_seq_len: int,
243 - kv_max_seq_len: int,
244 -) -> torch.Tensor:
245 - """Create a q_max_seq_len x kv_max_seq_len causal mask.
246 -
247 - Arguments:
248 - q_max_seq_len: query max seq len
249 - kv_max_seq_len: key/value max seq len
250 -
251 - Returns:
252 - 2D tensor, q_max_seq_len x kv_max_seq_len
253 -
254 - """
255 - # Create a matrix where entry (i, j) is True if i >= j
256 - mask = torch.triu(torch.ones(q_max_seq_len, kv_max_seq_len), diagonal=1)
257 - # Replace True with float('-inf') and False with 0
258 - mask = mask.masked_fill(mask == 1, float("-inf")).masked_fill(mask == 0, 0.0)
259 - return mask
260 -
261 -
262 -def ref_masked_attention(
263 - query: torch.Tensor,
264 - key: torch.Tensor,
265 - value: torch.Tensor,
266 - scale: float,
267 - custom_mask: torch.Tensor | None = None,
268 - q_seq_lens: list | None = None,
269 - kv_seq_lens: list | None = None,
270 -) -> torch.Tensor:
271 - """Reference ("golden") masked attention. Supports two types of masking:
272 -
273 - * Basic attention mask, utilizing {q,kv}_seq_lens args to mask out
274 - padding elements
275 - * Custom attention mask, which can force an arbitrary mask tensor, i.e.
276 - causal
277 -
278 - Arguments:
279 - query: batch_size x q_padded_seq_len x num_heads x head_size
280 - key: batch_size x kv_padded_seq_len x num_heads x head_size
281 - value: batch_size x kv_padded_seq_len x num_heads x head_size
282 - scale: Attention scale factor
283 - custom_mask: custom attention mask; good place to inject a causal attention mask
284 - q_seq_lens: list of unpadded query seq_lens for each batch index
285 - kv_seq_lens: list of unpadded key/value seq_lens for each batch index
286 -
287 - Returns:
288 - Attention result, batch_size x q_padded_seq_len x num_heads x head_size
289 -
290 - """
291 - assert q_seq_lens is not None
292 - assert kv_seq_lens is not None
293 -
294 - batch_size = query.shape[0]
295 - assert len(q_seq_lens) == batch_size
296 - assert len(kv_seq_lens) == batch_size
297 -
298 - attn_weights = scale * torch.einsum("bqhd,bkhd->bhqk", query, key).float()
299 -
300 - # Basic attention mask, derived from seq lens
301 - if (q_seq_lens is not None) or (kv_seq_lens is not None):
302 - attn_mask = torch.zeros_like(attn_weights)
303 - if q_seq_lens is not None:
304 - for bdx, plen in enumerate(q_seq_lens):
305 - attn_mask[bdx, :, plen:, :] = -torch.inf
306 - if kv_seq_lens is not None:
307 - for bdx, plen in enumerate(kv_seq_lens):
308 - attn_mask[bdx, :, :, plen:] = -torch.inf
309 -
310 - attn_weights = attn_weights + attn_mask.float()
311 -
312 - # Custom attention mask
313 - if custom_mask is not None:
314 - attn_weights = attn_weights + custom_mask.float()
315 -
316 - attn_weights = torch.softmax(attn_weights, dim=-1).to(value.dtype)
317 - out = torch.einsum("bhqk,bkhd->bqhd", attn_weights, value)
318 - return out
319 -
320 -
321 -def make_qkv(
322 - batch_size: int,
323 - max_q_seq_len: int,
324 - max_kv_seq_len: int | None,
325 - num_heads: int,
326 - head_size: int,
327 - device: torch.device | str,
328 - force_kv_seq_lens: list[int] | None = None,
329 - attn_type: AttentionType = AttentionType.ENCODER_DECODER,
330 - force_max_len: bool = False,
331 -) -> tuple[QKVInputs, QKVInputs, QKVInputs]:
332 - """Construct QKV test tensors for self- and cross-attention.
333 -
334 - Generates three query/key/value triplets:
335 -
336 - * "Baseline" query/key/value (for input to reference attention function)
337 - * "Prefill" query/key/value (last sequence offset zero'd out, for use as
338 - input to prefill kernel)
339 - * "Decode" query/key/value (only the last sequence offset from baseline,
340 - for use as input to decode kernel)
341 -
342 - Each Q/K/V triplet is associated with a list of q seqlens and a list of k/v
343 - seqlens
344 -
345 - Arguments:
346 - batch_size: number of sequences in the batch
347 - max_q_seq_len: max query seq len
348 - max_kv_seq_len: max key/value seq len
349 - num_heads: number of attention heads
350 - head_size: head dimension
351 - is_encoder_decoder_attn: if True, query seqlen may differ from
352 - key/value seqlen (as is often the case for cross-attention);
353 - o/w, query/key/value seqlens match at each batch index
354 - (max_kv_seq_len is unused)
355 - force_kv_seq_lens: if not None, overrides kv sequence lengths
356 - attn_type: encoder, decoder self, or enc/dec cross attention
357 - force_max_len: if True, all query seqlens are max_q_seq_len; o/w query
358 - seqlens are random in [2,max_q_seq_lens]. Same for key/value seqlens
359 - and max_kv_seq_len, unless forced by is_encoder_decoder_attn=False
360 - device: CPU or CUDA device
361 -
362 - Returns:
363 - Overall QKVInputs structure (containing full unpacked Q/K/V tensors)
364 - Prefill QKVInputs structure (containing all but the last sequence offset)
365 - Decode QKVInputs structure (containing all only the last sequence offset)
366 -
367 - """
368 - if force_max_len:
369 - q_seq_lens = [max_q_seq_len for _ in range(batch_size)]
370 - else:
371 - q_seq_lens = [random.randint(2, max_q_seq_len) for _ in range(batch_size)]
372 - kv_seq_lens = None
373 - if force_kv_seq_lens is not None:
374 - kv_seq_lens = force_kv_seq_lens
375 - elif attn_type != AttentionType.ENCODER_DECODER:
376 - # K,V seq lens match Q for self-attention
377 - kv_seq_lens = q_seq_lens
378 - else:
379 - # K,V seq lens are distinct from Q seq lens & random
380 - assert max_kv_seq_len is not None
381 - if force_max_len:
382 - kv_seq_lens = [max_kv_seq_len] * batch_size
383 - else:
384 - kv_seq_lens = [random.randint(2, max_kv_seq_len) for _ in range(batch_size)]
385 -
386 - query = torch.rand((batch_size, max_q_seq_len, num_heads, head_size)).to(device)
387 - key = torch.rand((batch_size, max_kv_seq_len, num_heads, head_size)).to(device)
388 - value = torch.rand((batch_size, max_kv_seq_len, num_heads, head_size)).to(device)
389 -
390 - prefill_query = torch.zeros((batch_size, max_q_seq_len, num_heads, head_size)).to(
391 - device
392 - )
393 - prefill_key = torch.zeros((batch_size, max_kv_seq_len, num_heads, head_size)).to(
394 - device
395 - )
396 - prefill_value = torch.zeros((batch_size, max_kv_seq_len, num_heads, head_size)).to(
397 - device
398 - )
399 -
400 - decode_query = torch.zeros((batch_size, 1, num_heads, head_size)).to(device)
401 - decode_key = torch.zeros((batch_size, 1, num_heads, head_size)).to(device)
402 - decode_value = torch.zeros((batch_size, 1, num_heads, head_size)).to(device)
403 -
404 - for bdx, (q_seq_len, kv_seq_len) in enumerate(zip(q_seq_lens, kv_seq_lens)):
405 - query[bdx, q_seq_len:, :, :] = 0
406 - key[bdx, kv_seq_len:, :, :] = 0
407 - value[bdx, kv_seq_len:, :, :] = 0
408 -
409 - prefill_query[bdx, 0 : (q_seq_len - 1), :, :] = query[
410 - bdx, 0 : (q_seq_len - 1), :, :
411 - ]
412 - prefill_key[bdx, 0 : (kv_seq_len - 1), :, :] = key[
413 - bdx, 0 : (kv_seq_len - 1), :, :
414 - ]
415 - prefill_value[bdx, 0 : (kv_seq_len - 1), :, :] = value[
416 - bdx, 0 : (kv_seq_len - 1), :, :
417 - ]
418 -
419 - decode_query[bdx, :, :, :] = query[bdx, (q_seq_len - 1) : q_seq_len, :, :]
420 - decode_key[bdx, :, :, :] = key[bdx, (kv_seq_len - 1) : kv_seq_len, :, :]
421 - decode_value[bdx, :, :, :] = value[bdx, (kv_seq_len - 1) : kv_seq_len, :, :]
422 -
423 - prefill_q_seq_lens = [plen - 1 for plen in q_seq_lens]
424 - prefill_kv_seq_lens = [plen - 1 for plen in kv_seq_lens]
425 -
426 - decode_q_seq_lens = [1 for _ in q_seq_lens]
427 - decode_kv_seq_lens = [1 for _ in kv_seq_lens]
428 -
429 - return (
430 - QKVInputs(
431 - query, # Overall QKV inputs
432 - key,
433 - value,
434 - q_seq_lens,
435 - kv_seq_lens,
436 - ),
437 - QKVInputs(
438 - prefill_query, # Prefill subset of QKV sequences
439 - prefill_key,
440 - prefill_value,
441 - prefill_q_seq_lens,
442 - prefill_kv_seq_lens,
443 - ),
444 - QKVInputs(
445 - decode_query, # Decode subset of KV sequences
446 - decode_key,
447 - decode_value,
448 - decode_q_seq_lens,
449 - decode_kv_seq_lens,
450 - ),
451 - )
452 -
453 -
454 -def pack_tensor(
455 - unpacked_tensor: torch.Tensor, seq_lens: list[int], device: torch.device | str
456 -) -> tuple[torch.Tensor, list[int]]:
457 - """Pack a batch_size x padded_seq_len x num_heads x head_size tensor into an
458 - unpadded number_of_tokens x num_heads x head_size tensor, where
459 - number_of_tokens = sum(seq_lens)
460 -
461 - Arguments:
462 - unpacked_tensor: batch_size x padded_seq_len x num_heads x head_size
463 - seq_lens: list of token counts for each seq
464 - device: CPU or CUDA device
465 -
466 - Returns:
467 - packed_tensor: number_of_tokens x num_heads x head_size
468 - start_loc_list: start idx of each batch elt in packed_tensor; [0] +
469 - list(itertools.accumulate(seq_lens))
470 -
471 - """
472 - num_tok = sum(seq_lens)
473 - num_heads = unpacked_tensor.shape[-2]
474 - head_size = unpacked_tensor.shape[-1]
475 - start_loc_list = [0] + list(itertools.accumulate(seq_lens))
476 - packed_tensor = torch.zeros((num_tok, num_heads, head_size), device=device)
477 -
478 - for bdx, (seq_len, start_loc) in enumerate(zip(seq_lens, start_loc_list)):
479 - packed_tensor[start_loc : (start_loc + seq_len), :, :] = unpacked_tensor[
480 - bdx, :seq_len, :, :
481 - ]
482 -
483 - return packed_tensor, start_loc_list
484 -
485 -
486 -def pack_qkv(qkv: QKVInputs, device: torch.device | str) -> PackedQKVInputs:
487 - """Individually pack each of Q, K and V, each with dimensions batch_size x
488 - padded_seq_len x num_heads x head_size, into respective number_of_tokens x
489 - num_heads x head_size tensors.
490 -
491 - For Q, number_of_tokens = sum(q_seq_lens).
492 -
493 - For K and V, number_of_tokens = sum(kv_seq_lens)
494 -
495 - Arguments:
496 - qkv: Unpacked (batch_size x padded_seq_len x num_heads x head_size)
497 - attention inputs
498 - device: CPU or CUDA device
499 -
500 - Returns:
501 - Packed (number_of_tokens x num_heads x head_size) QKV inputs
502 - derived from unpacked inputs
503 -
504 - """
505 - if qkv.query is None:
506 - packed_query = None
507 - q_start_loc_list = None
508 - else:
509 - packed_query, q_start_loc_list = pack_tensor(
510 - qkv.query, qkv.q_seq_lens, device=device
511 - )
512 - packed_key, kv_start_loc_list = pack_tensor(qkv.key, qkv.kv_seq_lens, device=device)
513 - packed_value, _ = pack_tensor(qkv.value, qkv.kv_seq_lens, device=device)
514 - return PackedQKVInputs(
515 - packed_query,
516 - packed_key,
517 - packed_value,
518 - q_start_loc_list,
519 - kv_start_loc_list,
520 - (None if q_start_loc_list is None else qkv.q_seq_lens),
521 - qkv.kv_seq_lens,
522 - )
523 -
524 -
525 -def _make_metadata_tensors(
526 - seq_lens: list[int] | None,
527 - context_lens: list[int] | None,
528 - encoder_seq_lens: list[int] | None,
529 - device: torch.device | str,
530 -) -> tuple[
531 - torch.Tensor,
532 - torch.Tensor,
533 - Any,
534 - Any,
535 - torch.Tensor | None,
536 - torch.Tensor,
537 - torch.Tensor,
538 - int | None,
539 -]:
540 - """Build scalar & tensor values required to build attention metadata structure.
541 -
542 - Arguments:
543 - seq_lens: list of token-counts for each decoder input seq
544 - context_lens: list of context length values for each seq
545 - encoder_seq_lens: list of token-counts for each encoder input seq
546 - device: CPU or CUDA device
547 -
548 - Returns:
549 - seq_lens_tensor: decoder seq_lens list, as tensor
550 - context_lens_tensor: context_lens list, as tensor
551 - max_context_len: max(context_lens)
552 - max_seq_len: max(seq_lens)
553 - seq_start_loc: start idx of each sequence
554 - encoder_seq_lens_tensor: encoder seq_lens list, as tensor
555 - encoder_seq_start_loc: start idx of each encoder sequence
556 - max_encoder_seq_len: encoder seq_lens list, as tensor
557 -
558 - """
559 - seq_lens_tensor = maybe_make_int_tensor(seq_lens, device)
560 - context_lens_tensor = maybe_make_int_tensor(context_lens, device)
561 - max_context_len = maybe_max(context_lens)
562 - max_seq_len = maybe_max(seq_lens)
563 -
564 - encoder_seq_lens_tensor = maybe_make_int_tensor(encoder_seq_lens, device)
565 - max_encoder_seq_len = None if encoder_seq_lens is None else max(encoder_seq_lens)
566 -
567 - seq_start_loc = None
568 -
569 - if seq_lens_tensor is not None:
570 - seq_start_loc = torch.zeros(
571 - seq_lens_tensor.shape[0] + 1,
572 - dtype=torch.int32,
573 - device=seq_lens_tensor.device,
574 - )
575 - torch.cumsum(
576 - seq_lens_tensor, dim=0, dtype=seq_start_loc.dtype, out=seq_start_loc[1:]
577 - )
578 -
579 - encoder_seq_start_loc = torch.zeros(
580 - encoder_seq_lens_tensor.shape[0] + 1,
581 - dtype=torch.int32,
582 - device=encoder_seq_lens_tensor.device,
583 - )
584 - torch.cumsum(
585 - encoder_seq_lens_tensor,
586 - dim=0,
587 - dtype=encoder_seq_start_loc.dtype,
588 - out=encoder_seq_start_loc[1:],
589 - )
590 -
591 - return (
592 - seq_lens_tensor,
593 - context_lens_tensor,
594 - max_context_len,
595 - max_seq_len,
596 - seq_start_loc,
597 - encoder_seq_lens_tensor,
598 - encoder_seq_start_loc,
599 - max_encoder_seq_len,
600 - )
601 -
602 -
603 -def make_kv_cache(
604 - num_blocks: int,
605 - num_heads: int,
606 - head_size: int,
607 - block_size: int,
608 - device: torch.device | str,
609 - backend: str,
610 - default_val: float = 0.0,
611 -) -> torch.Tensor:
612 - """Create a fake KV cache.
613 -
614 - Arguments:
615 - num_blocks: number of blocks in the KV cache
616 - num_heads: number of attention heads
617 - head_size: head dimension
618 - block_size: number of offsets within a block
619 - device: CPU or CUDA device
620 - backend: attention backend name; only 'FLASH_ATTN' is supported
621 - default_val: initialization value for KV cache elements
622 -
623 - Returns:
624 - kv_cache: 2 x num_blocks x block_size x num_heads x head_size
625 - for backend 'FLASH_ATTN'
626 -
627 - """
628 - if backend != "FLASH_ATTN":
629 - raise ValueError(f"Unknown backend value: '{backend}'. Expected 'FLASH_ATTN'.")
630 - kv_cache = torch.rand((2, num_blocks, block_size, num_heads, head_size)).to(device)
631 - if default_val is not None:
632 - kv_cache[:, :, :] = default_val
633 - return kv_cache
634 -
635 -
636 -def _num_tokens_to_min_blocks(num_tokens: int, block_size: int) -> int:
637 - """Compute the minimum number of blocks required to hold num_tokens tokens,
638 - given block_size
639 - """
640 - return (num_tokens + block_size - 1) // block_size
641 -
642 -
643 -def make_empty_slot_mapping_tensor(device: torch.device | str):
644 - return maybe_make_long_tensor([], device)
645 -
646 -
647 -def make_empty_block_tables_tensor(device: torch.device | str):
648 - return torch.tensor([], device=device)
649 -
650 -
651 -def split_slot_mapping(
652 - slot_mapping_list: torch.Tensor,
653 - seq_lens: list[int],
654 - device: torch.device | str,
655 -):
656 - """Split a slot mapping into valid prefill- and decode-phase slot mappings.
657 -
658 - Context:
659 - * Your goal is to test (1) prefill of N prompts, with prompt-lengths
660 - {K_i \\forall i \\in [0,N)}, followed by (2) decoding of a single token
661 - for all N prompts (N tokens total); the resultant sequence lengths
662 - after decode would be {K_i + 1 for i \\in [0,N)}
663 - * The test you want to do requires (1) having the prefill slot mapping
664 - for all tokens present during prefill, the number of which is
665 - M = \\sum_i{K_i}, and (2) having the decode slot mapping for all N
666 - decoded tokens
667 -
668 - This function consumes a single 1D slot mapping, which is the
669 - concatenation of N slot mappings each of length K_i + 1 (corresponding
670 - to the sequence lengths after decode), with a total length of
671 - P = \\sum_i{K_i + 1} = M + N
672 -
673 - The prefill-phase slot mapping results from excising the (K_i + 1)-th entry
674 - from each of the N subsequences in the slot mapping (i.e. omitting the
675 - decoded token's mapping.)
676 -
677 - The N excised entries are appended to obtain the decode-phase slot mapping
678 -
679 - Arguments:
680 - slot_mapping_list: Length-P 1D slot mapping (as list) reflecting all N
681 - post-decode sequences
682 - seq_lens: list of N post-decode sequence lengths (K_i + 1 in the
683 - description above)
684 - device: cuda, cpu, etc.
685 -
686 - Returns:
687 - prefill_slot_mapping: Length-M 1D slot mapping (as Tensor)
688 - reflecting all N prefill prompts
689 - decode_slot_mapping: Length-N 1D slot mapping (as Tensor) reflecting
690 - all N decoded tokens
691 -
692 - """
693 - prefill_slot_mapping = []
694 - decode_slot_mapping = []
695 -
696 - base_idx = 0
697 - for seq_len in seq_lens:
698 - prefill_slot_mapping.extend(
699 - slot_mapping_list[base_idx : (base_idx + seq_len - 1)]
700 - )
701 - decode_slot_mapping.append(slot_mapping_list[base_idx + seq_len - 1])
702 - base_idx += seq_len
703 -
704 - return (
705 - maybe_make_long_tensor(prefill_slot_mapping, device),
706 - maybe_make_long_tensor(decode_slot_mapping, device),
707 - )
708 -
709 -
710 -def make_block_tables_slot_mapping(
711 - block_size: int,
712 - seq_lens: list[int],
713 - device: torch.device | str,
714 - block_base_addr: int = 0,
715 -) -> tuple[torch.Tensor, list[int], int]:
716 - """Construct fake block tables & slot mappings.
717 -
718 - For a sequence with num_tokens tokens the minimum number
719 - of required KV cache blocks is
720 -
721 - num_blocks = (num_tokens + block_size - 1) // block_size
722 -
723 - Then the minimum KV cache size in blocks is
724 -
725 - total_cache_blocks = sum(num_blocks for all seqs)
726 -
727 - Then, the blocktable mapping counts downward from
728 -
729 - block_base_addr + total_cache_blocks
730 -
731 - to
732 -
733 - block_base_addr
734 -
735 -
736 - The constructed block-tables and slot-mapping are sized to the
737 - lengths of the sequences in their entirety (as reflected by seq_lens),
738 - i.e. the total of prefill prompt tokens + decoded tokens.
739 -
740 - Arguments:
741 - block_size: number of offsets per block
742 - seq_lens: list of token-counts for each sequence
743 - block_base_addr: the block table base address
744 - device: CPU or CUDA device
745 -
746 - Return:
747 - * block_tables_tensor: block table for sequence
748 - * slot_mapping_list: slot mapping for sequence
749 - * max_block_idx: the highest block address within this block table
750 -
751 - """
752 - # Provision minimum number of KV cache blocks
753 - num_blocks_list = [
754 - _num_tokens_to_min_blocks(num_tokens, block_size) for num_tokens in seq_lens
755 - ]
756 - max_block_table_len = max(num_blocks_list)
757 - block_table_pad_tokens = 10
758 -
759 - block_tables = []
760 - slot_mapping_list = []
761 - # Compute uppermost address of block table
762 - total_cache_blocks = sum(num_blocks_list)
763 - block_base_idx = block_base_addr + total_cache_blocks
764 - max_block_idx = block_base_idx
765 - for sdx, num_tokens in enumerate(seq_lens):
766 - num_blocks = num_blocks_list[sdx]
767 - block_table = list(range(block_base_idx, block_base_idx - num_blocks, -1))
768 - for idx in range(num_tokens):
769 - mapping_value = (idx % block_size) + block_table[
770 - idx // block_size
771 - ] * block_size
772 - slot_mapping_list.append(mapping_value)
773 -
774 - block_base_idx -= num_blocks
775 - block_tables.append(block_table)
776 -
777 - block_tables_tensor = make_tensor_with_pad(
778 - block_tables,
779 - max_len=max_block_table_len + block_table_pad_tokens,
780 - pad=0,
781 - dtype=torch.int,
782 - device=device,
783 - )
784 -
785 - return (block_tables_tensor, slot_mapping_list, max_block_idx)
786 -
787 -
788 -def assert_actual_matches_ideal(
789 - test_params: PhaseTestParameters, output_under_test: torch.Tensor, backend: str
790 -) -> None:
791 - """Assert that observed output matches the ideal output
792 - contained in the test parameters data structure.
793 -
794 - Arguments:
795 - test_params: Test parameters including packed ideal output
796 - output_under_test: actually observed output value
797 - backend: attention backend name; only 'FLASH_ATTN' is supported
798 -
799 - """
800 - ideal_output = test_params.packed_qkvo.ideal_output
801 - if backend != "FLASH_ATTN":
802 - raise ValueError(f"Unknown backend value: '{backend}'. Expected 'FLASH_ATTN'.")
803 - # For FlashAttention override the accuracy thresholds to non default
804 - # values since we notice a higher difference between the ideal and
805 - # actual output.
806 - torch.testing.assert_close(
807 - ideal_output, output_under_test.view_as(ideal_output), atol=0.01, rtol=0.016
808 - )
809 -
810 -
91 # Copied/modified from torch._refs.__init__.py
92 def fp8_allclose(
93 a: TensorLikeType,
tests/models/multimodal/generation/test_pixtral.py
+2 -62
@@ -6,15 +6,8 @@ from typing import TYPE_CHECKING, Any
6
7 import pytest
8 import torch
9 -from mistral_common.multimodal import download_image
10 -from mistral_common.protocol.instruct.chunk import ImageURLChunk
11 -from mistral_common.protocol.instruct.request import ChatCompletionRequest
12 -from mistral_common.tokens.tokenizers.mistral import MistralTokenizer
13 -from mistral_common.tokens.tokenizers.multimodal import image_from_chunk
14 -from transformers import AutoProcessor
15 -
16 -from vllm import SamplingParams, TextPrompt, TokensPrompt
17 -from vllm.inputs import MultiModalDataBuiltins
9 +
10 +from vllm import SamplingParams
11 from vllm.logprobs import Logprob, SampleLogprobs
12 from vllm.model_executor.models.pixtral import _make_packed_sequence_metadata
13 from vllm.platforms import current_platform
@@ -56,59 +49,6 @@ def _create_msg_format(urls: list[str]) -> list[dict[str, Any]]:
49 ]
50
51
59 -def _create_msg_format_hf(urls: list[str]) -> list[dict[str, Any]]:
60 - return [
61 - {
62 - "role": "user",
63 - "content": [
64 - {
65 - "type": "text",
66 - "content": PROMPT,
67 - },
68 - *({"type": "image", "image": download_image(url)} for url in urls),
69 - ],
70 - }
71 - ]
72 -
73 -
74 -def _create_engine_inputs(urls: list[str]) -> TokensPrompt:
75 - msg = _create_msg_format(urls)
76 -
77 - tokenizer = MistralTokenizer.from_model("pixtral")
78 -
79 - request = ChatCompletionRequest(messages=msg) # type: ignore[type-var]
80 - tokenized = tokenizer.encode_chat_completion(request)
81 -
82 - engine_inputs = TokensPrompt(prompt_token_ids=tokenized.tokens)
83 -
84 - images = []
85 - for chunk in request.messages[0].content:
86 - if isinstance(chunk, ImageURLChunk):
87 - images.append(image_from_chunk(chunk))
88 -
89 - mm_data = MultiModalDataBuiltins(image=images)
90 - engine_inputs["multi_modal_data"] = mm_data
91 -
92 - return engine_inputs
93 -
94 -
95 -def _create_engine_inputs_hf(urls: list[str]) -> TextPrompt:
96 - msg = _create_msg_format_hf(urls)
97 -
98 - tokenizer = AutoProcessor.from_pretrained("mistral-community/pixtral-12b")
99 - prompt = tokenizer.apply_chat_template(msg)
100 -
101 - images = []
102 - for chunk in msg[0]["content"]:
103 - if chunk["type"] == "image":
104 - images.append(chunk["image"])
105 -
106 - mm_data = MultiModalDataBuiltins(image=images)
107 - engine_inputs = TextPrompt(prompt=prompt, multi_modal_data=mm_data)
108 -
109 - return engine_inputs
110 -
111 -
52 SAMPLING_PARAMS = SamplingParams(max_tokens=512, temperature=0.0, logprobs=5)
53 LIMIT_MM_PER_PROMPT = dict(image=4)
54
tests/models/multimodal/generation/vlm_utils/model_utils.py
+1 -44
@@ -6,7 +6,6 @@ typically specific to a small subset of models.
6 """
7
8 import types
9 -from pathlib import PosixPath
9
10 import numpy as np
11 import numpy.typing as npt
@@ -29,7 +28,7 @@ from vllm.logprobs import SampleLogprobs
28 from vllm.platforms import current_platform
29 from vllm.utils.collection_utils import is_list_of
30
32 -from .....conftest import HfRunner, ImageAsset, ImageTestAssets
31 +from .....conftest import HfRunner, ImageTestAssets
32 from .types import RunnerOutput
33
34
@@ -48,17 +47,6 @@ def blip2_vllm_to_hf_output(vllm_output: RunnerOutput, model: str) -> RunnerOutp
47 return hf_output_ids, hf_output_str, out_logprobs
48
49
51 -def qwen_vllm_to_hf_output(
52 - vllm_output: RunnerOutput, model: str
53 -) -> tuple[list[int], str, SampleLogprobs | None]:
54 - """Sanitize vllm output [qwen models] to be comparable with hf output."""
55 - output_ids, output_str, out_logprobs = vllm_output
56 -
57 - hf_output_str = output_str + "<|endoftext|>"
58 -
59 - return output_ids, hf_output_str, out_logprobs
60 -
61 -
50 def qwen2_vllm_to_hf_output(
51 vllm_output: RunnerOutput, model: str
52 ) -> tuple[list[int], str, SampleLogprobs | None]:
@@ -239,37 +227,6 @@ def get_llava_embeddings(image_assets: ImageTestAssets):
227 return [asset.image_embeds for asset in image_assets]
228
229
242 -####### Prompt path encoders for models that need models on disk
243 -def qwen_prompt_path_encoder(
244 - tmp_path: PosixPath, prompt: str, assets: list[ImageAsset] | ImageTestAssets
245 -) -> str:
246 - """Given a temporary dir path, export one or more image assets into the
247 - tempdir & replace its contents with the local path to the string so that
248 - the HF version of Qwen-VL can resolve the path and load the image in its
249 - forward() call.
250 -
251 - Args:
252 - tmp_path: Tempdir for test under consideration.
253 - prompt: Prompt with image placeholders.
254 - assets: list of image assets whose len equals the num placeholders.
255 -
256 - """
257 - # Ensure that the number of placeholders matches the number of assets;
258 - # If this is not true, the test is probably written incorrectly.
259 - assert prompt.count("<img></img>") == len(assets)
260 -
261 - # Replace the placeholders with local paths to the exported assets
262 - for asset in assets:
263 - image_tmp_path = tmp_path / f"{asset.name}.jpg"
264 - asset.pil_image.save(image_tmp_path)
265 - prompt = prompt.replace(
266 - "<img></img>",
267 - f"<img>{image_tmp_path}</img>",
268 - 1,
269 - )
270 - return prompt
271 -
272 -
230 ####### Model-specific HuggingFace runner patchers
231 def deepseekvl2_patch_hf_runner(hf_model: HfRunner) -> HfRunner:
232 """Patches and returns an instance of the HfRunner to use for GLM4."""
tests/parser/engine/trace_builder.py
-20
@@ -1176,23 +1176,3 @@ def build_samples(model: str) -> tuple[Sample, ...]:
1176 builder = _BUILDERS[model]
1177 scenarios = _KIMI_K2_SCENARIOS if model == "kimi_k2" else SCENARIOS
1178 return tuple(s for s in (builder(sc) for sc in scenarios) if s is not None)
1179 -
1180 -
1181 -def build_sample(model: str, scenario: Scenario) -> Sample | None:
1182 - """Build a single sample for one model + scenario."""
1183 - return _BUILDERS[model](scenario)
1184 -
1185 -
1186 -def build_scaling_sample(
1187 - model: str, token_count: int, validate: bool = False
1188 -) -> Sample:
1189 - """Build a sample with approximately *token_count* tokens."""
1190 - sentence = "The quick brown fox jumps over the lazy dog. "
1191 - text = sentence * (token_count // 10 + 1)
1192 - scenario = Scenario(
1193 - id=f"scaling-{token_count}",
1194 - description=f"Scaling test with ~{token_count} tokens",
1195 - reasoning=text,
1196 - tool_calls=[_READ_TOOL],
1197 - )
1198 - return _BUILDERS[model](scenario, validate=validate)
tests/quantization/test_configs.py
-9
@@ -5,20 +5,11 @@
5 Run `pytest tests/quantization/test_configs.py --forked`.
6 """
7
8 -from dataclasses import dataclass
9 -
8 import pytest
9
10 from vllm.config import ModelConfig
11 from vllm.platforms import current_platform
12
15 -
16 -@dataclass
17 -class ModelPair:
18 - model_marlin: str
19 - model_gptq: str
20 -
21 -
13 # Model Id // Quantization Arg // Expected Type
14 MODEL_ARG_EXPTYPES = [
15 # AUTOGPTQ
tests/utils.py
-161
@@ -1,7 +1,6 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 -import asyncio
4 import atexit
5 import contextlib
6 import copy
@@ -23,7 +22,6 @@ from contextlib import ExitStack, contextmanager
22 from multiprocessing import Process, get_context
23 from pathlib import Path
24 from typing import TYPE_CHECKING, Any, Literal, cast
26 -from unittest.mock import patch
25
26 import anthropic
27 import cloudpickle
@@ -33,12 +31,9 @@ import pytest
31 import requests
32 import torch
33 import torch.nn.functional as F
36 -from huggingface_hub.constants import HF_HUB_OFFLINE
37 -from openai.types.completion import Completion
34 from typing_extensions import ParamSpec
35
36 import vllm.envs as envs
41 -from tests.models.utils import TextTextLogprobs
37 from vllm.distributed import (
38 ensure_model_parallel_initialized,
39 init_distributed_environment,
@@ -52,7 +47,6 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
47 from vllm.model_executor.model_loader import get_model_loader
48 from vllm.platforms import current_platform
49 from vllm.tokenizers import get_tokenizer
55 -from vllm.transformers_utils.repo_utils import hf_api
50 from vllm.utils.argparse_utils import FlexibleArgumentParser
51 from vllm.utils.mem_constants import GB_bytes
52 from vllm.utils.network_utils import get_open_port
@@ -69,24 +63,6 @@ logger = init_logger(__name__)
63 FP8_DTYPE = current_platform.fp8_dtype()
64
65
72 -def prewarm_hf_cache(assets: list[tuple[str, str]]) -> None:
73 - """Pre-populate the HF cache for (repo_id, filename) pairs that upstream
74 - trust_remote_code modules would otherwise fetch from third-party CDNs
75 - (often unreachable from US-based CI)."""
76 - if HF_HUB_OFFLINE:
77 - return
78 - for repo_id, filename in assets:
79 - try:
80 - hf_api().hf_hub_download(repo_id=repo_id, filename=filename)
81 - except Exception as e:
82 - logger.warning(
83 - "Failed to prefetch %s/%s: %r. Tests depending on this asset may fail.",
84 - repo_id,
85 - filename,
86 - e,
87 - )
88 -
89 -
66 if current_platform.is_cuda():
67 from vllm.third_party.pynvml import (
68 nvmlDeviceGetHandleByIndex,
@@ -1556,17 +1532,6 @@ def assert_rocm_custom_allreduce_backend_state_on_worker(
1532 )
1533
1534
1559 -@contextmanager
1560 -def error_on_warning(category: type[Warning] = Warning):
1561 - """Within the scope of this context manager, tests will fail if any warning
1562 - of the given category is emitted.
1563 - """
1564 - with warnings.catch_warnings():
1565 - warnings.filterwarnings("error", category=category)
1566 -
1567 - yield
1568 -
1569 -
1535 def get_physical_device_indices(devices: list[int]):
1536 visible_devices = os.environ.get("CUDA_VISIBLE_DEVICES")
1537 if visible_devices is None:
@@ -2157,88 +2122,6 @@ def multi_gpu_only(*, num_gpus: int = 2):
2122 return wrapper
2123
2124
2160 -async def completions_with_server_args(
2161 - prompts: list[str],
2162 - model_name: str,
2163 - server_cli_args: list[str],
2164 - num_logprobs: int | None,
2165 - max_wait_seconds: int = 240,
2166 - max_tokens: int | list = 5,
2167 -) -> list[Completion]:
2168 - """Construct a remote OpenAI server, obtain an async client to the
2169 - server & invoke the completions API to obtain completions.
2170 -
2171 - Args:
2172 - prompts: test prompts
2173 - model_name: model to spin up on the vLLM server
2174 - server_cli_args: CLI args for starting the server
2175 - num_logprobs: Number of logprobs to report (or `None`)
2176 - max_wait_seconds: timeout interval for bringing up server.
2177 - Default: 240sec
2178 - max_tokens: max_tokens value for each of the given input prompts.
2179 - if only one max_token value is given, the same value is used
2180 - for all the prompts.
2181 -
2182 - Returns:
2183 - OpenAI Completion instance
2184 -
2185 - """
2186 - if isinstance(max_tokens, int):
2187 - max_tokens = [max_tokens] * len(prompts)
2188 -
2189 - assert len(max_tokens) == len(prompts)
2190 -
2191 - outputs = None
2192 - with RemoteOpenAIServer(
2193 - model_name, server_cli_args, max_wait_seconds=max_wait_seconds
2194 - ) as server:
2195 - client = server.get_async_client()
2196 - outputs = [
2197 - client.completions.create(
2198 - model=model_name,
2199 - prompt=[p],
2200 - temperature=0,
2201 - stream=False,
2202 - max_tokens=max_tok,
2203 - logprobs=num_logprobs,
2204 - )
2205 - for p, max_tok in zip(prompts, max_tokens)
2206 - ]
2207 - outputs = await asyncio.gather(*outputs)
2208 -
2209 - assert outputs is not None, "Completion API call failed."
2210 -
2211 - return outputs
2212 -
2213 -
2214 -def get_client_text_generations(completions: list[Completion]) -> list[str]:
2215 - """Extract generated tokens from the output of a
2216 - request made to an Open-AI-protocol completions endpoint.
2217 - """
2218 - assert all([len(x.choices) == 1 for x in completions])
2219 - return [x.choices[0].text for x in completions]
2220 -
2221 -
2222 -def get_client_text_logprob_generations(
2223 - completions: list[Completion],
2224 -) -> list[TextTextLogprobs]:
2225 - """Operates on the output of a request made to an Open-AI-protocol
2226 - completions endpoint; obtains top-rank logprobs for each token in
2227 - each {class}`SequenceGroup`
2228 - """
2229 - text_generations = get_client_text_generations(completions)
2230 - text = "".join(text_generations)
2231 - return [
2232 - (
2233 - text_generations,
2234 - text,
2235 - (None if x.logprobs is None else x.logprobs.top_logprobs),
2236 - )
2237 - for completion in completions
2238 - for x in completion.choices
2239 - ]
2240 -
2241 -
2125 def has_module_attribute(module_name, attribute_name):
2126 """Helper function to check if a module has a specific attribute."""
2127 try:
@@ -2267,50 +2150,6 @@ def get_attn_backend_list_based_on_platform() -> list[str]:
2150 raise ValueError("Unsupported platform")
2151
2152
2270 -@contextmanager
2271 -def override_cutlass_fp8_supported(value: bool):
2272 - with patch(
2273 - "vllm.model_executor.layers.quantization.utils.w8a8_utils.cutlass_fp8_supported",
2274 - return_value=value,
2275 - ):
2276 - yield
2277 -
2278 -
2279 -def disable_aiter_plain_rmsnorm(monkeypatch) -> None:
2280 - """Patch dispatch_rocm_rmsnorm_func so the plain (non-fused) rms_norm path
2281 - always uses the native float32 kernel for the duration of a test.
2282 -
2283 - The fused path (rms_norm2d_with_add, selected when with_fused_add=True) is
2284 - left on AITER -- only the plain path is redirected to native.
2285 -
2286 - AITER's plain rms_norm accumulates variance in bfloat16 (~1 ULP/call),
2287 - which drifts the KV cache over many decode steps. This drift is irrelevant
2288 - for a trained model (rank-1/rank-2 gap ~1-3 nats >> 1 ULP), but breaks
2289 - logprob comparison tests with randomly-initialised models like
2290 - TitanML/tiny-mixtral whose rank-1/rank-2 gap is only O(1/sqrt(V)) ~0.006
2291 - nats -- smaller than the accumulated per-step error.
2292 - """
2293 - import torch
2294 -
2295 - import vllm.model_executor.layers.layernorm as _ln_mod
2296 - from vllm.model_executor.layers.layernorm import rms_norm as _native
2297 -
2298 - _orig = _ln_mod.dispatch_rocm_rmsnorm_func
2299 -
2300 - def _native_plain(
2301 - with_fused_add: bool, dtype: torch.dtype, use_aiter: bool = False
2302 - ):
2303 - if (
2304 - use_aiter
2305 - and not with_fused_add
2306 - and dtype in (torch.float16, torch.bfloat16)
2307 - ):
2308 - return _native
2309 - return _orig(with_fused_add, dtype, use_aiter)
2310 -
2311 - monkeypatch.setattr(_ln_mod, "dispatch_rocm_rmsnorm_func", _native_plain)
2312 -
2313 -
2153 def prep_prompts(batch_size: int, ln_range: tuple[int, int] = (800, 1100)):
2154 """Generate prompts which a bunch of assignments,
2155 then asking for the value of one of them.
tests/v1/attention/utils.py
-21
@@ -262,27 +262,6 @@ def create_vllm_config(
262 )
263
264
265 -def create_dummy_kv_cache(
266 - block_size: int,
267 - num_kv_heads: int,
268 - head_size: int,
269 - dtype: torch.dtype,
270 - device: torch.device,
271 - num_blocks: int = 100,
272 -) -> torch.Tensor:
273 - """Create a dummy KV cache tensor for testing."""
274 - kv_cache = torch.randn(
275 - num_blocks,
276 - 2, # K and V
277 - block_size,
278 - num_kv_heads,
279 - head_size,
280 - dtype=dtype,
281 - device=device,
282 - )
283 - return kv_cache
284 -
285 -
265 @dataclass
266 class BackendConfig:
267 name: str
tests/v1/engine/utils.py
-17
@@ -196,23 +196,6 @@ def _create_random_top_token_test_matrix(
196 return matrix, prompt_token_ranks
197
198
199 -def decode_token(
200 - tok_id: int,
201 - tokenizer: PythonBackend,
202 -) -> str:
203 - """Reproduce the process of detokenizing a token for testing purposes.
204 -
205 - Args:
206 - tok_id: token id to detokenize
207 - tokenizer: tokenizer to use for detokenization
208 -
209 - Returns:
210 - string representation of token
211 -
212 - """
213 - return tokenizer.convert_ids_to_tokens(tok_id)
214 -
215 -
199 def generate_dummy_sample_logprobs(
200 sampled_tokens_list: list,
201 num_logprobs: int,