|
2 | 2 | # SPDX-FileCopyrightText: Copyright contributors to the vLLM project |
3 | 3 |
|
4 | 4 | from contextlib import nullcontext |
5 | | -from types import MethodType |
6 | | -from typing import cast |
| 5 | +from typing import Optional, cast |
7 | 6 | from unittest.mock import MagicMock |
8 | 7 |
|
9 | 8 | import numpy as np |
10 | 9 | import pytest |
11 | 10 | import torch |
12 | | -from transformers import ProcessorMixin |
13 | 11 |
|
14 | 12 | from vllm.config import ModelConfig |
| 13 | +from vllm.inputs import InputProcessingContext |
15 | 14 | from vllm.multimodal import MULTIMODAL_REGISTRY |
16 | 15 | from vllm.multimodal.inputs import (MultiModalFieldElem, MultiModalKwargs, |
17 | 16 | MultiModalKwargsItem, |
@@ -1013,57 +1012,91 @@ def test_limit_mm_per_prompt_apply(model_id, num_images, limit, is_valid): |
1013 | 1012 | ) |
1014 | 1013 |
|
1015 | 1014 |
|
1016 | | -class _ProcessorProxy: |
| 1015 | +class DummyProcessor: |
1017 | 1016 |
|
1018 | | - def __init__(self, processor: ProcessorMixin) -> None: |
| 1017 | + def __init__(self, a: int = 0, b: int = 0) -> None: |
1019 | 1018 | super().__init__() |
1020 | 1019 |
|
1021 | | - self.__processor = processor |
1022 | | - |
1023 | | - def __getattr__(self, key: str): |
1024 | | - return getattr(self.__processor, key) |
| 1020 | + self.a = a |
| 1021 | + self.b = b |
1025 | 1022 |
|
1026 | 1023 | def __call__( |
1027 | 1024 | self, |
1028 | | - text=None, |
1029 | | - images=None, |
1030 | | - videos=None, |
1031 | | - exists=None, |
1032 | | - return_tensors=None, |
1033 | | - ): |
1034 | | - return dict(exists=exists) |
| 1025 | + a: int = 0, |
| 1026 | + c: int = 0, |
| 1027 | + return_tensors: Optional[str] = None, |
| 1028 | + ) -> dict[str, int]: |
| 1029 | + return dict(a=a, c=c) |
1035 | 1030 |
|
1036 | 1031 |
|
1037 | | -@pytest.mark.parametrize("model_id", ["Qwen/Qwen2-VL-2B-Instruct"]) # Dummy |
1038 | 1032 | # yapf: disable |
| 1033 | +@pytest.mark.parametrize("model_id", ["Qwen/Qwen2-VL-2B-Instruct"]) # Dummy |
1039 | 1034 | @pytest.mark.parametrize( |
1040 | | - ("call_kwargs", "expected_kwargs"), |
| 1035 | + ("config_kwargs", "inference_kwargs", "expected_kwargs"), |
1041 | 1036 | [ |
1042 | | - # Should ignore invalid kwargs |
1043 | | - ({"does_not_exist": 100}, {"exists": None}), |
1044 | | - ({"exists": 1}, {"exists": 1}), |
1045 | | - ({"does_not_exist": 100, "exists": 1}, {"exists": 1}), |
| 1037 | + ({"a": 1}, {}, {"a": 1, "b": 0}), |
| 1038 | + ({}, {"a": 1}, {"a": 1, "b": 0}), |
| 1039 | + # inference_kwargs should take precedence |
| 1040 | + ({"a": 1}, {"a": 2}, {"a": 2, "b": 0}), |
| 1041 | + # Should ignore extra kwargs |
| 1042 | + ({"a": 1, "c": 1}, {}, {"a": 1, "b": 0}), |
| 1043 | + ({"b": 1, "c": 1}, {}, {"a": 0, "b": 1}), |
1046 | 1044 | ], |
1047 | 1045 | ) |
1048 | 1046 | # yapf: enable |
1049 | | -def test_hf_processor_kwargs(model_id, call_kwargs, expected_kwargs): |
1050 | | - model_config = ModelConfig(model_id) |
| 1047 | +def test_hf_processor_init_kwargs( |
| 1048 | + model_id, |
| 1049 | + config_kwargs, |
| 1050 | + inference_kwargs, |
| 1051 | + expected_kwargs, |
| 1052 | +): |
| 1053 | + # Should not be used since there is nothing to convert to tokens |
| 1054 | + mock_tokenizer = cast(AnyTokenizer, object()) |
1051 | 1055 |
|
1052 | | - processor = MULTIMODAL_REGISTRY.create_processor(model_config) |
1053 | | - orig_get_hf_processor = processor.info.get_hf_processor |
| 1056 | + ctx = InputProcessingContext( |
| 1057 | + model_config=ModelConfig(model_id, mm_processor_kwargs=config_kwargs), |
| 1058 | + tokenizer=mock_tokenizer, |
| 1059 | + ) |
| 1060 | + |
| 1061 | + processor = ctx.get_hf_processor( |
| 1062 | + DummyProcessor, # type: ignore[arg-type] |
| 1063 | + **inference_kwargs, |
| 1064 | + ) |
| 1065 | + |
| 1066 | + for k, v in expected_kwargs.items(): |
| 1067 | + assert getattr(processor, k) == v |
1054 | 1068 |
|
1055 | | - def get_hf_processor(self, **kwargs): |
1056 | | - assert kwargs == call_kwargs |
1057 | | - return _ProcessorProxy(orig_get_hf_processor()) |
1058 | 1069 |
|
1059 | | - processor.info.get_hf_processor = MethodType(get_hf_processor, |
1060 | | - processor.info) |
| 1070 | +# yapf: disable |
| 1071 | +@pytest.mark.parametrize("model_id", ["Qwen/Qwen2-VL-2B-Instruct"]) # Dummy |
| 1072 | +@pytest.mark.parametrize( |
| 1073 | + ("config_kwargs", "inference_kwargs", "expected_kwargs"), |
| 1074 | + [ |
| 1075 | + ({"a": 1}, {}, {"a": 1, "c": 0}), |
| 1076 | + ({}, {"a": 1}, {"a": 1, "c": 0}), |
| 1077 | + # inference_kwargs should take precedence |
| 1078 | + ({"a": 1}, {"a": 2}, {"a": 2, "c": 0}), |
| 1079 | + # Should ignore extra kwargs |
| 1080 | + ({"a": 1, "c": 1}, {}, {"a": 1, "c": 1}), |
| 1081 | + ({"b": 1, "c": 1}, {}, {"a": 0, "c": 1}), |
| 1082 | + ], |
| 1083 | +) |
| 1084 | +# yapf: enable |
| 1085 | +def test_hf_processor_call_kwargs( |
| 1086 | + model_id, |
| 1087 | + config_kwargs, |
| 1088 | + inference_kwargs, |
| 1089 | + expected_kwargs, |
| 1090 | +): |
| 1091 | + # Should not be used since there is nothing to convert to tokens |
| 1092 | + mock_tokenizer = cast(AnyTokenizer, object()) |
1061 | 1093 |
|
1062 | | - out_kwargs = processor._call_hf_processor( |
1063 | | - prompt="", |
1064 | | - mm_data={}, |
1065 | | - mm_kwargs=call_kwargs, |
1066 | | - tok_kwargs={}, |
| 1094 | + ctx = InputProcessingContext( |
| 1095 | + model_config=ModelConfig(model_id, mm_processor_kwargs=config_kwargs), |
| 1096 | + tokenizer=mock_tokenizer, |
1067 | 1097 | ) |
1068 | 1098 |
|
1069 | | - assert out_kwargs == expected_kwargs |
| 1099 | + processor = ctx.get_hf_processor(DummyProcessor) # type: ignore[arg-type] |
| 1100 | + |
| 1101 | + result = ctx.call_hf_processor(processor, {}, inference_kwargs) |
| 1102 | + assert result == expected_kwargs |
0 commit comments