cartesia 1.4.0__py3-none-any.whl → 2.0.0__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.
Files changed (181) hide show
  1. cartesia/__init__.py +302 -3
  2. cartesia/api_status/__init__.py +6 -0
  3. cartesia/api_status/client.py +104 -0
  4. cartesia/api_status/requests/__init__.py +5 -0
  5. cartesia/api_status/requests/api_info.py +8 -0
  6. cartesia/api_status/types/__init__.py +5 -0
  7. cartesia/api_status/types/api_info.py +20 -0
  8. cartesia/base_client.py +156 -0
  9. cartesia/client.py +163 -40
  10. cartesia/core/__init__.py +50 -0
  11. cartesia/core/api_error.py +15 -0
  12. cartesia/core/client_wrapper.py +55 -0
  13. cartesia/core/datetime_utils.py +28 -0
  14. cartesia/core/file.py +67 -0
  15. cartesia/core/http_client.py +499 -0
  16. cartesia/core/jsonable_encoder.py +101 -0
  17. cartesia/core/pagination.py +88 -0
  18. cartesia/core/pydantic_utilities.py +296 -0
  19. cartesia/core/query_encoder.py +58 -0
  20. cartesia/core/remove_none_from_dict.py +11 -0
  21. cartesia/core/request_options.py +35 -0
  22. cartesia/core/serialization.py +272 -0
  23. cartesia/datasets/__init__.py +24 -0
  24. cartesia/datasets/requests/__init__.py +15 -0
  25. cartesia/datasets/requests/create_dataset_request.py +7 -0
  26. cartesia/datasets/requests/dataset.py +9 -0
  27. cartesia/datasets/requests/dataset_file.py +9 -0
  28. cartesia/datasets/requests/paginated_dataset_files.py +10 -0
  29. cartesia/datasets/requests/paginated_datasets.py +10 -0
  30. cartesia/datasets/types/__init__.py +17 -0
  31. cartesia/datasets/types/create_dataset_request.py +19 -0
  32. cartesia/datasets/types/dataset.py +21 -0
  33. cartesia/datasets/types/dataset_file.py +21 -0
  34. cartesia/datasets/types/file_purpose.py +5 -0
  35. cartesia/datasets/types/paginated_dataset_files.py +21 -0
  36. cartesia/datasets/types/paginated_datasets.py +21 -0
  37. cartesia/embedding/__init__.py +5 -0
  38. cartesia/embedding/types/__init__.py +5 -0
  39. cartesia/embedding/types/embedding.py +201 -0
  40. cartesia/environment.py +7 -0
  41. cartesia/infill/__init__.py +2 -0
  42. cartesia/infill/client.py +318 -0
  43. cartesia/tts/__init__.py +167 -0
  44. cartesia/{_async_websocket.py → tts/_async_websocket.py} +212 -85
  45. cartesia/tts/_websocket.py +479 -0
  46. cartesia/tts/client.py +407 -0
  47. cartesia/tts/requests/__init__.py +76 -0
  48. cartesia/tts/requests/cancel_context_request.py +17 -0
  49. cartesia/tts/requests/controls.py +11 -0
  50. cartesia/tts/requests/generation_request.py +58 -0
  51. cartesia/tts/requests/mp_3_output_format.py +11 -0
  52. cartesia/tts/requests/output_format.py +30 -0
  53. cartesia/tts/requests/phoneme_timestamps.py +10 -0
  54. cartesia/tts/requests/raw_output_format.py +11 -0
  55. cartesia/tts/requests/speed.py +7 -0
  56. cartesia/tts/requests/tts_request.py +24 -0
  57. cartesia/tts/requests/tts_request_embedding_specifier.py +16 -0
  58. cartesia/tts/requests/tts_request_id_specifier.py +16 -0
  59. cartesia/tts/requests/tts_request_voice_specifier.py +7 -0
  60. cartesia/tts/requests/wav_output_format.py +7 -0
  61. cartesia/tts/requests/web_socket_base_response.py +11 -0
  62. cartesia/tts/requests/web_socket_chunk_response.py +11 -0
  63. cartesia/tts/requests/web_socket_done_response.py +7 -0
  64. cartesia/tts/requests/web_socket_error_response.py +7 -0
  65. cartesia/tts/requests/web_socket_flush_done_response.py +9 -0
  66. cartesia/tts/requests/web_socket_phoneme_timestamps_response.py +9 -0
  67. cartesia/tts/requests/web_socket_raw_output_format.py +11 -0
  68. cartesia/tts/requests/web_socket_request.py +7 -0
  69. cartesia/tts/requests/web_socket_response.py +70 -0
  70. cartesia/tts/requests/web_socket_stream_options.py +8 -0
  71. cartesia/tts/requests/web_socket_timestamps_response.py +9 -0
  72. cartesia/tts/requests/web_socket_tts_output.py +18 -0
  73. cartesia/tts/requests/web_socket_tts_request.py +25 -0
  74. cartesia/tts/requests/word_timestamps.py +10 -0
  75. cartesia/tts/socket_client.py +302 -0
  76. cartesia/tts/types/__init__.py +90 -0
  77. cartesia/tts/types/cancel_context_request.py +28 -0
  78. cartesia/tts/types/context_id.py +3 -0
  79. cartesia/tts/types/controls.py +22 -0
  80. cartesia/tts/types/emotion.py +34 -0
  81. cartesia/tts/types/flush_id.py +3 -0
  82. cartesia/tts/types/generation_request.py +71 -0
  83. cartesia/tts/types/mp_3_output_format.py +23 -0
  84. cartesia/tts/types/natural_specifier.py +5 -0
  85. cartesia/tts/types/numerical_specifier.py +3 -0
  86. cartesia/tts/types/output_format.py +58 -0
  87. cartesia/tts/types/phoneme_timestamps.py +21 -0
  88. cartesia/tts/types/raw_encoding.py +5 -0
  89. cartesia/tts/types/raw_output_format.py +22 -0
  90. cartesia/tts/types/speed.py +7 -0
  91. cartesia/tts/types/supported_language.py +7 -0
  92. cartesia/tts/types/tts_request.py +35 -0
  93. cartesia/tts/types/tts_request_embedding_specifier.py +27 -0
  94. cartesia/tts/types/tts_request_id_specifier.py +27 -0
  95. cartesia/tts/types/tts_request_voice_specifier.py +7 -0
  96. cartesia/tts/types/wav_output_format.py +17 -0
  97. cartesia/tts/types/web_socket_base_response.py +22 -0
  98. cartesia/tts/types/web_socket_chunk_response.py +22 -0
  99. cartesia/tts/types/web_socket_done_response.py +17 -0
  100. cartesia/tts/types/web_socket_error_response.py +19 -0
  101. cartesia/tts/types/web_socket_flush_done_response.py +21 -0
  102. cartesia/tts/types/web_socket_phoneme_timestamps_response.py +20 -0
  103. cartesia/tts/types/web_socket_raw_output_format.py +22 -0
  104. cartesia/tts/types/web_socket_request.py +7 -0
  105. cartesia/tts/types/web_socket_response.py +125 -0
  106. cartesia/tts/types/web_socket_stream_options.py +19 -0
  107. cartesia/tts/types/web_socket_timestamps_response.py +20 -0
  108. cartesia/tts/types/web_socket_tts_output.py +29 -0
  109. cartesia/tts/types/web_socket_tts_request.py +37 -0
  110. cartesia/tts/types/word_timestamps.py +21 -0
  111. cartesia/{_constants.py → tts/utils/constants.py} +2 -2
  112. cartesia/tts/utils/tts.py +64 -0
  113. cartesia/tts/utils/types.py +70 -0
  114. cartesia/version.py +3 -1
  115. cartesia/voice_changer/__init__.py +27 -0
  116. cartesia/voice_changer/client.py +395 -0
  117. cartesia/voice_changer/requests/__init__.py +15 -0
  118. cartesia/voice_changer/requests/streaming_response.py +38 -0
  119. cartesia/voice_changer/types/__init__.py +17 -0
  120. cartesia/voice_changer/types/output_format_container.py +5 -0
  121. cartesia/voice_changer/types/streaming_response.py +64 -0
  122. cartesia/voices/__init__.py +81 -0
  123. cartesia/voices/client.py +1218 -0
  124. cartesia/voices/requests/__init__.py +29 -0
  125. cartesia/voices/requests/create_voice_request.py +23 -0
  126. cartesia/voices/requests/embedding_response.py +8 -0
  127. cartesia/voices/requests/embedding_specifier.py +10 -0
  128. cartesia/voices/requests/get_voices_response.py +24 -0
  129. cartesia/voices/requests/id_specifier.py +10 -0
  130. cartesia/voices/requests/localize_dialect.py +11 -0
  131. cartesia/voices/requests/localize_voice_request.py +28 -0
  132. cartesia/voices/requests/mix_voice_specifier.py +7 -0
  133. cartesia/voices/requests/mix_voices_request.py +9 -0
  134. cartesia/voices/requests/update_voice_request.py +15 -0
  135. cartesia/voices/requests/voice.py +43 -0
  136. cartesia/voices/requests/voice_metadata.py +36 -0
  137. cartesia/voices/types/__init__.py +53 -0
  138. cartesia/voices/types/base_voice_id.py +5 -0
  139. cartesia/voices/types/clone_mode.py +5 -0
  140. cartesia/voices/types/create_voice_request.py +34 -0
  141. cartesia/voices/types/embedding_response.py +20 -0
  142. cartesia/voices/types/embedding_specifier.py +22 -0
  143. cartesia/voices/types/gender.py +5 -0
  144. cartesia/voices/types/gender_presentation.py +5 -0
  145. cartesia/voices/types/get_voices_response.py +34 -0
  146. cartesia/voices/types/id_specifier.py +22 -0
  147. cartesia/voices/types/localize_dialect.py +11 -0
  148. cartesia/voices/types/localize_english_dialect.py +5 -0
  149. cartesia/voices/types/localize_french_dialect.py +5 -0
  150. cartesia/voices/types/localize_portuguese_dialect.py +5 -0
  151. cartesia/voices/types/localize_spanish_dialect.py +5 -0
  152. cartesia/voices/types/localize_target_language.py +7 -0
  153. cartesia/voices/types/localize_voice_request.py +39 -0
  154. cartesia/voices/types/mix_voice_specifier.py +7 -0
  155. cartesia/voices/types/mix_voices_request.py +20 -0
  156. cartesia/voices/types/update_voice_request.py +27 -0
  157. cartesia/voices/types/voice.py +54 -0
  158. cartesia/voices/types/voice_expand_options.py +5 -0
  159. cartesia/voices/types/voice_id.py +3 -0
  160. cartesia/voices/types/voice_metadata.py +48 -0
  161. cartesia/voices/types/weight.py +3 -0
  162. cartesia-2.0.0.dist-info/METADATA +414 -0
  163. cartesia-2.0.0.dist-info/RECORD +165 -0
  164. {cartesia-1.4.0.dist-info → cartesia-2.0.0.dist-info}/WHEEL +1 -1
  165. cartesia/_async_sse.py +0 -95
  166. cartesia/_logger.py +0 -3
  167. cartesia/_sse.py +0 -143
  168. cartesia/_types.py +0 -70
  169. cartesia/_websocket.py +0 -358
  170. cartesia/async_client.py +0 -82
  171. cartesia/async_tts.py +0 -176
  172. cartesia/resource.py +0 -44
  173. cartesia/tts.py +0 -292
  174. cartesia/utils/deprecated.py +0 -55
  175. cartesia/utils/retry.py +0 -87
  176. cartesia/utils/tts.py +0 -78
  177. cartesia/voices.py +0 -204
  178. cartesia-1.4.0.dist-info/METADATA +0 -663
  179. cartesia-1.4.0.dist-info/RECORD +0 -23
  180. cartesia-1.4.0.dist-info/licenses/LICENSE.md +0 -21
  181. /cartesia/{utils/__init__.py → py.typed} +0 -0
