speechcortex-sdk 0.1.0__tar.gz → 0.1.dev2__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 (37) hide show
  1. {speechcortex_sdk-0.1.0/speechcortex_sdk.egg-info → speechcortex_sdk-0.1.dev2}/PKG-INFO +8 -8
  2. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/README.md +2 -2
  3. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/pyproject.toml +6 -6
  4. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/requirements.txt +2 -0
  5. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/setup.py +5 -5
  6. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/__init__.py +25 -1
  7. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/client.py +21 -2
  8. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/clients/transcribe/__init__.py +25 -0
  9. speechcortex_sdk-0.1.dev2/speechcortex/clients/transcribe/batch/__init__.py +32 -0
  10. speechcortex_sdk-0.1.dev2/speechcortex/clients/transcribe/batch/client.py +564 -0
  11. speechcortex_sdk-0.1.dev2/speechcortex/clients/transcribe/batch/exceptions.py +59 -0
  12. speechcortex_sdk-0.1.dev2/speechcortex/clients/transcribe/batch/options.py +88 -0
  13. speechcortex_sdk-0.1.dev2/speechcortex/clients/transcribe/batch/response.py +149 -0
  14. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/options.py +3 -1
  15. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2/speechcortex_sdk.egg-info}/PKG-INFO +8 -8
  16. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex_sdk.egg-info/SOURCES.txt +5 -0
  17. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/LICENSE +0 -0
  18. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/MANIFEST.in +0 -0
  19. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/requirements-dev.txt +0 -0
  20. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/setup.cfg +0 -0
  21. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/audio/__init__.py +0 -0
  22. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/audio/microphone/__init__.py +0 -0
  23. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/audio/microphone/constants.py +0 -0
  24. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/audio/microphone/errors.py +0 -0
  25. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/audio/microphone/microphone.py +0 -0
  26. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/clients/__init__.py +0 -0
  27. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/clients/transcribe/enums.py +0 -0
  28. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/clients/transcribe/realtime/__init__.py +0 -0
  29. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/clients/transcribe/realtime/client.py +0 -0
  30. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/clients/transcribe/realtime/options.py +0 -0
  31. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/clients/transcribe/realtime/response.py +0 -0
  32. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/errors.py +0 -0
  33. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/utils/__init__.py +0 -0
  34. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex/utils/verboselogs/__init__.py +0 -0
  35. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex_sdk.egg-info/dependency_links.txt +0 -0
  36. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex_sdk.egg-info/requires.txt +0 -0
  37. {speechcortex_sdk-0.1.0 → speechcortex_sdk-0.1.dev2}/speechcortex_sdk.egg-info/top_level.txt +0 -0
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: speechcortex-sdk
3
- Version: 0.1.0
3
+ Version: 0.1.dev2
4
4
  Summary: The official Python SDK for SpeechCortex ASR platform.
5
- Home-page: https://github.com/skanda-observeai/zeus-sdk-py
5
+ Home-page: https://github.com/speechcortex/speechcortex-sdk
6
6
  Author: SpeechCortex Team
7
7
  Author-email: SpeechCortex Team <team@speechcortex.com>
8
8
  License: MIT
9
- Project-URL: Homepage, https://github.com/skanda-observeai/zeus-sdk-py
10
- Project-URL: Bug Tracker, https://github.com/skanda-observeai/zeus-sdk-py/issues
11
- Project-URL: Source Code, https://github.com/skanda-observeai/zeus-sdk-py
12
- Project-URL: Documentation, https://github.com/skanda-observeai/zeus-sdk-py#readme
9
+ Project-URL: Homepage, https://github.com/speechcortex/speechcortex-sdk
10
+ Project-URL: Bug Tracker, https://github.com/speechcortex/speechcortex-sdk/issues
11
+ Project-URL: Source Code, https://github.com/speechcortex/speechcortex-sdk
12
+ Project-URL: Documentation, https://github.com/speechcortex/speechcortex-sdk#readme
13
13
  Keywords: speechcortex,asr,speech-to-text,speech recognition
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
@@ -35,7 +35,7 @@ Dynamic: requires-python
35
35
 
36
36
  # SpeechCortex Python SDK
37
37
 
