Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f150801

Browse files
committedMar 18, 2025·
feat(api): Add speech endpoint (#219)
1 parent c124862 commit f150801

File tree

8 files changed

+467
-1
lines changed

8 files changed

+467
-1
lines changed
 

‎.stats.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
configured_endpoints: 15
1+
configured_endpoints: 16
22
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-055e57d5b3cad069182bb870f76dd7936e69fc88c9a1e51ad198400e38c1d076.yml

‎api.md

+6
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ Methods:
6060

6161
# Audio
6262

63+
## Speech
64+
65+
Methods:
66+
67+
- <code title="post /openai/v1/audio/speech">client.audio.speech.<a href="./src/groq/resources/audio/speech.py">create</a>(\*\*<a href="src/groq/types/audio/speech_create_params.py">params</a>) -> BinaryAPIResponse</code>
68+
6369
## Transcriptions
6470

6571
Types:

‎src/groq/resources/audio/__init__.py

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
AudioWithStreamingResponse,
99
AsyncAudioWithStreamingResponse,
1010
)
11+
from .speech import (
12+
Speech,
13+
AsyncSpeech,
14+
SpeechWithRawResponse,
15+
AsyncSpeechWithRawResponse,
16+
SpeechWithStreamingResponse,
17+
AsyncSpeechWithStreamingResponse,
18+
)
1119
from .translations import (
1220
Translations,
1321
AsyncTranslations,
@@ -26,6 +34,12 @@
2634
)
2735

