diff --git a/src/pipecat/services/openai.py b/src/pipecat/services/openai.py index 0864baf53..ae0cd70c1 100644 --- a/src/pipecat/services/openai.py +++ b/src/pipecat/services/openai.py @@ -11,7 +11,6 @@ from typing import AsyncGenerator, List, Literal import aiohttp from loguru import logger -from openai import OpenAI from PIL import Image from pipecat.frames.frames import (AudioRawFrame, ErrorFrame, Frame, @@ -27,7 +26,7 @@ from pipecat.services.ai_services import (ImageGenService, LLMService, TTSService) try: - from openai import AsyncOpenAI, AsyncStream + from openai import AsyncOpenAI, AsyncStream, BadRequestError from openai.types.chat import (ChatCompletion, ChatCompletionChunk, ChatCompletionFunctionMessageParam, ChatCompletionMessageParam, @@ -264,38 +263,40 @@ class OpenAIImageGenService(ImageGenService): yield frame -class WhisperTTSService(TTSService): +class OpenAITTSService(TTSService): def __init__( self, *, - api_key: str | None, + api_key: str | None = None, voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"] = "alloy", - response_format: Literal["mp3", "opus", "flac", "pcm"] = "pcm", model: Literal["tts-1", "tts-1-hd"] = "tts-1", **kwargs): super().__init__(**kwargs) self._voice = voice self._model = model - self._response_format = response_format self._client = AsyncOpenAI(api_key=api_key) async def run_tts(self, text: str) -> AsyncGenerator[Frame, None]: logger.debug(f"Generating TTS: [{text}]") - async with self._client.audio.speech.with_streaming_response.create( - input=text, - model=self._model, - voice=self._voice, - response_format=self._response_format - ) as r: - if r.status != 200: - error = await r.text() - logger.error(f"Error getting audio (status: {r.status}, error: {error})") - yield ErrorFrame(f"Error getting audio (status: {r.status}, error: {error})") - return - for chunk in r.iter_bytes(1024): - if len(chunk) > 0: - frame = AudioRawFrame(chunk, 16000, 1) - yield frame + try: + async with self._client.audio.speech.with_streaming_response.create( + input=text, + model=self._model, + voice=self._voice, + response_format="pcm", + ) as r: + if r.status_code != 200: + error = await r.text() + logger.error(f"Error getting audio (status: {r.status_code}, error: {error})") + yield ErrorFrame(f"Error getting audio (status: {r.status_code}, error: {error})") + return + async for chunk in r.iter_bytes(8192): + if len(chunk) > 0: + frame = AudioRawFrame(chunk, 24_000, 1) + yield frame + except BadRequestError as e: + logger.exception(f"Error generating TTS: {e}") + raise diff --git a/tests/test_openai_tts.py b/tests/test_openai_tts.py new file mode 100644 index 000000000..5bbf449b9 --- /dev/null +++ b/tests/test_openai_tts.py @@ -0,0 +1,39 @@ +import asyncio +import unittest + +import openai +import pyaudio +from dotenv import load_dotenv + +from pipecat.frames.frames import AudioRawFrame, ErrorFrame +from pipecat.services.openai import OpenAITTSService + +load_dotenv() + + +class TestWhisperOpenAIService(unittest.IsolatedAsyncioTestCase): + async def test_whisper_tts(self): + pa = pyaudio.PyAudio() + stream = pa.open(format=pyaudio.paInt16, + channels=1, + rate=24_000, + output=True) + + tts = OpenAITTSService(voice="nova") + + async for frame in tts.run_tts("Hello, there. Nice to meet you, seems to work well"): + self.assertIsInstance(frame, AudioRawFrame) + stream.write(frame.audio) + + await asyncio.sleep(.5) + stream.stop_stream() + pa.terminate() + + tts = OpenAITTSService(voice="invalid_voice") + with self.assertRaises(openai.BadRequestError): + async for frame in tts.run_tts("wont work"): + self.assertIsInstance(frame, ErrorFrame) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_whisper.py b/tests/test_whisper.py deleted file mode 100644 index 62037e8a0..000000000 --- a/tests/test_whisper.py +++ /dev/null @@ -1,14 +0,0 @@ -import unittest - -from pipecat.services.openai import WhisperTTSService - - -class TestWhisperOpenAIService(unittest.IsolatedAsyncioTestCase): - async def test_whisper_tts(self): - tts = WhisperTTSService() - # tts_response = await tts.run_tts("Hello, world") - await tts.say("Hi! If you want to talk to me, just say 'Hey Robot'.") - - -if __name__ == "__main__": - unittest.main()