typecast-python 0.3.11__tar.gz → 0.3.13__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 (20) hide show
  1. {typecast_python-0.3.11 → typecast_python-0.3.13}/PKG-INFO +5 -5
  2. {typecast_python-0.3.11 → typecast_python-0.3.13}/pyproject.toml +5 -5
  3. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/_user_agent.py +53 -3
  4. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/async_client.py +22 -4
  5. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/client.py +15 -4
  6. {typecast_python-0.3.11 → typecast_python-0.3.13}/.gitignore +0 -0
  7. {typecast_python-0.3.11 → typecast_python-0.3.13}/LICENSE +0 -0
  8. {typecast_python-0.3.11 → typecast_python-0.3.13}/README.md +0 -0
  9. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/__init__.py +0 -0
  10. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/_httpx_compat.py +0 -0
  11. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/_voice_clone.py +0 -0
  12. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/composer.py +0 -0
  13. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/conf.py +0 -0
  14. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/exceptions.py +0 -0
  15. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/models/__init__.py +0 -0
  16. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/models/error.py +0 -0
  17. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/models/subscription.py +0 -0
  18. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/models/tts.py +0 -0
  19. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/models/voices.py +0 -0
  20. {typecast_python-0.3.11 → typecast_python-0.3.13}/src/typecast/utils.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: typecast-python
3
- Version: 0.3.11
3
+ Version: 0.3.13
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
@@ -235,12 +235,12 @@ Requires-Dist: typing-extensions>=4.0.0
235
235
  Provides-Extra: dev
236
236
  Requires-Dist: aioresponses>=0.7.6; extra == 'dev'
237
237
  Requires-Dist: black<27.0.0,>=26.3.1; extra == 'dev'
238
- Requires-Dist: flake8<6.0.0,>=5.0.0; extra == 'dev'
238
+ Requires-Dist: flake8<8.0.0,>=5.0.0; extra == 'dev'
239
239
  Requires-Dist: httpx<1.0.0,>=0.28.1; extra == 'dev'
240
240
  Requires-Dist: isort>=5.0.0; extra == 'dev'
241
- Requires-Dist: mypy<1.11.0,>=1.0.0; extra == 'dev'
241
+ Requires-Dist: mypy<2.4.0,>=1.0.0; extra == 'dev'
242
242
  Requires-Dist: pytest-asyncio<2.0.0,>=1.4.0; extra == 'dev'
243
- Requires-Dist: pytest-cov<6.0.0,>=5.0.0; extra == 'dev'
243
+ Requires-Dist: pytest-cov<8.0.0,>=5.0.0; extra == 'dev'
244
244
  Requires-Dist: pytest-mock>=3.14.0; extra == 'dev'
245
245
  Requires-Dist: pytest<10.0.0,>=9.0.3; extra == 'dev'
