whisperserve-sdk 0.1.3__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.
- whisperserve_sdk-0.1.3/PKG-INFO +18 -0
- whisperserve_sdk-0.1.3/README.md +89 -0
- whisperserve_sdk-0.1.3/VERSION +1 -0
- whisperserve_sdk-0.1.3/__init__.py +4 -0
- whisperserve_sdk-0.1.3/async_client.py +119 -0
- whisperserve_sdk-0.1.3/client.py +115 -0
- whisperserve_sdk-0.1.3/pyproject.toml +38 -0
- whisperserve_sdk-0.1.3/setup.cfg +4 -0
- whisperserve_sdk-0.1.3/setup.py +16 -0
- whisperserve_sdk-0.1.3/whisperserve_sdk.egg-info/PKG-INFO +18 -0
- whisperserve_sdk-0.1.3/whisperserve_sdk.egg-info/SOURCES.txt +16 -0
- whisperserve_sdk-0.1.3/whisperserve_sdk.egg-info/dependency_links.txt +1 -0
- whisperserve_sdk-0.1.3/whisperserve_sdk.egg-info/requires.txt +1 -0
- whisperserve_sdk-0.1.3/whisperserve_sdk.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: whisperserve-sdk
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Python SDK for the WhisperServe Audio Transcription API
|
|
5
|
+
Author: Pure Tech
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/puretechteam/whisperserve
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Requires-Dist: httpx>=0.25.0
|
|
18
|
+
Dynamic: requires-python
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# WhisperServe SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for interacting with the WhisperServe Audio Transcription API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install whisperserve-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Synchronous Client
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from sdk import InferenceClient
|
|
17
|
+
|
|
18
|
+
client = InferenceClient(api_key="your-api-key")
|
|
19
|
+
|
|
20
|
+
result = client.transcribe("audio.wav")
|
|
21
|
+
print(result["transcription"])
|
|
22
|
+
|
|
23
|
+
usage = client.get_usage()
|
|
24
|
+
print(f"Total requests: {usage['total_requests']}")
|
|
25
|
+
|
|
26
|
+
client.close()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Asynchronous Client
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import asyncio
|
|
33
|
+
from sdk import AsyncInferenceClient
|
|
34
|
+
|
|
35
|
+
async def main():
|
|
36
|
+
async with AsyncInferenceClient(api_key="your-api-key") as client:
|
|
37
|
+
result = await client.transcribe("audio.wav")
|
|
38
|
+
print(result["transcription"])
|
|
39
|
+
|
|
40
|
+
asyncio.run(main())
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Error Handling
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from sdk import InferenceClient, InferenceError
|
|
47
|
+
|
|
48
|
+
client = InferenceClient(api_key="your-api-key")
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
result = client.transcribe("audio.wav")
|
|
52
|
+
except InferenceError as e:
|
|
53
|
+
print(f"Error {e.status_code}: {e.message}")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Context Manager
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from sdk import InferenceClient
|
|
60
|
+
|
|
61
|
+
with InferenceClient(api_key="your-api-key") as client:
|
|
62
|
+
result = client.transcribe("audio.wav")
|
|
63
|
+
print(result["transcription"])
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API Reference
|
|
67
|
+
|
|
68
|
+
### InferenceClient
|
|
69
|
+
|
|
70
|
+
| Method | Description |
|
|
71
|
+
|--------|-------------|
|
|
72
|
+
| `transcribe(audio_path)` | Transcribe an audio file |
|
|
73
|
+
| `transcribe_batch(audio_paths)` | Transcribe multiple audio files |
|
|
74
|
+
| `get_usage()` | Get usage statistics |
|
|
75
|
+
| `list_keys()` | List API keys |
|
|
76
|
+
| `revoke_key(key_id)` | Revoke an API key |
|
|
77
|
+
| `close()` | Close the HTTP client |
|
|
78
|
+
|
|
79
|
+
### AsyncInferenceClient
|
|
80
|
+
|
|
81
|
+
Same methods as `InferenceClient`, but async versions (prefixed with `async`). Supports `async with` context manager.
|
|
82
|
+
|
|
83
|
+
### InferenceError
|
|
84
|
+
|
|
85
|
+
Exception raised when API requests fail. Contains `message` and `status_code` attributes.
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
MIT License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.3
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import httpx
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from .client import InferenceError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AsyncInferenceClient:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
api_key: str,
|
|
12
|
+
base_url: str = "http://localhost:8000",
|
|
13
|
+
timeout: float = 30.0,
|
|
14
|
+
):
|
|
15
|
+
self._api_key = api_key
|
|
16
|
+
self._base_url = base_url.rstrip("/")
|
|
17
|
+
self._timeout = timeout
|
|
18
|
+
self._client: Optional[httpx.AsyncClient] = None
|
|
19
|
+
|
|
20
|
+
async def __aenter__(self):
|
|
21
|
+
self._client = httpx.AsyncClient(
|
|
22
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
|
23
|
+
timeout=self._timeout,
|
|
24
|
+
)
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
28
|
+
await self.close()
|
|
29
|
+
|
|
30
|
+
async def _get_client(self) -> httpx.AsyncClient:
|
|
31
|
+
if self._client is None:
|
|
32
|
+
self._client = httpx.AsyncClient(
|
|
33
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
|
34
|
+
timeout=self._timeout,
|
|
35
|
+
)
|
|
36
|
+
return self._client
|
|
37
|
+
|
|
38
|
+
async def _request_with_retry(
|
|
39
|
+
self,
|
|
40
|
+
method: str,
|
|
41
|
+
url: str,
|
|
42
|
+
*,
|
|
43
|
+
max_retries: int = 3,
|
|
44
|
+
**kwargs,
|
|
45
|
+
):
|
|
46
|
+
await self._get_client()
|
|
47
|
+
delay = 1.0
|
|
48
|
+
last_exception = None
|
|
49
|
+
for attempt in range(max_retries + 1):
|
|
50
|
+
try:
|
|
51
|
+
response = await self._client.request(method, url, **kwargs)
|
|
52
|
+
return response
|
|
53
|
+
except httpx.TransportError as e:
|
|
54
|
+
last_exception = e
|
|
55
|
+
if attempt < max_retries:
|
|
56
|
+
await asyncio.sleep(delay)
|
|
57
|
+
delay *= 2.0
|
|
58
|
+
continue
|
|
59
|
+
raise
|
|
60
|
+
raise last_exception # type: ignore[misc]
|
|
61
|
+
|
|
62
|
+
async def transcribe(self, audio_path: str) -> dict:
|
|
63
|
+
with open(audio_path, "rb") as f:
|
|
64
|
+
response = await self._request_with_retry(
|
|
65
|
+
"POST",
|
|
66
|
+
f"{self._base_url}/v1/inference",
|
|
67
|
+
files={"file": f},
|
|
68
|
+
)
|
|
69
|
+
if response.status_code != 200:
|
|
70
|
+
raise InferenceError(
|
|
71
|
+
response.json().get("detail", "Inference failed"),
|
|
72
|
+
status_code=response.status_code,
|
|
73
|
+
)
|
|
74
|
+
return response.json()
|
|
75
|
+
|
|
76
|
+
async def transcribe_batch(self, audio_paths: list) -> list:
|
|
77
|
+
tasks = [self.transcribe(path) for path in audio_paths]
|
|
78
|
+
return await asyncio.gather(*tasks)
|
|
79
|
+
|
|
80
|
+
async def get_usage(self) -> dict:
|
|
81
|
+
response = await self._request_with_retry(
|
|
82
|
+
"GET",
|
|
83
|
+
f"{self._base_url}/v1/self-serve/dashboard",
|
|
84
|
+
)
|
|
85
|
+
if response.status_code != 200:
|
|
86
|
+
raise InferenceError(
|
|
87
|
+
response.json().get("detail", "Failed to fetch usage"),
|
|
88
|
+
status_code=response.status_code,
|
|
89
|
+
)
|
|
90
|
+
return response.json()
|
|
91
|
+
|
|
92
|
+
async def list_keys(self) -> list:
|
|
93
|
+
response = await self._request_with_retry(
|
|
94
|
+
"GET",
|
|
95
|
+
f"{self._base_url}/v1/self-serve/api-keys",
|
|
96
|
+
)
|
|
97
|
+
if response.status_code != 200:
|
|
98
|
+
raise InferenceError(
|
|
99
|
+
response.json().get("detail", "Failed to list API keys"),
|
|
100
|
+
status_code=response.status_code,
|
|
101
|
+
)
|
|
102
|
+
return response.json()
|
|
103
|
+
|
|
104
|
+
async def revoke_key(self, key_id: int) -> bool:
|
|
105
|
+
response = await self._request_with_retry(
|
|
106
|
+
"DELETE",
|
|
107
|
+
f"{self._base_url}/v1/self-serve/api-keys/{key_id}",
|
|
108
|
+
)
|
|
109
|
+
if response.status_code != 200:
|
|
110
|
+
raise InferenceError(
|
|
111
|
+
response.json().get("detail", "Failed to revoke API key"),
|
|
112
|
+
status_code=response.status_code,
|
|
113
|
+
)
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
async def close(self):
|
|
117
|
+
if self._client is not None:
|
|
118
|
+
await self._client.aclose()
|
|
119
|
+
self._client = None
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import concurrent.futures
|
|
2
|
+
import time
|
|
3
|
+
import httpx
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class InferenceError(Exception):
|
|
8
|
+
def __init__(self, message: str, status_code: int = 500):
|
|
9
|
+
self.message = message
|
|
10
|
+
self.status_code = status_code
|
|
11
|
+
super().__init__(message)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InferenceClient:
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
api_key: str,
|
|
18
|
+
base_url: str = "http://localhost:8000",
|
|
19
|
+
timeout: float = 30.0,
|
|
20
|
+
):
|
|
21
|
+
self._api_key = api_key
|
|
22
|
+
self._base_url = base_url.rstrip("/")
|
|
23
|
+
self._timeout = timeout
|
|
24
|
+
self._client = httpx.Client(
|
|
25
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
|
26
|
+
timeout=timeout,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def _request_with_retry(
|
|
30
|
+
self,
|
|
31
|
+
method: str,
|
|
32
|
+
url: str,
|
|
33
|
+
*,
|
|
34
|
+
max_retries: int = 3,
|
|
35
|
+
**kwargs,
|
|
36
|
+
):
|
|
37
|
+
delay = 1.0
|
|
38
|
+
last_exception = None
|
|
39
|
+
for attempt in range(max_retries + 1):
|
|
40
|
+
try:
|
|
41
|
+
response = self._client.request(method, url, **kwargs)
|
|
42
|
+
return response
|
|
43
|
+
except httpx.TransportError as e:
|
|
44
|
+
last_exception = e
|
|
45
|
+
if attempt < max_retries:
|
|
46
|
+
time.sleep(delay)
|
|
47
|
+
delay *= 2.0
|
|
48
|
+
continue
|
|
49
|
+
raise
|
|
50
|
+
raise last_exception # type: ignore[misc]
|
|
51
|
+
|
|
52
|
+
def transcribe(self, audio_path: str) -> dict:
|
|
53
|
+
with open(audio_path, "rb") as f:
|
|
54
|
+
response = self._request_with_retry(
|
|
55
|
+
"POST",
|
|
56
|
+
f"{self._base_url}/v1/inference",
|
|
57
|
+
files={"file": f},
|
|
58
|
+
)
|
|
59
|
+
if response.status_code != 200:
|
|
60
|
+
raise InferenceError(
|
|
61
|
+
response.json().get("detail", "Inference failed"),
|
|
62
|
+
status_code=response.status_code,
|
|
63
|
+
)
|
|
64
|
+
return response.json()
|
|
65
|
+
|
|
66
|
+
def transcribe_batch(self, audio_paths: list) -> list:
|
|
67
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
68
|
+
futures = [executor.submit(self.transcribe, path) for path in audio_paths]
|
|
69
|
+
results = [f.result() for f in concurrent.futures.as_completed(futures)]
|
|
70
|
+
return results
|
|
71
|
+
|
|
72
|
+
def get_usage(self) -> dict:
|
|
73
|
+
response = self._request_with_retry(
|
|
74
|
+
"GET",
|
|
75
|
+
f"{self._base_url}/v1/self-serve/dashboard",
|
|
76
|
+
)
|
|
77
|
+
if response.status_code != 200:
|
|
78
|
+
raise InferenceError(
|
|
79
|
+
response.json().get("detail", "Failed to fetch usage"),
|
|
80
|
+
status_code=response.status_code,
|
|
81
|
+
)
|
|
82
|
+
return response.json()
|
|
83
|
+
|
|
84
|
+
def list_keys(self) -> list:
|
|
85
|
+
response = self._request_with_retry(
|
|
86
|
+
"GET",
|
|
87
|
+
f"{self._base_url}/v1/self-serve/api-keys",
|
|
88
|
+
)
|
|
89
|
+
if response.status_code != 200:
|
|
90
|
+
raise InferenceError(
|
|
91
|
+
response.json().get("detail", "Failed to list API keys"),
|
|
92
|
+
status_code=response.status_code,
|
|
93
|
+
)
|
|
94
|
+
return response.json()
|
|
95
|
+
|
|
96
|
+
def revoke_key(self, key_id: int) -> bool:
|
|
97
|
+
response = self._request_with_retry(
|
|
98
|
+
"DELETE",
|
|
99
|
+
f"{self._base_url}/v1/self-serve/api-keys/{key_id}",
|
|
100
|
+
)
|
|
101
|
+
if response.status_code != 200:
|
|
102
|
+
raise InferenceError(
|
|
103
|
+
response.json().get("detail", "Failed to revoke API key"),
|
|
104
|
+
status_code=response.status_code,
|
|
105
|
+
)
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
def close(self):
|
|
109
|
+
self._client.close()
|
|
110
|
+
|
|
111
|
+
def __enter__(self):
|
|
112
|
+
return self
|
|
113
|
+
|
|
114
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
115
|
+
self.close()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "whisperserve-sdk"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Python SDK for the WhisperServe Audio Transcription API"
|
|
9
|
+
requires-python = ">=3.8"
|
|
10
|
+
authors = [
|
|
11
|
+
{name = "Pure Tech"},
|
|
12
|
+
]
|
|
13
|
+
license = {text = "MIT"}
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.8",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"httpx>=0.25.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/puretechteam/whisperserve"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.dynamic]
|
|
32
|
+
version = {file = "VERSION"}
|
|
33
|
+
|
|
34
|
+
[tool.setuptools]
|
|
35
|
+
packages = ["sdk"]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.package-dir]
|
|
38
|
+
"sdk" = "."
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
|
|
3
|
+
with open("VERSION") as f:
|
|
4
|
+
version = f.read().strip()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name="whisperserve-sdk",
|
|
8
|
+
version=version,
|
|
9
|
+
description="Python SDK for the WhisperServe Audio Transcription API",
|
|
10
|
+
packages=["sdk"],
|
|
11
|
+
package_dir={"sdk": "."},
|
|
12
|
+
install_requires=[
|
|
13
|
+
"httpx>=0.25.0",
|
|
14
|
+
],
|
|
15
|
+
python_requires=">=3.8",
|
|
16
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: whisperserve-sdk
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Python SDK for the WhisperServe Audio Transcription API
|
|
5
|
+
Author: Pure Tech
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/puretechteam/whisperserve
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Requires-Dist: httpx>=0.25.0
|
|
18
|
+
Dynamic: requires-python
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
VERSION
|
|
3
|
+
__init__.py
|
|
4
|
+
async_client.py
|
|
5
|
+
client.py
|
|
6
|
+
pyproject.toml
|
|
7
|
+
setup.py
|
|
8
|
+
./__init__.py
|
|
9
|
+
./async_client.py
|
|
10
|
+
./client.py
|
|
11
|
+
./setup.py
|
|
12
|
+
whisperserve_sdk.egg-info/PKG-INFO
|
|
13
|
+
whisperserve_sdk.egg-info/SOURCES.txt
|
|
14
|
+
whisperserve_sdk.egg-info/dependency_links.txt
|
|
15
|
+
whisperserve_sdk.egg-info/requires.txt
|
|
16
|
+
whisperserve_sdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpx>=0.25.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sdk
|