cartesia/tts.py DELETED
@@ -1,292 +0,0 @@
1
- import json
2
- from typing import Iterator, List, Optional, Tuple
3
-
4
- import httpx
5
- import io
6
- from pydub import AudioSegment
7
-
8
- from cartesia._sse import _SSE
9
- from cartesia._types import (
10
- OutputFormat,
11
- OutputFormatMapping,
12
- VoiceControls,
13
- )
14
- from cartesia._websocket import _WebSocket
15
- from cartesia.resource import Resource
16
- from cartesia.utils.tts import _construct_tts_request, _validate_and_construct_voice
17
-
18
-
19
- class TTS(Resource):
20
- """This resource contains methods to generate audio using Cartesia's text-to-speech API."""
21
-
22
- def __init__(self, api_key: str, base_url: str, timeout: float):
23
- super().__init__(
24
- api_key=api_key,
25
- base_url=base_url,
26
- timeout=timeout,
27
- )
28
- self._sse_class = _SSE(self._http_url(), self.headers, self.timeout)
29
- self.sse = self._sse_class.send
30
-
31
- def websocket(self) -> _WebSocket:
32
- """This method returns a WebSocket object that can be used to generate audio using WebSocket.
33
-
34
- Returns:
35
- _WebSocket: A WebSocket object that can be used to generate audio using WebSocket.
36
- """
37
- ws = _WebSocket(self._ws_url(), self.api_key, self.cartesia_version)
38
- ws.connect()
39
- return ws
40
-
41
- def bytes(
42
- self,
43
- *,
44
- model_id: str,
45
- transcript: str,
46
- output_format: OutputFormat,
47
- voice_id: Optional[str] = None,
48
- voice_embedding: Optional[List[float]] = None,
49
- duration: Optional[int] = None,
50
- language: Optional[str] = None,
51
- _experimental_voice_controls: Optional[VoiceControls] = None,
52
- ) -> bytes:
53
- request_body = _construct_tts_request(
54
- model_id=model_id,
55
- transcript=transcript,
56
- output_format=output_format,
57
- voice_id=voice_id,
58
- voice_embedding=voice_embedding,
59
- duration=duration,
60
- language=language,
61
- _experimental_voice_controls=_experimental_voice_controls,
62
- )
63
-
64
- response = httpx.post(
65
- f"{self._http_url()}/tts/bytes",
66
- headers=self.headers,
67
- timeout=self.timeout,
68
- json=request_body,
69
- )
70
-
71
- if not response.is_success:
72
- raise ValueError(f"Failed to generate audio. Error: {response.text}")
73
-
74
- return response.content
75
-
76
- @staticmethod
77
- def get_output_format(output_format_name: str) -> OutputFormat:
78
- """Convenience method to get the output_format dictionary from a given output format name.
79
-
80
- Args:
81
- output_format_name (str): The name of the output format.
82
-
83
- Returns:
84
- OutputFormat: A dictionary containing the details of the output format to be passed into tts.sse() or tts.websocket().send()
85
-
86
- Raises:
87
- ValueError: If the output_format name is not supported
88
- """
89
- if output_format_name in OutputFormatMapping._format_mapping:
90
- output_format_obj = OutputFormatMapping.get_format(output_format_name)
91
- else:
92
- raise ValueError(f"Unsupported format: {output_format_name}")
93
-
94
- return OutputFormat(
95
- container=output_format_obj["container"],
96
- encoding=output_format_obj["encoding"],
97
- sample_rate=output_format_obj["sample_rate"],
98
- )
99
-
100
- @staticmethod
101
- def get_sample_rate(output_format_name: str) -> int:
102
- """Convenience method to get the sample rate for a given output format.
103
-
104
- Args:
105
- output_format_name (str): The name of the output format.
106
-
107
- Returns:
108
- int: The sample rate for the output format.
109
-
110
- Raises:
111
- ValueError: If the output_format name is not supported
112
- """
113
- if output_format_name in OutputFormatMapping._format_mapping:
114
- output_format_obj = OutputFormatMapping.get_format(output_format_name)
115
- else:
116
- raise ValueError(f"Unsupported format: {output_format_name}")
117
-
118
- return output_format_obj["sample_rate"]
119
-
120
- @staticmethod
121
- def _validate_and_construct_voice(
122
- voice_id: Optional[str] = None,
123
- voice_embedding: Optional[List[float]] = None,
124
- experimental_voice_controls: Optional[VoiceControls] = None,
125
- ) -> dict:
126
- """Validate and construct the voice dictionary for the request.
127
-
128
- Args:
129
- voice_id: The ID of the voice to use for generating audio.
130
- voice_embedding: The embedding of the voice to use for generating audio.
131
- experimental_voice_controls: Voice controls for emotion and speed.
132
- Note: This is an experimental feature and may rapidly change in the future.
133
-
134
- Returns:
135
- A dictionary representing the voice configuration.
136
-
137
- Raises:
138
- ValueError: If neither or both voice_id and voice_embedding are specified.
139
- """
140
- return _validate_and_construct_voice(voice_id, voice_embedding, experimental_voice_controls)
141
-
142
- def infill(
143
- self,
144
- *,
145
- model_id: str,
146
- language: str,
147
- transcript: str,
148
- voice_id: str,
149
- output_format: OutputFormat,
150
- left_audio_path: Optional[str] = None,
151
- right_audio_path: Optional[str] = None,
152
- experimental_voice_controls: Optional[VoiceControls] = None,
153
- ) -> Tuple[bytes, bytes]:
154
- """Generate infill audio between two existing audio segments.
155
-
156
- Args:
157
- model_id: The ID of the model to use for generating audio
158
- language: The language of the transcript
159
- transcript: The text to synthesize
160
- voice_id: The ID of the voice to use for generating audio
161
- output_format: The desired audio output format
162
- left_audio_path: Path to the audio file that comes before the infill
163
- right_audio_path: Path to the audio file that comes after the infill
164
- experimental_voice_controls: Optional voice control parameters
165
-
166
- Returns:
167
- A tuple containing:
168
- - The generated infill audio (bytes)
169
- - The complete concatenated audio (bytes)
170
- """
171
- if not left_audio_path and not right_audio_path:
172
- raise ValueError("Must specify at least one of left_audio_path or right_audio_path")
173
-
174
- headers = self.headers.copy()
175
- headers.pop("Content-Type", None)
176
-
177
- left_audio_file = None
178
- right_audio_file = None
179
- try:
180
- files = {}
181
- if left_audio_path:
182
- left_audio_file = open(left_audio_path, "rb")
183
- files["left_audio"] = left_audio_file
184
- if right_audio_path:
185
- right_audio_file = open(right_audio_path, "rb")
186
- files["right_audio"] = right_audio_file
187
-
188
- # Construct form data with output_format fields directly
189
- data = {
190
- "model_id": model_id,
191
- "language": language,
192
- "transcript": transcript,
193
- "voice_id": voice_id,
194
- "output_format[container]": output_format["container"],
195
- "output_format[encoding]": output_format["encoding"],
196
- "output_format[sample_rate]": output_format["sample_rate"],
197
- }
198
-
199
- # Add bit_rate for mp3 container
200
- if "bit_rate" in output_format:
201
- data["output_format[bit_rate]"] = output_format["bit_rate"]
202
-
203
- # Add voice controls if specified
204
- if experimental_voice_controls:
205
- if "speed" in experimental_voice_controls:
206
- data["voice[__experimental_controls][speed]"] = experimental_voice_controls[
207
- "speed"
208
- ]
209
- if "emotion" in experimental_voice_controls:
210
- # Pass emotions as a list instead of individual values
211
- data["voice[__experimental_controls][emotion][]"] = experimental_voice_controls[
212
- "emotion"
213
- ]
214
-
215
- response = httpx.post(
216
- f"{self._http_url()}/infill/bytes",
217
- headers=headers,
218
- timeout=self.timeout,
219
- files=files,
220
- data=data,
221
- )
222
-
223
- if not response.is_success:
224
- raise ValueError(
225
- f"Failed to infill audio. Status Code: {response.status_code}\n"
226
- f"Error: {response.text}"
227
- )
228
-
229
- if left_audio_file:
230
- left_audio_file.seek(0)
231
- left_audio = left_audio_file.read()
232
- else:
233
- left_audio = None
234
-
235
- if right_audio_file:
236
- right_audio_file.seek(0)
237
- right_audio = right_audio_file.read()
238
- else:
239
- right_audio = None
240
-
241
- infill_audio = response.content
242
- format = output_format["container"].lower()
243
- total_audio = self._concat_audio_segments(
244
- left_audio, infill_audio, right_audio, format=format
245
- )
246
- return infill_audio, total_audio
247
-
248
- finally:
249
- if left_audio_file:
250
- left_audio_file.close()
251
- if right_audio_file:
252
- right_audio_file.close()
253
-
254
- @staticmethod
255
- def _concat_audio_segments(
256
- left_audio: Optional[bytes],
257
- infill_audio: bytes,
258
- right_audio: Optional[bytes],
259
- format: str = "wav",
260
- ) -> bytes:
261
- """Helper method to concatenate three audio segments while preserving audio format and headers.
262
-
263
- Args:
264
- left_audio: The audio segment that comes before the infill
265
- infill_audio: The generated infill audio segment
266
- right_audio: The audio segment that comes after the infill
267
- format: The audio format (e.g., 'wav', 'mp3'). Defaults to 'wav'
268
-
269
- Returns:
270
- bytes: The concatenated audio as bytes
271
-
272
- Raises:
273
- ValueError: If the audio segments cannot be loaded or concatenated
274
- """
275
- try:
276
- # Convert bytes to AudioSegment objects
277
- combined = AudioSegment.empty()
278
- if left_audio:
279
- combined += AudioSegment.from_file(io.BytesIO(left_audio), format=format)
280
-
281
- combined += AudioSegment.from_file(io.BytesIO(infill_audio), format=format)
282
-
283
- if right_audio:
284
- combined += AudioSegment.from_file(io.BytesIO(right_audio), format=format)
285
-
286
- # Export to bytes
287
- output = io.BytesIO()
288
- combined.export(output, format=format)
289
- return output.getvalue()
290
-
291
- except Exception as e:
292
- raise ValueError(f"Failed to concatenate audio segments: {str(e)}")
@@ -1,55 +0,0 @@
1
- import os
2
- import warnings
3
- from typing import Any, Callable, TypeVar
4
-
5
- TCallable = TypeVar("TCallable", bound=Callable[..., Any])
6
-
7
- # List of statistics of deprecated functions.
8
- # This should only be used by the test suite to find any deprecated functions
9
- # that should be removed for this version.
10
- _TRACK_DEPRECATED_FUNCTION_STATS = os.environ.get("CARTESIA_TEST_DEPRECATED", "").lower() == "true"
11
- _DEPRECATED_FUNCTION_STATS = []
12
-
13
-
14
- def deprecated(
15
- reason=None, vdeprecated=None, vremove=None, replacement=None
16
- ) -> Callable[[TCallable], TCallable]:
17
- local_vars = locals()
18
-
19
- def fn(func: TCallable) -> TCallable:
20
- if isinstance(func, classmethod):
21
- func = func.__func__
22
- msg = _get_deprecated_msg(func, reason, vdeprecated, vremove, replacement)
23
- warnings.warn(msg, DeprecationWarning)
24
- return func
25
-
26
- if _TRACK_DEPRECATED_FUNCTION_STATS: # pragma: no cover
27
- _DEPRECATED_FUNCTION_STATS.append(local_vars)
28
-
29
- return fn
30
-
31
-
32
- def _get_deprecated_msg(wrapped, reason, vdeprecated, vremoved, replacement=None):
33
- fmt = "{name} is deprecated"
34
- if vdeprecated:
35
- fmt += " since v{vdeprecated}"
36
- if vremoved:
37
- fmt += " and will be removed in v{vremoved}"
38
- fmt += "."
39
-
40
- if reason:
41
- fmt += " ({reason})"
42
- if replacement:
43
- fmt += " -- Use {replacement} instead."
44
-
45
- return fmt.format(
46
- name=wrapped.__name__,
47
- reason=reason or "",
48
- vdeprecated=vdeprecated or "",
49
- vremoved=vremoved or "",
50
- replacement=replacement or "",
51
- )
52
-
53
-
54
- # This method is taken from the following source:
55
- # https://github.com/ad12/meddlr/blob/main/meddlr/utils/deprecated.py
cartesia/utils/retry.py DELETED
@@ -1,87 +0,0 @@
1
- import asyncio
2
- import time
3
- from functools import wraps
4
- from http.client import RemoteDisconnected
5
-
6
- from aiohttp.client_exceptions import ServerDisconnectedError
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/utils/tts.py DELETED
@@ -1,78 +0,0 @@
1
- from typing import List, Optional
2
-
3
- from cartesia._types import OutputFormat, VoiceControls
4
-
5
-
6
- def _validate_and_construct_voice(
7
- voice_id: Optional[str] = None,
8
- voice_embedding: Optional[List[float]] = None,
9
- experimental_voice_controls: Optional[VoiceControls] = None,
10
- ) -> dict:
11
- if voice_id is None and voice_embedding is None:
12
- raise ValueError("Either voice_id or voice_embedding must be specified.")
13
-
14
- voice = {}
15
-
16
- if voice_id is not None:
17
- voice["id"] = voice_id
18
-
19
- if voice_embedding is not None:
20
- voice["embedding"] = voice_embedding
21
-
22
- if experimental_voice_controls is not None:
23
- voice["__experimental_controls"] = experimental_voice_controls
24
-
25
- return voice
26
-
27
-
28
- def _construct_tts_request(
29
- *,
30
- model_id: str,
31
- output_format: OutputFormat,
32
- transcript: Optional[str] = None,
33
- voice_id: Optional[str] = None,
34
- voice_embedding: Optional[List[float]] = None,
35
- duration: Optional[int] = None,
36
- language: Optional[str] = None,
37
- add_timestamps: bool = False,
38
- context_id: Optional[str] = None,
39
- continue_: bool = False,
40
- flush: bool = False,
41
- _experimental_voice_controls: Optional[VoiceControls] = None,
42
- ):
43
- tts_request = {
44
- "model_id": model_id,
45
- "voice": _validate_and_construct_voice(
46
- voice_id,
47
- voice_embedding=voice_embedding,
48
- experimental_voice_controls=_experimental_voice_controls,
49
- ),
50
- "output_format": {
51
- "container": output_format["container"],
52
- "encoding": output_format["encoding"],
53
- "sample_rate": output_format["sample_rate"],
54
- },
55
- }
56
-
57
- if language is not None:
58
- tts_request["language"] = language
59
-
60
- if transcript is not None:
61
- tts_request["transcript"] = transcript
62
-
63
- if duration is not None:
64
- tts_request["duration"] = duration
65
-
66
- if add_timestamps:
67
- tts_request["add_timestamps"] = add_timestamps
68
-
69
- if context_id is not None:
70
- tts_request["context_id"] = context_id
71
-
72
- if continue_:
73
- tts_request["continue"] = continue_
74
-
75
- if flush:
76
- tts_request["flush"] = flush
77
-
78
- return tts_request