main
py 608 lines 22 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Unit tests for sequence and token pooling methods and their factories."""
4
5 from dataclasses import dataclass
6 from types import SimpleNamespace
7 from unittest.mock import patch
8
9 import numpy as np
10 import pytest
11 import torch
12 from transformers import PreTrainedConfig
13
14 from vllm.config import PoolerConfig, set_current_vllm_config
15 from vllm.model_executor.layers.pooler import PoolingParamsUpdate
16 from vllm.model_executor.layers.pooler.seqwise.methods import (
17 CLSPool,
18 LastPool,
19 MeanPool,
20 get_seq_pooling_method,
21 )
22 from vllm.model_executor.layers.pooler.special import DispatchPooler
23 from vllm.model_executor.layers.pooler.tokwise.methods import (
24 AllPool,
25 StepPool,
26 get_tok_pooling_method,
27 )
28 from vllm.pooling_params import PoolingParams
29 from vllm.tasks import PoolingTask
30 from vllm.v1.pool.metadata import PoolingCursor, PoolingMetadata, PoolingStates
31
32 _CPU = torch.device("cpu")
33
34
35 def _make_pooling_cursor(
36 prompt_lens: list[int],
37 *,
38 num_scheduled_tokens: list[int] | None = None,
39 seq_lens: list[int] | None = None,
40 device: torch.device = _CPU,
41 ) -> PoolingCursor:
42 """Build a PoolingCursor from a list of per-sequence prompt lengths."""
43 prompt_lens_cpu = torch.tensor(prompt_lens, dtype=torch.long)
44 if num_scheduled_tokens is None:
45 num_scheduled_tokens_cpu = prompt_lens_cpu.clone()
46 else:
47 num_scheduled_tokens_cpu = torch.tensor(num_scheduled_tokens, dtype=torch.long)
48 if seq_lens is None:
49 seq_lens_cpu = prompt_lens_cpu.clone()
50 else:
51 seq_lens_cpu = torch.tensor(seq_lens, dtype=torch.long)
52
53 cumsum = torch.zeros(len(prompt_lens) + 1, dtype=torch.long, device=device)
54 torch.cumsum(num_scheduled_tokens_cpu, dim=0, out=cumsum[1:])
55
56 return PoolingCursor(
57 first_token_indices_gpu=cumsum[: len(prompt_lens)].to(device),
58 last_token_indices_gpu=(cumsum[1:] - 1).to(device),
59 prompt_lens_cpu=prompt_lens_cpu,
60 seq_lens_cpu=seq_lens_cpu,
61 num_scheduled_tokens_cpu=num_scheduled_tokens_cpu,
62 partial_prefill=not torch.equal(prompt_lens_cpu, num_scheduled_tokens_cpu),
63 finished_mask=torch.eq(prompt_lens_cpu, seq_lens_cpu).tolist(),
64 )
65
66
67 def _make_metadata(
68 prompt_lens: list[int],
69 *,
70 tasks: list[PoolingTask] | None = None,
71 token_ids: list[list[int]] | None = None,
72 pooling_params: list[PoolingParams] | None = None,
73 num_scheduled_tokens: list[int] | None = None,
74 seq_lens: list[int] | None = None,
75 device: torch.device = _CPU,
76 ) -> PoolingMetadata:
77 """Build a minimal PoolingMetadata for testing pooling methods."""
78 n_seqs = len(prompt_lens)
79 if tasks is None:
80 tasks = ["embed"] * n_seqs
81 if pooling_params is None:
82 pooling_params = [PoolingParams(task=t) for t in tasks]
83
84 prompt_lens_tensor = torch.tensor(prompt_lens, dtype=torch.long)
85
86 prompt_token_ids_cpu = None
87 prompt_token_ids = None
88 if token_ids is not None:
89 max_len = max(len(t) for t in token_ids)
90 padded = [t + [0] * (max_len - len(t)) for t in token_ids]
91 prompt_token_ids_cpu = torch.tensor(padded, dtype=torch.long)
92 prompt_token_ids = prompt_token_ids_cpu.to(device)
93
94 cursor = _make_pooling_cursor(
95 prompt_lens,
96 num_scheduled_tokens=num_scheduled_tokens,
97 seq_lens=seq_lens,
98 device=device,
99 )
100
101 pooling_states = [PoolingStates() for _ in range(n_seqs)]
102
103 return PoolingMetadata(
104 prompt_lens=prompt_lens_tensor,
105 prompt_token_ids=prompt_token_ids,
106 prompt_token_ids_cpu=prompt_token_ids_cpu,
107 pooling_params=pooling_params,
108 pooling_states=pooling_states,
109 pooling_cursor=cursor,
110 )
111
112
113 def test_build_pooling_cursor_caches_cpu_status() -> None:
114 metadata = PoolingMetadata(
115 prompt_lens=torch.tensor([3, 4, 2], dtype=torch.long),
116 prompt_token_ids=None,
117 prompt_token_ids_cpu=None,
118 pooling_params=[PoolingParams(task="embed") for _ in range(3)],
119 pooling_states=[PoolingStates() for _ in range(3)],
120 )
121 metadata.build_pooling_cursor(
122 np.array([3, 2, 2], dtype=np.int32),
123 seq_lens_cpu=torch.tensor([3, 2, 1], dtype=torch.long),
124 device=_CPU,
125 )
126
127 cursor = metadata.get_pooling_cursor()
128
129 assert cursor.is_partial_prefill() is True
130 assert cursor.get_finished_mask() == [True, False, False]
131 assert cursor[:1].is_partial_prefill() is False
132 assert cursor[1:2].is_partial_prefill() is True
133 assert cursor[1:].get_finished_mask() == [False, False]
134
135
136 def test_pooling_params_update_combines_token_id_requirements() -> None:
137 update = PoolingParamsUpdate() | PoolingParamsUpdate(requires_token_ids=True)
138 params = PoolingParams(task="embed")
139
140 update.apply(params)
141
142 assert params.requires_token_ids is True
143
144
145 # ---------------------------------------------------------------------------
146 # CLSPool
147 # ---------------------------------------------------------------------------
148 class TestCLSPool:
149 def test_extracts_first_token(self):
150 hidden = torch.tensor(
151 [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0], [9.0, 10.0]]
152 )
153 metadata = _make_metadata([2, 3])
154 pooler = CLSPool()
155 out = pooler(hidden, metadata)
156 expected = torch.tensor([[1.0, 2.0], [5.0, 6.0]])
157 assert torch.equal(out, expected)
158
159 def test_rejects_partial_prefill(self):
160 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
161 metadata = _make_metadata([3], num_scheduled_tokens=[2])
162 pooler = CLSPool()
163 with pytest.raises(RuntimeError, match="partial prefill"):
164 pooler(hidden, metadata)
165
166
167 # ---------------------------------------------------------------------------
168 # LastPool
169 # ---------------------------------------------------------------------------
170 class TestLastPool:
171 def test_extracts_last_token(self):
172 hidden = torch.tensor(
173 [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0], [9.0, 10.0]]
174 )
175 metadata = _make_metadata([2, 3])
176 pooler = LastPool()
177 out = pooler(hidden, metadata)
178 expected = torch.tensor([[3.0, 4.0], [9.0, 10.0]])
179 assert torch.equal(out, expected)
180
181 def test_partial_prefill_extracts_last_scheduled(self):
182 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
183 metadata = _make_metadata([4], num_scheduled_tokens=[2])
184 pooler = LastPool()
185 out = pooler(hidden, metadata)
186 expected = torch.tensor([[3.0, 4.0]])
187 assert torch.equal(out, expected)
188
189
190 # ---------------------------------------------------------------------------
191 # MeanPool
192 # ---------------------------------------------------------------------------
193 class TestMeanPool:
194 def test_computes_mean(self):
195 hidden = torch.tensor(
196 [[1.0, 2.0], [3.0, 4.0], [10.0, 20.0]], dtype=torch.float32
197 )
198 metadata = _make_metadata([2, 1])
199 pooler = MeanPool()
200 out = pooler(hidden, metadata)
201 expected = torch.tensor([[2.0, 3.0], [10.0, 20.0]], dtype=torch.float32)
202 assert torch.allclose(out, expected, atol=1e-5)
203
204 def test_single_token_is_identity(self):
205 hidden = torch.tensor([[5.0, 10.0]], dtype=torch.float32)
206 metadata = _make_metadata([1])
207 pooler = MeanPool()
208 out = pooler(hidden, metadata)
209 assert torch.allclose(out, hidden, atol=1e-5)
210
211 def test_uniform_values_return_same(self):
212 hidden = torch.full((4, 3), 7.0, dtype=torch.float32)
213 metadata = _make_metadata([4])
214 pooler = MeanPool()
215 out = pooler(hidden, metadata)
216 expected = torch.full((1, 3), 7.0, dtype=torch.float32)
217 assert torch.allclose(out, expected, atol=1e-5)
218
219 def test_multiple_sequences(self):
220 hidden = torch.tensor(
221 [
222 [0.0, 0.0],
223 [2.0, 4.0],
224 [4.0, 8.0],
225 [10.0, 10.0],
226 ],
227 dtype=torch.float32,
228 )
229 metadata = _make_metadata([3, 1])
230 pooler = MeanPool()
231 out = pooler(hidden, metadata)
232 expected = torch.tensor([[2.0, 4.0], [10.0, 10.0]], dtype=torch.float32)
233 assert torch.allclose(out, expected, atol=1e-5)
234
235 def test_empty_batch(self):
236 hidden = torch.empty((0, 8), dtype=torch.float32)
237 metadata = _make_metadata([])
238 pooler = MeanPool()
239 out = pooler(hidden, metadata)
240 assert out.shape == (0, 8)
241
242 def test_rejects_partial_prefill(self):
243 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0]], dtype=torch.float32)
244 metadata = _make_metadata([3], num_scheduled_tokens=[2])
245 pooler = MeanPool()
246 with pytest.raises(RuntimeError, match="partial prefill"):
247 pooler(hidden, metadata)
248
249 def test_chunked_accumulation(self):
250 hidden = torch.arange(20, dtype=torch.float32).reshape(5, 4)
251 metadata = _make_metadata([3, 2])
252 pooler = MeanPool()
253 with patch(
254 "vllm.model_executor.layers.pooler.seqwise.methods"
255 "._MEAN_POOL_ACCUMULATION_CHUNK_BYTES",
256 16,
257 ):
258 out = pooler(hidden, metadata)
259 expected_seq0 = hidden[:3].float().mean(dim=0, keepdim=True)
260 expected_seq1 = hidden[3:].float().mean(dim=0, keepdim=True)
261 expected = torch.cat([expected_seq0, expected_seq1], dim=0)
262 assert torch.allclose(out, expected, atol=1e-5)
263
264 def test_upcasts_to_float32(self):
265 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0]], dtype=torch.float16)
266 metadata = _make_metadata([2])
267 pooler = MeanPool()
268 out = pooler(hidden, metadata)
269 assert out.dtype == torch.float32
270 expected = torch.tensor([[2.0, 3.0]], dtype=torch.float32)
271 assert torch.allclose(out, expected, atol=1e-2)
272
273
274 # ---------------------------------------------------------------------------
275 # get_seq_pooling_method factory
276 # ---------------------------------------------------------------------------
277 class TestGetSeqPoolingMethod:
278 def test_cls(self):
279 assert isinstance(get_seq_pooling_method("CLS"), CLSPool)
280
281 def test_last(self):
282 assert isinstance(get_seq_pooling_method("LAST"), LastPool)
283
284 def test_mean(self):
285 assert isinstance(get_seq_pooling_method("MEAN"), MeanPool)
286
287 def test_unknown_raises(self):
288 with pytest.raises(NotImplementedError, match="UNKNOWN"):
289 get_seq_pooling_method("UNKNOWN")
290
291
292 # ---------------------------------------------------------------------------
293 # AllPool
294 # ---------------------------------------------------------------------------
295
296
297 @dataclass
298 class _FakeSchedulerConfig:
299 enable_chunked_prefill: bool = False
300 async_scheduling: bool = False
301
302
303 @dataclass
304 class _FakeVllmConfig:
305 scheduler_config: _FakeSchedulerConfig
306
307
308 @pytest.mark.parametrize("tok_pooling_type", ["ALL", "STEP"])
309 def test_dispatch_seq_cls_honors_token_pooling_type(tok_pooling_type):
310 """Filter STEP rows before classification; preserve all rows for resolved ALL."""
311 pooler_config = PoolerConfig(
312 seq_pooling_type="LAST", tok_pooling_type=tok_pooling_type
313 )
314 config = SimpleNamespace(
315 scheduler_config=_FakeSchedulerConfig(),
316 model_config=SimpleNamespace(
317 pooler_config=pooler_config,
318 head_dtype=None,
319 hf_config=PreTrainedConfig(num_labels=3),
320 ),
321 )
322 classifier = torch.nn.Linear(4, 3)
323 with set_current_vllm_config(config):
324 pooler = DispatchPooler.for_seq_cls(pooler_config, classifier=classifier)
325
326 token_ids = [[2, 99, 3, 99], [4, 99], [5, 6]]
327 params = [
328 PoolingParams(
329 task="token_classify",
330 use_activation=False,
331 step_tag_id=99 if tok_pooling_type == "STEP" else None,
332 )
333 for _ in token_ids
334 ]
335 metadata = _make_metadata([4, 2, 2], token_ids=token_ids, pooling_params=params)
336 hidden = torch.arange(32, dtype=torch.float32).reshape(8, 4)
337 outputs = pooler(hidden, metadata)
338
339 expected_rows = (
340 [[1, 3], [5], []]
341 if tok_pooling_type == "STEP"
342 else [[0, 1, 2, 3], [4, 5], [6, 7]]
343 )
344 assert len(outputs) == len(expected_rows)
345 for output, rows in zip(outputs, expected_rows):
346 torch.testing.assert_close(output, classifier(hidden[rows]))
347 assert pooler.get_pooling_updates("token_classify").requires_token_ids == (
348 tok_pooling_type == "STEP"
349 )
350
351
352 class TestAllPool:
353 @staticmethod
354 def _make_all_pool(
355 *, chunked: bool = False, async_scheduling: bool = False
356 ) -> AllPool:
357 fake_config = _FakeVllmConfig(
358 scheduler_config=_FakeSchedulerConfig(
359 enable_chunked_prefill=chunked,
360 async_scheduling=async_scheduling,
361 ),
362 )
363 with patch(
364 "vllm.model_executor.layers.pooler.tokwise.methods.get_current_vllm_config",
365 return_value=fake_config,
366 ):
367 return AllPool()
368
369 def test_splits_by_sequence(self):
370 pooler = self._make_all_pool()
371 hidden = torch.tensor(
372 [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0], [9.0, 10.0]]
373 )
374 metadata = _make_metadata([2, 3])
375 out = pooler(hidden, metadata)
376 assert len(out) == 2
377 assert torch.equal(out[0], hidden[:2])
378 assert torch.equal(out[1], hidden[2:])
379
380 def test_single_sequence(self):
381 pooler = self._make_all_pool()
382 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
383 metadata = _make_metadata([3])
384 out = pooler(hidden, metadata)
385 assert len(out) == 1
386 assert torch.equal(out[0], hidden)
387
388 def test_chunked_prefill_returns_none_for_unfinished(self):
389 pooler = self._make_all_pool(chunked=True)
390 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
391 metadata = _make_metadata(
392 [4],
393 num_scheduled_tokens=[2],
394 seq_lens=[2],
395 )
396 out = pooler(hidden, metadata)
397 assert len(out) == 1
398 assert out[0] is None
399
400 def test_chunked_prefill_returns_concat_when_finished(self):
401 pooler = self._make_all_pool(chunked=True)
402
403 chunk1 = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
404 metadata1 = _make_metadata(
405 [4],
406 num_scheduled_tokens=[2],
407 seq_lens=[2],
408 )
409 out1 = pooler(chunk1, metadata1)
410 assert out1[0] is None
411 expected_chunk1 = chunk1.clone()
412 chunk1.zero_()
413
414 chunk2 = torch.tensor([[5.0, 6.0], [7.0, 8.0]])
415 metadata2 = _make_metadata(
416 [4],
417 num_scheduled_tokens=[2],
418 seq_lens=[4],
419 )
420 metadata2.pooling_states = metadata1.pooling_states
421 out2 = pooler(chunk2, metadata2)
422 assert out2[0] is not None
423 expected = torch.cat([expected_chunk1, chunk2], dim=0)
424 assert torch.equal(out2[0], expected)
425
426 @pytest.mark.parametrize("async_scheduling", [False, True])
427 def test_chunked_prefill_single_shot_matches_non_chunked(
428 self, async_scheduling: bool
429 ):
430 pooler = self._make_all_pool(chunked=True, async_scheduling=async_scheduling)
431 hidden = torch.tensor(
432 [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0], [9.0, 10.0]]
433 )
434 expected = [hidden[:2].clone(), hidden[2:].clone()]
435 metadata = _make_metadata([2, 3])
436 out = pooler(hidden, metadata)
437 if async_scheduling:
438 # Finished outputs must own their storage under async scheduling.
439 hidden.zero_()
440 assert len(out) == 2
441 assert torch.equal(out[0], expected[0])
442 assert torch.equal(out[1], expected[1])
443
444 def test_non_chunked_owns_output_under_async_scheduling(self):
445 pooler = self._make_all_pool(async_scheduling=True)
446 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
447 expected = [hidden[:2].clone(), hidden[2:].clone()]
448 metadata = _make_metadata([2, 1])
449 out = pooler(hidden, metadata)
450 hidden.zero_()
451 assert len(out) == 2
452 assert torch.equal(out[0], expected[0])
453 assert torch.equal(out[1], expected[1])
454
455 def test_chunked_prefill_mixed_finished_unfinished(self):
456 pooler = self._make_all_pool(chunked=True)
457 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
458 metadata = _make_metadata(
459 [2, 4],
460 num_scheduled_tokens=[2, 1],
461 seq_lens=[2, 1],
462 )
463 out = pooler(hidden, metadata)
464 assert len(out) == 2
465 assert torch.equal(out[0], hidden[:2])
466 assert out[1] is None
467
468
469 # ---------------------------------------------------------------------------
470 # StepPool
471 # ---------------------------------------------------------------------------
472 class TestStepPool:
473 @staticmethod
474 def _make_step_pool(*, chunked: bool = False) -> StepPool:
475 fake_config = _FakeVllmConfig(
476 scheduler_config=_FakeSchedulerConfig(
477 enable_chunked_prefill=chunked,
478 ),
479 )
480 with patch(
481 "vllm.model_executor.layers.pooler.tokwise.methods.get_current_vllm_config",
482 return_value=fake_config,
483 ):
484 return StepPool()
485
486 def test_filters_by_step_tag_id(self):
487 pooler = self._make_step_pool()
488 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0]])
489 token_ids = [[10, 99, 10, 20]]
490 params = [PoolingParams(task="token_classify", step_tag_id=10)]
491 metadata = _make_metadata([4], token_ids=token_ids, pooling_params=params)
492 out = pooler(hidden, metadata)
493 assert len(out) == 1
494 expected = torch.tensor([[1.0, 2.0], [5.0, 6.0]])
495 assert torch.equal(out[0], expected)
496
497 def test_filters_by_returned_token_ids(self):
498 pooler = self._make_step_pool()
499 hidden = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
500 token_ids = [[10, 20]]
501 params = [PoolingParams(task="token_classify", returned_token_ids=[0, 2])]
502 metadata = _make_metadata([2], token_ids=token_ids, pooling_params=params)
503 out = pooler(hidden, metadata)
504 assert len(out) == 1
505 expected = torch.tensor([[1.0, 3.0], [4.0, 6.0]])
506 assert torch.equal(out[0], expected)
507
508 def test_no_filtering_without_params(self):
509 pooler = self._make_step_pool()
510 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
511 token_ids = [[10, 20]]
512 params = [PoolingParams(task="token_classify")]
513 metadata = _make_metadata([2], token_ids=token_ids, pooling_params=params)
514 out = pooler(hidden, metadata)
515 assert len(out) == 1
516 assert torch.equal(out[0], hidden)
517
518 def test_combined_step_tag_and_returned_token_ids(self):
519 pooler = self._make_step_pool()
520 hidden = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]])
521 token_ids = [[99, 10, 99]]
522 params = [
523 PoolingParams(
524 task="token_classify",
525 step_tag_id=10,
526 returned_token_ids=[0, 2],
527 )
528 ]
529 metadata = _make_metadata([3], token_ids=token_ids, pooling_params=params)
530 out = pooler(hidden, metadata)
531 assert len(out) == 1
532 expected = torch.tensor([[4.0, 6.0]])
533 assert torch.equal(out[0], expected)
534
535 def test_step_tag_id_no_match_returns_empty(self):
536 pooler = self._make_step_pool()
537 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
538 token_ids = [[10, 20]]
539 params = [PoolingParams(task="token_classify", step_tag_id=999)]
540 metadata = _make_metadata([2], token_ids=token_ids, pooling_params=params)
541 out = pooler(hidden, metadata)
542 assert len(out) == 1
543 assert out[0].shape == (0, 2)
544
545 def test_chunked_prefill_propagates_none_for_unfinished(self):
546 pooler = self._make_step_pool(chunked=True)
547 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
548 token_ids = [[10, 20, 30, 40]]
549 params = [PoolingParams(task="token_classify", step_tag_id=10)]
550 metadata = _make_metadata(
551 [4],
552 token_ids=token_ids,
553 pooling_params=params,
554 num_scheduled_tokens=[2],
555 seq_lens=[2],
556 )
557 out = pooler(hidden, metadata)
558 assert len(out) == 1
559 assert out[0] is None
560
561 def test_chunked_prefill_filters_when_finished(self):
562 pooler = self._make_step_pool(chunked=True)
563 hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0], [7.0, 8.0]])
564 token_ids = [[10, 99, 10, 20]]
565 params = [PoolingParams(task="token_classify", step_tag_id=10)]
566 metadata = _make_metadata([4], token_ids=token_ids, pooling_params=params)
567 out = pooler(hidden, metadata)
568 assert len(out) == 1
569 expected = torch.tensor([[1.0, 2.0], [5.0, 6.0]])
570 assert torch.equal(out[0], expected)
571
572 def test_requires_token_ids_update(self):
573 pooler = self._make_step_pool()
574 update = pooler.get_pooling_updates("token_classify")
575 assert update.requires_token_ids is True
576
577
578 # ---------------------------------------------------------------------------
579 # get_tok_pooling_method factory
580 # ---------------------------------------------------------------------------
581 class TestGetTokPoolingMethod:
582 def test_all(self):
583 fake_config = _FakeVllmConfig(
584 scheduler_config=_FakeSchedulerConfig(
585 enable_chunked_prefill=False,
586 ),
587 )
588 with patch(
589 "vllm.model_executor.layers.pooler.tokwise.methods.get_current_vllm_config",
590 return_value=fake_config,
591 ):
592 assert isinstance(get_tok_pooling_method("ALL"), AllPool)
593
594 def test_step(self):
595 fake_config = _FakeVllmConfig(
596 scheduler_config=_FakeSchedulerConfig(
597 enable_chunked_prefill=False,
598 ),
599 )
600 with patch(
601 "vllm.model_executor.layers.pooler.tokwise.methods.get_current_vllm_config",
602 return_value=fake_config,
603 ):
604 assert isinstance(get_tok_pooling_method("STEP"), StepPool)
605
606 def test_unknown_raises(self):
607 with pytest.raises(NotImplementedError, match="UNKNOWN"):
608 get_tok_pooling_method("UNKNOWN")