-
-
Notifications
You must be signed in to change notification settings - Fork 19.6k
[Multimodal][Qwen3 Omni] Make Qwen3 Omni work with audio-in-video inputs in V1 engine. #27721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
382689b
hacky start to get qwen3 omni audio-in-video to work
huachenheli 9e6f9f0
cleanup
huachenheli 30fb5b5
Merge branch 'main' into qwen3audioinvideo
ywang96 f1192b4
Merge branch 'main' into qwen3audioinvideo
ywang96 3f6164d
fix default second_grid_ts
ywang96 6f2445a
Merge branch 'main' into qwen3audioinvideo
ywang96 deb5303
Merge branch 'main' into qwen3audioinvideo
huachenheli e6c033b
capture ray import AttributeError
huachenheli 8458c78
Merge branch 'fixray' into pr-27721
huachenheli 49d637b
Add unit test for qwen3 audio in video.
huachenheli e56f7dd
fix token ids
huachenheli 371a053
fix precommit
huachenheli 3905b89
ruff
huachenheli 79dbb68
Merge branch 'main' into qwen3audioinvideo
huachenheli d42d07e
Merge branch 'main' into qwen3audioinvideo
ywang96 c24fedf
Merge branch 'main' into qwen3audioinvideo
huachenheli dd4296d
Merge branch 'main' into qwen3audioinvideo
huachenheli 394de84
Merge branch 'main' into qwen3audioinvideo
huachenheli 2e69970
Merge branch 'main' into qwen3audioinvideo
ywang96 6941263
Merge branch 'main' into qwen3audioinvideo
huachenheli File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # SPDX-FileCopyrightText: Copyright contributors to the vLLM project | ||
| """ | ||
| This example shows how to use vLLM for running offline inference | ||
| with the correct prompt format on Qwen2.5-Omni (thinker only). | ||
| """ | ||
|
|
||
| from typing import NamedTuple | ||
|
|
||
| from vllm import LLM, SamplingParams | ||
| from vllm.assets.audio import AudioAsset | ||
| from vllm.assets.image import ImageAsset | ||
| from vllm.assets.video import VideoAsset | ||
| from vllm.multimodal.image import convert_image_mode | ||
| from vllm.utils.argparse_utils import FlexibleArgumentParser | ||
|
|
||
|
|
||
| class QueryResult(NamedTuple): | ||
| inputs: dict | ||
| limit_mm_per_prompt: dict[str, int] | ||
|
|
||
|
|
||
| # NOTE: The default `max_num_seqs` and `max_model_len` may result in OOM on | ||
| # lower-end GPUs. | ||
| # Unless specified, these settings have been tested to work on a single L4. | ||
|
|
||
| default_system = ( | ||
| "You are Qwen, a virtual human developed by the Qwen Team, Alibaba " | ||
| "Group, capable of perceiving auditory and visual inputs, as well as " | ||
| "generating text and speech." | ||
| ) | ||
|
|
||
|
|
||
| def get_mixed_modalities_query() -> QueryResult: | ||
| question = ( | ||
| "What is recited in the audio? " | ||
| "What is the content of this image? Why is this video funny?" | ||
| ) | ||
| prompt = ( | ||
| f"<|im_start|>system\n{default_system}<|im_end|>\n" | ||
| "<|im_start|>user\n<|audio_start|><|audio_pad|><|audio_end|>" | ||
| "<|vision_start|><|image_pad|><|vision_end|>" | ||
| "<|vision_start|><|video_pad|><|vision_end|>" | ||
| f"{question}<|im_end|>\n" | ||
| f"<|im_start|>assistant\n" | ||
| ) | ||
| return QueryResult( | ||
| inputs={ | ||
| "prompt": prompt, | ||
| "multi_modal_data": { | ||
| "audio": AudioAsset("mary_had_lamb").audio_and_sample_rate, | ||
| "image": convert_image_mode( | ||
| ImageAsset("cherry_blossom").pil_image, "RGB" | ||
| ), | ||
| "video": VideoAsset(name="baby_reading", num_frames=16).np_ndarrays, | ||
| }, | ||
| }, | ||
| limit_mm_per_prompt={"audio": 1, "image": 1, "video": 1}, | ||
| ) | ||
|
|
||
|
|
||
| def get_use_audio_in_video_query() -> QueryResult: | ||
| question = ( | ||
| "Describe the content of the video in details, then convert what the " | ||
| "baby say into text." | ||
| ) | ||
| prompt = ( | ||
| f"<|im_start|>system\n{default_system}<|im_end|>\n" | ||
| "<|im_start|>user\n<|vision_start|><|video_pad|><|vision_end|>" | ||
| f"{question}<|im_end|>\n" | ||
| f"<|im_start|>assistant\n" | ||
| ) | ||
| asset = VideoAsset(name="baby_reading", num_frames=16) | ||
| audio = asset.get_audio(sampling_rate=16000) | ||
| return QueryResult( | ||
| inputs={ | ||
| "prompt": prompt, | ||
| "multi_modal_data": { | ||
| "video": asset.np_ndarrays, | ||
| "audio": audio, | ||
| }, | ||
| "mm_processor_kwargs": { | ||
| "use_audio_in_video": True, | ||
| }, | ||
| }, | ||
| limit_mm_per_prompt={"audio": 1, "video": 1}, | ||
| ) | ||
|
|
||
|
|
||
| def get_multi_audios_query() -> QueryResult: | ||
| question = "Are these two audio clips the same?" | ||
| prompt = ( | ||
| f"<|im_start|>system\n{default_system}<|im_end|>\n" | ||
| "<|im_start|>user\n<|audio_start|><|audio_pad|><|audio_end|>" | ||
| "<|audio_start|><|audio_pad|><|audio_end|>" | ||
| f"{question}<|im_end|>\n" | ||
| f"<|im_start|>assistant\n" | ||
| ) | ||
| return QueryResult( | ||
| inputs={ | ||
| "prompt": prompt, | ||
| "multi_modal_data": { | ||
| "audio": [ | ||
| AudioAsset("winning_call").audio_and_sample_rate, | ||
| AudioAsset("mary_had_lamb").audio_and_sample_rate, | ||
| ], | ||
| }, | ||
| }, | ||
| limit_mm_per_prompt={ | ||
| "audio": 2, | ||
| }, | ||
| ) | ||
|
|
||
|
|
||
| query_map = { | ||
| "mixed_modalities": get_mixed_modalities_query, | ||
| "use_audio_in_video": get_use_audio_in_video_query, | ||
| "multi_audios": get_multi_audios_query, | ||
| } | ||
|
|
||
|
|
||
| def main(args): | ||
| model_name = "Qwen/Qwen3-Omni-30B-A3B-Instruct" | ||
| query_result = query_map[args.query_type]() | ||
|
|
||
| llm = LLM( | ||
| model=model_name, | ||
| max_model_len=12800, | ||
| max_num_seqs=5, | ||
| limit_mm_per_prompt=query_result.limit_mm_per_prompt, | ||
| seed=args.seed, | ||
| ) | ||
|
|
||
| # We set temperature to 0.2 so that outputs can be different | ||
| # even when all prompts are identical when running batch inference. | ||
| sampling_params = SamplingParams(temperature=0.2, max_tokens=256) | ||
|
|
||
| outputs = llm.generate(query_result.inputs, sampling_params=sampling_params) | ||
|
|
||
| for o in outputs: | ||
| generated_text = o.outputs[0].text | ||
| print(generated_text) | ||
|
|
||
|
|
||
| def parse_args(): | ||
| parser = FlexibleArgumentParser( | ||
| description="Demo on using vLLM for offline inference with " | ||
| "audio language models" | ||
| ) | ||
| parser.add_argument( | ||
| "--query-type", | ||
| "-q", | ||
| type=str, | ||
| default="mixed_modalities", | ||
| choices=query_map.keys(), | ||
| help="Query type.", | ||
| ) | ||
| parser.add_argument( | ||
| "--seed", | ||
| type=int, | ||
| default=None, | ||
| help="Set the seed when initializing `vllm.LLM`.", | ||
| ) | ||
|
|
||
| return parser.parse_args() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| args = parse_args() | ||
| main(args) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice, that can be very useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hope you folks can see this: when I try https://github.com/QwenLM/Qwen3-Omni/blob/main/web_demo.py with this fix, and use vllm as backend, and put system prompt as "what does this man say?"
python web_demo.py -c ../Qwen3-Omni-30B-A3B-Instruct --server-name localhostI still cannot get correct result.
However, if I switch to transformers as backend:
python web_demo.py -c ../Qwen3-Omni-30B-A3B-Instruct --server-name localhost --use-transformers --flash-attn2I get reasonable output.
So it seems there is still more work to do.