Skip to content

Commit

Permalink
Restructure Python v3 (Part 7) - Support API Versioning
Browse files Browse the repository at this point in the history
Restructure Pyhon v3 (Part 3)

Restructure Python v3 (Part 5) - Rename files

Restructure Python v3 (Part 7) - Support API Versioning
  • Loading branch information
dvonthenen committed Nov 16, 2023
1 parent 93bb0d5 commit 71cbc9a
Show file tree
Hide file tree
Showing 17 changed files with 568 additions and 422 deletions.
6 changes: 3 additions & 3 deletions deepgram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
from .errors import DeepgramError, DeepgramApiError, DeepgramUnknownApiError, DeepgramUnknownError

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

# prerecorded
from .clients.prerecorded.options import PrerecordedOptions
from .clients.prerecorded.client import PreRecordedClient, PrerecordedOptions

# manage
from .clients.manage.client import ManageClient
from .clients.manage.client import ManageClient, ProjectOptions, KeyOptions, ScopeOptions, InviteOptions, UsageRequestOptions, UsageSummaryOptions, UsageFieldsOptions
14 changes: 12 additions & 2 deletions deepgram/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
from .errors import DeepgramError

from .clients.listen import ListenClient
from .clients.manage.client import ManageClient
from .clients.onprem.client import OnPremClient
from .clients.manage.client import ManageClient # FUTURE VERSIONINING:, ManageClientV1
from .clients.onprem.client import OnPremClient # FUTURE VERSIONINING: , OnPremClientV1

