main
py 189 lines 5.82 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import json
5
6 import openai
7 import pybase64 as base64
8 import pytest
9 import pytest_asyncio
10
11 from tests.conftest import VideoTestAssets
12 from tests.utils import ROCM_EXTRA_ARGS, RemoteOpenAIServer
13
14 MODEL_NAME = "Qwen/Qwen2.5-Omni-3B"
15
16
17 @pytest.fixture(scope="module")
18 def server():
19 # Use module scope so the server is started once and shared across all
20 # tests in this file. Starting a new vLLM server per test on XPU can
21 # cause the second server startup to hang silently and exceed the
22 # wait-for-server timeout, resulting in RuntimeError.
23 args = [
24 "--max-model-len",
25 "16384",
26 "--enforce-eager",
27 "--limit-mm-per-prompt",
28 json.dumps({"audio": 3, "video": 3}),
29 "--trust-request-mm-kwargs",
30 *ROCM_EXTRA_ARGS,
31 ]
32
33 with RemoteOpenAIServer(MODEL_NAME, args) as remote_server:
34 yield remote_server
35
36
37 @pytest_asyncio.fixture
38 async def client(server):
39 async with server.get_async_client() as async_client:
40 yield async_client
41
42
43 @pytest.mark.core_model
44 @pytest.mark.asyncio
45 async def test_online_audio_in_video(
46 client: openai.AsyncOpenAI, video_assets: VideoTestAssets
47 ):
48 """Test video input with `audio_in_video=True`."""
49 # we don't use video_urls above because they missed audio stream.
50 video_path = video_assets[0].video_path
51 with open(video_path, "rb") as f:
52 video_base64 = base64.b64encode(f.read()).decode("utf-8")
53
54 messages = [
55 {
56 "role": "user",
57 "content": [
58 {"type": "text", "text": "What's in this video?"},
59 {
60 "type": "video_url",
61 "video_url": {"url": f"data:video/mp4;base64,{video_base64}"},
62 },
63 ],
64 }
65 ]
66
67 # multi-turn to test mm processor cache as well
68 for turn in range(2):
69 chat_completion = await client.chat.completions.create(
70 model=MODEL_NAME,
71 messages=messages,
72 max_tokens=8,
73 temperature=0.0,
74 extra_body={
75 "mm_processor_kwargs": {
76 "use_audio_in_video": True,
77 }
78 },
79 )
80
81 assert len(chat_completion.choices) == 1
82 choice = chat_completion.choices[0]
83 print(
84 f"[DEBUG][single-video] turn={turn} "
85 f"finish_reason={choice.finish_reason!r} "
86 f"content={choice.message.content!r} "
87 f"usage={chat_completion.usage}"
88 )
89 assert choice.finish_reason == "length"
90
91
92 @pytest.mark.core_model
93 @pytest.mark.asyncio
94 async def test_online_audio_in_video_multi_videos(
95 client: openai.AsyncOpenAI, video_assets: VideoTestAssets
96 ):
97 """Test multi-video input with `audio_in_video=True`."""
98 # we don't use video_urls above because they missed audio stream.
99 video_path = video_assets[0].video_path
100 with open(video_path, "rb") as f:
101 video_base64 = base64.b64encode(f.read()).decode("utf-8")
102
103 messages = [
104 {
105 "role": "user",
106 "content": [
107 {"type": "text", "text": "What's in these two videos?"},
108 {
109 "type": "video_url",
110 "video_url": {"url": f"data:video/mp4;base64,{video_base64}"},
111 },
112 {
113 "type": "video_url",
114 "video_url": {"url": f"data:video/mp4;base64,{video_base64}"},
115 },
116 ],
117 }
118 ]
119
120 # multi-turn to test mm processor cache as well
121 for turn in range(2):
122 chat_completion = await client.chat.completions.create(
123 model=MODEL_NAME,
124 messages=messages,
125 max_tokens=8,
126 temperature=0.0,
127 extra_body={
128 "mm_processor_kwargs": {
129 "use_audio_in_video": True,
130 }
131 },
132 )
133
134 assert len(chat_completion.choices) == 1
135 choice = chat_completion.choices[0]
136 print(
137 f"[DEBUG][multi-video] turn={turn} "
138 f"finish_reason={choice.finish_reason!r} "
139 f"content={choice.message.content!r} "
140 f"usage={chat_completion.usage}"
141 )
142 assert choice.finish_reason == "length"
143
144
145 @pytest.mark.core_model
146 @pytest.mark.asyncio
147 async def test_online_audio_in_video_interleaved(
148 client: openai.AsyncOpenAI, video_assets: VideoTestAssets
149 ):
150 """Test interleaved video/audio input with `audio_in_video=True`."""
151 # we don't use video_urls above because they missed audio stream.
152 video_path = video_assets[0].video_path
153 with open(video_path, "rb") as f:
154 video_base64 = base64.b64encode(f.read()).decode("utf-8")
155
156 messages = [
157 {
158 "role": "user",
159 "content": [
160 {"type": "text", "text": "What's in these two videos?"},
161 {
162 "type": "video_url",
163 "video_url": {"url": f"data:video/mp4;base64,{video_base64}"},
164 },
165 {
166 "type": "audio_url",
167 "audio_url": {"url": f"data:audio/mp4;base64,{video_base64}"},
168 },
169 {
170 "type": "video_url",
171 "video_url": {"url": f"data:video/mp4;base64,{video_base64}"},
172 },
173 ],
174 }
175 ]
176 with pytest.raises(
177 openai.BadRequestError,
178 match="use_audio_in_video requires equal number of audio and video items",
179 ):
180 await client.chat.completions.create(
181 model=MODEL_NAME,
182 messages=messages,
183 max_tokens=16,
184 extra_body={
185 "mm_processor_kwargs": {
186 "use_audio_in_video": True,
187 }
188 },
189 )