Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .buildkite/npu/test-npu-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,16 @@ steps:
depends_on: upload-nightly-pipeline
if: build.env("NIGHTLY") == "1" || build.pull_request.labels includes "nightly-test"
steps:
- label: ":sound: TTS · Drama 3 Test (A3)"
key: nightly-tts-drama-3-npu
source_file_dependencies: tts_drama_3_function
mirror_hardwares: a3_npu_2
timeout_in_minutes: 60
env:
VLLM_WORKER_MULTIPROC_METHOD: spawn
commands:
- pytest -s -v tests/e2e/online_serving/test_drama_3.py -m 'advanced_model and npu and A3' --run-level 'advanced_model'

- label: ":full_moon: TTS · Perf Test"
source_file_dependencies: tts_qwen3_tts_perf
timeout_in_minutes: 180
Expand Down
12 changes: 12 additions & 0 deletions .buildkite/npu/test-npu-ready.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ steps:
- pytest -s -v tests/e2e/online_serving/test_qwen3_omni.py -m 'core_model and npu and A3 and omni' --run-level 'core_model'
timeout_in_minutes: 60

- label: ":sound: TTS · Drama 3 Test (A3)"
key: ready-tts-drama-3-npu
source_file_dependencies: tts_drama_3_function
mirror_hardwares: a3_npu_2
timeout_in_minutes: 60
env:
VLLM_WORKER_MULTIPROC_METHOD: spawn
commands:
# Keep the smoke selection, but load real weights: core_model's dummy
# loader casts FP16 random values to FP8 on device, unsupported on A3.
- pytest -s -v tests/e2e/online_serving/test_drama_3.py -m 'core_model and npu and A3' --run-level 'advanced_model'

- label: ":sound: TTS · Qwen3-TTS CustomVoice Test"
mirror_hardwares: a3_npu_2
env:
Expand Down
10 changes: 5 additions & 5 deletions tests/e2e/online_serving/test_drama_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def get_prompt(prompt_type: str = "en") -> str:
@pytest.mark.core_model
@pytest.mark.advanced_model
@pytest.mark.tts
@hardware_test(res={"cuda": "H100"}, num_cards=1)
@hardware_test(res={"cuda": "H100", "npu": "A3"}, num_cards={"cuda": 1, "npu": 2})
@pytest.mark.parametrize("omni_server", tts_server_params, indirect=True)
def test_text_to_audio_001(omni_server, openai_client) -> None:
"""Baseline smoke: default deploy, non-streaming WAV.
Expand All @@ -79,7 +79,7 @@ def test_text_to_audio_001(omni_server, openai_client) -> None:

@pytest.mark.advanced_model
@pytest.mark.tts
@hardware_test(res={"cuda": "H100"}, num_cards=1)
@hardware_test(res={"cuda": "H100", "npu": "A3"}, num_cards={"cuda": 1, "npu": 2})
@pytest.mark.parametrize("omni_server", tts_server_params, indirect=True)
@pytest.mark.parametrize("seed", [0, 42], ids=["seed0", "seed42"])
def test_voice_clone_streaming_002(omni_server, openai_client, seed: int) -> None:
Expand Down Expand Up @@ -115,13 +115,13 @@ def test_voice_clone_streaming_002(omni_server, openai_client, seed: int) -> Non

@pytest.mark.advanced_model
@pytest.mark.tts
@hardware_test(res={"cuda": "H100"}, num_cards=1)
@hardware_test(res={"cuda": "H100", "npu": "A3"}, num_cards={"cuda": 1, "npu": 2})
@pytest.mark.parametrize("omni_server", tts_server_params, indirect=True)
def test_voice_clone_concurrent_003(omni_server, openai_client) -> None:
"""Concurrent voice-clone requests keep their own codebook-tower state.

Input Modal: text + ref_audio + ref_text
Datasets: MAX_CONCURRENT identical requests (max_num_seqs=8)
Datasets: MAX_CONCURRENT identical requests using the platform deploy config
"""
request_config = {
"model": omni_server.model,
Expand All @@ -138,7 +138,7 @@ def test_voice_clone_concurrent_003(omni_server, openai_client) -> None:

@pytest.mark.advanced_model
@pytest.mark.tts
@hardware_test(res={"cuda": "H100"}, num_cards=1)
@hardware_test(res={"cuda": "H100", "npu": "A3"}, num_cards={"cuda": 1, "npu": 2})
@pytest.mark.parametrize("omni_server", tts_server_params, indirect=True)
def test_voice_clone_requires_ref_text_004(omni_server, openai_client) -> None:
"""``ref_audio`` without ``ref_text`` is rejected."""
Expand Down
24 changes: 24 additions & 0 deletions tests/model_executor/models/fish_omni/test_fish_omni_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,27 @@ def test_tensor_parallel_is_allowed_on_npu_only(monkeypatch):
fish_model._reject_unsupported_fish_parallel(
SimpleNamespace(pipeline_parallel_size=1, prefill_context_parallel_size=2)
)


def test_npu_deploy_captures_full_decode_graphs():
"""Stage 0 must replay FULL decode graphs on Ascend.

Decode is one frame per stream per step: 40 backbone layers and 13 tower
layers of small kernels, where per-kernel launch cost dominates. The NPU
override in ``deploy/fish_omni.yaml`` has to ask for FULL capture and stop
the capture sizes at ``max_num_seqs``; a PIECEWISE-only override silently
keeps decode eager and costs roughly a third of single-stream throughput.
"""
from pathlib import Path

import yaml

deploy_path = Path(__file__).resolve().parents[4] / "vllm_omni" / "deploy" / "fish_omni.yaml"
deploy = yaml.safe_load(deploy_path.read_text())
npu = deploy["platforms"]["npu"]
stage0 = next(stage for stage in npu["stages"] if stage["stage_id"] == 0)
compilation = stage0["compilation_config"]

assert str(compilation["cudagraph_mode"]).upper() == "FULL_AND_PIECEWISE"
assert compilation["max_cudagraph_capture_size"] == stage0["max_num_seqs"]
assert max(compilation["cudagraph_capture_sizes"]) == stage0["max_num_seqs"]
17 changes: 16 additions & 1 deletion vllm_omni/deploy/fish_omni.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,23 @@ platforms:
enable_expert_parallel: true
max_num_seqs: 64
max_num_batched_tokens: 4096
# Decode steps replay one FULL graph per captured batch size; mixed
# steps (a new request's prompt rows) replay PIECEWISE backbone graphs.
# A pure decode step is the steady state of a stream: sampling one
# whole frame is 40 backbone layers plus 13 tower layers of small
# per-layer kernels, so the eager launch cost dominates the arithmetic.
# FULL capture is what makes it a single replay: measured on 2x910B1
# (TP2 + EP, 64 streams max), decode-only RTF at one stream went
# 1.24 -> 0.95 and TTFA p50 0.91 s -> 0.80 s, with p95 RTF 1.30 ->
# 1.14. Batching scales it further: 133 frames/s at 16 streams and
# 305 at 64 in the best of three runs. The graph captures the sample
# rows of its batch size, which is why the sizes below stop at
# max_num_seqs. Mamba/GDN state, the tower KV pages and the per-step
# frame buffers live at fixed addresses outside the replayed region.
compilation_config:
cudagraph_mode: PIECEWISE
cudagraph_mode: FULL_AND_PIECEWISE
max_cudagraph_capture_size: 64
cudagraph_capture_sizes: [1, 2, 4, 8, 16, 24, 32, 40, 48, 56, 64]
devices: "0,1"
- stage_id: 1
gpu_memory_utilization: 0.10
Expand Down
Loading