hakim 1.0.0__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 (40) hide show
  1. hakim-1.0.0/.gitignore +11 -0
  2. hakim-1.0.0/LICENSE +21 -0
  3. hakim-1.0.0/PKG-INFO +259 -0
  4. hakim-1.0.0/README.md +222 -0
  5. hakim-1.0.0/pyproject.toml +101 -0
  6. hakim-1.0.0/src/hakim/__init__.py +64 -0
  7. hakim-1.0.0/src/hakim/_transport.py +345 -0
  8. hakim-1.0.0/src/hakim/_types.py +789 -0
  9. hakim-1.0.0/src/hakim/_version.py +7 -0
  10. hakim-1.0.0/src/hakim/audio/__init__.py +18 -0
  11. hakim-1.0.0/src/hakim/audio/speech.py +177 -0
  12. hakim-1.0.0/src/hakim/audio/speech_stream_ws.py +262 -0
  13. hakim-1.0.0/src/hakim/audio/transcriptions.py +361 -0
  14. hakim-1.0.0/src/hakim/audio/translate.py +100 -0
  15. hakim-1.0.0/src/hakim/audio/translate_stream_ws.py +351 -0
  16. hakim-1.0.0/src/hakim/audio/voices.py +129 -0
  17. hakim-1.0.0/src/hakim/chat/__init__.py +5 -0
  18. hakim-1.0.0/src/hakim/chat/completions.py +297 -0
  19. hakim-1.0.0/src/hakim/client.py +464 -0
  20. hakim-1.0.0/src/hakim/errors.py +181 -0
  21. hakim-1.0.0/src/hakim/jobs.py +57 -0
  22. hakim-1.0.0/src/hakim/settings.py +99 -0
  23. hakim-1.0.0/src/hakim/usage.py +101 -0
  24. hakim-1.0.0/src/hakim/webhooks.py +237 -0
  25. hakim-1.0.0/tests/__init__.py +0 -0
  26. hakim-1.0.0/tests/_helpers.py +21 -0
  27. hakim-1.0.0/tests/conftest.py +22 -0
  28. hakim-1.0.0/tests/test_audio_speech.py +110 -0
  29. hakim-1.0.0/tests/test_audio_speech_stream_ws.py +155 -0
  30. hakim-1.0.0/tests/test_audio_transcriptions.py +208 -0
  31. hakim-1.0.0/tests/test_audio_translate_stream_ws.py +270 -0
  32. hakim-1.0.0/tests/test_chat_completions.py +247 -0
  33. hakim-1.0.0/tests/test_jobs.py +93 -0
  34. hakim-1.0.0/tests/test_package.py +23 -0
  35. hakim-1.0.0/tests/test_settings.py +160 -0
  36. hakim-1.0.0/tests/test_sync_client.py +46 -0
  37. hakim-1.0.0/tests/test_transport.py +334 -0
  38. hakim-1.0.0/tests/test_usage.py +61 -0
  39. hakim-1.0.0/tests/test_voices.py +137 -0
  40. hakim-1.0.0/tests/test_webhooks.py +182 -0
