From a4709c2537499c3f5cc77bf89a042b14a39c9743 Mon Sep 17 00:00:00 2001 From: dvonthenen Date: Wed, 29 Nov 2023 11:36:48 -0800 Subject: [PATCH] Implement Logging Levels for Supportability --- deepgram/audio/microphone/microphone.py | 71 ++- deepgram/client.py | 30 +- deepgram/clients/listen.py | 21 +- deepgram/clients/live/v1/client.py | 66 ++- deepgram/clients/live/v1/legacy_client.py | 44 +- deepgram/clients/live/v1/response.py | 109 ++--- deepgram/clients/manage/v1/client.py | 178 ++++++- deepgram/clients/manage/v1/response.py | 311 +++++++----- deepgram/clients/onprem/v1/client.py | 33 +- deepgram/clients/prerecorded/v1/client.py | 199 +++++--- deepgram/clients/prerecorded/v1/response.py | 508 ++++++++++++++------ deepgram/options.py | 3 + examples/manage/balances/main.py | 9 +- examples/prerecorded/file/main.py | 29 +- requirements.txt | 1 + setup.py | 1 + 16 files changed, 1134 insertions(+), 479 deletions(-) diff --git a/deepgram/audio/microphone/microphone.py b/deepgram/audio/microphone/microphone.py index 9189d2b2..536bf064 100644 --- a/deepgram/audio/microphone/microphone.py +++ b/deepgram/audio/microphone/microphone.py @@ -7,14 +7,14 @@ import threading import pyaudio from array import array -from sys import byteorder +import logging, verboselogs from .errors import DeepgramMicrophoneError FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 16000 -CHUNK = 8000 +CHUNK = 8194 class Microphone: @@ -23,8 +23,18 @@ class Microphone: """ def __init__( - self, push_callback, format=FORMAT, rate=RATE, chunk=CHUNK, channels=CHANNELS + self, + push_callback, + verbose=logging.WARNING, + format=FORMAT, + rate=RATE, + chunk=CHUNK, + channels=CHANNELS, ): + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + self.logger.setLevel(verbose) + self.audio = pyaudio.PyAudio() self.chunk = chunk self.rate = rate @@ -34,20 +44,36 @@ def __init__( self.stream = None def is_active(self): + self.logger.debug("Microphone.is_active ENTER") if self.stream is None: + self.logger.error("stream is None") + self.logger.debug("Microphone.is_active LEAVE") return False - return self.stream.is_active() + + val = self.stream.is_active() + self.logger.info("is_active: %s", val) + self.logger.debug("Microphone.is_active LEAVE") + return def start(self): + self.logger.debug("Microphone.start ENTER") + if self.stream is not None: + self.logger.error("stream is None") + self.logger.debug("Microphone.start LEAVE") raise DeepgramMicrophoneError("Microphone already started") + self.logger.info("format: %s", self.format) + self.logger.info("channels: %d", self.channels) + self.logger.info("rate: %d", self.rate) + self.logger.info("chunk: %d", self.chunk) + self.stream = self.audio.open( format=self.format, channels=self.channels, rate=self.rate, input=True, - frames_per_buffer=CHUNK, + frames_per_buffer=self.chunk, ) self.exit = False @@ -57,7 +83,12 @@ def start(self): self.thread = threading.Thread(target=self.processing) self.thread.start() + self.logger.notice("start succeeded") + self.logger.debug("Microphone.start LEAVE") + def processing(self): + self.logger.debug("Microphone.processing ENTER") + try: while True: data = self.stream.read(self.chunk) @@ -66,27 +97,45 @@ def processing(self): localExit = self.exit self.lock.release() if localExit: + self.logger.info("exit is True") break if data is None: + self.logger.info("data is None") continue if inspect.iscoroutinefunction(self.push_callback): + self.logger.verbose("async/await callback") asyncio.run(self.push_callback(data)) else: + self.logger.verbose("regular threaded callback") self.push_callback(data) + self.logger.notice("processing exiting...") + self.logger.debug("Microphone.processing LEAVE") + except Exception as e: - print(f"Error while sending: {str(e)}") + self.logger.error("Error while sending: %s", str(e)) + self.logger.debug("Microphone.processing LEAVE") raise def finish(self): + self.logger.debug("Microphone.finish ENTER") + self.lock.acquire() + self.logger.notice("signal exit") self.exit = True self.lock.release() - self.thread.join() - self.thread = None + if self.thread is not None: + self.thread.join() + self.thread = None + self.logger.notice("processing/send thread joined") - self.stream.stop_stream() - self.stream.close() - self.stream = None + if self.stream is not None: + self.stream.stop_stream() + self.stream.close() + self.stream = None + self.logger.notice("stream/recv thread joined") + + self.logger.notice("finish succeeded") + self.logger.debug("Microphone.finish LEAVE") diff --git a/deepgram/client.py b/deepgram/client.py index 3908b644..3ee7050f 100644 --- a/deepgram/client.py +++ b/deepgram/client.py @@ -4,8 +4,9 @@ from typing import Optional from importlib import import_module +import logging, verboselogs -from .clients.listen import ListenClient +from .clients.listen import ListenClient, PreRecordedClient from .clients.manage.client import ManageClient from .clients.onprem.client import OnPremClient @@ -30,10 +31,13 @@ class DeepgramClient: listen: Returns a ListenClient instance for interacting with Deepgram's transcription services. manage: Returns a ManageClient instance for managing Deepgram resources. onprem: Returns an OnPremClient instance for interacting with Deepgram's on-premises API. - """ def __init__(self, api_key: str, config: Optional[DeepgramClientOptions] = None): + verboselogs.install() + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + if not api_key: raise DeepgramApiKeyError("Deepgram API key is required") @@ -44,6 +48,8 @@ def __init__(self, api_key: str, config: Optional[DeepgramClientOptions] = None) config.set_apikey(self.api_key) self.config = config + self.logger.setLevel(logging.SPAM) + @property def listen(self): return ListenClient(self.config) @@ -59,6 +65,9 @@ def onprem(self): # INTERNAL CLASSES class Version: def __init__(self, config, parent: str): + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + self.logger.setLevel(config.verbose) self.config = config self.parent = parent @@ -75,8 +84,11 @@ def __init__(self, config, parent: str): # raise DeepgramModuleError("Invalid parent") def v(self, version: str = ""): - # print(f"version: {version}") + self.logger.debug("Version.v ENTER") + self.logger.info("version: %s", version) if len(version) == 0: + self.logger.error("version is empty") + self.logger.debug("Version.v LEAVE") raise DeepgramModuleError("Invalid module version") className = "" @@ -86,22 +98,30 @@ def v(self, version: str = ""): case "onprem": className = "OnPremClient" case _: + self.logger.error("parent unknown: %s", self.parent) + self.logger.debug("Version.v LEAVE") raise DeepgramModuleError("Invalid parent type") # create class path path = f"deepgram.clients.{self.parent}.v{version}.client" - # print(f"path: {path}") - # print(f"className: {className}") + self.logger.info("path: %s", path) + self.logger.info("className: %s", className) # import class mod = import_module(path) if mod is None: + self.logger.error("module path is None") + self.logger.debug("Version.v LEAVE") raise DeepgramModuleError("Unable to find package") my_class = getattr(mod, className) if my_class is None: + self.logger.error("my_class is None") + self.logger.debug("Version.v LEAVE") raise DeepgramModuleError("Unable to find class") # instantiate class myClass = my_class(self.config) + self.logger.notice("Version.v succeeded") + self.logger.debug("Version.v LEAVE") return myClass diff --git a/deepgram/clients/listen.py b/deepgram/clients/listen.py index 83f5e78f..8bbfc49d 100644 --- a/deepgram/clients/listen.py +++ b/deepgram/clients/listen.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT from importlib import import_module +import logging, verboselogs from ..options import DeepgramClientOptions @@ -30,6 +31,9 @@ def legacylive(self): # INTERNAL CLASSES class Version: def __init__(self, config, parent: str): + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + self.logger.setLevel(config.verbose) self.config = config self.parent = parent @@ -46,8 +50,11 @@ def __init__(self, config, parent: str): # raise DeepgramModuleError("Invalid parent") def v(self, version: str = ""): - # print(f"version: {version}") + self.logger.debug("Version.v ENTER") + self.logger.info("version: %s", version) if len(version) == 0: + self.logger.error("version is empty") + self.logger.debug("Version.v LEAVE") raise DeepgramModuleError("Invalid module version") className = "" @@ -57,22 +64,30 @@ def v(self, version: str = ""): case "prerecorded": className = "PreRecordedClient" case _: + self.logger.error("parent unknown: %s", self.parent) + self.logger.debug("Version.v LEAVE") raise DeepgramModuleError("Invalid parent type") # create class path path = f"deepgram.clients.{self.parent}.v{version}.client" - # print(f"path: {path}") - # print(f"className: {className}") + self.logger.info("path: %s", path) + self.logger.info("className: %s", className) # import class mod = import_module(path) if mod is None: + self.logger.error("module path is None") + self.logger.debug("Version.v LEAVE") raise DeepgramModuleError("Unable to find package") my_class = getattr(mod, className) if my_class is None: + self.logger.error("my_class is None") + self.logger.debug("Version.v LEAVE") raise DeepgramModuleError("Unable to find class") # instantiate class myClass = my_class(self.config) + self.logger.notice("Version.v succeeded") + self.logger.debug("Version.v LEAVE") return myClass diff --git a/deepgram/clients/live/v1/client.py b/deepgram/clients/live/v1/client.py index 6e71354d..a49590eb 100644 --- a/deepgram/clients/live/v1/client.py +++ b/deepgram/clients/live/v1/client.py @@ -5,6 +5,7 @@ from websockets.sync.client import connect import threading import time +import logging, verboselogs from ....options import DeepgramClientOptions from ..enums import LiveTranscriptionEvents @@ -43,6 +44,10 @@ def __init__(self, config: DeepgramClientOptions): if config is None: raise DeepgramError("Config are required") + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + self.logger.setLevel(config.verbose) + self.config = config self.endpoint = "v1/listen" self._socket = None @@ -51,9 +56,14 @@ def __init__(self, config: DeepgramClientOptions): self.websocket_url = convert_to_websocket_url(self.config.url, self.endpoint) def start(self, options: LiveOptions = None): + self.logger.debug("LiveClient.start ENTER") + # TODO: options + self.options = options if self._socket is not None: + self.logger.error("socket is already initialized") + self.logger.debug("LiveClient.start LEAVE") raise DeepgramWebsocketError("Websocket already started") url_with_params = append_query_params(self.websocket_url, self.options) @@ -70,9 +80,13 @@ def start(self, options: LiveOptions = None): # keepalive thread self.processing = None if self.config.options.get("keepalive") == "true": + self.logger.info("KeepAlive enabled") self.processing = threading.Thread(target=self._processing) self.processing.start() + self.logger.notice("start succeeded") + self.logger.debug("LiveClient.start LEAVE") + def on(self, event, handler): # registers event handlers for specific events if event in LiveTranscriptionEvents and callable(handler): self._event_handlers[event].append(handler) @@ -84,22 +98,26 @@ def _emit( handler(*args, **kwargs) def _listening(self) -> None: + self.logger.debug("LiveClient._listening ENTER") + while True: try: self.lock_exit.acquire() myExit = self.exit self.lock_exit.release() if myExit: + self.logger.notice("exiting gracefully") + self.logger.debug("LiveClient._listening LEAVE") return message = self._socket.recv() if len(message) == 0: - # print("empty message") + self.logger.info("message is empty") continue data = json.loads(message) response_type = data.get("type") - # print(f"response_type: {response_type}") + self.logger.verbose("response_type: %s", response_type) match response_type: case LiveTranscriptionEvents.Transcript.value: @@ -121,9 +139,9 @@ def _listening(self) -> None: self._emit(LiveTranscriptionEvents.Error, error) except Exception as e: - # print(f"Exception in _listening: {e}") if e.code == 1000: - # print("Websocket closed") + self.logger.notice("exiting thread gracefully") + self.logger.debug("LiveClient._listening LEAVE") return error: ErrorResponse = { @@ -134,8 +152,12 @@ def _listening(self) -> None: } self._emit(LiveTranscriptionEvents.Error, error) + self.logger.error("Exception in _listening: %s", str(e)) + self.logger.debug("LiveClient._listening LEAVE") + def _processing(self) -> None: - # print("Starting KeepAlive") + self.logger.debug("LiveClient._processing ENTER") + while True: try: time.sleep(PING_INTERVAL) @@ -144,16 +166,18 @@ def _processing(self) -> None: myExit = self.exit self.lock_exit.release() if myExit: + self.logger.notice("exiting gracefully") + self.logger.debug("LiveClient._processing LEAVE") return # deepgram keepalive - # print("Sending KeepAlive") + self.logger.debug("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") + self.logger.notice("exiting thread gracefully") + self.logger.debug("LiveClient._processing LEAVE") return error: ErrorResponse = { @@ -164,37 +188,55 @@ def _processing(self) -> None: } self._emit(LiveTranscriptionEvents.Error, error) + self.logger.error("Exception in _processing: %s", str(e)) + self.logger.debug("LiveClient._processing LEAVE") + def send(self, data) -> int: + self.logger.spam("LiveClient.send ENTER") + self.logger.spam("data: %s", data) + if self._socket: self.lock_send.acquire() ret = self._socket.send(data) self.lock_send.release() + + self.logger.spam("send bytes: %d", ret) + self.logger.spam("LiveClient.send LEAVE") return ret + + self.logger.spam("message is empty") + self.logger.spam("LiveClient.send LEAVE") return 0 def finish(self): - # print("Send CloseStream") + self.logger.spam("LiveClient.finish ENTER") + if self._socket: + self.logger.notice("sending CloseStream...") self._socket.send(json.dumps({"type": "CloseStream"})) time.sleep(1) - # print("Closing connection...") self.lock_exit.acquire() + self.logger.notice("signal exit") 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 + self.logger.notice("processing thread joined") - # print("Waiting for threads to finish...") if self.listening is not None: self.listening.join() self.listening = None + self.logger.notice("listening thread joined") if self._socket: + self.logger.notice("closing socket...") self._socket.close() self._socket = None self.lock_exit = None + + self.logger.notice("finish succeeded") + self.logger.spam("LiveClient.finish LEAVE") diff --git a/deepgram/clients/live/v1/legacy_client.py b/deepgram/clients/live/v1/legacy_client.py index 137196d1..2b09fb2f 100644 --- a/deepgram/clients/live/v1/legacy_client.py +++ b/deepgram/clients/live/v1/legacy_client.py @@ -4,6 +4,7 @@ import asyncio import json import websockets +import logging, verboselogs from ....options import DeepgramClientOptions from ..enums import LiveTranscriptionEvents @@ -39,6 +40,10 @@ def __init__(self, config: DeepgramClientOptions): if config is None: raise DeepgramError("Config are required") + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + self.logger.setLevel(config.verbose) + self.config = config self.endpoint = "v1/listen" self._socket = None @@ -46,14 +51,22 @@ def __init__(self, config: DeepgramClientOptions): self.websocket_url = convert_to_websocket_url(self.config.url, self.endpoint) async def __call__(self, options: LiveOptions = None): + self.logger.debug("LegacyLiveClient.__call__ ENTER") + # TODO: options + 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()) + + self.logger.notice("__call__ succeeded") + self.logger.debug("LegacyLiveClient.__call__ LEAVE") return self except websockets.ConnectionClosed as e: await self._emit(LiveTranscriptionEvents.Close, e.code) + self.logger.notice("exception: websockets.ConnectionClosed") + self.logger.debug("LegacyLiveClient.__call__ LEAVE") def on(self, event, handler): # registers event handlers for specific events if event in LiveTranscriptionEvents and callable(handler): @@ -66,31 +79,60 @@ async def _emit( handler(*args, **kwargs) async def _start(self) -> None: + self.logger.debug("LegacyLiveClient._start ENTER") + async for message in self._socket: try: data = json.loads(message) response_type = data.get("type") match response_type: case LiveTranscriptionEvents.Transcript.value: + self.logger.verbose( + "response_type: %s, data: %s", response_type, data + ) await self._emit(LiveTranscriptionEvents.Transcript, data) case LiveTranscriptionEvents.Error.value: + self.logger.verbose( + "response_type: %s, data: %s", response_type, data + ) await self._emit(LiveTranscriptionEvents.Error, data) case LiveTranscriptionEvents.Metadata.value: + self.logger.verbose( + "response_type: %s, data: %s", response_type, data + ) await self._emit(LiveTranscriptionEvents.Metadata, data) case _: + self.logger.error( + "response_type: %s, data: %s", response_type, data + ) await self._emit(LiveTranscriptionEvents.Error, data) except json.JSONDecodeError as e: await self._emit(LiveTranscriptionEvents.Error, e.code) + self.logger.error("exception: json.JSONDecodeError: %s", str(e)) + self.logger.debug("LegacyLiveClient._start LEAVE") async def send(self, data): + self.logger.spam("LegacyLiveClient.send ENTER") + self.logger.spam("data: %s", data) + if self._socket: await self._socket.send(data) + self.logger.spam("data sent") + + self.logger.spam("LegacyLiveClient.send LEAVE") async def finish(self): + self.logger.debug("LegacyLiveClient.finish LEAVE") + if self._socket: + self.logger.notice("send CloseStream...") await self._socket.send(json.dumps({"type": "CloseStream"})) - # await self._socket.send("") # Send a zero-byte message + self.logger.notice("socket.wait_closed...") await self._socket.wait_closed() + self.logger.notice("socket.wait_closed succeeded") + + self.logger.notice("finish succeeded") + self.logger.debug("LegacyLiveClient.finish LEAVE") async def _socket_connect(websocket_url, headers): diff --git a/deepgram/clients/live/v1/response.py b/deepgram/clients/live/v1/response.py index 757f75d6..e55f0b4c 100644 --- a/deepgram/clients/live/v1/response.py +++ b/deepgram/clients/live/v1/response.py @@ -13,11 +13,11 @@ @dataclass_json @dataclass class Word: - word: Optional[str] - start: Optional[float] - end: Optional[float] - confidence: Optional[float] - punctuated_word: Optional[str] + word: Optional[str] = "" + start: Optional[float] = 0 + end: Optional[float] = 0 + confidence: Optional[float] = 0 + punctuated_word: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -27,9 +27,9 @@ def __getitem__(self, key): @dataclass_json @dataclass class Alternative: - transcript: Optional[str] - confidence: Optional[float] - words: Optional[List[Word]] + transcript: Optional[str] = "" + confidence: Optional[float] = 0 + words: Optional[List[Word]] = None def __getitem__(self, key): _dict = self.to_dict() @@ -41,7 +41,7 @@ def __getitem__(self, key): @dataclass_json @dataclass class Channel: - alternatives: Optional[List[Alternative]] + alternatives: Optional[List[Alternative]] = None def __getitem__(self, key): _dict = self.to_dict() @@ -55,9 +55,9 @@ def __getitem__(self, key): @dataclass_json @dataclass class ModelInfo: - name: Optional[str] - version: Optional[str] - arch: Optional[str] + name: Optional[str] = "" + version: Optional[str] = "" + arch: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -67,9 +67,9 @@ def __getitem__(self, key): @dataclass_json @dataclass class Metadata: - request_id: Optional[str] - model_info: Optional[ModelInfo] - model_uuid: Optional[str] + request_id: Optional[str] = "" + model_info: Optional[ModelInfo] = None + model_uuid: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -83,14 +83,14 @@ def __getitem__(self, key): @dataclass_json @dataclass class LiveResultResponse: - type: Optional[str] - channel_index: Optional[List[int]] - duration: Optional[float] - start: Optional[float] - is_final: Optional[bool] - speech_final: Optional[bool] - channel: Optional[Channel] - metadata: Optional[Metadata] + type: Optional[str] = "" + channel_index: Optional[List[int]] = None + duration: Optional[float] = 0 + start: Optional[float] = 0 + is_final: Optional[bool] = False + speech_final: Optional[bool] = False + channel: Optional[Channel] = None + metadata: Optional[Metadata] = None def __getitem__(self, key): _dict = self.to_dict() @@ -111,9 +111,9 @@ def __getitem__(self, key): @dataclass_json @dataclass class ModelInfo: - name: Optional[str] - version: Optional[str] - arch: Optional[str] + name: Optional[str] = "" + version: Optional[str] = "" + arch: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -123,43 +123,24 @@ def __getitem__(self, key): @dataclass_json @dataclass class MetadataResponse: - type: Optional[str] - transaction_key: Optional[str] - request_id: Optional[str] - sha256: Optional[str] - created: Optional[str] - duration: Optional[float] - channels: Optional[int] - 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: - self.type = type - self.transaction_key = transaction_key - self.request_id = request_id - self.sha256 = sha256 - self.created = created - self.duration = duration - self.channels = channels - self.models = models - self.model_info = model_info + type: Optional[str] = "" + transaction_key: Optional[str] = "" + request_id: Optional[str] = "" + sha256: Optional[str] = "" + created: Optional[str] = "" + duration: Optional[float] = 0 + channels: Optional[int] = 0 + models: Optional[List[str]] = None + model_info: Optional[Dict[str, ModelInfo]] = None def __getitem__(self, key): _dict = self.to_dict() - # TODO: fix this - # if _dict["model_info"] is not None: - # _dict["model_info"] = [ModelInfo.from_dict(value) for value in _dict["model_info"]] + if _dict["models"] is not None: + _dict["models"] = [str(models) for models in _dict["models"]] + if _dict["model_info"] is not None: + _dict["model_info"] = [ + ModelInfo.from_dict(value) for value in _dict["model_info"] + ] return _dict[key] @@ -169,10 +150,10 @@ def __getitem__(self, key): @dataclass_json @dataclass class ErrorResponse: - description: Optional[str] - message: Optional[str] - type: Optional[str] - variant: Optional[str] + description: Optional[str] = "" + message: Optional[str] = "" + type: Optional[str] = "" + variant: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() diff --git a/deepgram/clients/manage/v1/client.py b/deepgram/clients/manage/v1/client.py index c6aa2689..360619fa 100644 --- a/deepgram/clients/manage/v1/client.py +++ b/deepgram/clients/manage/v1/client.py @@ -2,6 +2,8 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT +import logging, verboselogs + from ....options import DeepgramClientOptions from ...abstract_client import AbstractRestfulClient @@ -59,6 +61,10 @@ class ManageClient(AbstractRestfulClient): """ def __init__(self, config: DeepgramClientOptions): + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + self.logger.setLevel(config.verbose) + self.config = config self.endpoint = "v1/projects" super().__init__(config) @@ -68,126 +74,248 @@ async def list_projects(self): return self.get_projects() async def get_projects(self): + self.logger.debug("ManageClient.get_projects ENTER") url = f"{self.config.url}/{self.endpoint}" - return ProjectsResponse.from_json(await self.get(url)) + res = ProjectsResponse.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_projects succeeded") + self.logger.debug("ManageClient.get_projects LEAVE") + return res async def get_project(self, project_id: str): + self.logger.debug("ManageClient.get_project ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}" - return Project.from_json(await self.get(url)) + res = Project.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_project succeeded") + self.logger.debug("ManageClient.get_project LEAVE") + return res async def update_project_option(self, project_id: str, options: ProjectOptions): + self.logger.debug("ManageClient.update_project_option ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}" - return Message.from_json(await self.patch(url, json=options)) + res = Message.from_json(await self.patch(url, json=options)) + self.logger.info("result: %s", res) + self.logger.notice("update_project_option succeeded") + self.logger.debug("ManageClient.update_project_option LEAVE") + return res async def update_project(self, project_id: str, name=""): + self.logger.debug("ManageClient.update_project ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}" options: ProjectOptions = { "name": name, } - return Message.from_json(await self.patch(url, json=options)) + res = Message.from_json(await self.patch(url, json=options)) + self.logger.info("result: %s", res) + self.logger.notice("update_project succeeded") + self.logger.debug("ManageClient.update_project LEAVE") + return res async def delete_project(self, project_id: str) -> None: + self.logger.debug("ManageClient.delete_project ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}" - return Message.from_json(await self.delete(url)) + res = Message.from_json(await self.delete(url)) + self.logger.info("result: %s", res) + self.logger.notice("delete_project succeeded") + self.logger.debug("ManageClient.delete_project LEAVE") + return res # keys async def list_keys(self, project_id: str): return self.get_keys(project_id) async def get_keys(self, project_id: str): + self.logger.debug("ManageClient.get_keys ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/keys" - result = await self.get(url) - return KeysResponse.from_json(result) + res = KeysResponse.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_keys succeeded") + self.logger.debug("ManageClient.get_keys LEAVE") + return res async def get_key(self, project_id: str, key_id: str): + self.logger.debug("ManageClient.get_key ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/keys/{key_id}" - return KeyResponse.from_json(await self.get(url)) + res = KeyResponse.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_key succeeded") + self.logger.debug("ManageClient.get_key LEAVE") + return res async def create_key(self, project_id: str, options: KeyOptions): + self.logger.debug("ManageClient.create_key ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/keys" - return Key.from_json(await self.post(url, json=options)) + res = Key.from_json(await self.post(url, json=options)) + self.logger.info("result: %s", res) + self.logger.notice("create_key succeeded") + self.logger.debug("ManageClient.create_key LEAVE") + return res async def delete_key(self, project_id: str, key_id: str) -> None: + self.logger.debug("ManageClient.delete_key ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/keys/{key_id}" - return Message.from_json(await self.delete(url)) + res = Message.from_json(await self.delete(url)) + self.logger.info("result: %s", res) + self.logger.notice("delete_key succeeded") + self.logger.debug("ManageClient.delete_key LEAVE") + return res # members + async def list_members(self, project_id: str): + return self.get_members(project_id) + async def get_members(self, project_id: str): + self.logger.debug("ManageClient.get_members ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/members" - return MembersResponse.from_json(await self.get(url)) + res = MembersResponse.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_members succeeded") + self.logger.debug("ManageClient.get_members LEAVE") + return res async def remove_member(self, project_id: str, member_id: str) -> None: + self.logger.debug("ManageClient.remove_member ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/members/{member_id}" - return Message.from_json(await self.delete(url)) + res = Message.from_json(await self.delete(url)) + self.logger.info("result: %s", res) + self.logger.notice("remove_member succeeded") + self.logger.debug("ManageClient.remove_member LEAVE") + return res # scopes async def get_member_scopes(self, project_id: str, member_id: str): + self.logger.debug("ManageClient.get_member_scopes ENTER") url = ( f"{self.config.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" ) - return ScopesResponse.from_json(await self.get(url)) + res = ScopesResponse.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_member_scopes succeeded") + self.logger.debug("ManageClient.get_member_scopes LEAVE") + return res async def update_member_scope( self, project_id: str, member_id: str, options: ScopeOptions ): + self.logger.debug("ManageClient.update_member_scope ENTER") url = ( f"{self.config.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" ) - return Message.from_json(await self.put(url, json=options)) + res = Message.from_json(await self.put(url, json=options)) + self.logger.info("result: %s", res) + self.logger.notice("update_member_scope succeeded") + self.logger.debug("ManageClient.update_member_scope LEAVE") + return res # invites async def list_invites(self, project_id: str): return self.get_invites(project_id) async def get_invites(self, project_id: str): + self.logger.debug("ManageClient.get_invites ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/invites" - return InvitesResponse.from_json(await self.get(url)) + res = InvitesResponse.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_invites succeeded") + self.logger.debug("ManageClient.get_invites LEAVE") + return res async def send_invite_options(self, project_id: str, options: InviteOptions): + self.logger.debug("ManageClient.send_invite_options ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/invites" - return Message.from_json(await self.post(url, json=options)) + res = Message.from_json(await self.post(url, json=options)) + self.logger.info("result: %s", res) + self.logger.notice("send_invite_options succeeded") + self.logger.debug("ManageClient.send_invite_options LEAVE") + return res async def send_invite(self, project_id: str, email: str, scope="member"): + self.logger.debug("ManageClient.send_invite ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/invites" options: InviteOptions = { "email": email, "scope": scope, } - return Message.from_json(await self.post(url, json=options)) + res = Message.from_json(await self.post(url, json=options)) + self.logger.info("result: %s", res) + self.logger.notice("send_invite succeeded") + self.logger.debug("ManageClient.send_invite LEAVE") + return res async def delete_invite(self, project_id: str, email: str): + self.logger.debug("ManageClient.delete_invite ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/invites/{email}" - return Message.from_json(await self.delete(url)) + res = Message.from_json(await self.delete(url)) + self.logger.info("result: %s", res) + self.logger.notice("delete_invite succeeded") + self.logger.debug("ManageClient.delete_invite LEAVE") + return res async def leave_project(self, project_id: str): + self.logger.debug("ManageClient.leave_project ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/leave" - return Message.from_json(await self.delete(url)) + res = Message.from_json(await self.delete(url)) + self.logger.info("result: %s", res) + self.logger.notice("leave_project succeeded") + self.logger.debug("ManageClient.leave_project LEAVE") + return res # usage async def get_usage_requests(self, project_id: str, options: UsageRequestOptions): + self.logger.debug("ManageClient.get_usage_requests ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/requests" - return UsageRequestsResponse.from_json(await self.get(url, options)) + res = UsageRequestsResponse.from_json(await self.get(url, options)) + self.logger.info("result: %s", res) + self.logger.notice("get_usage_requests succeeded") + self.logger.debug("ManageClient.get_usage_requests LEAVE") + return res async def get_usage_request(self, project_id: str, request_id: str): + self.logger.debug("ManageClient.get_usage_request ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/requests/{request_id}" - return UsageRequest.from_json(await self.get(url)) + res = UsageRequest.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_usage_request succeeded") + self.logger.debug("ManageClient.get_usage_request LEAVE") + return res async def get_usage_summary(self, project_id: str, options: UsageSummaryOptions): + self.logger.debug("ManageClient.get_usage_summary ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/usage" - return UsageSummaryResponse.from_json(await self.get(url, options)) + res = UsageSummaryResponse.from_json(await self.get(url, options)) + self.logger.info("result: %s", res) + self.logger.notice("get_usage_summary succeeded") + self.logger.debug("ManageClient.get_usage_summary LEAVE") + return res async def get_usage_fields(self, project_id: str, options: UsageFieldsOptions): + self.logger.debug("ManageClient.get_usage_fields ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/usage/fields" - return UsageFieldsResponse.from_json(await self.get(url, options)) + res = UsageFieldsResponse.from_json(await self.get(url, options)) + self.logger.info("result: %s", res) + self.logger.notice("get_usage_fields succeeded") + self.logger.debug("ManageClient.get_usage_fields LEAVE") + return res # balances async def list_balances(self, project_id: str): return self.get_balances(project_id) async def get_balances(self, project_id: str): + self.logger.debug("ManageClient.get_balances ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/balances" - return BalancesResponse.from_json(await self.get(url)) + res = BalancesResponse.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_balances succeeded") + self.logger.debug("ManageClient.get_balances LEAVE") + return res async def get_balance(self, project_id: str, balance_id: str): + self.logger.debug("ManageClient.get_balance ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/balances/{balance_id}" - return Balance.from_json(await self.get(url)) + res = Balance.from_json(await self.get(url)) + self.logger.info("result: %s", res) + self.logger.notice("get_balance succeeded") + self.logger.debug("ManageClient.get_balance LEAVE") + return res diff --git a/deepgram/clients/manage/v1/response.py b/deepgram/clients/manage/v1/response.py index 9d1445e1..cc5fbdcb 100644 --- a/deepgram/clients/manage/v1/response.py +++ b/deepgram/clients/manage/v1/response.py @@ -13,7 +13,7 @@ @dataclass_json @dataclass class Message: - message: Optional[str] + message: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -26,8 +26,8 @@ def __getitem__(self, key): @dataclass_json @dataclass class Project: - project_id: Optional[str] - name: Optional[str] + project_id: Optional[str] = "" + name: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -37,7 +37,7 @@ def __getitem__(self, key): @dataclass_json @dataclass class ProjectsResponse: - projects: Optional[List[Project]] + projects: Optional[List[Project]] = None def __getitem__(self, key): _dict = self.to_dict() @@ -48,8 +48,14 @@ def __getitem__(self, key): return _dict[key] -class ProjectOptions(TypedDict, total=False): - name: Optional[str] +@dataclass_json +@dataclass +class ProjectOptions: + name: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] # Members @@ -58,10 +64,10 @@ class ProjectOptions(TypedDict, total=False): @dataclass_json @dataclass class Member: - email: Optional[str] - first_name: Optional[str] - last_name: Optional[str] - member_id: Optional[str] + email: Optional[str] = "" + first_name: Optional[str] = "" + last_name: Optional[str] = "" + member_id: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -71,7 +77,7 @@ def __getitem__(self, key): @dataclass_json @dataclass class MembersResponse: - members: Optional[List[Member]] + members: Optional[List[Member]] = None def __getitem__(self, key): _dict = self.to_dict() @@ -84,21 +90,23 @@ def __getitem__(self, key): @dataclass_json @dataclass class Key: - api_key_id: Optional[str] - comment: Optional[str] - created: Optional[str] - scopes: Optional[List[str]] + api_key_id: Optional[str] = "" + comment: Optional[str] = "" + created: Optional[str] = "" + scopes: Optional[List[str]] = None def __getitem__(self, key): _dict = self.to_dict() + if _dict["scopes"] is not None: + _dict["scopes"] = [str(scopes) for scopes in _dict["scopes"]] return _dict[key] @dataclass_json @dataclass class KeyResponse: - api_key: Optional[Key] - member: Optional[Member] + api_key: Optional[Key] = None + member: Optional[Member] = None def __getitem__(self, key): _dict = self.to_dict() @@ -112,7 +120,7 @@ def __getitem__(self, key): @dataclass_json @dataclass class KeysResponse: - api_keys: Optional[List[KeyResponse]] + api_keys: Optional[List[KeyResponse]] = None def __getitem__(self, key): _dict = self.to_dict() @@ -123,27 +131,45 @@ def __getitem__(self, key): return _dict[key] -class KeyOptions(TypedDict): - comment: Optional[str] - scopes: Optional[List[str]] - tags: Optional[List[str]] - time_to_live_in_seconds: Optional[int] - expiration_date: Optional[datetime] +@dataclass_json +@dataclass +class KeyOptions: + comment: Optional[str] = "" + time_to_live_in_seconds: Optional[int] = 0 + expiration_date: Optional[str] = "" + scopes: Optional[List[str]] = None + tags: Optional[List[str]] = None + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["scopes"] is not None: + _dict["scopes"] = [str(scopes) for scopes in _dict["scopes"]] + if _dict["tags"] is not None: + _dict["tags"] = [str(tags) for tags in _dict["tags"]] + return _dict[key] # Scopes @dataclass_json @dataclass class ScopesResponse: - scopes: List[str] + scopes: Optional[List[str]] = None def __getitem__(self, key): _dict = self.to_dict() + if _dict["scopes"] is not None: + _dict["scopes"] = [str(scopes) for scopes in _dict["scopes"]] return _dict[key] -class ScopeOptions(TypedDict): - scope: str +@dataclass_json +@dataclass +class ScopeOptions: + scope: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] # Invites @@ -152,8 +178,8 @@ class ScopeOptions(TypedDict): @dataclass_json @dataclass class Invite: - email: Optional[str] - scope: Optional[str] + email: Optional[str] = "" + scope: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -163,7 +189,7 @@ def __getitem__(self, key): @dataclass_json @dataclass class InvitesResponse: - invites: List[Invite] + invites: Optional[List[Invite]] = None def __getitem__(self, key): _dict = self.to_dict() @@ -172,20 +198,26 @@ def __getitem__(self, key): return _dict[key] +@dataclass_json +@dataclass class InviteOptions: - email: Optional[str] - scope: Optional[str] + email: Optional[str] = "" + scope: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] # Usage @dataclass_json @dataclass class Config: - # diarize: Optional[bool] - # language: Optional[str] - # model: Optional[str] - # punctuate: Optional[bool] - # utterances: Optional[bool] + language: Optional[str] = "" + model: Optional[str] = "" + punctuate: Optional[bool] = False + utterances: Optional[bool] = False + diarize: Optional[bool] = False def __getitem__(self, key): _dict = self.to_dict() @@ -195,30 +227,36 @@ def __getitem__(self, key): @dataclass_json @dataclass class Details: - usd: Optional[float] - duration: Optional[float] - total_audio: Optional[float] - channels: Optional[int] - streams: Optional[int] - models: List[str] - method: Optional[str] - tags: Optional[List[str]] - features: Optional[List[str]] - config: Optional[Config] + usd: Optional[float] = 0 + duration: Optional[float] = 0 + total_audio: Optional[float] = 0 + channels: Optional[int] = 0 + streams: Optional[int] = 0 + method: Optional[str] = "" + models: Optional[List[str]] = None + tags: Optional[List[str]] = None + features: Optional[List[str]] = None + config: Optional[Config] = None def __getitem__(self, key): _dict = self.to_dict() - # if _dict["config"] is not None: - # _dict["config"] = Config.from_dict(_dict["config"]) + if _dict["models"] is not None: + _dict["models"] = [str(models) for models in _dict["models"]] + if _dict["tags"] is not None: + _dict["tags"] = [str(tags) for tags in _dict["tags"]] + if _dict["features"] is not None: + _dict["features"] = [str(features) for features in _dict["features"]] + if _dict["config"] is not None: + _dict["config"] = Config.from_dict(_dict["config"]) return _dict[key] @dataclass_json @dataclass class Callback: - attempts: Optional[int] - code: Optional[int] - completed: Optional[str] + attempts: Optional[int] = 0 + code: Optional[int] = 0 + completed: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -228,9 +266,9 @@ def __getitem__(self, key): @dataclass_json @dataclass class Response: - details: Optional[Details] - code: Optional[int] - completed: Optional[str] + code: Optional[int] = 0 + completed: Optional[str] = "" + details: Optional[Details] = None def __getitem__(self, key): _dict = self.to_dict() @@ -242,29 +280,29 @@ def __getitem__(self, key): @dataclass_json @dataclass class UsageRequest: - request_id: Optional[str] - created: Optional[str] - path: Optional[str] - # accessor: Optional[str] - api_key_id: Optional[str] - response: Optional[Response] - # callback: Optional[Callback] + request_id: Optional[str] = "" + created: Optional[str] = "" + path: Optional[str] = "" + api_key_id: Optional[str] = "" + response: Optional[Response] = None + callback: Optional[Callback] = None + accessor: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() if _dict["response"] is not None: _dict["response"] = Response.from_dict(_dict["response"]) - # if _dict["callback"] is not None: - # _dict["callback"] = Callback.from_dict(_dict["callback"]) + if _dict["callback"] is not None: + _dict["callback"] = Callback.from_dict(_dict["callback"]) return _dict[key] @dataclass_json @dataclass class UsageRequestsResponse: - page: Optional[int] - limit: Optional[int] - requests: List[UsageRequest] + page: Optional[int] = 0 + limit: Optional[int] = 0 + requests: Optional[List[UsageRequest]] = None def __getitem__(self, key): _dict = self.to_dict() @@ -275,45 +313,53 @@ def __getitem__(self, key): 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] - accessor: Optional[str] - tag: Optional[str] - method: Optional[str] - model: Optional[str] - multichannel: Optional[bool] - interim_results: Optional[bool] - punctuate: Optional[bool] - ner: Optional[bool] - utterances: Optional[bool] - replace: Optional[bool] - profanity_filter: Optional[bool] - keywords: Optional[bool] - detect_topics: Optional[bool] - diarize: Optional[bool] - search: Optional[bool] - redact: Optional[bool] - alternatives: Optional[bool] - numerals: Optional[bool] - smart_format: Optional[bool] +class UsageRequestOptions: + start: Optional[str] = "" + end: Optional[str] = "" + limit: Optional[int] = 0 + status: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +class UsageSummaryOptions: + start: Optional[str] = "" + end: Optional[str] = "" + accessor: Optional[str] = "" + tag: Optional[str] = "" + method: Optional[str] = "" + model: Optional[str] = "" + multichannel: Optional[bool] = False + interim_results: Optional[bool] = False + punctuate: Optional[bool] = False + ner: Optional[bool] = False + utterances: Optional[bool] = False + replace: Optional[bool] = False + profanity_filter: Optional[bool] = False + keywords: Optional[bool] = False + detect_topics: Optional[bool] = False + diarize: Optional[bool] = False + search: Optional[bool] = False + redact: Optional[bool] = False + alternatives: Optional[bool] = False + numerals: Optional[bool] = False + smart_format: Optional[bool] = False + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] @dataclass_json @dataclass class Results: - start: Optional[str] - end: Optional[str] - hours: Optional[int] - total_hours: Optional[int] - requests: Optional[int] + start: Optional[str] = "" + end: Optional[str] = "" + hours: Optional[int] = 0 + total_hours: Optional[int] = 0 + requests: Optional[int] = 0 def __getitem__(self, key): _dict = self.to_dict() @@ -323,8 +369,8 @@ def __getitem__(self, key): @dataclass_json @dataclass class Resolution: - units: Optional[str] - amount: Optional[int] + units: Optional[str] = "" + amount: Optional[int] = 0 def __getitem__(self, key): _dict = self.to_dict() @@ -334,10 +380,10 @@ def __getitem__(self, key): @dataclass_json @dataclass class UsageSummaryResponse: - start: Optional[str] - end: Optional[str] - resolution: Optional[Resolution] - results: Optional[List[Results]] + start: Optional[str] = "" + end: Optional[str] = "" + resolution: Optional[Resolution] = None + results: Optional[List[Results]] = None def __getitem__(self, key): _dict = self.to_dict() @@ -353,10 +399,10 @@ def __getitem__(self, key): @dataclass_json @dataclass class UsageModel: - name: Optional[str] - language: Optional[str] - version: Optional[str] - model_id: Optional[str] + name: Optional[str] = "" + language: Optional[str] = "" + version: Optional[str] = "" + model_id: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -366,22 +412,39 @@ def __getitem__(self, key): @dataclass_json @dataclass class UsageFieldsResponse: - tags: Optional[List[str]] - models: Optional[List[UsageModel]] - processing_methods: Optional[List[str]] - # languages: Optional[List[str]] - features: Optional[List[str]] + tags: Optional[List[str]] = None + models: Optional[List[UsageModel]] = None + processing_methods: Optional[List[str]] = None + features: Optional[List[str]] = None + languages: Optional[List[str]] = None def __getitem__(self, key): _dict = self.to_dict() + if _dict["tags"] is not None: + _dict["tags"] = [str(tags) for tags in _dict["tags"]] if _dict["models"] is not None: _dict["models"] = [UsageModel.from_dict(model) for model in _dict["models"]] + if _dict["processing_methods"] is not None: + _dict["processing_methods"] = [ + str(processing_methods) + for processing_methods in _dict["processing_methods"] + ] + if _dict["features"] is not None: + _dict["features"] = [str(features) for features in _dict["features"]] + if _dict["languages"] is not None: + _dict["languages"] = [str(model) for model in _dict["languages"]] return _dict[key] -class UsageFieldsOptions(TypedDict): - start: Optional[str] - end: Optional[str] +class UsageFieldsOptions: + start: Optional[str] = "" + end: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["details"] is not None: + _dict["details"] = Details.from_dict(_dict["details"]) + return _dict[key] # Billing @@ -390,10 +453,10 @@ class UsageFieldsOptions(TypedDict): @dataclass_json @dataclass class Balance: - balance_id: Optional[str] - amount: Optional[str] - units: Optional[str] - purchase_order_id: Optional[str] + balance_id: Optional[str] = "" + amount: Optional[str] = "" + units: Optional[str] = "" + purchase_order_id: Optional[str] = "" def __getitem__(self, key): _dict = self.to_dict() @@ -403,7 +466,7 @@ def __getitem__(self, key): @dataclass_json @dataclass class BalancesResponse: - balances: Optional[List[Balance]] + balances: Optional[List[Balance]] = None def __getitem__(self, key): _dict = self.to_dict() diff --git a/deepgram/clients/onprem/v1/client.py b/deepgram/clients/onprem/v1/client.py index fff1366c..0beffe7c 100644 --- a/deepgram/clients/onprem/v1/client.py +++ b/deepgram/clients/onprem/v1/client.py @@ -2,6 +2,8 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT +import logging, verboselogs + from ...abstract_client import AbstractRestfulClient @@ -26,26 +28,49 @@ class OnPremClient(AbstractRestfulClient): """ def __init__(self, config): + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + self.logger.setLevel(config.verbose) self.config = config self.endpoint = "v1/projects" super().__init__(config) async def list_onprem_credentials(self, project_id: str): + self.logger.debug("OnPremClient.list_onprem_credentials ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials" - return await self.get(url) + res = await self.get(url) + self.logger.info("result: %s", res) + self.logger.notice("list_onprem_credentials succeeded") + self.logger.debug("OnPremClient.list_onprem_credentials LEAVE") + return res async def get_onprem_credentials( self, project_id: str, distribution_credentials_id: str ): + self.logger.debug("OnPremClient.get_onprem_credentials ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" - return await self.get(url) + res = await self.get(url) + self.logger.info("result: %s", res) + self.logger.notice("get_onprem_credentials succeeded") + self.logger.debug("OnPremClient.get_onprem_credentials LEAVE") + return res async def create_onprem_credentials(self, project_id: str, options): + self.logger.debug("OnPremClient.create_onprem_credentials ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/" - return await self.post(url, json=options) + res = await self.post(url, json=options) + self.logger.info("result: %s", res) + self.logger.notice("create_onprem_credentials succeeded") + self.logger.debug("OnPremClient.create_onprem_credentials LEAVE") + return res async def delete_onprem_credentials( self, project_id: str, distribution_credentials_id: str ): + self.logger.debug("OnPremClient.delete_onprem_credentials ENTER") url = f"{self.config.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" - return await self.delete(url) + res = await self.delete(url) + self.logger.info("result: %s", res) + self.logger.notice("delete_onprem_credentials succeeded") + self.logger.debug("OnPremClient.delete_onprem_credentials LEAVE") + return res diff --git a/deepgram/clients/prerecorded/v1/client.py b/deepgram/clients/prerecorded/v1/client.py index a5fff8bb..7879dc06 100644 --- a/deepgram/clients/prerecorded/v1/client.py +++ b/deepgram/clients/prerecorded/v1/client.py @@ -2,6 +2,8 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT +import logging, verboselogs + from ...abstract_client import AbstractRestfulClient from ..errors import DeepgramTypeError from ..helpers import is_buffer_source, is_readstream_source, is_url_source @@ -13,52 +15,77 @@ class PreRecordedClient(AbstractRestfulClient): """ - A client class for handling pre-recorded audio data. Provides methods for transcribing audio from URLs and files. + 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. - - Args: - config (DeepgramClientOptions): all the options for the client. - """ + self.logger = logging.getLogger(__name__) + self.logger.addHandler(logging.StreamHandler()) + self.logger.setLevel(config.verbose) self.config = config super().__init__(config) + """ + Transcribes audio from a URL source. + + Args: + source (UrlSource): The URL source of the audio to transcribe. + options (PrerecordedOptions): Additional options for the transcription (default is None). + endpoint (str): The API endpoint for the transcription (default is "v1/listen"). + + Returns: + SyncPrerecordedResponse: An object containing the transcription result. + + Raises: + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + Exception: For any other unexpected exceptions. + """ + async def transcribe_url( self, source: UrlSource, options: PrerecordedOptions = None, endpoint: str = "v1/listen", ) -> PrerecordedResponse: - """ - Transcribes audio from a URL source. - - Args: - source (UrlSource): The URL source of the audio to transcribe. - options (PrerecordedOptions): Additional options for the transcription (default is None). - endpoint (str): The API endpoint for the transcription (default is "v1/listen"). - - Returns: - SyncPrerecordedResponse: An object containing the transcription result. - - Raises: - DeepgramApiError: Raised for known API errors. - DeepgramUnknownApiError: Raised for unknown API errors. - Exception: For any other unexpected exceptions. - """ - + self.logger.debug("PreRecordedClient.transcribe_url ENTER") url = f"{self.config.url}/{endpoint}" if options is not None and "callback" in options: + self.logger.debug("PreRecordedClient.transcribe_url LEAVE") return await self.transcribe_url_callback( source, options["callback"], options, endpoint ) if is_url_source(source): body = source else: + self.logger.error("Unknown transcription source type") + self.logger.debug("PreRecordedClient.transcribe_url LEAVE") raise DeepgramTypeError("Unknown transcription source type") - return await self.post(url, options, json=body) + + res = PrerecordedResponse.from_json(await self.post(url, options, json=body)) + self.logger.info("result: %s", res) + self.logger.notice("transcribe_url succeeded") + self.logger.debug("PreRecordedClient.transcribe_url LEAVE") + return res + + """ + Transcribes audio from a URL source and sends the result to a callback URL. + + Args: + source (UrlSource): The URL source of the audio to transcribe. + callback (str): The callback URL where the transcription results will be sent. + options (PrerecordedOptions): Additional options for the transcription (default is None). + endpoint (str): The API endpoint for the transcription (default is "v1/listen"). + + Returns: + AsyncPrerecordedResponse: An object containing the request_id or an error message. + + Raises: + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + Exception: For any other unexpected exceptions. + """ async def transcribe_url_callback( self, @@ -67,23 +94,8 @@ async def transcribe_url_callback( options: PrerecordedOptions = None, endpoint: str = "v1/listen", ) -> AsyncPrerecordedResponse: - """ - Transcribes audio from a URL source and sends the result to a callback URL. - - Args: - source (UrlSource): The URL source of the audio to transcribe. - callback (str): The callback URL where the transcription results will be sent. - options (PrerecordedOptions): Additional options for the transcription (default is None). - endpoint (str): The API endpoint for the transcription (default is "v1/listen"). - - Returns: - AsyncPrerecordedResponse: An object containing the request_id or an error message. - - Raises: - DeepgramApiError: Raised for known API errors. - DeepgramUnknownApiError: Raised for unknown API errors. - Exception: For any other unexpected exceptions. - """ + self.logger.debug("PreRecordedClient.transcribe_url_callback ENTER") + url = f"{self.config.url}/{endpoint}" if options is None: options = {} @@ -91,8 +103,35 @@ async def transcribe_url_callback( if is_url_source(source): body = source else: + self.logger.error("Unknown transcription source type") + self.logger.debug("PreRecordedClient.transcribe_url_callback LEAVE") raise DeepgramTypeError("Unknown transcription source type") - return await self.post(url, options, json=body) + + res = AsyncPrerecordedResponse.from_json( + await self.post(url, options, json=body) + ) + self.logger.info("result: %s", res) + self.logger.notice("transcribe_url_callback succeeded") + self.logger.debug("PreRecordedClient.transcribe_url_callback LEAVE") + return res + + """ + Transcribes audio from a local file source. + + Args: + source (FileSource): The local file source of the audio to transcribe. + options (PrerecordedOptions): Additional options for the transcription (default is None). + endpoint (str): The API endpoint for the transcription (default is "v1/listen"). + + Returns: + SyncPrerecordedResponse: An object containing the transcription result or an error message. + + Raises: + + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + Exception: For any other unexpected exceptions. + """ async def transcribe_file( self, @@ -100,23 +139,7 @@ async def transcribe_file( options: PrerecordedOptions = None, endpoint: str = "v1/listen", ) -> PrerecordedResponse: - """ - Transcribes audio from a local file source. - - Args: - source (FileSource): The local file source of the audio to transcribe. - options (PrerecordedOptions): Additional options for the transcription (default is None). - endpoint (str): The API endpoint for the transcription (default is "v1/listen"). - - Returns: - SyncPrerecordedResponse: An object containing the transcription result or an error message. - - Raises: - - DeepgramApiError: Raised for known API errors. - DeepgramUnknownApiError: Raised for unknown API errors. - Exception: For any other unexpected exceptions. - """ + self.logger.debug("PreRecordedClient.transcribe_file ENTER") url = f"{self.config.url}/{endpoint}" if is_buffer_source(source): @@ -124,8 +147,33 @@ async def transcribe_file( elif is_readstream_source(source): body = source["stream"] else: + self.logger.error("Unknown transcription source type") + self.logger.debug("PreRecordedClient.transcribe_file LEAVE") raise DeepgramTypeError("Unknown transcription source type") - return await self.post(url, options, content=body) + + res = PrerecordedResponse.from_json(await self.post(url, options, content=body)) + self.logger.info("result: %s", res) + self.logger.notice("transcribe_file succeeded") + self.logger.debug("PreRecordedClient.transcribe_file LEAVE") + return res + + """ + Transcribes audio from a local file source and sends the result to a callback URL. + + Args: + source (FileSource): The local file source of the audio to transcribe. + callback (str): The callback URL where the transcription results will be sent. + options (PrerecordedOptions): Additional options for the transcription (default is None). + endpoint (str): The API endpoint for the transcription (default is "v1/listen"). + + Returns: + AsyncPrerecordedResponse: An object containing the request_id or an error message. + + Raises: + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + Exception: For any other unexpected exceptions. + """ async def transcribe_file_callback( self, @@ -134,23 +182,7 @@ async def transcribe_file_callback( options: PrerecordedOptions = None, endpoint: str = "v1/listen", ) -> AsyncPrerecordedResponse: - """ - Transcribes audio from a local file source and sends the result to a callback URL. - - Args: - source (FileSource): The local file source of the audio to transcribe. - callback (str): The callback URL where the transcription results will be sent. - options (PrerecordedOptions): Additional options for the transcription (default is None). - endpoint (str): The API endpoint for the transcription (default is "v1/listen"). - - Returns: - AsyncPrerecordedResponse: An object containing the request_id or an error message. - - Raises: - DeepgramApiError: Raised for known API errors. - DeepgramUnknownApiError: Raised for unknown API errors. - Exception: For any other unexpected exceptions. - """ + self.logger.debug("PreRecordedClient.transcribe_file_callback ENTER") url = f"{self.config.url}/{endpoint}" if options is None: @@ -161,5 +193,14 @@ async def transcribe_file_callback( elif is_readstream_source(source): body = source["stream"] else: + self.logger.error("Unknown transcription source type") + self.logger.debug("PreRecordedClient.transcribe_file_callback LEAVE") raise DeepgramTypeError("Unknown transcription source type") - return await self.post(url, options, content=body) + + res = AsyncPrerecordedResponse.from_json( + await self.post(url, options, json=body) + ) + self.logger.info("result: %s", res) + self.logger.notice("transcribe_file_callback succeeded") + self.logger.debug("PreRecordedClient.transcribe_file_callback LEAVE") + return res diff --git a/deepgram/clients/prerecorded/v1/response.py b/deepgram/clients/prerecorded/v1/response.py index 98e27a16..532df37f 100644 --- a/deepgram/clients/prerecorded/v1/response.py +++ b/deepgram/clients/prerecorded/v1/response.py @@ -2,162 +2,386 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT +from dataclasses import dataclass +from dataclasses_json import dataclass_json from typing import List, Optional, TypedDict, Dict # Async Prerecorded Response Types: -class AsyncPrerecordedResponse(TypedDict): - request_id: str +@dataclass_json +@dataclass +class AsyncPrerecordedResponse: + request_id: Optional[str] = "" + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] -# Prerecorded Response Types: - - -class Metadata(TypedDict): - transaction_key: Optional[str] - request_id: Optional[str] - sha256: Optional[str] - created: Optional[str] - 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"]] - - -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] +# Prerecorded Response Types: +@dataclass_json +@dataclass +class SummaryInfo: + input_tokens: Optional[int] = 0 + output_tokens: Optional[int] = 0 + model_uuid: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class ModelInfo: + name: Optional[str] = "" + version: Optional[str] = "" + arch: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Metadata: + transaction_key: Optional[str] = "" + request_id: Optional[str] = "" + sha256: Optional[str] = "" + created: Optional[str] = "" + duration: Optional[float] = 0 + channels: Optional[int] = 0 + models: Optional[List[str]] = None + warnings: Optional[List[Warning]] = None + model_info: Optional[Dict[str, ModelInfo]] = None + summary_info: Optional[SummaryInfo] = None + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["models"] is not None: + _dict["models"] = [str(models) for models in _dict["models"]] + if _dict["model_info"] is not None: + _dict["model_info"] = [ + ModelInfo.from_dict(model_info) for model_info in _dict["model_info"] + ] + if _dict["summary_info"] is not None: + _dict["summary_info"] = SummaryInfo.from_dict(_dict["summary_info"]) + if _dict["warnings"] is not None: + _dict["warnings"] = [ + Warning.from_dict(warning) for warning in _dict["warnings"] + ] + return _dict[key] + + +@dataclass_json +@dataclass +class SummaryV2: + summary: Optional[str] = "" + start_word: Optional[float] = 0 + end_word: Optional[float] = 0 + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass 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 - 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] - end: Optional[float] - confidence: Optional[float] - punctuated_word: Optional[str] - 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] - end: Optional[float] - num_words: Optional[float] - speaker: Optional[int] + summary: Optional[str] = "" + start_word: Optional[float] = 0 + end_word: Optional[float] = 0 + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] -class Paragraphs(TypedDict): - transcript: Optional[str] - paragraphs: Optional[List[Paragraph]] +@dataclass_json +@dataclass +class Summary: + result: Optional[str] = "" + short: Optional[str] = "" -class Topic(TypedDict): - topic: Optional[str] - confidence: Optional[float] + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] -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] - confidence: Optional[float] - channel: Optional[int] - transcript: Optional[str] - words: Optional[List[Word]] - speaker: Optional[int] - id: Optional[str] - - -class Entity(TypedDict): - label: Optional[str] - value: Optional[str] - confidence: Optional[float] - start_word: Optional[float] - end_word: Optional[float] - - -class Alternative(TypedDict): - transcript: Optional[str] - confidence: Optional[float] - words: Optional[List[Word]] - summaries: Optional[List[SummaryV2]] - paragraphs: Optional[Paragraphs] - entities: Optional[List[Entity]] - 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] +@dataclass_json +@dataclass +class Summary(Summary): # internal reference to old name + result: Optional[str] = "" + short: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Hit: + confidence: Optional[float] = 0 + start: Optional[float] = 0 + end: Optional[float] = 0 + snippet: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Word: + word: Optional[str] = "" + start: Optional[float] = 0 + end: Optional[float] = 0 + confidence: Optional[float] = 0 + punctuated_word: Optional[str] = "" + speaker: Optional[int] = 0 + speaker_confidence: Optional[float] = 0 + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Sentence: + text: Optional[str] = "" + start: Optional[float] = 0 + end: Optional[float] = 0 + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Paragraph: + sentences: Optional[List[Sentence]] = None + start: Optional[float] = 0 + end: Optional[float] = 0 + num_words: Optional[float] = 0 + speaker: Optional[int] = 0 + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["sentences"] is not None: + _dict["sentences"] = [ + Sentence.from_dict(sentences) for sentences in _dict["sentences"] + ] + return _dict[key] + + +@dataclass_json +@dataclass +class Paragraphs: + transcript: Optional[str] = "" + paragraphs: Optional[List[Paragraph]] = None + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["paragraphs"] is not None: + _dict["paragraphs"] = [ + Paragraph.from_dict(paragraphs) for paragraphs in _dict["paragraphs"] + ] + return _dict[key] + + +@dataclass_json +@dataclass +class Topic: + topic: Optional[str] = "" + confidence: Optional[float] = 0 + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Topics: + topics: Optional[List[Topic]] = None + text: Optional[str] = "" + start_word: Optional[float] = 0 + end_word: Optional[float] = 0 + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["topics"] is not None: + _dict["topics"] = [Topic.from_dict(topics) for topics in _dict["topics"]] + return _dict[key] + + +@dataclass_json +@dataclass +class Translation: + language: Optional[str] = "" + translation: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Warning: + parameter: Optional[str] = "" + type: Optional[str] = "" + message: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Search: + query: Optional[str] = "" + hits: Optional[List[Hit]] = None + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["hits"] is not None: + _dict["hits"] = [Hit.from_dict(hits) for hits in _dict["hits"]] + return _dict[key] + + +@dataclass_json +@dataclass +class Utterance: + start: Optional[float] = 0 + end: Optional[float] = 0 + confidence: Optional[float] = 0 + channel: Optional[int] = 0 + transcript: Optional[str] = "" + words: Optional[List[Word]] = None + speaker: Optional[int] = 0 + id: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["words"] is not None: + _dict["words"] = [Word.from_dict(words) for words in _dict["words"]] + return _dict[key] + + +@dataclass_json +@dataclass +class Entity: + label: Optional[str] = "" + value: Optional[str] = "" + confidence: Optional[float] = 0 + start_word: Optional[float] = 0 + end_word: Optional[float] = 0 + + def __getitem__(self, key): + _dict = self.to_dict() + return _dict[key] + + +@dataclass_json +@dataclass +class Alternative: + transcript: Optional[str] = "" + confidence: Optional[float] = 0 + words: Optional[List[Word]] = None + summaries: Optional[List[SummaryV2]] = None + paragraphs: Optional[Paragraphs] = None + entities: Optional[List[Entity]] = None + translations: Optional[List[Translation]] = None + topics: Optional[List[Topics]] = None + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["words"] is not None: + _dict["words"] = [Word.from_dict(words) for words in _dict["words"]] + if _dict["summaries"] is not None: + _dict["summaries"] = [ + SummaryV2.from_dict(summaries) for summaries in _dict["summaries"] + ] + if _dict["paragraphs"] is not None: + _dict["paragraphs"] = Paragraphs.from_dict(_dict["paragraphs"]) + if _dict["entities"] is not None: + _dict["entities"] = [ + Entity.from_dict(entities) for entities in _dict["entities"] + ] + if _dict["translations"] is not None: + _dict["translations"] = [ + Translation.from_dict(translations) + for translations in _dict["translations"] + ] + if _dict["topics"] is not None: + _dict["topics"] = [Topics.from_dict(topics) for topics in _dict["topics"]] + return _dict[key] + + +@dataclass_json +@dataclass +class Channel: + search: Optional[List[Search]] = None + alternatives: Optional[List[Alternative]] = None + detected_language: Optional[str] = "" + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["search"] is not None: + _dict["search"] = [Search.from_dict(search) for search in _dict["search"]] + if _dict["alternatives"] is not None: + _dict["alternatives"] = [ + Alternative.from_dict(alternatives) + for alternatives in _dict["alternatives"] + ] + return _dict[key] + + +@dataclass_json +@dataclass +class Result: + channels: Optional[List[Channel]] = None + utterances: Optional[List[Utterance]] = None + summary: Optional[Summary] = None + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["channels"] is not None: + _dict["channels"] = [ + Channel.from_dict(channels) for channels in _dict["channels"] + ] + if _dict["utterances"] is not None: + _dict["utterances"] = [ + Utterance.from_dict(utterances) for utterances in _dict["utterances"] + ] + if _dict["summary"] is not None: + _dict["summary"] = Summary.from_dict(_dict["summary"]) + return _dict[key] + + +# Prerecorded Response Result: + + +@dataclass_json +@dataclass +class PrerecordedResponse: + metadata: Optional[Metadata] = None + results: Optional[Result] = None + + def __getitem__(self, key): + _dict = self.to_dict() + if _dict["metadata"] is not None: + _dict["metadata"] = Metadata.from_dict(_dict["metadata"]) + if _dict["results"] is not None: + _dict["results"] = Result.from_dict(_dict["results"]) + return _dict[key] diff --git a/deepgram/options.py b/deepgram/options.py index bd38cb5d..823aba07 100644 --- a/deepgram/options.py +++ b/deepgram/options.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 +import logging, verboselogs from typing import Dict import re @@ -24,9 +25,11 @@ def __init__( self, api_key: str = "", url: str = "", + verbose: int = logging.WARNING, headers: Dict[str, str] = None, options: Dict[str, str] = None, ): + self.verbose = verbose self.api_key = api_key if headers is None: self.headers = { diff --git a/examples/manage/balances/main.py b/examples/manage/balances/main.py index 25a1b8b3..5476e869 100644 --- a/examples/manage/balances/main.py +++ b/examples/manage/balances/main.py @@ -6,8 +6,9 @@ import os import sys from dotenv import load_dotenv +import logging, verboselogs -from deepgram import DeepgramClient +from deepgram import DeepgramClient, DeepgramClientOptions load_dotenv() @@ -15,7 +16,11 @@ API_KEY = os.getenv("DG_API_KEY") # Create a Deepgram client using the API key -deepgram: DeepgramClient = DeepgramClient(API_KEY) +config: DeepgramClientOptions = DeepgramClientOptions( + verbose=logging.SPAM, +) + +deepgram: DeepgramClient = DeepgramClient(API_KEY, config) async def main(): diff --git a/examples/prerecorded/file/main.py b/examples/prerecorded/file/main.py index 2d6a80c4..9fb85cb3 100644 --- a/examples/prerecorded/file/main.py +++ b/examples/prerecorded/file/main.py @@ -5,22 +5,33 @@ import asyncio import os from dotenv import load_dotenv +import logging, verboselogs -from deepgram import DeepgramClient, PrerecordedOptions, FileSource +from deepgram import ( + DeepgramClientOptions, + DeepgramClient, + PrerecordedOptions, + FileSource, +) load_dotenv() API_KEY = os.getenv("DG_API_KEY") AUDIO_FILE = "preamble.wav" -options: PrerecordedOptions = { - "model": "nova", - "smart_format": "true", - "summarize": "v2", -} +# Create a Deepgram client using the API key +config: DeepgramClientOptions = DeepgramClientOptions( + verbose=logging.SPAM, +) + +options: PrerecordedOptions = PrerecordedOptions( + model="nova", + smart_format="true", + summarize="v2", +) # STEP 1 Create a Deepgram client using the API key (optional - add config options) -deepgram = DeepgramClient(API_KEY) +deepgram: DeepgramClient = DeepgramClient(API_KEY, config) # STEP 2 Call the transcribe_file method on the prerecorded class @@ -43,6 +54,10 @@ async def main(): try: response = await transcribe_file() print(response) + print("") + json = response.to_json() + print("") + print(json) except Exception as e: print(f"Exception: {e}") diff --git a/requirements.txt b/requirements.txt index 2faff566..a7bc0665 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ dataclasses-json dataclasses typing_extensions python-dotenv +verboselogs # streaming libs pyaudio diff --git a/setup.py b/setup.py index 6b697158..3257789e 100644 --- a/setup.py +++ b/setup.py @@ -32,6 +32,7 @@ "python-dotenv", "asyncio", "aiohttp", + "verboselogs", ], keywords=["deepgram", "deepgram speech-to-text"], classifiers=[