diff --git a/deepgram/__init__.py b/deepgram/__init__.py index ddee19b1..18e51e6e 100644 --- a/deepgram/__init__.py +++ b/deepgram/__init__.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT # version -__version__ = '3.0.0' +__version__ = "3.0.0" # entry point for the deepgram python sdk from .client import DeepgramClient, DeepgramApiKeyError @@ -14,10 +14,25 @@ from .clients.live.client import LiveClient, LegacyLiveClient, LiveOptions # prerecorded -from .clients.prerecorded.client import PreRecordedClient, PrerecordedOptions, PrerecordedSource, FileSource, UrlSource +from .clients.prerecorded.client import ( + PreRecordedClient, + PrerecordedOptions, + PrerecordedSource, + FileSource, + UrlSource, +) # manage -from .clients.manage.client import ManageClient, ProjectOptions, KeyOptions, ScopeOptions, InviteOptions, UsageRequestOptions, UsageSummaryOptions, UsageFieldsOptions +from .clients.manage.client import ( + ManageClient, + ProjectOptions, + KeyOptions, + ScopeOptions, + InviteOptions, + UsageRequestOptions, + UsageSummaryOptions, + UsageFieldsOptions, +) # utilities from .audio.microphone.microphone import Microphone diff --git a/deepgram/audio/microphone/errors.py b/deepgram/audio/microphone/errors.py index 3e5af99c..cfd729fa 100644 --- a/deepgram/audio/microphone/errors.py +++ b/deepgram/audio/microphone/errors.py @@ -2,6 +2,7 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT + class DeepgramMicrophoneError(Exception): """ Exception raised for known errors related to Microphone library. @@ -11,10 +12,11 @@ class DeepgramMicrophoneError(Exception): status (str): The HTTP status associated with the API error. original_error (str - json): The original error that was raised. """ + def __init__(self, message: str): super().__init__(message) self.name = "DeepgramMicrophoneError" self.message = message - + def __str__(self): - return f"{self.name}: {self.message}" \ No newline at end of file + return f"{self.name}: {self.message}" diff --git a/deepgram/audio/microphone/microphone.py b/deepgram/audio/microphone/microphone.py index fcd2c89c..9189d2b2 100644 --- a/deepgram/audio/microphone/microphone.py +++ b/deepgram/audio/microphone/microphone.py @@ -16,11 +16,15 @@ RATE = 16000 CHUNK = 8000 + class Microphone: """ - TODO + TODO """ - def __init__(self, push_callback, format=FORMAT, rate=RATE, chunk=CHUNK, channels=CHANNELS): + + def __init__( + self, push_callback, format=FORMAT, rate=RATE, chunk=CHUNK, channels=CHANNELS + ): self.audio = pyaudio.PyAudio() self.chunk = chunk self.rate = rate @@ -37,7 +41,7 @@ def is_active(self): def start(self): if self.stream is not None: raise DeepgramMicrophoneError("Microphone already started") - + self.stream = self.audio.open( format=self.format, channels=self.channels, @@ -86,4 +90,3 @@ def finish(self): self.stream.stop_stream() self.stream.close() self.stream = None - \ No newline at end of file diff --git a/deepgram/client.py b/deepgram/client.py index 56e4f0e5..3908b644 100644 --- a/deepgram/client.py +++ b/deepgram/client.py @@ -12,6 +12,7 @@ from .options import DeepgramClientOptions from .errors import DeepgramApiKeyError, DeepgramModuleError + class DeepgramClient: """ Represents a client for interacting with the Deepgram API. @@ -31,32 +32,33 @@ class DeepgramClient: onprem: Returns an OnPremClient instance for interacting with Deepgram's on-premises API. """ + def __init__(self, api_key: str, config: Optional[DeepgramClientOptions] = None): if not api_key: raise DeepgramApiKeyError("Deepgram API key is required") self.api_key = api_key - if config is None: # Use default configuration + if config is None: # Use default configuration self.config = DeepgramClientOptions(self.api_key) else: config.set_apikey(self.api_key) self.config = config - + @property def listen(self): return ListenClient(self.config) - + @property def manage(self): return self.Version(self.config, "manage") - + @property def onprem(self): return self.Version(self.config, "onprem") # INTERNAL CLASSES class Version: - def __init__(self, config, parent : str): + def __init__(self, config, parent: str): self.config = config self.parent = parent diff --git a/deepgram/clients/abstract_client.py b/deepgram/clients/abstract_client.py index ef551eed..a71f46b3 100644 --- a/deepgram/clients/abstract_client.py +++ b/deepgram/clients/abstract_client.py @@ -8,6 +8,7 @@ from ..options import DeepgramClientOptions from .errors import DeepgramError, DeepgramApiError, DeepgramUnknownApiError + class AbstractRestfulClient: """ An abstract base class for a RESTful HTTP client. @@ -28,6 +29,7 @@ class AbstractRestfulClient: DeepgramApiError: Raised for known API errors. DeepgramUnknownApiError: Raised for unknown API errors. """ + def __init__(self, config: DeepgramClientOptions): if config is None: raise DeepgramError("Config are required") @@ -36,19 +38,27 @@ def __init__(self, config: DeepgramClientOptions): self.client = httpx.AsyncClient() async def get(self, url: str, options=None): - return await self._handle_request('GET', url, params=options, headers=self.config.headers) + return await self._handle_request( + "GET", url, params=options, headers=self.config.headers + ) async def post(self, url: str, options=None, **kwargs): - return await self._handle_request('POST', url, params=options, headers=self.config.headers, **kwargs) + return await self._handle_request( + "POST", url, params=options, headers=self.config.headers, **kwargs + ) async def put(self, url: str, options=None, **kwargs): - return await self._handle_request('PUT', url, params=options, headers=self.config.headers, **kwargs) + return await self._handle_request( + "PUT", url, params=options, headers=self.config.headers, **kwargs + ) async def patch(self, url: str, options=None, **kwargs): - return await self._handle_request('PATCH', url, params=options, headers=self.config.headers, **kwargs) + return await self._handle_request( + "PATCH", url, params=options, headers=self.config.headers, **kwargs + ) async def delete(self, url: str): - return await self._handle_request('DELETE', url, headers=self.config.headers) + return await self._handle_request("DELETE", url, headers=self.config.headers) async def _handle_request(self, method, url, **kwargs): try: @@ -61,7 +71,9 @@ async def _handle_request(self, method, url, **kwargs): status_code = e.response.status_code or 500 try: json_object = json.loads(e.response.text) - raise DeepgramApiError(json_object.get('message'), status_code, json.dumps(json_object)) from e + raise DeepgramApiError( + json_object.get("message"), status_code, json.dumps(json_object) + ) from e except json.decoder.JSONDecodeError: raise DeepgramUnknownApiError(e.response.text, status_code) from e except ValueError as e: diff --git a/deepgram/clients/errors.py b/deepgram/clients/errors.py index fc1ad179..6fffb4b9 100644 --- a/deepgram/clients/errors.py +++ b/deepgram/clients/errors.py @@ -2,6 +2,7 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT + class DeepgramError(Exception): """ Exception raised for unknown errors related to the Deepgram API. @@ -10,6 +11,7 @@ class DeepgramError(Exception): message (str): The error message describing the exception. status (str): The HTTP status associated with the API error. """ + def __init__(self, message: str): super().__init__(message) self.name = "DeepgramError" @@ -18,6 +20,7 @@ def __init__(self, message: str): def __str__(self): return f"{self.name}: {self.message}" + class DeepgramModuleError(Exception): """ Base class for exceptions raised for a missing Deepgram module. @@ -25,10 +28,12 @@ class DeepgramModuleError(Exception): Attributes: message (str): The error message describing the exception. """ + def __init__(self, message: str): super().__init__(message) self.name = "DeepgramModuleError" + class DeepgramApiError(Exception): """ Exception raised for known errors (in json response format) related to the Deepgram API. @@ -38,16 +43,18 @@ class DeepgramApiError(Exception): status (str): The HTTP status associated with the API error. original_error (str - json): The original error that was raised. """ - def __init__(self, message: str, status: str, original_error = None): + + def __init__(self, message: str, status: str, original_error=None): super().__init__(message) self.name = "DeepgramApiError" self.status = status self.message = message self.original_error = original_error - + def __str__(self): return f"{self.name}: {self.message} (Status: {self.status})" + class DeepgramUnknownApiError(Exception): """ Exception raised for unknown errors related to the Deepgram API. @@ -56,6 +63,7 @@ class DeepgramUnknownApiError(Exception): message (str): The error message describing the exception. status (str): The HTTP status associated with the API error. """ + def __init__(self, message: str, status: str): super().__init__(message, status) self.name = "DeepgramUnknownApiError" diff --git a/deepgram/clients/listen.py b/deepgram/clients/listen.py index 9772140c..83f5e78f 100644 --- a/deepgram/clients/listen.py +++ b/deepgram/clients/listen.py @@ -10,25 +10,26 @@ from .live.client import LiveClient, LegacyLiveClient from .errors import DeepgramModuleError + class ListenClient: def __init__(self, config: DeepgramClientOptions): self.config = config - + @property def prerecorded(self): return self.Version(self.config, "prerecorded") - + @property def live(self): return self.Version(self.config, "live") - + @property def legacylive(self): return LegacyLiveClient(self.config) # INTERNAL CLASSES class Version: - def __init__(self, config, parent : str): + def __init__(self, config, parent: str): self.config = config self.parent = parent diff --git a/deepgram/clients/live/client.py b/deepgram/clients/live/client.py index 7e585c20..36eb029b 100644 --- a/deepgram/clients/live/client.py +++ b/deepgram/clients/live/client.py @@ -6,24 +6,29 @@ from .v1.legacy_client import LegacyLiveClient as LegacyLiveClientLatest from .v1.options import LiveOptions as LiveOptionsLatest -''' +""" The client.py points to the current supported version in the SDK. Older versions are supported in the SDK for backwards compatibility. -''' +""" + + class LiveOptions(LiveOptionsLatest): - pass + pass + class LiveClient(LiveClientLatest): - """ + """ Please see LiveClientLatest for details """ - def __init__(self, config): - super().__init__(config) + + def __init__(self, config): + super().__init__(config) + class LegacyLiveClient(LegacyLiveClientLatest): - """ + """ Please see LiveClientLatest for details """ - def __init__(self, config): - super().__init__(config) - \ No newline at end of file + + def __init__(self, config): + super().__init__(config) diff --git a/deepgram/clients/live/enums.py b/deepgram/clients/live/enums.py index 6f2c88c6..40324ab6 100644 --- a/deepgram/clients/live/enums.py +++ b/deepgram/clients/live/enums.py @@ -4,6 +4,7 @@ from enum import Enum + class LiveTranscriptionEvents(Enum): Open = "open" Close = "close" diff --git a/deepgram/clients/live/errors.py b/deepgram/clients/live/errors.py index 8b63e476..30c3a57f 100644 --- a/deepgram/clients/live/errors.py +++ b/deepgram/clients/live/errors.py @@ -2,6 +2,7 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT + class DeepgramError(Exception): """ Exception raised for unknown errors related to the Deepgram API. @@ -10,6 +11,7 @@ class DeepgramError(Exception): message (str): The error message describing the exception. status (str): The HTTP status associated with the API error. """ + def __init__(self, message: str): super().__init__(message) self.name = "DeepgramError" @@ -18,6 +20,7 @@ def __init__(self, message: str): def __str__(self): return f"{self.name}: {self.message}" + class DeepgramWebsocketError(Exception): """ Exception raised for known errors related to Websocket library. @@ -27,10 +30,11 @@ class DeepgramWebsocketError(Exception): status (str): The HTTP status associated with the API error. original_error (str - json): The original error that was raised. """ + def __init__(self, message: str): super().__init__(message) self.name = "DeepgramWebsocketError" self.message = message - + def __str__(self): return f"{self.name}: {self.message}" diff --git a/deepgram/clients/live/helpers.py b/deepgram/clients/live/helpers.py index cccd7988..5eb7b68d 100644 --- a/deepgram/clients/live/helpers.py +++ b/deepgram/clients/live/helpers.py @@ -4,10 +4,11 @@ from urllib.parse import urlparse, urlunparse, parse_qs, urlencode + def append_query_params(url, params=""): parsed_url = urlparse(url) query_params = parse_qs(parsed_url.query) - + if params: for key, value in params.items(): if isinstance(value, bool): @@ -17,11 +18,12 @@ def append_query_params(url, params=""): query_params[key] = query_params.get(key, []) + [str(item)] else: query_params[key] = [str(value)] - + updated_query_string = urlencode(query_params, doseq=True) updated_url = parsed_url._replace(query=updated_query_string).geturl() return updated_url + def convert_to_websocket_url(base_url, endpoint): parsed_url = urlparse(base_url) domain = parsed_url.netloc @@ -30,4 +32,4 @@ def convert_to_websocket_url(base_url, endpoint): else: websocket_scheme = "ws" websocket_url = urlunparse((websocket_scheme, domain, endpoint, "", "", "")) - return websocket_url \ No newline at end of file + return websocket_url diff --git a/deepgram/clients/live/v1/client.py b/deepgram/clients/live/v1/client.py index 065e63e8..6e71354d 100644 --- a/deepgram/clients/live/v1/client.py +++ b/deepgram/clients/live/v1/client.py @@ -16,181 +16,185 @@ PING_INTERVAL = 5 + class LiveClient: - """ - Client for interacting with Deepgram's live transcription services over WebSockets. + """ + Client for interacting with Deepgram's live transcription services over WebSockets. - This class provides methods to establish a WebSocket connection for live transcription and handle real-time transcription events. + This class provides methods to establish a WebSocket connection for live transcription and handle real-time transcription events. - Args: - config (DeepgramClientOptions): all the options for the client. + Args: + config (DeepgramClientOptions): all the options for the client. - Attributes: - endpoint (str): The API endpoint for live transcription. - _socket (websockets.WebSocketClientProtocol): The WebSocket connection object. - _event_handlers (dict): Dictionary of event handlers for specific events. - websocket_url (str): The WebSocket URL used for connection. + Attributes: + endpoint (str): The API endpoint for live transcription. + _socket (websockets.WebSocketClientProtocol): The WebSocket connection object. + _event_handlers (dict): Dictionary of event handlers for specific events. + websocket_url (str): The WebSocket URL used for connection. - Methods: - __call__: Establishes a WebSocket connection for live transcription. - on: Registers event handlers for specific events. - send: Sends data over the WebSocket connection. - finish: Closes the WebSocket connection gracefully. + Methods: + __call__: Establishes a WebSocket connection for live transcription. + on: Registers event handlers for specific events. + send: Sends data over the WebSocket connection. + finish: Closes the WebSocket connection gracefully. """ - def __init__(self, config: DeepgramClientOptions): - if config is None: - raise DeepgramError("Config are required") - - self.config = config - self.endpoint = "v1/listen" - self._socket = None - self.exit = False - self._event_handlers = { event: [] for event in LiveTranscriptionEvents } - self.websocket_url = convert_to_websocket_url(self.config.url, self.endpoint) - - def start(self, options: LiveOptions = None): - self.options = options - - if self._socket is not None: - raise DeepgramWebsocketError("Websocket already started") - - url_with_params = append_query_params(self.websocket_url, self.options) - self._socket = connect(url_with_params, additional_headers=self.config.headers) - - self.exit = False - self.lock_exit = threading.Lock() - self.lock_send = threading.Lock() - - # listening thread - self.listening = threading.Thread(target=self._listening) - self.listening.start() - - # keepalive thread - self.processing = None - if self.config.options.get("keepalive") == "true": - self.processing = threading.Thread(target=self._processing) - self.processing.start() - - def on(self, event, handler): # registers event handlers for specific events - if event in LiveTranscriptionEvents and callable(handler): - self._event_handlers[event].append(handler) - - def _emit(self, event, *args, **kwargs): # triggers the registered event handlers for a specific event - for handler in self._event_handlers[event]: - handler(*args, **kwargs) - - def _listening(self) -> None: - while True: - try: - self.lock_exit.acquire() - myExit = self.exit - self.lock_exit.release() - if myExit: - return - - message = self._socket.recv() - if len(message) == 0: - # print("empty message") - continue - - data = json.loads(message) - response_type = data.get("type") - # print(f"response_type: {response_type}") - - match response_type: - case LiveTranscriptionEvents.Transcript.value: - result = LiveResultResponse.from_json(message) - self._emit(LiveTranscriptionEvents.Transcript, result=result) - case LiveTranscriptionEvents.Metadata.value: - result = MetadataResponse.from_json(message) - self._emit(LiveTranscriptionEvents.Metadata, metadata=result) - case LiveTranscriptionEvents.Error.value: - result = ErrorResponse.from_json(message) - self._emit(LiveTranscriptionEvents.Error, error=result) - case _: - error: ErrorResponse = { - 'type': 'UnhandledMessage', - 'description': 'Unknown message type', - 'message': f'Unhandle message type: {response_type}', - 'variant': '', - } - self._emit(LiveTranscriptionEvents.Error, error) - - except Exception as e: - # print(f"Exception in _listening: {e}") - if e.code == 1000: - # print("Websocket closed") - return - - error: ErrorResponse = { - 'type': 'Exception', - 'description': 'Unknown error _listening', - 'message': f'{e}', - 'variant': '', - } - self._emit(LiveTranscriptionEvents.Error, error) - - def _processing(self) -> None: - # print("Starting KeepAlive") - while True: - try: - time.sleep(PING_INTERVAL) - - self.lock_exit.acquire() - myExit = self.exit - self.lock_exit.release() - if myExit: - return - - # deepgram keepalive - # print("Sending KeepAlive") - self.send(json.dumps({"type": "KeepAlive"})) - - except Exception as e: - # print(f"Exception in _processing: {e}") - if e.code == 1000: - # print("Websocket closed") - return - - error: ErrorResponse = { - 'type': 'Exception', - 'description': 'Unknown error in _processing', - 'message': f'{e}', - 'variant': '', - } - self._emit(LiveTranscriptionEvents.Error, error) - - def send(self, data) -> int: - if self._socket: - self.lock_send.acquire() - ret = self._socket.send(data) - self.lock_send.release() - return ret - return 0 - - def finish(self): - # print("Send CloseStream") - if self._socket: - self._socket.send(json.dumps({"type": "CloseStream"})) - time.sleep(1) - - # print("Closing connection...") - self.lock_exit.acquire() - self.exit = True - self.lock_exit.release() - - # print("Waiting for threads to finish...") - if self.processing is not None: - self.processing.join() - self.processing = None - - # print("Waiting for threads to finish...") - if self.listening is not None: - self.listening.join() - self.listening = None - - if self._socket: - self._socket.close() - - self._socket = None - self.lock_exit = None + + def __init__(self, config: DeepgramClientOptions): + if config is None: + raise DeepgramError("Config are required") + + self.config = config + self.endpoint = "v1/listen" + self._socket = None + self.exit = False + self._event_handlers = {event: [] for event in LiveTranscriptionEvents} + self.websocket_url = convert_to_websocket_url(self.config.url, self.endpoint) + + def start(self, options: LiveOptions = None): + self.options = options + + if self._socket is not None: + raise DeepgramWebsocketError("Websocket already started") + + url_with_params = append_query_params(self.websocket_url, self.options) + self._socket = connect(url_with_params, additional_headers=self.config.headers) + + self.exit = False + self.lock_exit = threading.Lock() + self.lock_send = threading.Lock() + + # listening thread + self.listening = threading.Thread(target=self._listening) + self.listening.start() + + # keepalive thread + self.processing = None + if self.config.options.get("keepalive") == "true": + self.processing = threading.Thread(target=self._processing) + self.processing.start() + + def on(self, event, handler): # registers event handlers for specific events + if event in LiveTranscriptionEvents and callable(handler): + self._event_handlers[event].append(handler) + + def _emit( + self, event, *args, **kwargs + ): # triggers the registered event handlers for a specific event + for handler in self._event_handlers[event]: + handler(*args, **kwargs) + + def _listening(self) -> None: + while True: + try: + self.lock_exit.acquire() + myExit = self.exit + self.lock_exit.release() + if myExit: + return + + message = self._socket.recv() + if len(message) == 0: + # print("empty message") + continue + + data = json.loads(message) + response_type = data.get("type") + # print(f"response_type: {response_type}") + + match response_type: + case LiveTranscriptionEvents.Transcript.value: + result = LiveResultResponse.from_json(message) + self._emit(LiveTranscriptionEvents.Transcript, result=result) + case LiveTranscriptionEvents.Metadata.value: + result = MetadataResponse.from_json(message) + self._emit(LiveTranscriptionEvents.Metadata, metadata=result) + case LiveTranscriptionEvents.Error.value: + result = ErrorResponse.from_json(message) + self._emit(LiveTranscriptionEvents.Error, error=result) + case _: + error: ErrorResponse = { + "type": "UnhandledMessage", + "description": "Unknown message type", + "message": f"Unhandle message type: {response_type}", + "variant": "", + } + self._emit(LiveTranscriptionEvents.Error, error) + + except Exception as e: + # print(f"Exception in _listening: {e}") + if e.code == 1000: + # print("Websocket closed") + return + + error: ErrorResponse = { + "type": "Exception", + "description": "Unknown error _listening", + "message": f"{e}", + "variant": "", + } + self._emit(LiveTranscriptionEvents.Error, error) + + def _processing(self) -> None: + # print("Starting KeepAlive") + while True: + try: + time.sleep(PING_INTERVAL) + + self.lock_exit.acquire() + myExit = self.exit + self.lock_exit.release() + if myExit: + return + + # deepgram keepalive + # print("Sending KeepAlive") + self.send(json.dumps({"type": "KeepAlive"})) + + except Exception as e: + # print(f"Exception in _processing: {e}") + if e.code == 1000: + # print("Websocket closed") + return + + error: ErrorResponse = { + "type": "Exception", + "description": "Unknown error in _processing", + "message": f"{e}", + "variant": "", + } + self._emit(LiveTranscriptionEvents.Error, error) + + def send(self, data) -> int: + if self._socket: + self.lock_send.acquire() + ret = self._socket.send(data) + self.lock_send.release() + return ret + return 0 + + def finish(self): + # print("Send CloseStream") + if self._socket: + self._socket.send(json.dumps({"type": "CloseStream"})) + time.sleep(1) + + # print("Closing connection...") + self.lock_exit.acquire() + self.exit = True + self.lock_exit.release() + + # print("Waiting for threads to finish...") + if self.processing is not None: + self.processing.join() + self.processing = None + + # print("Waiting for threads to finish...") + if self.listening is not None: + self.listening.join() + self.listening = None + + if self._socket: + self._socket.close() + + self._socket = None + self.lock_exit = None diff --git a/deepgram/clients/live/v1/legacy_client.py b/deepgram/clients/live/v1/legacy_client.py index 69adac4f..137196d1 100644 --- a/deepgram/clients/live/v1/legacy_client.py +++ b/deepgram/clients/live/v1/legacy_client.py @@ -12,85 +12,90 @@ from .options import LiveOptions + class LegacyLiveClient: - """ - Client for interacting with Deepgram's live transcription services over WebSockets. + """ + Client for interacting with Deepgram's live transcription services over WebSockets. - This class provides methods to establish a WebSocket connection for live transcription and handle real-time transcription events. + This class provides methods to establish a WebSocket connection for live transcription and handle real-time transcription events. - Args: - config (DeepgramClientOptions): all the options for the client. + Args: + config (DeepgramClientOptions): all the options for the client. - Attributes: - endpoint (str): The API endpoint for live transcription. - _socket (websockets.WebSocketClientProtocol): The WebSocket connection object. - _event_handlers (dict): Dictionary of event handlers for specific events. - websocket_url (str): The WebSocket URL used for connection. + Attributes: + endpoint (str): The API endpoint for live transcription. + _socket (websockets.WebSocketClientProtocol): The WebSocket connection object. + _event_handlers (dict): Dictionary of event handlers for specific events. + websocket_url (str): The WebSocket URL used for connection. - Methods: - __call__: Establishes a WebSocket connection for live transcription. - on: Registers event handlers for specific events. - send: Sends data over the WebSocket connection. - finish: Closes the WebSocket connection gracefully. + Methods: + __call__: Establishes a WebSocket connection for live transcription. + on: Registers event handlers for specific events. + send: Sends data over the WebSocket connection. + finish: Closes the WebSocket connection gracefully. """ - def __init__(self, config: DeepgramClientOptions): - if config is None: - raise DeepgramError("Config are required") - - self.config = config - self.endpoint = "v1/listen" - self._socket = None - self._event_handlers = { event: [] for event in LiveTranscriptionEvents } - self.websocket_url = convert_to_websocket_url(self.config.url, self.endpoint) - - async def __call__(self, options: LiveOptions = None): - self.options = options - url_with_params = append_query_params(self.websocket_url, self.options) - try: - self._socket = await _socket_connect(url_with_params, self.config.headers) - asyncio.create_task(self._start()) - return self - except websockets.ConnectionClosed as e: - await self._emit(LiveTranscriptionEvents.Close, e.code) - - def on(self, event, handler): # registers event handlers for specific events - if event in LiveTranscriptionEvents and callable(handler): - self._event_handlers[event].append(handler) - - async def _emit(self, event, *args, **kwargs): # triggers the registered event handlers for a specific event - for handler in self._event_handlers[event]: - handler(*args, **kwargs) - - async def _start(self) -> None: - async for message in self._socket: + + def __init__(self, config: DeepgramClientOptions): + if config is None: + raise DeepgramError("Config are required") + + self.config = config + self.endpoint = "v1/listen" + self._socket = None + self._event_handlers = {event: [] for event in LiveTranscriptionEvents} + self.websocket_url = convert_to_websocket_url(self.config.url, self.endpoint) + + async def __call__(self, options: LiveOptions = None): + self.options = options + url_with_params = append_query_params(self.websocket_url, self.options) try: - data = json.loads(message) - response_type = data.get("type") - match response_type: - case LiveTranscriptionEvents.Transcript.value: - await self._emit(LiveTranscriptionEvents.Transcript, data) - case LiveTranscriptionEvents.Error.value: - await self._emit(LiveTranscriptionEvents.Error, data) - case LiveTranscriptionEvents.Metadata.value: - await self._emit(LiveTranscriptionEvents.Metadata, data) - case _: - await self._emit(LiveTranscriptionEvents.Error, data) - except json.JSONDecodeError as e: - await self._emit(LiveTranscriptionEvents.Error, e.code) - - async def send(self, data): - if self._socket: - await self._socket.send(data) - - async def finish(self): - if self._socket: - await self._socket.send(json.dumps({"type": "CloseStream"})) - # await self._socket.send("") # Send a zero-byte message - await self._socket.wait_closed() + self._socket = await _socket_connect(url_with_params, self.config.headers) + asyncio.create_task(self._start()) + return self + except websockets.ConnectionClosed as e: + await self._emit(LiveTranscriptionEvents.Close, e.code) + + def on(self, event, handler): # registers event handlers for specific events + if event in LiveTranscriptionEvents and callable(handler): + self._event_handlers[event].append(handler) + + async def _emit( + self, event, *args, **kwargs + ): # triggers the registered event handlers for a specific event + for handler in self._event_handlers[event]: + handler(*args, **kwargs) + + async def _start(self) -> None: + async for message in self._socket: + try: + data = json.loads(message) + response_type = data.get("type") + match response_type: + case LiveTranscriptionEvents.Transcript.value: + await self._emit(LiveTranscriptionEvents.Transcript, data) + case LiveTranscriptionEvents.Error.value: + await self._emit(LiveTranscriptionEvents.Error, data) + case LiveTranscriptionEvents.Metadata.value: + await self._emit(LiveTranscriptionEvents.Metadata, data) + case _: + await self._emit(LiveTranscriptionEvents.Error, data) + except json.JSONDecodeError as e: + await self._emit(LiveTranscriptionEvents.Error, e.code) + + async def send(self, data): + if self._socket: + await self._socket.send(data) + + async def finish(self): + if self._socket: + await self._socket.send(json.dumps({"type": "CloseStream"})) + # await self._socket.send("") # Send a zero-byte message + await self._socket.wait_closed() + async def _socket_connect(websocket_url, headers): destination = websocket_url updated_headers = headers return await websockets.connect( destination, extra_headers=updated_headers, ping_interval=5 - ) \ No newline at end of file + ) diff --git a/deepgram/clients/live/v1/options.py b/deepgram/clients/live/v1/options.py index da232bbf..e5efe0b0 100644 --- a/deepgram/clients/live/v1/options.py +++ b/deepgram/clients/live/v1/options.py @@ -4,6 +4,7 @@ from typing import List, TypedDict + class LiveOptions(TypedDict, total=False): callback: str channels: int @@ -26,4 +27,3 @@ class LiveOptions(TypedDict, total=False): tag: list tier: str version: str - diff --git a/deepgram/clients/live/v1/response.py b/deepgram/clients/live/v1/response.py index 0fc3f8d0..757f75d6 100644 --- a/deepgram/clients/live/v1/response.py +++ b/deepgram/clients/live/v1/response.py @@ -9,6 +9,7 @@ # Result Message + @dataclass_json @dataclass class Word: @@ -22,6 +23,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class Alternative: @@ -35,6 +37,7 @@ def __getitem__(self, key): _dict["words"] = [Word.from_dict(project) for project in _dict["words"]] return _dict[key] + @dataclass_json @dataclass class Channel: @@ -43,9 +46,12 @@ class Channel: def __getitem__(self, key): _dict = self.to_dict() if _dict["alternatives"] is not None: - _dict["alternatives"] = [Alternative.from_dict(project) for project in _dict["alternatives"]] + _dict["alternatives"] = [ + Alternative.from_dict(project) for project in _dict["alternatives"] + ] return _dict[key] + @dataclass_json @dataclass class ModelInfo: @@ -57,6 +63,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class Metadata: @@ -67,9 +74,12 @@ class Metadata: def __getitem__(self, key): _dict = self.to_dict() if _dict["model_info"] is not None: - _dict["model_info"] = [ModelInfo.from_dict(project) for project in _dict["model_info"]] + _dict["model_info"] = [ + ModelInfo.from_dict(project) for project in _dict["model_info"] + ] return _dict[key] + @dataclass_json @dataclass class LiveResultResponse: @@ -85,13 +95,19 @@ class LiveResultResponse: def __getitem__(self, key): _dict = self.to_dict() if _dict["channel"] is not None: - _dict["channel"] = [Channel.from_dict(project) for project in _dict["channel"]] + _dict["channel"] = [ + Channel.from_dict(project) for project in _dict["channel"] + ] if _dict["metadata"] is not None: - _dict["metadata"] = [Metadata.from_dict(project) for project in _dict["metadata"]] + _dict["metadata"] = [ + Metadata.from_dict(project) for project in _dict["metadata"] + ] return _dict[key] + # Metadata Message + @dataclass_json @dataclass class ModelInfo: @@ -103,6 +119,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class MetadataResponse: @@ -116,7 +133,18 @@ class MetadataResponse: models: Optional[List[str]] model_info: Optional[Dict[str, ModelInfo]] - def __init__(self, type: str, transaction_key: str, request_id: str, sha256: str, created: datetime, duration: float, channels: int, models: List[str], model_info: Dict[str, ModelInfo]) -> None: + def __init__( + self, + type: str, + transaction_key: str, + request_id: str, + sha256: str, + created: datetime, + duration: float, + channels: int, + models: List[str], + model_info: Dict[str, ModelInfo], + ) -> None: self.type = type self.transaction_key = transaction_key self.request_id = request_id @@ -134,8 +162,10 @@ def __getitem__(self, key): # _dict["model_info"] = [ModelInfo.from_dict(value) for value in _dict["model_info"]] return _dict[key] + # Error Message + @dataclass_json @dataclass class ErrorResponse: @@ -147,4 +177,3 @@ class ErrorResponse: def __getitem__(self, key): _dict = self.to_dict() return _dict[key] - \ No newline at end of file diff --git a/deepgram/clients/manage/client.py b/deepgram/clients/manage/client.py index 600f4cc8..ec9c8d18 100644 --- a/deepgram/clients/manage/client.py +++ b/deepgram/clients/manage/client.py @@ -3,36 +3,54 @@ # SPDX-License-Identifier: MIT from .v1.client import ManageClient as ManageClientLatest -from .v1.response import ProjectOptions as ProjectOptionsLatest, KeyOptions as KeyOptionsLatest, ScopeOptions as ScopeOptionsLatest, InviteOptions as InviteOptionsLatest, UsageRequestOptions as UsageRequestOptionsLatest, UsageSummaryOptions as UsageSummaryOptionsLatest, UsageFieldsOptions as UsageFieldsOptionsLatest - -''' +from .v1.response import ( + ProjectOptions as ProjectOptionsLatest, + KeyOptions as KeyOptionsLatest, + ScopeOptions as ScopeOptionsLatest, + InviteOptions as InviteOptionsLatest, + UsageRequestOptions as UsageRequestOptionsLatest, + UsageSummaryOptions as UsageSummaryOptionsLatest, + UsageFieldsOptions as UsageFieldsOptionsLatest, +) + +""" The client.py points to the current supported version in the SDK. Older versions are supported in the SDK for backwards compatibility. -''' +""" + + class ProjectOptions(ProjectOptionsLatest): - pass + pass + class KeyOptions(KeyOptionsLatest): - pass + pass + class ScopeOptions(ScopeOptionsLatest): - pass + pass + class InviteOptions(InviteOptionsLatest): - pass + pass + class UsageRequestOptions(UsageRequestOptionsLatest): - pass + pass + class UsageSummaryOptions(UsageSummaryOptionsLatest): - pass + pass + class UsageFieldsOptions(UsageFieldsOptionsLatest): - pass + pass + class ManageClient(ManageClientLatest): """ Please see ManageClientLatest for details """ + def __init__(self, config): - super().__init__(config) + super().__init__(config) diff --git a/deepgram/clients/manage/v1/client.py b/deepgram/clients/manage/v1/client.py index 667517a0..c6aa2689 100644 --- a/deepgram/clients/manage/v1/client.py +++ b/deepgram/clients/manage/v1/client.py @@ -5,8 +5,33 @@ from ....options import DeepgramClientOptions from ...abstract_client import AbstractRestfulClient -from .response import Project, ProjectsResponse, Message, KeysResponse, KeyResponse, Key, MembersResponse, ScopesResponse, InvitesResponse, UsageRequestsResponse, UsageRequest, UsageSummaryResponse, UsageFieldsResponse, BalancesResponse, Balance -from .response import ProjectOptions, KeyOptions, ScopeOptions, InviteOptions, UsageRequestOptions, UsageSummaryOptions, UsageFieldsOptions +from .response import ( + Project, + ProjectsResponse, + Message, + KeysResponse, + KeyResponse, + Key, + MembersResponse, + ScopesResponse, + InvitesResponse, + UsageRequestsResponse, + UsageRequest, + UsageSummaryResponse, + UsageFieldsResponse, + BalancesResponse, + Balance, +) +from .response import ( + ProjectOptions, + KeyOptions, + ScopeOptions, + InviteOptions, + UsageRequestOptions, + UsageSummaryOptions, + UsageFieldsOptions, +) + class ManageClient(AbstractRestfulClient): """ @@ -32,14 +57,16 @@ class ManageClient(AbstractRestfulClient): DeepgramUnknownApiError: Raised for unknown API errors. Exception: For any other unexpected exceptions. """ - def __init__(self, config : DeepgramClientOptions): - self.config = config - self.endpoint = "v1/projects" - super().__init__(config) - + + def __init__(self, config: DeepgramClientOptions): + self.config = config + self.endpoint = "v1/projects" + super().__init__(config) + # projects async def list_projects(self): return self.get_projects() + async def get_projects(self): url = f"{self.config.url}/{self.endpoint}" return ProjectsResponse.from_json(await self.get(url)) @@ -51,6 +78,7 @@ async def get_project(self, project_id: str): async def update_project_option(self, project_id: str, options: ProjectOptions): url = f"{self.config.url}/{self.endpoint}/{project_id}" return Message.from_json(await self.patch(url, json=options)) + async def update_project(self, project_id: str, name=""): url = f"{self.config.url}/{self.endpoint}/{project_id}" options: ProjectOptions = { @@ -65,6 +93,7 @@ async def delete_project(self, project_id: str) -> None: # keys async def list_keys(self, project_id: str): return self.get_keys(project_id) + async def get_keys(self, project_id: str): url = f"{self.config.url}/{self.endpoint}/{project_id}/keys" result = await self.get(url) @@ -93,16 +122,23 @@ async def remove_member(self, project_id: str, member_id: str) -> None: # scopes async def get_member_scopes(self, project_id: str, member_id: str): - url = f"{self.config.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" + url = ( + f"{self.config.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" + ) return ScopesResponse.from_json(await self.get(url)) - async def update_member_scope(self, project_id: str, member_id: str, options: ScopeOptions): - url = f"{self.config.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" + async def update_member_scope( + self, project_id: str, member_id: str, options: ScopeOptions + ): + url = ( + f"{self.config.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" + ) return Message.from_json(await self.put(url, json=options)) # invites async def list_invites(self, project_id: str): return self.get_invites(project_id) + async def get_invites(self, project_id: str): url = f"{self.config.url}/{self.endpoint}/{project_id}/invites" return InvitesResponse.from_json(await self.get(url)) @@ -110,6 +146,7 @@ async def get_invites(self, project_id: str): async def send_invite_options(self, project_id: str, options: InviteOptions): url = f"{self.config.url}/{self.endpoint}/{project_id}/invites" return Message.from_json(await self.post(url, json=options)) + async def send_invite(self, project_id: str, email: str, scope="member"): url = f"{self.config.url}/{self.endpoint}/{project_id}/invites" options: InviteOptions = { @@ -146,6 +183,7 @@ async def get_usage_fields(self, project_id: str, options: UsageFieldsOptions): # balances async def list_balances(self, project_id: str): return self.get_balances(project_id) + async def get_balances(self, project_id: str): url = f"{self.config.url}/{self.endpoint}/{project_id}/balances" return BalancesResponse.from_json(await self.get(url)) diff --git a/deepgram/clients/manage/v1/response.py b/deepgram/clients/manage/v1/response.py index 1a0252cc..9d1445e1 100644 --- a/deepgram/clients/manage/v1/response.py +++ b/deepgram/clients/manage/v1/response.py @@ -9,6 +9,7 @@ # Result Message + @dataclass_json @dataclass class Message: @@ -18,8 +19,10 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + # Projects + @dataclass_json @dataclass class Project: @@ -30,6 +33,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class ProjectsResponse: @@ -38,14 +42,19 @@ class ProjectsResponse: def __getitem__(self, key): _dict = self.to_dict() if _dict["projects"] is not None: - _dict["projects"] = [Project.from_dict(project) for project in _dict["projects"]] + _dict["projects"] = [ + Project.from_dict(project) for project in _dict["projects"] + ] return _dict[key] + class ProjectOptions(TypedDict, total=False): name: Optional[str] + # Members + @dataclass_json @dataclass class Member: @@ -58,6 +67,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class MembersResponse: @@ -69,6 +79,7 @@ def __getitem__(self, key): _dict["members"] = [Member.from_dict(member) for member in _dict["members"]] return _dict[key] + # Keys @dataclass_json @dataclass @@ -82,6 +93,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class KeyResponse: @@ -96,6 +108,7 @@ def __getitem__(self, key): _dict["member"] = Member.from_dict(_dict["member"]) return _dict[key] + @dataclass_json @dataclass class KeysResponse: @@ -104,9 +117,12 @@ class KeysResponse: def __getitem__(self, key): _dict = self.to_dict() if _dict["api_keys"] is not None: - _dict["api_keys"] = [KeyResponse.from_dict(key) for key in _dict["api_keys"]] + _dict["api_keys"] = [ + KeyResponse.from_dict(key) for key in _dict["api_keys"] + ] return _dict[key] + class KeyOptions(TypedDict): comment: Optional[str] scopes: Optional[List[str]] @@ -114,6 +130,7 @@ class KeyOptions(TypedDict): time_to_live_in_seconds: Optional[int] expiration_date: Optional[datetime] + # Scopes @dataclass_json @dataclass @@ -124,11 +141,14 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + class ScopeOptions(TypedDict): scope: str + # Invites + @dataclass_json @dataclass class Invite: @@ -139,6 +159,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class InvitesResponse: @@ -150,10 +171,12 @@ def __getitem__(self, key): _dict["invites"] = [Invite.from_dict(invite) for invite in _dict["invites"]] return _dict[key] + class InviteOptions: email: Optional[str] scope: Optional[str] - + + # Usage @dataclass_json @dataclass @@ -168,6 +191,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class Details: @@ -188,6 +212,7 @@ def __getitem__(self, key): # _dict["config"] = Config.from_dict(_dict["config"]) return _dict[key] + @dataclass_json @dataclass class Callback: @@ -199,6 +224,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class Response: @@ -212,6 +238,7 @@ def __getitem__(self, key): _dict["details"] = Details.from_dict(_dict["details"]) return _dict[key] + @dataclass_json @dataclass class UsageRequest: @@ -231,6 +258,7 @@ def __getitem__(self, key): # _dict["callback"] = Callback.from_dict(_dict["callback"]) return _dict[key] + @dataclass_json @dataclass class UsageRequestsResponse: @@ -241,15 +269,19 @@ class UsageRequestsResponse: def __getitem__(self, key): _dict = self.to_dict() if _dict["requests"] is not None: - _dict["requests"] = [UsageRequest.from_dict(request) for request in _dict["requests"]] + _dict["requests"] = [ + UsageRequest.from_dict(request) for request in _dict["requests"] + ] return _dict[key] + class UsageRequestOptions(TypedDict): start: Optional[str] end: Optional[str] limit: Optional[int] status: Optional[str] + class UsageSummaryOptions(TypedDict): start: Optional[str] end: Optional[str] @@ -273,6 +305,7 @@ class UsageSummaryOptions(TypedDict): numerals: Optional[bool] smart_format: Optional[bool] + @dataclass_json @dataclass class Results: @@ -286,6 +319,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class Resolution: @@ -296,6 +330,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class UsageSummaryResponse: @@ -309,9 +344,12 @@ def __getitem__(self, key): if _dict["resolution"] is not None: _dict["resolution"] = Resolution.from_dict(_dict["resolution"]) if _dict["results"] is not None: - _dict["results"] = [Results.from_dict(result) for result in _dict["results"]] + _dict["results"] = [ + Results.from_dict(result) for result in _dict["results"] + ] return _dict[key] + @dataclass_json @dataclass class UsageModel: @@ -324,6 +362,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class UsageFieldsResponse: @@ -339,12 +378,15 @@ def __getitem__(self, key): _dict["models"] = [UsageModel.from_dict(model) for model in _dict["models"]] return _dict[key] + class UsageFieldsOptions(TypedDict): start: Optional[str] end: Optional[str] + # Billing + @dataclass_json @dataclass class Balance: @@ -357,6 +399,7 @@ def __getitem__(self, key): _dict = self.to_dict() return _dict[key] + @dataclass_json @dataclass class BalancesResponse: @@ -365,5 +408,7 @@ class BalancesResponse: def __getitem__(self, key): _dict = self.to_dict() if _dict["balances"] is not None: - _dict["balances"] = [Balance.from_dict(balance) for balance in _dict["balances"]] - return _dict[key] \ No newline at end of file + _dict["balances"] = [ + Balance.from_dict(balance) for balance in _dict["balances"] + ] + return _dict[key] diff --git a/deepgram/clients/onprem/client.py b/deepgram/clients/onprem/client.py index 5800b3d7..9870faa0 100644 --- a/deepgram/clients/onprem/client.py +++ b/deepgram/clients/onprem/client.py @@ -4,13 +4,16 @@ from .v1.client import OnPremClient as OnPremClientLatest -''' +""" The client.py points to the current supported version in the SDK. Older versions are supported in the SDK for backwards compatibility. -''' +""" + + class OnPremClient(OnPremClientLatest): - """ + """ Please see OnPremClientLatest for details """ - def __init__(self, config): - super().__init__(config) + + def __init__(self, config): + super().__init__(config) diff --git a/deepgram/clients/onprem/v1/client.py b/deepgram/clients/onprem/v1/client.py index 1f28aa69..fff1366c 100644 --- a/deepgram/clients/onprem/v1/client.py +++ b/deepgram/clients/onprem/v1/client.py @@ -4,8 +4,9 @@ from ...abstract_client import AbstractRestfulClient + class OnPremClient(AbstractRestfulClient): - """ + """ Client for interacting with Deepgram's on-premises API. This class provides methods to manage and interact with on-premises projects and distribution credentials. @@ -23,26 +24,28 @@ class OnPremClient(AbstractRestfulClient): delete_onprem_credentials: Deletes an on-premises distribution credential for a project. """ - def __init__(self, config): - self.config = config - self.endpoint = "v1/projects" - super().__init__(config) - - async def list_onprem_credentials(self, project_id: str): - url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials" - return await self.get(url) - - async def get_onprem_credentials(self, project_id: str, distribution_credentials_id: str): - url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" - return await self.get(url) - - async def create_onprem_credentials(self, project_id: str, options): - url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/" - return await self.post(url,json=options) - - async def delete_onprem_credentials(self, project_id: str, distribution_credentials_id: str): - url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" - return await self.delete(url) - - + def __init__(self, config): + self.config = config + self.endpoint = "v1/projects" + super().__init__(config) + + async def list_onprem_credentials(self, project_id: str): + url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials" + return await self.get(url) + + async def get_onprem_credentials( + self, project_id: str, distribution_credentials_id: str + ): + url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" + return await self.get(url) + + async def create_onprem_credentials(self, project_id: str, options): + url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/" + return await self.post(url, json=options) + + async def delete_onprem_credentials( + self, project_id: str, distribution_credentials_id: str + ): + url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" + return await self.delete(url) diff --git a/deepgram/clients/prerecorded/client.py b/deepgram/clients/prerecorded/client.py index c7f36bc8..ad506265 100644 --- a/deepgram/clients/prerecorded/client.py +++ b/deepgram/clients/prerecorded/client.py @@ -6,17 +6,21 @@ from .v1.options import PrerecordedOptions as PrerecordedOptionsLatest from .source import PrerecordedSource, FileSource, UrlSource -''' +""" The client.py points to the current supported version in the SDK. Older versions are supported in the SDK for backwards compatibility. -''' +""" + + class PrerecordedOptions(PrerecordedOptionsLatest): pass + class PreRecordedClient(PreRecordedClientLatest): """ Please see PreRecordedClientLatest for details """ + def __init__(self, config): self.config = config super().__init__(config) diff --git a/deepgram/clients/prerecorded/errors.py b/deepgram/clients/prerecorded/errors.py index 94d2abb7..fc0d4d4c 100644 --- a/deepgram/clients/prerecorded/errors.py +++ b/deepgram/clients/prerecorded/errors.py @@ -2,6 +2,7 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT + class DeepgramTypeError(Exception): """ Exception raised for unknown errors related to unknown Types for Transcription. @@ -10,10 +11,11 @@ class DeepgramTypeError(Exception): message (str): The error message describing the exception. status (str): The HTTP status associated with the API error. """ + def __init__(self, message: str): super().__init__(message) self.name = "DeepgramTypeError" self.message = message def __str__(self): - return f"{self.name}: {self.message}" \ No newline at end of file + return f"{self.name}: {self.message}" diff --git a/deepgram/clients/prerecorded/helpers.py b/deepgram/clients/prerecorded/helpers.py index 225b07be..c0e46604 100644 --- a/deepgram/clients/prerecorded/helpers.py +++ b/deepgram/clients/prerecorded/helpers.py @@ -4,11 +4,14 @@ from .source import PrerecordedSource + def is_buffer_source(provided_source: PrerecordedSource) -> bool: return "buffer" in provided_source + def is_readstream_source(provided_source: PrerecordedSource) -> bool: return "stream" in provided_source -def is_url_source(provided_source: PrerecordedSource) -> bool: + +def is_url_source(provided_source: PrerecordedSource) -> bool: return "url" in provided_source diff --git a/deepgram/clients/prerecorded/source.py b/deepgram/clients/prerecorded/source.py index 65f025b6..b498cebd 100644 --- a/deepgram/clients/prerecorded/source.py +++ b/deepgram/clients/prerecorded/source.py @@ -6,6 +6,7 @@ from io import BufferedReader from typing_extensions import TypedDict + class ReadStreamSource(TypedDict): """ Represents a data source for reading binary data from a stream-like source. @@ -17,9 +18,11 @@ class ReadStreamSource(TypedDict): stream (BufferedReader): A BufferedReader object for reading binary data. mimetype (str): The MIME type or content type associated with the data. """ + stream: BufferedReader mimetype: str + class UrlSource(TypedDict): """ Represents a data source for specifying the location of a file via a URL. @@ -30,8 +33,10 @@ class UrlSource(TypedDict): Attributes: url (str): The URL pointing to the hosted file. """ + url: str + class BufferSource(TypedDict): """ Represents a data source for handling raw binary data. @@ -43,8 +48,10 @@ class BufferSource(TypedDict): buffer (bytes): The binary data. mimetype (str): The MIME type or content type associated with the data. """ + buffer: bytes mimetype: str + PrerecordedSource = Union[UrlSource, BufferSource, ReadStreamSource] FileSource = Union[BufferSource, ReadStreamSource] diff --git a/deepgram/clients/prerecorded/v1/client.py b/deepgram/clients/prerecorded/v1/client.py index 708ba806..a5fff8bb 100644 --- a/deepgram/clients/prerecorded/v1/client.py +++ b/deepgram/clients/prerecorded/v1/client.py @@ -10,10 +10,12 @@ from .options import PrerecordedOptions from .response import AsyncPrerecordedResponse, PrerecordedResponse + class PreRecordedClient(AbstractRestfulClient): """ A client class for handling pre-recorded audio data. Provides methods for transcribing audio from URLs and files. """ + def __init__(self, config): """ Initializes a new instance of the PreRecordedClient. @@ -23,9 +25,12 @@ def __init__(self, config): """ self.config = config super().__init__(config) - + async def transcribe_url( - self, source: UrlSource, options: PrerecordedOptions = None, endpoint: str="v1/listen" + self, + source: UrlSource, + options: PrerecordedOptions = None, + endpoint: str = "v1/listen", ) -> PrerecordedResponse: """ Transcribes audio from a URL source. @@ -46,14 +51,22 @@ async def transcribe_url( url = f"{self.config.url}/{endpoint}" if options is not None and "callback" in options: - return await self.transcribe_url_callback(source, options["callback"], options, endpoint) + return await self.transcribe_url_callback( + source, options["callback"], options, endpoint + ) if is_url_source(source): body = source else: raise DeepgramTypeError("Unknown transcription source type") return await self.post(url, options, json=body) - - async def transcribe_url_callback( self, source: UrlSource, callback:str, options: PrerecordedOptions = None, endpoint: str="v1/listen") -> AsyncPrerecordedResponse: + + async def transcribe_url_callback( + self, + source: UrlSource, + callback: str, + options: PrerecordedOptions = None, + endpoint: str = "v1/listen", + ) -> AsyncPrerecordedResponse: """ Transcribes audio from a URL source and sends the result to a callback URL. @@ -74,15 +87,19 @@ async def transcribe_url_callback( self, source: UrlSource, callback:str, option url = f"{self.config.url}/{endpoint}" if options is None: options = {} - options['callback'] = callback + options["callback"] = callback if is_url_source(source): body = source else: raise DeepgramTypeError("Unknown transcription source type") return await self.post(url, options, json=body) - - async def transcribe_file(self, source: FileSource, options: PrerecordedOptions=None, endpoint: str = "v1/listen") -> PrerecordedResponse: + async def transcribe_file( + self, + source: FileSource, + options: PrerecordedOptions = None, + endpoint: str = "v1/listen", + ) -> PrerecordedResponse: """ Transcribes audio from a local file source. @@ -110,7 +127,13 @@ async def transcribe_file(self, source: FileSource, options: PrerecordedOptions= raise DeepgramTypeError("Unknown transcription source type") return await self.post(url, options, content=body) - async def transcribe_file_callback(self, source: FileSource, callback:str, options: PrerecordedOptions = None, endpoint: str="v1/listen") -> AsyncPrerecordedResponse: + async def transcribe_file_callback( + self, + source: FileSource, + callback: str, + options: PrerecordedOptions = None, + endpoint: str = "v1/listen", + ) -> AsyncPrerecordedResponse: """ Transcribes audio from a local file source and sends the result to a callback URL. @@ -132,7 +155,7 @@ async def transcribe_file_callback(self, source: FileSource, callback:str, optio url = f"{self.config.url}/{endpoint}" if options is None: options = {} - options['callback'] = callback + options["callback"] = callback if is_buffer_source(source): body = source["buffer"] elif is_readstream_source(source): diff --git a/deepgram/clients/prerecorded/v1/options.py b/deepgram/clients/prerecorded/v1/options.py index 82e542d5..4b27a703 100644 --- a/deepgram/clients/prerecorded/v1/options.py +++ b/deepgram/clients/prerecorded/v1/options.py @@ -4,6 +4,7 @@ from typing import Union, List, TypedDict + class PrerecordedOptions(TypedDict, total=False): alternatives: int callback: str diff --git a/deepgram/clients/prerecorded/v1/response.py b/deepgram/clients/prerecorded/v1/response.py index 01e6f3e3..98e27a16 100644 --- a/deepgram/clients/prerecorded/v1/response.py +++ b/deepgram/clients/prerecorded/v1/response.py @@ -6,11 +6,14 @@ # Async Prerecorded Response Types: + class AsyncPrerecordedResponse(TypedDict): request_id: str + # Prerecorded Response Types: + class Metadata(TypedDict): transaction_key: Optional[str] request_id: Optional[str] @@ -19,34 +22,43 @@ class Metadata(TypedDict): duration: Optional[float] channels: Optional[int] models: Optional[List[str]] - model_info: Optional[Dict[str, 'ModelInfo']] - summary_info: Optional[Dict[str, 'SummaryV2']] - warnings: Optional[List['Warning']] + model_info: Optional[Dict[str, "ModelInfo"]] + summary_info: Optional[Dict[str, "SummaryV2"]] + warnings: Optional[List["Warning"]] + class ModelInfo(TypedDict): name: Optional[str] version: Optional[str] arch: Optional[str] + class SummaryV2(TypedDict): summary: Optional[str] start_word: Optional[float] end_word: Optional[float] -class Summaries(SummaryV2): # internal reference to old name + + +class Summaries(SummaryV2): # internal reference to old name pass + class Summary(TypedDict): result: Optional[str] short: Optional[str] -class Summary(Summary): # internal reference to old name + + +class Summary(Summary): # internal reference to old name pass + class Hit(TypedDict): confidence: Optional[float] start: Optional[float] end: Optional[float] snippet: Optional[str] + class Word(TypedDict): word: Optional[str] start: Optional[float] @@ -56,11 +68,13 @@ class Word(TypedDict): speaker: Optional[int] speaker_confidence: Optional[float] + class Sentence(TypedDict): text: Optional[str] start: Optional[float] end: Optional[float] + class Paragraph(TypedDict): sentences: Optional[List[Sentence]] start: Optional[float] @@ -68,33 +82,40 @@ class Paragraph(TypedDict): num_words: Optional[float] speaker: Optional[int] + class Paragraphs(TypedDict): transcript: Optional[str] paragraphs: Optional[List[Paragraph]] + class Topic(TypedDict): topic: Optional[str] confidence: Optional[float] + class Topics(TypedDict): topics: Optional[List[Topic]] text: Optional[str] start_word: Optional[float] end_word: Optional[float] + class Translation(TypedDict): language: Optional[str] translation: Optional[str] + class Warning(TypedDict): parameter: Optional[str] type: Optional[str] message: Optional[str] + class Search(TypedDict): query: Optional[str] hits: Optional[List[Hit]] + class Utterance(TypedDict): start: Optional[float] end: Optional[float] @@ -105,6 +126,7 @@ class Utterance(TypedDict): speaker: Optional[int] id: Optional[str] + class Entity(TypedDict): label: Optional[str] value: Optional[str] @@ -112,6 +134,7 @@ class Entity(TypedDict): start_word: Optional[float] end_word: Optional[float] + class Alternative(TypedDict): transcript: Optional[str] confidence: Optional[float] @@ -122,16 +145,19 @@ class Alternative(TypedDict): translations: Optional[List[Translation]] topics: Optional[List[Topics]] + class Channel(TypedDict): search: Optional[List[Search]] alternatives: Optional[List[Alternative]] detected_language: Optional[str] + class Result(TypedDict): channels: Optional[List[Channel]] utterances: Optional[List[Utterance]] summary: Optional[Summary] + class PrerecordedResponse(TypedDict): metadata: Optional[Metadata] results: Optional[Result] diff --git a/deepgram/errors.py b/deepgram/errors.py index 84f9a743..23439e8b 100644 --- a/deepgram/errors.py +++ b/deepgram/errors.py @@ -2,6 +2,7 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT + class DeepgramApiKeyError(Exception): """ Base class for exceptions raised for a missing Deepgram API Key. @@ -9,10 +10,12 @@ class DeepgramApiKeyError(Exception): Attributes: message (str): The error message describing the exception. """ + def __init__(self, message: str): super().__init__(message) self.name = "DeepgramApiKeyError" + class DeepgramModuleError(Exception): """ Base class for exceptions raised for a missing Deepgram module. @@ -20,6 +23,7 @@ class DeepgramModuleError(Exception): Attributes: message (str): The error message describing the exception. """ + def __init__(self, message: str): super().__init__(message) self.name = "DeepgramModuleError" diff --git a/deepgram/options.py b/deepgram/options.py index 85d67fb9..bd38cb5d 100644 --- a/deepgram/options.py +++ b/deepgram/options.py @@ -5,6 +5,7 @@ from typing import Dict import re + class DeepgramClientOptions: """ @@ -19,18 +20,23 @@ class DeepgramClientOptions: - url (str): The URL used to interact with production, On-prem, and other Deepgram environments. Defaults to `api.deepgram.com`. """ - def __init__(self, api_key: str = "", url: str = "", headers: Dict[str, str] = None, options: Dict[str, str] = None): + def __init__( + self, + api_key: str = "", + url: str = "", + headers: Dict[str, str] = None, + options: Dict[str, str] = None, + ): self.api_key = api_key if headers is None: self.headers = { "Accept": "application/json", - "Authorization": f"Token {self.api_key}" + "Authorization": f"Token {self.api_key}", } else: - self.headers.update({ - "Accept": "application/json", - "Authorization": f"Token {self.api_key}" - }) + self.headers.update( + {"Accept": "application/json", "Authorization": f"Token {self.api_key}"} + ) if len(url) == 0: url = "api.deepgram.com" self.url = self._get_url(url) @@ -40,12 +46,11 @@ def __init__(self, api_key: str = "", url: str = "", headers: Dict[str, str] = N def set_apikey(self, api_key: str): self.api_key = api_key - self.headers.update({ - "Accept": "application/json", - "Authorization": f"Token {self.api_key}" - }) + self.headers.update( + {"Accept": "application/json", "Authorization": f"Token {self.api_key}"} + ) def _get_url(self, url): - if not re.match(r'^https?://', url, re.IGNORECASE): - url = 'https://' + url - return url.strip('/') \ No newline at end of file + if not re.match(r"^https?://", url, re.IGNORECASE): + url = "https://" + url + return url.strip("/") diff --git a/examples/manage/balances/main.py b/examples/manage/balances/main.py index 6c908f98..25a1b8b3 100644 --- a/examples/manage/balances/main.py +++ b/examples/manage/balances/main.py @@ -17,6 +17,7 @@ # Create a Deepgram client using the API key deepgram: DeepgramClient = DeepgramClient(API_KEY) + async def main(): try: # get projects @@ -42,7 +43,9 @@ async def main(): myBalanceId = None for balance in listResp.balances: myBalanceId = balance.balance_id - print(f"GetBalance() - Name: {balance.balance_id}, Amount: {balance.amount}") + print( + f"GetBalance() - Name: {balance.balance_id}, Amount: {balance.amount}" + ) # get balance getResp = await deepgram.manage.v("1").get_balance(myId, myBalanceId) @@ -50,5 +53,6 @@ async def main(): except Exception as e: print(f"Exception: {e}") + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/manage/invitations/main.py b/examples/manage/invitations/main.py index 7254bbd6..67ff3f94 100644 --- a/examples/manage/invitations/main.py +++ b/examples/manage/invitations/main.py @@ -17,6 +17,7 @@ # Create a Deepgram client using the API key deepgram: DeepgramClient = DeepgramClient(API_KEY) + async def main(): try: # get projects @@ -42,10 +43,7 @@ async def main(): print(f"GetInvites() - Name: {invite.email}, Amount: {invite.scope}") # send invite - options: InviteOptions = { - "email": "spam@spam.com", - "scope": "member" - } + options: InviteOptions = {"email": "spam@spam.com", "scope": "member"} getResp = await deepgram.manage.v("1").send_invite_options(myId, options) print(f"SendInvite() - Msg: {getResp.message}") @@ -68,5 +66,6 @@ async def main(): except Exception as e: print(f"Exception: {e}") + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/manage/keys/main.py b/examples/manage/keys/main.py index 8027983e..98904d86 100644 --- a/examples/manage/keys/main.py +++ b/examples/manage/keys/main.py @@ -17,6 +17,7 @@ # Create a Deepgram client using the API key deepgram: DeepgramClient = DeepgramClient(API_KEY) + async def main(): try: # get projects @@ -39,13 +40,12 @@ async def main(): print("No keys found") else: for key in listResp.api_keys: - print(f"GetKeys() - ID: {key.api_key.api_key_id}, Member: {key.member.email}, Comment: {key.api_key.comment}, Scope: {key.api_key.scopes}") + print( + f"GetKeys() - ID: {key.api_key.api_key_id}, Member: {key.member.email}, Comment: {key.api_key.comment}, Scope: {key.api_key.scopes}" + ) # create key - options: KeyOptions = { - "comment": "MyTestKey", - "scopes": ["member"] - } + options: KeyOptions = {"comment": "MyTestKey", "scopes": ["member"]} myKeyId = None createResp = await deepgram.manage.v("1").create_key(myId, options) @@ -54,7 +54,9 @@ async def main(): sys.exit(1) else: myKeyId = createResp.api_key_id - print(f"CreateKey() - ID: {myKeyId}, Comment: {createResp.comment} Scope: {createResp.scopes}") + print( + f"CreateKey() - ID: {myKeyId}, Comment: {createResp.comment} Scope: {createResp.scopes}" + ) # list keys listResp = await deepgram.manage.v("1").get_keys(myId) @@ -62,7 +64,9 @@ async def main(): print("No keys found") else: for key in listResp.api_keys: - print(f"GetKeys() - ID: {key.api_key.api_key_id}, Member: {key.member.email}, Comment: {key.api_key.comment}, Scope: {key.api_key.scopes}") + print( + f"GetKeys() - ID: {key.api_key.api_key_id}, Member: {key.member.email}, Comment: {key.api_key.comment}, Scope: {key.api_key.scopes}" + ) # get key getResp = await deepgram.manage.v("1").get_key(myId, myKeyId) @@ -70,7 +74,9 @@ async def main(): print(f"GetKey failed.") sys.exit(1) else: - print(f"GetKey() - ID: {key.api_key.api_key_id}, Member: {key.member.email}, Comment: {key.api_key.comment}, Scope: {key.api_key.scopes}") + print( + f"GetKey() - ID: {key.api_key.api_key_id}, Member: {key.member.email}, Comment: {key.api_key.comment}, Scope: {key.api_key.scopes}" + ) # delete key deleteResp = await deepgram.manage.v("1").delete_key(myId, myKeyId) @@ -86,9 +92,12 @@ async def main(): print("No keys found") else: for key in listResp.api_keys: - print(f"GetKeys() - ID: {key.api_key.api_key_id}, Member: {key.member.email}, Comment: {key.api_key.comment}, Scope: {key.api_key.scopes}") + print( + f"GetKeys() - ID: {key.api_key.api_key_id}, Member: {key.member.email}, Comment: {key.api_key.comment}, Scope: {key.api_key.scopes}" + ) except Exception as e: print(f"Exception: {e}") - + + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/manage/members/main.py b/examples/manage/members/main.py index 203dcdda..fff54816 100644 --- a/examples/manage/members/main.py +++ b/examples/manage/members/main.py @@ -18,6 +18,7 @@ # Create a Deepgram client using the API key deepgram: DeepgramClient = DeepgramClient(API_KEY) + async def main(): try: # get projects @@ -47,7 +48,9 @@ async def main(): # delete member if delMemberId == None: print("") - print("This example requires a project who already exists who name is in \"DELETE_MEMBER_BY_EMAIL\".") + print( + 'This example requires a project who already exists who name is in "DELETE_MEMBER_BY_EMAIL".' + ) print("This is required to exercise the RemoveMember function.") print("In the absence of this, this example will exit early.") print("") @@ -70,6 +73,7 @@ async def main(): print(f"GetMembers() - ID: {member.member_id}, Email: {member.email}") except Exception as e: print(f"Exception: {e}") - + + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/manage/projects/main.py b/examples/manage/projects/main.py index 3ac27cdc..22977cbe 100644 --- a/examples/manage/projects/main.py +++ b/examples/manage/projects/main.py @@ -18,6 +18,7 @@ # Create a Deepgram client using the API key deepgram: DeepgramClient = DeepgramClient(API_KEY) + async def main(): try: # get projects @@ -36,7 +37,6 @@ async def main(): myName = project.name print(f"ListProjects() - ID: {myId}, Name: {myName}") - # get project getResp = await deepgram.manage.v("1").get_project(myId) print(f"GetProject() - Name: {getResp.name}") @@ -46,7 +46,9 @@ async def main(): "name": "My TEST RENAME Example", } - updateResp = await deepgram.manage.v("1").update_project_option(myId, updateOptions) + updateResp = await deepgram.manage.v("1").update_project_option( + myId, updateOptions + ) if updateResp is None: print(f"UpdateProject failed.") sys.exit(1) @@ -76,8 +78,12 @@ async def main(): # delete project if myDeleteId == None: print("") - print("This example requires a project who already exists who name is in the value \"DELETE_PROJECT_ID\".") - print("This is required to exercise the UpdateProject and DeleteProject function.") + print( + 'This example requires a project who already exists who name is in the value "DELETE_PROJECT_ID".' + ) + print( + "This is required to exercise the UpdateProject and DeleteProject function." + ) print("In the absence of this, this example will exit early.") print("") sys.exit(1) @@ -90,5 +96,6 @@ async def main(): except Exception as e: print(f"Exception: {e}") + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/manage/scopes/main.py b/examples/manage/scopes/main.py index f3786b96..b4d90d90 100644 --- a/examples/manage/scopes/main.py +++ b/examples/manage/scopes/main.py @@ -18,6 +18,7 @@ # Create a Deepgram client using the API key deepgram: DeepgramClient = DeepgramClient(API_KEY) + async def main(): try: # get projects @@ -46,7 +47,9 @@ async def main(): print(f"GetMembers() - ID: {member.member_id}, Email: {member.email}") if memberId == None: - print("This example requires a member who is already a member with email in the value of \"MEMBER_BY_EMAIL\".") + print( + 'This example requires a member who is already a member with email in the value of "MEMBER_BY_EMAIL".' + ) print("This is required to exercise the UpdateMemberScope function.") print("In the absence of this, this example will exit early.") sys.exit(1) @@ -56,14 +59,16 @@ async def main(): if memberResp is None: print("No scopes found") sys.exit(1) - print(f"GetMemberScope() - ID: {myId}, Email: {memberId}, Scope: {memberResp.scopes}") + print( + f"GetMemberScope() - ID: {myId}, Email: {memberId}, Scope: {memberResp.scopes}" + ) # update scope - options: ScopeOptions = { - "scope": "admin" - } + options: ScopeOptions = {"scope": "admin"} - updateResp = await deepgram.manage.v("1").update_member_scope(myId, memberId, options) + updateResp = await deepgram.manage.v("1").update_member_scope( + myId, memberId, options + ) print(f"UpdateMemberScope() - Msg: {updateResp.message}") # get member scope @@ -71,14 +76,16 @@ async def main(): if memberResp is None: print("No scopes found") sys.exit(1) - print(f"GetMemberScope() - ID: {myId}, Email: {memberId}, Scope: {memberResp.scopes}") + print( + f"GetMemberScope() - ID: {myId}, Email: {memberId}, Scope: {memberResp.scopes}" + ) # update scope - options: ScopeOptions = { - "scope": "member" - } + options: ScopeOptions = {"scope": "member"} - updateResp = await deepgram.manage.v("1").update_member_scope(myId, memberId, options) + updateResp = await deepgram.manage.v("1").update_member_scope( + myId, memberId, options + ) print(f"UpdateMemberScope() - Msg: {updateResp.message}") # get member scope @@ -86,9 +93,12 @@ async def main(): if memberResp is None: print("No scopes found") sys.exit(1) - print(f"GetMemberScope() - ID: {myId}, Email: {memberId}, Scope: {memberResp.scopes}") + print( + f"GetMemberScope() - ID: {myId}, Email: {memberId}, Scope: {memberResp.scopes}" + ) except Exception as e: print(f"Exception: {e}") + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/manage/usage/main.py b/examples/manage/usage/main.py index 298ec3b5..9b8b9398 100644 --- a/examples/manage/usage/main.py +++ b/examples/manage/usage/main.py @@ -7,7 +7,12 @@ import sys from dotenv import load_dotenv -from deepgram import DeepgramClient, UsageFieldsOptions, UsageSummaryOptions, UsageRequestOptions +from deepgram import ( + DeepgramClient, + UsageFieldsOptions, + UsageSummaryOptions, + UsageRequestOptions, +) load_dotenv() @@ -17,6 +22,7 @@ # Create a Deepgram client using the API key deepgram: DeepgramClient = DeepgramClient(API_KEY) + async def main(): try: # get projects @@ -51,7 +57,9 @@ async def main(): print("No request found") else: for request in listResp.requests: - print(f"GetUsageRequest() - ID: {request.request_id}, Path: {request.path}") + print( + f"GetUsageRequest() - ID: {request.request_id}, Path: {request.path}" + ) print("") # get fields @@ -62,7 +70,9 @@ async def main(): sys.exit(1) else: for model in listResp.models: - print(f"GetUsageFields Models - ID: {model.model_id}, Name: {model.name}") + print( + f"GetUsageFields Models - ID: {model.model_id}, Name: {model.name}" + ) for method in listResp.processing_methods: print(f"GetUsageFields Methods: {method}") print("") @@ -77,6 +87,7 @@ async def main(): print(f"GetSummary - {item.requests} Calls/{listResp.resolution.units}") except Exception as e: print(f"Exception: {e}") - + + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/prerecorded/file/main.py b/examples/prerecorded/file/main.py index 1855a050..2d6a80c4 100644 --- a/examples/prerecorded/file/main.py +++ b/examples/prerecorded/file/main.py @@ -10,7 +10,7 @@ load_dotenv() -API_KEY = os.getenv('DG_API_KEY') +API_KEY = os.getenv("DG_API_KEY") AUDIO_FILE = "preamble.wav" options: PrerecordedOptions = { @@ -22,19 +22,23 @@ # STEP 1 Create a Deepgram client using the API key (optional - add config options) deepgram = DeepgramClient(API_KEY) + # STEP 2 Call the transcribe_file method on the prerecorded class async def transcribe_file(): # Logic to read the file - with open(AUDIO_FILE, 'rb') as file: + with open(AUDIO_FILE, "rb") as file: buffer_data = file.read() payload: FileSource = { "buffer": buffer_data, } - - file_response = await deepgram.listen.prerecorded.v("1").transcribe_file(payload, options) + + file_response = await deepgram.listen.prerecorded.v("1").transcribe_file( + payload, options + ) return file_response + async def main(): try: response = await transcribe_file() @@ -42,5 +46,6 @@ async def main(): except Exception as e: print(f"Exception: {e}") + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/prerecorded/url/main.py b/examples/prerecorded/url/main.py index f7642c43..5f3a83e4 100644 --- a/examples/prerecorded/url/main.py +++ b/examples/prerecorded/url/main.py @@ -10,8 +10,10 @@ load_dotenv() -API_KEY = os.getenv('DG_API_KEY') -AUDIO_URL = {"url":"https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav"} +API_KEY = os.getenv("DG_API_KEY") +AUDIO_URL = { + "url": "https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav" +} options: PrerecordedOptions = { "model": "nova", @@ -22,9 +24,12 @@ # STEP 1 Create a Deepgram client using the API key (optional - add config options) deepgram = DeepgramClient(API_KEY) + # STEP 2 Call the transcribe_url method on the prerecorded class async def transcribe_url(): - url_response = await deepgram.listen.prerecorded.v("1").transcribe_url(AUDIO_URL, options) + url_response = await deepgram.listen.prerecorded.v("1").transcribe_url( + AUDIO_URL, options + ) return url_response @@ -34,6 +39,7 @@ async def main(): print(response) except Exception as e: print(f"Exception: {e}") - + + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/streaming/http/main.py b/examples/streaming/http/main.py index 9cd66be8..558afb99 100644 --- a/examples/streaming/http/main.py +++ b/examples/streaming/http/main.py @@ -11,49 +11,49 @@ load_dotenv() -options: LiveOptions = { - 'model': 'nova', - 'interim_results': False, - 'language': 'en-US' - } +options: LiveOptions = {"model": "nova", "interim_results": False, "language": "en-US"} # URL for the realtime streaming audio you would like to transcribe -URL = 'http://stream.live.vc.bbcmedia.co.uk/bbc_world_service' +URL = "http://stream.live.vc.bbcmedia.co.uk/bbc_world_service" -deepgram_api_key = os.getenv('DG_API_KEY') +deepgram_api_key = os.getenv("DG_API_KEY") async def main(): - deepgram: DeepgramClient = DeepgramClient(deepgram_api_key) - - # Create a websocket connection to Deepgram - try: - dg_connection = await deepgram.listen.legacylive(options) - except Exception as e: - print(f'Could not open socket: {e}') - return - - # Listen for transcripts received from Deepgram and write them to the console - dg_connection.on(LiveTranscriptionEvents.Transcript, print) - - # Listen for metadata received from Deepgram and write to the console - dg_connection.on(LiveTranscriptionEvents.Metadata, print) - - # Listen for the connection to close - dg_connection.on(LiveTranscriptionEvents.Close, lambda c: print(f'Connection closed with code {c}.')) - - # Send streaming audio from the URL to Deepgram - async with aiohttp.ClientSession() as session: - async with session.get(URL) as audio: - while True: - data = await audio.content.readany() - # send audio data through the socket - await dg_connection.send(data) - # If no data is being sent from the live stream, then break out of the loop. - if not data: - break - - # Indicate that we've finished sending data by sending the {"type": "CloseStream"} - await dg_connection.finish() - -asyncio.run(main()) \ No newline at end of file + deepgram: DeepgramClient = DeepgramClient(deepgram_api_key) + + # Create a websocket connection to Deepgram + try: + dg_connection = await deepgram.listen.legacylive(options) + except Exception as e: + print(f"Could not open socket: {e}") + return + + # Listen for transcripts received from Deepgram and write them to the console + dg_connection.on(LiveTranscriptionEvents.Transcript, print) + + # Listen for metadata received from Deepgram and write to the console + dg_connection.on(LiveTranscriptionEvents.Metadata, print) + + # Listen for the connection to close + dg_connection.on( + LiveTranscriptionEvents.Close, + lambda c: print(f"Connection closed with code {c}."), + ) + + # Send streaming audio from the URL to Deepgram + async with aiohttp.ClientSession() as session: + async with session.get(URL) as audio: + while True: + data = await audio.content.readany() + # send audio data through the socket + await dg_connection.send(data) + # If no data is being sent from the live stream, then break out of the loop. + if not data: + break + + # Indicate that we've finished sending data by sending the {"type": "CloseStream"} + await dg_connection.finish() + + +asyncio.run(main()) diff --git a/examples/streaming/microphone/main.py b/examples/streaming/microphone/main.py index 11532d19..11a3421c 100644 --- a/examples/streaming/microphone/main.py +++ b/examples/streaming/microphone/main.py @@ -5,19 +5,26 @@ import os from dotenv import load_dotenv -from deepgram import DeepgramClient, DeepgramClientOptions, LiveTranscriptionEvents, LiveOptions, Microphone +from deepgram import ( + DeepgramClient, + DeepgramClientOptions, + LiveTranscriptionEvents, + LiveOptions, + Microphone, +) load_dotenv() options: LiveOptions = { - 'punctuate': True, - 'language': 'en-US', - 'encoding': 'linear16', - 'channels': 1, - 'sample_rate': 16000, + "punctuate": True, + "language": "en-US", + "encoding": "linear16", + "channels": 1, + "sample_rate": 16000, } -deepgram_api_key = os.getenv('DG_API_KEY') +deepgram_api_key = os.getenv("DG_API_KEY") + def on_message(result=None): if result is None: @@ -27,6 +34,7 @@ def on_message(result=None): return print(f"speaker: {sentence}") + def on_metadata(metadata=None): if metadata is None: return @@ -34,6 +42,7 @@ def on_metadata(metadata=None): print(metadata) print("") + def on_error(error=None): if error is None: return @@ -41,8 +50,8 @@ def on_error(error=None): print(error) print("") + def main(): - # config: DeepgramClientOptions = DeepgramClientOptions(options={'keepalive': 'true'}) deepgram: DeepgramClient = DeepgramClient(deepgram_api_key) @@ -54,7 +63,7 @@ def main(): dg_connection.on(LiveTranscriptionEvents.Transcript, on_message) dg_connection.on(LiveTranscriptionEvents.Metadata, on_metadata) dg_connection.on(LiveTranscriptionEvents.Error, on_error) - + # Open a microphone stream microphone = Microphone(dg_connection.send) @@ -63,7 +72,7 @@ def main(): # wait until finished input("Press Enter to stop recording...\n\n") - + # Wait for the connection to close microphone.finish() @@ -73,8 +82,9 @@ def main(): print("Finished") except Exception as e: - print(f'Could not open socket: {e}') + print(f"Could not open socket: {e}") return + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/setup.py b/setup.py index ca151b8f..49c81266 100644 --- a/setup.py +++ b/setup.py @@ -5,33 +5,36 @@ import setuptools import os.path -with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md'), encoding="utf8") as file: +with open( + os.path.join(os.path.abspath(os.path.dirname(__file__)), "README.md"), + encoding="utf8", +) as file: long_description = file.read() setuptools.setup( - name='deepgram-sdk', - description='The official Python SDK for the Deepgram automated speech recognition platform.', + name="deepgram-sdk", + description="The official Python SDK for the Deepgram automated speech recognition platform.", long_description=long_description, - long_description_content_type='text/markdown', - license='MIT', - url='https://github.com/deepgram/deepgram-python-sdk', - author='Luca Todd', - author_email='luca.todd@deepgram.com', + long_description_content_type="text/markdown", + license="MIT", + url="https://github.com/deepgram/deepgram-python-sdk", + author="Luca Todd", + author_email="luca.todd@deepgram.com", classifiers=[ - 'Intended Audience :: Developers', - 'Programming Language :: Python :: 3', + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", ], - keywords='deepgram speech-to-text', + keywords="deepgram speech-to-text", packages=setuptools.find_packages(), install_requires=[ - 'httpx', - 'websockets', + "httpx", + "websockets", 'typing-extensions; python_version < "3.8.0"', - 'dataclasses-json', - 'dataclasses', - 'typing_extensions', - 'python-dotenv', - 'asyncio', - 'aiohttp' + "dataclasses-json", + "dataclasses", + "typing_extensions", + "python-dotenv", + "asyncio", + "aiohttp", ], )