[MR V2] Migrate to Model Runner V2
Squashed migration rebased onto current main. Collapses the 46-commit dev/migrate-MR-v2 history (10 merge commits + superseded pre-rebase work) into a single commit, so the diff reflects only the genuine MR-V2 feature (worker_v2/ runners + model_states, omni schedulers, stage engine pipeline, pooling_output serialization bridge). Content already merged to main separately (step_audio2, tts_adapters, npu/_310p, etc.) drops out. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
cead615288
commit
9678ae58cf
74 files changed
+8244
-95
No files matched your search
@@ -0,0 +1,172 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from vllm_omni.model_executor.models.qwen2_5_omni.qwen2_5_omni import (
|
||||
Qwen2_5OmniForConditionalGeneration,
|
||||
)
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.core_model,
|
||||
pytest.mark.cpu,
|
||||
pytest.mark.skip(
|
||||
reason="Methods _build_thinker_to_talker_latent and _build_talker_decode_reply_cache not yet implemented in Qwen2_5OmniForConditionalGeneration"
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def test_build_thinker_to_talker_latent_adds_token_embeddings():
|
||||
model = object.__new__(Qwen2_5OmniForConditionalGeneration)
|
||||
hidden = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
|
||||
embeds = torch.tensor([[10.0, 20.0], [30.0, 40.0]])
|
||||
|
||||
latent = model._build_thinker_to_talker_latent(hidden, embeds, input_ids=None)
|
||||
|
||||
assert torch.equal(latent, hidden + embeds)
|
||||
|
||||
|
||||
def test_build_thinker_to_talker_latent_masks_multimodal_token_embeddings():
|
||||
model = object.__new__(Qwen2_5OmniForConditionalGeneration)
|
||||
nn.Module.__init__(model)
|
||||
model.thinker_config = SimpleNamespace(
|
||||
audio_token_index=7,
|
||||
image_token_index=8,
|
||||
video_token_index=9,
|
||||
)
|
||||
|
||||
class _FakeThinker:
|
||||
@staticmethod
|
||||
def embed_input_ids(input_ids: torch.Tensor) -> torch.Tensor:
|
||||
values = input_ids.to(torch.float32)
|
||||
return torch.stack([values, values + 100.0], dim=-1)
|
||||
|
||||
model.thinker = _FakeThinker()
|
||||
hidden = torch.zeros((4, 2), dtype=torch.float32)
|
||||
input_ids = torch.tensor([1, 7, 2, 8], dtype=torch.long)
|
||||
multimodal_embeds = torch.full((4, 2), 999.0, dtype=torch.float32)
|
||||
|
||||
latent = model._build_thinker_to_talker_latent(hidden, multimodal_embeds, input_ids)
|
||||
|
||||
assert torch.equal(
|
||||
latent,
|
||||
torch.tensor(
|
||||
[
|
||||
[1.0, 101.0],
|
||||
[0.0, 0.0],
|
||||
[2.0, 102.0],
|
||||
[0.0, 0.0],
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_build_talker_decode_reply_cache_appends_text_eos_pad():
|
||||
model = object.__new__(Qwen2_5OmniForConditionalGeneration)
|
||||
model.embed_text_eos_token = torch.tensor([[100.0, 101.0]])
|
||||
model.embed_text_pad_token = torch.tensor([[200.0, 201.0]])
|
||||
thinker_result = torch.tensor([[1.0, 1.0], [2.0, 2.0], [3.0, 3.0]])
|
||||
|
||||
reply = model._build_talker_decode_reply_cache(
|
||||
thinker_result,
|
||||
dtype=torch.float32,
|
||||
device=torch.device("cpu"),
|
||||
)
|
||||
|
||||
assert torch.equal(
|
||||
reply,
|
||||
torch.tensor(
|
||||
[
|
||||
[2.0, 2.0],
|
||||
[3.0, 3.0],
|
||||
[100.0, 101.0],
|
||||
[200.0, 201.0],
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_talker_decode_keeps_final_reply_pad_vector():
|
||||
model = object.__new__(Qwen2_5OmniForConditionalGeneration)
|
||||
nn.Module.__init__(model)
|
||||
model.model = nn.Linear(2, 2)
|
||||
|
||||
class _FakeTalker:
|
||||
@staticmethod
|
||||
def embed_input_ids(input_ids: torch.Tensor) -> torch.Tensor:
|
||||
return torch.zeros((input_ids.numel(), 2), dtype=torch.float32)
|
||||
|
||||
model.talker = _FakeTalker()
|
||||
input_ids = torch.tensor([123], dtype=torch.long)
|
||||
input_embeds = torch.zeros((1, 2), dtype=torch.float32)
|
||||
final_pad = torch.tensor([[5.0, 6.0]], dtype=torch.float32)
|
||||
|
||||
_, out_embeds, update = model.thinker_to_talker_decode_one_step(
|
||||
input_ids,
|
||||
input_embeds,
|
||||
{"embed": {"thinker_reply": final_pad}},
|
||||
)
|
||||
|
||||
torch.testing.assert_close(out_embeds, final_pad)
|
||||
assert update == {}
|
||||
|
||||
|
||||
class _FakeToken2Wav(nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.weight = nn.Parameter(torch.zeros(()))
|
||||
self.calls = []
|
||||
self.token2wav = SimpleNamespace(
|
||||
factor=SimpleNamespace(factor=2),
|
||||
code2wav_dit_model=SimpleNamespace(mel_dim=3),
|
||||
)
|
||||
|
||||
def forward(
|
||||
self,
|
||||
code: torch.Tensor,
|
||||
*,
|
||||
conditioning: torch.Tensor,
|
||||
reference_mel: torch.Tensor,
|
||||
num_steps: int = 10,
|
||||
**_: object,
|
||||
) -> torch.Tensor:
|
||||
self.calls.append(
|
||||
{
|
||||
"code": code.clone(),
|
||||
"conditioning": conditioning.clone(),
|
||||
"reference_mel": reference_mel.clone(),
|
||||
"num_steps": num_steps,
|
||||
}
|
||||
)
|
||||
return torch.arange(6, dtype=torch.float32)
|
||||
|
||||
def process_chunk(self, **_: object) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
raise AssertionError("_codec_to_audio should not truncate codec through process_chunk")
|
||||
|
||||
|
||||
def test_codec_to_audio_uses_full_token2wav_forward():
|
||||
model = object.__new__(Qwen2_5OmniForConditionalGeneration)
|
||||
nn.Module.__init__(model)
|
||||
token2wav = _FakeToken2Wav()
|
||||
cond = torch.ones((1, 4), dtype=torch.float32)
|
||||
ref_mel = torch.full((1, 300, 3), 2.0, dtype=torch.float32)
|
||||
model.token2wav = token2wav
|
||||
model.token2wav_config = SimpleNamespace(
|
||||
dit_config=SimpleNamespace(enc_emb_dim=4, mel_dim=3),
|
||||
)
|
||||
model._token2wav_conds = {"Chelsie": cond}
|
||||
model._token2wav_ref_mels = {"Chelsie": ref_mel}
|
||||
|
||||
out = model._codec_to_audio(torch.arange(80, dtype=torch.long), "Chelsie")
|
||||
|
||||
assert len(token2wav.calls) == 1
|
||||
call = token2wav.calls[0]
|
||||
torch.testing.assert_close(call["code"], torch.arange(80, dtype=torch.long).unsqueeze(0))
|
||||
torch.testing.assert_close(call["conditioning"], cond)
|
||||
torch.testing.assert_close(call["reference_mel"], ref_mel)
|
||||
assert call["num_steps"] == 10
|
||||
torch.testing.assert_close(out, torch.arange(6, dtype=torch.float32))
|
||||
@@ -0,0 +1,30 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from vllm_omni.model_executor.models.qwen3_omni.qwen3_omni import (
|
||||
Qwen3OmniMoeForConditionalGeneration,
|
||||
)
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
def test_non_thinker_mrope_positions_do_not_require_multimodal_kwargs():
|
||||
model = object.__new__(Qwen3OmniMoeForConditionalGeneration)
|
||||
model.model_stage = "talker"
|
||||
|
||||
positions, delta = model.get_mrope_input_positions([1, 2, 3])
|
||||
|
||||
assert delta == 0
|
||||
assert torch.equal(
|
||||
positions,
|
||||
torch.tensor(
|
||||
[
|
||||
[0, 1, 2],
|
||||
[0, 1, 2],
|
||||
[0, 1, 2],
|
||||
]
|
||||
),
|
||||
)
|
||||
Reference in new issue
Block a user