typecast-python 0.3.10__py3-none-any.whl → 0.3.12__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.
@@ -0,0 +1,191 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, Tuple
4
+
5
+ import httpx
6
+
7
+
8
+ def _timeout(value: Any) -> Any:
9
+ if isinstance(value, ClientTimeout):
10
+ total = value.total or value.sock_read or 5.0
11
+ return httpx.Timeout(
12
+ total, connect=value.connect or value.sock_connect or total
13
+ )
14
+ if isinstance(value, tuple):
15
+ return httpx.Timeout(value[1], connect=value[0])
16
+ return value
17
+
18
+
19
+ def _headers(defaults: Dict[str, str], supplied: Optional[dict]) -> Dict[str, str]:
20
+ headers = dict(defaults)
21
+ for key, value in (supplied or {}).items():
22
+ if value is None:
23
+ headers.pop(key, None)
24
+ else:
25
+ headers[key] = value
26
+ return headers
27
+
28
+
29
+ class RequestsCompatResponse:
30
+ def __init__(self, response: httpx.Response):
31
+ self._response = response
32
+
33
+ def __getattr__(self, name: str) -> Any:
34
+ return getattr(self._response, name)
35
+
36
+ def iter_content(self, chunk_size: int) -> Iterator[bytes]:
37
+ return self._response.iter_bytes(chunk_size)
38
+
39
+ def close(self) -> None:
40
+ self._response.close()
41
+
42
+
43
+ class RequestsCompatSession:
44
+ def __init__(self, client: Optional[httpx.Client] = None):
45
+ self._client = client or httpx.Client(timeout=None)
46
+ self.headers: Dict[str, str] = {}
47
+
48
+ def _request(self, method: str, url: str, **kwargs: Any) -> RequestsCompatResponse:
49
+ stream = kwargs.pop("stream", False)
50
+ kwargs["headers"] = _headers(self.headers, kwargs.get("headers"))
51
+ if "timeout" in kwargs:
52
+ kwargs["timeout"] = _timeout(kwargs["timeout"])
53
+ if stream:
54
+ request = self._client.build_request(method, url, **kwargs)
55
+ response = self._client.send(request, stream=True)
56
+ else:
57
+ response = self._client.request(method, url, **kwargs)
58
+ return RequestsCompatResponse(response)
59
+
60
+ def get(self, url: str, **kwargs: Any) -> RequestsCompatResponse:
61
+ return self._request("GET", url, **kwargs)
62
+
63
+ def post(self, url: str, **kwargs: Any) -> RequestsCompatResponse:
64
+ return self._request("POST", url, **kwargs)
65
+
66
+ def delete(self, url: str, **kwargs: Any) -> RequestsCompatResponse:
67
+ return self._request("DELETE", url, **kwargs)
68
+
69
+ def close(self) -> None:
70
+ self._client.close()
71
+
72
+
73
+ class ClientTimeout:
74
+ def __init__(
75
+ self,
76
+ *,
77
+ total: Optional[float] = None,
78
+ connect: Optional[float] = None,
79
+ sock_connect: Optional[float] = None,
80
+ sock_read: Optional[float] = None,
81
+ ):
82
+ self.total = total
83
+ self.connect = connect
84
+ self.sock_connect = sock_connect
85
+ self.sock_read = sock_read
86
+
87
+
88
+ class FormData:
89
+ def __init__(self):
90
+ self.fields: List[Tuple[str, Any, Optional[str], Optional[str]]] = []
91
+
92
+ def add_field(
93
+ self,
94
+ name: str,
95
+ value: Any,
96
+ *,
97
+ filename: Optional[str] = None,
98
+ content_type: Optional[str] = None,
99
+ ) -> None:
100
+ self.fields.append((name, value, filename, content_type))
101
+
102
+
103
+ class _AsyncContent:
104
+ def __init__(self, response: httpx.Response):
105
+ self._response = response
106
+
107
+ async def iter_chunked(self, chunk_size: int) -> AsyncIterator[bytes]:
108
+ async for chunk in self._response.aiter_bytes(chunk_size):
109
+ yield chunk
110
+
111
+
112
+ class AiohttpCompatResponse:
113
+ def __init__(self, response: httpx.Response):
114
+ self._response = response
115
+ self.status = response.status_code
116
+ self.headers = response.headers
117
+ self.content = _AsyncContent(response)
118
+
119
+ async def read(self) -> bytes:
120
+ return await self._response.aread()
121
+
122
+ async def text(self) -> str:
123
+ await self._response.aread()
124
+ return self._response.text
125
+
126
+ async def json(self) -> Any:
127
+ await self._response.aread()
128
+ return self._response.json()
129
+
130
+ async def close(self) -> None:
131
+ await self._response.aclose()
132
+
133
+
134
+ class _AsyncRequestContext:
135
+ def __init__(self, request: Any):
136
+ self._request = request
137
+ self._response: Optional[AiohttpCompatResponse] = None
138
+
139
+ async def __aenter__(self) -> AiohttpCompatResponse:
140
+ response = await self._request
141
+ self._response = response
142
+ return response
143
+
144
+ async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
145
+ if self._response:
146
+ await self._response.close()
147
+
148
+
149
+ class AiohttpCompatSession:
150
+ def __init__(
151
+ self,
152
+ headers: Optional[dict] = None,
153
+ client: Optional[httpx.AsyncClient] = None,
154
+ ):
155
+ self._client = client or httpx.AsyncClient(timeout=300)
156
+ self.headers = dict(headers or {})
157
+
158
+ async def _request(
159
+ self, method: str, url: str, **kwargs: Any
160
+ ) -> AiohttpCompatResponse:
161
+ kwargs["headers"] = _headers(self.headers, kwargs.get("headers"))
162
+ if "timeout" in kwargs:
163
+ kwargs["timeout"] = _timeout(kwargs["timeout"])
164
+ form = kwargs.get("data")
165
+ if isinstance(form, FormData):
166
+ kwargs.pop("data")
167
+ kwargs["data"] = {
168
+ name: value
169
+ for name, value, filename, _ in form.fields
170
+ if filename is None
171
+ }
172
+ kwargs["files"] = [
173
+ (name, (filename, value, content_type))
174
+ for name, value, filename, content_type in form.fields
175
+ if filename is not None
176
+ ]
177
+ request = self._client.build_request(method, url, **kwargs)
178
+ response = await self._client.send(request, stream=True)
179
+ return AiohttpCompatResponse(response)
180
+
181
+ def get(self, url: str, **kwargs: Any) -> _AsyncRequestContext:
182
+ return _AsyncRequestContext(self._request("GET", url, **kwargs))
183
+
184
+ def post(self, url: str, **kwargs: Any) -> _AsyncRequestContext:
185
+ return _AsyncRequestContext(self._request("POST", url, **kwargs))
186
+
187
+ def delete(self, url: str, **kwargs: Any) -> _AsyncRequestContext:
188
+ return _AsyncRequestContext(self._request("DELETE", url, **kwargs))
189
+
190
+ async def close(self) -> None:
191
+ await self._client.aclose()
typecast/_user_agent.py CHANGED
@@ -1,13 +1,33 @@
1
1
  import platform
