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.
@@ -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,4 @@
1
+ from .client import InferenceClient, InferenceError
2
+ from .async_client import AsyncInferenceClient
3
+
4
+ __all__ = ["InferenceClient", "AsyncInferenceClient", "InferenceError"]
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ httpx>=0.25.0