typecast-python 0.3.5__tar.gz → 0.3.7__tar.gz

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 (19) hide show
  1. {typecast_python-0.3.5 → typecast_python-0.3.7}/PKG-INFO +1 -1
  2. {typecast_python-0.3.5 → typecast_python-0.3.7}/pyproject.toml +1 -1
  3. typecast_python-0.3.7/src/typecast/_user_agent.py +70 -0
  4. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/async_client.py +8 -4
  5. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/client.py +11 -4
  6. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/models/tts.py +9 -4
  7. {typecast_python-0.3.5 → typecast_python-0.3.7}/.gitignore +0 -0
  8. {typecast_python-0.3.5 → typecast_python-0.3.7}/LICENSE +0 -0
  9. {typecast_python-0.3.5 → typecast_python-0.3.7}/README.md +0 -0
  10. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/__init__.py +0 -0
  11. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/_voice_clone.py +0 -0
  12. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/composer.py +0 -0
  13. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/conf.py +0 -0
  14. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/exceptions.py +0 -0
  15. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/models/__init__.py +0 -0
  16. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/models/error.py +0 -0
  17. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/models/subscription.py +0 -0
  18. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/models/voices.py +0 -0
  19. {typecast_python-0.3.5 → typecast_python-0.3.7}/src/typecast/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typecast-python
3
- Version: 0.3.5
3
+ Version: 0.3.7
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "typecast-python"
7
- version = "0.3.5"
7
+ version = "0.3.7"
8
8
  description = "Official Typecast Python SDK - Convert text to lifelike speech using AI-powered voices"
