aboutsummaryrefslogtreecommitdiff
path: root/app/tests
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-09 15:55:06 -0400
committerhistoria <historiavg@proton.me>2026-09-09 15:55:06 -0400
commit71f2c85aa5ea2aa5fe5f31537f5979459e82fcdd (patch)
tree2bdb01e2d2e4d3bc334c9014cff4cffbb0bcda08 /app/tests
parent0157ce4a347f9625e1e9d09e2bbf0fbfad722557 (diff)
downloadtts-audiobook-generator-71f2c85aa5ea2aa5fe5f31537f5979459e82fcdd.tar.gz
feat: instructions options translated automatically by --instructions
Diffstat (limited to 'app/tests')
-rw-r--r--app/tests/test_tts.py56
1 files changed, 56 insertions, 0 deletions
diff --git a/app/tests/test_tts.py b/app/tests/test_tts.py
index 7170262..c0ba666 100644
--- a/app/tests/test_tts.py
+++ b/app/tests/test_tts.py
@@ -1042,6 +1042,14 @@ class AudioCppFamilyDetectionTests(unittest.TestCase):
self.assertIs(client.profile, AUDIOCPP_DEFAULT_FAMILY_PROFILE)
self.assertEqual(client.profile.language_style, AUDIOCPP_LANG_OMIT)
+ def test_breeze_tts_uses_the_option_instruction_channel(self):
+ # BreezeTTS 2 reads its instruction from the request options, not
+ # the OpenAI-style "instructions" field.
+ profile = AUDIOCPP_FAMILY_PROFILES["breeze_tts"]
+ self.assertEqual(profile.instruction_channel,
+ audiocpp_client.AUDIOCPP_INSTRUCTION_OPTION)
+ self.assertEqual(profile.language_style, AUDIOCPP_LANG_OMIT)
+
def test_speech_to_speech_only_family_rejected_at_connect(self):
# A family whose model spec has no text-synthesis task
# (PersonaPlex, s2s-only) cannot narrate regardless of its hosted
@@ -1612,6 +1620,54 @@ class AudioCppTTSClientRequestTests(unittest.TestCase):
payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8"))
self.assertEqual(payload["instructions"], "Read whisper quiet.")
+ def test_option_channel_family_sends_the_instruction_as_a_request_option(self):
+ # BreezeTTS 2 ignores the top-level OpenAI "instructions" field;
+ # the instruction goes inside "options" for those families.
+ client = self._make_client(
+ family="breeze_tts", preset_mode=True, voice="narrator",
+ instructions="Speak slowly with a restrained tone.")
+ with patch("converter.clients.faster.urllib.request.urlopen",
+ return_value=self._post_response(self._wav_bytes())) as mock_urlopen:
+ client._request_wav("Hello.")
+ payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8"))
+ self.assertNotIn("instructions", payload)
+ self.assertEqual(payload["voice"], "narrator")
+ self.assertEqual(payload["options"],
+ {"instruction": "Speak slowly with a restrained tone."})
+
+ def test_option_channel_family_keeps_other_request_options(self):
+ client = self._make_client(
+ family="breeze_tts", instructions="Calm and steady.",
+ request_options={"guidance_scale": "2.5"})
+ with patch("converter.clients.faster.urllib.request.urlopen",
+ return_value=self._post_response(self._wav_bytes())) as mock_urlopen:
+ client._request_wav("Hello.")
+ payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8"))
+ self.assertEqual(payload["options"],
+ {"guidance_scale": "2.5", "instruction": "Calm and steady."})
+
+ def test_option_channel_family_respects_the_callers_instruction_option(self):
+ # An explicit --option instruction=... is the caller's instruction
+ # and is not overwritten by the --instructions value.
+ client = self._make_client(
+ family="breeze_tts", instructions="from the flag",
+ request_options={"instruction": "from the option"})
+ with patch("converter.clients.faster.urllib.request.urlopen",
+ return_value=self._post_response(self._wav_bytes())) as mock_urlopen:
+ client._request_wav("Hello.")
+ payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8"))
+ self.assertEqual(payload["options"]["instruction"], "from the option")
+
+ def test_option_channel_family_omits_options_when_no_instruction(self):
+ client = self._make_client(family="breeze_tts", preset_mode=True,
+ voice="narrator")
+ with patch("converter.clients.faster.urllib.request.urlopen",
+ return_value=self._post_response(self._wav_bytes())) as mock_urlopen:
+ client._request_wav("Hello.")
+ payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8"))
+ self.assertNotIn("options", payload)
+ self.assertNotIn("instructions", payload)
+
def test_preset_mode_sends_instructions_alongside_voice(self):
# Clone + style control: both the server-side voice and the
# instruction reach the model.