diff --git a/deepgram/__init__.py b/deepgram/__init__.py index 68c1e450..ba09e452 100644 --- a/deepgram/__init__.py +++ b/deepgram/__init__.py @@ -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 diff --git a/deepgram/client.py b/deepgram/client.py index e2829eba..d6e4f32d 100644 --- a/deepgram/client.py +++ b/deepgram/client.py @@ -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: """ @@ -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) diff --git a/deepgram/clients/abstract_client.py b/deepgram/clients/abstract_client.py index f76ea344..3b21e169 100644 --- a/deepgram/clients/abstract_client.py +++ b/deepgram/clients/abstract_client.py @@ -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: diff --git a/deepgram/clients/listen.py b/deepgram/clients/listen.py index 4912dcc7..4d91c771 100644 --- a/deepgram/clients/listen.py +++ b/deepgram/clients/listen.py @@ -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 @@ -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) diff --git a/deepgram/clients/live/client.py b/deepgram/clients/live/client.py index d9c4bf98..ca1026bb 100644 --- a/deepgram/clients/live/client.py +++ b/deepgram/clients/live/client.py @@ -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() \ No newline at end of file + super().__init__(base_url, api_key, headers) + \ No newline at end of file diff --git a/deepgram/clients/live/v1_client.py b/deepgram/clients/live/v1_client.py new file mode 100644 index 00000000..d93c85ff --- /dev/null +++ b/deepgram/clients/live/v1_client.py @@ -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() \ No newline at end of file diff --git a/deepgram/clients/live/options.py b/deepgram/clients/live/v1_options.py similarity index 93% rename from deepgram/clients/live/options.py rename to deepgram/clients/live/v1_options.py index b761c81e..0fa5eefc 100644 --- a/deepgram/clients/live/options.py +++ b/deepgram/clients/live/v1_options.py @@ -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 diff --git a/deepgram/clients/manage/client.py b/deepgram/clients/manage/client.py index 05d35b13..0d28cce8 100644 --- a/deepgram/clients/manage/client.py +++ b/deepgram/clients/manage/client.py @@ -2,126 +2,37 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT -from .response import Project, ProjectsResponse, Message, ProjectOptions, KeysResponse, Key, KeyOptions, CreateKeyResponse, MembersResponse, ScopesResponse, ScopeOptions, InvitesResponse, InviteOptions, UsageRequestsResponse, UsageRequestOptions, UsageRequest, UsageSummaryOptions, UsageSummaryResponse, UsageFieldsResponse, UsageFieldsOptions, BalancesResponse, Balance +from .v1_client import ManageClientV1 +from .v1_response import ProjectOptionsV1, KeyOptionsV1, ScopeOptionsV1, InviteOptionsV1, UsageRequestOptionsV1, UsageSummaryOptionsV1, UsageFieldsOptionsV1 -from ..abstract_client import AbstractRestfulClient +''' +The client.py points to the current supported version in the SDK. +Older versions are supported in the SDK for backwards compatibility. +''' +class ProjectOptions(ProjectOptionsV1): + pass -class ManageClient(AbstractRestfulClient): - """ - A client for managing Deepgram projects and associated resources via the Deepgram API. +class KeyOptions(KeyOptionsV1): + pass + +class ScopeOptions(ScopeOptionsV1): + pass - This class provides methods for performing various operations on Deepgram projects, including: - - Retrieving project details - - Updating project settings - - Managing project members and scopes - - Handling project invitations - - Monitoring project usage and balances +class InviteOptions(InviteOptionsV1): + pass - Args: - url (str): The base URL of the Deepgram API. - headers (dict): Optional HTTP headers to include in requests. +class UsageRequestOptions(UsageRequestOptionsV1): + pass - Attributes: - url (str): The base URL of the Deepgram API. - headers (dict): Optional HTTP headers to include in requests. - endpoint (str): The API endpoint for managing projects. +class UsageSummaryOptions(UsageSummaryOptionsV1): + pass - Raises: - DeepgramApiError: Raised for known API errors. - DeepgramUnknownApiError: Raised for unknown API errors. - DeepgramUnknownError: Raised for unexpected errors not specific to the API. - Exception: For any other unexpected exceptions. +class UsageFieldsOptions(UsageFieldsOptionsV1): + pass + +class ManageClient(ManageClientV1): + """ + Please see ManageClientV1 for details """ def __init__(self, url, headers): - self.url = url - self.headers = headers - self.endpoint = "v1/projects" super().__init__(url, headers) - - async def get_projects(self) -> ProjectsResponse: - url = f"{self.url}/{self.endpoint}" - return await self.get(url) - - async def get_project(self, project_id: str) -> Project: - url = f"{self.url}/{self.endpoint}/{project_id}" - return await self.get(url) - - async def update_project(self, project_id: str, options: ProjectOptions) -> Message: - url = f"{self.url}/{self.endpoint}/{project_id}" - return await self.patch(url, json=options) - - async def delete_project(self, project_id: str) -> None: - url = f"{self.url}/{self.endpoint}/{project_id}" - return await self.delete(url) - - async def get_project_keys(self, project_id: str) -> KeysResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/keys" - return await self.get(url) - - async def get_project_key(self, project_id: str, key_id: str) -> Key: - url = f"{self.url}/{self.endpoint}/{project_id}/keys/{key_id}" - return await self.get(url) - - async def create_project_key(self, project_id: str, options: KeyOptions) -> CreateKeyResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/keys" - return await self.post(url, json=options) - - async def delete_project_key(self, project_id: str, key_id: str) -> None: - url = f"{self.url}/{self.endpoint}/{project_id}/keys/{key_id}" - return await self.delete(url) - - async def get_project_members(self, project_id: str) -> MembersResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/members" - return await self.get(url) - - async def remove_project_member(self, project_id: str, member_id: str) -> None: - url = f"{self.url}/{self.endpoint}/{project_id}/members/{member_id}" - return await self.delete(url) - - async def get_project_member_scopes(self, project_id: str, member_id: str) -> ScopesResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" - return await self.get(url) - - async def update_project_member_scope(self, project_id: str, member_id: str, options: ScopeOptions) -> Message: - url = f"{self.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" - return await self.put(url, json=options) - - async def get_project_invites(self, project_id: str) -> InvitesResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/invites" - return await self.get(url) - - async def send_project_invite(self, project_id: str, options: InviteOptions) -> Message: - url = f"{self.url}/{self.endpoint}/{project_id}/invites" - return await self.post(url, json=options) - - async def delete_project_invite(self, project_id: str, email: str) -> Message: - url = f"{self.url}/{self.endpoint}/{project_id}/invites/{email}" - return await self.delete(url) - - async def leave_project(self, project_id: str) -> Message: - url = f"{self.url}/{self.endpoint}/{project_id}/leave" - return await self.delete(url) - - async def get_project_usage_requests(self, project_id: str, options: UsageRequestOptions) -> UsageRequestsResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/requests" - return await self.get(url, options) - - async def get_project_usage_request(self, project_id: str, request_id: str) -> UsageRequest: - url = f"{self.url}/{self.endpoint}/{project_id}/requests/{request_id}" - return await self.get(url) - - async def get_project_usage_summary(self, project_id: str, options: UsageSummaryOptions) -> UsageSummaryResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/usage" - return await self.get(url, options) - - async def get_project_usage_fields(self, project_id: str, options: UsageFieldsOptions) -> UsageFieldsResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/usage/fields" - return await self.get(url, options) - - async def get_project_balances(self, project_id: str) -> BalancesResponse: - url = f"{self.url}/{self.endpoint}/{project_id}/balances" - return await self.get(url) - - async def get_project_balance(self, project_id: str, balance_id: str) -> Balance: - url = f"{self.url}/{self.endpoint}/{project_id}/balances/{balance_id}" - return await self.get(url) diff --git a/deepgram/clients/manage/v1_client.py b/deepgram/clients/manage/v1_client.py new file mode 100644 index 00000000..f2f65897 --- /dev/null +++ b/deepgram/clients/manage/v1_client.py @@ -0,0 +1,127 @@ +# 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 .v1_response import Project, ProjectsResponse, Message, ProjectOptionsV1, KeysResponse, Key, KeyOptionsV1, CreateKeyResponse, MembersResponse, ScopesResponse, ScopeOptionsV1, InvitesResponse, InviteOptionsV1, UsageRequestsResponse, UsageRequestOptionsV1, UsageRequest, UsageSummaryOptionsV1, UsageSummaryResponse, UsageFieldsResponse, UsageFieldsOptionsV1, BalancesResponse, Balance + +from ..abstract_client import AbstractRestfulClient + +class ManageClientV1(AbstractRestfulClient): + """ + A client for managing Deepgram projects and associated resources via the Deepgram API. + + This class provides methods for performing various operations on Deepgram projects, including: + - Retrieving project details + - Updating project settings + - Managing project members and scopes + - Handling project invitations + - Monitoring project usage and balances + + Args: + url (str): The base URL of the Deepgram API. + headers (dict): Optional HTTP headers to include in requests. + + Attributes: + url (str): The base URL of the Deepgram API. + headers (dict): Optional HTTP headers to include in requests. + endpoint (str): The API endpoint for managing projects. + + Raises: + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + DeepgramUnknownError: Raised for unexpected errors not specific to the API. + Exception: For any other unexpected exceptions. + """ + def __init__(self, url, headers): + self.url = url + self.headers = headers + self.endpoint = "v1/projects" + super().__init__(url, headers) + + async def get_projects(self) -> ProjectsResponse: + url = f"{self.url}/{self.endpoint}" + return await self.get(url) + + async def get_project(self, project_id: str) -> Project: + url = f"{self.url}/{self.endpoint}/{project_id}" + return await self.get(url) + + async def update_project(self, project_id: str, options: ProjectOptionsV1) -> Message: + url = f"{self.url}/{self.endpoint}/{project_id}" + return await self.patch(url, json=options) + + async def delete_project(self, project_id: str) -> None: + url = f"{self.url}/{self.endpoint}/{project_id}" + return await self.delete(url) + + async def get_project_keys(self, project_id: str) -> KeysResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/keys" + return await self.get(url) + + async def get_project_key(self, project_id: str, key_id: str) -> Key: + url = f"{self.url}/{self.endpoint}/{project_id}/keys/{key_id}" + return await self.get(url) + + async def create_project_key(self, project_id: str, options: KeyOptionsV1) -> CreateKeyResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/keys" + return await self.post(url, json=options) + + async def delete_project_key(self, project_id: str, key_id: str) -> None: + url = f"{self.url}/{self.endpoint}/{project_id}/keys/{key_id}" + return await self.delete(url) + + async def get_project_members(self, project_id: str) -> MembersResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/members" + return await self.get(url) + + async def remove_project_member(self, project_id: str, member_id: str) -> None: + url = f"{self.url}/{self.endpoint}/{project_id}/members/{member_id}" + return await self.delete(url) + + async def get_project_member_scopes(self, project_id: str, member_id: str) -> ScopesResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" + return await self.get(url) + + async def update_project_member_scope(self, project_id: str, member_id: str, options: ScopeOptionsV1) -> Message: + url = f"{self.url}/{self.endpoint}/{project_id}/members/{member_id}/scopes" + return await self.put(url, json=options) + + async def get_project_invites(self, project_id: str) -> InvitesResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/invites" + return await self.get(url) + + async def send_project_invite(self, project_id: str, options: InviteOptionsV1) -> Message: + url = f"{self.url}/{self.endpoint}/{project_id}/invites" + return await self.post(url, json=options) + + async def delete_project_invite(self, project_id: str, email: str) -> Message: + url = f"{self.url}/{self.endpoint}/{project_id}/invites/{email}" + return await self.delete(url) + + async def leave_project(self, project_id: str) -> Message: + url = f"{self.url}/{self.endpoint}/{project_id}/leave" + return await self.delete(url) + + async def get_project_usage_requests(self, project_id: str, options: UsageRequestOptionsV1) -> UsageRequestsResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/requests" + return await self.get(url, options) + + async def get_project_usage_request(self, project_id: str, request_id: str) -> UsageRequest: + url = f"{self.url}/{self.endpoint}/{project_id}/requests/{request_id}" + return await self.get(url) + + async def get_project_usage_summary(self, project_id: str, options: UsageSummaryOptionsV1) -> UsageSummaryResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/usage" + return await self.get(url, options) + + async def get_project_usage_fields(self, project_id: str, options: UsageFieldsOptionsV1) -> UsageFieldsResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/usage/fields" + return await self.get(url, options) + + async def get_project_balances(self, project_id: str) -> BalancesResponse: + url = f"{self.url}/{self.endpoint}/{project_id}/balances" + return await self.get(url) + + async def get_project_balance(self, project_id: str, balance_id: str) -> Balance: + url = f"{self.url}/{self.endpoint}/{project_id}/balances/{balance_id}" + return await self.get(url) diff --git a/deepgram/clients/manage/response.py b/deepgram/clients/manage/v1_response.py similarity index 94% rename from deepgram/clients/manage/response.py rename to deepgram/clients/manage/v1_response.py index d7a6fd25..44d15f53 100644 --- a/deepgram/clients/manage/response.py +++ b/deepgram/clients/manage/v1_response.py @@ -17,7 +17,7 @@ class Project(TypedDict): class ProjectsResponse(TypedDict): projects: List[Project] -class ProjectOptions(TypedDict, total=False): +class ProjectOptionsV1(TypedDict, total=False): name: Optional[str] company: Optional[str] @@ -57,7 +57,7 @@ class CreateKeyResponse(TypedDict): scopes: List[str] tags: Optional[List[str]] -class KeyOptions(TypedDict): +class KeyOptionsV1(TypedDict): comment: str scopes: List[str] tags: Optional[List[str]] @@ -69,7 +69,7 @@ class KeyOptions(TypedDict): class ScopesResponse(TypedDict): scopes: List[str] -class ScopeOptions(TypedDict): +class ScopeOptionsV1(TypedDict): scope: str # Invites @@ -81,7 +81,7 @@ class Invite(TypedDict): class InvitesResponse(TypedDict): projects: List[Invite] -class InviteOptions(TypedDict): +class InviteOptionsV1(TypedDict): email: str scope: str @@ -138,13 +138,13 @@ class UsageRequestsResponse(TypedDict): limit: int requests: List[UsageRequest] -class UsageRequestOptions(TypedDict): +class UsageRequestOptionsV1(TypedDict): start: Optional[str] end: Optional[str] limit: Optional[int] status: Optional[str] -class UsageSummaryOptions(TypedDict): +class UsageSummaryOptionsV1(TypedDict): start: Optional[str] end: Optional[str] accessor: Optional[str] @@ -197,7 +197,7 @@ class UsageFieldsResponse(TypedDict): languages: List[str] features: List[str] -class UsageFieldsOptions(TypedDict): +class UsageFieldsOptionsV1(TypedDict): start: Optional[str] end: Optional[str] diff --git a/deepgram/clients/onprem/client.py b/deepgram/clients/onprem/client.py index 299af78b..1a8cdcbd 100644 --- a/deepgram/clients/onprem/client.py +++ b/deepgram/clients/onprem/client.py @@ -2,49 +2,15 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT -from ..abstract_client import AbstractRestfulClient +from .v1_client import OnPremClientV1 -class OnPremClient(AbstractRestfulClient): +''' +The client.py points to the current supported version in the SDK. +Older versions are supported in the SDK for backwards compatibility. +''' +class OnPremClient(OnPremClientV1): """ - Client for interacting with Deepgram's on-premises API. - - This class provides methods to manage and interact with on-premises projects and distribution credentials. - - Args: - url (str): The base URL for API requests. - headers (dict): Additional HTTP headers for API requests. - - Attributes: - endpoint (str): The API endpoint for on-premises projects. - - Methods: - list_onprem_credentials: Lists on-premises distribution credentials for a specific project. - get_onprem_credentials: Retrieves details of a specific on-premises distribution credential for a project. - create_onprem_credentials: Creates a new on-premises distribution credential for a project. - delete_onprem_credentials: Deletes an on-premises distribution credential for a project. - + Please see OnPremClientV1 for details """ def __init__(self, url, headers): - self.url = url - self.headers = headers - self.endpoint = "v1/projects" super().__init__(url, headers) - - async def list_onprem_credentials(self, project_id: str): - url = f"{self.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials" - return await self.get(url) - - async def get_onprem_credentials(self, project_id: str, distribution_credentials_id: str): - url = f"{self.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" - return await self.get(url) - - async def create_onprem_credentials(self, project_id: str, options): - url = f"{self.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/" - return await self.post(url,json=options) - - async def delete_onprem_credentials(self, project_id: str, distribution_credentials_id: str): - url = f"{self.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" - return await self.delete(url) - - - diff --git a/deepgram/clients/onprem/v1_client.py b/deepgram/clients/onprem/v1_client.py new file mode 100644 index 00000000..75ddef31 --- /dev/null +++ b/deepgram/clients/onprem/v1_client.py @@ -0,0 +1,50 @@ +# 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 ..abstract_client import AbstractRestfulClient + +class OnPremClientV1(AbstractRestfulClient): + """ + Client for interacting with Deepgram's on-premises API. + + This class provides methods to manage and interact with on-premises projects and distribution credentials. + + Args: + url (str): The base URL for API requests. + headers (dict): Additional HTTP headers for API requests. + + Attributes: + endpoint (str): The API endpoint for on-premises projects. + + Methods: + list_onprem_credentials: Lists on-premises distribution credentials for a specific project. + get_onprem_credentials: Retrieves details of a specific on-premises distribution credential for a project. + create_onprem_credentials: Creates a new on-premises distribution credential for a project. + delete_onprem_credentials: Deletes an on-premises distribution credential for a project. + + """ + def __init__(self, url, headers): + self.url = url + self.headers = headers + self.endpoint = "v1/projects" + super().__init__(url, headers) + + async def list_onprem_credentials(self, project_id: str): + url = f"{self.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials" + return await self.get(url) + + async def get_onprem_credentials(self, project_id: str, distribution_credentials_id: str): + url = f"{self.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" + return await self.get(url) + + async def create_onprem_credentials(self, project_id: str, options): + url = f"{self.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/" + return await self.post(url,json=options) + + async def delete_onprem_credentials(self, project_id: str, distribution_credentials_id: str): + url = f"{self.url}/{self.endpoint}/{project_id}/onprem/distribution/credentials/{distribution_credentials_id}" + return await self.delete(url) + + + diff --git a/deepgram/clients/prerecorded/client.py b/deepgram/clients/prerecorded/client.py index 938f384f..2061e444 100644 --- a/deepgram/clients/prerecorded/client.py +++ b/deepgram/clients/prerecorded/client.py @@ -2,152 +2,24 @@ # Use of this source code is governed by a MIT license that can be found in the LICENSE file. # SPDX-License-Identifier: MIT -from ...errors import DeepgramError -from ..abstract_client import AbstractRestfulClient - -from .helpers import is_buffer_source, is_readstream_source, is_url_source from .source import UrlSource, FileSource -from .options import PrerecordedOptions -from .response import AsyncPrerecordedResponse, SyncPrerecordedResponse -class PreRecordedClient(AbstractRestfulClient): +from .v1_client import PreRecordedClientV1 +from .v1_options import PrerecordedOptionsV1 + +''' +The client.py points to the current supported version in the SDK. +Older versions are supported in the SDK for backwards compatibility. +''' +class PrerecordedOptions(PrerecordedOptionsV1): + pass + +class PreRecordedClient(PreRecordedClientV1): """ - A client class for handling pre-recorded audio data. Provides methods for transcribing audio from URLs and files. + Please see PreRecordedClientV1 for details """ def __init__(self, url, headers): - """ - Initializes a new instance of the PreRecordedClient. - - Args: - url (str): The URL for API requests. - headers (dict): Headers to include in API requests. - """ self.url = url self.headers = headers super().__init__(url, headers) - - async def transcribe_url( - self, source: UrlSource, options: PrerecordedOptions = None, endpoint: str = "v1/listen" - ) -> SyncPrerecordedResponse: - """ - 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: - DeepgramError: If the "callback" option is provided for a synchronous transcription. - DeepgramError: If the source type is unknown. - DeepgramApiError: Raised for known API errors. - DeepgramUnknownApiError: Raised for unknown API errors. - DeepgramUnknownError: Raised for unexpected errors not specific to the API. - Exception: For any other unexpected exceptions. - """ - - url = f"{self.url}/{endpoint}" - if options is not None and "callback" in options: - raise DeepgramError( - "Callback cannot be provided as an option to a synchronous transcription. Use `transcribe_url_callback` instead.") - if is_url_source(source): - body = source - else: - raise DeepgramError("Unknown transcription source type") - return await self.post(url, options, json=body) - - async def transcribe_url_callback(self, source: UrlSource, callback: str, options: PrerecordedOptions = None, endpoint: str = "v1/listen") -> AsyncPrerecordedResponse: - """ - 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. - DeepgramUnknownError: Raised for unexpected errors not specific to the API. - Exception: For any other unexpected exceptions. - """ - url = f"{self.url}/{endpoint}" - if options is None: - options = {} - options['callback'] = callback - if is_url_source(source): - body = source - else: - raise DeepgramError("Unknown transcription source type") - return await self.post(url, options, json=body) - - async def transcribe_file(self, source: FileSource, options: PrerecordedOptions = None, endpoint: str = "v1/listen") -> SyncPrerecordedResponse: - """ - 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: - DeepgramError: If the "callback" option is provided for a synchronous transcription. - DeepgramError: If the source type is unknown. - DeepgramApiError: Raised for known API errors. - DeepgramUnknownApiError: Raised for unknown API errors. - DeepgramUnknownError: Raised for unexpected errors not specific to the API. - Exception: For any other unexpected exceptions. - """ - - url = f"{self.url}/{endpoint}" - if is_buffer_source(source): - body = source["buffer"] - elif is_readstream_source(source): - body = source["stream"] - else: - raise DeepgramError("Unknown transcription source type") - return await self.post(url, options, content=body) - - async def transcribe_file_callback(self, source: FileSource, callback: str, options: PrerecordedOptions = None, endpoint: str = "v1/listen") -> AsyncPrerecordedResponse: - """ - 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: - DeepgramError: If the source type is unknown. - DeepgramApiError: Raised for known API errors. - DeepgramUnknownApiError: Raised for unknown API errors. - DeepgramUnknownError: Raised for unexpected errors not specific to the API. - Exception: For any other unexpected exceptions. - """ - - url = f"{self.url}/{endpoint}" - if options is None: - options = {} - options['callback'] = callback - if is_buffer_source(source): - body = source["buffer"] - elif is_readstream_source(source): - body = source["stream"] - else: - raise DeepgramError("Unknown transcription source type") - return await self.post(url, options, content=body) diff --git a/deepgram/clients/prerecorded/v1_client.py b/deepgram/clients/prerecorded/v1_client.py new file mode 100644 index 00000000..9bf44665 --- /dev/null +++ b/deepgram/clients/prerecorded/v1_client.py @@ -0,0 +1,151 @@ +# 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 ...errors import DeepgramError +from ..abstract_client import AbstractRestfulClient + +from .helpers import is_buffer_source, is_readstream_source, is_url_source +from .source import UrlSource, FileSource +from .v1_options import PrerecordedOptionsV1 +from .v1_response import AsyncPrerecordedResponseV1, SyncPrerecordedResponseV1 + +class PreRecordedClientV1(AbstractRestfulClient): + """ + A client class for handling pre-recorded audio data. Provides methods for transcribing audio from URLs and files. + """ + def __init__(self, url, headers): + """ + Initializes a new instance of the PreRecordedClient. + + Args: + url (str): The URL for API requests. + headers (dict): Headers to include in API requests. + """ + self.url = url + self.headers = headers + super().__init__(url, headers) + + async def transcribe_url( + self, source: UrlSource, options: PrerecordedOptionsV1 = None, endpoint: str="v1/listen" + ) -> SyncPrerecordedResponseV1: + """ + 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: + DeepgramError: If the "callback" option is provided for a synchronous transcription. + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + DeepgramUnknownError: Raised for unexpected errors not specific to the API. + Exception: For any other unexpected exceptions. + """ + + url = f"{self.url}/{endpoint}" + if options is not None and "callback" in options: + raise DeepgramError("Callback cannot be provided as an option to a synchronous transcription. Use `transcribe_url_callback` instead.") + if is_url_source(source): + body = source + else: + raise DeepgramError("Unknown transcription source type") + return await self.post(url, options, json=body) + + async def transcribe_url_callback( self, source: UrlSource, callback:str, options: PrerecordedOptionsV1 = None, endpoint: str="v1/listen") -> AsyncPrerecordedResponseV1: + """ + 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: + DeepgramError: If the "callback" option is provided for a synchronous transcription. + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + DeepgramUnknownError: Raised for unexpected errors not specific to the API. + Exception: For any other unexpected exceptions. + """ + url = f"{self.url}/{endpoint}" + if options is None: + options = {} + options['callback'] = callback + if is_url_source(source): + body = source + else: + raise DeepgramError("Unknown transcription source type") + return await self.post(url, options, json=body) + + + async def transcribe_file(self, source: FileSource, options: PrerecordedOptionsV1=None, endpoint: str = "v1/listen") -> SyncPrerecordedResponseV1: + """ + 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: + DeepgramError: If the "callback" option is provided for a synchronous transcription. + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + DeepgramUnknownError: Raised for unexpected errors not specific to the API. + Exception: For any other unexpected exceptions. + """ + + url = f"{self.url}/{endpoint}" + if is_buffer_source(source): + body = source["buffer"] + elif is_readstream_source(source): + body = source["stream"] + else: + raise DeepgramError("Unknown transcription source type") + return await self.post(url, options, content=body) + + async def transcribe_file_callback(self, source: FileSource, callback:str, options: PrerecordedOptionsV1 = None, endpoint: str="v1/listen") -> AsyncPrerecordedResponseV1: + """ + 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: + DeepgramError: If the "callback" option is provided for a synchronous transcription. + DeepgramApiError: Raised for known API errors. + DeepgramUnknownApiError: Raised for unknown API errors. + DeepgramUnknownError: Raised for unexpected errors not specific to the API. + Exception: For any other unexpected exceptions. + """ + + url = f"{self.url}/{endpoint}" + if options is None: + options = {} + options['callback'] = callback + if is_buffer_source(source): + body = source["buffer"] + elif is_readstream_source(source): + body = source["stream"] + else: + raise DeepgramError("Unknown transcription source type") + return await self.post(url, options, content=body) diff --git a/deepgram/clients/prerecorded/options.py b/deepgram/clients/prerecorded/v1_options.py similarity index 93% rename from deepgram/clients/prerecorded/options.py rename to deepgram/clients/prerecorded/v1_options.py index 82e542d5..c13e78b6 100644 --- a/deepgram/clients/prerecorded/options.py +++ b/deepgram/clients/prerecorded/v1_options.py @@ -4,7 +4,7 @@ from typing import Union, List, TypedDict -class PrerecordedOptions(TypedDict, total=False): +class PrerecordedOptionsV1(TypedDict, total=False): alternatives: int callback: str detect_entities: bool diff --git a/deepgram/clients/prerecorded/response.py b/deepgram/clients/prerecorded/v1_response.py similarity index 83% rename from deepgram/clients/prerecorded/response.py rename to deepgram/clients/prerecorded/v1_response.py index dd856f4e..98335f37 100644 --- a/deepgram/clients/prerecorded/response.py +++ b/deepgram/clients/prerecorded/v1_response.py @@ -6,7 +6,7 @@ # Async Prerecorded Response Types: -class AsyncPrerecordedResponse(TypedDict): +class AsyncPrerecordedResponseV1(TypedDict): request_id: str # Sync Prerecorded Response Types: @@ -27,12 +27,12 @@ class ModelInfo(TypedDict): version: str arch: str -class Summary(TypedDict): +class SummaryV2(TypedDict): summary: Optional[str] start_word: Optional[float] end_word: Optional[float] -class TranscriptionSummary(TypedDict): +class SummaryV1(TypedDict): result: str short: str @@ -42,7 +42,7 @@ class Hit(TypedDict): end: float snippet: str -class WordBase(TypedDict): +class Word(TypedDict): word: str start: float end: float @@ -61,8 +61,9 @@ class Paragraph(TypedDict): start: float end: float num_words: float + speaker: Optional[int] -class ParagraphGroup(TypedDict): +class Paragraphs(TypedDict): transcript: str paragraphs: List[Paragraph] @@ -70,7 +71,7 @@ class Topic(TypedDict): topic: str confidence: float -class TopicGroup(TypedDict): +class Topics(TypedDict): topics: List[Topic] text: str start_word: float @@ -95,7 +96,7 @@ class Utterance(TypedDict): confidence: float channel: int transcript: str - words: List[WordBase] + words: List[Word] speaker: Optional[int] id: str @@ -109,12 +110,12 @@ class Entity(TypedDict): class Alternative(TypedDict): transcript: str confidence: float - words: List[WordBase] - summaries: Optional[List[Summary]] - paragraphs: Optional[ParagraphGroup] + words: List[Word] + summaries: Optional[List[SummaryV2]] + paragraphs: Optional[Paragraphs] entities: Optional[List[Entity]] translations: Optional[List[Translation]] - topics: Optional[List[TopicGroup]] + topics: Optional[List[Topics]] class Channel(TypedDict): search: Optional[List[Search]] @@ -124,8 +125,8 @@ class Channel(TypedDict): class Result(TypedDict): channels: List[Channel] utterances: Optional[List[Utterance]] - summary: Optional[TranscriptionSummary] + summary: Optional[SummaryV1] -class SyncPrerecordedResponse(TypedDict): +class SyncPrerecordedResponseV1(TypedDict): metadata: Metadata results: Result diff --git a/examples/demo_versioning.py b/examples/demo_versioning.py new file mode 100644 index 00000000..ee1c209d --- /dev/null +++ b/examples/demo_versioning.py @@ -0,0 +1,28 @@ +# 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 asyncio +import os +from dotenv import load_dotenv + +from deepgram import DeepgramClient +from deepgram.clients.manage.client import ManageClientV1 + +load_dotenv() + +API_KEY = os.getenv('DG_API_KEY_MANAGE') + +# Create a Deepgram client using the API key +deepgram: DeepgramClient = DeepgramClient(API_KEY) + +async def main(): + response = await deepgram.manage.get_projects() + print(response) + +# FUTURE VERSIONINING: +# response = await deepgram.manage_v1.get_projects() +# print(response) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file