2
+ import re
2
3
  import sys
3
4
  from importlib import metadata
4
-
5
- import aiohttp
6
- import requests
5
+ from typing import Optional
7
6
 
8
7
  from . import conf
9
8
 
10
9
 
10
+ _GENERATED_BY_PATTERN = re.compile(r"[a-z0-9][a-z0-9._-]{0,31}\Z")
11
+
12
+
13
+ def attribution_suffix(
14
+ source: Optional[str] = None,
15
+ generated_by: Optional[str] = None,
16
+ ) -> str:
17
+ if source is None and generated_by is None:
18
+ return ""
19
+ if (
20
+ not isinstance(source, str)
21
+ or not isinstance(generated_by, str)
22
+ or source not in {"llms", "skill"}
23
+ or not generated_by
24
+ ):
25
+ raise ValueError("source (llms or skill) and generated_by must be provided together")
26
+ if not _GENERATED_BY_PATTERN.fullmatch(generated_by):
27
+ raise ValueError("generated_by must be a lowercase token of at most 32 characters")
28
+ return f" typecast-integration/1 (source={source}; generated_by={generated_by})"
29
+
30
+
11
31
  def _package_version(package_name: str) -> str:
12
32
  try:
13
33
  return metadata.version(package_name)
@@ -39,6 +59,8 @@ def build_user_agent(
39
59
  http_library: str,
40
60
  host: str,
41
61
  transport: str = "rest",
62
+ source: Optional[str] = None,
63
+ generated_by: Optional[str] = None,
42
64
  ) -> str:
