Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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