[ci]add merge ci for streaming input (#2965)
Signed-off-by: CHEN <116010019@link.cuhk.edu.cn>
This commit is contained in:
1 parent
8e1bbc9ab5
commit
e82c0a5e1d
3 files changed
+64
-18
No files matched your search
@@ -275,6 +275,46 @@ steps:
|
||||
path: /mnt/hf-cache
|
||||
type: DirectoryOrCreate
|
||||
|
||||
- label: "Audio Streaming Input Test with H100"
|
||||
timeout_in_minutes: 30
|
||||
depends_on: upload-merge-pipeline
|
||||
commands:
|
||||
- export VLLM_TEST_CLEAN_GPU_MEMORY="1"
|
||||
- pytest -s -v tests/entrypoints/openai_api/test_qwen3_omni_realtime_websocket.py -m "advanced_model" --run-level "advanced_model"
|
||||
agents:
|
||||
queue: "mithril-h100-pool"
|
||||
plugins:
|
||||
- kubernetes:
|
||||
podSpec:
|
||||
containers:
|
||||
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
|
||||
resources:
|
||||
limits:
|
||||
nvidia.com/gpu: 2
|
||||
volumeMounts:
|
||||
- name: devshm
|
||||
mountPath: /dev/shm
|
||||
- name: hf-cache
|
||||
mountPath: /root/.cache/huggingface
|
||||
env:
|
||||
- name: HF_HOME
|
||||
value: /root/.cache/huggingface
|
||||
- name: HF_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hf-token-secret
|
||||
key: token
|
||||
nodeSelector:
|
||||
node.kubernetes.io/instance-type: gpu-h100-sxm
|
||||
volumes:
|
||||
- name: devshm
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
- name: hf-cache
|
||||
hostPath:
|
||||
path: /mnt/hf-cache
|
||||
type: DirectoryOrCreate
|
||||
|
||||
- label: "Diffusion Image Edit Test with H100 (1 GPU)"
|
||||
timeout_in_minutes: 20
|
||||
depends_on: upload-merge-pipeline
|
||||
|
||||
+15
-14
@@ -23,33 +23,28 @@ from tests.helpers.media import (
|
||||
generate_synthetic_audio,
|
||||
)
|
||||
from tests.helpers.runtime import OmniServerParams
|
||||
from tests.helpers.stage_config import get_deploy_config_path, modify_stage_config
|
||||
from tests.helpers.stage_config import get_deploy_config_path
|
||||
|
||||
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
|
||||
|
||||
MODEL = "Qwen/Qwen3-Omni-30B-A3B-Instruct"
|
||||
|
||||
# Synthetic input for realtime E2E (``generate_synthetic_audio``); distinct cache file per phrase.
|
||||
REALTIME_SYNTH_PHRASE_TEXT = "Translate into Chinese: Beijing is the Capital of China"
|
||||
|
||||
# The new-schema CI overlay bakes in async_chunk: False and covers CUDA/ROCm/XPU
|
||||
# via its ``platforms:`` section, so one path serves all three.
|
||||
default_stage_config = get_deploy_config_path("ci/qwen3_omni_moe.yaml")
|
||||
|
||||
|
||||
def _realtime_stage_config_path() -> str:
|
||||
"""CI omni layout without async_chunk; stage 0 thinker max_tokens=10."""
|
||||
return modify_stage_config(
|
||||
default_stage_config,
|
||||
updates={"stages": {0: {"default_sampling_params.max_tokens": 10}}},
|
||||
)
|
||||
|
||||
|
||||
realtime_server_params = [
|
||||
pytest.param(
|
||||
OmniServerParams(
|
||||
model=MODEL,
|
||||
stage_config_path=_realtime_stage_config_path(),
|
||||
stage_config_path=default_stage_config,
|
||||
use_stage_cli=True,
|
||||
server_args=["--no-async-chunk"],
|
||||
),
|
||||
id="thinker_max_tokens_10",
|
||||
id="default",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -169,9 +164,15 @@ class TestQwen3OmniRealtimeWebSocket:
|
||||
"""
|
||||
Short streamed 16 kHz mono PCM16 input; expect streamed PCM16 audio deltas and
|
||||
transcription. Verify Whisper(output audio) aligns with model text (same idea
|
||||
as multimodal omni e2e).
|
||||
as multimodal omni e2e). Input speech is synthesized from
|
||||
``REALTIME_SYNTH_PHRASE_TEXT``.
|
||||
"""
|
||||
syn = generate_synthetic_audio(10, 1, sample_rate=16000)
|
||||
syn = generate_synthetic_audio(
|
||||
10,
|
||||
1,
|
||||
sample_rate=16000,
|
||||
phrase_text=REALTIME_SYNTH_PHRASE_TEXT,
|
||||
)
|
||||
wav_bytes = base64.b64decode(syn["base64"])
|
||||
pcm16 = _pcm16_mono_16k_from_wav_bytes(wav_bytes)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import base64
|
||||
import concurrent.futures
|
||||
import gc
|
||||
import hashlib
|
||||
import io
|
||||
import logging
|
||||
import math
|
||||
@@ -66,6 +67,7 @@ def generate_synthetic_audio(
|
||||
num_channels: int,
|
||||
sample_rate: int = 48000,
|
||||
*,
|
||||
phrase_text: str = "test",
|
||||
force_regenerate: bool = False,
|
||||
cache_dir: Path | str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
@@ -74,12 +76,16 @@ def generate_synthetic_audio(
|
||||
|
||||
Caches the WAV under ``cache_dir`` when given, else under the default temp
|
||||
subdirectory. Reuses the file when the same
|
||||
``duration`` / ``num_channels`` / ``sample_rate`` are requested unless
|
||||
``force_regenerate`` is true.
|
||||
``duration`` / ``num_channels`` / ``sample_rate`` / ``phrase_text`` are
|
||||
requested unless ``force_regenerate`` is true.
|
||||
|
||||
The cache filename includes a SHA-256 digest of ``phrase_text`` so different
|
||||
phrases never share a WAV cache entry.
|
||||
"""
|
||||
root = _resolve_synthetic_media_cache_dir(cache_dir)
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
cache_path = root / f"synth_audio_d{duration}_ch{num_channels}_sr{sample_rate}.wav"
|
||||
phrase_key = hashlib.sha256(phrase_text.encode("utf-8")).hexdigest()
|
||||
cache_path = root / f"synth_audio_d{duration}_ch{num_channels}_sr{sample_rate}_pt{phrase_key}.wav"
|
||||
|
||||
if not force_regenerate and cache_path.is_file():
|
||||
data, _sr = sf.read(str(cache_path), dtype="float32", always_2d=True)
|
||||
@@ -204,7 +210,6 @@ def generate_synthetic_audio(
|
||||
enhanced = enhanced / peak * 0.95
|
||||
return enhanced.astype(np.float32)
|
||||
|
||||
phrase_text = "test"
|
||||
num_samples = int(sample_rate * max(1, duration))
|
||||
audio_data = np.zeros((num_samples, num_channels), dtype=np.float32)
|
||||
|
||||
|
||||
Reference in new issue
Block a user