246
246
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "typecast-python"
7
- version = "0.3.11"
7
+ version = "0.3.13"
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"}
@@ -43,14 +43,14 @@ dependencies = [
43
43
  [project.optional-dependencies]
44
44
  dev = [
45
45
  "pytest>=9.0.3,<10.0.0",
46
- "pytest-cov>=5.0.0,<6.0.0",
46
+ "pytest-cov>=5.0.0,<8.0.0",
47
47
  "pytest-mock>=3.14.0",
48
48
  "pytest-asyncio>=1.4.0,<2.0.0",
49
49
  "aioresponses>=0.7.6",
50
50
  "httpx>=0.28.1,<1.0.0",
51
51
  "black>=26.3.1,<27.0.0",
52
- "flake8>=5.0.0,<6.0.0",
53
- "mypy>=1.0.0,<1.11.0",
52
+ "flake8>=5.0.0,<8.0.0",
53
+ "mypy>=1.0.0,<2.4.0",
54
54
  "isort>=5.0.0",
55
55
  ]
56
56
 
@@ -93,7 +93,7 @@ multi_line_output = 3
93
93
  [dependency-groups]
94
94
  dev = [
95
95
  "pytest>=9.0.3,<10.0.0",
96
- "pytest-cov>=5.0.0,<6.0.0",
96
+ "pytest-cov>=5.0.0,<8.0.0",
97
97
  "pytest-mock>=3.14.0",
98
98
  "pytest-asyncio>=1.4.0,<2.0.0",
99
99
  "aioresponses>=0.7.6",
@@ -1,10 +1,35 @@
1
1
  import platform
2
+ import re
2
3
  import sys
3
4
  from importlib import metadata
5
+ from typing import Optional
4
6
 
5
7
  from . import conf
6
8
 
7
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", "api-page", "api-docs"}
23
+ or not generated_by
24
+ ):
25
+ raise ValueError(
26
+ "source (llms, skill, api-page, or api-docs) and generated_by must be provided together"
27
+ )
28
+ if not _GENERATED_BY_PATTERN.fullmatch(generated_by):
29
+ raise ValueError("generated_by must be a lowercase token of at most 32 characters")
30
+ return f" typecast-integration/1 (source={source}; generated_by={generated_by})"
31
+
32
+
8
33
  def _package_version(package_name: str) -> str:
9
34
  try:
10
35
  return metadata.version(package_name)
@@ -36,6 +61,8 @@ def build_user_agent(
36
61
  http_library: str,
37
62
  host: str,
38
63
  transport: str = "rest",
64
+ source: Optional[str] = None,
65
+ generated_by: Optional[str] = None,
39
66
  ) -> str:
40
67
  sdk_version = _package_version("typecast-python")
41
68
  base = "default" if conf.is_default_host(host) else "custom"
@@ -46,31 +73,54 @@ def build_user_agent(
46
73
  f"{http_library} "
47
74
  f"(mode={mode}; base={base}; transport={transport}; "
48
75
  f"os={_os_name()}; arch={_arch_name()}; sdk_env=python; platform=server)"
76
+ f"{attribution_suffix(source, generated_by)}"
49
77
  )
50
78
 
51
79
 
52
- def requests_user_agent(host: str, transport: str = "rest") -> str:
80
+ def requests_user_agent(
81
+ host: str,
82
+ transport: str = "rest",
83
+ source: Optional[str] = None,
84
+ generated_by: Optional[str] = None,
85
+ ) -> str:
53
86
  return build_user_agent(
54
87
  mode="sync",
55
88
  http_library=f"requests/{_package_version('requests')}",
56
89
  host=host,
57
90
  transport=transport,
91
+ source=source,
92
+ generated_by=generated_by,
58
93
  )
59
94
 
60
95
 
61
- def aiohttp_user_agent(host: str, transport: str = "rest") -> str:
96
+ def aiohttp_user_agent(
97
+ host: str,
98
+ transport: str = "rest",
99
+ source: Optional[str] = None,
100
+ generated_by: Optional[str] = None,
101
+ ) -> str:
62
102
  return build_user_agent(
63
103
  mode="async",
64
104
  http_library=f"aiohttp/{_package_version('aiohttp')}",
65
105
  host=host,
66
106
  transport=transport,
107
+ source=source,
108
+ generated_by=generated_by,
67
109
  )
68
110
 
69
111
 
70
- def httpx_user_agent(host: str, mode: str, transport: str = "rest") -> str:
112
+ def httpx_user_agent(
113
+ host: str,
114
+ mode: str,
115
+ transport: str = "rest",
116
+ source: Optional[str] = None,
117
+ generated_by: Optional[str] = None,
118
+ ) -> str:
71
119
  return build_user_agent(
72
120
  mode=mode,
73
121
  http_library=f"httpx/{_package_version('httpx')}",
74
122
  host=host,
75
123
  transport=transport,
124
+ source=source,
125
+ generated_by=generated_by,
76
126
  )
@@ -17,7 +17,7 @@ from ._voice_clone import (
17
17
  validate_clone_inputs,
18
18
  validate_custom_voice_id,
19
19
  )
20
- from ._user_agent import aiohttp_user_agent, httpx_user_agent
20
+ from ._user_agent import aiohttp_user_agent, attribution_suffix, httpx_user_agent
21
21
 
22
22
  if TYPE_CHECKING or sys.version_info < (3, 10): # pragma: no cover
23
23
  from ._httpx_compat import AiohttpCompatSession, ClientTimeout, FormData
@@ -77,6 +77,8 @@ class AsyncTypecast:
77
77
  host: Optional[str] = None,
78
78
  api_key: Optional[str] = None,
79
79
  session: Optional[Any] = None,
80
+ source: Optional[str] = None,
81
+ generated_by: Optional[str] = None,
80
82
  ):