43
65
  sdk_version = _package_version("typecast-python")
44
66
  base = "default" if conf.is_default_host(host) else "custom"
@@ -49,22 +71,54 @@ def build_user_agent(
49
71
  f"{http_library} "
50
72
  f"(mode={mode}; base={base}; transport={transport}; "
51
73
  f"os={_os_name()}; arch={_arch_name()}; sdk_env=python; platform=server)"
74
+ f"{attribution_suffix(source, generated_by)}"
52
75
  )
53
76
 
54
77
 
55
- def requests_user_agent(host: str, transport: str = "rest") -> str:
78
+ def requests_user_agent(
79
+ host: str,
80
+ transport: str = "rest",
81
+ source: Optional[str] = None,
82
+ generated_by: Optional[str] = None,
83
+ ) -> str:
56
84
  return build_user_agent(
57
85
  mode="sync",
58
- http_library=f"requests/{requests.__version__}",
86
+ http_library=f"requests/{_package_version('requests')}",
59
87
  host=host,
60
88
  transport=transport,
89
+ source=source,
90
+ generated_by=generated_by,
61
91
  )
62
92
 
63
93
 
64
- def aiohttp_user_agent(host: str, transport: str = "rest") -> str:
94
+ def aiohttp_user_agent(
95
+ host: str,
96
+ transport: str = "rest",
97
+ source: Optional[str] = None,
98
+ generated_by: Optional[str] = None,
99
+ ) -> str:
65
100
  return build_user_agent(
66
101
  mode="async",
67
- http_library=f"aiohttp/{aiohttp.__version__}",
102
+ http_library=f"aiohttp/{_package_version('aiohttp')}",
103
+ host=host,
104
+ transport=transport,
105
+ source=source,
106
+ generated_by=generated_by,
107
+ )
108
+
109
+
110
+ def httpx_user_agent(
111
+ host: str,
112
+ mode: str,
113
+ transport: str = "rest",
114
+ source: Optional[str] = None,
115
+ generated_by: Optional[str] = None,
116
+ ) -> str:
117
+ return build_user_agent(
118
+ mode=mode,
119
+ http_library=f"httpx/{_package_version('httpx')}",
68
120
  host=host,
69
121
  transport=transport,
122
+ source=source,
123
+ generated_by=generated_by,
70
124
  )
typecast/async_client.py CHANGED
@@ -1,11 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import sys
4
5
  from pathlib import Path
5
- from typing import AsyncIterator, BinaryIO, Optional, Union
6
+ from typing import TYPE_CHECKING, Any, AsyncIterator, BinaryIO, Optional, Union
6
7
  from urllib.parse import quote
7
8
 
8
- import aiohttp
9
+ if sys.version_info >= (3, 10): # pragma: no cover - version-specific import
10
+ import aiohttp
11
+ else: # pragma: no cover
12
+ aiohttp = None # type: ignore[assignment]
9
13
 
10
14
  from . import conf
11
15
  from ._voice_clone import (
@@ -13,7 +17,10 @@ from ._voice_clone import (
13
17
  validate_clone_inputs,
14
18
  validate_custom_voice_id,
15
19
  )
16
- from ._user_agent import aiohttp_user_agent
20
+ from ._user_agent import aiohttp_user_agent, attribution_suffix, httpx_user_agent
21
+
22
+ if TYPE_CHECKING or sys.version_info < (3, 10): # pragma: no cover
23
+ from ._httpx_compat import AiohttpCompatSession, ClientTimeout, FormData
17
24
  from .client import _guess_audio_mime
18
25
  from .client import _output_with_inferred_format
19
26
  from .client import _validate_output_path
@@ -69,7 +76,9 @@ class AsyncTypecast:
69
76
  self,
70
77
  host: Optional[str] = None,
71
78
  api_key: Optional[str] = None,
72
- session: Optional[aiohttp.ClientSession] = None,
79
+ session: Optional[Any] = None,
80
+ source: Optional[str] = None,
81
+ generated_by: Optional[str] = None,
73
82
  ):