class DeepgramClient:
"""
Expand Down Expand Up @@ -67,6 +67,16 @@ def listen(self):
def manage(self):
return ManageClient(self.url, self.headers)

# FUTURE VERSIONINING:
# @property
# def manage_v1(self):
# return ManageClientV1(self.url, self.headers)

@property
def onprem(self):
return OnPremClient(self.url, self.headers)

# FUTURE VERSIONINING:
# @property
# def onprem_v1(self):
# return OnPremClientV1(self.url, self.headers)
1 change: 0 additions & 1 deletion deepgram/clients/abstract_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ async 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.json()
except httpx._exceptions.HTTPError as e:
Expand Down
16 changes: 13 additions & 3 deletions deepgram/clients/listen.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +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

from .prerecorded.client import PreRecordedClient
from .live.client import LiveClient
from .prerecorded.client import PreRecordedClient # FUTURE VERSIONINING:, PreRecordedClientV1
from .live.client import LiveClient # FUTURE VERSIONINING:, LiveClientV1
from typing import Dict, Any, Optional


Expand All @@ -16,7 +16,17 @@ def __init__(self, url: str, api_key: str, headers: Optional[Dict[str, Any]]):
@property
def prerecorded(self):
return PreRecordedClient(self.url, self.headers)


# FUTURE VERSIONINING:
# @property
# def prerecorded_v1(self):
# return PreRecordedClientV1(self.url, self.headers)

@property
def live(self):
return LiveClient(self.url, self.api_key, self.headers)

# FUTURE VERSIONINING:
# @property
# def live_v1(self):
# return LiveClientV1(self.url, self.api_key, self.headers)
108 changes: 12 additions & 96 deletions deepgram/clients/live/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,104 +2,20 @@
# Use of this source code is governed by a MIT license that can be found in the LICENSE file.
# SPDX-License-Identifier: MIT

from .enums import LiveTranscriptionEvents
from .helpers import convert_to_websocket_url, append_query_params
from .v1_client import LiveClientV1
from .v1_options import LiveOptionsV1

from deepgram.errors import DeepgramApiError
'''
The client.py points to the current supported version in the SDK.
Older versions are supported in the SDK for backwards compatibility.
'''
class LiveOptions(LiveOptionsV1):
pass

import asyncio
import json
import websockets

class LiveClient:
class LiveClient(LiveClientV1):
"""
Client for interacting with Deepgram's live transcription services over WebSockets.
This class provides methods to establish a WebSocket connection for live transcription and handle real-time transcription events.
Args:
base_url (str): The base URL for WebSocket connection.
api_key (str): The Deepgram API key used for authentication.
headers (dict): Additional HTTP headers for WebSocket connection.
Attributes:
endpoint (str): The API endpoint for live transcription.
_socket (websockets.WebSocketClientProtocol): The WebSocket connection object.
_event_handlers (dict): Dictionary of event handlers for specific events.
websocket_url (str): The WebSocket URL used for connection.
Methods:
__call__: Establishes a WebSocket connection for live transcription.
on: Registers event handlers for specific events.
send: Sends data over the WebSocket connection.
finish: Closes the WebSocket connection gracefully.
Please see LiveClientV1 for details
"""
def __init__(self, base_url, api_key, headers):
self.base_url = base_url
self.api_key = api_key
self.headers = headers
self.endpoint = "v1/listen"
self._socket = None
self._event_handlers = { event: [] for event in LiveTranscriptionEvents }
self.websocket_url = convert_to_websocket_url(base_url, self.endpoint)

async def __call__(self, options=None):
url_with_params = append_query_params(self.websocket_url, options)
try:
self._socket = await _socket_connect(url_with_params, self.headers)
asyncio.create_task(self._start())
return self
except websockets.ConnectionClosed as e:
await self._emit(LiveTranscriptionEvents.Close, e.code)


def on(self, event, handler): # registers event handlers for specific events
if event in LiveTranscriptionEvents and callable(handler):
self._event_handlers[event].append(handler)

async def _emit(self, event, *args, **kwargs): # triggers the registered event handlers for a specific event
for handler in self._event_handlers[event]:
handler(*args, **kwargs)

async def _start(self) -> None:
async for message in self._socket:
try:
data = json.loads(message)
response_type = data.get("type")
if response_type == LiveTranscriptionEvents.Transcript.value:
await self._emit(LiveTranscriptionEvents.Transcript, data)
if "metadata" in data:
await self._emit(LiveTranscriptionEvents.Metadata, data["metadata"])
except json.JSONDecodeError as e:
await self._emit(LiveTranscriptionEvents.Error, e.code)

async def send(self, data):
if self._socket:
await self._socket.send(data)

async def finish(self):
if self._socket:
await self._socket.send(json.dumps({"type": "CloseStream"}))
# await self._socket.send("") # Send a zero-byte message
await self._socket.wait_closed()

async def _socket_connect(websocket_url, headers):
destination = websocket_url
updated_headers = headers

async def attempt():
try:
return await websockets.connect(
destination, extra_headers=updated_headers, ping_interval=5
)
except websockets.exceptions.InvalidHandshake as exc:
raise DeepgramApiError(exc, http_library_error=exc) from exc

# tries = 4
# while tries > 0:
# try:
# return await attempt()
# except Exception as exc:
# tries -= 1
# continue
return await attempt()
super().__init__(base_url, api_key, headers)

105 changes: 105 additions & 0 deletions deepgram/clients/live/v1_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# 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

from .enums import LiveTranscriptionEvents
from .helpers import convert_to_websocket_url, append_query_params

from deepgram.errors import DeepgramApiError

import asyncio
import json
import websockets

class LiveClientV1:
"""
Client for interacting with Deepgram's live transcription services over WebSockets.
This class provides methods to establish a WebSocket connection for live transcription and handle real-time transcription events.
Args:
base_url (str): The base URL for WebSocket connection.
api_key (str): The Deepgram API key used for authentication.
headers (dict): Additional HTTP headers for WebSocket connection.
Attributes:
endpoint (str): The API endpoint for live transcription.
_socket (websockets.WebSocketClientProtocol): The WebSocket connection object.
_event_handlers (dict): Dictionary of event handlers for specific events.
websocket_url (str): The WebSocket URL used for connection.
Methods:
__call__: Establishes a WebSocket connection for live transcription.
on: Registers event handlers for specific events.
send: Sends data over the WebSocket connection.
finish: Closes the WebSocket connection gracefully.
"""
def __init__(self, base_url, api_key, headers):
self.base_url = base_url
self.api_key = api_key
self.headers = headers
self.endpoint = "v1/listen"
self._socket = None
self._event_handlers = { event: [] for event in LiveTranscriptionEvents }
self.websocket_url = convert_to_websocket_url(base_url, self.endpoint)

async def __call__(self, options=None):
url_with_params = append_query_params(self.websocket_url, options)
try:
self._socket = await _socket_connect(url_with_params, self.headers)
asyncio.create_task(self._start())
return self
except websockets.ConnectionClosed as e:
await self._emit(LiveTranscriptionEvents.Close, e.code)


def on(self, event, handler): # registers event handlers for specific events
if event in LiveTranscriptionEvents and callable(handler):
self._event_handlers[event].append(handler)

async def _emit(self, event, *args, **kwargs): # triggers the registered event handlers for a specific event
for handler in self._event_handlers[event]:
handler(*args, **kwargs)

async def _start(self) -> None:
async for message in self._socket:
try:
data = json.loads(message)
response_type = data.get("type")
if response_type == LiveTranscriptionEvents.Transcript.value:
await self._emit(LiveTranscriptionEvents.Transcript, data)
if "metadata" in data:
await self._emit(LiveTranscriptionEvents.Metadata, data["metadata"])
except json.JSONDecodeError as e:
await self._emit(LiveTranscriptionEvents.Error, e.code)

async def send(self, data):
if self._socket:
await self._socket.send(data)

async def finish(self):
if self._socket:
await self._socket.send(json.dumps({"type": "CloseStream"}))
# await self._socket.send("") # Send a zero-byte message
await self._socket.wait_closed()

async def _socket_connect(websocket_url, headers):
destination = websocket_url
updated_headers = headers

async def attempt():
try:
return await websockets.connect(
destination, extra_headers=updated_headers, ping_interval=5
)
except websockets.exceptions.InvalidHandshake as exc:
raise DeepgramApiError(exc, http_library_error=exc) from exc

# tries = 4
# while tries > 0:
# try:
# return await attempt()
# except Exception as exc:
# tries -= 1
# continue
return await attempt()
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from typing import Union, List, TypedDict

class LiveOptions(TypedDict, total=False):
class LiveOptionsV1(TypedDict, total=False):
callback: str
channels: int
diarize: bool
Expand Down
Loading

0 comments on commit 71cbc9a

Please sign in to comment.