main
py 925 lines 29.6 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Callable
5 from contextlib import contextmanager, nullcontext
6
7 import pytest
8
9 from tests.models.registry import HF_EXAMPLE_MODELS
10 from tests.utils import multi_gpu_test
11 from vllm import LLM
12 from vllm.config import CUDAGraphMode
13 from vllm.engine.arg_utils import EngineArgs
14 from vllm.platforms import current_platform
15 from vllm.sampling_params import SamplingParams
16 from vllm.v1.cudagraph_dispatcher import CudagraphDispatcher
17
18 from ...utils import check_logprobs_close, check_outputs_equal
19
20 # Mark all tests as hybrid
21 pytestmark = pytest.mark.hybrid_model
22
23 # NOTE: The first model in each list is taken as the primary model,
24 # meaning that it will be used in all tests in this file
25 # The rest of the models will only be tested by test_models
26
27 APC_MULTIPLY_BY = 300
28
29 SSM_MODELS = [
30 "state-spaces/mamba-130m-hf",
31 "tiiuae/falcon-mamba-tiny-dev",
32 # mamba2-codestral in transformers is broken pending:
33 # https://github.com/huggingface/transformers/pull/40861
34 # "yujiepan/mamba2-codestral-v0.1-tiny-random",
35 ]
36
37 HYBRID_MODELS = [
38 "ai21labs/Jamba-tiny-dev",
39 "Zyphra/Zamba2-1.2B-instruct",
40 "ibm-granite/granite-4.0-tiny-preview",
41 "tiiuae/Falcon-H1-0.5B-Base",
42 "LiquidAI/LFM2-1.2B",
43 "tiny-random/qwen3-next-moe",
44 ]
45
46 FULL_CUDA_GRAPH_MODELS = [
47 "ai21labs/Jamba-tiny-dev",
48 "Zyphra/Zamba2-1.2B-instruct",
49 ]
50
51 FP32_STATE_MODELS = [
52 "state-spaces/mamba-130m-hf",
53 "Zyphra/Zamba2-1.2B-instruct",
54 ]
55
56 # Avoid OOM
57 MAX_NUM_SEQS = 4
58
59 ATTN_BACKEND = "TRITON_ATTN" if current_platform.is_rocm() else "auto"
60
61
62 def _set_conv_state_layout(monkeypatch, layout: str) -> None:
63 """Set conv state layout env var and clear cache to pick up new value."""
64 from vllm.model_executor.layers.mamba import mamba_utils
65
66 monkeypatch.setenv("VLLM_SSM_CONV_STATE_LAYOUT", layout)
67 mamba_utils.get_conv_state_layout.cache_clear()
68
69
70 @pytest.mark.parametrize("model", SSM_MODELS + HYBRID_MODELS)
71 @pytest.mark.parametrize("max_tokens", [64])
72 @pytest.mark.parametrize("num_logprobs", [5])
73 def test_models(
74 hf_runner,
75 vllm_runner,
76 gpu_memory_cleared,
77 example_prompts,
78 monkeypatch,
79 model: str,
80 max_tokens: int,
81 num_logprobs: int,
82 ) -> None:
83 try:
84 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
85 model_info.check_available_online(on_fail="skip")
86 model_info.check_transformers_version(on_fail="skip")
87 except ValueError:
88 pass
89
90 with hf_runner(model) as hf_model:
91 hf_outputs = hf_model.generate_greedy_logprobs_limit(
92 example_prompts, max_tokens, num_logprobs
93 )
94
95 with vllm_runner(
96 model,
97 max_num_seqs=MAX_NUM_SEQS,
98 attention_backend=ATTN_BACKEND,
99 enable_chunked_prefill=True,
100 ) as vllm_model:
101 vllm_outputs = vllm_model.generate_greedy_logprobs(
102 example_prompts, max_tokens, num_logprobs
103 )
104
105 check_logprobs_close(
106 outputs_0_lst=hf_outputs,
107 outputs_1_lst=vllm_outputs,
108 name_0="hf",
109 name_1="vllm",
110 )
111
112
113 @pytest.mark.parametrize("model", [SSM_MODELS[0], HYBRID_MODELS[0]])
114 @pytest.mark.parametrize("max_tokens", [64])
115 @pytest.mark.parametrize("num_logprobs", [5])
116 @pytest.mark.parametrize("conv_state_layout", ["SD", "DS"])
117 def test_batching(
118 vllm_runner,
119 example_prompts,
120 monkeypatch,
121 model: str,
122 max_tokens: int,
123 num_logprobs: int,
124 conv_state_layout: str,
125 ) -> None:
126 try:
127 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
128 model_info.check_available_online(on_fail="skip")
129 model_info.check_transformers_version(on_fail="skip")
130 except ValueError:
131 pass
132
133 _set_conv_state_layout(monkeypatch, conv_state_layout)
134
135 for_loop_outputs = []
136 with vllm_runner(
137 model, max_num_seqs=MAX_NUM_SEQS, enable_chunked_prefill=True
138 ) as vllm_model:
139 for prompt in example_prompts:
140 (single_output,) = vllm_model.generate_greedy_logprobs(
141 [prompt], max_tokens, num_logprobs
142 )
143 for_loop_outputs.append(single_output)
144
145 batched_outputs = vllm_model.generate_greedy_logprobs(
146 example_prompts, max_tokens, num_logprobs
147 )
148
149 check_logprobs_close(
150 outputs_0_lst=for_loop_outputs,
151 outputs_1_lst=batched_outputs,
152 name_0="for_loop_vllm",
153 name_1="batched_vllm",
154 )
155
156
157 @pytest.mark.parametrize("model", [SSM_MODELS[0], HYBRID_MODELS[0]])
158 @pytest.mark.parametrize("max_tokens", [10])
159 @pytest.mark.parametrize("conv_state_layout", ["SD", "DS"])
160 def test_chunked_prefill_with_parallel_sampling(
161 vllm_runner,
162 example_prompts,
163 monkeypatch,
164 model: str,
165 max_tokens: int,
166 conv_state_layout: str,
167 ) -> None:
168 """Tests chunked prefill in conjunction with n > 1.
169
170 In this case, prefill is populated with decoding tokens and
171 we test that it doesn't fail.
172
173 This test might fail if cache is not allocated correctly for n > 1
174 decoding steps inside a chunked prefill forward pass
175 (where we have both prefill and decode together)
176 """
177 _set_conv_state_layout(monkeypatch, conv_state_layout)
178
179 sampling_params = SamplingParams(n=3, temperature=1, seed=0, max_tokens=max_tokens)
180 with vllm_runner(
181 model,
182 enable_chunked_prefill=True,
183 # forces prefill chunks with decoding
184 max_num_batched_tokens=MAX_NUM_SEQS * 3,
185 max_num_seqs=MAX_NUM_SEQS,
186 attention_backend=ATTN_BACKEND,
187 ) as vllm_model:
188 vllm_model.generate(example_prompts, sampling_params)
189
190
191 @pytest.mark.parametrize("model", [SSM_MODELS[0], HYBRID_MODELS[0]])
192 @pytest.mark.parametrize("max_tokens", [20])
193 @pytest.mark.parametrize("conv_state_layout", ["SD", "DS"])
194 def test_mamba_cache_cg_padding(
195 vllm_runner,
196 example_prompts,
197 monkeypatch,
198 model: str,
199 max_tokens: int,
200 conv_state_layout: str,
201 ) -> None:
202 """This test is for verifying that mamba cache is padded to CG captured
203 batch size. If it's not, a torch RuntimeError will be raised because
204 tensor dimensions aren't compatible.
205 """
206 _set_conv_state_layout(monkeypatch, conv_state_layout)
207
208 vllm_config = EngineArgs(model=model, trust_remote_code=True).create_engine_config()
209 cudagraph_dispatcher = CudagraphDispatcher(vllm_config)
210 cudagraph_dispatcher.initialize_cudagraph_keys(
211 vllm_config.compilation_config.cudagraph_mode
212 )
213 if cudagraph_dispatcher.cudagraph_mode == CUDAGraphMode.NONE:
214 pytest.skip("CUDA/XPU graph is disabled.Please enable it to run this test. ")
215 while (
216 len(example_prompts)
217 == cudagraph_dispatcher.dispatch(len(example_prompts))[1].num_tokens
218 ):
219 example_prompts.append(example_prompts[0])
220
221 try:
222 with vllm_runner(model, enable_chunked_prefill=True) as vllm_model:
223 vllm_model.generate_greedy(example_prompts, max_tokens)
224 except RuntimeError:
225 pytest.fail(
226 "Couldn't run batch size which is not equal to a Cuda Graph "
227 "captured batch size. "
228 "Could be related to mamba cache not padded correctly"
229 )
230
231
232 @pytest.mark.parametrize("model", [SSM_MODELS[0], HYBRID_MODELS[0]])
233 def test_fail_upon_inc_requests_and_finished_requests_lt_available_blocks(
234 vllm_runner,
235 example_prompts,
236 model: str,
237 ) -> None:
238 """This test is for verifying that the hybrid inner state management doesn't
239 collapse in case where the number of incoming requests and
240 finished_requests_ids is larger than the maximum mamba block capacity.
241
242 This could generally happen due to the fact that hybrid does support
243 statelessness mechanism where it can clean up new incoming requests in
244 a single step.
245 """
246 try:
247 with vllm_runner(
248 model, max_num_seqs=MAX_NUM_SEQS, enable_chunked_prefill=True
249 ) as vllm_model:
250 vllm_model.generate_greedy([example_prompts[0]] * 100, 10)
251 except ValueError:
252 pytest.fail(
253 "Hybrid inner state wasn't cleaned up properly between"
254 "steps finished requests registered unnecessarily "
255 )
256
257
258 @pytest.mark.parametrize("model", [SSM_MODELS[0], HYBRID_MODELS[0]])
259 def test_state_cleanup(
260 vllm_runner,
261 example_prompts,
262 model: str,
263 ) -> None:
264 """This test is for verifying that the Hybrid state is cleaned up between
265 steps.
266
267 If it's not cleaned, an error would be expected.
268 """
269 try:
270 with vllm_runner(
271 model, max_num_seqs=MAX_NUM_SEQS, enable_chunked_prefill=True
272 ) as vllm_model:
273 for _ in range(10):
274 vllm_model.generate_greedy([example_prompts[0]] * 100, 1)
275 except ValueError:
276 pytest.fail(
277 "Hybrid inner state wasn't cleaned up between states, "
278 "could be related to finished_requests_ids"
279 )
280
281
282 @multi_gpu_test(num_gpus=2)
283 @pytest.mark.parametrize("model", [SSM_MODELS[0], HYBRID_MODELS[0]])
284 @pytest.mark.parametrize("max_tokens", [64])
285 @pytest.mark.parametrize("num_logprobs", [5])
286 def test_distributed_correctness(
287 vllm_runner,
288 example_prompts,
289 model: str,
290 max_tokens: int,
291 num_logprobs: int,
292 ) -> None:
293 with vllm_runner(
294 model,
295 tensor_parallel_size=1,
296 max_num_seqs=MAX_NUM_SEQS,
297 enable_chunked_prefill=True,
298 ) as vllm_model:
299 vllm_outputs_tp_1 = vllm_model.generate_greedy_logprobs(
300 example_prompts, max_tokens, num_logprobs
301 )
302
303 with vllm_runner(
304 model,
305 tensor_parallel_size=2,
306 max_num_seqs=MAX_NUM_SEQS,
307 enable_chunked_prefill=True,
308 ) as vllm_model:
309 vllm_outputs_tp_2 = vllm_model.generate_greedy_logprobs(
310 example_prompts, max_tokens, num_logprobs
311 )
312
313 check_logprobs_close(
314 outputs_0_lst=vllm_outputs_tp_1,
315 outputs_1_lst=vllm_outputs_tp_2,
316 name_0="vllm_tp_1",
317 name_1="vllm_tp_2",
318 )
319
320
321 @pytest.mark.parametrize("model", FULL_CUDA_GRAPH_MODELS)
322 @pytest.mark.parametrize("max_tokens", [64])
323 @pytest.mark.parametrize("num_logprobs", [5])
324 def test_full_cuda_graph(
325 hf_runner,
326 vllm_runner,
327 example_prompts,
328 monkeypatch,
329 model: str,
330 max_tokens: int,
331 num_logprobs: int,
332 ) -> None:
333 try:
334 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
335 model_info.check_available_online(on_fail="skip")
336 model_info.check_transformers_version(on_fail="skip")
337 except ValueError:
338 pass
339
340 with hf_runner(model) as hf_model:
341 hf_outputs = hf_model.generate_greedy_logprobs_limit(
342 example_prompts, max_tokens, num_logprobs
343 )
344
345 with vllm_runner(
346 model,
347 max_num_seqs=MAX_NUM_SEQS,
348 attention_backend=ATTN_BACKEND,
349 enable_chunked_prefill=True,
350 ) as vllm_model:
351 vllm_outputs = vllm_model.generate_greedy_logprobs(
352 example_prompts, max_tokens, num_logprobs
353 )
354
355 check_logprobs_close(
356 outputs_0_lst=hf_outputs,
357 outputs_1_lst=vllm_outputs,
358 name_0="hf",
359 name_1="vllm",
360 )
361
362
363 @pytest.mark.parametrize("model", FP32_STATE_MODELS)
364 @pytest.mark.parametrize("max_tokens", [64])
365 @pytest.mark.parametrize("num_logprobs", [5])
366 @pytest.mark.parametrize(
367 "cache_dtype_param", ["mamba_ssm_cache_dtype", "mamba_cache_dtype"]
368 )
369 def test_fp32_cache_state(
370 hf_runner,
371 vllm_runner,
372 example_prompts,
373 monkeypatch,
374 model: str,
375 max_tokens: int,
376 num_logprobs: int,
377 cache_dtype_param: str,
378 ) -> None:
379 try:
380 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
381 model_info.check_available_online(on_fail="skip")
382 model_info.check_transformers_version(on_fail="skip")
383 except ValueError:
384 pass
385
386 with hf_runner(model) as hf_model:
387 hf_outputs = hf_model.generate_greedy_logprobs_limit(
388 example_prompts, max_tokens, num_logprobs
389 )
390
391 # Leave enough headroom for repeated engine initialization on a
392 # 32.5 GiB MIG.
393 with vllm_runner(
394 model,
395 max_num_seqs=MAX_NUM_SEQS,
396 gpu_memory_utilization=0.9,
397 enable_chunked_prefill=True,
398 **{cache_dtype_param: "float32"},
399 ) as vllm_model:
400 vllm_outputs = vllm_model.generate_greedy_logprobs(
401 example_prompts, max_tokens, num_logprobs
402 )
403
404 check_logprobs_close(
405 outputs_0_lst=hf_outputs,
406 outputs_1_lst=vllm_outputs,
407 name_0="hf",
408 name_1="vllm",
409 )
410
411
412 # Helper functions for the APC tests
413 def _get_vllm_runner_params(
414 model: str,
415 max_model_len: int,
416 tensor_parallel_size: int = 1,
417 ):
418 return {
419 "model_name": model,
420 "enable_chunked_prefill": True,
421 "enable_prefix_caching": False,
422 "max_model_len": max_model_len,
423 "tensor_parallel_size": tensor_parallel_size,
424 "gpu_memory_utilization": 0.4,
425 "attention_backend": ATTN_BACKEND,
426 }
427
428
429 @contextmanager
430 def _owned_vllm_runner(vllm_runner, kwargs):
431 with vllm_runner(**kwargs) as runner:
432 yield runner
433
434
435 def _get_vLLM_output(
436 vllm_runner,
437 kwargs,
438 prompts,
439 max_tokens,
440 num_logprobs,
441 num_repetitions=1,
442 vllm_model=None,
443 ):
444 runner_context = (
445 _owned_vllm_runner(vllm_runner, kwargs)
446 if vllm_model is None
447 else nullcontext(vllm_model)
448 )
449 with runner_context as runner:
450 outs = []
451 for _ in range(num_repetitions):
452 if num_logprobs < 0:
453 vllm_output = runner.generate_greedy(prompts, max_tokens)
454 else:
455 vllm_output = runner.generate_greedy_logprobs(
456 prompts, max_tokens, num_logprobs
457 )
458 outs.append(vllm_output)
459
460 return outs, vllm_model
461
462
463 @pytest.mark.parametrize("model", [HYBRID_MODELS[0]])
464 @pytest.mark.parametrize("max_tokens", [64])
465 @pytest.mark.parametrize("n_repetitions", [2])
466 # If num_logprobs is set to -1, then the stringent version
467 # of the test is executed using `check_outputs_equal`
468 # instead of `check_logprobs_close`
469 @pytest.mark.parametrize("num_logprobs", [5])
470 @pytest.mark.parametrize("tensor_parallel_size", [1])
471 def test_apc_single_prompt(
472 hf_runner,
473 vllm_runner,
474 example_prompts,
475 monkeypatch,
476 model: str,
477 max_tokens: int,
478 n_repetitions: int,
479 num_logprobs: int,
480 tensor_parallel_size: int,
481 ) -> None:
482 try:
483 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
484 model_info.check_available_online(on_fail="skip")
485 model_info.check_transformers_version(on_fail="skip")
486 except ValueError:
487 pass
488
489 compare_operator: Callable = (
490 check_logprobs_close if num_logprobs > 0 else check_outputs_equal # type: ignore
491 )
492
493 # Sample prompts.
494 generated_prompts = [APC_MULTIPLY_BY * example_prompts[0]]
495
496 max_model_len = max(len(prompt) + max_tokens for prompt in generated_prompts)
497 vllm_runner_kwargs = _get_vllm_runner_params(
498 model, max_model_len, tensor_parallel_size=tensor_parallel_size
499 )
500 vllm_runner_kwargs["mamba_ssm_cache_dtype"] = "float32"
501 vllm_outputs_no_cache, _ = _get_vLLM_output(
502 vllm_runner, vllm_runner_kwargs, generated_prompts, max_tokens, num_logprobs
503 )
504
505 vllm_runner_kwargs["enable_prefix_caching"] = True
506 vllm_outputs_cache_rep, _ = _get_vLLM_output(
507 vllm_runner,
508 vllm_runner_kwargs,
509 generated_prompts,
510 max_tokens,
511 num_logprobs,
512 n_repetitions,
513 )
514
515 for r_idx, vllm_outputs_cache_itn in enumerate(vllm_outputs_cache_rep):
516 # In the first repetition, the caches are filled
517 # In the second repetition, these caches are reused
518
519 compare_operator(
520 outputs_0_lst=vllm_outputs_no_cache[0],
521 outputs_1_lst=vllm_outputs_cache_itn,
522 name_0="vllm_no_cache",
523 name_1=f"vllm_cache_it_{r_idx + 1}",
524 )
525
526
527 @pytest.mark.parametrize("model", [HYBRID_MODELS[0]])
528 @pytest.mark.parametrize("max_tokens", [64])
529 @pytest.mark.parametrize("n_repetitions", [2])
530 # If num_logprobs is set to -1, then the stringent version
531 # of the test is executed using `check_outputs_equal`
532 # instead of `check_logprobs_close`
533 @pytest.mark.parametrize("num_logprobs", [5])
534 @pytest.mark.parametrize("tensor_parallel_size", [1])
535 def test_apc_single_prompt_block_align_alignment(
536 hf_runner,
537 vllm_runner,
538 example_prompts,
539 monkeypatch,
540 model: str,
541 max_tokens: int,
542 n_repetitions: int,
543 num_logprobs: int,
544 tensor_parallel_size: int,
545 ) -> None:
546 try:
547 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
548 model_info.check_available_online(on_fail="skip")
549 model_info.check_transformers_version(on_fail="skip")
550 except ValueError:
551 pass
552
553 compare_operator: Callable = (
554 check_logprobs_close if num_logprobs > 0 else check_outputs_equal # type: ignore
555 )
556
557 # Sample prompts. This custom prompt is used, as it causes the most issues
558 generated_prompts = ["The president of the United States is " * APC_MULTIPLY_BY]
559
560 max_model_len = max(len(prompt) + max_tokens for prompt in generated_prompts)
561 vllm_runner_kwargs = _get_vllm_runner_params(
562 model, max_model_len, tensor_parallel_size=tensor_parallel_size
563 )
564 vllm_runner_kwargs["mamba_ssm_cache_dtype"] = "float32"
565
566 vllm_outputs_no_cache, _ = _get_vLLM_output(
567 vllm_runner, vllm_runner_kwargs, generated_prompts, max_tokens, num_logprobs
568 )
569
570 vllm_runner_kwargs["enable_prefix_caching"] = True
571 with vllm_runner(**vllm_runner_kwargs) as vllm_model:
572 # Retrieve the default mamba state block size
573 vllm_config = vllm_model.llm.llm_engine.vllm_config
574 mamba_block_size = vllm_config.cache_config.mamba_block_size
575
576 # In case the hybrid model does not have the
577 # "mamba_block_size" assume a fixed constant
578 if mamba_block_size is None:
579 mamba_block_size = 512
580
581 mamba_block_size_multiplier = 10
582 for offsets in [-3, 3, mamba_block_size // 4 + 3, mamba_block_size // 2 - 3]:
583 vllm_runner_kwargs["max_num_batched_tokens"] = (
584 mamba_block_size_multiplier * mamba_block_size - offsets
585 )
586 vllm_outputs_cache_rep, _ = _get_vLLM_output(
587 vllm_runner,
588 vllm_runner_kwargs,
589 generated_prompts,
590 max_tokens,
591 num_logprobs,
592 n_repetitions,
593 )
594
595 # Check alignment of the output logits when using APC
596 for r_idx, vllm_outputs_cache_itn in enumerate(vllm_outputs_cache_rep):
597 # In the first repetition, the caches are filled
598 # In the second repetition, these caches are reused
599
600 compare_operator(
601 outputs_0_lst=vllm_outputs_no_cache[0],
602 outputs_1_lst=vllm_outputs_cache_itn,
603 name_0="vllm_no_cache",
604 name_1=f"vllm_cache_it_{r_idx + 1}",
605 )
606
607
608 @pytest.mark.parametrize("model", [HYBRID_MODELS[0]])
609 @pytest.mark.parametrize("max_tokens", [64])
610 @pytest.mark.parametrize("n_repetitions", [2])
611 # If num_logprobs is set to -1, then the stringent version
612 # of the test is executed using `check_outputs_equal`
613 # instead of `check_logprobs_close`
614 @pytest.mark.parametrize("num_logprobs", [5])
615 @pytest.mark.parametrize("tensor_parallel_size", [1])
616 def test_apc_multiple_prompts_all_cached_outputs(
617 hf_runner,
618 vllm_runner,
619 example_prompts,
620 monkeypatch,
621 model: str,
622 max_tokens: int,
623 n_repetitions: int,
624 num_logprobs: int,
625 tensor_parallel_size: int,
626 ) -> None:
627 try:
628 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
629 model_info.check_available_online(on_fail="skip")
630 model_info.check_transformers_version(on_fail="skip")
631 except ValueError:
632 pass
633
634 compare_operator: Callable = (
635 check_logprobs_close if num_logprobs > 0 else check_outputs_equal # type: ignore
636 )
637
638 # Sample prompts.
639 generated_prompts = [APC_MULTIPLY_BY * prompt for prompt in example_prompts]
640
641 max_model_len = max(len(prompt) + max_tokens for prompt in generated_prompts)
642 vllm_runner_kwargs = _get_vllm_runner_params(
643 model, max_model_len, tensor_parallel_size=tensor_parallel_size
644 )
645 vllm_runner_kwargs["mamba_ssm_cache_dtype"] = "float32"
646 # Reduce the effects of batch variance on ROCm since batch invariance is not
647 # yet supported. See: https://github.com/vllm-project/vllm/issues/27433
648 if current_platform.is_rocm():
649 vllm_runner_kwargs["max_num_seqs"] = 4
650
651 vllm_outputs_no_cache, _ = _get_vLLM_output(
652 vllm_runner, vllm_runner_kwargs, generated_prompts, max_tokens, num_logprobs
653 )
654
655 vllm_runner_kwargs["enable_prefix_caching"] = True
656 vllm_outputs_cache_rep, _ = _get_vLLM_output(
657 vllm_runner,
658 vllm_runner_kwargs,
659 generated_prompts,
660 max_tokens,
661 num_logprobs,
662 n_repetitions,
663 )
664
665 for r_idx, vllm_outputs_cache_itn in enumerate(vllm_outputs_cache_rep):
666 # In the first repetition, the caches are filled
667 # In the second repetition, these caches are reused
668
669 compare_operator(
670 outputs_0_lst=vllm_outputs_no_cache[0],
671 outputs_1_lst=vllm_outputs_cache_itn,
672 name_0="vllm_no_cache",
673 name_1=f"vllm_cache_it_{r_idx + 1}",
674 )
675
676
677 @pytest.mark.parametrize("model", [HYBRID_MODELS[0]])
678 @pytest.mark.parametrize("max_tokens", [64])
679 @pytest.mark.parametrize("n_repetitions", [2])
680 # If num_logprobs is set to -1, then the stringent version
681 # of the test is executed using `check_outputs_equal`
682 # instead of `check_logprobs_close`
683 @pytest.mark.parametrize("num_logprobs", [5])
684 @pytest.mark.parametrize("tensor_parallel_size", [1])
685 def test_apc_multiple_prompts_block_align_alignment(
686 hf_runner,
687 vllm_runner,
688 example_prompts,
689 monkeypatch,
690 model: str,
691 max_tokens: int,
692 n_repetitions: int,
693 num_logprobs: int,
694 tensor_parallel_size: int,
695 ) -> None:
696 try:
697 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
698 model_info.check_available_online(on_fail="skip")
699 model_info.check_transformers_version(on_fail="skip")
700 except ValueError:
701 pass
702
703 compare_operator: Callable = (
704 check_logprobs_close if num_logprobs > 0 else check_outputs_equal # type: ignore
705 )
706
707 # Sample prompts. This custom prompt is used, as it causes the most issues
708 prompt_text = "The president of the United States is "
709 prompt_offsets = [0, 3, 7, 13, 17, 22, 25, 31]
710 generated_prompts = [
711 prompt_text[offset:] * APC_MULTIPLY_BY for offset in prompt_offsets
712 ]
713
714 max_model_len = max(len(prompt) + max_tokens for prompt in generated_prompts)
715 vllm_runner_kwargs = _get_vllm_runner_params(
716 model, max_model_len, tensor_parallel_size
717 )
718 vllm_runner_kwargs["mamba_ssm_cache_dtype"] = "float32"
719
720 vllm_outputs_no_cache, _ = _get_vLLM_output(
721 vllm_runner, vllm_runner_kwargs, generated_prompts, max_tokens, num_logprobs
722 )
723
724 vllm_runner_kwargs["enable_prefix_caching"] = True
725 with vllm_runner(**vllm_runner_kwargs) as vllm_model:
726 # Retrieve the default mamba state block size
727 vllm_config = vllm_model.llm.llm_engine.vllm_config
728 mamba_block_size = vllm_config.cache_config.mamba_block_size
729
730 # In case the hybrid model does not have the
731 # "mamba_block_size" assume a fixed constant
732 if mamba_block_size is None:
733 mamba_block_size = 512
734
735 mamba_block_size_multiplier = 10
736 for offsets in [-3, 3, mamba_block_size // 4 + 3, mamba_block_size // 2 - 3]:
737 vllm_runner_kwargs["max_num_batched_tokens"] = (
738 mamba_block_size_multiplier * mamba_block_size - offsets
739 )
740 vllm_outputs_cache_rep, _ = _get_vLLM_output(
741 vllm_runner,
742 vllm_runner_kwargs,
743 generated_prompts,
744 max_tokens,
745 num_logprobs,
746 n_repetitions,
747 )
748
749 # Check alignment of the output logits when using APC
750 for r_idx, vllm_outputs_cache_itn in enumerate(vllm_outputs_cache_rep):
751 # In the first repetition, the caches are filled
752 # In the second repetition, these caches are reused
753
754 compare_operator(
755 outputs_0_lst=vllm_outputs_no_cache[0],
756 outputs_1_lst=vllm_outputs_cache_itn,
757 name_0="vllm_no_cache",
758 name_1=f"vllm_cache_it_{r_idx + 1}",
759 )
760
761
762 @pytest.mark.parametrize("model", [HYBRID_MODELS[0]])
763 @pytest.mark.parametrize("max_tokens", [64])
764 @pytest.mark.parametrize("n_repetitions", [2])
765 # If num_logprobs is set to -1, then the stringent version
766 # of the test is executed using `check_outputs_equal`
767 # instead of `check_logprobs_close`
768 @pytest.mark.parametrize("num_logprobs", [5])
769 @pytest.mark.parametrize("tensor_parallel_size", [1])
770 def test_apc_multiple_prompts_partial_cached_outputs(
771 hf_runner,
772 vllm_runner,
773 example_prompts,
774 monkeypatch,
775 model: str,
776 max_tokens: int,
777 n_repetitions: int,
778 num_logprobs: int,
779 tensor_parallel_size: int,
780 ) -> None:
781 try:
782 model_info = HF_EXAMPLE_MODELS.find_hf_info(model)
783 model_info.check_available_online(on_fail="skip")
784 model_info.check_transformers_version(on_fail="skip")
785 except ValueError:
786 pass
787
788 compare_operator: Callable = (
789 check_logprobs_close if num_logprobs > 0 else check_outputs_equal # type: ignore
790 )
791
792 # Sample prompts.
793 generated_prompts = [APC_MULTIPLY_BY * prompt for prompt in example_prompts]
794
795 max_model_len = max(len(prompt) + max_tokens for prompt in generated_prompts)
796 vllm_runner_kwargs = _get_vllm_runner_params(
797 model, max_model_len, tensor_parallel_size=tensor_parallel_size
798 )
799 vllm_runner_kwargs["mamba_ssm_cache_dtype"] = "float32"
800
801 vllm_outputs_no_cache, _ = _get_vLLM_output(
802 vllm_runner, vllm_runner_kwargs, generated_prompts, max_tokens, num_logprobs
803 )
804
805 # Cache only part of all the prompts
806 vllm_runner_kwargs["enable_prefix_caching"] = True
807 with _owned_vllm_runner(vllm_runner, vllm_runner_kwargs) as vllm_model:
808 vllm_outputs_partial_cache, _ = _get_vLLM_output(
809 vllm_runner,
810 vllm_runner_kwargs,
811 generated_prompts[:3],
812 max_tokens,
813 num_logprobs,
814 vllm_model=vllm_model,
815 )
816
817 compare_operator(
818 outputs_0_lst=vllm_outputs_no_cache[0][:3],
819 outputs_1_lst=vllm_outputs_partial_cache[0],
820 name_0="vllm_no_cache",
821 name_1="vllm_partial_cache",
822 )
823
824 vllm_outputs_cache_rep, _ = _get_vLLM_output(
825 vllm_runner,
826 vllm_runner_kwargs,
827 generated_prompts,
828 max_tokens,
829 num_logprobs,
830 n_repetitions,
831 vllm_model=vllm_model,
832 )
833
834 for r_idx, vllm_outputs_cache_itn in enumerate(vllm_outputs_cache_rep):
835 # In the first repetition, the caches are filled
836 # In the second repetition, these caches are reused
837
838 compare_operator(
839 outputs_0_lst=vllm_outputs_no_cache[0],
840 outputs_1_lst=vllm_outputs_cache_itn,
841 name_0="vllm_no_cache",
842 name_1=f"vllm_cache_it_{r_idx + 1}",
843 )
844
845
846 # Test that outputs match whether prefix caching is enabled or not for mamba.
847 @pytest.mark.parametrize("model", ["tiiuae/falcon-mamba-7b"])
848 def test_same_mamba_output_apc_on_vs_off(
849 vllm_runner,
850 model: str,
851 ) -> None:
852 num_logprobs = 5
853 prompts = [
854 "hello what is one plus one what is one plus one what is one plus one the answer is", # noqa: E501
855 "hello what is one plus one what is one plus one what is one plus one the answer is", # noqa: E501
856 ]
857 max_tokens = 20
858 max_model_len = max(len(p) for p in prompts) + max_tokens + 64
859
860 base_kwargs = _get_vllm_runner_params(model, max_model_len)
861 base_kwargs.update(
862 enforce_eager=True, block_size=16, seed=42, gpu_memory_utilization=0.8
863 )
864
865 # No prefix caching
866 kwargs_no_apc = {**base_kwargs, "enable_prefix_caching": False}
867 with vllm_runner(**kwargs_no_apc) as vllm_model:
868 outputs_no_apc, _ = _get_vLLM_output(
869 vllm_runner,
870 kwargs_no_apc,
871 prompts,
872 max_tokens,
873 num_logprobs=num_logprobs,
874 vllm_model=vllm_model,
875 )
876 # With prefix caching
877 kwargs_with_apc = {
878 **base_kwargs,
879 "enable_prefix_caching": True,
880 "mamba_block_size": 16,
881 }
882 with vllm_runner(**kwargs_with_apc) as vllm_model:
883 outputs_with_apc, _ = _get_vLLM_output(
884 vllm_runner,
885 kwargs_with_apc,
886 prompts,
887 max_tokens,
888 num_logprobs=num_logprobs,
889 vllm_model=vllm_model,
890 )
891
892 check_logprobs_close(
893 outputs_0_lst=outputs_no_apc[0],
894 outputs_1_lst=outputs_with_apc[0],
895 name_0="vllm_no_apc",
896 name_1="vllm_with_apc",
897 )
898
899
900 # we have to use a real large model to get reasonable results
901 # the model can't be a hybrid model as we need block_size 16
902 @pytest.mark.parametrize("model", ["tiiuae/falcon-mamba-7b"])
903 def test_apc_common_prefix_same_batch(
904 model: str,
905 monkeypatch,
906 ) -> None:
907 # Required to put the two requests in the same batch
908 monkeypatch.setenv("VLLM_ENABLE_V1_MULTIPROCESSING", "0")
909 llm = LLM(
910 model=model,
911 enforce_eager=True,
912 block_size=16,
913 mamba_block_size=16,
914 enable_prefix_caching=True,
915 seed=42,
916 attention_backend=ATTN_BACKEND,
917 )
918 prompts = [
919 "hello what is one plus one what is one plus one what is one plus one the answer is", # noqa: E501
920 "hello what is one plus one what is one plus one what is one plus one the answer is", # noqa: E501
921 ]
922 sampling_params = SamplingParams(temperature=0.0, max_tokens=20)
923 outputs = llm.generate(prompts, sampling_params)
924 for output in outputs:
925 assert "two" in output.outputs[0].text