74
83
  """Initialize the async Typecast client.
75
84
 
@@ -81,6 +90,8 @@ class AsyncTypecast:
81
90
  __aenter__ will not create a new session and __aexit__ will not close it
82
91
  (the caller owns its lifecycle). Auth headers (`X-API-KEY`, `User-Agent`)
83
92
  are attached per-request via `_request_headers()`.
93
+ source: Integration source, either 'llms' or 'skill'.
94
+ generated_by: Lowercase token identifying the coding agent.
84
95
 
85
96
  Raises:
86
97
  ValueError: If no API key is provided and TYPECAST_API_KEY is not set
@@ -88,19 +99,39 @@ class AsyncTypecast:
88
99
  """
89
100
  self.host = conf.get_host(host)
90
101
  self.api_key = conf.get_api_key(api_key)
102
+ attribution_suffix(source, generated_by)
103
+ self.source = source
104
+ self.generated_by = generated_by
91
105
  if not self.api_key and conf.is_default_host(self.host):
92
106
  raise ValueError("API key is required for the default Typecast API host")
93
107
  self._owns_session = session is None
94
- self.session: Optional[aiohttp.ClientSession] = session
108
+ self.session: Optional[Any] = session
95
109
 
96
110
  async def __aenter__(self):
97
111
  # When an external session is injected, do not create a new one.
98
112
  # Per-request auth headers are attached via _request_headers() on each call.
99
113
  if self.session is None:
100
- headers = {"User-Agent": aiohttp_user_agent(self.host)}
114
+ headers = {
115
+ "User-Agent": (
116
+ aiohttp_user_agent(
117
+ self.host, source=self.source, generated_by=self.generated_by
118
+ )
119
+ if aiohttp
120
+ else httpx_user_agent(
121
+ self.host,
122
+ "async",
123
+ source=self.source,
124
+ generated_by=self.generated_by,
125
+ )
126
+ )
127
+ }
101
128
  if self.api_key:
102
129
  headers["X-API-KEY"] = self.api_key
103
- self.session = aiohttp.ClientSession(headers=headers)
130
+ self.session = (
131
+ aiohttp.ClientSession(headers=headers)
132
+ if aiohttp
133
+ else AiohttpCompatSession(headers=headers)
134
+ )
104
135
  return self
105
136
 
106
137
  async def __aexit__(self, exc_type, exc_val, exc_tb):