9
9
  authors = [
10
10
  {name = "Neosapience", email = "help@typecast.ai"}
@@ -0,0 +1,70 @@
1
+ import platform
2
+ import sys
3
+ from importlib import metadata
4
+
5
+ import aiohttp
6
+ import requests
7
+
8
+ from . import conf
9
+
10
+
11
+ def _package_version(package_name: str) -> str:
12
+ try:
13
+ return metadata.version(package_name)
14
+ except metadata.PackageNotFoundError:
15
+ return "dev"
16
+
17
+
18
+ def _os_name() -> str:
19
+ system = platform.system().lower()
20
+ if system == "darwin":
21
+ return "macos"
22
+ if system.startswith("windows"):
23
+ return "windows"
24
+ return system or "unknown"
25
+
26
+
27
+ def _arch_name() -> str:
28
+ machine = platform.machine().lower()
29
+ if machine in {"x86_64", "amd64"}:
30
+ return "x64"
31
+ if machine in {"aarch64", "arm64"}:
32
+ return "arm64"
33
+ return machine or "unknown"
34
+
35
+
36
+ def build_user_agent(
37
+ *,
38
+ mode: str,
39
+ http_library: str,
40
+ host: str,
41
+ transport: str = "rest",
42
+ ) -> str:
43
+ sdk_version = _package_version("typecast-python")
44
+ base = "default" if conf.is_default_host(host) else "custom"
45
+ return (
46
+ f"typecast-python/{sdk_version} "
47
+ f"Python/{sys.version_info.major}.{sys.version_info.minor} "
48
+ f"{platform.python_implementation()}/{platform.python_version()} "
49
+ f"{http_library} "
50
+ f"(mode={mode}; base={base}; transport={transport}; "
51
+ f"os={_os_name()}; arch={_arch_name()}; sdk_env=python; platform=server)"
52
+ )
53
+
54
+
55
+ def requests_user_agent(host: str, transport: str = "rest") -> str:
56
+ return build_user_agent(
57
+ mode="sync",
58
+ http_library=f"requests/{requests.__version__}",
59
+ host=host,
60
+ transport=transport,
61
+ )
62
+
63
+
64
+ def aiohttp_user_agent(host: str, transport: str = "rest") -> str:
65
+ return build_user_agent(
66
+ mode="async",
67
+ http_library=f"aiohttp/{aiohttp.__version__}",
68
+ host=host,
69
+ transport=transport,
70
+ )
@@ -11,6 +11,7 @@ from ._voice_clone import (
11
11
  validate_clone_inputs,
12
12
  validate_custom_voice_id,
13
13
  )
14
+ from ._user_agent import aiohttp_user_agent
14
15
  from .client import _guess_audio_mime
15
16
  from .client import _output_with_inferred_format
16
17
  from .client import _validate_output_path
@@ -81,7 +82,7 @@ class AsyncTypecast:
81
82
  async def __aenter__(self):
82
83
  # Auth header at session scope; per-request Content-Type is set by aiohttp
83
84
  # (json= auto-sets application/json, data=FormData() auto-sets multipart).
84
- headers = {}
85
+ headers = {"User-Agent": aiohttp_user_agent(self.host)}
85
86
  if self.api_key:
86
87
  headers["X-API-KEY"] = self.api_key
87
88
  self.session = aiohttp.ClientSession(headers=headers)
@@ -191,8 +192,7 @@ class AsyncTypecast:
191
192
 
192
193
  Args:
193
194
  request: Streaming TTS request. Uses `OutputStream`, which omits
194
- `volume` and `target_lufs` (not supported by the streaming
195
- endpoint).
195
+ `volume` (not supported by the streaming endpoint).
196
196
  chunk_size: Maximum bytes returned per yielded chunk.
197
197
 
198
198
  Yields:
@@ -204,7 +204,11 @@ class AsyncTypecast:
204
204
  NotFoundError, UnprocessableEntityError, RateLimitError,
205
205
  InternalServerError, TypecastError: depending on response status.
206
206
  """
207
- if not isinstance(chunk_size, int) or isinstance(chunk_size, bool) or chunk_size < 1:
207
+ if (
208
+ not isinstance(chunk_size, int)
209
+ or isinstance(chunk_size, bool)
210
+ or chunk_size < 1
211
+ ):
208
212
  raise ValueError("chunk_size must be a positive integer")
209
213
  if not self.session:
210
214
  raise TypecastError("Client session not initialized. Use async with.")
@@ -10,6 +10,7 @@ from ._voice_clone import (
10
10
  validate_clone_inputs,
11
11
  validate_custom_voice_id,
12
12
  )
13
+ from ._user_agent import requests_user_agent
13
14
  from .composer import SpeechComposer
14
15
  from .exceptions import (
15
16
  BadRequestError,
@@ -111,7 +112,10 @@ class Typecast:
111
112
  if not self.api_key and conf.is_default_host(self.host):
112
113
  raise ValueError("API key is required for the default Typecast API host")
113
114
  self.session = requests.Session()
114
- headers = {"Content-Type": "application/json"}
115
+ headers = {
116
+ "Content-Type": "application/json",
117
+ "User-Agent": requests_user_agent(self.host),
118
+ }
115
119
  if self.api_key:
116
120
  headers["X-API-KEY"] = self.api_key
117
121
  self.session.headers.update(headers)
@@ -225,8 +229,7 @@ class Typecast:
225
229
 
226
230
  Args:
227
231
  request: Streaming TTS request. Uses `OutputStream`, which omits
228
- `volume` and `target_lufs` (not supported by the streaming
229
- endpoint).
232
+ `volume` (not supported by the streaming endpoint).
230
233
  chunk_size: Maximum bytes returned per yielded chunk.
231
234
 
232
235
  Yields:
@@ -237,7 +240,11 @@ class Typecast:
237
240
  NotFoundError, UnprocessableEntityError, RateLimitError,
238
241
  InternalServerError, TypecastError: depending on response status.
239
242
  """
240
- if not isinstance(chunk_size, int) or isinstance(chunk_size, bool) or chunk_size < 1:
243
+ if (
244
+ not isinstance(chunk_size, int)
245
+ or isinstance(chunk_size, bool)
246
+ or chunk_size < 1
247
+ ):
241
248
  raise ValueError("chunk_size must be a positive integer")
242
249
  endpoint = "/v1/text-to-speech/stream"
243
250
  response = self.session.post(
@@ -183,9 +183,8 @@ class TTSResponse(BaseModel):
183
183
  class OutputStream(BaseModel):
184
184
  """Audio output settings for streaming mode.
185
185
 
186
- Streaming mode does not support `volume` or `target_lufs` because the
187
- server has to commit each chunk before the full waveform is known.
188
- Passing either field raises a validation error so misuse fails fast.
186
+ Streaming mode does not support `volume`, but it supports `target_lufs`
187
+ for absolute loudness normalization.
189
188
  """
190
189
 
191
190
  model_config = ConfigDict(extra="forbid")
@@ -195,12 +194,18 @@ class OutputStream(BaseModel):
195
194
  audio_format: Optional[str] = Field(
196
195
  default="wav", description="Audio format", examples=["wav", "mp3"]
197
196
  )
197
+ target_lufs: Optional[float] = Field(
198
+ default=None,
199
+ ge=-70.0,
200
+ le=0.0,
201
+ description="Target loudness in LUFS for streaming output normalization (-70 to 0).",
202
+ )
198
203
 
199
204
 
200
205
  class TTSRequestStream(BaseModel):
201
206
  """Request body for `POST /v1/text-to-speech/stream`.
202
207
 
203
- Mirrors `TTSRequest` but uses `OutputStream` (no volume / target_lufs).
208
+ Mirrors `TTSRequest` but uses `OutputStream` (no volume).
204
209
  """
205
210
 
206
211
  model_config = ConfigDict(json_schema_extra={"exclude_none": True})
File without changes