Files
vllm-omni/tests/model_executor/models/test_minimax_music3_prompt.py
T
2026-08-18 04:33:13 +00:00

204 lines
7.4 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Unit tests for MiniMax Music 3 prompt assembly and window arithmetic.
The prompt transformations are not cosmetic: they decide token positions, and
token positions decide the audio. These tests pin the documented behaviours
that a well-meaning cleanup would otherwise "fix".
"""
from __future__ import annotations
import pytest
from vllm_omni.model_executor.models.minimax_music3.chunking import (
chunk_windows,
crop_sample_bounds,
overlap_mel_length,
)
from vllm_omni.model_executor.models.minimax_music3.constants import (
AR_CHUNK_FRAMES,
AR_CHUNK_HOP_FRAMES,
BLEND_MEL_FRAMES,
DAV_HOP_SAMPLES,
HOP_MEL_FRAMES,
)
from vllm_omni.model_executor.models.minimax_music3.prompt import (
SPECIAL_TOKEN_IDS,
build_cfg_null_token_ids,
build_prompt,
clean_caption,
normalize_lyrics,
)
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
class TestNormalizeLyrics:
def test_prepends_start_tag(self) -> None:
assert normalize_lyrics("[Verse]\nhello").startswith("[start]\n")
def test_lowercases_structure_tags(self) -> None:
assert "[verse]" in normalize_lyrics("[Verse]\nhello")
assert "[Verse]" not in normalize_lyrics("[Verse]\nhello")
def test_text_after_a_leading_tag_is_dropped(self) -> None:
"""Documented contract: a tag must sit on its own line.
Lyrics written next to a tag are silently lost, which is why the
serving docs tell callers to put tags on their own line.
"""
out = normalize_lyrics("[Verse] Walking down the street")
assert "Walking" not in out
assert "[verse]" in out
def test_text_on_its_own_line_survives(self) -> None:
out = normalize_lyrics("[Verse]\nWalking down the street")
assert "Walking down the street" in out
def test_bracket_space_becomes_newline(self) -> None:
assert normalize_lyrics("a [chorus]") == "[start]\na\n[chorus]"
class TestCleanCaption:
def test_special_token_form_is_rewritten_not_passed_through(self) -> None:
"""A caption must not be able to inject a real special token."""
assert clean_caption("<|bpm 92|>") == "bpm is 92"
assert "<|" not in clean_caption("<|bpm 92|>")
def test_lone_tag_keeps_its_inner_text(self) -> None:
assert clean_caption("<|instrumental|>") == "instrumental"
def test_markdown_is_stripped(self) -> None:
assert clean_caption("## Heading") == "Heading"
assert clean_caption("**bold**") == "bold"
assert clean_caption("- item") == "item"
def test_blank_lines_collapse_at_two_not_three(self) -> None:
"""The source collapses runs of two or more, not three or more.
This shifts blank-line token positions and therefore the audio, so it
is pinned deliberately.
"""
assert clean_caption("a\n\nb") == "a\nb"
assert clean_caption("a\n\n\n\nb") == "a\nb"
class TestBuildPrompt:
def test_framing_tokens_are_present_and_ordered(self) -> None:
prompt = build_prompt("a bright pop song", "[Verse]\nhello")
assert prompt.startswith("<|im_start|><|caption_start|>")
assert prompt.endswith("<|im_end|><|audio_start|>")
assert prompt.index("<|caption_end|>") < prompt.index("<|lyrics_start|>")
class TestBuildCfgNullTokenIds:
@staticmethod
def _prompt_ids(body_len: int) -> list[int]:
return (
[SPECIAL_TOKEN_IDS["<|im_start|>"]]
+ list(range(1000, 1000 + body_len))
+ [SPECIAL_TOKEN_IDS["<|im_end|>"], SPECIAL_TOKEN_IDS["<|audio_start|>"]]
)
def test_length_is_preserved(self) -> None:
"""Equal length is what lets the two rows decode in lockstep."""
ids = self._prompt_ids(12)
assert len(build_cfg_null_token_ids(ids)) == len(ids)
def test_body_is_replaced_and_framing_survives(self) -> None:
ids = self._prompt_ids(12)
null = build_cfg_null_token_ids(ids)
assert null[0] == SPECIAL_TOKEN_IDS["<|im_start|>"]
assert null[-2] == SPECIAL_TOKEN_IDS["<|im_end|>"]
assert null[-1] == SPECIAL_TOKEN_IDS["<|audio_start|>"]
assert set(null[1:-2]) == {SPECIAL_TOKEN_IDS["<|audio_cfg|>"]}
def test_input_is_not_mutated(self) -> None:
ids = self._prompt_ids(6)
before = list(ids)
build_cfg_null_token_ids(ids)
assert ids == before
def test_minimal_prompt_with_empty_body(self) -> None:
ids = [
SPECIAL_TOKEN_IDS["<|im_start|>"],
SPECIAL_TOKEN_IDS["<|audio_cfg|>"],
SPECIAL_TOKEN_IDS["<|im_end|>"],
SPECIAL_TOKEN_IDS["<|audio_start|>"],
]
assert build_cfg_null_token_ids(ids) == ids
def test_too_short_is_rejected(self) -> None:
with pytest.raises(ValueError, match="too short"):
build_cfg_null_token_ids([1, 2, 3])
def test_wrong_framing_is_rejected(self) -> None:
with pytest.raises(ValueError, match="framing"):
build_cfg_null_token_ids([9, 9, 9, 9, 9])
class TestChunkWindows:
def test_no_frames_yields_nothing(self) -> None:
assert chunk_windows(0) == []
def test_short_song_is_one_final_window(self) -> None:
windows = chunk_windows(50)
assert len(windows) == 1
assert (windows[0].start, windows[0].end) == (0, 50)
assert windows[0].is_first and windows[0].is_last
def test_exactly_one_window(self) -> None:
windows = chunk_windows(AR_CHUNK_FRAMES)
assert len(windows) == 1
assert windows[0].is_last
def test_windows_advance_by_the_hop(self) -> None:
windows = chunk_windows(450)
starts = [w.start for w in windows]
assert starts == list(range(0, starts[-1] + 1, AR_CHUNK_HOP_FRAMES))
assert windows[-1].is_last
assert not any(w.is_last for w in windows[:-1])
def test_windows_cover_every_frame(self) -> None:
frames = 437
windows = chunk_windows(frames)
covered: set[int] = set()
for window in windows:
covered.update(range(window.start, window.end))
assert covered == set(range(frames))
def test_final_window_is_clamped_to_the_song(self) -> None:
windows = chunk_windows(437)
assert windows[-1].end == 437
def test_only_the_first_window_is_first(self) -> None:
windows = chunk_windows(600)
assert windows[0].is_first
assert not any(w.is_first for w in windows[1:])
class TestCropBounds:
def test_single_window_song_is_not_cropped(self) -> None:
window = chunk_windows(50)[0]
assert crop_sample_bounds(window) == (0, 0)
def test_first_window_keeps_its_head(self) -> None:
left, right = crop_sample_bounds(chunk_windows(600)[0])
assert left == 0
assert right == (HOP_MEL_FRAMES - BLEND_MEL_FRAMES) * DAV_HOP_SAMPLES
def test_last_window_keeps_its_tail(self) -> None:
left, right = crop_sample_bounds(chunk_windows(600)[-1])
assert left == BLEND_MEL_FRAMES * DAV_HOP_SAMPLES
assert right == 0
def test_middle_window_is_cropped_on_both_sides(self) -> None:
windows = chunk_windows(900)
middle = windows[len(windows) // 2]
left, right = crop_sample_bounds(middle)
assert left > 0 and right > 0
def test_overlap_is_half_a_window(self) -> None:
assert overlap_mel_length() == HOP_MEL_FRAMES // 2