@@ -140,7 +171,11 @@ class AsyncTypecast:
140
171
  """
141
172
  if self._owns_session:
142
173
  return None
143
- headers = {"User-Agent": aiohttp_user_agent(self.host)}
174
+ headers = {
175
+ "User-Agent": aiohttp_user_agent(
176
+ self.host, source=self.source, generated_by=self.generated_by
177
+ )
178
+ }
144
179
  if self.api_key:
145
180
  headers["X-API-KEY"] = self.api_key
146
181
  return headers
@@ -247,7 +282,11 @@ class AsyncTypecast:
247
282
  if not self.session:
248
283
  raise TypecastError("Client session not initialized. Use async with.")
249
284
  endpoint = "/v1/text-to-speech/stream"
250
- stream_timeout = aiohttp.ClientTimeout(sock_connect=10, sock_read=300)
285
+ stream_timeout = (
286
+ aiohttp.ClientTimeout(sock_connect=10, sock_read=300)
287
+ if aiohttp
288
+ else ClientTimeout(sock_connect=10, sock_read=300)
289
+ )
251
290
  async with self.session.post(
252
291
  f"{self.host}{endpoint}",
253
292
  json=request.model_dump(exclude_none=True),
@@ -258,7 +297,9 @@ class AsyncTypecast:
258
297
  error_text = await response.text()
259
298
  self._handle_error(response.status, error_text)
260
299
 
261
- async for chunk in response.content.iter_chunked(chunk_size): # pragma: no branch
300
+ async for chunk in response.content.iter_chunked(
301
+ chunk_size
302
+ ): # pragma: no branch
262
303
  yield chunk
263
304
 
264
305
  async def text_to_speech_with_timestamps(
@@ -332,7 +373,7 @@ class AsyncTypecast:
332
373
  audio_bytes, filename = validate_clone_inputs(audio, name)
333
374
  model_str = normalize_clone_model(model)
334
375
 
335
- form = aiohttp.FormData()
376
+ form: Any = aiohttp.FormData() if aiohttp else FormData()
336
377
  form.add_field("name", name)
337
378
  form.add_field("model", model_str)
338
379
  form.add_field(
@@ -341,7 +382,11 @@ class AsyncTypecast:
341
382
  filename=filename,
342
383
  content_type=_guess_audio_mime(filename),
343
384
  )
344
- timeout = aiohttp.ClientTimeout(total=300, connect=10)
385
+ timeout = (
386
+ aiohttp.ClientTimeout(total=300, connect=10)
387
+ if aiohttp
388
+ else ClientTimeout(total=300, connect=10)
389
+ )
345
390
  async with self.session.post(
346
391
  f"{self.host}/v1/voices/clone",
347
392
  data=form,
@@ -367,7 +412,11 @@ class AsyncTypecast:
367
412
  raise TypecastError("Client session not initialized; use 'async with'.")
368
413
 
369
414
  validate_custom_voice_id(voice_id)
370
- timeout = aiohttp.ClientTimeout(total=60, connect=10)
415
+ timeout = (
416
+ aiohttp.ClientTimeout(total=60, connect=10)
417
+ if aiohttp
418
+ else ClientTimeout(total=60, connect=10)
419
+ )
371
420
  async with self.session.delete(
372
421
  f"{self.host}/v1/voices/{quote(voice_id, safe='')}",
373
422
  timeout=timeout,
typecast/client.py CHANGED
@@ -1,10 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import sys
3
4
  from pathlib import Path
4
- from typing import BinaryIO, Iterator, Optional, Union
5
+ from typing import TYPE_CHECKING, Any, BinaryIO, Iterator, Optional, Union
5
6
  from urllib.parse import quote
6
7
 
7
- import requests
8
+ if sys.version_info >= (3, 10): # pragma: no cover - version-specific import
9
+ import requests
10
+ else: # pragma: no cover
11
+ requests = None # type: ignore[assignment]
8
12
 
9
13
  from . import conf
10
14
  from ._voice_clone import (
@@ -12,7 +16,10 @@ from ._voice_clone import (
12
16
  validate_clone_inputs,
13
17
  validate_custom_voice_id,
14
18
  )
15
- from ._user_agent import requests_user_agent
19
+ from ._user_agent import attribution_suffix, httpx_user_agent, requests_user_agent
20
+
21
+ if TYPE_CHECKING or sys.version_info < (3, 10): # pragma: no cover
22
+ from ._httpx_compat import RequestsCompatSession
16
23
  from .composer import SpeechComposer
17
24
  from .exceptions import (
18
25
  BadRequestError,
@@ -103,7 +110,9 @@ class Typecast:
103
110
  self,
104
111
  host: Optional[str] = None,
105
112
  api_key: Optional[str] = None,
106
- session: Optional[requests.Session] = None,
113
+ session: Optional[Any] = None,
114
+ source: Optional[str] = None,
115
+ generated_by: Optional[str] = None,
107
116
  ):
108
117
  """Initialize the Typecast client.
109
118
 
@@ -115,22 +124,32 @@ class Typecast:
115
124
  the client will not create a new session nor close it; auth headers
116
125
  (`X-API-KEY`, `User-Agent`) are attached per-request via
117
126
  `_request_headers()`.
