main
py 222 lines 6.56 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """This example shows how to use vLLM for running offline inference
4 with the correct prompt format on Qwen3-Omni (thinker only).
5 """
6
7 from typing import NamedTuple
8
9 from vllm import LLM, SamplingParams
10 from vllm.assets.audio import AudioAsset
11 from vllm.assets.image import ImageAsset
12 from vllm.assets.video import VideoAsset
13 from vllm.multimodal.image import convert_image_mode
14 from vllm.utils.argparse_utils import FlexibleArgumentParser
15
16
17 class QueryResult(NamedTuple):
18 inputs: dict
19 limit_mm_per_prompt: dict[str, int]
20
21
22 # NOTE: The default `max_num_seqs` and `max_model_len` may result in OOM on
23 # lower-end GPUs.
24 # Unless specified, these settings have been tested to work on a single L4.
25
26 default_system = (
27 "You are Qwen, a virtual human developed by the Qwen Team, Alibaba "
28 "Group, capable of perceiving auditory and visual inputs, as well as "
29 "generating text and speech."
30 )
31
32
33 def get_mixed_modalities_query() -> QueryResult:
34 question = (
35 "What is recited in the audio? "
36 "What is the content of this image? Why is this video funny?"
37 )
38 prompt = (
39 f"<|im_start|>system\n{default_system}<|im_end|>\n"
40 "<|im_start|>user\n<|audio_start|><|audio_pad|><|audio_end|>"
41 "<|vision_start|><|image_pad|><|vision_end|>"
42 "<|vision_start|><|video_pad|><|vision_end|>"
43 f"{question}<|im_end|>\n"
44 f"<|im_start|>assistant\n"
45 )
46 return QueryResult(
47 inputs={
48 "prompt": prompt,
49 "multi_modal_data": {
50 "audio": AudioAsset("mary_had_lamb").audio_and_sample_rate,
51 "image": convert_image_mode(
52 ImageAsset("cherry_blossom").pil_image, "RGB"
53 ),
54 "video": VideoAsset(name="baby_reading", num_frames=16).np_ndarrays,
55 },
56 },
57 limit_mm_per_prompt={"audio": 1, "image": 1, "video": 1},
58 )
59
60
61 def get_use_audio_in_video_query() -> QueryResult:
62 question = (
63 "Describe the content of the video in details, then convert what the "
64 "baby say into text."
65 )
66 prompt = (
67 f"<|im_start|>system\n{default_system}<|im_end|>\n"
68 "<|im_start|>user\n<|vision_start|><|video_pad|><|vision_end|>"
69 f"{question}<|im_end|>\n"
70 f"<|im_start|>assistant\n"
71 )
72 asset = VideoAsset(name="baby_reading", num_frames=16)
73 audio = asset.get_audio(sampling_rate=16000)
74 return QueryResult(
75 inputs={
76 "prompt": prompt,
77 "multi_modal_data": {
78 "video": asset.np_ndarrays,
79 "audio": audio,
80 },
81 "mm_processor_kwargs": {
82 "use_audio_in_video": True,
83 },
84 },
85 limit_mm_per_prompt={"audio": 1, "video": 1},
86 )
87
88
89 def get_multi_audios_query() -> QueryResult:
90 question = "Are these two audio clips the same?"
91 prompt = (
92 f"<|im_start|>system\n{default_system}<|im_end|>\n"
93 "<|im_start|>user\n<|audio_start|><|audio_pad|><|audio_end|>"
94 "<|audio_start|><|audio_pad|><|audio_end|>"
95 f"{question}<|im_end|>\n"
96 f"<|im_start|>assistant\n"
97 )
98 return QueryResult(
99 inputs={
100 "prompt": prompt,
101 "multi_modal_data": {
102 "audio": [
103 AudioAsset("winning_call").audio_and_sample_rate,
104 AudioAsset("mary_had_lamb").audio_and_sample_rate,
105 ],
106 },
107 },
108 limit_mm_per_prompt={
109 "audio": 2,
110 },
111 )
112
113
114 def get_multi_images_query() -> QueryResult:
115 question = "What are the differences between these two images?"
116 prompt = (
117 f"<|im_start|>system\n{default_system}<|im_end|>\n"
118 "<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>"
119 "<|vision_start|><|image_pad|><|vision_end|>"
120 f"{question}<|im_end|>\n"
121 f"<|im_start|>assistant\n"
122 )
123 return QueryResult(
124 inputs={
125 "prompt": prompt,
126 "multi_modal_data": {
127 "image": [
128 convert_image_mode(ImageAsset("cherry_blossom").pil_image, "RGB"),
129 convert_image_mode(ImageAsset("stop_sign").pil_image, "RGB"),
130 ],
131 },
132 },
133 limit_mm_per_prompt={
134 "image": 2,
135 },
136 )
137
138
139 query_map = {
140 "mixed_modalities": get_mixed_modalities_query,
141 "use_audio_in_video": get_use_audio_in_video_query,
142 "multi_audios": get_multi_audios_query,
143 "multi_images": get_multi_images_query,
144 }
145
146
147 def main(args):
148 model_name = args.model
149 query_result = query_map[args.query_type]()
150
151 llm = LLM(
152 model=model_name,
153 max_model_len=args.max_model_len,
154 max_num_seqs=5,
155 limit_mm_per_prompt=query_result.limit_mm_per_prompt,
156 seed=args.seed,
157 tensor_parallel_size=args.tensor_parallel_size,
158 gpu_memory_utilization=args.gpu_memory_utilization,
159 )
160
161 # We set temperature to 0.2 so that outputs can be different
162 # even when all prompts are identical when running batch inference.
163 sampling_params = SamplingParams(temperature=0.2, max_tokens=256)
164
165 outputs = llm.generate(query_result.inputs, sampling_params=sampling_params)
166
167 for o in outputs:
168 generated_text = o.outputs[0].text
169 print(generated_text)
170
171
172 def parse_args():
173 parser = FlexibleArgumentParser(
174 description="Demo on using vLLM for offline inference with "
175 "audio language models"
176 )
177 parser.add_argument(
178 "--query-type",
179 "-q",
180 type=str,
181 default="mixed_modalities",
182 choices=query_map.keys(),
183 help="Query type.",
184 )
185 parser.add_argument(
186 "--seed",
187 type=int,
188 default=0,
189 help="Set the seed when initializing `vllm.LLM`.",
190 )
191 parser.add_argument(
192 "--model",
193 type=str,
194 default="Qwen/Qwen3-Omni-30B-A3B-Instruct",
195 help="Model name or path.",
196 )
197 parser.add_argument(
198 "--tensor-parallel-size",
199 "-tp",
200 type=int,
201 default=1,
202 help="Tensor parallel size for distributed inference.",
203 )
204 parser.add_argument(
205 "--gpu-memory-utilization",
206 type=float,
207 default=0.9,
208 help="GPU memory utilization (0.0 to 1.0).",
209 )
210 parser.add_argument(
211 "--max-model-len",
212 type=int,
213 default=12800,
214 help="Maximum model context length.",
215 )
216
217 return parser.parse_args()
218
219
220 if __name__ == "__main__":
221 args = parse_args()
222 main(args)