From 2c6c07c4f68385cf4f625a5cdb0dec710e9c0fff Mon Sep 17 00:00:00 2001 From: vveerrgg Date: Wed, 8 Apr 2026 15:20:33 -0700 Subject: [PATCH] [TTS] Add missing _generate_pcm_chunks for OmniOpenAIServingSpeech streaming (#2569) Signed-off-by: Yueqian Lin Signed-off-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com> Co-authored-by: vveerrgg --- .../openai_api/test_serving_speech_stream.py | 15 +++++++++++++++ vllm_omni/entrypoints/openai/serving_speech.py | 9 +++++++++ 2 files changed, 24 insertions(+) diff --git a/tests/entrypoints/openai_api/test_serving_speech_stream.py b/tests/entrypoints/openai_api/test_serving_speech_stream.py index bd136ac72..1d26b5855 100644 --- a/tests/entrypoints/openai_api/test_serving_speech_stream.py +++ b/tests/entrypoints/openai_api/test_serving_speech_stream.py @@ -385,3 +385,18 @@ class TestStreamingSpeechWebSocket: speech_service.engine_client.abort.assert_awaited_once_with("req-abort") assert websocket.send_json.await_count == 2 + + +class TestGeneratePcmChunksContract: + """Guard: _generate_pcm_chunks must exist on OmniOpenAIServingSpeech. + + The WebSocket handler calls speech_service._generate_pcm_chunks() + at runtime. If the method is removed, all WS TTS streaming breaks + with an AttributeError. This test catches that at CI time. + """ + + def test_generate_pcm_chunks_defined(self): + assert hasattr(OmniOpenAIServingSpeech, "_generate_pcm_chunks") + assert asyncio.iscoroutinefunction(OmniOpenAIServingSpeech._generate_pcm_chunks) or callable( + OmniOpenAIServingSpeech._generate_pcm_chunks + ) diff --git a/vllm_omni/entrypoints/openai/serving_speech.py b/vllm_omni/entrypoints/openai/serving_speech.py index a4b029393..5903c0cd6 100644 --- a/vllm_omni/entrypoints/openai/serving_speech.py +++ b/vllm_omni/entrypoints/openai/serving_speech.py @@ -1459,6 +1459,15 @@ class OmniOpenAIServingSpeech(OpenAIServing, AudioMixin): ) return request_id, generator, tts_params + async def _generate_pcm_chunks(self, generator, request_id: str): + """Yield raw PCM byte chunks from the engine generator. + + Delegates to ``_generate_audio_chunks`` with ``response_format="pcm"``. + Used by the WebSocket streaming handler and ``_iter_pcm_audio_bytes``. + """ + async for chunk in self._generate_audio_chunks(generator, request_id, response_format="pcm"): + yield chunk + async def _iter_pcm_audio_bytes(self, request: OpenAICreateSpeechRequest): """Yield raw PCM bytes for a speech request as soon as chunks are decoded.""" request_id, generator, _ = await self._prepare_speech_generation(request)