Forward backend options during streaming speech generation (#1389) (#1390)

This commit is contained in:
Hua.T
2026-10-11 17:23:05 -04:00
committed by GitHub
parent 55fadb4c56
commit e981d2d8fc
2 changed files with 31 additions and 5 deletions
@@ -92,7 +92,7 @@ class TextToSpeech(Pipeline):
# Streaming response # Streaming response
if stream: if stream:
return self.stream(texts, speaker, encoding) return self.stream(texts, speaker, encoding, **kwargs)
# Transform text to speech # Transform text to speech
results = [self.execute(x, speaker, encoding, **kwargs) for x in texts] results = [self.execute(x, speaker, encoding, **kwargs) for x in texts]
@@ -137,7 +137,7 @@ class TextToSpeech(Pipeline):
return exists return exists
def stream(self, texts, speaker, encoding): def stream(self, texts, speaker, encoding, **kwargs):
""" """
Iterates over texts, splits into segments and yields snippets of audio. Iterates over texts, splits into segments and yields snippets of audio.
This method is designed to integrate with streaming LLM generation. This method is designed to integrate with streaming LLM generation.
@@ -146,6 +146,7 @@ class TextToSpeech(Pipeline):
texts: list of input texts texts: list of input texts
speaker: speaker id speaker: speaker id
encoding: audio encoding format encoding: audio encoding format
kwargs: additional keyword args
Returns: Returns:
snippets of audio as NumPy arrays or audio bytes depending on encoding parameter snippets of audio as NumPy arrays or audio bytes depending on encoding parameter
@@ -157,11 +158,11 @@ class TextToSpeech(Pipeline):
if x == "\n" or (x.strip().endswith(".") and len([y for y in buffer if y]) > 2): if x == "\n" or (x.strip().endswith(".") and len([y for y in buffer if y]) > 2):
data, buffer = "".join(buffer), [] data, buffer = "".join(buffer), []
yield self.execute(data, speaker, encoding) yield self.execute(data, speaker, encoding, **kwargs)
if buffer: if buffer:
data = "".join(buffer) data = "".join(buffer)
yield self.execute(data, speaker, encoding) yield self.execute(data, speaker, encoding, **kwargs)
def execute(self, text, speaker, encoding, **kwargs): def execute(self, text, speaker, encoding, **kwargs):
""" """
@@ -4,7 +4,9 @@ TextToSpeech module tests
import unittest import unittest
from unittest.mock import patch from unittest.mock import call, patch
import numpy as np
from txtai.pipeline import TextToSpeech from txtai.pipeline import TextToSpeech
@@ -80,3 +82,26 @@ class TestTextToSpeech(unittest.TestCase):
# Check that data is generated # Check that data is generated
self.assertGreater(len(speech), 0) self.assertGreater(len(speech), 0)
self.assertEqual(rate, 22050) self.assertEqual(rate, 22050)
@patch("txtai.pipeline.audio.texttospeech.Kokoro")
@patch.object(TextToSpeech, "hasfile")
def testStreamingOptions(self, hasfile, kokoro):
"""
Test backend options reach both flushed and final streaming segments
"""
hasfile.side_effect = lambda _, name: name in ("model.onnx", "voices.json")
audio = np.array([0.1, -0.1], dtype=np.float32)
kokoro.return_value.return_value = (audio, 24000)
tts = TextToSpeech("neuml/kokoro-int8-onnx", rate=None)
chunks = list(tts(iter(["One ", "two ", "three.", "Tail"]), stream=True, speaker="af", speed=1.5, transcribe=False))
self.assertEqual(
kokoro.return_value.call_args_list,
[call("One two three.", "af", speed=1.5, transcribe=False), call("Tail", "af", speed=1.5, transcribe=False)],
)
self.assertEqual(len(chunks), 2)
for data, rate in chunks:
np.testing.assert_array_equal(data, audio)
self.assertEqual(rate, 24000)