main
py 1,352 lines 53.8 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Common tests for testing .generate() functionality for single / multiple
4 image, embedding, and video support for different VLMs in vLLM.
5 """
6
7 import math
8 from collections import defaultdict
9 from pathlib import PosixPath
10
11 import pytest
12 from packaging.version import Version
13 from transformers import (
14 AutoModel,
15 AutoModelForCausalLM,
16 AutoModelForImageTextToText,
17 AutoModelForTextToWaveform,
18 )
19 from transformers import __version__ as TRANSFORMERS_VERSION
20
21 from vllm.platforms import current_platform
22 from vllm.utils.func_utils import identity
23
24 from ....conftest import (
25 IMAGE_ASSETS,
26 AudioTestAssets,
27 HfRunner,
28 ImageTestAssets,
29 VideoTestAssets,
30 VllmRunner,
31 )
32 from ....utils import create_new_process_for_each_test, large_gpu_mark, multi_gpu_marks
33 from .vlm_utils import custom_inputs, model_utils, runners
34 from .vlm_utils.case_filtering import get_parametrized_options
35 from .vlm_utils.types import (
36 CustomTestOptions,
37 ExpandableVLMTestArgs,
38 VLMTestInfo,
39 VLMTestType,
40 )
41
42 COMMON_BROADCAST_SETTINGS = {
43 "test_type": VLMTestType.IMAGE,
44 "dtype": "half",
45 "max_tokens": 5,
46 "tensor_parallel_size": 2,
47 "hf_model_kwargs": {"device_map": "auto"},
48 "image_size_factors": [(0.25, 0.5, 1.0)],
49 "distributed_executor_backend": (
50 "ray",
51 "mp",
52 ),
53 }
54
55 ### Test configuration for specific models
56 # NOTE: The convention of the test settings below is to lead each test key
57 # with the name of the model arch used in the test, using underscores in place
58 # of hyphens; this makes it more convenient to filter tests for a specific kind
59 # of model. For example....
60 #
61 # To run all test types for a specific key:
62 # use the k flag to substring match with a leading square bracket; if the
63 # model arch happens to be a substring of another one, you can add a
64 # trailing hyphen. E.g.,
65 # - pytest $TEST_FILE -k "[llava-"
66 # prevents matching on "[llava_next-" & will match just the enabled cases
67 # for llava, i.e., single image, image embedding, and custom input tests.
68 #
69 # To run a test for a Test Info for just one of multiple models:
70 # use the k flag to substring match the model name, e.g.,
71 # - pytest $TEST_FILE -k OpenGVLab/InternVL2-1B
72 # prevents matching on nGVLab/InternVL2-2B.
73 #
74 # You can also combine substrings to match more granularly.
75 # ex 1:
76 # pytest $TEST_FILE -k "test_single_image and OpenGVLab/InternVL2-1B"
77 # will run only test_single_image* for OpenGVLab/InternVL2-1B; this would
78 # match both wrappers for single image tests, since it also matches
79 # test_single_image_heavy (which forks if we have a distributed backend)
80 # ex 2:
81 # pytest $TEST_FILE -k "[llava- or [intern_vl-"
82 # will run all of the tests for only llava & internvl.
83 #
84 # NOTE you can add --collect-only to any of the above commands to see
85 # which cases would be selected and deselected by pytest. In general,
86 # this is a good idea for checking your command first, since tests are slow.
87
88
89 def _granite4_vision_vllm_to_hf_output(vllm_output, model):
90 """Post-processor for granite4_vision vLLM output.
91
92 Self-contained to avoid calling AutoConfig/AutoTokenizer without
93 trust_remote_code (needed while the model is not in upstream HF).
94 """
95 output_ids, output_str, out_logprobs = vllm_output
96 mm_token_id = 100352
97 hf_output_ids = [
98 token_id
99 for idx, token_id in enumerate(output_ids)
100 if token_id != mm_token_id or idx == 0 or output_ids[idx - 1] != mm_token_id
101 ]
102 hf_output_str = (
103 output_str[1:] if output_str and output_str[0] == " " else output_str
104 )
105 eos_token_id = 100257
106 if hf_output_ids and hf_output_ids[-1] == eos_token_id:
107 hf_output_str = hf_output_str + "<|end_of_text|>"
108 return hf_output_ids, hf_output_str, out_logprobs
109
110
111 VLM_TEST_SETTINGS = {
112 #### Core tests to always run in the CI
113 "llava": VLMTestInfo(
114 models=["llava-hf/llava-1.5-7b-hf"],
115 test_type=(VLMTestType.EMBEDDING, VLMTestType.IMAGE, VLMTestType.CUSTOM_INPUTS),
116 prompt_formatter=lambda img_prompt: f"USER: {img_prompt}\nASSISTANT:",
117 convert_assets_to_embeddings=model_utils.get_llava_embeddings,
118 max_model_len=4096,
119 auto_cls=AutoModelForImageTextToText,
120 vllm_output_post_proc=model_utils.llava_image_vllm_to_hf_output,
121 custom_test_opts=[
122 CustomTestOptions(
123 inputs=custom_inputs.multi_image_multi_aspect_ratio_inputs(
124 formatter=lambda img_prompt: f"USER: {img_prompt}\nASSISTANT:"
125 ),
126 limit_mm_per_prompt={"image": 4},
127 )
128 ],
129 vllm_runner_kwargs={"enable_mm_embeds": True},
130 marks=[pytest.mark.core_model, pytest.mark.cpu_model],
131 ),
132 "paligemma": VLMTestInfo(
133 models=["google/paligemma-3b-mix-224"],
134 test_type=VLMTestType.IMAGE,
135 prompt_formatter=identity,
136 img_idx_to_prompt=lambda idx: "",
137 # Paligemma uses its own sample prompts because the default one fails
138 single_image_prompts=IMAGE_ASSETS.prompts(
139 {
140 "stop_sign": "caption es",
141 "cherry_blossom": "What is in the picture?",
142 }
143 ),
144 auto_cls=AutoModelForImageTextToText,
145 vllm_output_post_proc=model_utils.paligemma_vllm_to_hf_output,
146 ),
147 "qwen2_5_vl": VLMTestInfo(
148 models=["Qwen/Qwen2.5-VL-3B-Instruct"],
149 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE, VLMTestType.VIDEO),
150 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
151 img_idx_to_prompt=lambda idx: "<|vision_start|><|image_pad|><|vision_end|>",
152 video_idx_to_prompt=lambda idx: "<|vision_start|><|video_pad|><|vision_end|>",
153 enforce_eager=False,
154 needs_video_metadata=True,
155 max_model_len=4096,
156 max_num_seqs=2,
157 auto_cls=AutoModelForImageTextToText,
158 vllm_output_post_proc=model_utils.qwen2_vllm_to_hf_output,
159 patch_hf_runner=model_utils.qwen3_vl_patch_hf_runner,
160 image_size_factors=[(0.25,), (0.25, 0.25, 0.25), (0.25, 0.2, 0.15)],
161 marks=[pytest.mark.core_model, pytest.mark.cpu_model],
162 ),
163 "qwen2_5_omni": VLMTestInfo(
164 models=["Qwen/Qwen2.5-Omni-3B"],
165 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE, VLMTestType.VIDEO),
166 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
167 img_idx_to_prompt=lambda idx: "<|vision_bos|><|IMAGE|><|vision_eos|>",
168 video_idx_to_prompt=lambda idx: "<|vision_bos|><|VIDEO|><|vision_eos|>",
169 max_model_len=4096,
170 max_num_seqs=2,
171 num_logprobs=6 if current_platform.is_cpu() else 5,
172 auto_cls=AutoModelForTextToWaveform,
173 vllm_output_post_proc=model_utils.qwen2_vllm_to_hf_output,
174 patch_hf_runner=model_utils.qwen2_5_omni_patch_hf_runner,
175 image_size_factors=[(0.25,), (0.25, 0.25, 0.25), (0.25, 0.2, 0.15)],
176 marks=[pytest.mark.core_model, pytest.mark.cpu_model],
177 ),
178 "qwen3_vl": VLMTestInfo(
179 models=["Qwen/Qwen3-VL-4B-Instruct"],
180 test_type=(
181 VLMTestType.IMAGE,
182 VLMTestType.MULTI_IMAGE,
183 VLMTestType.VIDEO,
184 ),
185 enforce_eager=False,
186 needs_video_metadata=True,
187 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
188 img_idx_to_prompt=lambda idx: "<|vision_start|><|image_pad|><|vision_end|>", # noqa: E501
189 video_idx_to_prompt=lambda idx: "<|vision_start|><|video_pad|><|vision_end|>", # noqa: E501
190 max_model_len=4096,
191 max_num_seqs=2,
192 num_logprobs=20,
193 auto_cls=AutoModelForImageTextToText,
194 vllm_output_post_proc=model_utils.qwen2_vllm_to_hf_output,
195 patch_hf_runner=model_utils.qwen3_vl_patch_hf_runner,
196 image_size_factors=[(0.25,), (0.25, 0.25, 0.25), (0.25, 0.2, 0.15)],
197 marks=[
198 pytest.mark.core_model,
199 ],
200 vllm_runner_kwargs={"attention_backend": "TRITON_ATTN"}
201 if current_platform.is_rocm()
202 else {},
203 ),
204 "ultravox": VLMTestInfo(
205 models=["fixie-ai/ultravox-v0_5-llama-3_2-1b"],
206 test_type=VLMTestType.AUDIO,
207 prompt_formatter=lambda audio_prompt: f"<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n{audio_prompt}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n", # noqa: E501
208 audio_idx_to_prompt=lambda idx: "<|audio|>",
209 max_model_len=4096,
210 max_num_seqs=2,
211 auto_cls=AutoModel,
212 hf_output_post_proc=model_utils.ultravox_trunc_hf_output,
213 marks=[
214 pytest.mark.core_model,
215 pytest.mark.cpu_model,
216 # TODO: Remove skip once model has been upstreamed to Transformers
217 pytest.mark.skip(
218 reason="Custom model code is not compatible with Transformers v5"
219 ),
220 ],
221 ),
222 #### Transformers fallback to test
223 ## To reduce test burden, we only test batching arbitrary image size
224 # Dynamic image length and number of patches
225 "llava-onevision-transformers": VLMTestInfo(
226 models=["llava-hf/llava-onevision-qwen2-0.5b-ov-hf"],
227 test_type=VLMTestType.IMAGE,
228 prompt_formatter=lambda vid_prompt: f"<|im_start|>user\n{vid_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
229 max_model_len=16384,
230 hf_model_kwargs=model_utils.llava_onevision_hf_model_kwargs(
231 "llava-hf/llava-onevision-qwen2-0.5b-ov-hf"
232 ),
233 auto_cls=AutoModelForImageTextToText,
234 vllm_output_post_proc=model_utils.llava_onevision_vllm_to_hf_output,
235 image_size_factors=[(0.25, 0.5, 1.0)],
236 vllm_runner_kwargs={
237 "model_impl": "transformers",
238 },
239 marks=[pytest.mark.core_model],
240 ),
241 # Gemma3 has bidirectional mask on images
242 "gemma3-transformers": VLMTestInfo(
243 models=["google/gemma-3-4b-it"],
244 test_type=VLMTestType.IMAGE,
245 prompt_formatter=lambda vid_prompt: f"<'<bos><start_of_turn>user\n{vid_prompt}<start_of_image><end_of_turn>\n<start_of_turn>model\n", # noqa: E501
246 max_model_len=4096,
247 auto_cls=AutoModelForImageTextToText,
248 vllm_output_post_proc=model_utils.gemma3_vllm_to_hf_output,
249 image_size_factors=[(0.25, 0.5, 1.0)],
250 vllm_runner_kwargs={
251 "model_impl": "transformers",
252 },
253 marks=[pytest.mark.core_model],
254 ),
255 "idefics3-transformers": VLMTestInfo(
256 models=["HuggingFaceTB/SmolVLM-256M-Instruct"],
257 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
258 prompt_formatter=lambda img_prompt: f"<|begin_of_text|>User:{img_prompt}<end_of_utterance>\nAssistant:", # noqa: E501
259 img_idx_to_prompt=lambda idx: "<image>",
260 max_model_len=8192,
261 max_num_seqs=2,
262 auto_cls=AutoModelForImageTextToText,
263 hf_output_post_proc=model_utils.idefics3_trunc_hf_output,
264 image_size_factors=[(0.25, 0.5, 1.0)],
265 vllm_runner_kwargs={
266 "model_impl": "transformers",
267 },
268 marks=[pytest.mark.core_model],
269 ),
270 # Pixel values from processor are not 4D or 5D arrays
271 "qwen2_5_vl-transformers": VLMTestInfo(
272 models=["Qwen/Qwen2.5-VL-3B-Instruct"],
273 test_type=VLMTestType.IMAGE,
274 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
275 img_idx_to_prompt=lambda idx: "<|vision_start|><|image_pad|><|vision_end|>",
276 max_model_len=4096,
277 max_num_seqs=2,
278 auto_cls=AutoModelForImageTextToText,
279 vllm_output_post_proc=model_utils.qwen2_vllm_to_hf_output,
280 image_size_factors=[(0.25, 0.2, 0.15)],
281 vllm_runner_kwargs={
282 "model_impl": "transformers",
283 },
284 marks=[large_gpu_mark(min_gb=32)],
285 ),
286 #### Extended model tests
287 "aria": VLMTestInfo(
288 models=["rhymes-ai/Aria"],
289 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
290 prompt_formatter=lambda img_prompt: f"<|im_start|>user\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n ", # noqa: E501
291 img_idx_to_prompt=lambda idx: "<fim_prefix><|img|><fim_suffix>\n",
292 max_model_len=4096,
293 max_num_seqs=2,
294 auto_cls=AutoModelForImageTextToText,
295 single_image_prompts=IMAGE_ASSETS.prompts(
296 {
297 "stop_sign": "<vlm_image>Please describe the image shortly.",
298 "cherry_blossom": "<vlm_image>Please infer the season with reason.",
299 }
300 ),
301 multi_image_prompt="<vlm_image><vlm_image>Describe the two images shortly.",
302 stop_str=["<|im_end|>"],
303 image_size_factors=[(0.10, 0.15)],
304 max_tokens=64,
305 marks=[
306 pytest.mark.skip(
307 reason="Aria needs to update for latest transformers, "
308 "must have a vision_processor.py."
309 "An issue has been filed:"
310 "https://huggingface.co/rhymes-ai/Aria/discussions/23"
311 ),
312 large_gpu_mark(min_gb=64),
313 ],
314 ),
315 "blip2": VLMTestInfo(
316 models=["Salesforce/blip2-opt-2.7b"],
317 test_type=VLMTestType.IMAGE,
318 prompt_formatter=lambda img_prompt: f"Question: {img_prompt} Answer:",
319 img_idx_to_prompt=lambda idx: "",
320 auto_cls=AutoModelForImageTextToText,
321 vllm_output_post_proc=model_utils.blip2_vllm_to_hf_output,
322 ),
323 "cosmos3": VLMTestInfo(
324 models=["nvidia/Cosmos3-Nano"],
325 test_type=(
326 VLMTestType.IMAGE,
327 VLMTestType.MULTI_IMAGE,
328 VLMTestType.VIDEO,
329 ),
330 enforce_eager=False,
331 needs_video_metadata=True,
332 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
333 img_idx_to_prompt=lambda idx: "<|vision_start|><|image_pad|><|vision_end|>", # noqa: E501
334 video_idx_to_prompt=lambda idx: "<|vision_start|><|video_pad|><|vision_end|>", # noqa: E501
335 max_model_len=4096,
336 max_num_seqs=2,
337 num_logprobs=20,
338 auto_cls=AutoModelForImageTextToText,
339 vllm_output_post_proc=model_utils.qwen2_vllm_to_hf_output,
340 patch_hf_runner=model_utils.qwen3_vl_patch_hf_runner,
341 image_size_factors=[(0.25,), (0.25, 0.25, 0.25), (0.25, 0.2, 0.15)],
342 marks=[
343 pytest.mark.skip(
344 reason="Transformers has no cosmos3_omni mapping, so the HF "
345 "reference runner cannot load the checkpoint."
346 )
347 ],
348 ),
349 "deepseek_vl_v2": VLMTestInfo(
350 models=["Isotr0py/deepseek-vl2-tiny"], # model repo using dynamic module
351 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
352 prompt_formatter=lambda img_prompt: f"<|User|>: {img_prompt}\n\n<|Assistant|>: ", # noqa: E501
353 max_model_len=4096,
354 max_num_seqs=2,
355 single_image_prompts=IMAGE_ASSETS.prompts(
356 {
357 "stop_sign": "<image>\nWhat's the content in the center of the image?",
358 "cherry_blossom": "<image>\nPlease infer the season with reason in details.", # noqa: E501
359 }
360 ),
361 multi_image_prompt="image_1:<image>\nimage_2:<image>\nWhich image can we see the car and the tower?", # noqa: E501
362 patch_hf_runner=model_utils.deepseekvl2_patch_hf_runner,
363 hf_output_post_proc=model_utils.deepseekvl2_trunc_hf_output,
364 stop_str=["<|end▁of▁sentence|>", "<|begin▁of▁sentence|>"],
365 image_size_factors=[(1.0,), (1.0, 1.0, 1.0), (0.1, 0.5, 1.0)],
366 ),
367 "gemma3": VLMTestInfo(
368 models=["google/gemma-3-4b-it"],
369 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
370 prompt_formatter=lambda img_prompt: f"<bos><start_of_turn>user\n{img_prompt}<end_of_turn>\n<start_of_turn>model\n", # noqa: E501
371 single_image_prompts=IMAGE_ASSETS.prompts(
372 {
373 "stop_sign": "<start_of_image>What's the content in the center of the image?", # noqa: E501
374 "cherry_blossom": "<start_of_image>What is the season?",
375 }
376 ),
377 multi_image_prompt="<start_of_image><start_of_image>Describe the two images in detail.", # noqa: E501
378 max_model_len=4096,
379 max_num_seqs=2,
380 auto_cls=AutoModelForImageTextToText,
381 vllm_runner_kwargs={"mm_processor_kwargs": {"do_pan_and_scan": True}},
382 patch_hf_runner=model_utils.gemma3_patch_hf_runner,
383 ),
384 "gemma4": VLMTestInfo(
385 models=["google/gemma-4-E2B-it"],
386 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
387 prompt_formatter=lambda img_prompt: f"<bos><|turn>user\n{img_prompt}<turn|>\n<|turn>model\n", # noqa: E501
388 single_image_prompts=IMAGE_ASSETS.prompts(
389 {
390 "stop_sign": "<|image|>What's the content in the center of the image?", # noqa: E501
391 "cherry_blossom": "<|image|>What is the season?",
392 }
393 ),
394 multi_image_prompt="<|image|><|image|>Describe the two images in detail.", # noqa: E501
395 max_model_len=4096,
396 max_num_seqs=2,
397 auto_cls=AutoModelForImageTextToText,
398 vllm_runner_kwargs={"limit_mm_per_prompt": {"image": 4}},
399 ),
400 "granite_vision": VLMTestInfo(
401 models=["ibm-granite/granite-vision-3.3-2b"],
402 test_type=(VLMTestType.IMAGE),
403 prompt_formatter=lambda img_prompt: f"<|user|>\n{img_prompt}\n<|assistant|>\n",
404 max_model_len=8192,
405 auto_cls=AutoModelForImageTextToText,
406 vllm_output_post_proc=model_utils.llava_image_vllm_to_hf_output,
407 ),
408 "glm4v": VLMTestInfo(
409 models=["zai-org/glm-4v-9b"],
410 test_type=VLMTestType.IMAGE,
411 prompt_formatter=lambda img_prompt: f"<|user|>\n{img_prompt}<|assistant|>",
412 single_image_prompts=IMAGE_ASSETS.prompts(
413 {
414 "stop_sign": "<|begin_of_image|><|endoftext|><|end_of_image|>What's the content in the center of the image?", # noqa: E501
415 "cherry_blossom": "<|begin_of_image|><|endoftext|><|end_of_image|>What is the season?", # noqa: E501
416 }
417 ),
418 max_model_len=2048,
419 max_num_seqs=2,
420 get_stop_token_ids=lambda tok: [151329, 151336, 151338],
421 patch_hf_runner=model_utils.glm4v_patch_hf_runner,
422 # The image embeddings match with HF but the outputs of the language
423 # decoder are only consistent up to 2 decimal places.
424 # So, we need to reduce the number of tokens for the test to pass.
425 max_tokens=8,
426 num_logprobs=10,
427 auto_cls=AutoModelForCausalLM,
428 marks=[
429 pytest.mark.skip(
430 reason="The code for this model has a bug."
431 "Please see the issue here:"
432 "https://huggingface.co/zai-org/glm-4v-9b/discussions/46."
433 ),
434 large_gpu_mark(min_gb=32),
435 ],
436 ),
437 "glm4_1v": VLMTestInfo(
438 models=["zai-org/GLM-4.1V-9B-Thinking"],
439 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
440 prompt_formatter=lambda img_prompt: f"[gMASK]<|user|>\n{img_prompt}<|assistant|>\n", # noqa: E501
441 img_idx_to_prompt=lambda idx: "<|begin_of_image|><|image|><|end_of_image|>",
442 video_idx_to_prompt=lambda idx: "<|begin_of_video|><|video|><|end_of_video|>",
443 max_model_len=2048,
444 max_num_seqs=2,
445 get_stop_token_ids=lambda tok: [151329, 151336, 151338],
446 num_logprobs=10,
447 image_size_factors=[(0.25,), (0.25, 0.25, 0.25), (0.25, 0.2, 0.15)],
448 auto_cls=AutoModelForImageTextToText,
449 marks=[large_gpu_mark(min_gb=32)],
450 ),
451 "glm4_1v-video": VLMTestInfo(
452 models=["zai-org/GLM-4.1V-9B-Thinking"],
453 # GLM4.1V require include video metadata for input
454 test_type=VLMTestType.CUSTOM_INPUTS,
455 prompt_formatter=lambda vid_prompt: f"[gMASK]<|user|>\n{vid_prompt}<|assistant|>\n", # noqa: E501
456 max_model_len=4096,
457 max_num_seqs=2,
458 auto_cls=AutoModelForImageTextToText,
459 patch_hf_runner=model_utils.glm4_1v_patch_hf_runner,
460 custom_test_opts=[
461 CustomTestOptions(
462 inputs=custom_inputs.video_with_metadata_glm4_1v(),
463 limit_mm_per_prompt={"video": 1},
464 )
465 ],
466 marks=[large_gpu_mark(min_gb=32)],
467 ),
468 "glm_ocr": VLMTestInfo(
469 models=["zai-org/GLM-OCR"],
470 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
471 prompt_formatter=lambda img_prompt: f"[gMASK]<|user|>\n{img_prompt}<|assistant|>\n", # noqa: E501
472 img_idx_to_prompt=lambda idx: "<|begin_of_image|><|image|><|end_of_image|>",
473 video_idx_to_prompt=lambda idx: "<|begin_of_video|><|video|><|end_of_video|>",
474 max_model_len=2048,
475 max_num_seqs=2,
476 get_stop_token_ids=lambda tok: [151329, 151336, 151338],
477 num_logprobs=10,
478 image_size_factors=[(0.25,), (0.25, 0.25, 0.25), (0.25, 0.2, 0.15)],
479 auto_cls=AutoModelForImageTextToText,
480 marks=[
481 pytest.mark.skip(
482 reason="This test fails on both AMD and NV"
483 "hardware. please see the issue:"
484 "https://github.com/vllm-project/vllm/issues/42016"
485 ),
486 large_gpu_mark(min_gb=32),
487 ],
488 ),
489 "granite4_vision": VLMTestInfo(
490 models=["ibm-granite/granite-vision-4.1-4b"],
491 test_type=(VLMTestType.IMAGE),
492 prompt_formatter=lambda img_prompt: f"<|user|>\n{img_prompt}\n<|assistant|>\n",
493 max_model_len=8192,
494 auto_cls=AutoModelForImageTextToText,
495 vllm_output_post_proc=_granite4_vision_vllm_to_hf_output,
496 image_size_factors=[(1.0,)],
497 vllm_runner_kwargs={
498 "enable_lora": True,
499 "max_lora_rank": 256,
500 "default_mm_loras": {"image": "ibm-granite/granite-vision-4.1-4b"},
501 },
502 ),
503 "h2ovl": VLMTestInfo(
504 models=[
505 "h2oai/h2ovl-mississippi-800m",
506 "h2oai/h2ovl-mississippi-2b",
507 ],
508 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
509 prompt_formatter=lambda img_prompt: f"<|prompt|>{img_prompt}<|end|><|answer|>",
510 single_image_prompts=IMAGE_ASSETS.prompts(
511 {
512 "stop_sign": "<image>\nWhat's the content in the center of the image?",
513 "cherry_blossom": "<image>\nWhat is the season?",
514 }
515 ),
516 multi_image_prompt="Image-1: <image>\nImage-2: <image>\nDescribe the two images in short.", # noqa: E501
517 max_model_len=8192,
518 use_tokenizer_eos=True,
519 num_logprobs=10,
520 patch_hf_runner=model_utils.h2ovl_patch_hf_runner,
521 ),
522 "idefics3": VLMTestInfo(
523 models=["HuggingFaceTB/SmolVLM-256M-Instruct"],
524 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
525 prompt_formatter=lambda img_prompt: f"<|begin_of_text|>User:{img_prompt}<end_of_utterance>\nAssistant:", # noqa: E501
526 img_idx_to_prompt=lambda idx: "<image>",
527 max_model_len=8192,
528 max_num_seqs=2,
529 auto_cls=AutoModelForImageTextToText,
530 hf_output_post_proc=model_utils.idefics3_trunc_hf_output,
531 ),
532 "intern_vl": VLMTestInfo(
533 models=[
534 "OpenGVLab/InternVL2-1B",
535 "OpenGVLab/InternVL2-2B",
536 ],
537 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
538 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>Assistant\n", # noqa: E501
539 single_image_prompts=IMAGE_ASSETS.prompts(
540 {
541 "stop_sign": "<image>\nWhat's the content in the center of the image?",
542 "cherry_blossom": "<image>\nWhat is the season?",
543 }
544 ),
545 multi_image_prompt="Image-1: <image>\nImage-2: <image>\nDescribe the two images in short.", # noqa: E501
546 max_model_len=4096,
547 use_tokenizer_eos=True,
548 patch_hf_runner=model_utils.internvl_patch_hf_runner,
549 # TODO: Remove skip once model has been upstreamed to Transformers
550 marks=[
551 pytest.mark.skip(
552 reason="Custom model code tries to access data from meta-tensor"
553 )
554 ],
555 ),
556 "intern_vl-video": VLMTestInfo(
557 models=[
558 "OpenGVLab/InternVL3-1B",
559 ],
560 test_type=VLMTestType.VIDEO,
561 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>Assistant\n", # noqa: E501
562 video_idx_to_prompt=lambda idx: "<video>",
563 max_model_len=8192,
564 use_tokenizer_eos=True,
565 patch_hf_runner=model_utils.internvl_patch_hf_runner,
566 num_logprobs=10 if current_platform.is_rocm() else 5,
567 # TODO: Remove skip once model has been upstreamed to Transformers
568 marks=[
569 pytest.mark.skip(
570 reason="Custom model code tries to access data from meta-tensor"
571 )
572 ],
573 ),
574 "intern_vl-hf": VLMTestInfo(
575 models=["OpenGVLab/InternVL3-1B-hf"],
576 test_type=(
577 VLMTestType.IMAGE,
578 VLMTestType.MULTI_IMAGE,
579 VLMTestType.VIDEO,
580 ),
581 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>Assistant\n", # noqa: E501
582 img_idx_to_prompt=lambda idx: "<IMG_CONTEXT>",
583 video_idx_to_prompt=lambda idx: "<video>",
584 max_model_len=8192,
585 use_tokenizer_eos=True,
586 auto_cls=AutoModelForImageTextToText,
587 ),
588 "isaac": VLMTestInfo(
589 # NOTE: PerceptronAI/Isaac-0.1 removed because the upstream HF
590 # repo has a stale model.safetensors.index.json that references
591 # shard files which no longer exist (consolidated into a single
592 # model.safetensors on 2026-03-20). Re-add once upstream fixes
593 # the index file.
594 models=[
595 "PerceptronAI/Isaac-0.2-2B-Preview",
596 ],
597 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
598 prompt_formatter=lambda img_prompt: (
599 f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n"
600 ),
601 img_idx_to_prompt=lambda idx: "<image>",
602 single_image_prompts=IMAGE_ASSETS.prompts(
603 {
604 "stop_sign": "<vlm_image>Please describe the image shortly.",
605 "cherry_blossom": "<vlm_image>Please infer the season with reason.",
606 }
607 ),
608 multi_image_prompt=(
609 "Picture 1: <vlm_image>\n"
610 "Picture 2: <vlm_image>\n"
611 "Describe these two images with one paragraph respectively."
612 ),
613 enforce_eager=False,
614 max_model_len=4096,
615 max_num_seqs=2,
616 hf_model_kwargs={"device_map": "auto"},
617 patch_hf_runner=model_utils.isaac_patch_hf_runner,
618 image_size_factors=[(0.25,), (0.25, 0.25, 0.25), (0.25, 0.2, 0.15)],
619 # TODO: Remove skip once model has been upstreamed to Transformers
620 marks=[pytest.mark.skip(reason="Custom model imports deleted object")], # noqa: E501
621 ),
622 "kimi_vl": VLMTestInfo(
623 models=["moonshotai/Kimi-VL-A3B-Instruct"],
624 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
625 prompt_formatter=lambda img_prompt: f"<|im_user|>user<|im_middle|>{img_prompt}<|im_end|><|im_assistant|>assistant<|im_middle|>", # noqa: E501
626 img_idx_to_prompt=lambda _: "<|media_start|>image<|media_content|><|media_pad|><|media_end|>", # noqa: E501
627 max_model_len=8192,
628 max_num_seqs=2,
629 dtype="bfloat16",
630 tensor_parallel_size=1,
631 vllm_output_post_proc=model_utils.kimiv_vl_vllm_to_hf_output,
632 marks=[large_gpu_mark(min_gb=48)],
633 ),
634 "llama4": VLMTestInfo(
635 models=["meta-llama/Llama-4-Scout-17B-16E-Instruct"],
636 prompt_formatter=lambda img_prompt: f"<|begin_of_text|><|header_start|>user<|header_end|>\n\n{img_prompt}<|eot|><|header_start|>assistant<|header_end|>\n\n", # noqa: E501
637 img_idx_to_prompt=lambda _: "<|image|>",
638 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
639 distributed_executor_backend="mp",
640 image_size_factors=[(0.25, 0.5, 1.0)],
641 hf_model_kwargs={"device_map": "auto"},
642 max_model_len=8192,
643 max_num_seqs=4,
644 dtype="bfloat16",
645 auto_cls=AutoModelForImageTextToText,
646 tensor_parallel_size=4,
647 marks=multi_gpu_marks(num_gpus=4),
648 ),
649 "llava_next": VLMTestInfo(
650 models=["llava-hf/llava-v1.6-mistral-7b-hf"],
651 test_type=(VLMTestType.IMAGE, VLMTestType.CUSTOM_INPUTS),
652 prompt_formatter=lambda img_prompt: f"[INST] {img_prompt} [/INST]",
653 max_model_len=10240,
654 auto_cls=AutoModelForImageTextToText,
655 vllm_output_post_proc=model_utils.llava_image_vllm_to_hf_output,
656 custom_test_opts=[
657 CustomTestOptions(
658 inputs=custom_inputs.multi_image_multi_aspect_ratio_inputs(
659 formatter=lambda img_prompt: f"[INST] {img_prompt} [/INST]"
660 ),
661 limit_mm_per_prompt={"image": 4},
662 )
663 ],
664 ),
665 "llava_onevision": VLMTestInfo(
666 models=["llava-hf/llava-onevision-qwen2-0.5b-ov-hf"],
667 test_type=VLMTestType.CUSTOM_INPUTS,
668 prompt_formatter=lambda vid_prompt: f"<|im_start|>user\n{vid_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
669 num_video_frames=16,
670 max_model_len=16384,
671 hf_model_kwargs=model_utils.llava_onevision_hf_model_kwargs(
672 "llava-hf/llava-onevision-qwen2-0.5b-ov-hf"
673 ),
674 auto_cls=AutoModelForImageTextToText,
675 vllm_output_post_proc=model_utils.llava_onevision_vllm_to_hf_output,
676 custom_test_opts=[
677 CustomTestOptions(
678 inputs=custom_inputs.multi_video_multi_aspect_ratio_inputs(
679 formatter=lambda vid_prompt: f"<|im_start|>user\n{vid_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
680 ),
681 limit_mm_per_prompt={"video": 4},
682 )
683 ],
684 ),
685 "llava_next_video": VLMTestInfo(
686 models=["llava-hf/LLaVA-NeXT-Video-7B-hf"],
687 test_type=VLMTestType.VIDEO,
688 prompt_formatter=lambda vid_prompt: f"USER: {vid_prompt} ASSISTANT:",
689 num_video_frames=16,
690 max_model_len=4096,
691 max_num_seqs=2,
692 auto_cls=AutoModelForImageTextToText,
693 vllm_output_post_proc=model_utils.llava_video_vllm_to_hf_output,
694 ),
695 "minicpmv_25": VLMTestInfo(
696 models=["openbmb/MiniCPM-Llama3-V-2_5"],
697 test_type=VLMTestType.IMAGE,
698 prompt_formatter=lambda img_prompt: f"<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n{img_prompt}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n", # noqa: E501
699 img_idx_to_prompt=lambda idx: "(<image>./</image>)\n",
700 max_model_len=4096,
701 max_num_seqs=2,
702 get_stop_token_ids=lambda tok: [tok.eos_id, tok.eot_id],
703 hf_output_post_proc=model_utils.minicpmv_trunc_hf_output,
704 patch_hf_runner=model_utils.minicpmv_25_patch_hf_runner,
705 ),
706 "minicpmo_26": VLMTestInfo(
707 models=["openbmb/MiniCPM-o-2_6"],
708 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
709 prompt_formatter=lambda img_prompt: f"<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n{img_prompt}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n", # noqa: E501
710 img_idx_to_prompt=lambda idx: "(<image>./</image>)\n",
711 max_model_len=4096,
712 max_num_seqs=2,
713 get_stop_token_ids=lambda tok: tok.convert_tokens_to_ids(
714 ["<|im_end|>", "<|endoftext|>"]
715 ),
716 hf_output_post_proc=model_utils.minicpmv_trunc_hf_output,
717 patch_hf_runner=model_utils.minicpmo_26_patch_hf_runner,
718 ),
719 "minicpmv_26": VLMTestInfo(
720 models=["openbmb/MiniCPM-V-2_6"],
721 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
722 prompt_formatter=lambda img_prompt: f"<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n{img_prompt}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n", # noqa: E501
723 img_idx_to_prompt=lambda idx: "(<image>./</image>)\n",
724 max_model_len=4096,
725 max_num_seqs=2,
726 get_stop_token_ids=lambda tok: tok.convert_tokens_to_ids(
727 ["<|im_end|>", "<|endoftext|>"]
728 ),
729 hf_output_post_proc=model_utils.minicpmv_trunc_hf_output,
730 patch_hf_runner=model_utils.minicpmv_26_patch_hf_runner,
731 ),
732 "molmo": VLMTestInfo(
733 models=["allenai/Molmo-7B-D-0924"],
734 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
735 prompt_formatter=identity,
736 max_model_len=4096,
737 max_num_seqs=2,
738 patch_hf_runner=model_utils.molmo_patch_hf_runner,
739 ),
740 "moondream3": VLMTestInfo(
741 models=["moondream/moondream3-preview"],
742 test_type=VLMTestType.IMAGE,
743 prompt_formatter=identity,
744 img_idx_to_prompt=lambda idx: "<|endoftext|><image>",
745 # Common-image coverage here targets query/caption. The native
746 # detect/point skills are not exposed by vLLM.
747 single_image_prompts=IMAGE_ASSETS.prompts(
748 {
749 "stop_sign": "<vlm_image><|md_reserved_0|>query<|md_reserved_1|>What is this sign?<|md_reserved_2|>", # noqa: E501
750 "cherry_blossom": (
751 "<vlm_image><|md_reserved_0|>query<|md_reserved_1|>What season is shown?<|md_reserved_2|>" # noqa: E501
752 ),
753 }
754 ),
755 max_model_len=4096,
756 max_num_seqs=2,
757 dtype="bfloat16",
758 hf_processor=model_utils.moondream3_processor,
759 patch_hf_runner=model_utils.moondream3_patch_hf_runner,
760 # Single size factor to avoid GPU OOM when running multiple test
761 # cases sequentially (9B MoE model uses ~18 GiB per instance).
762 image_size_factors=[(1.0,)],
763 # Moondream3 is 9B params with MoE, needs significant GPU memory
764 marks=[large_gpu_mark(min_gb=48)],
765 ),
766 "ovis1_6-gemma2": VLMTestInfo(
767 models=["AIDC-AI/Ovis1.6-Gemma2-9B"],
768 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
769 prompt_formatter=lambda img_prompt: f"<bos><start_of_turn>user\n{img_prompt}<end_of_turn>\n<start_of_turn>model\n", # noqa: E501
770 img_idx_to_prompt=lambda idx: "<image>\n",
771 max_model_len=4096,
772 max_num_seqs=2,
773 dtype="half",
774 # use sdpa mode for hf runner since ovis2 didn't work with flash_attn
775 hf_model_kwargs={"llm_attn_implementation": "sdpa"},
776 patch_hf_runner=model_utils.ovis_patch_hf_runner,
777 marks=[large_gpu_mark(min_gb=32)],
778 ),
779 "ovis2": VLMTestInfo(
780 models=["AIDC-AI/Ovis2-1B"],
781 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
782 prompt_formatter=lambda img_prompt: f"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
783 img_idx_to_prompt=lambda idx: "<image>\n",
784 max_model_len=4096,
785 max_num_seqs=2,
786 dtype="half",
787 # use sdpa mode for hf runner since ovis2 didn't work with flash_attn
788 hf_model_kwargs={"llm_attn_implementation": "sdpa"},
789 patch_hf_runner=model_utils.ovis_patch_hf_runner,
790 ),
791 "ovis2_5": VLMTestInfo(
792 models=["AIDC-AI/Ovis2.5-2B"],
793 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE, VLMTestType.VIDEO),
794 prompt_formatter=lambda img_prompt: f"<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
795 img_idx_to_prompt=lambda idx: "<image>\n",
796 video_idx_to_prompt=lambda idx: "<video>\n",
797 max_model_len=4096,
798 max_num_seqs=2,
799 dtype="half",
800 num_logprobs=10,
801 patch_hf_runner=model_utils.ovis2_5_patch_hf_runner,
802 ),
803 "paddleocr_vl": VLMTestInfo(
804 models=["PaddlePaddle/PaddleOCR-VL"],
805 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
806 prompt_formatter=lambda img_prompt: f"USER: {img_prompt}\nASSISTANT:",
807 img_idx_to_prompt=lambda idx: (
808 "<|IMAGE_START|><|IMAGE_PLACEHOLDER|><|IMAGE_END|>"
809 ),
810 multi_image_prompt=(
811 "Image-1: <|IMAGE_START|><|IMAGE_PLACEHOLDER|><|IMAGE_END|>\n"
812 "Image-2: <|IMAGE_START|><|IMAGE_PLACEHOLDER|><|IMAGE_END|>\n"
813 "Describe these two images separately."
814 ),
815 max_model_len=8192,
816 max_num_seqs=2,
817 auto_cls=AutoModelForCausalLM,
818 patch_hf_runner=model_utils.paddleocr_vl_patch_hf_runner,
819 image_size_factors=[(0.25,)],
820 marks=[
821 pytest.mark.skipif(
822 Version(TRANSFORMERS_VERSION) >= Version("5.0.0"),
823 reason="Model's custom code uses ROPE_INIT_FUNCTIONS"
824 "['default'] which was removed in transformers v5",
825 ),
826 ],
827 ),
828 "phi3v": VLMTestInfo(
829 models=["microsoft/Phi-3.5-vision-instruct"],
830 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
831 prompt_formatter=lambda img_prompt: f"<|user|>\n{img_prompt}<|end|>\n<|assistant|>\n", # noqa: E501
832 img_idx_to_prompt=lambda idx: f"<|image_{idx}|>\n",
833 max_model_len=4096,
834 max_num_seqs=2,
835 runner="generate",
836 # use sdpa mode for hf runner since phi3v didn't work with flash_attn
837 hf_model_kwargs={"_attn_implementation": "sdpa"},
838 use_tokenizer_eos=True,
839 vllm_output_post_proc=model_utils.phi3v_vllm_to_hf_output,
840 num_logprobs=10,
841 ),
842 "pixtral_hf": VLMTestInfo(
843 models=["nm-testing/pixtral-12b-FP8-dynamic"],
844 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
845 prompt_formatter=lambda img_prompt: f"<s>[INST]{img_prompt}[/INST]",
846 img_idx_to_prompt=lambda idx: "[IMG]",
847 max_model_len=8192,
848 max_num_seqs=2,
849 auto_cls=AutoModelForImageTextToText,
850 marks=[
851 large_gpu_mark(min_gb=48),
852 pytest.mark.skipif(
853 current_platform.is_rocm(),
854 reason="Model produces a vector of <UNK> output in HF on ROCm",
855 ),
856 ],
857 ),
858 "qianfan_ocr": VLMTestInfo(
859 models=["baidu/Qianfan-OCR"],
860 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
861 prompt_formatter=lambda img_prompt: f"<|im_start|>user\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
862 img_idx_to_prompt=lambda idx: "<image>",
863 max_model_len=4096,
864 use_tokenizer_eos=True,
865 auto_cls=AutoModelForImageTextToText,
866 ),
867 "qwen2_vl": VLMTestInfo(
868 models=["Qwen/Qwen2-VL-2B-Instruct"],
869 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE, VLMTestType.VIDEO),
870 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
871 img_idx_to_prompt=lambda idx: "<|vision_start|><|image_pad|><|vision_end|>",
872 video_idx_to_prompt=lambda idx: "<|vision_start|><|video_pad|><|vision_end|>",
873 multi_image_prompt="Picture 1: <vlm_image>\nPicture 2: <vlm_image>\nDescribe these two images with one paragraph respectively.", # noqa: E501
874 max_model_len=4096,
875 max_num_seqs=2,
876 # Qwen2-VL multi-image generation can accumulate backend-specific
877 # numerical drift during long autoregressive decoding.
878 #
879 # On CPU with torch 2.13, HF and vLLM agree for a long prefix before
880 # diverging near the end of generation. See pytorch/pytorch#187735.
881 #
882 # A similar late-generation divergence is observed on XPU with BF16
883 # ViT FlashAttention. The issue is not resolved by increasing
884 # num_logprobs from 10 to 20, while FP32 and BF16 with Triton ViT
885 # attention pass in local reproduction.
886 #
887 # Cap max_tokens on CPU and XPU to keep the correctness comparison
888 # before the numerically sensitive late-generation region.
889 max_tokens=64
890 if current_platform.is_cpu() or current_platform.is_xpu()
891 else 128,
892 num_logprobs=20 if current_platform.is_cpu() else 10,
893 auto_cls=AutoModelForImageTextToText,
894 vllm_output_post_proc=model_utils.qwen2_vllm_to_hf_output,
895 image_size_factors=[(0.25,), (0.25, 0.25, 0.25), (0.25, 0.2, 0.15)],
896 marks=[pytest.mark.cpu_model],
897 ),
898 "skywork_r1v": VLMTestInfo(
899 models=["Skywork/Skywork-R1V-38B"],
900 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
901 prompt_formatter=lambda img_prompt: f"<|begin▁of▁sentence|><|User|>\n{img_prompt}<|Assistant|><think>\n", # noqa: E501
902 single_image_prompts=IMAGE_ASSETS.prompts(
903 {
904 "stop_sign": "<image>\nWhat's the content in the center of the image?",
905 "cherry_blossom": "<image>\nWhat is the season?",
906 }
907 ),
908 multi_image_prompt="<image>\n<image>\nDescribe the two images in short.",
909 max_model_len=4096,
910 use_tokenizer_eos=True,
911 patch_hf_runner=model_utils.skyworkr1v_patch_hf_runner,
912 marks=[large_gpu_mark(min_gb=80)],
913 ),
914 "smolvlm": VLMTestInfo(
915 models=["HuggingFaceTB/SmolVLM2-2.2B-Instruct"],
916 test_type=(VLMTestType.IMAGE, VLMTestType.MULTI_IMAGE),
917 prompt_formatter=lambda img_prompt: f"<|im_start|>User:{img_prompt}<end_of_utterance>\nAssistant:", # noqa: E501
918 img_idx_to_prompt=lambda idx: "<image>",
919 max_model_len=8192,
920 max_num_seqs=2,
921 auto_cls=AutoModelForImageTextToText,
922 hf_output_post_proc=model_utils.smolvlm_trunc_hf_output,
923 num_logprobs=10,
924 ),
925 ### Tensor parallel / multi-gpu broadcast tests
926 "llava-broadcast": VLMTestInfo(
927 models=["llava-hf/llava-1.5-7b-hf"],
928 prompt_formatter=lambda img_prompt: f"USER: {img_prompt}\nASSISTANT:",
929 max_model_len=4096,
930 auto_cls=AutoModelForImageTextToText,
931 vllm_output_post_proc=model_utils.llava_image_vllm_to_hf_output,
932 marks=multi_gpu_marks(num_gpus=2),
933 **COMMON_BROADCAST_SETTINGS, # type: ignore
934 ),
935 "llava_next-broadcast": VLMTestInfo(
936 models=["llava-hf/llava-v1.6-mistral-7b-hf"],
937 prompt_formatter=lambda img_prompt: f"[INST] {img_prompt} [/INST]",
938 max_model_len=10240,
939 auto_cls=AutoModelForImageTextToText,
940 vllm_output_post_proc=model_utils.llava_image_vllm_to_hf_output,
941 marks=multi_gpu_marks(num_gpus=2),
942 **COMMON_BROADCAST_SETTINGS, # type: ignore
943 ),
944 ### Custom input edge-cases for specific models
945 "intern_vl-diff-patches": VLMTestInfo(
946 models=["OpenGVLab/InternVL2-2B"],
947 prompt_formatter=lambda img_prompt: f"<|im_start|>User\n{img_prompt}<|im_end|>\n<|im_start|>Assistant\n", # noqa: E501
948 test_type=VLMTestType.CUSTOM_INPUTS,
949 max_model_len=4096,
950 use_tokenizer_eos=True,
951 patch_hf_runner=model_utils.internvl_patch_hf_runner,
952 custom_test_opts=[
953 CustomTestOptions(
954 inputs=inp,
955 limit_mm_per_prompt={"image": 2},
956 )
957 for inp in custom_inputs.different_patch_input_cases_internvl()
958 ],
959 # TODO: Remove skip once model has been upstreamed to Transformers
960 marks=[
961 pytest.mark.skip(
962 reason="Custom model code tries to access data from meta-tensor"
963 )
964 ],
965 ),
966 "llava_onevision-multiple-images": VLMTestInfo(
967 models=["llava-hf/llava-onevision-qwen2-0.5b-ov-hf"],
968 test_type=VLMTestType.CUSTOM_INPUTS,
969 max_model_len=16384,
970 max_num_seqs=2,
971 auto_cls=AutoModelForImageTextToText,
972 hf_model_kwargs=model_utils.llava_onevision_hf_model_kwargs(
973 "llava-hf/llava-onevision-qwen2-0.5b-ov-hf"
974 ),
975 vllm_output_post_proc=model_utils.llava_onevision_vllm_to_hf_output,
976 custom_test_opts=[
977 CustomTestOptions(
978 inputs=custom_inputs.multi_image_multi_aspect_ratio_inputs(
979 formatter=lambda vid_prompt: f"<|im_start|>user\n{vid_prompt}<|im_end|>\n<|im_start|>assistant\n", # noqa: E501
980 ),
981 limit_mm_per_prompt={"image": 4},
982 )
983 ],
984 ),
985 # regression test for https://github.com/vllm-project/vllm/issues/15122
986 "qwen2_5_vl-windows-attention": VLMTestInfo(
987 models=["Qwen/Qwen2.5-VL-3B-Instruct"],
988 test_type=VLMTestType.CUSTOM_INPUTS,
989 max_model_len=4096,
990 max_num_seqs=2,
991 auto_cls=AutoModelForImageTextToText,
992 vllm_output_post_proc=model_utils.qwen2_vllm_to_hf_output,
993 custom_test_opts=[
994 CustomTestOptions(
995 inputs=custom_inputs.windows_attention_image_qwen2_5_vl(),
996 limit_mm_per_prompt={"image": 1},
997 )
998 ],
999 ),
1000 }
1001
1002
1003 def _mark_splits(
1004 test_settings: dict[str, VLMTestInfo],
1005 *,
1006 num_groups: int,
1007 ) -> dict[str, VLMTestInfo]:
1008 name_by_test_info_id = {id(v): k for k, v in test_settings.items()}
1009 test_infos_by_model = defaultdict[str, list[VLMTestInfo]](list)
1010
1011 for info in test_settings.values():
1012 for model in info.models:
1013 test_infos_by_model[model].append(info)
1014
1015 models = sorted(test_infos_by_model.keys())
1016 split_size = math.ceil(len(models) / num_groups)
1017
1018 new_test_settings = dict[str, VLMTestInfo]()
1019
1020 for i in range(num_groups):
1021 models_in_group = models[i * split_size : (i + 1) * split_size]
1022
1023 for model in models_in_group:
1024 for info in test_infos_by_model[model]:
1025 new_marks = (info.marks or []) + [pytest.mark.split(group=i)]
1026 new_info = info._replace(marks=new_marks)
1027 new_test_settings[name_by_test_info_id[id(info)]] = new_info
1028
1029 missing_keys = test_settings.keys() - new_test_settings.keys()
1030 assert not missing_keys, f"Missing keys: {missing_keys}"
1031
1032 return new_test_settings
1033
1034
1035 VLM_TEST_SETTINGS = _mark_splits(VLM_TEST_SETTINGS, num_groups=2)
1036
1037
1038 ### Test wrappers
1039 # Wrappers around the core test running func for:
1040 # - single image
1041 # - multi-image
1042 # - image embeddings
1043 # - video
1044 # - audio
1045 # - custom inputs
1046 @pytest.mark.parametrize(
1047 "model_type,test_case",
1048 get_parametrized_options(
1049 VLM_TEST_SETTINGS,
1050 test_type=VLMTestType.IMAGE,
1051 create_new_process_for_each_test=False,
1052 ),
1053 )
1054 def test_single_image_models(
1055 tmp_path: PosixPath,
1056 model_type: str,
1057 test_case: ExpandableVLMTestArgs,
1058 hf_runner: type[HfRunner],
1059 vllm_runner: type[VllmRunner],
1060 image_assets: ImageTestAssets,
1061 ):
1062 model_test_info = VLM_TEST_SETTINGS[model_type]
1063 runners.run_single_image_test(
1064 tmp_path=tmp_path,
1065 model_test_info=model_test_info,
1066 test_case=test_case,
1067 hf_runner=hf_runner,
1068 vllm_runner=vllm_runner,
1069 image_assets=image_assets,
1070 )
1071
1072
1073 @pytest.mark.parametrize(
1074 "model_type,test_case",
1075 get_parametrized_options(
1076 VLM_TEST_SETTINGS,
1077 test_type=VLMTestType.MULTI_IMAGE,
1078 create_new_process_for_each_test=False,
1079 ),
1080 )
1081 def test_multi_image_models(
1082 tmp_path: PosixPath,
1083 model_type: str,
1084 test_case: ExpandableVLMTestArgs,
1085 hf_runner: type[HfRunner],
1086 vllm_runner: type[VllmRunner],
1087 image_assets: ImageTestAssets,
1088 ):
1089 model_test_info = VLM_TEST_SETTINGS[model_type]
1090 runners.run_multi_image_test(
1091 tmp_path=tmp_path,
1092 model_test_info=model_test_info,
1093 test_case=test_case,
1094 hf_runner=hf_runner,
1095 vllm_runner=vllm_runner,
1096 image_assets=image_assets,
1097 )
1098
1099
1100 @pytest.mark.parametrize(
1101 "model_type,test_case",
1102 get_parametrized_options(
1103 VLM_TEST_SETTINGS,
1104 test_type=VLMTestType.EMBEDDING,
1105 create_new_process_for_each_test=False,
1106 ),
1107 )
1108 def test_image_embedding_models(
1109 model_type: str,
1110 test_case: ExpandableVLMTestArgs,
1111 hf_runner: type[HfRunner],
1112 vllm_runner: type[VllmRunner],
1113 image_assets: ImageTestAssets,
1114 ):
1115 model_test_info = VLM_TEST_SETTINGS[model_type]
1116 runners.run_embedding_test(
1117 model_test_info=model_test_info,
1118 test_case=test_case,
1119 hf_runner=hf_runner,
1120 vllm_runner=vllm_runner,
1121 image_assets=image_assets,
1122 )
1123
1124
1125 @pytest.mark.parametrize(
1126 "model_type,test_case",
1127 get_parametrized_options(
1128 VLM_TEST_SETTINGS,
1129 test_type=VLMTestType.VIDEO,
1130 create_new_process_for_each_test=False,
1131 ),
1132 )
1133 def test_video_models(
1134 model_type: str,
1135 test_case: ExpandableVLMTestArgs,
1136 hf_runner: type[HfRunner],
1137 vllm_runner: type[VllmRunner],
1138 video_assets: VideoTestAssets,
1139 ):
1140 model_test_info = VLM_TEST_SETTINGS[model_type]
1141 runners.run_video_test(
1142 model_test_info=model_test_info,
1143 test_case=test_case,
1144 hf_runner=hf_runner,
1145 vllm_runner=vllm_runner,
1146 video_assets=video_assets,
1147 )
1148
1149
1150 @pytest.mark.parametrize(
1151 "model_type,test_case",
1152 get_parametrized_options(
1153 VLM_TEST_SETTINGS,
1154 test_type=VLMTestType.AUDIO,
1155 create_new_process_for_each_test=False,
1156 ),
1157 )
1158 def test_audio_models(
1159 model_type: str,
1160 test_case: ExpandableVLMTestArgs,
1161 hf_runner: type[HfRunner],
1162 vllm_runner: type[VllmRunner],
1163 audio_assets: AudioTestAssets,
1164 ):
1165 model_test_info = VLM_TEST_SETTINGS[model_type]
1166 runners.run_audio_test(
1167 model_test_info=model_test_info,
1168 test_case=test_case,
1169 hf_runner=hf_runner,
1170 vllm_runner=vllm_runner,
1171 audio_assets=audio_assets,
1172 )
1173
1174
1175 @pytest.mark.parametrize(
1176 "model_type,test_case",
1177 get_parametrized_options(
1178 VLM_TEST_SETTINGS,
1179 test_type=VLMTestType.CUSTOM_INPUTS,
1180 create_new_process_for_each_test=False,
1181 ),
1182 )
1183 def test_custom_inputs_models(
1184 model_type: str,
1185 test_case: ExpandableVLMTestArgs,
1186 hf_runner: type[HfRunner],
1187 vllm_runner: type[VllmRunner],
1188 ):
1189 model_test_info = VLM_TEST_SETTINGS[model_type]
1190 runners.run_custom_inputs_test(
1191 model_test_info=model_test_info,
1192 test_case=test_case,
1193 hf_runner=hf_runner,
1194 vllm_runner=vllm_runner,
1195 )
1196
1197
1198 #### Tests filtering for things running each test as a new process
1199 @pytest.mark.parametrize(
1200 "model_type,test_case",
1201 get_parametrized_options(
1202 VLM_TEST_SETTINGS,
1203 test_type=VLMTestType.IMAGE,
1204 create_new_process_for_each_test=True,
1205 ),
1206 )
1207 @create_new_process_for_each_test("spawn")
1208 def test_single_image_models_heavy(
1209 tmp_path: PosixPath,
1210 model_type: str,
1211 test_case: ExpandableVLMTestArgs,
1212 hf_runner: type[HfRunner],
1213 vllm_runner: type[VllmRunner],
1214 image_assets: ImageTestAssets,
1215 ):
1216 model_test_info = VLM_TEST_SETTINGS[model_type]
1217 runners.run_single_image_test(
1218 tmp_path=tmp_path,
1219 model_test_info=model_test_info,
1220 test_case=test_case,
1221 hf_runner=hf_runner,
1222 vllm_runner=vllm_runner,
1223 image_assets=image_assets,
1224 )
1225
1226
1227 @pytest.mark.parametrize(
1228 "model_type,test_case",
1229 get_parametrized_options(
1230 VLM_TEST_SETTINGS,
1231 test_type=VLMTestType.MULTI_IMAGE,
1232 create_new_process_for_each_test=True,
1233 ),
1234 )
1235 @create_new_process_for_each_test()
1236 def test_multi_image_models_heavy(
1237 tmp_path: PosixPath,
1238 model_type: str,
1239 test_case: ExpandableVLMTestArgs,
1240 hf_runner: type[HfRunner],
1241 vllm_runner: type[VllmRunner],
1242 image_assets: ImageTestAssets,
1243 ):
1244 model_test_info = VLM_TEST_SETTINGS[model_type]
1245 runners.run_multi_image_test(
1246 tmp_path=tmp_path,
1247 model_test_info=model_test_info,
1248 test_case=test_case,
1249 hf_runner=hf_runner,
1250 vllm_runner=vllm_runner,
1251 image_assets=image_assets,
1252 )
1253
1254
1255 @pytest.mark.parametrize(
1256 "model_type,test_case",
1257 get_parametrized_options(
1258 VLM_TEST_SETTINGS,
1259 test_type=VLMTestType.EMBEDDING,
1260 create_new_process_for_each_test=True,
1261 ),
1262 )
1263 @create_new_process_for_each_test()
1264 def test_image_embedding_models_heavy(
1265 model_type: str,
1266 test_case: ExpandableVLMTestArgs,
1267 hf_runner: type[HfRunner],
1268 vllm_runner: type[VllmRunner],
1269 image_assets: ImageTestAssets,
1270 ):
1271 model_test_info = VLM_TEST_SETTINGS[model_type]
1272 runners.run_embedding_test(
1273 model_test_info=model_test_info,
1274 test_case=test_case,
1275 hf_runner=hf_runner,
1276 vllm_runner=vllm_runner,
1277 image_assets=image_assets,
1278 )
1279
1280
1281 @pytest.mark.parametrize(
1282 "model_type,test_case",
1283 get_parametrized_options(
1284 VLM_TEST_SETTINGS,
1285 test_type=VLMTestType.VIDEO,
1286 create_new_process_for_each_test=True,
1287 ),
1288 )
1289 def test_video_models_heavy(
1290 model_type: str,
1291 test_case: ExpandableVLMTestArgs,
1292 hf_runner: type[HfRunner],
1293 vllm_runner: type[VllmRunner],
1294 video_assets: VideoTestAssets,
1295 ):
1296 model_test_info = VLM_TEST_SETTINGS[model_type]
1297 runners.run_video_test(
1298 model_test_info=model_test_info,
1299 test_case=test_case,
1300 hf_runner=hf_runner,
1301 vllm_runner=vllm_runner,
1302 video_assets=video_assets,
1303 )
1304
1305
1306 @pytest.mark.parametrize(
1307 "model_type,test_case",
1308 get_parametrized_options(
1309 VLM_TEST_SETTINGS,
1310 test_type=VLMTestType.AUDIO,
1311 create_new_process_for_each_test=True,
1312 ),
1313 )
1314 def test_audio_models_heavy(
1315 model_type: str,
1316 test_case: ExpandableVLMTestArgs,
1317 hf_runner: type[HfRunner],
1318 vllm_runner: type[VllmRunner],
1319 audio_assets: AudioTestAssets,
1320 ):
1321 model_test_info = VLM_TEST_SETTINGS[model_type]
1322 runners.run_audio_test(
1323 model_test_info=model_test_info,
1324 test_case=test_case,
1325 hf_runner=hf_runner,
1326 vllm_runner=vllm_runner,
1327 audio_assets=audio_assets,
1328 )
1329
1330
1331 @pytest.mark.parametrize(
1332 "model_type,test_case",
1333 get_parametrized_options(
1334 VLM_TEST_SETTINGS,
1335 test_type=VLMTestType.CUSTOM_INPUTS,
1336 create_new_process_for_each_test=True,
1337 ),
1338 )
1339 @create_new_process_for_each_test()
1340 def test_custom_inputs_models_heavy(
1341 model_type: str,
1342 test_case: ExpandableVLMTestArgs,
1343 hf_runner: type[HfRunner],
1344 vllm_runner: type[VllmRunner],
1345 ):
1346 model_test_info = VLM_TEST_SETTINGS[model_type]
1347 runners.run_custom_inputs_test(
1348 model_test_info=model_test_info,
1349 test_case=test_case,
1350 hf_runner=hf_runner,
1351 vllm_runner=vllm_runner,
1352 )