main
py 156 lines 5.15 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from abc import ABC, abstractmethod
4 from collections.abc import Mapping
5 from typing import Generic, TypeVar
6
7 import numpy as np
8 import numpy.typing as npt
9 from PIL import Image
10
11 from vllm.config.multimodal import (
12 AudioDummyOptions,
13 ImageDummyOptions,
14 MultiModalDummyOptions,
15 VideoDummyOptions,
16 )
17 from vllm.inputs import MultiModalDataDict
18 from vllm.logger import init_logger
19
20 from .context import BaseProcessingInfo
21
22 _I = TypeVar("_I", bound=BaseProcessingInfo)
23
24 logger = init_logger(__name__)
25
26
27 class BaseDummyInputsBuilder(ABC, Generic[_I]):
28 """Abstract base class that constructs the dummy data to profile
29 multi-modal models.
30 """
31
32 def __init__(self, info: _I) -> None:
33 super().__init__()
34
35 self.info = info
36
37 @abstractmethod
38 def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
39 """Build the text input corresponding to `mm_counts`."""
40 raise NotImplementedError
41
42 @abstractmethod
43 def get_dummy_mm_data(
44 self,
45 seq_len: int,
46 mm_counts: Mapping[str, int],
47 mm_options: MultiModalDummyOptions,
48 ) -> MultiModalDataDict:
49 """Build the multimodal input which, after processing, results in
50 the maximum possible number of placeholder tokens.
51
52 Args:
53 seq_len: Sequence length
54 mm_counts: Count of items per modality
55 mm_options: Configurable options per modality (optional).
56 If None, use model defaults for backward compatibility.
57 If provided, models can use these to customize dummy
58 data generation.
59
60 """
61 raise NotImplementedError
62
63 def _get_dummy_audios(
64 self,
65 *,
66 length: int,
67 num_audios: int,
68 overrides: AudioDummyOptions | None = None,
69 ) -> list[npt.NDArray]:
70 if num_audios == 0:
71 return []
72 if overrides and overrides.length:
73 if overrides.length > length:
74 logger.warning(
75 "audio.length override (%d) exceeds model's "
76 "maximum length (%d), will be ignored",
77 overrides.length,
78 length,
79 )
80 length = min(length, overrides.length)
81 audio = np.zeros((length,))
82 return [audio] * num_audios
83
84 def _get_dummy_images(
85 self,
86 *,
87 width: int,
88 height: int,
89 num_images: int,
90 overrides: ImageDummyOptions | None = None,
91 ) -> list[Image.Image]:
92 if num_images == 0:
93 return []
94 if overrides:
95 if overrides.width:
96 if overrides.width > width:
97 logger.warning(
98 "image.width override (%d) exceeds model's "
99 "maximum width (%d), will be ignored",
100 overrides.width,
101 width,
102 )
103 width = min(width, overrides.width)
104 if overrides.height:
105 if overrides.height > height:
106 logger.warning(
107 "image.height override (%d) exceeds model's "
108 "maximum height (%d), will be ignored",
109 overrides.height,
110 height,
111 )
112 height = min(height, overrides.height)
113 image = Image.new("RGB", (width, height), color=255)
114 return [image] * num_images
115
116 def _get_dummy_videos(
117 self,
118 *,
119 width: int,
120 height: int,
121 num_frames: int,
122 num_videos: int,
123 overrides: VideoDummyOptions | None = None,
124 ) -> list[npt.NDArray]:
125 if num_videos == 0:
126 return []
127 if overrides:
128 if overrides.num_frames:
129 if overrides.num_frames > num_frames:
130 logger.warning(
131 "video.num_frames override (%d) exceeds model's "
132 "maximum number of frames (%d), will be ignored",
133 overrides.num_frames,
134 num_frames,
135 )
136 num_frames = min(num_frames, overrides.num_frames)
137 if overrides.width:
138 if overrides.width > width:
139 logger.warning(
140 "video.width override (%d) exceeds model's "
141 "maximum width (%d), will be ignored",
142 overrides.width,
143 width,
144 )
145 width = min(width, overrides.width)
146 if overrides.height:
147 if overrides.height > height:
148 logger.warning(
149 "video.height override (%d) exceeds model's "
150 "maximum height (%d), will be ignored",
151 overrides.height,
152 height,
153 )
154 height = min(height, overrides.height)
155 video = np.full((num_frames, height, width, 3), 255, dtype=np.uint8)
156 return [video] * num_videos