[Misc] Remove dead code in ViT functionality test (#48220)
Signed-off-by: Isotr0py <Isotr0py@outlook.com>
Isotr0py committed
Jul 10, 2026 at 19:17 UTC
68ea76e78005bb53d9ef6b855b6c23411f5bb1e1
1 file changed
-438
tests/models/multimodal/generation/test_vit_backend_functionality.py
deleted
-438
@@ -1,438 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-"""
4
-Consolidated test for ViT attention backend functionality across multiple models.
5
-
6
-This test validates that each multimodal model can successfully generate outputs
7
-using different ViT attention backends. Tests are parametrized by model and backend.
8
-"""
9
-
10
-from typing import Any
11
-
12
-import pytest
13
-from transformers import AutoProcessor
14
-
15
-from vllm import LLM, SamplingParams
16
-from vllm.multimodal.utils import encode_image_url
17
-from vllm.multimodal.video import sample_frames_from_video
18
-from vllm.platforms import current_platform
19
-from vllm.v1.attention.backends.registry import AttentionBackendEnum
20
-
21
-from ....utils import create_new_process_for_each_test
22
-from ...utils import dummy_hf_overrides
23
-
24
-# Dots.OCR prompt from official repository
25
-# https://github.com/rednote-hilab/dots.ocr/blob/d72d1d8c5bdd0362eb264f714cdbd1e5daa7cdff/dots_ocr/utils/prompts.py#L3
26
-# ruff: noqa: E501
27
-DOTS_OCR_PROMPT = """Please output the layout information from the PDF image, including each layout element's bbox, its category, and the corresponding text content within the bbox.
28
-
29
-1. Bbox format: [x1, y1, x2, y2]
30
-
31
-2. Layout Categories: The possible categories are ['Caption', 'Footnote', 'Formula', 'List-item', 'Page-footer', 'Page-header', 'Picture', 'Section-header', 'Table', 'Text', 'Title'].
32
-
33
-3. Text Extraction & Formatting Rules:
34
- - Picture: For the 'Picture' category, the text field should be omitted.
35
- - Formula: Format its text as LaTeX.
36
- - Table: Format its text as HTML.
37
- - All Others (Text, Title, etc.): Format their text as Markdown.
38
-
39
-4. Constraints:
40
- - The output text must be the original text from the image, with no translation.
41
- - All layout elements must be sorted according to human reading order.
42
-
43
-5. Final Output: The entire output must be a single JSON object.
44
-"""
45
-
46
-VIDEO_PLACEHOLDER = "<|vision_start|><|video_pad|><|vision_end|>"
47
-
48
-
49
-# Model configurations
50
-MODEL_CONFIGS: dict[str, dict[str, Any]] = {
51
- "dots_ocr": {
52
- "model_name": "rednote-hilab/dots.ocr",
53
- "interface": "llm_chat",
54
- "max_model_len": 32768,
55
- "max_num_seqs": 1,
56
- "limit_mm_per_prompt": {"image": 1},
57
- "sampling_params": {
58
- "temperature": 0.1,
59
- "max_tokens": 16384,
60
- "top_p": 0.9,
61
- "stop_token_ids": None,
62
- },
63
- "use_specific_image": "stop_sign",
64
- "prompt_builder": "build_dots_ocr_prompt",
65
- "output_validator": lambda x: len(x) > 10 and "stop" in x.lower(),
66
- },
67
- "ernie45_vl": {
68
- "model_name": "baidu/ERNIE-4.5-VL-28B-A3B-PT",
69
- "interface": "llm_generate",
70
- "max_model_len": 16384,
71
- "max_num_seqs": 2,
72
- "sampling_params": {
73
- "temperature": 0.0,
74
- "max_tokens": 256,
75
- "stop_token_ids": None,
76
- },
77
- "use_processor": True,
78
- "question": "What is the content of each image?",
79
- },
80
- "glm4_1v": {
81
- "model_name": "zai-org/GLM-4.1V-9B-Thinking",
82
- "interface": "llm_generate",
83
- "max_model_len": 32768,
84
- "max_num_seqs": 2,
85
- "sampling_params": {
86
- "temperature": 0.0,
87
- "max_tokens": 256,
88
- "stop_token_ids": None,
89
- },
90
- "use_processor": True,
91
- "question": "What is the content of each image?",
92
- },
93
- "glm_ocr": {
94
- "model_name": "zai-org/GLM-OCR",
95
- "interface": "llm_generate",
96
- "max_model_len": 131072,
97
- "max_num_seqs": 2,
98
- "sampling_params": {
99
- "temperature": 0.0,
100
- "max_tokens": 256,
101
- "stop_token_ids": None,
102
- },
103
- "use_processor": True,
104
- "question": "Text Recognition:",
105
- },
106
- "keye_vl": {
107
- "model_name": "Kwai-Keye/Keye-VL-8B-Preview",
108
- "interface": "llm_generate",
109
- "max_model_len": 8192,
110
- "max_num_seqs": 5,
111
- "sampling_params": {
112
- "temperature": 0.0,
113
- "max_tokens": 256,
114
- "stop_token_ids": None,
115
- },
116
- "supported_backends": {
117
- AttentionBackendEnum.FLASH_ATTN,
118
- AttentionBackendEnum.ROCM_AITER_FA,
119
- },
120
- "use_processor": True,
121
- "question": "What is the content of each image?",
122
- },
123
- "ovis2_5": {
124
- "model_name": "AIDC-AI/Ovis2.5-2B",
125
- "interface": "llm_generate",
126
- "max_model_len": 8192,
127
- "max_num_seqs": 2,
128
- "sampling_params": {
129
- "temperature": 0.0,
130
- "max_tokens": 256,
131
- "stop_token_ids": None,
132
- },
133
- "prompt_builder": "build_ovis_prompt",
134
- "question": "What is the content of each image?",
135
- },
136
- "qwen2_5_vl": {
137
- "model_name": "Qwen/Qwen2.5-VL-3B-Instruct",
138
- "interface": "vllm_runner",
139
- "media_type": "video",
140
- "max_model_len": 4000,
141
- "max_num_seqs": 1,
142
- "limit_mm_per_prompt": {"video": 1},
143
- "sampling_params": {
144
- "max_tokens": 128,
145
- },
146
- "runner_kwargs": {
147
- "runner": "generate",
148
- "dtype": "bfloat16",
149
- },
150
- "video_params": {
151
- "num_frames": 16,
152
- "pruning_rates": [0.0, 0.75],
153
- },
154
- },
155
- "qwen2_5_omni": {
156
- "model_name": "Qwen/Qwen2.5-Omni-3B",
157
- "interface": "llm_generate",
158
- "max_model_len": 32768,
159
- "max_num_seqs": 2,
160
- "limit_mm_per_prompt": {"image": 3, "video": 3, "audio": 3},
161
- "sampling_params": {
162
- "temperature": 0.6,
163
- "top_p": 0.95,
164
- "top_k": 20,
165
- "max_tokens": 16384,
166
- },
167
- "use_processor": True,
168
- "question": "What is the content of each image?",
169
- },
170
- "qwen3_omni": {
171
- "model_name": "Qwen/Qwen3-Omni-30B-A3B-Instruct",
172
- "interface": "llm_generate",
173
- "max_model_len": 32768,
174
- "max_num_seqs": 2,
175
- "limit_mm_per_prompt": {"image": 3, "video": 3, "audio": 3},
176
- "sampling_params": {
177
- "temperature": 0.6,
178
- "top_p": 0.95,
179
- "top_k": 20,
180
- "max_tokens": 16384,
181
- },
182
- "use_processor": True,
183
- "question": "What is the content of each image?",
184
- },
185
-}
186
-
187
-
188
-# Prompt builder functions
189
-def build_dots_ocr_prompt(images, config):
190
- """Build Dots.OCR specific prompt with OCR instructions."""
191
- # Use only stop_sign image for Dots.OCR
192
- image = images[0] # Already filtered to stop_sign
193
- image_url = encode_image_url(image)
194
-
195
- placeholders = [{"type": "image_url", "image_url": {"url": image_url}}]
196
- messages = [
197
- {
198
- "role": "user",
199
- "content": [
200
- *placeholders,
201
- {
202
- "type": "text",
203
- "text": f"<|img|><|imgpad|><|endofimg|>{DOTS_OCR_PROMPT}",
204
- },
205
- ],
206
- },
207
- ]
208
-
209
- return messages
210
-
211
-
212
-def build_processor_prompt(images, config):
213
- """Build prompt using AutoProcessor.apply_chat_template()."""
214
- processor = AutoProcessor.from_pretrained(
215
- config["model_name"], trust_remote_code=True
216
- )
217
-
218
- image_urls = [encode_image_url(img) for img in images]
219
- placeholders = [{"type": "image", "image": url} for url in image_urls]
220
- messages = [
221
- {
222
- "role": "user",
223
- "content": [
224
- *placeholders,
225
- {"type": "text", "text": config["question"]},
226
- ],
227
- },
228
- ]
229
-
230
- return processor.apply_chat_template(
231
- messages, tokenize=False, add_generation_prompt=True
232
- )
233
-
234
-
235
-def build_ovis_prompt(images, config):
236
- """Build Ovis2.5 specific prompt with custom format."""
237
- image_urls = [encode_image_url(img) for img in images]
238
-
239
- placeholders = "\n".join(
240
- f"Image-{i}: <image>\n" for i, _ in enumerate(image_urls, start=1)
241
- )
242
-
243
- return (
244
- f"<|im_start|>user\n\n{placeholders}\n{config['question']}<|im_end|>\n"
245
- "<|im_start|>assistant\n"
246
- )
247
-
248
-
249
-def build_qwen2_5_video_prompt():
250
- """Build Qwen2.5-VL video prompt with EVS placeholder."""
251
- return (
252
- f"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n"
253
- f"<|im_start|>user\n{VIDEO_PLACEHOLDER}"
254
- "Describe this video with a short sentence (no more than 20 words)"
255
- "<|im_end|><|im_start|>assistant\n"
256
- )
257
-
258
-
259
-# Handler functions
260
-def run_llm_generate_test(config, mm_encoder_attn_backend, image_assets):
261
- """Standard LLM.generate() interface handler."""
262
- images = [asset.pil_image for asset in image_assets]
263
-
264
- # Build prompt
265
- if config.get("use_processor"):
266
- prompt = build_processor_prompt(images, config)
267
- else:
268
- prompt_builder_name = config.get("prompt_builder", "build_ovis_prompt")
269
- prompt_builder = globals()[prompt_builder_name]
270
- prompt = prompt_builder(images, config)
271
-
272
- # Determine limit_mm_per_prompt
273
- limit_mm_per_prompt = config.get("limit_mm_per_prompt", {"image": len(images)})
274
-
275
- # Create engine
276
- llm = LLM(
277
- model=config["model_name"],
278
- trust_remote_code=True,
279
- max_model_len=config["max_model_len"],
280
- max_num_seqs=config["max_num_seqs"],
281
- limit_mm_per_prompt=limit_mm_per_prompt,
282
- mm_encoder_attn_backend=mm_encoder_attn_backend,
283
- hf_overrides=dummy_hf_overrides,
284
- load_format="dummy",
285
- seed=42,
286
- )
287
-
288
- # Generate
289
- sampling_params = SamplingParams(**config["sampling_params"])
290
- outputs = llm.generate(
291
- {
292
- "prompt": prompt,
293
- "multi_modal_data": {"image": images},
294
- },
295
- sampling_params=sampling_params,
296
- )
297
-
298
- # Validate
299
- for o in outputs:
300
- generated_text = o.outputs[0].text
301
- validator = config.get("output_validator", lambda x: len(x) > 10)
302
- assert validator(generated_text), (
303
- f"Validation failed for {config['model_name']}: {generated_text}"
304
- )
305
-
306
-
307
-def run_llm_chat_test(config, mm_encoder_attn_backend, image_assets):
308
- """LLM.chat() interface handler for Dots.OCR."""
309
- # Filter to stop_sign image only
310
- stop_sign_image = [
311
- asset.pil_image for asset in image_assets if asset.name == "stop_sign"
312
- ][0]
313
-
314
- # Build messages
315
- messages = build_dots_ocr_prompt([stop_sign_image], config)
316
-
317
- # Create engine
318
- llm = LLM(
319
- model=config["model_name"],
320
- trust_remote_code=True,
321
- max_model_len=config["max_model_len"],
322
- max_num_seqs=config["max_num_seqs"],
323
- limit_mm_per_prompt=config["limit_mm_per_prompt"],
324
- mm_encoder_attn_backend=mm_encoder_attn_backend,
325
- hf_overrides=dummy_hf_overrides,
326
- load_format="dummy",
327
- seed=42,
328
- )
329
-
330
- # Generate using chat
331
- sampling_params = SamplingParams(**config["sampling_params"])
332
- outputs = llm.chat(messages=messages, sampling_params=sampling_params)
333
-
334
- # Validate
335
- for o in outputs:
336
- generated_text = o.outputs[0].text
337
- validator = config.get("output_validator", lambda x: len(x) > 10)
338
- assert validator(generated_text), (
339
- f"Validation failed for {config['model_name']}: {generated_text}"
340
- )
341
-
342
-
343
-def run_video_test(config, mm_encoder_attn_backend, video_assets, vllm_runner):
344
- """Video test with EVS (Efficient Video Sampling) handler."""
345
- for pruning_rate in config["video_params"]["pruning_rates"]:
346
- num_frames = config["video_params"]["num_frames"]
347
-
348
- # Sample frames from video
349
- sampled_vids = [
350
- sample_frames_from_video(asset.np_ndarrays, num_frames)
351
- for asset in video_assets
352
- ]
353
-
354
- # Build prompt and prepare video
355
- prompt = build_qwen2_5_video_prompt()
356
- prompts = [prompt]
357
- videos = [sampled_vids[0]]
358
-
359
- # Run with vllm_runner context manager
360
- with vllm_runner(
361
- config["model_name"],
362
- max_model_len=config["max_model_len"],
363
- max_num_seqs=config["max_num_seqs"],
364
- limit_mm_per_prompt=config["limit_mm_per_prompt"],
365
- tensor_parallel_size=1,
366
- video_pruning_rate=pruning_rate,
367
- mm_encoder_attn_backend=mm_encoder_attn_backend,
368
- hf_overrides=dummy_hf_overrides,
369
- load_format="dummy",
370
- **config["runner_kwargs"],
371
- ) as vllm_model:
372
- outputs = vllm_model.generate_greedy(
373
- prompts,
374
- config["sampling_params"]["max_tokens"],
375
- videos=videos,
376
- )
377
-
378
- # Validate output
379
- assert len(outputs) == 1, f"Expected 1 output, got {len(outputs)}"
380
- output_ids, output_text = outputs[0]
381
- assert len(output_ids) > 0, "Generated no output IDs"
382
- assert len(output_text) > 0, "Generated empty text"
383
- assert isinstance(output_text, str), (
384
- f"Output is not string: {type(output_text)}"
385
- )
386
-
387
-
388
-# Main test function
389
-@pytest.mark.parametrize("model_key", list(MODEL_CONFIGS.keys()))
390
-@pytest.mark.parametrize(
391
- "mm_encoder_attn_backend",
392
- [None] + current_platform.get_supported_vit_attn_backends(),
393
-)
394
-@pytest.mark.skip(reason="Broken test due to memory segmentation fault")
395
-@create_new_process_for_each_test()
396
-def test_vit_backend_functionality(
397
- model_key: str,
398
- mm_encoder_attn_backend: AttentionBackendEnum | None,
399
- image_assets,
400
- video_assets,
401
- vllm_runner,
402
- request,
403
-):
404
- """Test ViT attention backend functionality for multimodal models.
405
-
406
- This test validates that each model can successfully generate outputs
407
- using different ViT attention backends. The test:
408
- 1. Filters unsupported backends per model
409
- 2. Applies appropriate GPU marks
410
- 3. Routes to the correct test handler based on interface
411
- 4. Validates output meets minimum requirements
412
- """
413
- config = MODEL_CONFIGS[model_key]
414
-
415
- # Step 1: Backend filtering
416
- if (
417
- "supported_backends" in config
418
- and mm_encoder_attn_backend is not None
419
- and mm_encoder_attn_backend not in config["supported_backends"]
420
- ):
421
- pytest.skip(
422
- f"{model_key} does not support {mm_encoder_attn_backend} backend now."
423
- )
424
-
425
- # Step 2: Apply GPU marks dynamically
426
- if "gpu_marks" in config:
427
- for mark in config["gpu_marks"]:
428
- request.applymarker(mark)
429
-
430
- # Step 3: Route to appropriate handler
431
- if config.get("media_type") == "video":
432
- run_video_test(config, mm_encoder_attn_backend, video_assets, vllm_runner)
433
- elif config["interface"] == "llm_chat":
434
- run_llm_chat_test(config, mm_encoder_attn_backend, image_assets)
435
- elif config["interface"] == "llm_generate":
436
- run_llm_generate_test(config, mm_encoder_attn_backend, image_assets)
437
- else:
438
- raise ValueError(f"Unknown interface: {config['interface']}")