diff --git a/src/agents/voice/__init__.py b/src/agents/voice/__init__.py index e11ee4467f..749c6c5ed0 100644 --- a/src/agents/voice/__init__.py +++ b/src/agents/voice/__init__.py @@ -5,6 +5,7 @@ StreamedTranscriptionSession, STTModel, STTModelSettings, + TTSCustomVoice, TTSModel, TTSModelSettings, TTSVoice, @@ -29,6 +30,7 @@ "StreamedAudioInput", "STTModel", "STTModelSettings", + "TTSCustomVoice", "TTSModel", "TTSModelSettings", "TTSVoice", diff --git a/src/agents/voice/model.py b/src/agents/voice/model.py index 5698fbeefe..8ed3c5b62f 100644 --- a/src/agents/voice/model.py +++ b/src/agents/voice/model.py @@ -5,6 +5,8 @@ from dataclasses import dataclass from typing import Any, Literal +from typing_extensions import TypedDict + from .imports import np, npt from .input import AudioInput, StreamedAudioInput from .utils import get_sentence_based_splitter @@ -14,22 +16,33 @@ ) DEFAULT_TTS_BUFFER_SIZE = 120 -TTSVoice = Literal[ - "alloy", - "ash", - "ballad", - "coral", - "echo", - "fable", - "onyx", - "nova", - "sage", - "shimmer", - "verse", - "marin", - "cedar", -] -"""Exportable type for the TTSModelSettings voice enum""" + +class TTSCustomVoice(TypedDict): + """A custom OpenAI TTS voice reference.""" + + id: str + """The custom voice ID.""" + + +TTSVoice = ( + Literal[ + "alloy", + "ash", + "ballad", + "coral", + "echo", + "fable", + "onyx", + "nova", + "sage", + "shimmer", + "verse", + "marin", + "cedar", + ] + | TTSCustomVoice +) +"""Exportable type for built-in TTS voices and custom voice IDs.""" @dataclass diff --git a/tests/voice/test_tts_voice_types.py b/tests/voice/test_tts_voice_types.py index 346f73132f..8cb919a159 100644 --- a/tests/voice/test_tts_voice_types.py +++ b/tests/voice/test_tts_voice_types.py @@ -1,7 +1,25 @@ -from typing import get_args +from typing import Literal, get_args, get_origin -from agents.voice.model import TTSVoice +import agents.voice as voice +from agents.voice import TTSCustomVoice, TTSModelSettings, TTSVoice + + +def _builtin_voice_values() -> set[str]: + literal_type = next(arg for arg in get_args(TTSVoice) if get_origin(arg) is Literal) + return set(get_args(literal_type)) def test_tts_voice_type_includes_current_openai_builtin_voices() -> None: - assert {"ballad", "verse", "marin", "cedar"} <= set(get_args(TTSVoice)) + assert {"ballad", "verse", "marin", "cedar"} <= _builtin_voice_values() + + +def test_tts_voice_type_accepts_custom_voice_ids() -> None: + custom_voice: TTSCustomVoice = {"id": "voice_1234"} + settings = TTSModelSettings(voice=custom_voice) + + assert TTSCustomVoice in get_args(TTSVoice) + assert settings.voice == {"id": "voice_1234"} + + +def test_tts_custom_voice_is_exported_from_agents_voice() -> None: + assert "TTSCustomVoice" in voice.__all__