|
3 | 3 | """Unit tests for EmbedIOProcessor.""" |
4 | 4 |
|
5 | 5 | import pytest |
| 6 | +from pydantic import TypeAdapter |
6 | 7 |
|
7 | 8 | from vllm import PoolingParams |
8 | 9 | from vllm.entrypoints.pooling.embed.io_processor import EmbedIOProcessor |
9 | 10 | from vllm.entrypoints.pooling.embed.protocol import ( |
10 | 11 | CohereEmbedContent, |
11 | 12 | CohereEmbedInput, |
12 | 13 | CohereEmbedRequest, |
| 14 | + EmbeddingBatchChatInputRequest, |
| 15 | + EmbeddingBatchChatRequest, |
| 16 | + EmbeddingChatInputRequest, |
| 17 | + EmbeddingChatRequest, |
| 18 | + EmbeddingCompletionRequest, |
| 19 | + EmbeddingRequest, |
13 | 20 | ) |
14 | 21 | from vllm.entrypoints.pooling.typing import PoolingServeContext |
15 | 22 |
|
16 | 23 |
|
| 24 | +class TestEmbeddingRequestParsing: |
| 25 | + """Unit tests for OpenAI embedding request parsing.""" |
| 26 | + |
| 27 | + def test_input_messages_parses_as_chat_request(self): |
| 28 | + request = TypeAdapter(EmbeddingRequest).validate_python( |
| 29 | + { |
| 30 | + "model": "test", |
| 31 | + "input": [{"role": "user", "content": "hello"}], |
| 32 | + "chat_template_kwargs": {"instruction": "Represent the query: "}, |
| 33 | + } |
| 34 | + ) |
| 35 | + |
| 36 | + assert isinstance(request, EmbeddingChatInputRequest) |
| 37 | + assert request.input == [{"role": "user", "content": "hello"}] |
| 38 | + assert request.messages == [{"role": "user", "content": "hello"}] |
| 39 | + assert request.chat_template_kwargs == {"instruction": "Represent the query: "} |
| 40 | + |
| 41 | + def test_batched_input_messages_parses_as_batch_chat_input_request(self): |
| 42 | + request = TypeAdapter(EmbeddingRequest).validate_python( |
| 43 | + { |
| 44 | + "model": "test", |
| 45 | + "input": [ |
| 46 | + [{"role": "user", "content": "hello"}], |
| 47 | + [{"role": "user", "content": "goodbye"}], |
| 48 | + ], |
| 49 | + "chat_template_kwargs": {"instruction": "Represent the query: "}, |
| 50 | + } |
| 51 | + ) |
| 52 | + |
| 53 | + assert isinstance(request, EmbeddingBatchChatInputRequest) |
| 54 | + assert request.input == [ |
| 55 | + [{"role": "user", "content": "hello"}], |
| 56 | + [{"role": "user", "content": "goodbye"}], |
| 57 | + ] |
| 58 | + assert request.messages == [ |
| 59 | + [{"role": "user", "content": "hello"}], |
| 60 | + [{"role": "user", "content": "goodbye"}], |
| 61 | + ] |
| 62 | + assert request.chat_template_kwargs == {"instruction": "Represent the query: "} |
| 63 | + |
| 64 | + def test_token_ids_still_parse_as_completion_request(self): |
| 65 | + request = TypeAdapter(EmbeddingRequest).validate_python( |
| 66 | + { |
| 67 | + "model": "test", |
| 68 | + "input": [[1, 2, 3], [4, 5]], |
| 69 | + } |
| 70 | + ) |
| 71 | + |
| 72 | + assert isinstance(request, EmbeddingCompletionRequest) |
| 73 | + assert request.input == [[1, 2, 3], [4, 5]] |
| 74 | + |
| 75 | + def test_messages_still_parses_as_chat_request(self): |
| 76 | + request = TypeAdapter(EmbeddingRequest).validate_python( |
| 77 | + { |
| 78 | + "model": "test", |
| 79 | + "messages": [{"role": "user", "content": "hello"}], |
| 80 | + "chat_template_kwargs": {"instruction": "Represent the query: "}, |
| 81 | + } |
| 82 | + ) |
| 83 | + |
| 84 | + assert isinstance(request, EmbeddingChatRequest) |
| 85 | + assert request.messages == [{"role": "user", "content": "hello"}] |
| 86 | + assert request.chat_template_kwargs == {"instruction": "Represent the query: "} |
| 87 | + |
| 88 | + def test_batched_messages_parses_as_batch_chat_request(self): |
| 89 | + request = TypeAdapter(EmbeddingRequest).validate_python( |
| 90 | + { |
| 91 | + "model": "test", |
| 92 | + "messages": [ |
| 93 | + [{"role": "user", "content": "hello"}], |
| 94 | + [{"role": "user", "content": "goodbye"}], |
| 95 | + ], |
| 96 | + "chat_template_kwargs": {"instruction": "Represent the query: "}, |
| 97 | + } |
| 98 | + ) |
| 99 | + |
| 100 | + assert isinstance(request, EmbeddingBatchChatRequest) |
| 101 | + assert request.messages == [ |
| 102 | + [{"role": "user", "content": "hello"}], |
| 103 | + [{"role": "user", "content": "goodbye"}], |
| 104 | + ] |
| 105 | + assert request.chat_template_kwargs == {"instruction": "Represent the query: "} |
| 106 | + |
| 107 | + |
17 | 108 | class TestResolveTruncation: |
18 | 109 | """Unit tests for EmbedIOProcessor._resolve_cohere_truncation.""" |
19 | 110 |
|
@@ -324,3 +415,113 @@ def batch_render_chat( |
324 | 415 | }, |
325 | 416 | ) |
326 | 417 | ] |
| 418 | + |
| 419 | + |
| 420 | +class TestPreProcessOpenAIEmbeddingChatOnline: |
| 421 | + """Unit tests for OpenAI embedding chat preprocessing.""" |
| 422 | + |
| 423 | + class _FakeModelConfig: |
| 424 | + max_model_len = 128 |
| 425 | + encoder_config: dict[str, object] = {} |
| 426 | + pooler_config = None |
| 427 | + multimodal_config = None |
| 428 | + is_encoder_decoder = False |
| 429 | + |
| 430 | + class _FakeRenderer: |
| 431 | + tokenizer = object() |
| 432 | + |
| 433 | + def __init__(self): |
| 434 | + self.calls = [] |
| 435 | + |
| 436 | + def render_chat( |
| 437 | + self, |
| 438 | + all_messages, |
| 439 | + chat_params, |
| 440 | + tok_params, |
| 441 | + prompt_extras=None, |
| 442 | + ): |
| 443 | + self.calls.append( |
| 444 | + { |
| 445 | + "all_messages": all_messages, |
| 446 | + "chat_params": chat_params, |
| 447 | + "tok_params": tok_params, |
| 448 | + "prompt_extras": prompt_extras, |
| 449 | + } |
| 450 | + ) |
| 451 | + return all_messages, [ |
| 452 | + {"prompt_token_ids": [index]} for index, _ in enumerate(all_messages) |
| 453 | + ] |
| 454 | + |
| 455 | + @classmethod |
| 456 | + def _make_handler(cls, renderer): |
| 457 | + handler = object.__new__(EmbedIOProcessor) |
| 458 | + handler.renderer = renderer |
| 459 | + handler.model_config = cls._FakeModelConfig() |
| 460 | + handler.chat_template = "template" |
| 461 | + handler.chat_template_content_format = "auto" |
| 462 | + handler.trust_request_chat_template = False |
| 463 | + handler.enable_chunked_processing = False |
| 464 | + return handler |
| 465 | + |
| 466 | + @staticmethod |
| 467 | + def _make_context( |
| 468 | + request: ( |
| 469 | + EmbeddingChatRequest |
| 470 | + | EmbeddingBatchChatRequest |
| 471 | + | EmbeddingChatInputRequest |
| 472 | + | EmbeddingBatchChatInputRequest |
| 473 | + ), |
| 474 | + ) -> PoolingServeContext[ |
| 475 | + EmbeddingChatRequest |
| 476 | + | EmbeddingBatchChatRequest |
| 477 | + | EmbeddingChatInputRequest |
| 478 | + | EmbeddingBatchChatInputRequest |
| 479 | + ]: |
| 480 | + return PoolingServeContext( |
| 481 | + request=request, |
| 482 | + pooling_params=PoolingParams(), |
| 483 | + model_name="test", |
| 484 | + request_id="embd-test", |
| 485 | + ) |
| 486 | + |
| 487 | + def test_chat_template_kwargs_forwarded_for_batched_input_messages(self): |
| 488 | + request = TypeAdapter(EmbeddingRequest).validate_python( |
| 489 | + { |
| 490 | + "model": "test", |
| 491 | + "input": [ |
| 492 | + [{"role": "user", "content": "hello"}], |
| 493 | + [{"role": "user", "content": "goodbye"}], |
| 494 | + ], |
| 495 | + "add_generation_prompt": True, |
| 496 | + "chat_template_kwargs": {"instruction": "Represent the query: "}, |
| 497 | + "mm_processor_kwargs": {"max_pixels": 1}, |
| 498 | + "cache_salt": "salt", |
| 499 | + } |
| 500 | + ) |
| 501 | + assert isinstance(request, EmbeddingBatchChatInputRequest) |
| 502 | + |
| 503 | + renderer = self._FakeRenderer() |
| 504 | + handler = self._make_handler(renderer) |
| 505 | + ctx = self._make_context(request) |
| 506 | + |
| 507 | + handler.pre_process_online(ctx) |
| 508 | + |
| 509 | + assert ctx.engine_inputs == [ |
| 510 | + {"prompt_token_ids": [0]}, |
| 511 | + {"prompt_token_ids": [1]}, |
| 512 | + ] |
| 513 | + assert len(renderer.calls) == 1 |
| 514 | + |
| 515 | + call = renderer.calls[0] |
| 516 | + assert call["all_messages"] == request.messages |
| 517 | + assert call["prompt_extras"] == { |
| 518 | + "mm_processor_kwargs": {"max_pixels": 1}, |
| 519 | + "cache_salt": "salt", |
| 520 | + } |
| 521 | + |
| 522 | + chat_template_kwargs = call["chat_params"].chat_template_kwargs |
| 523 | + assert chat_template_kwargs["instruction"] == "Represent the query: " |
| 524 | + assert chat_template_kwargs["add_generation_prompt"] is True |
| 525 | + assert chat_template_kwargs["continue_final_message"] is False |
| 526 | + assert "tools" not in chat_template_kwargs |
| 527 | + assert chat_template_kwargs["tokenize"] is False |
0 commit comments