2836
__all__ = [
37+
"Speech",
38+
"AsyncSpeech",
39+
"SpeechWithRawResponse",
40+
"AsyncSpeechWithRawResponse",
41+
"SpeechWithStreamingResponse",
42+
"AsyncSpeechWithStreamingResponse",
2943
"Transcriptions",
3044
"AsyncTranscriptions",
3145
"TranscriptionsWithRawResponse",

‎src/groq/resources/audio/audio.py

+32
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
from __future__ import annotations
44

5+
from .speech import (
6+
Speech,
7+
AsyncSpeech,
8+
SpeechWithRawResponse,
9+
AsyncSpeechWithRawResponse,
10+
SpeechWithStreamingResponse,
11+
AsyncSpeechWithStreamingResponse,
12+
)
513
from ..._compat import cached_property
614
from ..._resource import SyncAPIResource, AsyncAPIResource
715
from .translations import (
@@ -25,6 +33,10 @@
2533

2634

2735
class Audio(SyncAPIResource):
36+
@cached_property
37+
def speech(self) -> Speech:
38+
return Speech(self._client)
39+
2840
@cached_property
2941
def transcriptions(self) -> Transcriptions:
3042
return Transcriptions(self._client)
@@ -54,6 +66,10 @@ def with_streaming_response(self) -> AudioWithStreamingResponse:
5466

5567

5668
class AsyncAudio(AsyncAPIResource):
69+
@cached_property
70+
def speech(self) -> AsyncSpeech:
71+
return AsyncSpeech(self._client)
72+
5773
@cached_property
5874
def transcriptions(self) -> AsyncTranscriptions:
5975
return AsyncTranscriptions(self._client)
@@ -86,6 +102,10 @@ class AudioWithRawResponse:
86102
def __init__(self, audio: Audio) -> None:
87103
self._audio = audio
88104

105+
@cached_property
106+
def speech(self) -> SpeechWithRawResponse:
107+
return SpeechWithRawResponse(self._audio.speech)
108+
89109
@cached_property
90110
def transcriptions(self) -> TranscriptionsWithRawResponse:
91111
return TranscriptionsWithRawResponse(self._audio.transcriptions)
@@ -99,6 +119,10 @@ class AsyncAudioWithRawResponse:
99119
def __init__(self, audio: AsyncAudio) -> None:
100120
self._audio = audio
101121

122+
@cached_property
123+
def speech(self) -> AsyncSpeechWithRawResponse:
124+
return AsyncSpeechWithRawResponse(self._audio.speech)
125+
102126
@cached_property
103127
def transcriptions(self) -> AsyncTranscriptionsWithRawResponse:
104128
return AsyncTranscriptionsWithRawResponse(self._audio.transcriptions)
@@ -112,6 +136,10 @@ class AudioWithStreamingResponse:
112136
def __init__(self, audio: Audio) -> None:
113137
self._audio = audio
114138

139+
@cached_property
140+
def speech(self) -> SpeechWithStreamingResponse:
141+
return SpeechWithStreamingResponse(self._audio.speech)
142+
115143
@cached_property
116144
def transcriptions(self) -> TranscriptionsWithStreamingResponse:
117145
return TranscriptionsWithStreamingResponse(self._audio.transcriptions)
@@ -125,6 +153,10 @@ class AsyncAudioWithStreamingResponse:
125153
def __init__(self, audio: AsyncAudio) -> None:
126154
self._audio = audio
127155

156+
@cached_property
157+
def speech(self) -> AsyncSpeechWithStreamingResponse:
158+
return AsyncSpeechWithStreamingResponse(self._audio.speech)
159+
128160
@cached_property
129161
def transcriptions(self) -> AsyncTranscriptionsWithStreamingResponse:
130162
return AsyncTranscriptionsWithStreamingResponse(self._audio.transcriptions)

‎src/groq/resources/audio/speech.py

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from __future__ import annotations
4+
5+
from typing_extensions import Literal
6+
7+
import httpx
8+
9+
from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
10+
from ..._utils import (
11+
maybe_transform,
12+
async_maybe_transform,
13+
)
14+
from ..._compat import cached_property
15+
from ..._resource import SyncAPIResource, AsyncAPIResource
16+
from ..._response import (
17+
BinaryAPIResponse,
18+
AsyncBinaryAPIResponse,
19+
StreamedBinaryAPIResponse,
20+
AsyncStreamedBinaryAPIResponse,
21+
to_custom_raw_response_wrapper,
22+
to_custom_streamed_response_wrapper,
23+
async_to_custom_raw_response_wrapper,
24+
async_to_custom_streamed_response_wrapper,
25+
)
26+
from ...types.audio import speech_create_params
27+
from ..._base_client import make_request_options
28+
29+
__all__ = ["Speech", "AsyncSpeech"]
30+
31+
32+
class Speech(SyncAPIResource):
33+
@cached_property
34+
def with_raw_response(self) -> SpeechWithRawResponse:
35+
"""
36+
This property can be used as a prefix for any HTTP method call to return
37+
the raw response object instead of the parsed content.
38+
39+
For more information, see https://www.github.com/groq/groq-python#accessing-raw-response-data-eg-headers
40+
"""
41+
return SpeechWithRawResponse(self)
42+
43+
@cached_property
44+
def with_streaming_response(self) -> SpeechWithStreamingResponse:
45+
"""
46+
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
47+
48+
For more information, see https://www.github.com/groq/groq-python#with_streaming_response
49+
"""
50+
return SpeechWithStreamingResponse(self)
51+
52+
def create(
53+
self,
54+
*,
55+
input: str,
56+
model: str,
57+
voice: str,
58+
response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN,
59+
speed: float | NotGiven = NOT_GIVEN,
60+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
61+
# The extra values given here take precedence over values defined on the client or passed to this method.
62+
extra_headers: Headers | None = None,
63+
extra_query: Query | None = None,
64+
extra_body: Body | None = None,
65+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
66+
) -> BinaryAPIResponse:
67+
"""
68+
Generates audio from the input text.
69+
70+
Args:
71+
input: The text to generate audio for.
72+
73+
model: One of the available TTS models
74+
75+
voice: The voice to use when generating the audio.
76+
77+
response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`,
78+
`wav`, and `pcm`.
79+
80+
speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is
81+
the default.
82+
83+
extra_headers: Send extra headers
84+
85+
extra_query: Add additional query parameters to the request
86+
87+
extra_body: Add additional JSON properties to the request
88+
89+
timeout: Override the client-level default timeout for this request, in seconds
90+
"""
91+
extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
92+
return self._post(
93+
"/openai/v1/audio/speech",
94+
body=maybe_transform(
95+
{
96+
"input": input,
97+
"model": model,
98+
"voice": voice,
99+
"response_format": response_format,
100+
"speed": speed,
101+
},
102+
speech_create_params.SpeechCreateParams,
103+
),
104+
options=make_request_options(
105+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
106+
),
107+
cast_to=BinaryAPIResponse,
108+
)
109+
110+
111+
class AsyncSpeech(AsyncAPIResource):
112+
@cached_property
113+
def with_raw_response(self) -> AsyncSpeechWithRawResponse:
114+
"""
115+
This property can be used as a prefix for any HTTP method call to return
116+
the raw response object instead of the parsed content.
117+
118+
For more information, see https://www.github.com/groq/groq-python#accessing-raw-response-data-eg-headers
119+
"""
120+
return AsyncSpeechWithRawResponse(self)
121+
122+
@cached_property
123+
def with_streaming_response(self) -> AsyncSpeechWithStreamingResponse:
124+
"""
125+
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
126+
127+
For more information, see https://www.github.com/groq/groq-python#with_streaming_response
128+
"""
129+
return AsyncSpeechWithStreamingResponse(self)
130+
131+
async def create(
132+
self,
133+
*,
134+
input: str,
135+
model: str,
136+
voice: str,
137+
response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN,
138+
speed: float | NotGiven = NOT_GIVEN,
139+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
140+
# The extra values given here take precedence over values defined on the client or passed to this method.
141+
extra_headers: Headers | None = None,
142+
extra_query: Query | None = None,
143+
extra_body: Body | None = None,
144+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
145+
) -> AsyncBinaryAPIResponse:
146+
"""
147+
Generates audio from the input text.
148+
149+
Args:
150+
input: The text to generate audio for.
151+
152+
model: One of the available TTS models
153+
154+
voice: The voice to use when generating the audio.
155+
156+
response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, `flac`,
157+
`wav`, and `pcm`.
158+
159+
speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is
160+
the default.
161+
162+
extra_headers: Send extra headers
163+
164+
extra_query: Add additional query parameters to the request
165+
166+
extra_body: Add additional JSON properties to the request
167+
168+
timeout: Override the client-level default timeout for this request, in seconds
169+
"""
170+
extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
171+
return await self._post(
172+
"/openai/v1/audio/speech",
173+
body=await async_maybe_transform(
174+
{
175+
"input": input,
176+
"model": model,
177+
"voice": voice,
178+
"response_format": response_format,
179+
"speed": speed,
180+
},
181+
speech_create_params.SpeechCreateParams,
182+
),
183+
options=make_request_options(
184+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
185+
),
186+
cast_to=AsyncBinaryAPIResponse,
187+
)
188+
189+
190+
class SpeechWithRawResponse:
191+
def __init__(self, speech: Speech) -> None:
192+
self._speech = speech
193+
194+
self.create = to_custom_raw_response_wrapper(
195+
speech.create,
196+
BinaryAPIResponse,
197+
)
198+
199+
200+
class AsyncSpeechWithRawResponse:
201+
def __init__(self, speech: AsyncSpeech) -> None:
202+
self._speech = speech
203+
204+
self.create = async_to_custom_raw_response_wrapper(
205+
speech.create,
206+
AsyncBinaryAPIResponse,
207+
)
208+
209+
210+
class SpeechWithStreamingResponse:
211+
def __init__(self, speech: Speech) -> None:
212+
self._speech = speech
213+
214+
self.create = to_custom_streamed_response_wrapper(
215+
speech.create,
216+
StreamedBinaryAPIResponse,
217+
)
218+
219+
220+
class AsyncSpeechWithStreamingResponse:
221+
def __init__(self, speech: AsyncSpeech) -> None:
222+
self._speech = speech
223+
224+
self.create = async_to_custom_streamed_response_wrapper(
225+
speech.create,
226+
AsyncStreamedBinaryAPIResponse,
227+
)

‎src/groq/types/audio/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44

55
from .translation import Translation as Translation
66
from .transcription import Transcription as Transcription
7+
from .speech_create_params import SpeechCreateParams as SpeechCreateParams
78
from .translation_create_params import TranslationCreateParams as TranslationCreateParams
89
from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from __future__ import annotations
4+
5+
from typing_extensions import Literal, Required, TypedDict
6+
7+
__all__ = ["SpeechCreateParams"]
8+
9+
10+
class SpeechCreateParams(TypedDict, total=False):
11+
input: Required[str]
12+
"""The text to generate audio for."""
13+
14+
model: Required[str]
15+
"""One of the available TTS models"""
16+
17+
voice: Required[str]
18+
"""The voice to use when generating the audio."""
19+
20+
response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"]
21+
"""The format to audio in.
22+
23+
Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`.
24+
"""
25+
26+
speed: float
27+
"""The speed of the generated audio.
28+
29+
Select a value from `0.25` to `4.0`. `1.0` is the default.
30+
"""
+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from __future__ import annotations
4+
5+
import os
6+
from typing import Any, cast
7+
8+
import httpx
9+
import pytest
10+
from respx import MockRouter
11+
12+
from groq import Groq, AsyncGroq
13+
from groq._response import (
14+
BinaryAPIResponse,
15+
AsyncBinaryAPIResponse,
16+
StreamedBinaryAPIResponse,
17+
AsyncStreamedBinaryAPIResponse,
18+
)
19+
20+
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
21+
22+
23+
class TestSpeech:
24+
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
25+
26+
@parametrize
27+
@pytest.mark.respx(base_url=base_url)
28+
def test_method_create(self, client: Groq, respx_mock: MockRouter) -> None:
29+
respx_mock.post("/openai/v1/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
30+
speech = client.audio.speech.create(
31+
input="input",
32+
model="model",
33+
voice="voice",
34+
)
35+
assert speech.is_closed
36+
assert speech.json() == {"foo": "bar"}
37+
assert cast(Any, speech.is_closed) is True
38+
assert isinstance(speech, BinaryAPIResponse)
39+
40+
@parametrize
41+
@pytest.mark.respx(base_url=base_url)
42+
def test_method_create_with_all_params(self, client: Groq, respx_mock: MockRouter) -> None:
43+
respx_mock.post("/openai/v1/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
44+
speech = client.audio.speech.create(
45+
input="input",
46+
model="model",
47+
voice="voice",
48+
response_format="mp3",
49+
speed=0.25,
50+
)
51+
assert speech.is_closed
52+
assert speech.json() == {"foo": "bar"}
53+
assert cast(Any, speech.is_closed) is True
54+
assert isinstance(speech, BinaryAPIResponse)
55+
56+
@parametrize
57+
@pytest.mark.respx(base_url=base_url)
58+
def test_raw_response_create(self, client: Groq, respx_mock: MockRouter) -> None:
59+
respx_mock.post("/openai/v1/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
60+
61+
speech = client.audio.speech.with_raw_response.create(
62+
input="input",
63+
model="model",
64+
voice="voice",
65+
)
66+
67+
assert speech.is_closed is True
68+
assert speech.http_request.headers.get("X-Stainless-Lang") == "python"
69+
assert speech.json() == {"foo": "bar"}
70+
assert isinstance(speech, BinaryAPIResponse)
71+
72+
@parametrize
73+
@pytest.mark.respx(base_url=base_url)
74+
def test_streaming_response_create(self, client: Groq, respx_mock: MockRouter) -> None:
75+
respx_mock.post("/openai/v1/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
76+
with client.audio.speech.with_streaming_response.create(
77+
input="input",
78+
model="model",
79+
voice="voice",
80+
) as speech:
81+
assert not speech.is_closed
82+
assert speech.http_request.headers.get("X-Stainless-Lang") == "python"
83+
84+
assert speech.json() == {"foo": "bar"}
85+
assert cast(Any, speech.is_closed) is True
86+
assert isinstance(speech, StreamedBinaryAPIResponse)
87+
88+
assert cast(Any, speech.is_closed) is True
89+
90+
91+
class TestAsyncSpeech:
92+
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
93+
94+
@parametrize
95+
@pytest.mark.respx(base_url=base_url)
96+
async def test_method_create(self, async_client: AsyncGroq, respx_mock: MockRouter) -> None:
97+
respx_mock.post("/openai/v1/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
98+
speech = await async_client.audio.speech.create(
99+
input="input",
100+
model="model",
101+
voice="voice",
102+
)
103+
assert speech.is_closed
104+
assert await speech.json() == {"foo": "bar"}
105+
assert cast(Any, speech.is_closed) is True
106+
assert isinstance(speech, AsyncBinaryAPIResponse)
107+
108+
@parametrize
109+
@pytest.mark.respx(base_url=base_url)
110+
async def test_method_create_with_all_params(self, async_client: AsyncGroq, respx_mock: MockRouter) -> None:
111+
respx_mock.post("/openai/v1/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
112+
speech = await async_client.audio.speech.create(
113+
input="input",
114+
model="model",
115+
voice="voice",
116+
response_format="mp3",
117+
speed=0.25,
118+
)
119+
assert speech.is_closed
120+
assert await speech.json() == {"foo": "bar"}
121+
assert cast(Any, speech.is_closed) is True
122+
assert isinstance(speech, AsyncBinaryAPIResponse)
123+
124+
@parametrize
125+
@pytest.mark.respx(base_url=base_url)
126+
async def test_raw_response_create(self, async_client: AsyncGroq, respx_mock: MockRouter) -> None:
127+
respx_mock.post("/openai/v1/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
128+
129+
speech = await async_client.audio.speech.with_raw_response.create(
130+
input="input",
131+
model="model",
132+
voice="voice",
133+
)
134+
135+
assert speech.is_closed is True
136+
assert speech.http_request.headers.get("X-Stainless-Lang") == "python"
137+
assert await speech.json() == {"foo": "bar"}
138+
assert isinstance(speech, AsyncBinaryAPIResponse)
139+
140+
@parametrize
141+
@pytest.mark.respx(base_url=base_url)
142+
async def test_streaming_response_create(self, async_client: AsyncGroq, respx_mock: MockRouter) -> None:
143+
respx_mock.post("/openai/v1/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
144+
async with async_client.audio.speech.with_streaming_response.create(
145+
input="input",
146+
model="model",
147+
voice="voice",
148+
) as speech:
149+
assert not speech.is_closed
150+
assert speech.http_request.headers.get("X-Stainless-Lang") == "python"
151+
152+
assert await speech.json() == {"foo": "bar"}
153+
assert cast(Any, speech.is_closed) is True
154+
assert isinstance(speech, AsyncStreamedBinaryAPIResponse)
155+
156+
assert cast(Any, speech.is_closed) is True

0 commit comments

Comments
 (0)
Please sign in to comment.