cartesia 1.0.0__py2.py3-none-any.whl → 1.0.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cartesia/_types.py +44 -6
- cartesia/client.py +39 -24
- cartesia/version.py +1 -1
- {cartesia-1.0.0.dist-info → cartesia-1.0.1.dist-info}/METADATA +79 -28
- cartesia-1.0.1.dist-info/RECORD +8 -0
- cartesia/utils.py +0 -87
- cartesia-1.0.0.dist-info/RECORD +0 -9
- {cartesia-1.0.0.dist-info → cartesia-1.0.1.dist-info}/WHEEL +0 -0
- {cartesia-1.0.0.dist-info → cartesia-1.0.1.dist-info}/top_level.txt +0 -0
cartesia/_types.py
CHANGED
@@ -1,26 +1,63 @@
|
|
1
1
|
from typing import List, TypedDict
|
2
|
+
from cartesia.utils.deprecated import deprecated
|
3
|
+
|
2
4
|
|
3
5
|
class OutputFormatMapping:
|
6
|
+
_format_mapping = {
|
7
|
+
"raw_pcm_f32le_44100": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 44100},
|
8
|
+
"raw_pcm_s16le_44100": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 44100},
|
9
|
+
"raw_pcm_f32le_24000": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 24000},
|
10
|
+
"raw_pcm_s16le_24000": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 24000},
|
11
|
+
"raw_pcm_f32le_22050": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 22050},
|
12
|
+
"raw_pcm_s16le_22050": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 22050},
|
13
|
+
"raw_pcm_f32le_16000": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 16000},
|
14
|
+
"raw_pcm_s16le_16000": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 16000},
|
15
|
+
"raw_pcm_f32le_8000": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 8000},
|
16
|
+
"raw_pcm_s16le_8000": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 8000},
|
17
|
+
"raw_pcm_mulaw_8000": {"container": "raw", "encoding": "pcm_mulaw", "sample_rate": 8000},
|
18
|
+
"raw_pcm_alaw_8000": {"container": "raw", "encoding": "pcm_alaw", "sample_rate": 8000},
|
19
|
+
}
|
20
|
+
|
21
|
+
@classmethod
|
22
|
+
def get_format(cls, format_name):
|
23
|
+
if format_name in cls._format_mapping:
|
24
|
+
return cls._format_mapping[format_name]
|
25
|
+
else:
|
26
|
+
raise ValueError(f"Unsupported format: {format_name}")
|
27
|
+
|
28
|
+
|
29
|
+
class DeprecatedOutputFormatMapping:
|
30
|
+
"""Deprecated formats as of v1.0.1. These will be removed in v1.2.0. Use :class:`OutputFormatMapping` instead."""
|
31
|
+
|
4
32
|
_format_mapping = {
|
5
33
|
"fp32": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 44100},
|
6
34
|
"pcm": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 44100},
|
35
|
+
"fp32_8000": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 8000},
|
7
36
|
"fp32_16000": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 16000},
|
8
37
|
"fp32_22050": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 22050},
|
38
|
+
"fp32_24000": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 24000},
|
9
39
|
"fp32_44100": {"container": "raw", "encoding": "pcm_f32le", "sample_rate": 44100},
|
40
|
+
"pcm_8000": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 8000},
|
10
41
|
"pcm_16000": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 16000},
|
11
42
|
"pcm_22050": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 22050},
|
43
|
+
"pcm_24000": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 24000},
|
12
44
|
"pcm_44100": {"container": "raw", "encoding": "pcm_s16le", "sample_rate": 44100},
|
13
45
|
"mulaw_8000": {"container": "raw", "encoding": "pcm_mulaw", "sample_rate": 8000},
|
14
46
|
"alaw_8000": {"container": "raw", "encoding": "pcm_alaw", "sample_rate": 8000},
|
15
47
|
}
|
16
48
|
|
17
|
-
@
|
18
|
-
|
19
|
-
|
20
|
-
|
49
|
+
@deprecated(
|
50
|
+
vdeprecated="1.0.1",
|
51
|
+
vremove="1.2.0",
|
52
|
+
reason="Old output format names are being deprecated in favor of names aligned with the Cartesia API. Use names from `OutputFormatMapping` instead.",
|
53
|
+
)
|
54
|
+
def get_format_deprecated(self, format_name):
|
55
|
+
if format_name in self._format_mapping:
|
56
|
+
return self._format_mapping[format_name]
|
21
57
|
else:
|
22
58
|
raise ValueError(f"Unsupported format: {format_name}")
|
23
|
-
|
59
|
+
|
60
|
+
|
24
61
|
class VoiceMetadata(TypedDict):
|
25
62
|
id: str
|
26
63
|
name: str
|
@@ -30,7 +67,8 @@ class VoiceMetadata(TypedDict):
|
|
30
67
|
user_id: str
|
31
68
|
created_at: str
|
32
69
|
language: str
|
33
|
-
|
70
|
+
|
71
|
+
|
34
72
|
class OutputFormat(TypedDict):
|
35
73
|
container: str
|
36
74
|
encoding: str
|
cartesia/client.py
CHANGED
@@ -12,10 +12,12 @@ import logging
|
|
12
12
|
import requests
|
13
13
|
from websockets.sync.client import connect
|
14
14
|
|
15
|
-
from cartesia.utils import retry_on_connection_error, retry_on_connection_error_async
|
15
|
+
from cartesia.utils.retry import retry_on_connection_error, retry_on_connection_error_async
|
16
|
+
from cartesia.utils.deprecated import deprecated
|
16
17
|
from cartesia._types import (
|
17
18
|
OutputFormat,
|
18
19
|
OutputFormatMapping,
|
20
|
+
DeprecatedOutputFormatMapping,
|
19
21
|
VoiceMetadata,
|
20
22
|
)
|
21
23
|
|
@@ -131,14 +133,7 @@ class Voices(Resource):
|
|
131
133
|
"""List all voices in your voice library.
|
132
134
|
|
133
135
|
Returns:
|
134
|
-
This method returns a list of VoiceMetadata objects
|
135
|
-
- id: The ID of the voice.
|
136
|
-
- name: The name of the voice.
|
137
|
-
- description: The description of the voice.
|
138
|
-
- embedding: The embedding of the voice.
|
139
|
-
- is_public: Whether the voice is public.
|
140
|
-
- user_id: The ID of the user who created the voice.
|
141
|
-
- created_at: The timestamp (str) when the voice was created.
|
136
|
+
This method returns a list of VoiceMetadata objects.
|
142
137
|
"""
|
143
138
|
response = httpx.get(
|
144
139
|
f"{self._http_url()}/voices",
|
@@ -159,14 +154,7 @@ class Voices(Resource):
|
|
159
154
|
id: The ID of the voice.
|
160
155
|
|
161
156
|
Returns:
|
162
|
-
A
|
163
|
-
- id: The ID of the voice.
|
164
|
-
- name: The name of the voice.
|
165
|
-
- description: The description of the voice.
|
166
|
-
- embedding: The embedding of the voice as a list of floats.
|
167
|
-
- is_public: Whether the voice is public.
|
168
|
-
- user_id: The ID of the user who created the voice.
|
169
|
-
- created_at: The timestamp when the voice was created.
|
157
|
+
A VoiceMetadata object containing the voice metadata.
|
170
158
|
"""
|
171
159
|
url = f"{self._http_url()}/voices/{id}"
|
172
160
|
response = httpx.get(url, headers=self.headers, timeout=self.timeout)
|
@@ -344,8 +332,11 @@ class _WebSocket:
|
|
344
332
|
stream: Whether to stream the audio or not. (Default is True)
|
345
333
|
|
346
334
|
Returns:
|
347
|
-
If `stream` is True, the method returns a generator that yields chunks
|
348
|
-
If `stream` is False, the method returns a dictionary
|
335
|
+
If `stream` is True, the method returns a generator that yields chunks. Each chunk is a dictionary.
|
336
|
+
If `stream` is False, the method returns a dictionary.
|
337
|
+
Both the generator and the dictionary contain the following key(s):
|
338
|
+
- audio: The audio as bytes.
|
339
|
+
- context_id: The context ID for the request.
|
349
340
|
"""
|
350
341
|
self.connect()
|
351
342
|
|
@@ -490,8 +481,10 @@ class _SSE:
|
|
490
481
|
stream: Whether to stream the audio or not.
|
491
482
|
|
492
483
|
Returns:
|
493
|
-
If `stream` is True, the method returns a generator that yields chunks. Each chunk is a dictionary
|
494
|
-
If `stream` is False, the method returns a dictionary
|
484
|
+
If `stream` is True, the method returns a generator that yields chunks. Each chunk is a dictionary.
|
485
|
+
If `stream` is False, the method returns a dictionary.
|
486
|
+
Both the generator and the dictionary contain the following key(s):
|
487
|
+
- audio: The audio as bytes.
|
495
488
|
"""
|
496
489
|
voice = self._validate_and_construct_voice(voice_id, voice_embedding)
|
497
490
|
|
@@ -581,15 +574,26 @@ class TTS(Resource):
|
|
581
574
|
return ws
|
582
575
|
|
583
576
|
def get_output_format(self, output_format_name: str) -> OutputFormat:
|
584
|
-
"""Convenience method to get the output_format
|
577
|
+
"""Convenience method to get the output_format dictionary from a given output format name.
|
585
578
|
|
586
579
|
Args:
|
587
580
|
output_format_name (str): The name of the output format.
|
588
581
|
|
589
582
|
Returns:
|
590
583
|
OutputFormat: A dictionary containing the details of the output format to be passed into tts.sse() or tts.websocket().send()
|
584
|
+
|
585
|
+
Raises:
|
586
|
+
ValueError: If the output_format name is not supported
|
591
587
|
"""
|
592
|
-
|
588
|
+
if output_format_name in OutputFormatMapping._format_mapping:
|
589
|
+
output_format_obj = OutputFormatMapping.get_format(output_format_name)
|
590
|
+
elif output_format_name in DeprecatedOutputFormatMapping._format_mapping:
|
591
|
+
output_format_obj = DeprecatedOutputFormatMapping.get_format_deprecated(
|
592
|
+
output_format_name
|
593
|
+
)
|
594
|
+
else:
|
595
|
+
raise ValueError(f"Unsupported format: {output_format_name}")
|
596
|
+
|
593
597
|
return OutputFormat(
|
594
598
|
container=output_format_obj["container"],
|
595
599
|
encoding=output_format_obj["encoding"],
|
@@ -604,8 +608,19 @@ class TTS(Resource):
|
|
604
608
|
|
605
609
|
Returns:
|
606
610
|
int: The sample rate for the output format.
|
611
|
+
|
612
|
+
Raises:
|
613
|
+
ValueError: If the output_format name is not supported
|
607
614
|
"""
|
608
|
-
|
615
|
+
if output_format_name in OutputFormatMapping._format_mapping:
|
616
|
+
output_format_obj = OutputFormatMapping.get_format(output_format_name)
|
617
|
+
elif output_format_name in DeprecatedOutputFormatMapping._format_mapping:
|
618
|
+
output_format_obj = DeprecatedOutputFormatMapping.get_format_deprecated(
|
619
|
+
output_format_name
|
620
|
+
)
|
621
|
+
else:
|
622
|
+
raise ValueError(f"Unsupported format: {output_format_name}")
|
623
|
+
|
609
624
|
return output_format_obj["sample_rate"]
|
610
625
|
|
611
626
|
|
cartesia/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "1.0.
|
1
|
+
__version__ = "1.0.1"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: cartesia
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.1
|
4
4
|
Summary: The official Python library for the Cartesia API.
|
5
5
|
Home-page:
|
6
6
|
Author: Cartesia, Inc.
|
@@ -38,10 +38,8 @@ Requires-Dist: numpy ; extra == 'dev'
|
|
38
38
|
|
39
39
|
The official Cartesia Python library which provides convenient access to the Cartesia REST and Websocket API from any Python 3.8+ application.
|
40
40
|
|
41
|
-
**Note:** This API is still in alpha. Please expect breaking changes and report any issues you encounter.
|
42
|
-
|
43
41
|
> [!IMPORTANT]
|
44
|
-
> The client library introduces breaking changes in v1.0.0, which was released on June 24th 2024. See the [release notes](https://github.com/cartesia-ai/cartesia-python/discussions/44)
|
42
|
+
> The client library introduces breaking changes in v1.0.0, which was released on June 24th 2024. See the [release notes](https://github.com/cartesia-ai/cartesia-python/releases/tag/v1.0.0) and [migration guide](https://github.com/cartesia-ai/cartesia-python/discussions/44). Reach out to us on [Discord](https://discord.gg/ZVxavqHB9X) for any support requests!
|
45
43
|
|
46
44
|
## Documentation
|
47
45
|
|
@@ -76,7 +74,11 @@ print("The embedding for", voice["name"], "is", voice["embedding"])
|
|
76
74
|
cloned_voice_embedding = client.voices.clone(filepath="path/to/voice")
|
77
75
|
|
78
76
|
# Create a new voice
|
79
|
-
new_voice = client.voices.create(
|
77
|
+
new_voice = client.voices.create(
|
78
|
+
name="New Voice",
|
79
|
+
description="A clone of my own voice",
|
80
|
+
embedding=cloned_voice_embedding,
|
81
|
+
)
|
80
82
|
```
|
81
83
|
|
82
84
|
## Text-to-Speech
|
@@ -111,14 +113,17 @@ rate = 44100
|
|
111
113
|
stream = None
|
112
114
|
|
113
115
|
# Generate and stream audio
|
114
|
-
for output in client.tts.sse(
|
116
|
+
for output in client.tts.sse(
|
117
|
+
model_id=model_id,
|
118
|
+
transcript=transcript,
|
119
|
+
voice_embedding=voice["embedding"],
|
120
|
+
stream=True,
|
121
|
+
output_format=output_format,
|
122
|
+
):
|
115
123
|
buffer = output["audio"]
|
116
124
|
|
117
125
|
if not stream:
|
118
|
-
stream = p.open(format=pyaudio.paFloat32,
|
119
|
-
channels=1,
|
120
|
-
rate=rate,
|
121
|
-
output=True)
|
126
|
+
stream = p.open(format=pyaudio.paFloat32, channels=1, rate=rate, output=True)
|
122
127
|
|
123
128
|
# Write the audio data to the stream
|
124
129
|
stream.write(buffer)
|
@@ -136,6 +141,7 @@ import asyncio
|
|
136
141
|
import pyaudio
|
137
142
|
import os
|
138
143
|
|
144
|
+
|
139
145
|
async def write_stream():
|
140
146
|
client = AsyncCartesia(api_key=os.environ.get("CARTESIA_API_KEY"))
|
141
147
|
voice_name = "Barbershop Man"
|
@@ -158,15 +164,19 @@ async def write_stream():
|
|
158
164
|
stream = None
|
159
165
|
|
160
166
|
# Generate and stream audio
|
161
|
-
async for output in await client.tts.sse(
|
167
|
+
async for output in await client.tts.sse(
|
168
|
+
model_id=model_id,
|
169
|
+
transcript=transcript,
|
170
|
+
voice_embedding=voice["embedding"],
|
171
|
+
stream=True,
|
172
|
+
output_format=output_format,
|
162
173
|
):
|
163
174
|
buffer = output["audio"]
|
164
175
|
|
165
176
|
if not stream:
|
166
|
-
stream = p.open(
|
167
|
-
|
168
|
-
|
169
|
-
output=True)
|
177
|
+
stream = p.open(
|
178
|
+
format=pyaudio.paFloat32, channels=1, rate=rate, output=True
|
179
|
+
)
|
170
180
|
|
171
181
|
# Write the audio data to the stream
|
172
182
|
stream.write(buffer)
|
@@ -174,6 +184,8 @@ async def write_stream():
|
|
174
184
|
stream.stop_stream()
|
175
185
|
stream.close()
|
176
186
|
p.terminate()
|
187
|
+
await client.close()
|
188
|
+
|
177
189
|
|
178
190
|
asyncio.run(write_stream())
|
179
191
|
```
|
@@ -210,14 +222,17 @@ stream = None
|
|
210
222
|
ws = client.tts.websocket()
|
211
223
|
|
212
224
|
# Generate and stream audio using the websocket
|
213
|
-
for output in ws.send(
|
225
|
+
for output in ws.send(
|
226
|
+
model_id=model_id,
|
227
|
+
transcript=transcript,
|
228
|
+
voice_embedding=voice["embedding"],
|
229
|
+
stream=True,
|
230
|
+
output_format=output_format,
|
231
|
+
):
|
214
232
|
buffer = output["audio"]
|
215
233
|
|
216
234
|
if not stream:
|
217
|
-
stream = p.open(format=pyaudio.paFloat32,
|
218
|
-
channels=1,
|
219
|
-
rate=rate,
|
220
|
-
output=True)
|
235
|
+
stream = p.open(format=pyaudio.paFloat32, channels=1, rate=rate, output=True)
|
221
236
|
|
222
237
|
# Write the audio data to the stream
|
223
238
|
stream.write(buffer)
|
@@ -226,7 +241,7 @@ stream.stop_stream()
|
|
226
241
|
stream.close()
|
227
242
|
p.terminate()
|
228
243
|
|
229
|
-
ws.close()
|
244
|
+
ws.close() # Close the websocket connection
|
230
245
|
```
|
231
246
|
|
232
247
|
### Multilingual Text-to-Speech [Alpha]
|
@@ -262,14 +277,18 @@ rate = 44100
|
|
262
277
|
stream = None
|
263
278
|
|
264
279
|
# Pass in the corresponding language code to the `language` parameter to generate and stream audio.
|
265
|
-
for output in client.tts.sse(
|
280
|
+
for output in client.tts.sse(
|
281
|
+
model_id=model_id,
|
282
|
+
transcript=transcript,
|
283
|
+
voice_embedding=voice["embedding"],
|
284
|
+
stream=True,
|
285
|
+
output_format=output_format,
|
286
|
+
language=language,
|
287
|
+
):
|
266
288
|
buffer = output["audio"]
|
267
289
|
|
268
290
|
if not stream:
|
269
|
-
stream = p.open(format=pyaudio.paFloat32,
|
270
|
-
channels=1,
|
271
|
-
rate=rate,
|
272
|
-
output=True)
|
291
|
+
stream = p.open(format=pyaudio.paFloat32, channels=1, rate=rate, output=True)
|
273
292
|
|
274
293
|
stream.write(buffer)
|
275
294
|
|
@@ -304,7 +323,12 @@ with Cartesia(api_key=os.environ.get("CARTESIA_API_KEY")) as client:
|
|
304
323
|
audio_data = io.BytesIO()
|
305
324
|
|
306
325
|
# Generate and stream audio
|
307
|
-
for output in client.tts.sse(
|
326
|
+
for output in client.tts.sse(
|
327
|
+
model_id="sonic-english",
|
328
|
+
transcript=transcript,
|
329
|
+
voice_embedding=voice["embedding"],
|
330
|
+
stream=True,
|
331
|
+
output_format=output_format,
|
308
332
|
):
|
309
333
|
buffer = output["audio"]
|
310
334
|
audio_data.write(buffer)
|
@@ -343,7 +367,12 @@ async with AsyncCartesia(api_key=os.environ.get("CARTESIA_API_KEY")) as client:
|
|
343
367
|
audio_data = io.BytesIO()
|
344
368
|
|
345
369
|
# Generate and stream audio
|
346
|
-
async for output in client.tts.sse(
|
370
|
+
async for output in client.tts.sse(
|
371
|
+
model_id="sonic-english",
|
372
|
+
transcript=transcript,
|
373
|
+
voice_id=voice_id,
|
374
|
+
stream=True,
|
375
|
+
output_format=output_format,
|
347
376
|
):
|
348
377
|
buffer = output["audio"]
|
349
378
|
audio_data.write(buffer)
|
@@ -358,6 +387,28 @@ audio = Audio(np.frombuffer(audio_data.read(), dtype=np.float32), rate=rate)
|
|
358
387
|
display(audio)
|
359
388
|
```
|
360
389
|
|
390
|
+
### Utility methods
|
391
|
+
|
392
|
+
#### Output Formats
|
393
|
+
|
394
|
+
You can use the `client.tts.get_output_format` method to convert string-based output format names into the `output_format` dictionary which is expected by the `output_format` parameter. You can see the `OutputFormatMapping` class in `cartesia._types` for the currently supported output format names. You can also view the currently supported `output_format`s in our [API Reference](https://docs.cartesia.ai/api-reference/endpoints/stream-speech-server-sent-events).
|
395
|
+
|
396
|
+
The previously used `output_format` strings are now deprecated and will be removed in v1.2.0. These are listed in the `DeprecatedOutputFormatMapping` class in `cartesia._types`.
|
397
|
+
|
398
|
+
```python
|
399
|
+
# Get the output format dictionary from string name
|
400
|
+
output_format = client.tts.get_output_format("raw_pcm_f32le_44100")
|
401
|
+
|
402
|
+
# Pass in the output format dictionary to generate and stream audio
|
403
|
+
generator = client.tts.sse(
|
404
|
+
model_id=model,
|
405
|
+
transcript=transcript,
|
406
|
+
voice_id=SAMPLE_VOICE_ID,
|
407
|
+
stream=True,
|
408
|
+
output_format=output_format,
|
409
|
+
)
|
410
|
+
```
|
411
|
+
|
361
412
|
To avoid storing your API key in the source code, we recommend doing one of the following:
|
362
413
|
|
363
414
|
1. Use [`python-dotenv`](https://pypi.org/project/python-dotenv/) to add `CARTESIA_API_KEY="my-api-key"` to your .env file.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
cartesia/__init__.py,sha256=jMIf2O7dTGxvTA5AfXtmh1H_EGfMtQseR5wXrjNRbLs,93
|
2
|
+
cartesia/_types.py,sha256=msXRqNwVx_mbcLIQgRJYEl8U-hO9LRPWmscnX89cBCY,3747
|
3
|
+
cartesia/client.py,sha256=jMlFDPRtKVDelqevHlv7YZJgOES3ws9BFN_6uUyN0W8,32720
|
4
|
+
cartesia/version.py,sha256=d4QHYmS_30j0hPN8NmNPnQ_Z0TphDRbu4MtQj9cT9e8,22
|
5
|
+
cartesia-1.0.1.dist-info/METADATA,sha256=rwqWm86ez9EzEovMQGPqClXh7U7cTsMGgDwl6DVO42o,12394
|
6
|
+
cartesia-1.0.1.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
|
7
|
+
cartesia-1.0.1.dist-info/top_level.txt,sha256=rTX4HnnCegMxl1FK9czpVC7GAvf3SwDzPG65qP-BS4w,9
|
8
|
+
cartesia-1.0.1.dist-info/RECORD,,
|
cartesia/utils.py
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
import time
|
2
|
-
|
3
|
-
from aiohttp.client_exceptions import ServerDisconnectedError
|
4
|
-
import asyncio
|
5
|
-
from functools import wraps
|
6
|
-
from http.client import RemoteDisconnected
|
7
|
-
from httpx import TimeoutException
|
8
|
-
from requests.exceptions import ConnectionError
|
9
|
-
|
10
|
-
|
11
|
-
def retry_on_connection_error(max_retries=3, backoff_factor=1, logger=None):
|
12
|
-
"""Retry a function if a ConnectionError, RemoteDisconnected, ServerDisconnectedError, or TimeoutException occurs.
|
13
|
-
|
14
|
-
Args:
|
15
|
-
max_retries (int): The maximum number of retries.
|
16
|
-
backoff_factor (int): The factor to increase the delay between retries.
|
17
|
-
logger (logging.Logger): The logger to use for logging.
|
18
|
-
"""
|
19
|
-
|
20
|
-
def decorator(func):
|
21
|
-
@wraps(func)
|
22
|
-
def wrapper(*args, **kwargs):
|
23
|
-
retry_count = 0
|
24
|
-
while retry_count < max_retries:
|
25
|
-
try:
|
26
|
-
return func(*args, **kwargs)
|
27
|
-
except (
|
28
|
-
ConnectionError,
|
29
|
-
RemoteDisconnected,
|
30
|
-
ServerDisconnectedError,
|
31
|
-
TimeoutException,
|
32
|
-
) as e:
|
33
|
-
logger.info(f"Retrying after exception: {e}")
|
34
|
-
retry_count += 1
|
35
|
-
if retry_count < max_retries:
|
36
|
-
delay = backoff_factor * (2 ** (retry_count - 1))
|
37
|
-
logger.warn(
|
38
|
-
f"Attempt {retry_count + 1}/{max_retries} in {delay} seconds..."
|
39
|
-
)
|
40
|
-
time.sleep(delay)
|
41
|
-
else:
|
42
|
-
raise Exception(f"Exception occurred after {max_retries} tries.") from e
|
43
|
-
|
44
|
-
return wrapper
|
45
|
-
|
46
|
-
return decorator
|
47
|
-
|
48
|
-
|
49
|
-
def retry_on_connection_error_async(max_retries=3, backoff_factor=1, logger=None):
|
50
|
-
"""Retry an asynchronous function if a ConnectionError, RemoteDisconnected, ServerDisconnectedError, or TimeoutException occurs.
|
51
|
-
|
52
|
-
Args:
|
53
|
-
max_retries (int): The maximum number of retries.
|
54
|
-
backoff_factor (int): The factor to increase the delay between retries.
|
55
|
-
logger (logging.Logger): The logger to use for logging.
|
56
|
-
"""
|
57
|
-
|
58
|
-
def decorator(func):
|
59
|
-
@wraps(func)
|
60
|
-
async def wrapper(*args, **kwargs):
|
61
|
-
retry_count = 0
|
62
|
-
while retry_count < max_retries:
|
63
|
-
try:
|
64
|
-
async for chunk in func(*args, **kwargs):
|
65
|
-
yield chunk
|
66
|
-
# If the function completes without raising an exception return
|
67
|
-
return
|
68
|
-
except (
|
69
|
-
ConnectionError,
|
70
|
-
RemoteDisconnected,
|
71
|
-
ServerDisconnectedError,
|
72
|
-
TimeoutException,
|
73
|
-
) as e:
|
74
|
-
logger.info(f"Retrying after exception: {e}")
|
75
|
-
retry_count += 1
|
76
|
-
if retry_count < max_retries:
|
77
|
-
delay = backoff_factor * (2 ** (retry_count - 1))
|
78
|
-
logger.warn(
|
79
|
-
f"Attempt {retry_count + 1}/{max_retries} in {delay} seconds..."
|
80
|
-
)
|
81
|
-
await asyncio.sleep(delay)
|
82
|
-
else:
|
83
|
-
raise Exception(f"Exception occurred after {max_retries} tries.") from e
|
84
|
-
|
85
|
-
return wrapper
|
86
|
-
|
87
|
-
return decorator
|
cartesia-1.0.0.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
cartesia/__init__.py,sha256=jMIf2O7dTGxvTA5AfXtmh1H_EGfMtQseR5wXrjNRbLs,93
|
2
|
-
cartesia/_types.py,sha256=M-gtOFGBRWYAHUpJZNEl2jodqmNm_ZIPQUHkXLyP_-s,1503
|
3
|
-
cartesia/client.py,sha256=9uSIYzGGE-Bgcsq5qJ1ApVesAGz_l5Olg07ZUvw268Q,32306
|
4
|
-
cartesia/utils.py,sha256=nuwWRfu3MOVTxIQMLjYf6WLaxSlnu_GdE3QjTV0zisQ,3339
|
5
|
-
cartesia/version.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22
|
6
|
-
cartesia-1.0.0.dist-info/METADATA,sha256=PAsYxXc-s1TO_2ROmlLUH4w4CXI7zRMOjmSo0cHg_Os,11399
|
7
|
-
cartesia-1.0.0.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
|
8
|
-
cartesia-1.0.0.dist-info/top_level.txt,sha256=rTX4HnnCegMxl1FK9czpVC7GAvf3SwDzPG65qP-BS4w,9
|
9
|
-
cartesia-1.0.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|