hakim-1.0.0/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .env
hakim-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hakim AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
hakim-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,259 @@
1
+ Metadata-Version: 2.4
2
+ Name: hakim
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for the Hakim voice AI API (TTS, STT, voice cloning, webhooks, jobs).
5
+ Project-URL: Homepage, https://tryhakim.ai
6
+ Project-URL: Documentation, https://tryhakim.ai/docs
7
+ Project-URL: Source, https://github.com/tryHakimAI/hakim-python
8
+ Project-URL: Issues, https://github.com/tryHakimAI/hakim-python/issues
9
+ Project-URL: Changelog, https://github.com/tryHakimAI/hakim-python/blob/main/CHANGELOG.md
10
+ Author: Hakim
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: ai,arabic,hakim,speech,stt,tts,voice,voice-cloning,webhooks
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.10
27
+ Requires-Dist: httpx>=0.27
28
+ Requires-Dist: websockets>=13
29
+ Provides-Extra: dev
30
+ Requires-Dist: build>=1.2; extra == 'dev'
31
+ Requires-Dist: mypy>=1.13; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
33
+ Requires-Dist: pytest>=8; extra == 'dev'
34
+ Requires-Dist: ruff>=0.7; extra == 'dev'
35
+ Requires-Dist: twine>=5; extra == 'dev'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # hakim — official Python SDK
39
+
40
+ [![PyPI version](https://img.shields.io/pypi/v/hakim.svg)](https://pypi.org/project/hakim/)
41
+ [![Python](https://img.shields.io/pypi/pyversions/hakim.svg)](https://pypi.org/project/hakim/)
42
+
43
+ Official Python SDK for [Hakim](https://tryhakim.ai) — Arabic-first TTS,
44
+ STT, voice cloning, webhooks, and batch jobs. Mirrors the surface of the
45
+ Node SDK (`@hakim/voice`).
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ pip install hakim
51
+ ```
52
+
53
+ ## Quick start (async)
54
+
55
+ ```python
56
+ import asyncio
57
+ from hakim import AsyncHakim
58
+
59
+ async def main() -> None:
60
+ async with AsyncHakim(api_key="hk_...") as hakim:
61
+ audio = await hakim.audio.speech.create(
62
+ model="hakim-fast-v1",
63
+ input="مرحبا بالعالم",
64
+ voice="omar",
65
+ )
66
+ with open("out.mp3", "wb") as f:
67
+ f.write(audio)
68
+
69
+ asyncio.run(main())
70
+ ```
71
+
72
+ ## Quick start (sync)
73
+
74
+ ```python
75
+ from hakim import Hakim
76
+
77
+ with Hakim(api_key="hk_...") as hakim:
78
+ audio = hakim.tts_create(
79
+ model="hakim-fast-v1",
80
+ input="مرحبا بالعالم",
81
+ voice="omar",
82
+ )
83
+ ```
84
+
85
+ ## Chat completions
86
+
87
+ OpenAI-compatible chat API backed by `hakim-chat-v1` (HKM LLM 1).
88
+ Drop-in for any code that targets the OpenAI Chat Completions
89
+ reference — swap the base URL and key and you're done.
90
+
91
+ ```python
92
+ async with AsyncHakim(api_key="hk_...") as hakim:
93
+ # Non-streaming.
94
+ completion = await hakim.chat.completions.create(
95
+ model="hakim-chat-v1",
96
+ messages=[
97
+ {"role": "system", "content": "أنت مساعد عربي مفيد."},
98
+ {"role": "user", "content": "اكتب قصيدة قصيرة عن البحر."},
99
+ ],
100
+ temperature=0.7,
101
+ )
102
+ print(completion["choices"][0]["message"]["content"])
103
+
104
+ # Streaming (SSE). The final chunk carries the rolled-up `usage`.
105
+ stream = await hakim.chat.completions.stream(
106
+ model="hakim-chat-v1",
107
+ messages=[{"role": "user", "content": "مرحبا!"}],
108
+ )
109
+ async for chunk in stream:
110
+ delta = chunk["choices"][0]["delta"]
111
+ if "content" in delta:
112
+ print(delta["content"], end="", flush=True)
113
+ ```
114
+
115
+ ### Reasoning / chain-of-thought
116
+
117
+ `hakim-chat-v1` is a thinking-capable model. CoT is **off by
118
+ default** — it adds 10–50× latency and burns completion tokens
119
+ that don't reach your UI in time. Opt in on **non-streaming**
120
+ requests only:
121
+
122
+ ```python
123
+ completion = await hakim.chat.completions.create(
124
+ model="hakim-chat-v1",
125
+ messages=[{"role": "user", "content": "حلّ ٢٤ × ١٧ خطوة بخطوة."}],
126
+ reasoning={"enabled": True},
127
+ )
128
+ print(completion["choices"][0]["message"]["reasoning"]) # chain-of-thought
129
+ print(completion["choices"][0]["message"]["content"]) # final answer
130
+ ```
131
+
132
+ Streaming requests with `reasoning={"enabled": True}` are rejected
133
+ with a `400 invalid_request_error` — real-time agents cannot afford
134
+ the latency cost. The `stream()` method intentionally omits the
135
+ `reasoning` parameter so a misuse fails at edit time rather than
136
+ on the wire.
137
+
138
+ ## Streaming TTS
139
+
140
+ ```python
141
+ async with AsyncHakim(api_key="hk_...") as hakim:
142
+ stream = await hakim.audio.speech.stream(
143
+ model="hakim-fast-v1",
144
+ input="salam",
145
+ voice="omar",
146
+ )
147
+ async for chunk in stream:
148
+ write_to_speaker(chunk)
149
+ ```
150
+
151
+ ## Speech-to-text (sync upload)
152
+
153
+ ```python
154
+ async with AsyncHakim(api_key="hk_...") as hakim:
155
+ result = await hakim.audio.transcriptions.create(
156
+ file=open("clip.wav", "rb"),
157
+ model="hakim-arab-v2",
158
+ response_format="json",
159
+ )
160
+ print(result["text"])
161
+ ```
162
+
163
+ ## Speech-to-text (realtime)
164
+
165
+ ```python
166
+ async with AsyncHakim(api_key="hk_...") as hakim:
167
+ async with hakim.audio.transcriptions.stream(
168
+ model="hakim-arab-v2",
169
+ language="ar",
170
+ sample_rate=16000,
171
+ ) as handle:
172
+ async def pump_audio() -> None:
173
+ async for chunk in mic_iter():
174
+ await handle.send_audio(chunk)
175
+ await handle.commit()
176
+
177
+ asyncio.create_task(pump_audio())
178
+
179
+ async for event in handle:
180
+ if event["type"] == "partial":
181
+ print("~", event["text"])
182
+ elif event["type"] == "final":
183
+ print("✔", event["text"])
184
+ ```
185
+
186
+ ## Voice cloning
187
+
188
+ ```python
189
+ with open("my_voice.wav", "rb") as f:
190
+ voice = await hakim.voices.create(
191
+ sample=f,
192
+ name="My Voice",
193
+ language="ar",
194
+ consent_confirmed=True,
195
+ )
196
+
197
+ while voice["status"] == "processing":
198
+ await asyncio.sleep(2.0)
199
+ voice = await hakim.voices.retrieve(voice["id"])
200
+ ```
201
+
202
+ ## Webhooks
203
+
204
+ ```python
205
+ from hakim import verify_webhook_signature
206
+
207
+ def handle_webhook(request) -> None:
208
+ body = request.body
209
+ result = verify_webhook_signature(
210
+ secret="whsec_...",
211
+ body=body,
212
+ signature=request.headers["hakim-signature"],
213
+ )
214
+ if not result.valid:
215
+ raise ValueError(f"bad signature: {result.reason}")
216
+ event = json.loads(body)
217
+ ...
218
+ ```
219
+
220
+ ## Errors
221
+
222
+ Every HTTP-level failure raises a :class:`HakimError` subclass you can
223
+ branch on:
224
+
225
+ ```python
226
+ from hakim import Hakim, RateLimitError, HakimError
227
+
228
+ try:
229
+ hakim.tts_create(...)
230
+ except RateLimitError as err:
231
+ retry_after = (err.retry_after_ms or 1000) / 1000
232
+ time.sleep(retry_after)
233
+ except HakimError as err:
234
+ print(err.status, err.code, err.request_id)
235
+ ```
236
+
237
+ ## Observability
238
+
239
+ Every request auto-attaches:
240
+
241
+ - `Authorization: Bearer <api_key>`
242
+ - `User-Agent: hakim-python/<version> (python/<py_version>; <platform>)`
243
+ - `X-Request-Id: sdk-<uuid>` (echoed back by the server — attach to your
244
+ logs for end-to-end tracing).
245
+ - `Idempotency-Key: <uuid>` on mutating JSON calls (auto-generated;
246
+ override via `idempotency_key=` when you need deterministic keys).
247
+
248
+ Transient failures (5xx, 429, 408, 425, network errors) are retried
249
+ with exponential back-off (±25% jitter) up to `max_retries` (default
250
+ 2). `Retry-After` is honoured when the server sends it.
251
+
252
+ ## Development
253
+
254
+ ```bash
255
+ pip install -e '.[dev]'
256
+ ruff check src tests
257
+ mypy src
258
+ pytest
259
+ ```
hakim-1.0.0/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # hakim — official Python SDK
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/hakim.svg)](https://pypi.org/project/hakim/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/hakim.svg)](https://pypi.org/project/hakim/)
5
+
6
+ Official Python SDK for [Hakim](https://tryhakim.ai) — Arabic-first TTS,
7
+ STT, voice cloning, webhooks, and batch jobs. Mirrors the surface of the
8
+ Node SDK (`@hakim/voice`).
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install hakim
14
+ ```
15
+
16
+ ## Quick start (async)
17
+
18
+ ```python
19
+ import asyncio
20
+ from hakim import AsyncHakim
21
+
22
+ async def main() -> None:
23
+ async with AsyncHakim(api_key="hk_...") as hakim:
24
+ audio = await hakim.audio.speech.create(
25
+ model="hakim-fast-v1",
26
+ input="مرحبا بالعالم",
27
+ voice="omar",
28
+ )
29
+ with open("out.mp3", "wb") as f:
30
+ f.write(audio)
31
+
32
+ asyncio.run(main())
33
+ ```
34
+
35
+ ## Quick start (sync)
36
+
37
+ ```python
38
+ from hakim import Hakim
39
+
40
+ with Hakim(api_key="hk_...") as hakim:
41
+ audio = hakim.tts_create(
42
+ model="hakim-fast-v1",
43
+ input="مرحبا بالعالم",
44
+ voice="omar",
45
+ )
46
+ ```
47
+
48
+ ## Chat completions
49
+
50
+ OpenAI-compatible chat API backed by `hakim-chat-v1` (HKM LLM 1).
51
+ Drop-in for any code that targets the OpenAI Chat Completions
52
+ reference — swap the base URL and key and you're done.
53
+
54
+ ```python
55
+ async with AsyncHakim(api_key="hk_...") as hakim:
56
+ # Non-streaming.
57
+ completion = await hakim.chat.completions.create(
58
+ model="hakim-chat-v1",
59
+ messages=[
60
+ {"role": "system", "content": "أنت مساعد عربي مفيد."},
61
+ {"role": "user", "content": "اكتب قصيدة قصيرة عن البحر."},
62
+ ],
63
+ temperature=0.7,
64
+ )
65
+ print(completion["choices"][0]["message"]["content"])
66
+
67
+ # Streaming (SSE). The final chunk carries the rolled-up `usage`.
68
+ stream = await hakim.chat.completions.stream(
69
+ model="hakim-chat-v1",
70
+ messages=[{"role": "user", "content": "مرحبا!"}],
71
+ )
72
+ async for chunk in stream:
73
+ delta = chunk["choices"][0]["delta"]
74
+ if "content" in delta:
75
+ print(delta["content"], end="", flush=True)
76
+ ```
77
+
78
+ ### Reasoning / chain-of-thought
79
+
80
+ `hakim-chat-v1` is a thinking-capable model. CoT is **off by
81
+ default** — it adds 10–50× latency and burns completion tokens
82
+ that don't reach your UI in time. Opt in on **non-streaming**
83
+ requests only:
84
+
85
+ ```python
86
+ completion = await hakim.chat.completions.create(
87
+ model="hakim-chat-v1",
88
+ messages=[{"role": "user", "content": "حلّ ٢٤ × ١٧ خطوة بخطوة."}],
89
+ reasoning={"enabled": True},
90
+ )
91
+ print(completion["choices"][0]["message"]["reasoning"]) # chain-of-thought
92
+ print(completion["choices"][0]["message"]["content"]) # final answer
93
+ ```
94
+
95
+ Streaming requests with `reasoning={"enabled": True}` are rejected
96
+ with a `400 invalid_request_error` — real-time agents cannot afford
97
+ the latency cost. The `stream()` method intentionally omits the
98
+ `reasoning` parameter so a misuse fails at edit time rather than
99
+ on the wire.
100
+
101
+ ## Streaming TTS
102
+
103
+ ```python
104
+ async with AsyncHakim(api_key="hk_...") as hakim:
105
+ stream = await hakim.audio.speech.stream(
106
+ model="hakim-fast-v1",
107
+ input="salam",
108
+ voice="omar",
109
+ )
110
+ async for chunk in stream:
111
+ write_to_speaker(chunk)
112
+ ```
113
+
114
+ ## Speech-to-text (sync upload)
115
+
116
+ ```python
117
+ async with AsyncHakim(api_key="hk_...") as hakim:
118
+ result = await hakim.audio.transcriptions.create(
119
+ file=open("clip.wav", "rb"),
120
+ model="hakim-arab-v2",
121
+ response_format="json",
122
+ )
123
+ print(result["text"])
124
+ ```
125
+
126
+ ## Speech-to-text (realtime)
127
+
128
+ ```python
129
+ async with AsyncHakim(api_key="hk_...") as hakim:
130
+ async with hakim.audio.transcriptions.stream(
131
+ model="hakim-arab-v2",
132
+ language="ar",
133
+ sample_rate=16000,
134
+ ) as handle:
135
+ async def pump_audio() -> None:
136
+ async for chunk in mic_iter():
137
+ await handle.send_audio(chunk)
138
+ await handle.commit()
139
+
140
+ asyncio.create_task(pump_audio())
141
+
142
+ async for event in handle:
143
+ if event["type"] == "partial":
144
+ print("~", event["text"])
145
+ elif event["type"] == "final":
146
+ print("✔", event["text"])
147
+ ```
148
+
149
+ ## Voice cloning
150
+
151
+ ```python
152
+ with open("my_voice.wav", "rb") as f:
153
+ voice = await hakim.voices.create(
154
+ sample=f,
155
+ name="My Voice",
156
+ language="ar",
157
+ consent_confirmed=True,
158
+ )
159
+
160
+ while voice["status"] == "processing":
161
+ await asyncio.sleep(2.0)
162
+ voice = await hakim.voices.retrieve(voice["id"])
163
+ ```
164
+
165
+ ## Webhooks
166
+
167
+ ```python
168
+ from hakim import verify_webhook_signature
169
+
170
+ def handle_webhook(request) -> None:
171
+ body = request.body
172
+ result = verify_webhook_signature(
173
+ secret="whsec_...",
174
+ body=body,
175
+ signature=request.headers["hakim-signature"],
176
+ )
177
+ if not result.valid:
178
+ raise ValueError(f"bad signature: {result.reason}")
179
+ event = json.loads(body)
180
+ ...
181
+ ```
182
+
183
+ ## Errors
184
+
185
+ Every HTTP-level failure raises a :class:`HakimError` subclass you can
186
+ branch on:
187
+
188
+ ```python
189
+ from hakim import Hakim, RateLimitError, HakimError
190
+
191
+ try:
192
+ hakim.tts_create(...)
193
+ except RateLimitError as err:
194
+ retry_after = (err.retry_after_ms or 1000) / 1000
195
+ time.sleep(retry_after)
196
+ except HakimError as err:
197
+ print(err.status, err.code, err.request_id)
198
+ ```
199
+
200
+ ## Observability
201
+
202
+ Every request auto-attaches:
203
+
204
+ - `Authorization: Bearer <api_key>`
205
+ - `User-Agent: hakim-python/<version> (python/<py_version>; <platform>)`
206
+ - `X-Request-Id: sdk-<uuid>` (echoed back by the server — attach to your
207
+ logs for end-to-end tracing).
208
+ - `Idempotency-Key: <uuid>` on mutating JSON calls (auto-generated;
209
+ override via `idempotency_key=` when you need deterministic keys).
210
+
211
+ Transient failures (5xx, 429, 408, 425, network errors) are retried
212
+ with exponential back-off (±25% jitter) up to `max_retries` (default
213
+ 2). `Retry-After` is honoured when the server sends it.
214
+
215
+ ## Development
216
+
217
+ ```bash
218
+ pip install -e '.[dev]'
219
+ ruff check src tests
220
+ mypy src
221
+ pytest
222
+ ```
@@ -0,0 +1,101 @@
1
+ [project]
2
+ name = "hakim"
3
+ version = "1.0.0"
4
+ description = "Official Python SDK for the Hakim voice AI API (TTS, STT, voice cloning, webhooks, jobs)."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "Hakim" }]
9
+ keywords = [
10
+ "hakim",
11
+ "tts",
12
+ "stt",
13
+ "speech",
14
+ "voice-cloning",
15
+ "arabic",
16
+ "ai",
17
+ "voice",
18
+ "webhooks",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 5 - Production/Stable",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.10",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Programming Language :: Python :: 3.13",
30
+ "Topic :: Multimedia :: Sound/Audio :: Speech",
31
+ "Topic :: Software Development :: Libraries :: Python Modules",
32
+ "Typing :: Typed",
33
+ ]
34
+ dependencies = [
35
+ "httpx>=0.27",
36
+ "websockets>=13",
37
+ ]
38
+
39
+ [project.optional-dependencies]
40
+ dev = [
41
+ "pytest>=8",
42
+ "pytest-asyncio>=0.24",
43
+ "ruff>=0.7",
44
+ "mypy>=1.13",
45
+ "build>=1.2",
46
+ "twine>=5",
47
+ ]
48
+
49
+ [project.urls]
50
+ Homepage = "https://tryhakim.ai"
51
+ Documentation = "https://tryhakim.ai/docs"
52
+ Source = "https://github.com/tryHakimAI/hakim-python"
53
+ Issues = "https://github.com/tryHakimAI/hakim-python/issues"
54
+ Changelog = "https://github.com/tryHakimAI/hakim-python/blob/main/CHANGELOG.md"
55
+
56
+ [build-system]
57
+ requires = ["hatchling"]
58
+ build-backend = "hatchling.build"
59
+
60
+ [tool.hatch.build.targets.wheel]
61
+ packages = ["src/hakim"]
62
+
63
+ [tool.hatch.build.targets.sdist]
64
+ include = [
65
+ "src/hakim",
66
+ "tests",
67
+ "README.md",
68
+ "pyproject.toml",
69
+ ]
70
+
71
+ [tool.ruff]
72
+ line-length = 100
73
+ target-version = "py310"
74
+
75
+ [tool.ruff.lint]
76
+ # Keep the default set + pull in the "bugbear" / naming / simplify rules
77
+ # we rely on for API hygiene.
78
+ select = ["E", "F", "W", "I", "B", "N", "UP", "SIM", "RUF"]
79
+ ignore = [
80
+ # Ambiguous naming (e.g. `l` vs `1`). We never clash with these.
81
+ "E741",
82
+ ]
83
+
84
+ [tool.mypy]
85
+ strict = true
86
+ python_version = "3.10"
87
+
88
+ [[tool.mypy.overrides]]
89
+ # `websockets` publishes stubs-in-wheel which sometimes lag the runtime.
90
+ # Don't fail strict mypy on the optional realtime helper's import.
91
+ module = ["websockets", "websockets.*"]
92
+ ignore_missing_imports = true
93
+
94
+ [tool.pytest.ini_options]
95
+ testpaths = ["tests"]
96
+ addopts = "-ra --strict-markers"
97
+ # Anchor the editable install so tests work even on Python 3.13+, which
98
+ # skips `.pth` files whose filenames start with `_` (the layout hatchling
99
+ # writes for `pip install -e .`).
100
+ pythonpath = ["src"]
101
+ asyncio_mode = "auto"
@@ -0,0 +1,64 @@
1
+ """Hakim — official Python SDK for the Hakim voice AI API.
2
+
3
+ Quickstart (async):
4
+
5
+ from hakim import AsyncHakim
6
+
7
+ async with AsyncHakim(api_key=...) as hakim:
8
+ audio = await hakim.tts_create(
9
+ model="hakim-fast-v1",
10
+ input="مرحبا بالعالم",
11
+ voice="omar",
12
+ )
13
+
14
+ Quickstart (sync):
15
+
16
+ from hakim import Hakim
17
+
18
+ with Hakim(api_key=...) as hakim:
19
+ audio = hakim.tts_create(
20
+ model="hakim-fast-v1", input="مرحبا بالعالم", voice="omar",
21
+ )
22
+
23
+ Every namespace from the Node SDK (`audio.speech`, `audio.transcriptions`,
24
+ `voices`, `webhooks`, `jobs`, `usage`) is available on both clients.
25
+ """
26
+
27
+ from ._version import SDK_NAME, SDK_VERSION
28
+ from .client import AsyncHakim, Hakim
29
+ from .errors import (
30
+ APIError,
31
+ AuthenticationError,
32
+ ConnectionError,
33
+ HakimError,
34
+ IdempotencyConflictError,
35
+ InvalidRequestError,
36
+ NotFoundError,
37
+ PermissionError,
38
+ QuotaExceededError,
39
+ RateLimitError,
40
+ ServiceUnavailableError,
41
+ )
42
+ from .webhooks import VerifyResult, verify_webhook_signature
43
+
44
+ __all__ = [
45
+ "SDK_NAME",
46
+ "SDK_VERSION",
47
+ "APIError",
48
+ "AsyncHakim",
49
+ "AuthenticationError",
50
+ "ConnectionError",
51
+ "Hakim",
52
+ "HakimError",
53
+ "IdempotencyConflictError",
54
+ "InvalidRequestError",
55
+ "NotFoundError",
56
+ "PermissionError",
57
+ "QuotaExceededError",
58
+ "RateLimitError",
59
+ "ServiceUnavailableError",
60
+ "VerifyResult",
61
+ "verify_webhook_signature",
62
+ ]
63
+
64
+ __version__ = SDK_VERSION