Skip to content

Commit

Permalink
Merge pull request #200 from dvonthenen/handle-sync-async-prerecorded
Browse files Browse the repository at this point in the history
Implement Sync (Default) and Async For All Functions/Classes
  • Loading branch information
davidvonthenen authored Dec 7, 2023
2 parents 3fbf383 + d330f61 commit 94e9e7c
Show file tree
Hide file tree
Showing 31 changed files with 1,355 additions and 219 deletions.
10 changes: 9 additions & 1 deletion deepgram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@

# live
from .clients.live.enums import LiveTranscriptionEvents
from .clients.live.client import LiveClient, LegacyLiveClient, LiveOptions
from .clients.live.client import LiveClient, AsyncLiveClient, LiveOptions

# onprem
from .clients.onprem.client import (
OnPremClient,
AsyncOnPremClient,
)

# prerecorded
from .clients.prerecorded.client import (
PreRecordedClient,
AsyncPreRecordedClient,
PrerecordedOptions,
PrerecordedSource,
FileSource,
Expand All @@ -25,6 +32,7 @@
# manage
from .clients.manage.client import (
ManageClient,
AsyncManageClient,
ProjectOptions,
KeyOptions,
ScopeOptions,
Expand Down
39 changes: 34 additions & 5 deletions deepgram/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@
import logging, verboselogs
import os

from .clients.live.client import LiveOptions
from .clients.prerecorded.client import PrerecordedOptions
from .clients.listen import ListenClient, PreRecordedClient
from .clients.manage.client import ManageClient
from .clients.listen import (
ListenClient,
PreRecordedClient,
AsyncLiveClient,
AsyncPreRecordedClient,
PrerecordedOptions,
LiveOptions,
)
from .clients.onprem.client import OnPremClient
from .clients.onprem.v1.async_client import AsyncOnPremClient
from .clients.manage.client import ManageClient
from .clients.manage.v1.async_client import AsyncManageClient

from .options import DeepgramClientOptions
from .errors import DeepgramApiKeyError, DeepgramModuleError
Expand Down Expand Up @@ -66,10 +73,18 @@ def listen(self):
def manage(self):
return self.Version(self.config, "manage")

@property
def asyncmanage(self):
return self.Version(self.config, "asyncmanage")

@property
def onprem(self):
return self.Version(self.config, "onprem")

@property
def asynconprem(self):
return self.Version(self.config, "asynconprem")

# INTERNAL CLASSES
class Version:
def __init__(self, config, parent: str):
Expand Down Expand Up @@ -99,19 +114,33 @@ def v(self, version: str = ""):
self.logger.debug("Version.v LEAVE")
raise DeepgramModuleError("Invalid module version")

parent = ""
fileName = ""
className = ""
match self.parent:
case "manage":
parent = "manage"
fileName = "client"
className = "ManageClient"
case "asyncmanage":
parent = "manage"
fileName = "async_client"
className = "AsyncManageClient"
case "onprem":
parent = "onprem"
fileName = "client"
className = "OnPremClient"
case "asynconprem":
parent = "onprem"
fileName = "async_client"
className = "AsyncOnPremClient"
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"
path = f"deepgram.clients.{parent}.v{version}.{fileName}"
self.logger.info("path: %s", path)
self.logger.info("className: %s", className)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .errors import DeepgramError, DeepgramApiError, DeepgramUnknownApiError


class AbstractRestfulClient:
class AbstractAsyncRestClient:
"""
An abstract base class for a RESTful HTTP client.
Expand Down
83 changes: 83 additions & 0 deletions deepgram/clients/abstract_sync_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Copyright 2023 Deepgram SDK contributors. All Rights Reserved.
# Use of this source code is governed by a MIT license that can be found in the LICENSE file.
# SPDX-License-Identifier: MIT

import httpx
import json

from ..options import DeepgramClientOptions
from .errors import DeepgramError, DeepgramApiError, DeepgramUnknownApiError


class AbstractSyncRestClient:
"""
An abstract base class for a RESTful HTTP client.
This class provides common HTTP methods (GET, POST, PUT, PATCH, DELETE) for making asynchronous HTTP requests.
It handles error responses and provides basic JSON parsing.
Args:
url (Dict[str, str]): The base URL for the RESTful API, including any path segments.
headers (Optional[Dict[str, Any]]): Optional HTTP headers to include in requests.
Attributes:
url (Dict[str, str]): The base URL for the RESTful API.
client (httpx.AsyncClient): An asynchronous HTTP client for making requests.
headers (Optional[Dict[str, Any]]): Optional HTTP headers to include in requests.
Exceptions:
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")

self.config = config

def get(self, url: str, options=None):
return self._handle_request(
"GET", url, params=options, headers=self.config.headers
)

def post(self, url: str, options=None, **kwargs):
return self._handle_request(
"POST", url, params=options, headers=self.config.headers, **kwargs
)

def put(self, url: str, options=None, **kwargs):
return self._handle_request(
"PUT", url, params=options, headers=self.config.headers, **kwargs
)

def patch(self, url: str, options=None, **kwargs):
return self._handle_request(
"PATCH", url, params=options, headers=self.config.headers, **kwargs
)

def delete(self, url: str):
return self._handle_request("DELETE", url, headers=self.config.headers)

def _handle_request(self, method, url, **kwargs):
try:
with httpx.Client() as client:
response = client.request(method, url, **kwargs)
response.raise_for_status()
return response.text
except httpx._exceptions.HTTPError as e:
if isinstance(e, httpx.HTTPStatusError):
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
except json.decoder.JSONDecodeError:
raise DeepgramUnknownApiError(e.response.text, status_code) from e
except ValueError as e:
raise DeepgramUnknownApiError(e.response.text, status_code) from e
else:
raise
except Exception as e:
raise
35 changes: 30 additions & 5 deletions deepgram/clients/listen.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,37 @@

from ..options import DeepgramClientOptions

from .prerecorded.client import PreRecordedClient
from .live.client import LiveClient, LegacyLiveClient
from .prerecorded.client import (
PreRecordedClient,
AsyncPreRecordedClient,
PrerecordedOptions,
)
from .live.client import LiveClient, AsyncLiveClient, LiveOptions
from .errors import DeepgramModuleError


class ListenClient:
def __init__(self, config: DeepgramClientOptions):
self.logger = logging.getLogger(__name__)
self.logger.addHandler(logging.StreamHandler())
self.logger.setLevel(config.verbose)
self.config = config

@property
def prerecorded(self):
return self.Version(self.config, "prerecorded")

@property
def asyncprerecorded(self):
return self.Version(self.config, "asyncprerecorded")

@property
def live(self):
return self.Version(self.config, "live")

@property
def legacylive(self):
return LegacyLiveClient(self.config)
def asynclive(self):
return self.Version(self.config, "asynclive")

# INTERNAL CLASSES
class Version:
Expand Down Expand Up @@ -57,19 +68,33 @@ def v(self, version: str = ""):
self.logger.debug("Version.v LEAVE")
raise DeepgramModuleError("Invalid module version")

parent = ""
fileName = ""
className = ""
match self.parent:
case "live":
parent = "live"
fileName = "client"
className = "LiveClient"
case "asynclive":
parent = "live"
fileName = "async_client"
className = "AsyncLiveClient"
case "prerecorded":
parent = "prerecorded"
fileName = "client"
className = "PreRecordedClient"
case "asyncprerecorded":
parent = "prerecorded"
fileName = "async_client"
className = "AsyncPreRecordedClient"
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"
path = f"deepgram.clients.{parent}.v{version}.{fileName}"
self.logger.info("path: %s", path)
self.logger.info("className: %s", className)

Expand Down
4 changes: 2 additions & 2 deletions deepgram/clients/live/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: MIT

from .v1.client import LiveClient as LiveClientLatest
from .v1.legacy_client import LegacyLiveClient as LegacyLiveClientLatest
from .v1.async_client import AsyncLiveClient as AsyncLiveClientLatest
from .v1.options import LiveOptions as LiveOptionsLatest

"""
Expand All @@ -25,7 +25,7 @@ def __init__(self, config):
super().__init__(config)


class LegacyLiveClient(LegacyLiveClientLatest):
class AsyncLiveClient(AsyncLiveClientLatest):
"""
Please see LiveClientLatest for details
"""
Expand Down
3 changes: 0 additions & 3 deletions deepgram/clients/live/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
# SPDX-License-Identifier: MIT

from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
from typing import Dict

from .errors import DeepgramError


def append_query_params(url, params):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from .options import LiveOptions


class LegacyLiveClient:
class AsyncLiveClient:
"""
Client for interacting with Deepgram's live transcription services over WebSockets.
Expand Down Expand Up @@ -51,7 +51,7 @@ 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")
self.logger.debug("AsyncLiveClient.__call__ ENTER")
self.logger.info("options: %s", options)

self.options = options
Expand All @@ -65,12 +65,12 @@ async def __call__(self, options: LiveOptions = None):
asyncio.create_task(self._start())

self.logger.notice("__call__ succeeded")
self.logger.debug("LegacyLiveClient.__call__ LEAVE")
self.logger.debug("AsyncLiveClient.__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")
self.logger.debug("AsyncLiveClient.__call__ LEAVE")

def on(self, event, handler): # registers event handlers for specific events
if event in LiveTranscriptionEvents and callable(handler):
Expand All @@ -83,7 +83,7 @@ async def _emit(
handler(*args, **kwargs)

async def _start(self) -> None:
self.logger.debug("LegacyLiveClient._start ENTER")
self.logger.debug("AsyncLiveClient._start ENTER")

async for message in self._socket:
try:
Expand Down Expand Up @@ -113,20 +113,20 @@ async def _start(self) -> None:
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")
self.logger.debug("AsyncLiveClient._start LEAVE")

async def send(self, data):
self.logger.spam("LegacyLiveClient.send ENTER")
self.logger.spam("AsyncLiveClient.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")
self.logger.spam("AsyncLiveClient.send LEAVE")

async def finish(self):
self.logger.debug("LegacyLiveClient.finish LEAVE")
self.logger.debug("AsyncLiveClient.finish LEAVE")

if self._socket:
self.logger.notice("send CloseStream...")
Expand All @@ -136,7 +136,7 @@ async def finish(self):
self.logger.notice("socket.wait_closed succeeded")

self.logger.notice("finish succeeded")
self.logger.debug("LegacyLiveClient.finish LEAVE")
self.logger.debug("AsyncLiveClient.finish LEAVE")


async def _socket_connect(websocket_url, headers):
Expand Down
10 changes: 10 additions & 0 deletions deepgram/clients/manage/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# SPDX-License-Identifier: MIT

from .v1.client import ManageClient as ManageClientLatest
from .v1.async_client import AsyncManageClient as AsyncManageClientLatest
from .v1.options import (
ProjectOptions as ProjectOptionsLatest,
KeyOptions as KeyOptionsLatest,
Expand Down Expand Up @@ -54,3 +55,12 @@ class ManageClient(ManageClientLatest):

def __init__(self, config):
super().__init__(config)


class AsyncManageClient(AsyncManageClientLatest):
"""
Please see AsyncManageClientLatest for details
"""

def __init__(self, config):
super().__init__(config)
Loading

0 comments on commit 94e9e7c

Please sign in to comment.