127
+ source: Integration source, either 'llms' or 'skill'.
128
+ generated_by: Lowercase token identifying the coding agent.
118
129
 
119
130
  Raises:
120
131
  ValueError: If no API key is provided and TYPECAST_API_KEY is not set.
121
132
  """
122
133
  self.host = conf.get_host(host)
123
134
  self.api_key = conf.get_api_key(api_key)
135
+ attribution_suffix(source, generated_by)
136
+ self.source = source
137
+ self.generated_by = generated_by
124
138
  if not self.api_key and conf.is_default_host(self.host):
125
139
  raise ValueError("API key is required for the default Typecast API host")
126
140
  self._owns_session = session is None
141
+ self.session: Any
127
142
  if session is not None:
128
143
  self.session = session
129
144
  else:
130
- self.session = requests.Session()
145
+ self.session = requests.Session() if requests else RequestsCompatSession()
131
146
  headers = {
132
147
  "Content-Type": "application/json",
133
- "User-Agent": requests_user_agent(self.host),
148
+ "User-Agent": (
149
+ requests_user_agent(self.host, source=source, generated_by=generated_by)
150
+ if requests
151
+ else httpx_user_agent(self.host, "sync", source=source, generated_by=generated_by)
152
+ ),
134
153
  }
135
154
  if self.api_key:
136
155
  headers["X-API-KEY"] = self.api_key
@@ -167,7 +186,11 @@ class Typecast:
167
186
  """
168
187
  if self._owns_session:
169
188
  return None
170
- headers = {"User-Agent": requests_user_agent(self.host)}
189
+ headers = {
190
+ "User-Agent": requests_user_agent(
191
+ self.host, source=self.source, generated_by=self.generated_by
192
+ )
193
+ }
171
194
  if self.api_key:
172
195
  headers["X-API-KEY"] = self.api_key
173
196
  return headers
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: typecast-python
3
- Version: 0.3.10
3
+ Version: 0.3.12
4
4
  Summary: Official Typecast Python SDK - Convert text to lifelike speech using AI-powered voices
5
5
  Project-URL: Homepage, https://typecast.ai
6
6
  Project-URL: Documentation, https://typecast.ai/docs/overview
@@ -227,20 +227,22 @@ Classifier: Programming Language :: Python :: 3.14
227
227
  Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
228
228
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
229
229
  Requires-Python: <3.15,>=3.8
230
- Requires-Dist: aiohttp>=3.9.0
230
+ Requires-Dist: aiohttp>=3.14.1; python_version >= '3.10'
231
+ Requires-Dist: httpx<1,>=0.28.1; python_version < '3.10'
231
232
  Requires-Dist: pydantic>=2.0.0
232
- Requires-Dist: requests>=2.28.0
233
+ Requires-Dist: requests>=2.33.0; python_version >= '3.10'
233
234
  Requires-Dist: typing-extensions>=4.0.0
234
235
  Provides-Extra: dev
235
236
  Requires-Dist: aioresponses>=0.7.6; extra == 'dev'
236
- Requires-Dist: black<25.0.0,>=23.0.0; extra == 'dev'
237
- Requires-Dist: flake8<6.0.0,>=5.0.0; extra == 'dev'
237
+ Requires-Dist: black<27.0.0,>=26.3.1; extra == 'dev'
238
+ Requires-Dist: flake8<8.0.0,>=5.0.0; extra == 'dev'
239
+ Requires-Dist: httpx<1.0.0,>=0.28.1; extra == 'dev'
238
240
  Requires-Dist: isort>=5.0.0; extra == 'dev'
239
- Requires-Dist: mypy<1.11.0,>=1.0.0; extra == 'dev'
240
- Requires-Dist: pytest-asyncio<1.0.0,>=0.21.0; extra == 'dev'
241
- Requires-Dist: pytest-cov<6.0.0,>=5.0.0; extra == 'dev'
241
+ Requires-Dist: mypy<2.4.0,>=1.0.0; extra == 'dev'
242
+ Requires-Dist: pytest-asyncio<2.0.0,>=1.4.0; extra == 'dev'
243
+ Requires-Dist: pytest-cov<8.0.0,>=5.0.0; extra == 'dev'
242
244
  Requires-Dist: pytest-mock>=3.14.0; extra == 'dev'
