From fafcd7987015d49d9c95af398ad9549118891d5b Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 11 Sep 2025 13:53:52 -0400 Subject: [PATCH] OpenAITTSService: add speed arg --- CHANGELOG.md | 3 +++ src/pipecat/services/openai/tts.py | 24 ++++++++++++++++-------- uv.lock | 14 ++++++++++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7524b0d53..4f62b009d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added a `speed` arg to `OpenAITTSService` to control the speed of the voice + response. + - Added `FrameProcessor.push_interruption_task_frame_and_wait()`. Use this method to programatically interrupt the bot from any part of the pipeline. This guarantees that all the processors in the pipeline are diff --git a/src/pipecat/services/openai/tts.py b/src/pipecat/services/openai/tts.py index 6e82496a4..a67392caf 100644 --- a/src/pipecat/services/openai/tts.py +++ b/src/pipecat/services/openai/tts.py @@ -64,6 +64,7 @@ class OpenAITTSService(TTSService): model: str = "gpt-4o-mini-tts", sample_rate: Optional[int] = None, instructions: Optional[str] = None, + speed: Optional[float] = None, **kwargs, ): """Initialize OpenAI TTS service. @@ -75,6 +76,7 @@ class OpenAITTSService(TTSService): model: TTS model to use. Defaults to "gpt-4o-mini-tts". sample_rate: Output audio sample rate in Hz. If None, uses OpenAI's default 24kHz. instructions: Optional instructions to guide voice synthesis behavior. + speed: Voice speed control (0.25 to 4.0, default 1.0). **kwargs: Additional keyword arguments passed to TTSService. """ if sample_rate and sample_rate != self.OPENAI_SAMPLE_RATE: @@ -84,6 +86,7 @@ class OpenAITTSService(TTSService): ) super().__init__(sample_rate=sample_rate, **kwargs) + self._speed = speed self.set_model_name(model) self.set_voice(voice) self._instructions = instructions @@ -133,17 +136,22 @@ class OpenAITTSService(TTSService): try: await self.start_ttfb_metrics() - # Setup extra body parameters - extra_body = {} + # Setup API parameters + create_params = { + "input": text, + "model": self.model_name, + "voice": VALID_VOICES[self._voice_id], + "response_format": "pcm", + } + if self._instructions: - extra_body["instructions"] = self._instructions + create_params["instructions"] = self._instructions + + if self._speed: + create_params["speed"] = self._speed async with self._client.audio.speech.with_streaming_response.create( - input=text, - model=self.model_name, - voice=VALID_VOICES[self._voice_id], - response_format="pcm", - extra_body=extra_body, + **create_params ) as r: if r.status_code != 200: error = await r.text() diff --git a/uv.lock b/uv.lock index b19949183..34ede6c3b 100644 --- a/uv.lock +++ b/uv.lock @@ -4307,6 +4307,12 @@ local-smart-turn = [ { name = "torchaudio" }, { name = "transformers" }, ] +local-smart-turn-v3 = [ + { name = "onnxruntime" }, + { name = "torch" }, + { name = "torchaudio" }, + { name = "transformers" }, +] mcp = [ { name = "mcp", extra = ["cli"] }, ] @@ -4460,7 +4466,8 @@ requires-dist = [ { name = "numba", specifier = "==0.61.2" }, { name = "numpy", specifier = ">=1.26.4,<3" }, { name = "nvidia-riva-client", marker = "extra == 'riva'", specifier = "~=2.21.1" }, - { name = "onnxruntime", marker = "extra == 'silero'", specifier = "~=1.20.1" }, + { name = "onnxruntime", marker = "extra == 'local-smart-turn-v3'", specifier = ">=1.20.1,<2" }, + { name = "onnxruntime", marker = "extra == 'silero'", specifier = ">=1.20.1,<2" }, { name = "openai", specifier = ">=1.74.0,<=1.99.1" }, { name = "opencv-python", marker = "extra == 'webrtc'", specifier = "~=4.11.0.86" }, { name = "openpipe", marker = "extra == 'openpipe'", specifier = "~=4.50.0" }, @@ -4488,8 +4495,11 @@ requires-dist = [ { name = "tenacity", marker = "extra == 'livekit'", specifier = ">=8.2.3,<10.0.0" }, { name = "timm", marker = "extra == 'moondream'", specifier = "~=1.0.13" }, { name = "torch", marker = "extra == 'local-smart-turn'", specifier = ">=2.5.0,<3" }, + { name = "torch", marker = "extra == 'local-smart-turn-v3'", specifier = ">=2.5.0,<3" }, { name = "torchaudio", marker = "extra == 'local-smart-turn'", specifier = ">=2.5.0,<3" }, + { name = "torchaudio", marker = "extra == 'local-smart-turn-v3'", specifier = ">=2.5.0,<3" }, { name = "transformers", marker = "extra == 'local-smart-turn'" }, + { name = "transformers", marker = "extra == 'local-smart-turn-v3'" }, { name = "transformers", marker = "extra == 'moondream'", specifier = ">=4.48.0" }, { name = "transformers", marker = "extra == 'ultravox'", specifier = ">=4.48.0" }, { name = "uvicorn", marker = "extra == 'runner'", specifier = ">=0.32.0,<1.0.0" }, @@ -4513,7 +4523,7 @@ requires-dist = [ { name = "websockets", marker = "extra == 'soniox'", specifier = ">=13.1,<15.0" }, { name = "websockets", marker = "extra == 'websocket'", specifier = ">=13.1,<15.0" }, ] -provides-extras = ["aic", "anthropic", "assemblyai", "asyncai", "aws", "aws-nova-sonic", "azure", "cartesia", "cerebras", "deepseek", "daily", "deepgram", "elevenlabs", "fal", "fireworks", "fish", "gladia", "google", "grok", "groq", "gstreamer", "heygen", "inworld", "krisp", "koala", "langchain", "livekit", "lmnt", "local", "mcp", "mem0", "mistral", "mlx-whisper", "moondream", "nim", "neuphonic", "noisereduce", "openai", "openpipe", "openrouter", "perplexity", "playht", "qwen", "rime", "riva", "runner", "sambanova", "sarvam", "sentry", "local-smart-turn", "remote-smart-turn", "silero", "simli", "soniox", "soundfile", "speechmatics", "tavus", "together", "tracing", "ultravox", "webrtc", "websocket", "whisper"] +provides-extras = ["aic", "anthropic", "assemblyai", "asyncai", "aws", "aws-nova-sonic", "azure", "cartesia", "cerebras", "deepseek", "daily", "deepgram", "elevenlabs", "fal", "fireworks", "fish", "gladia", "google", "grok", "groq", "gstreamer", "heygen", "inworld", "krisp", "koala", "langchain", "livekit", "lmnt", "local", "mcp", "mem0", "mistral", "mlx-whisper", "moondream", "nim", "neuphonic", "noisereduce", "openai", "openpipe", "openrouter", "perplexity", "playht", "qwen", "rime", "riva", "runner", "sambanova", "sarvam", "sentry", "local-smart-turn", "local-smart-turn-v3", "remote-smart-turn", "silero", "simli", "soniox", "soundfile", "speechmatics", "tavus", "together", "tracing", "ultravox", "webrtc", "websocket", "whisper"] [package.metadata.requires-dev] dev = [