38
- [![CI](https://github.com/skanda-observeai/zeus-sdk-py/actions/workflows/ci.yml/badge.svg)](https://github.com/skanda-observeai/zeus-sdk-py/actions/workflows/ci.yml)
38
+ [![CI](https://github.com/speechcortex/speechcortex-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/speechcortex/speechcortex-sdk/actions/workflows/ci.yml)
39
39
  [![PyPI version](https://badge.fury.io/py/speechcortex-sdk.svg)](https://badge.fury.io/py/speechcortex-sdk)
40
40
  [![Python Versions](https://img.shields.io/pypi/pyversions/speechcortex-sdk.svg)](https://pypi.org/project/speechcortex-sdk/)
41
41
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -57,7 +57,7 @@ Official Python SDK for SpeechCortex ASR (Automatic Speech Recognition) platform
57
57
  ## Installation
58
58
 
59
59
  ```bash
60
- pip install "git+https://github.com/skanda-observeai/zeus-sdk-py.git@package_init"
60
+ pip install "git+https://github.com/speechcortex/speechcortex-sdk.git@package_init"
61
61
  ```
62
62
 
63
63
  ```bash
@@ -1,6 +1,6 @@
1
1
  # SpeechCortex Python SDK
2
2
 
3
- [![CI](https://github.com/skanda-observeai/zeus-sdk-py/actions/workflows/ci.yml/badge.svg)](https://github.com/skanda-observeai/zeus-sdk-py/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/speechcortex/speechcortex-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/speechcortex/speechcortex-sdk/actions/workflows/ci.yml)
4
4
  [![PyPI version](https://badge.fury.io/py/speechcortex-sdk.svg)](https://badge.fury.io/py/speechcortex-sdk)
5
5
  [![Python Versions](https://img.shields.io/pypi/pyversions/speechcortex-sdk.svg)](https://pypi.org/project/speechcortex-sdk/)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -22,7 +22,7 @@ Official Python SDK for SpeechCortex ASR (Automatic Speech Recognition) platform
22
22
  ## Installation
23
23
 
24
24
  ```bash
25
- pip install "git+https://github.com/skanda-observeai/zeus-sdk-py.git@package_init"
25
+ pip install "git+https://github.com/speechcortex/speechcortex-sdk.git@package_init"
26
26
  ```
27
27
 
28
28
  ```bash
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "speechcortex-sdk"
10
- version = "0.1.0"
10
+ version = "0.1.dev2"
11
11
  description = "The official Python SDK for SpeechCortex ASR platform."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -36,17 +36,17 @@ dependencies = [
36
36
  ]
37
37
 
38
38
  [project.urls]
39
- "Homepage" = "https://github.com/skanda-observeai/zeus-sdk-py"
40
- "Bug Tracker" = "https://github.com/skanda-observeai/zeus-sdk-py/issues"
41
- "Source Code" = "https://github.com/skanda-observeai/zeus-sdk-py"
42
- "Documentation" = "https://github.com/skanda-observeai/zeus-sdk-py#readme"
39
+ "Homepage" = "https://github.com/speechcortex/speechcortex-sdk"
40
+ "Bug Tracker" = "https://github.com/speechcortex/speechcortex-sdk/issues"
41
+ "Source Code" = "https://github.com/speechcortex/speechcortex-sdk"
42
+ "Documentation" = "https://github.com/speechcortex/speechcortex-sdk#readme"
43
43
 
44
44
  ######
45
45
  # poetry configuration (optional)
46
46
  ######
47
47
  [tool.poetry]
48
48
  name = "speechcortex-sdk"
49
- version = "0.1.0"
49
+ version = "0.1.dev2"
50
50
  description = "The official Python SDK for SpeechCortex ASR platform."
51
51
  authors = ["SpeechCortex Team <team@speechcortex.com>"]
52
52
  license = "MIT"
@@ -6,3 +6,5 @@ aiohttp>=3.9.1
6
6
  aiofiles>=23.2.1
7
7
  aenum>=3.1.0
8
8
  deprecation>=2.1.0
9
+ requests>=2.31.0
10
+ urllib3>=2.0.0
@@ -15,14 +15,14 @@ DESCRIPTION = "The official Python SDK for SpeechCortex ASR platform."
15
15
 
16
16
  setup(
17
17
  name="speechcortex-sdk",
18
- version="0.1.0",
18
+ version="0.1.dev2",
19
19
  author="SpeechCortex Team",
20
20
  author_email="team@speechcortex.com",
21
- url="https://github.com/skanda-observeai/zeus-sdk-py",
21
+ url="https://github.com/speechcortex/speechcortex-sdk",
22
22
  project_urls={
23
- "Bug Tracker": "https://github.com/skanda-observeai/zeus-sdk-py/issues",
24
- "Source Code": "https://github.com/skanda-observeai/zeus-sdk-py",
25
- "Documentation": "https://github.com/skanda-observeai/zeus-sdk-py#readme",
23
+ "Bug Tracker": "https://github.com/speechcortex/speechcortex-sdk/issues",
24
+ "Source Code": "https://github.com/speechcortex/speechcortex-sdk",
25
+ "Documentation": "https://github.com/speechcortex/speechcortex-sdk#readme",
26
26
  },
27
27
  description=DESCRIPTION,
28
28
  long_description=LONG_DESCRIPTION,
@@ -8,7 +8,7 @@ SpeechCortex Python SDK
8
8
  Official Python SDK for SpeechCortex ASR (Automatic Speech Recognition) platform.
9
9
  """
10
10
 
11
- __version__ = "0.1.0"
11
+ __version__ = "0.1.dev2"
12
12
 
13
13
  # Core client
14
14
  from .client import SpeechCortexClient, SpeechCortex, TranscribeRouter
@@ -46,6 +46,18 @@ from .clients.transcribe import (
46
46
  Alternative,
47
47
  Channel,
48
48
  Word,
49
+ # Batch transcription
50
+ BatchClient,
51
+ BatchOptions,
52
+ TranscriptionConfig,
53
+ JobDetails,
54
+ TranscriptionResult,
55
+ TranscriptionStatus,
56
+ BatchError,
57
+ JobNotFoundError,
58
+ JobFailedError,
59
+ TranscriptionNotReadyError,
60
+ BatchTimeoutError,
49
61
  )
50
62
 
51
63
  # Audio utilities
@@ -105,6 +117,18 @@ __all__ = [
105
117
  "Alternative",
106
118
  "Channel",
107
119
  "Word",
120
+ # Batch transcription
121
+ "BatchClient",
122
+ "BatchOptions",
123
+ "TranscriptionConfig",
124
+ "JobDetails",
125
+ "TranscriptionResult",
126
+ "TranscriptionStatus",
127
+ "BatchError",
128
+ "JobNotFoundError",
129
+ "JobFailedError",
130
+ "TranscriptionNotReadyError",
131
+ "BatchTimeoutError",
108
132
  # Audio
109
133
  "Microphone",
110
134
  "SpeechCortexMicrophoneError",
@@ -6,7 +6,7 @@ from typing import Optional
6
6
 
7
7
  from .options import SpeechCortexClientOptions
8
8
  from .errors import SpeechCortexError, SpeechCortexApiKeyError
9
- from .clients.transcribe import RealtimeClient
9
+ from .clients.transcribe import RealtimeClient, BatchClient
10
10
 
11
11
 
12
12
  class TranscribeRouter:
@@ -15,6 +15,7 @@ class TranscribeRouter:
15
15
  def __init__(self, config: SpeechCortexClientOptions):
16
16
  self._config = config
17
17
  self._realtime_client: Optional[RealtimeClient] = None
18
+ self._batch_client: Optional[BatchClient] = None
18
19
 
19
20
  def realtime(self) -> RealtimeClient:
20
21
  """
@@ -26,6 +27,17 @@ class TranscribeRouter:
26
27
  if self._realtime_client is None:
27
28
  self._realtime_client = RealtimeClient(self._config)
28
29
  return self._realtime_client
30
+
31
+ def batch(self) -> BatchClient:
32
+ """
33
+ Get a batch/post-call transcription client.
34
+
35
+ Returns:
36
+ BatchClient instance for batch transcription
37
+ """
38
+ if self._batch_client is None:
39
+ self._batch_client = BatchClient(self._config)
40
+ return self._batch_client
29
41
 
30
42
 
31
43
  class _LegacyWebsocket:
@@ -95,7 +107,14 @@ class SpeechCortexClient:
95
107
  Access transcription services.
96
108
 
97
109
  Returns:
98
- TranscribeRouter for accessing real-time and batch transcription
110
+ TranscribeRouter for accessing real-time and batch transcription.
111
+
112
+ Example:
113
+ >>> # Real-time transcription
114
+ >>> client.transcribe.realtime()
115
+ >>>
116
+ >>> # Batch/post-call transcription
117
+ >>> client.transcribe.batch()
99
118
  """
100
119
  if self._transcribe is None:
101
120
  self._transcribe = TranscribeRouter(self._config)
@@ -19,6 +19,19 @@ from .realtime import (
19
19
  Channel,
20
20
  Word,
21
21
  )
22
+ from .batch import (
23
+ BatchClient,
24
+ BatchOptions,
25
+ TranscriptionConfig,
26
+ JobDetails,
27
+ TranscriptionResult,
28
+ TranscriptionStatus,
29
+ BatchError,
30
+ JobNotFoundError,
31
+ JobFailedError,
32
+ TranscriptionNotReadyError,
33
+ BatchTimeoutError,
34
+ )
22
35
 
23
36
  __all__ = [
24
37
  "LiveTranscriptionEvents",
@@ -36,4 +49,16 @@ __all__ = [
36
49
  "Alternative",
37
50
  "Channel",
38
51
  "Word",
52
+ # Batch transcription
53
+ "BatchClient",
54
+ "BatchOptions",
55
+ "TranscriptionConfig",
56
+ "JobDetails",
57
+ "TranscriptionResult",
58
+ "TranscriptionStatus",
59
+ "BatchError",
60
+ "JobNotFoundError",
61
+ "JobFailedError",
62
+ "TranscriptionNotReadyError",
63
+ "BatchTimeoutError",
39
64
  ]
@@ -0,0 +1,32 @@
1
+ # Copyright 2024 SpeechCortex SDK contributors. All Rights Reserved.
2
+ # Use of this source code is governed by a MIT license that can be found in the LICENSE file.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ from .client import BatchClient
6
+ from .options import BatchOptions, TranscriptionConfig
7
+ from .response import (
8
+ JobDetails,
9
+ TranscriptionResult,
10
+ TranscriptionStatus,
11
+ )
12
+ from .exceptions import (
13
+ BatchError,
14
+ JobNotFoundError,
15
+ JobFailedError,
16
+ TranscriptionNotReadyError,
17
+ BatchTimeoutError,
18
+ )
19
+
20
+ __all__ = [
21
+ "BatchClient",
22
+ "BatchOptions",
23
+ "TranscriptionConfig",
24
+ "JobDetails",
25
+ "TranscriptionResult",
26
+ "TranscriptionStatus",
27
+ "BatchError",
28
+ "JobNotFoundError",
29
+ "JobFailedError",
30
+ "TranscriptionNotReadyError",
31
+ "BatchTimeoutError",
32
+ ]
@@ -0,0 +1,564 @@
1
+ # Copyright 2024 SpeechCortex SDK contributors. All Rights Reserved.
2
+ # Use of this source code is governed by a MIT license that can be found in the LICENSE file.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """
6
+ Async batch transcription client for SpeechCortex SDK.
7
+
8
+ This client provides a pure async interface for post-call/batch transcription,
9
+ allowing users to submit audio files or presigned URLs, poll for status, and
10
+ retrieve transcription results using aiohttp.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import asyncio
16
+ import logging
17
+ import os
18
+ import sys
19
+ from pathlib import Path
20
+ from typing import Any, BinaryIO, Dict, Optional, Union
21
+ from uuid import UUID
22
+
23
+ import aiohttp
24
+ import aiofiles
25
+
26
+ from ....options import SpeechCortexClientOptions
27
+ from ....errors import SpeechCortexError, SpeechCortexApiKeyError
28
+ from .options import BatchOptions, TranscriptionConfig
29
+ from .response import JobDetails, TranscriptionResult, TranscriptionStatus
30
+ from .exceptions import (
31
+ BatchError,
32
+ JobNotFoundError,
33
+ JobFailedError,
34
+ TranscriptionNotReadyError,
35
+ BatchTimeoutError,
36
+ )
37
+
38
+
39
+ logger = logging.getLogger(__name__)
40
+ logger.addHandler(logging.NullHandler())
41
+
42
+
43
+ class BatchClient:
44
+ """
45
+ Async client for SpeechCortex batch/post-call transcription.
46
+
47
+ This client provides a pure async interface for submitting transcription jobs,
48
+ polling for status, and retrieving results. It uses aiohttp for non-blocking
49
+ HTTP communication.
50
+
51
+ Supports async context manager for automatic resource cleanup.
52
+
53
+ Example:
54
+ >>> from speechcortex import SpeechCortexClient
55
+ >>>
56
+ >>> client = SpeechCortexClient(api_key="your_key")
57
+ >>> batch = client.transcribe.batch()
58
+ >>>
59
+ >>> async with batch:
60
+ ... job = await batch.submit_job(
61
+ ... presigned_url="https://example.com/audio.mp3",
62
+ ... language="en-US"
63
+ ... )
64
+ ... result = await batch.wait_for_completion(job.job_id)
65
+ ... print(result.transcription)
66
+ """
67
+
68
+ def __init__(self, config: SpeechCortexClientOptions) -> None:
69
+ """
70
+ Initialize the batch transcription client.
71
+
72
+ Args:
73
+ config: SpeechCortexClientOptions containing API key, URL, etc.
74
+
75
+ Raises:
76
+ SpeechCortexError: If config is None.
77
+ SpeechCortexApiKeyError: If API key is missing.
78
+ """
79
+ if config is None:
80
+ raise SpeechCortexError("Config is required")
81
+
82
+ if not config.api_key:
83
+ raise SpeechCortexApiKeyError("API key is required for batch transcription")
84
+
85
+ self._config = config
86
+ self._api_key = config.api_key
87
+
88
+ # Derive the REST base URL from the config URL
89
+ # Config URL may be wss:// for realtime; we need https:// for REST
90
+ raw_url = config.url.rstrip("/")
91
+ if raw_url.startswith("wss://"):
92
+ raw_url = raw_url.replace("wss://", "https://", 1)
93
+ elif raw_url.startswith("ws://"):
94
+ raw_url = raw_url.replace("ws://", "http://", 1)
95
+ self._base_url = raw_url
96
+
97
+ # Batch API path
98
+ self._batch_path = config.batch_path
99
+
100
+ # Session is lazily created
101
+ self._session: Optional[aiohttp.ClientSession] = None
102
+ self._closed = False
103
+
104
+ logger.debug(
105
+ "BatchClient initialized (base_url=%s, batch_path=%s)",
106
+ self._base_url,
107
+ self._batch_path,
108
+ )
109
+
110
+ # ------------------------------------------------------------------
111
+ # Async context manager
112
+ # ------------------------------------------------------------------
113
+ async def __aenter__(self) -> BatchClient:
114
+ """Async context manager entry."""
115
+ await self._ensure_session()
116
+ return self
117
+
118
+ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
119
+ """Async context manager exit with automatic cleanup."""
120
+ await self.close()
121
+
122
+ # ------------------------------------------------------------------
123
+ # Public API
124
+ # ------------------------------------------------------------------
125
+ async def submit_job(
126
+ self,
127
+ presigned_url: Optional[str] = None,
128
+ audio_file: Optional[Union[str, Path, BinaryIO, bytes]] = None,
129
+ config: Optional[TranscriptionConfig] = None,
130
+ **kwargs: Any,
131
+ ) -> JobDetails:
132
+ """
133
+ Submit a transcription job.
134
+
135
+ Either presigned_url or audio_file must be provided, but not both.
136
+
137
+ Args:
138
+ presigned_url: Presigned URL to audio file (S3, etc.)
139
+ audio_file: Audio file path, file-like object, or bytes.
140
+ config: TranscriptionConfig with transcription parameters.
141
+ **kwargs: Additional transcription parameters (language, model, etc.)
142
+
143
+ Returns:
144
+ JobDetails object with job_id and initial status.
145
+
146
+ Raises:
147
+ BatchError: If job submission fails.
148
+ SpeechCortexApiKeyError: If API key is invalid.
149
+ ValueError: If neither or both of presigned_url / audio_file are provided.
150
+ """
151
+ if presigned_url and audio_file:
152
+ raise ValueError("Cannot specify both presigned_url and audio_file")
153
+ if not presigned_url and not audio_file:
154
+ raise ValueError("Must provide either presigned_url or audio_file")
155
+
156
+ # Build transcription config
157
+ if config is None:
158
+ config = TranscriptionConfig()
159
+
160
+ # Override config with kwargs
161
+ for key, value in kwargs.items():
162
+ if hasattr(config, key):
163
+ setattr(config, key, value)
164
+
165
+ query_params = config.to_query_params()
166
+
167
+ try:
168
+ if presigned_url:
169
+ return await self._submit_presigned_url(presigned_url, query_params)
170
+ else:
171
+ return await self._submit_file_upload(audio_file, query_params) # type: ignore[arg-type]
172
+ except SpeechCortexApiKeyError:
173
+ raise
174
+ except BatchError:
175
+ raise
176
+ except Exception as e:
177
+ raise BatchError(f"Failed to submit job: {e}") from e
178
+
179
+ async def get_status(self, job_id: Union[str, UUID]) -> TranscriptionStatus:
180
+ """
181
+ Get the current status of a transcription job.
182
+
183
+ Args:
184
+ job_id: Job identifier (UUID string or UUID object).
185
+
186
+ Returns:
187
+ TranscriptionStatus object with current status.
188
+
189
+ Raises:
190
+ JobNotFoundError: If job is not found.
191
+ BatchError: If request fails.
192
+ """
193
+ job_id_str = str(job_id)
194
+ url = f"{self._base_url}{self._batch_path}/status/{job_id_str}"
195
+
196
+ logger.debug("Getting status for job: %s", job_id_str)
197
+
198
+ try:
199
+ data = await self._request("GET", url)
200
+ return TranscriptionStatus.from_dict(data)
201
+ except JobNotFoundError:
202
+ raise
203
+ except Exception as e:
204
+ raise BatchError(f"Failed to get status: {e}") from e
205
+
206
+ async def get_transcription(self, job_id: Union[str, UUID]) -> TranscriptionResult:
207
+ """
208
+ Get the transcription result for a completed job.
209
+
210
+ Args:
211
+ job_id: Job identifier (UUID string or UUID object).
212
+
213
+ Returns:
214
+ TranscriptionResult object with transcription data.
215
+
216
+ Raises:
217
+ JobNotFoundError: If job is not found.
218
+ TranscriptionNotReadyError: If job is not completed yet.
219
+ JobFailedError: If job failed.
220
+ BatchError: If request fails.
221
+ """
222
+ job_id_str = str(job_id)
223
+ url = f"{self._base_url}{self._batch_path}/get_transcription/{job_id_str}"
224
+
225
+ logger.debug("Getting transcription for job: %s", job_id_str)
226
+
227
+ try:
228
+ data = await self._request("GET", url, allow_202=True)
229
+
230
+ # 202 means job is not ready yet (handled via status code in _request)
231
+ if data.get("_status_code") == 202:
232
+ status = data.get("status", "unknown")
233
+ raise TranscriptionNotReadyError(job_id_str, status)
234
+
235
+ # Check if job failed
236
+ if data.get("status", "").upper() == "FAILED":
237
+ error_msg = data.get("message", "Transcription failed")
238
+ raise JobFailedError(job_id_str, error_msg)
239
+
240
+ return TranscriptionResult.from_dict(data)
241
+
242
+ except (JobNotFoundError, TranscriptionNotReadyError, JobFailedError):
243
+ raise
244
+ except BatchError:
245
+ raise
246
+ except Exception as e:
247
+ raise BatchError(f"Failed to get transcription: {e}") from e
248
+
249
+ async def wait_for_completion(
250
+ self,
251
+ job_id: Union[str, UUID],
252
+ options: Optional[BatchOptions] = None,
253
+ **kwargs: Any,
254
+ ) -> TranscriptionResult:
255
+ """
256
+ Wait for a job to complete and return the result.
257
+
258
+ This method polls the job status at regular intervals until the job
259
+ completes or fails. Uses asyncio.wait_for for timeout handling.
260
+
261
+ Args:
262
+ job_id: Job identifier (UUID string or UUID object).
263
+ options: BatchOptions with polling configuration.
264
+ **kwargs: Additional options (polling_interval, timeout).
265
+
266
+ Returns:
267
+ TranscriptionResult object with transcription data.
268
+
269
+ Raises:
270
+ BatchTimeoutError: If job doesn't complete within timeout.
271
+ JobFailedError: If job fails.
272
+ BatchError: If request fails.
273
+ """
274
+ if options is None:
275
+ options = BatchOptions()
276
+
277
+ polling_interval = kwargs.get("polling_interval", options.polling_interval)
278
+ timeout = kwargs.get("timeout", options.timeout)
279
+ job_id_str = str(job_id)
280
+
281
+ logger.info(
282
+ "Waiting for job completion: %s (polling_interval=%.1fs, timeout=%s)",
283
+ job_id_str,
284
+ polling_interval,
285
+ timeout or "none",
286
+ )
287
+
288
+ try:
289
+ await asyncio.wait_for(
290
+ self._poll_job_status(job_id_str, polling_interval),
291
+ timeout=timeout,
292
+ )
293
+ return await self.get_transcription(job_id_str)
294
+
295
+ except asyncio.TimeoutError:
296
+ raise BatchTimeoutError(job_id_str, timeout) from None
297
+
298
+ async def transcribe(
299
+ self,
300
+ presigned_url: Optional[str] = None,
301
+ audio_file: Optional[Union[str, Path, BinaryIO, bytes]] = None,
302
+ config: Optional[TranscriptionConfig] = None,
303
+ options: Optional[BatchOptions] = None,
304
+ **kwargs: Any,
305
+ ) -> TranscriptionResult:
306
+ """
307
+ Convenience method: submit job and wait for completion.
308
+
309
+ Combines submit_job() and wait_for_completion() in a single call.
310
+
311
+ Args:
312
+ presigned_url: Presigned URL to audio file.
313
+ audio_file: Audio file path, file-like object, or bytes.
314
+ config: TranscriptionConfig with transcription parameters.
315
+ options: BatchOptions with polling configuration.
316
+ **kwargs: Additional parameters (language, model, polling_interval, timeout, etc.)
317
+
318
+ Returns:
319
+ TranscriptionResult object with transcription data.
320
+
321
+ Raises:
322
+ BatchError: If any step fails.
323
+ """
324
+ # Extract polling options from kwargs
325
+ polling_kwargs: Dict[str, Any] = {}
326
+ for key in ("polling_interval", "timeout"):
327
+ if key in kwargs:
328
+ polling_kwargs[key] = kwargs.pop(key)
329
+
330
+ # Submit job
331
+ job = await self.submit_job(
332
+ presigned_url=presigned_url,
333
+ audio_file=audio_file,
334
+ config=config,
335
+ **kwargs,
336
+ )
337
+
338
+ logger.debug("Waiting for job completion (job_id=%s)", job.job_id)
339
+
340
+ # Wait for completion
341
+ result = await self.wait_for_completion(
342
+ job.job_id,
343
+ options=options,
344
+ **polling_kwargs,
345
+ )
346
+
347
+ logger.info("Transcription completed successfully (job_id=%s)", job.job_id)
348
+ return result
349
+
350
+ async def close(self) -> None:
351
+ """
352
+ Close the HTTP session and cleanup resources.
353
+
354
+ Safe to call multiple times.
355
+ """
356
+ if self._session and not self._closed:
357
+ try:
358
+ await self._session.close()
359
+ except Exception:
360
+ pass # best-effort cleanup
361
+ finally:
362
+ self._session = None
363
+ self._closed = True
364
+ logger.debug("BatchClient session closed")
365
+
366
+ # ------------------------------------------------------------------
367
+ # Internal helpers
368
+ # ------------------------------------------------------------------
369
+ async def _ensure_session(self) -> None:
370
+ """Create aiohttp session lazily."""
371
+ if self._session is None and not self._closed:
372
+ timeout = aiohttp.ClientTimeout(total=300.0, connect=30.0)
373
+ self._session = aiohttp.ClientSession(timeout=timeout)
374
+ logger.debug("aiohttp session created")
375
+
376
+ def _get_headers(self) -> Dict[str, str]:
377
+ """Build common request headers."""
378
+ return {
379
+ "X-API-Key": self._api_key,
380
+ "User-Agent": (
381
+ f"speechcortex-sdk python/{sys.version_info.major}.{sys.version_info.minor}"
382
+ ),
383
+ }
384
+
385
+ async def _request(
386
+ self,
387
+ method: str,
388
+ url: str,
389
+ *,
390
+ params: Optional[Dict[str, Any]] = None,
391
+ json_data: Optional[Dict[str, Any]] = None,
392
+ form_data: Optional[aiohttp.FormData] = None,
393
+ allow_202: bool = False,
394
+ ) -> Dict[str, Any]:
395
+ """
396
+ Send an HTTP request and return JSON response.
397
+
398
+ Args:
399
+ method: HTTP method (GET, POST).
400
+ url: Full URL.
401
+ params: Query parameters.
402
+ json_data: JSON body.
403
+ form_data: Multipart form data.
404
+ allow_202: If True, return 202 responses with _status_code marker.
405
+
406
+ Returns:
407
+ JSON response as dict.
408
+
409
+ Raises:
410
+ SpeechCortexApiKeyError: On 401.
411
+ JobNotFoundError: On 404.
412
+ BatchError: On other errors.
413
+ """
414
+ await self._ensure_session()
415
+ assert self._session is not None
416
+
417
+ headers = self._get_headers()
418
+
419
+ kwargs: Dict[str, Any] = {"headers": headers, "params": params}
420
+ if json_data is not None:
421
+ kwargs["json"] = json_data
422
+ if form_data is not None:
423
+ # Don't set Content-Type for multipart — aiohttp handles it
424
+ kwargs["data"] = form_data
425
+
426
+ logger.debug("HTTP %s %s", method, url)
427
+
428
+ try:
429
+ async with self._session.request(method, url, **kwargs) as response:
430
+ if response.status == 401:
431
+ raise SpeechCortexApiKeyError("Invalid API key")
432
+
433
+ if response.status == 404:
434
+ raise JobNotFoundError("unknown", "Resource not found")
435
+
436
+ if response.status == 202 and allow_202:
437
+ data = await response.json()
438
+ data["_status_code"] = 202
439
+ return data
440
+
441
+ if response.status >= 400:
442
+ error_text = await response.text()
443
+ logger.error("HTTP %d: %s", response.status, error_text)
444
+ raise BatchError(f"HTTP {response.status}: {error_text}")
445
+
446
+ return await response.json()
447
+
448
+ except (SpeechCortexApiKeyError, JobNotFoundError, BatchError):
449
+ raise
450
+ except aiohttp.ClientError as e:
451
+ raise BatchError(f"Request failed: {e}") from e
452
+ except asyncio.TimeoutError:
453
+ raise BatchError(f"Request timeout: {method} {url}") from None
454
+ except Exception as e:
455
+ raise BatchError(f"Unexpected error: {e}") from e
456
+
457
+ async def _submit_presigned_url(
458
+ self, presigned_url: str, query_params: Dict[str, Any]
459
+ ) -> JobDetails:
460
+ """Submit job with presigned URL."""
461
+ url = f"{self._base_url}{self._batch_path}/transcribe"
462
+
463
+ logger.debug("Submitting job with presigned URL: %s", url)
464
+
465
+ data = await self._request(
466
+ "POST",
467
+ url,
468
+ params=query_params,
469
+ json_data={"presigned_url": presigned_url},
470
+ )
471
+
472
+ if "job_id" not in data:
473
+ error_msg = data.get("message", "No job_id in response")
474
+ raise BatchError(f"Job submission failed: {error_msg}")
475
+
476
+ logger.info("Job submitted successfully: %s", data.get("job_id"))
477
+ return JobDetails.from_dict(data)
478
+
479
+ async def _submit_file_upload(
480
+ self,
481
+ audio_file: Union[str, Path, BinaryIO, bytes],
482
+ query_params: Dict[str, Any],
483
+ ) -> JobDetails:
484
+ """Submit job with file upload."""
485
+ url = f"{self._base_url}{self._batch_path}/transcribe/upload"
486
+
487
+ logger.debug("Submitting job with file upload: %s", url)
488
+
489
+ form = aiohttp.FormData()
490
+
491
+ if isinstance(audio_file, (str, Path)):
492
+ file_path = Path(audio_file)
493
+ if not file_path.exists():
494
+ raise FileNotFoundError(f"Audio file not found: {audio_file}")
495
+
496
+ async with aiofiles.open(file_path, "rb") as f:
497
+ file_content = await f.read()
498
+ form.add_field(
499
+ "audio_file",
500
+ file_content,
501
+ filename=file_path.name,
502
+ content_type="audio/mpeg",
503
+ )
504
+ elif isinstance(audio_file, bytes):
505
+ form.add_field(
506
+ "audio_file",
507
+ audio_file,
508
+ filename="audio.mp3",
509
+ content_type="audio/mpeg",
510
+ )
511
+ else:
512
+ # File-like object
513
+ content = audio_file.read()
514
+ filename = getattr(audio_file, "name", "audio.mp3")
515
+ if hasattr(filename, "split"):
516
+ filename = os.path.basename(filename)
517
+ form.add_field(
518
+ "audio_file",
519
+ content,
520
+ filename=filename,
521
+ content_type="audio/mpeg",
522
+ )
523
+
524
+ data = await self._request(
525
+ "POST",
526
+ url,
527
+ params=query_params,
528
+ form_data=form,
529
+ )
530
+
531
+ if "job_id" not in data:
532
+ error_msg = data.get("message", "No job_id in response")
533
+ raise BatchError(f"Job submission failed: {error_msg}")
534
+
535
+ logger.info("Job submitted successfully: %s", data.get("job_id"))
536
+ return JobDetails.from_dict(data)
537
+
538
+ async def _poll_job_status(self, job_id: str, polling_interval: float) -> None:
539
+ """Poll job status until completion or failure."""
540
+ logger.debug(
541
+ "Starting status polling for job_id=%s (interval=%.1fs)",
542
+ job_id,
543
+ polling_interval,
544
+ )
545
+ poll_count = 0
546
+
547
+ while True:
548
+ poll_count += 1
549
+ status = await self.get_status(job_id)
550
+ status_upper = status.status.upper()
551
+
552
+ logger.debug(
553
+ "Job %s status: %s (poll #%d)", job_id, status_upper, poll_count
554
+ )
555
+
556
+ if status_upper == "COMPLETED":
557
+ logger.info("Job completed (job_id=%s, polls=%d)", job_id, poll_count)
558
+ return
559
+
560
+ if status_upper == "FAILED":
561
+ error_msg = status.error_message or "Job failed"
562
+ raise JobFailedError(job_id, error_msg)
563
+
564
+ await asyncio.sleep(polling_interval)
@@ -0,0 +1,59 @@
1
+ # Copyright 2024 SpeechCortex SDK contributors. All Rights Reserved.
2
+ # Use of this source code is governed by a MIT license that can be found in the LICENSE file.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """
6
+ Batch transcription specific exceptions.
7
+ """
8
+
9
+ from ....errors import SpeechCortexError, SpeechCortexApiError, SpeechCortexTimeoutError
10
+
11
+
12
+ class BatchError(SpeechCortexError):
13
+ """
14
+ Base exception for all batch transcription errors.
15
+ """
16
+ pass
17
+
18
+
19
+ class JobNotFoundError(BatchError):
20
+ """
21
+ Exception raised when a transcription job is not found.
22
+ """
23
+ def __init__(self, job_id: str, message: str = None):
24
+ msg = message or f"Job not found: {job_id}"
25
+ super().__init__(msg)
26
+ self.job_id = job_id
27
+
28
+
29
+ class JobFailedError(BatchError):
30
+ """
31
+ Exception raised when a transcription job fails.
32
+ """
33
+ def __init__(self, job_id: str, error_message: str = None):
34
+ msg = error_message or f"Job {job_id} failed"
35
+ super().__init__(msg)
36
+ self.job_id = job_id
37
+ self.error_message = error_message
38
+
39
+
40
+ class TranscriptionNotReadyError(BatchError):
41
+ """
42
+ Exception raised when attempting to get transcription result before job is completed.
43
+ """
44
+ def __init__(self, job_id: str, status: str):
45
+ msg = f"Transcription not ready for job {job_id}. Current status: {status}"
46
+ super().__init__(msg)
47
+ self.job_id = job_id
48
+ self.status = status
49
+
50
+
51
+ class BatchTimeoutError(SpeechCortexTimeoutError):
52
+ """
53
+ Exception raised when waiting for job completion times out.
54
+ """
55
+ def __init__(self, job_id: str, timeout: float):
56
+ msg = f"Job {job_id} did not complete within {timeout} seconds"
57
+ super().__init__(msg)
58
+ self.job_id = job_id
59
+ self.timeout = timeout
@@ -0,0 +1,88 @@
1
+ # Copyright 2024 SpeechCortex SDK contributors. All Rights Reserved.
2
+ # Use of this source code is governed by a MIT license that can be found in the LICENSE file.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """
6
+ Configuration options for batch transcription.
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from typing import Optional, Dict, Any, Union
11
+ import os
12
+
13
+
14
+ @dataclass
15
+ class TranscriptionConfig:
16
+ """
17
+ Configuration for transcription parameters.
18
+
19
+ Attributes:
20
+ language: Language code (e.g., "en-US", "ENGLISH")
21
+ model: Transcription model to use (default: "batch-zeus")
22
+ diarize: Enable speaker diarization (default: False)
23
+ punctuate: Enable punctuation (default: True)
24
+ smart_format: Enable smart formatting (default: True)
25
+ channel: Number of audio channels (default: 2)
26
+ pci: PCI compliance flag (default: False)
27
+ extra_params: Additional parameters as dict or JSON string
28
+ """
29
+ language: str = "en-US"
30
+ model: str = "batch-zeus"
31
+ diarize: bool = False
32
+ punctuate: bool = False
33
+ smart_format: bool = False
34
+ channel: int = 2
35
+ pci: bool = False
36
+ extra_params: Optional[Union[Dict[str, Any], str]] = None
37
+
38
+ def to_query_params(self) -> Dict[str, Any]:
39
+ """Convert config to query parameters for API request."""
40
+ params = {
41
+ "language": self.language,
42
+ "model": self.model,
43
+ "diarize": str(self.diarize).lower(),
44
+ "punctuate": str(self.punctuate).lower(),
45
+ "smart_format": str(self.smart_format).lower(),
46
+ "channel": str(self.channel),
47
+ "pci": str(self.pci).lower(),
48
+ }
49
+
50
+ # Handle extra_params
51
+ if self.extra_params:
52
+ if isinstance(self.extra_params, str):
53
+ import json
54
+ try:
55
+ extra = json.loads(self.extra_params)
56
+ if isinstance(extra, dict):
57
+ params.update(extra)
58
+ except json.JSONDecodeError:
59
+ pass
60
+ elif isinstance(self.extra_params, dict):
61
+ params.update(self.extra_params)
62
+
63
+ return params
64
+
65
+
66
+ @dataclass
67
+ class BatchOptions:
68
+ """
69
+ Options for batch transcription operations.
70
+
71
+ Attributes:
72
+ polling_interval: Time in seconds between status checks (default: 3.0)
73
+ timeout: Maximum time in seconds to wait for completion (default: None, no timeout)
74
+ batch_path: API path for batch transcription endpoints (default: "/api/v1/transcription")
75
+ """
76
+ polling_interval: float = 3.0
77
+ timeout: Optional[float] = None
78
+ batch_path: str = field(default_factory=lambda: os.getenv(
79
+ "SPEECHCORTEX_BATCH_PATH",
80
+ "/api/v1/transcription"
81
+ ))
82
+
83
+ def __post_init__(self):
84
+ """Validate options after initialization."""
85
+ if self.polling_interval <= 0:
86
+ raise ValueError("polling_interval must be greater than 0")
87
+ if self.timeout is not None and self.timeout <= 0:
88
+ raise ValueError("timeout must be greater than 0")
@@ -0,0 +1,149 @@
1
+ # Copyright 2024 SpeechCortex SDK contributors. All Rights Reserved.
2
+ # Use of this source code is governed by a MIT license that can be found in the LICENSE file.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """
6
+ Response models for batch transcription.
7
+ """
8
+
9
+ from dataclasses import dataclass
10
+ from datetime import datetime
11
+ from typing import Optional, Dict, Any
12
+ from uuid import UUID
13
+
14
+
15
+ @dataclass
16
+ class JobDetails:
17
+ """
18
+ Details about a transcription job.
19
+
20
+ Attributes:
21
+ job_id: Unique job identifier
22
+ status: Current job status (pending, downloading, uploading, queued, processing, completed, failed)
23
+ created_at: Job creation timestamp
24
+ updated_at: Last update timestamp (optional)
25
+ error_message: Error message if job failed (optional)
26
+ """
27
+ job_id: UUID
28
+ status: str
29
+ created_at: datetime
30
+ updated_at: Optional[datetime] = None
31
+ error_message: Optional[str] = None
32
+
33
+ @classmethod
34
+ def from_dict(cls, data: Dict[str, Any]) -> "JobDetails":
35
+ """Create JobDetails from API response dictionary."""
36
+ # Handle both UUID string and UUID object
37
+ job_id = data.get("job_id")
38
+ if isinstance(job_id, str):
39
+ job_id = UUID(job_id)
40
+ elif not isinstance(job_id, UUID):
41
+ raise ValueError(f"Invalid job_id format: {job_id}")
42
+
43
+ # Parse timestamps
44
+ created_at = data.get("created_at")
45
+ if isinstance(created_at, str):
46
+ # Handle ISO format with or without timezone
47
+ created_at = created_at.replace("Z", "+00:00")
48
+ created_at = datetime.fromisoformat(created_at)
49
+ elif not isinstance(created_at, datetime):
50
+ created_at = datetime.now()
51
+
52
+ updated_at = data.get("updated_at")
53
+ if updated_at:
54
+ if isinstance(updated_at, str):
55
+ updated_at = updated_at.replace("Z", "+00:00")
56
+ updated_at = datetime.fromisoformat(updated_at)
57
+ elif not isinstance(updated_at, datetime):
58
+ updated_at = None
59
+
60
+ return cls(
61
+ job_id=job_id,
62
+ status=data.get("status", "pending"),
63
+ created_at=created_at,
64
+ updated_at=updated_at,
65
+ error_message=data.get("error_message"),
66
+ )
67
+
68
+
69
+ @dataclass
70
+ class TranscriptionStatus:
71
+ """
72
+ Status information for a transcription job.
73
+
74
+ Attributes:
75
+ job_id: Unique job identifier
76
+ status: Current job status
77
+ created_at: Job creation timestamp
78
+ updated_at: Last update timestamp (optional)
79
+ error_message: Error message if job failed (optional)
80
+ """
81
+ job_id: UUID
82
+ status: str
83
+ created_at: Optional[datetime] = None
84
+ updated_at: Optional[datetime] = None
85
+ error_message: Optional[str] = None
86
+
87
+ @classmethod
88
+ def from_dict(cls, data: Dict[str, Any]) -> "TranscriptionStatus":
89
+ """Create TranscriptionStatus from API response dictionary."""
90
+ job_id = data.get("job_id")
91
+ if isinstance(job_id, str):
92
+ job_id = UUID(job_id)
93
+ elif not isinstance(job_id, UUID):
94
+ raise ValueError(f"Invalid job_id format: {job_id}")
95
+
96
+ created_at = None
97
+ if data.get("created_at"):
98
+ created_at_str = data["created_at"]
99
+ if isinstance(created_at_str, str):
100
+ created_at_str = created_at_str.replace("Z", "+00:00")
101
+ created_at = datetime.fromisoformat(created_at_str)
102
+
103
+ updated_at = None
104
+ if data.get("updated_at"):
105
+ updated_at_str = data["updated_at"]
106
+ if isinstance(updated_at_str, str):
107
+ updated_at_str = updated_at_str.replace("Z", "+00:00")
108
+ updated_at = datetime.fromisoformat(updated_at_str)
109
+
110
+ return cls(
111
+ job_id=job_id,
112
+ status=data.get("status", "pending"),
113
+ created_at=created_at,
114
+ updated_at=updated_at,
115
+ error_message=data.get("error_message"),
116
+ )
117
+
118
+
119
+ @dataclass
120
+ class TranscriptionResult:
121
+ """
122
+ Transcription result for a completed job.
123
+
124
+ Attributes:
125
+ job_id: Unique job identifier
126
+ status: Job status
127
+ transcription: Transcription data (dict) - only present if completed
128
+ message: Status message
129
+ """
130
+ job_id: UUID
131
+ status: str
132
+ transcription: Optional[Dict[str, Any]] = None
133
+ message: Optional[str] = None
134
+
135
+ @classmethod
136
+ def from_dict(cls, data: Dict[str, Any]) -> "TranscriptionResult":
137
+ """Create TranscriptionResult from API response dictionary."""
138
+ job_id = data.get("job_id")
139
+ if isinstance(job_id, str):
140
+ job_id = UUID(job_id)
141
+ elif not isinstance(job_id, UUID):
142
+ raise ValueError(f"Invalid job_id format: {job_id}")
143
+
144
+ return cls(
145
+ job_id=job_id,
146
+ status=data.get("status", "pending"),
147
+ transcription=data.get("transcription"),
148
+ message=data.get("message"),
149
+ )
@@ -15,7 +15,7 @@ from .errors import SpeechCortexApiKeyError
15
15
  try:
16
16
  from . import __version__
17
17
  except ImportError:
18
- __version__ = "0.1.0"
18
+ __version__ = "0.1.1"
19
19
 
20
20
 
21
21
  class SpeechCortexClientOptions: # pylint: disable=too-many-instance-attributes
@@ -44,6 +44,7 @@ class SpeechCortexClientOptions: # pylint: disable=too-many-instance-attributes
44
44
  headers: Optional[Dict] = None,
45
45
  options: Optional[Dict] = None,
46
46
  realtime_path: str = "/transcribe/realtime",
47
+ batch_path: str = "/api/v1/transcription",
47
48
  ):
48
49
  self._logger = verboselogs.VerboseLogger(__name__)
49
50
  self._logger.addHandler(logging.StreamHandler())
@@ -114,6 +115,7 @@ class SpeechCortexClientOptions: # pylint: disable=too-many-instance-attributes
114
115
  break
115
116
 
116
117
  self.options = options
118
+ self.batch_path = batch_path
117
119
 
118
120
  # Normalize realtime path (ensure starts with '/'; strip trailing '?')
119
121
  if realtime_path is None or realtime_path == "":
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: speechcortex-sdk
3
- Version: 0.1.0
3
+ Version: 0.1.dev2
4
4
  Summary: The official Python SDK for SpeechCortex ASR platform.
5
- Home-page: https://github.com/skanda-observeai/zeus-sdk-py
5
+ Home-page: https://github.com/speechcortex/speechcortex-sdk
6
6
  Author: SpeechCortex Team
7
7
  Author-email: SpeechCortex Team <team@speechcortex.com>
8
8
  License: MIT
9
- Project-URL: Homepage, https://github.com/skanda-observeai/zeus-sdk-py
10
- Project-URL: Bug Tracker, https://github.com/skanda-observeai/zeus-sdk-py/issues
11
- Project-URL: Source Code, https://github.com/skanda-observeai/zeus-sdk-py
12
- Project-URL: Documentation, https://github.com/skanda-observeai/zeus-sdk-py#readme
9
+ Project-URL: Homepage, https://github.com/speechcortex/speechcortex-sdk
10
+ Project-URL: Bug Tracker, https://github.com/speechcortex/speechcortex-sdk/issues
11
+ Project-URL: Source Code, https://github.com/speechcortex/speechcortex-sdk
12
+ Project-URL: Documentation, https://github.com/speechcortex/speechcortex-sdk#readme
13
13
  Keywords: speechcortex,asr,speech-to-text,speech recognition
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
@@ -35,7 +35,7 @@ Dynamic: requires-python
35
35
 
36
36
  # SpeechCortex Python SDK
37
37
 
38
- [![CI](https://github.com/skanda-observeai/zeus-sdk-py/actions/workflows/ci.yml/badge.svg)](https://github.com/skanda-observeai/zeus-sdk-py/actions/workflows/ci.yml)
38
+ [![CI](https://github.com/speechcortex/speechcortex-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/speechcortex/speechcortex-sdk/actions/workflows/ci.yml)
39
39
  [![PyPI version](https://badge.fury.io/py/speechcortex-sdk.svg)](https://badge.fury.io/py/speechcortex-sdk)
40
40
  [![Python Versions](https://img.shields.io/pypi/pyversions/speechcortex-sdk.svg)](https://pypi.org/project/speechcortex-sdk/)
41
41
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -57,7 +57,7 @@ Official Python SDK for SpeechCortex ASR (Automatic Speech Recognition) platform
57
57
  ## Installation
58
58
 
59
59
  ```bash
60
- pip install "git+https://github.com/skanda-observeai/zeus-sdk-py.git@package_init"
60
+ pip install "git+https://github.com/speechcortex/speechcortex-sdk.git@package_init"
61
61
  ```
62
62
 
63
63
  ```bash
@@ -17,6 +17,11 @@ speechcortex/audio/microphone/microphone.py
17
17
  speechcortex/clients/__init__.py
18
18
  speechcortex/clients/transcribe/__init__.py
19
19
  speechcortex/clients/transcribe/enums.py
20
+ speechcortex/clients/transcribe/batch/__init__.py
21
+ speechcortex/clients/transcribe/batch/client.py
22
+ speechcortex/clients/transcribe/batch/exceptions.py
23
+ speechcortex/clients/transcribe/batch/options.py
24
+ speechcortex/clients/transcribe/batch/response.py
20
25
  speechcortex/clients/transcribe/realtime/__init__.py
21
26
  speechcortex/clients/transcribe/realtime/client.py
22
27
  speechcortex/clients/transcribe/realtime/options.py