243
- Requires-Dist: pytest<9.0.0,>=7.0.0; extra == 'dev'
245
+ Requires-Dist: pytest<10.0.0,>=9.0.3; extra == 'dev'
244
246
  Description-Content-Type: text/markdown
245
247
 
246
248
  <div align="center">
@@ -1,8 +1,9 @@
1
1
  typecast/__init__.py,sha256=L6m0Z1jn1UgGjPp9AFiTwhHk9DVV_B4WrLfQDEfbcZI,1093
2
- typecast/_user_agent.py,sha256=R_0eSKpuP9jLp_wDJcJGFvFtUh9BZwEgsT1buVeHRAA,1786
2
+ typecast/_httpx_compat.py,sha256=Xw4AvybiiXyaA2WATBUYHwST8clE8R7R0flLMpdvBC4,6170
3
+ typecast/_user_agent.py,sha256=-QTjpg1fDDsPI_n5sYf-4zPqna_24w0TWqp8OknTthQ,3350
3
4
  typecast/_voice_clone.py,sha256=TN2tbB3b5lC5uFCBnbERhz37bNJlbv6VZ-vj70EfTs4,3464
4
- typecast/async_client.py,sha256=Gidl-gj0zGMzZCPzmMRcr2GiYFi0cBmZOwrLhSVRL_Y,21700
5
- typecast/client.py,sha256=Ac6hcginhJheh6CcstzaMtyXzRbnSfUerI8rt6l_f58,21695
5
+ typecast/async_client.py,sha256=leZ_DGgeJBDCSr7LJCQ6VxCEt-sjYK8X2khEZIsLO7w,23400
6
+ typecast/client.py,sha256=k-dyxf-JcgXD0WFDW2_K2iRag9iwkTW_wt30z26Qi-0,22756
6
7
  typecast/composer.py,sha256=pkRSVIv99uuAwPyOXw7KSW7NAkqvAUK4hCS5pmBb7bA,7313
7
8
  typecast/conf.py,sha256=yfOHYZDvIshWWMriN5wE0GFPR8AX3zv4Rg2655OvA9Q,724
8
9
  typecast/exceptions.py,sha256=Y0ZzYebe8zOSOSAHbXfKR0G_RJgdmZXxi15Z7ZxPLIk,1568
@@ -12,7 +13,7 @@ typecast/models/error.py,sha256=XomIjx7jvlCjItqzJuCAT4mXC9jwTjxR8lLDUk6P8KA,152
12
13
  typecast/models/subscription.py,sha256=EIaAAo3cCRw8LYT_O6D9AVwxqIHrWCijzl4UTx7FZB8,894
13
14
  typecast/models/tts.py,sha256=xiO1e936vFr7vZqUErTEhexydoaMem6BcY_x1A1zFow,16495
14
15
  typecast/models/voices.py,sha256=3UeBLwAlaNbQQUrt7FO18zqczLoP7jmi2x68yaUtllU,2687
15
- typecast_python-0.3.10.dist-info/METADATA,sha256=z8W8o3SeRtx4C9PWgEvRCJ3fOcfoVTy2Utp0mqUvkOs,25786
16
- typecast_python-0.3.10.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
17
- typecast_python-0.3.10.dist-info/licenses/LICENSE,sha256=HvtJ-S89uUkuYmt-OvVk4MRxmzwtbn84__qJtSrGU2Q,11348
18
- typecast_python-0.3.10.dist-info/RECORD,,
16
+ typecast_python-0.3.12.dist-info/METADATA,sha256=RrOSDitrzeRnGq2syDnkVdWv4H0Cb097jASB02sFIJM,25947
17
+ typecast_python-0.3.12.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
18
+ typecast_python-0.3.12.dist-info/licenses/LICENSE,sha256=HvtJ-S89uUkuYmt-OvVk4MRxmzwtbn84__qJtSrGU2Q,11348
19
+ typecast_python-0.3.12.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.31.0
2
+ Generator: hatchling 1.32.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any