81
83
  """Initialize the async Typecast client.
82
84
 
@@ -88,6 +90,8 @@ class AsyncTypecast:
88
90
  __aenter__ will not create a new session and __aexit__ will not close it
89
91
  (the caller owns its lifecycle). Auth headers (`X-API-KEY`, `User-Agent`)
90
92
  are attached per-request via `_request_headers()`.
93
+ source: Integration source: 'llms', 'skill', 'api-page', or 'api-docs'.
94
+ generated_by: Lowercase token identifying the coding agent.
91
95
 
92
96
  Raises:
93
97
  ValueError: If no API key is provided and TYPECAST_API_KEY is not set
@@ -95,6 +99,9 @@ class AsyncTypecast:
95
99
  """
96
100
  self.host = conf.get_host(host)
97
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
98
105
  if not self.api_key and conf.is_default_host(self.host):
99
106
  raise ValueError("API key is required for the default Typecast API host")
100
107
  self._owns_session = session is None
@@ -106,9 +113,16 @@ class AsyncTypecast:
106
113
  if self.session is None:
107
114
  headers = {
108
115
  "User-Agent": (
109
- aiohttp_user_agent(self.host)
116
+ aiohttp_user_agent(
117
+ self.host, source=self.source, generated_by=self.generated_by
118
+ )
110
119
  if aiohttp
111
- else httpx_user_agent(self.host, "async")
120
+ else httpx_user_agent(
121
+ self.host,
122
+ "async",
123
+ source=self.source,
124
+ generated_by=self.generated_by,
125
+ )
112
126
  )
113
127
  }
114
128
  if self.api_key:
@@ -157,7 +171,11 @@ class AsyncTypecast:
157
171
  """
158
172
  if self._owns_session:
159
173
  return None
160
- 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
+ }
161
179
  if self.api_key:
162
180
  headers["X-API-KEY"] = self.api_key
163
181
  return headers
@@ -16,7 +16,7 @@ from ._voice_clone import (
16
16
  validate_clone_inputs,
17
17
  validate_custom_voice_id,
18
18
  )
19
- from ._user_agent import httpx_user_agent, requests_user_agent
19
+ from ._user_agent import attribution_suffix, httpx_user_agent, requests_user_agent
20
20
 
21
21
  if TYPE_CHECKING or sys.version_info < (3, 10): # pragma: no cover
22
22
  from ._httpx_compat import RequestsCompatSession
@@ -111,6 +111,8 @@ class Typecast:
111
111
  host: Optional[str] = None,
112
112
  api_key: Optional[str] = None,
113
113
  session: Optional[Any] = None,
114
+ source: Optional[str] = None,
115
+ generated_by: Optional[str] = None,
114
116
  ):
115
117
  """Initialize the Typecast client.
116
118
 
@@ -122,12 +124,17 @@ class Typecast:
122
124
  the client will not create a new session nor close it; auth headers
123
125
  (`X-API-KEY`, `User-Agent`) are attached per-request via
124
126
  `_request_headers()`.
127
+ source: Integration source: 'llms', 'skill', 'api-page', or 'api-docs'.
128
+ generated_by: Lowercase token identifying the coding agent.
125
129
 
126
130
  Raises:
127
131
  ValueError: If no API key is provided and TYPECAST_API_KEY is not set.
128
132
  """
129
133
  self.host = conf.get_host(host)
130
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
131
138
  if not self.api_key and conf.is_default_host(self.host):
132
139
  raise ValueError("API key is required for the default Typecast API host")
133
140
  self._owns_session = session is None
@@ -139,9 +146,9 @@ class Typecast:
139
146
  headers = {
140
147
  "Content-Type": "application/json",
141
148
  "User-Agent": (
142
- requests_user_agent(self.host)
149
+ requests_user_agent(self.host, source=source, generated_by=generated_by)
143
150
  if requests
144
- else httpx_user_agent(self.host, "sync")
151
+ else httpx_user_agent(self.host, "sync", source=source, generated_by=generated_by)
145
152
  ),
146
153
  }
147
154
  if self.api_key:
@@ -179,7 +186,11 @@ class Typecast:
179
186
  """
180
187
  if self._owns_session:
181
188
  return None
182
- 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
+ }
183
194
  if self.api_key:
184
195
  headers["X-API-KEY"] = self.api_key
185
196
  return headers