Skip to content

Commit 51db0c1

Browse files
Add voices support to the sdk (#391)
1 parent 9806cf1 commit 51db0c1

File tree

5 files changed

+215
-1
lines changed

5 files changed

+215
-1
lines changed

src/together/resources/audio/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from together.resources.audio.speech import AsyncSpeech, Speech
44
from together.resources.audio.transcriptions import AsyncTranscriptions, Transcriptions
55
from together.resources.audio.translations import AsyncTranslations, Translations
6+
from together.resources.audio.voices import AsyncVoices, Voices
67
from together.types import (
78
TogetherClient,
89
)
@@ -24,6 +25,10 @@ def transcriptions(self) -> Transcriptions:
2425
def translations(self) -> Translations:
2526
return Translations(self._client)
2627

28+
@cached_property
29+
def voices(self) -> Voices:
30+
return Voices(self._client)
31+
2732

2833
class AsyncAudio:
2934
def __init__(self, client: TogetherClient) -> None:
@@ -40,3 +45,7 @@ def transcriptions(self) -> AsyncTranscriptions:
4045
@cached_property
4146
def translations(self) -> AsyncTranslations:
4247
return AsyncTranslations(self._client)
48+
49+
@cached_property
50+
def voices(self) -> AsyncVoices:
51+
return AsyncVoices(self._client)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from __future__ import annotations
2+
3+
from together.abstract import api_requestor
4+
from together.together_response import TogetherResponse
5+
from together.types import (
6+
TogetherClient,
7+
TogetherRequest,
8+
VoiceListResponse,
9+
)
10+
11+
12+
class Voices:
13+
def __init__(self, client: TogetherClient) -> None:
14+
self._client = client
15+
16+
def list(self) -> VoiceListResponse:
17+
"""
18+
Method to return list of available voices on the API
19+
20+
Returns:
21+
VoiceListResponse: Response containing models and their available voices
22+
"""
23+
requestor = api_requestor.APIRequestor(
24+
client=self._client,
25+
)
26+
27+
response, _, _ = requestor.request(
28+
options=TogetherRequest(
29+
method="GET",
30+
url="voices",
31+
),
32+
stream=False,
33+
)
34+
35+
assert isinstance(response, TogetherResponse)
36+
37+
return VoiceListResponse(**response.data)
38+
39+
40+
class AsyncVoices:
41+
def __init__(self, client: TogetherClient) -> None:
42+
self._client = client
43+
44+
async def list(self) -> VoiceListResponse:
45+
"""
46+
Async method to return list of available voices on the API
47+
48+
Returns:
49+
VoiceListResponse: Response containing models and their available voices
50+
"""
51+
requestor = api_requestor.APIRequestor(
52+
client=self._client,
53+
)
54+
55+
response, _, _ = await requestor.arequest(
56+
options=TogetherRequest(
57+
method="GET",
58+
url="voices",
59+
),
60+
stream=False,
61+
)
62+
63+
assert isinstance(response, TogetherResponse)
64+
65+
return VoiceListResponse(**response.data)

src/together/types/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
AudioTranslationVerboseResponse,
1616
AudioTranscriptionResponseFormat,
1717
AudioTimestampGranularities,
18+
ModelVoices,
19+
VoiceListResponse,
1820
)
1921
from together.types.chat_completions import (
2022
ChatCompletionChunk,
@@ -140,6 +142,8 @@
140142
"AudioTranslationVerboseResponse",
141143
"AudioTranscriptionResponseFormat",
142144
"AudioTimestampGranularities",
145+
"ModelVoices",
146+
"VoiceListResponse",
143147
"DedicatedEndpoint",
144148
"ListEndpoint",
145149
"Autoscaling",

src/together/types/audio_speech.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import base64
44
from enum import Enum
5-
from typing import BinaryIO, Iterator, List, Optional, Union
5+
from re import S
6+
from typing import BinaryIO, Dict, Iterator, List, Optional, Union
67

78
from pydantic import BaseModel, ConfigDict
89

@@ -295,3 +296,16 @@ class AudioTranslationVerboseResponse(BaseModel):
295296
text: str
296297
segments: Optional[List[AudioTranscriptionSegment]] = None
297298
words: Optional[List[AudioTranscriptionWord]] = None
299+
300+
301+
class ModelVoices(BaseModel):
302+
"""Represents a model with its available voices."""
303+
304+
model: str
305+
voices: List[Dict[str, str]] # Each voice is a dict with 'name' key
306+
307+
308+
class VoiceListResponse(BaseModel):
309+
"""Response containing a list of models and their available voices."""
310+
311+
data: List[ModelVoices]
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import os
2+
3+
import pytest
4+
5+
from together.client import AsyncTogether, Together
6+
from together.types.audio_speech import ModelVoices, VoiceListResponse
7+
8+
9+
class TestTogetherVoices:
10+
@pytest.fixture
11+
def sync_together_client(self) -> Together:
12+
"""
13+
Initialize sync client with API key from environment
14+
"""
15+
TOGETHER_API_KEY = os.getenv("TOGETHER_API_KEY")
16+
return Together(api_key=TOGETHER_API_KEY)
17+
18+
@pytest.fixture
19+
def async_together_client(self) -> AsyncTogether:
20+
"""
21+
Initialize async client with API key from environment
22+
"""
23+
TOGETHER_API_KEY = os.getenv("TOGETHER_API_KEY")
24+
return AsyncTogether(api_key=TOGETHER_API_KEY)
25+
26+
def test_sync_voices_list(self, sync_together_client):
27+
"""
28+
Test sync voices list endpoint
29+
"""
30+
response = sync_together_client.audio.voices.list()
31+
32+
# Verify response type
33+
assert isinstance(response, VoiceListResponse)
34+
35+
# Verify data structure
36+
assert hasattr(response, "data")
37+
assert isinstance(response.data, list)
38+
assert len(response.data) > 0
39+
40+
# Verify each model has the correct structure
41+
for model_voices in response.data:
42+
assert isinstance(model_voices, ModelVoices)
43+
assert hasattr(model_voices, "model")
44+
assert isinstance(model_voices.model, str)
45+
assert len(model_voices.model) > 0
46+
print(model_voices)
47+
assert hasattr(model_voices, "voices")
48+
assert isinstance(model_voices.voices, list)
49+
assert len(model_voices.voices) > 0
50+
51+
# Verify each voice has a name
52+
for voice in model_voices.voices:
53+
assert isinstance(voice, dict)
54+
assert "name" in voice
55+
assert isinstance(voice["name"], str)
56+
assert len(voice["name"]) > 0
57+
58+
@pytest.mark.asyncio
59+
async def test_async_voices_list(self, async_together_client):
60+
"""
61+
Test async voices list endpoint
62+
"""
63+
response = await async_together_client.audio.voices.list()
64+
65+
# Verify response type
66+
assert isinstance(response, VoiceListResponse)
67+
68+
# Verify data structure
69+
assert hasattr(response, "data")
70+
assert isinstance(response.data, list)
71+
assert len(response.data) > 0
72+
73+
# Verify each model has the correct structure
74+
for model_voices in response.data:
75+
assert isinstance(model_voices, ModelVoices)
76+
assert hasattr(model_voices, "model")
77+
assert isinstance(model_voices.model, str)
78+
assert len(model_voices.model) > 0
79+
print(model_voices)
80+
assert hasattr(model_voices, "voices")
81+
assert isinstance(model_voices.voices, list)
82+
assert len(model_voices.voices) > 0
83+
84+
# Verify each voice has a name
85+
for voice in model_voices.voices:
86+
assert isinstance(voice, dict)
87+
assert "name" in voice
88+
assert isinstance(voice["name"], str)
89+
assert len(voice["name"]) > 0
90+
91+
def test_sync_voices_content(self, sync_together_client):
92+
"""
93+
Test that sync voices list returns expected models
94+
"""
95+
response = sync_together_client.audio.voices.list()
96+
97+
# Get list of model names
98+
model_names = [model_voices.model for model_voices in response.data]
99+
100+
# Verify we have at least some known models
101+
assert len(model_names) > 0
102+
103+
# Check that each model has at least one voice
104+
for model_voices in response.data:
105+
assert len(model_voices.voices) > 0
106+
107+
@pytest.mark.asyncio
108+
async def test_async_voices_content(self, async_together_client):
109+
"""
110+
Test that async voices list returns expected models
111+
"""
112+
response = await async_together_client.audio.voices.list()
113+
114+
# Get list of model names
115+
model_names = [model_voices.model for model_voices in response.data]
116+
117+
# Verify we have at least some known models
118+
assert len(model_names) > 0
119+
120+
# Check that each model has at least one voice
121+
for model_voices in response.data:
122+
assert len(model_voices.voices) > 0

0 commit comments

Comments
 (0)