From 3474a88884694c8cd955f45fcefd15e4373c20fa Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Thu, 10 Apr 2025 15:37:18 -0700 Subject: [PATCH 1/4] feat: support short-lived tokens endpoint --- deepgram/client.py | 30 ++++++++++++++ deepgram/clients/__init__.py | 6 +++ deepgram/clients/auth/__init__.py | 9 +++++ deepgram/clients/auth/client.py | 11 +++++ deepgram/clients/auth/v1/__init__.py | 8 ++++ deepgram/clients/auth/v1/async_client.py | 51 ++++++++++++++++++++++++ deepgram/clients/auth/v1/client.py | 51 ++++++++++++++++++++++++ deepgram/clients/auth/v1/response.py | 24 +++++++++++ examples/auth/async_token/main.py | 40 +++++++++++++++++++ examples/auth/token/main.py | 39 ++++++++++++++++++ 10 files changed, 269 insertions(+) create mode 100644 deepgram/clients/auth/__init__.py create mode 100644 deepgram/clients/auth/client.py create mode 100644 deepgram/clients/auth/v1/__init__.py create mode 100644 deepgram/clients/auth/v1/async_client.py create mode 100644 deepgram/clients/auth/v1/client.py create mode 100644 deepgram/clients/auth/v1/response.py create mode 100644 examples/auth/async_token/main.py create mode 100644 examples/auth/token/main.py diff --git a/deepgram/client.py b/deepgram/client.py index 2cd0c02f..5c3e973e 100644 --- a/deepgram/client.py +++ b/deepgram/client.py @@ -248,6 +248,14 @@ # ErrorResponse, ) +# auth client classes +from .clients import AuthRESTClient, AsyncAuthRESTClient + +# auth client responses +from .clients import ( + GrantTokenResponse, +) + # manage client classes/input from .clients import ManageClient, AsyncManageClient from .clients import ( @@ -495,6 +503,20 @@ def asyncmanage(self): """ return self.Version(self._config, "asyncmanage") + @property + def auth(self): + """ + Returns an AuthRESTClient instance for managing short-lived tokens. + """ + return self.Version(self._config, "auth") + + @property + def asyncauth(self): + """ + Returns an AsyncAuthRESTClient instance for managing short-lived tokens. + """ + return self.Version(self._config, "asyncauth") + @property @deprecation.deprecated( deprecated_in="3.4.0", @@ -606,6 +628,14 @@ def v(self, version: str = ""): parent = "selfhosted" filename = "async_client" classname = "AsyncSelfHostedClient" + case "auth": + parent = "auth" + filename = "client" + classname = "AuthRESTClient" + case "asyncauth": + parent = "auth" + filename = "async_client" + classname = "AsyncAuthRESTClient" case _: self._logger.error("parent unknown: %s", self._parent) self._logger.debug("Version.v LEAVE") diff --git a/deepgram/clients/__init__.py b/deepgram/clients/__init__.py index 7b62062d..75f46213 100644 --- a/deepgram/clients/__init__.py +++ b/deepgram/clients/__init__.py @@ -312,6 +312,12 @@ Balance, ) +# auth +from .auth import AuthRESTClient, AsyncAuthRESTClient +from .auth import ( + GrantTokenResponse, +) + # selfhosted from .selfhosted import ( OnPremClient, diff --git a/deepgram/clients/auth/__init__.py b/deepgram/clients/auth/__init__.py new file mode 100644 index 00000000..d5ac419c --- /dev/null +++ b/deepgram/clients/auth/__init__.py @@ -0,0 +1,9 @@ +# Copyright 2023-2024 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 .client import AuthRESTClient +from .client import AsyncAuthRESTClient +from .client import ( + GrantTokenResponse, +) \ No newline at end of file diff --git a/deepgram/clients/auth/client.py b/deepgram/clients/auth/client.py new file mode 100644 index 00000000..2e74f243 --- /dev/null +++ b/deepgram/clients/auth/client.py @@ -0,0 +1,11 @@ +# Copyright 2023-2024 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.client import AuthRESTClient as AuthRESTClientLatest +from .v1.async_client import AsyncAuthRESTClient as AsyncAuthRESTClientLatest +from .v1.response import GrantTokenResponse as GrantTokenResponseLatest + +AuthRESTClient = AuthRESTClientLatest +AsyncAuthRESTClient = AsyncAuthRESTClientLatest +GrantTokenResponse = GrantTokenResponseLatest diff --git a/deepgram/clients/auth/v1/__init__.py b/deepgram/clients/auth/v1/__init__.py new file mode 100644 index 00000000..90751974 --- /dev/null +++ b/deepgram/clients/auth/v1/__init__.py @@ -0,0 +1,8 @@ +# Copyright 2024 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 .client import AuthRESTClient +from .async_client import AsyncAuthRESTClient +from .client import ( + GrantTokenResponse, +) \ No newline at end of file diff --git a/deepgram/clients/auth/v1/async_client.py b/deepgram/clients/auth/v1/async_client.py new file mode 100644 index 00000000..00df1d71 --- /dev/null +++ b/deepgram/clients/auth/v1/async_client.py @@ -0,0 +1,51 @@ +# Copyright 2024 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 logging + +from ....utils import verboselogs +from ....options import DeepgramClientOptions +from ...common import AbstractAsyncRestClient +from .response import GrantTokenResponse + + +class AsyncAuthRESTClient(AbstractAsyncRestClient): + """ + A client class for handling pre-recorded audio data. + Provides methods for transcribing audio from URLs and files. + """ + + _logger: verboselogs.VerboseLogger + _config: DeepgramClientOptions + _endpoint: str + + def __init__(self, config: DeepgramClientOptions): + self._logger = verboselogs.VerboseLogger(__name__) + self._logger.addHandler(logging.StreamHandler()) + self._logger.setLevel(config.verbose) + self._config = config + self._endpoint = "v1/auth/grant" + super().__init__(config) + + async def grant_token(self): + """ + Generates a temporary JWT with a 30 second TTL. + + Returns: + GrantTokenResponse: An object containing the transcription result. + + Raises: + DeepgramTypeError: Raised for known API errors. + """ + self._logger.debug("AuthRestClient.grant_token ENTER") + + url = f"{self._config.url}/{self._endpoint}" + self._logger.info("url: %s", url) + result = await self.post(url, headers={"auth": self._config.api_key}) + self._logger.info("json: %s", result) + res = GrantTokenResponse.from_json(result) + self._logger.verbose("result: %s", res) + self._logger.notice("grant_token succeeded") + self._logger.debug("AuthRestClient.grant_token LEAVE") + return res diff --git a/deepgram/clients/auth/v1/client.py b/deepgram/clients/auth/v1/client.py new file mode 100644 index 00000000..db27c096 --- /dev/null +++ b/deepgram/clients/auth/v1/client.py @@ -0,0 +1,51 @@ +# Copyright 2024 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 logging + +from ....utils import verboselogs +from ....options import DeepgramClientOptions +from ...common import AbstractSyncRestClient +from .response import GrantTokenResponse + + +class AuthRESTClient(AbstractSyncRestClient): + """ + A client class for handling pre-recorded audio data. + Provides methods for transcribing audio from URLs and files. + """ + + _logger: verboselogs.VerboseLogger + _config: DeepgramClientOptions + _endpoint: str + + def __init__(self, config: DeepgramClientOptions): + self._logger = verboselogs.VerboseLogger(__name__) + self._logger.addHandler(logging.StreamHandler()) + self._logger.setLevel(config.verbose) + self._config = config + self._endpoint = "v1/auth/grant" + super().__init__(config) + + def grant_token(self): + """ + Generates a temporary JWT with a 30 second TTL. + + Returns: + GrantTokenResponse: An object containing the transcription result. + + Raises: + DeepgramTypeError: Raised for known API errors. + """ + self._logger.debug("AuthRestClient.grant_token ENTER") + + url = f"{self._config.url}/{self._endpoint}" + self._logger.info("url: %s", url) + result = self.post(url, headers={"auth": self._config.api_key}) + self._logger.info("json: %s", result) + res = GrantTokenResponse.from_json(result) + self._logger.verbose("result: %s", res) + self._logger.notice("grant_token succeeded") + self._logger.debug("AuthRestClient.grant_token LEAVE") + return res diff --git a/deepgram/clients/auth/v1/response.py b/deepgram/clients/auth/v1/response.py new file mode 100644 index 00000000..3a457d93 --- /dev/null +++ b/deepgram/clients/auth/v1/response.py @@ -0,0 +1,24 @@ +# Copyright 2023-2024 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 dataclasses import dataclass, field +from dataclasses_json import config as dataclass_config + +from ...common import ( + BaseResponse, +) + +@dataclass +class GrantTokenResponse(BaseResponse): + """ + The response object for the authentication grant token endpoint. + """ + access_token: str = field( + metadata=dataclass_config(field_name='access_token'), + default=None, + ) + expires_in: int = field( + metadata=dataclass_config(field_name='expires_in'), + default=30, + ) \ No newline at end of file diff --git a/examples/auth/async_token/main.py b/examples/auth/async_token/main.py new file mode 100644 index 00000000..5fbf2b82 --- /dev/null +++ b/examples/auth/async_token/main.py @@ -0,0 +1,40 @@ +# Copyright 2023-2024 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 +import logging +from deepgram.utils import verboselogs + +from deepgram import ( + DeepgramClient, + PrerecordedOptions, + DeepgramClientOptions +) + +load_dotenv() + +AUDIO_URL = { + "url": "https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav" +} + + +async def main(): + try: + # STEP 1 Create a Deepgram client using the DEEPGRAM_API_KEY from your environment variables + config = DeepgramClientOptions( + verbose=verboselogs.SPAM, + ) + deepgram: DeepgramClient = DeepgramClient("", config) + + # STEP 2 Call the grant_token method on the auth rest class + response = await deepgram.asyncauth.v("1").grant_token() + print(f"response: {response}\n\n") + except Exception as e: + print(f"Exception: {e}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/auth/token/main.py b/examples/auth/token/main.py new file mode 100644 index 00000000..975464a0 --- /dev/null +++ b/examples/auth/token/main.py @@ -0,0 +1,39 @@ +# Copyright 2023-2024 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 os +from dotenv import load_dotenv +import logging +from deepgram.utils import verboselogs + +from deepgram import ( + DeepgramClient, + PrerecordedOptions, + DeepgramClientOptions +) + +load_dotenv() + +AUDIO_URL = { + "url": "https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav" +} + + +def main(): + try: + # STEP 1 Create a Deepgram client using the DEEPGRAM_API_KEY from your environment variables + config = DeepgramClientOptions( + verbose=verboselogs.SPAM, + ) + deepgram: DeepgramClient = DeepgramClient("", config) + + # STEP 2 Call the grant_token method on the auth rest class + response = deepgram.auth.v("1").grant_token() + print(f"response: {response}\n\n") + except Exception as e: + print(f"Exception: {e}") + + +if __name__ == "__main__": + main() From 72e61a2cd3b55b1fd10ee6a8aa9374f0987decef Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Thu, 10 Apr 2025 15:39:21 -0700 Subject: [PATCH 2/4] fix: default value --- deepgram/clients/auth/v1/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepgram/clients/auth/v1/response.py b/deepgram/clients/auth/v1/response.py index 3a457d93..cf72f357 100644 --- a/deepgram/clients/auth/v1/response.py +++ b/deepgram/clients/auth/v1/response.py @@ -16,7 +16,7 @@ class GrantTokenResponse(BaseResponse): """ access_token: str = field( metadata=dataclass_config(field_name='access_token'), - default=None, + default="", ) expires_in: int = field( metadata=dataclass_config(field_name='expires_in'), From eb0363830dd63ceb4450aaa26138b890aa496a0f Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Thu, 10 Apr 2025 15:55:19 -0700 Subject: [PATCH 3/4] fix: coderabbit gave some good tips this time --- deepgram/clients/auth/v1/__init__.py | 2 +- deepgram/clients/auth/v1/async_client.py | 8 ++++---- deepgram/clients/auth/v1/client.py | 8 ++++---- examples/auth/async_token/main.py | 2 +- examples/auth/token/main.py | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/deepgram/clients/auth/v1/__init__.py b/deepgram/clients/auth/v1/__init__.py index 90751974..f449c80a 100644 --- a/deepgram/clients/auth/v1/__init__.py +++ b/deepgram/clients/auth/v1/__init__.py @@ -3,6 +3,6 @@ # SPDX-License-Identifier: MIT from .client import AuthRESTClient from .async_client import AsyncAuthRESTClient -from .client import ( +from .response import ( GrantTokenResponse, ) \ No newline at end of file diff --git a/deepgram/clients/auth/v1/async_client.py b/deepgram/clients/auth/v1/async_client.py index 00df1d71..43ecb490 100644 --- a/deepgram/clients/auth/v1/async_client.py +++ b/deepgram/clients/auth/v1/async_client.py @@ -12,8 +12,8 @@ class AsyncAuthRESTClient(AbstractAsyncRestClient): """ - A client class for handling pre-recorded audio data. - Provides methods for transcribing audio from URLs and files. + A client class for handling authentication endpoints. + Provides method for generating a temporary JWT token. """ _logger: verboselogs.VerboseLogger @@ -33,7 +33,7 @@ async def grant_token(self): Generates a temporary JWT with a 30 second TTL. Returns: - GrantTokenResponse: An object containing the transcription result. + GrantTokenResponse: An object containing the authentication token and its expiration time. Raises: DeepgramTypeError: Raised for known API errors. @@ -42,7 +42,7 @@ async def grant_token(self): url = f"{self._config.url}/{self._endpoint}" self._logger.info("url: %s", url) - result = await self.post(url, headers={"auth": self._config.api_key}) + result = await self.post(url, headers={"Authorization": f"Token {self._config.api_key}"}) self._logger.info("json: %s", result) res = GrantTokenResponse.from_json(result) self._logger.verbose("result: %s", res) diff --git a/deepgram/clients/auth/v1/client.py b/deepgram/clients/auth/v1/client.py index db27c096..c75815da 100644 --- a/deepgram/clients/auth/v1/client.py +++ b/deepgram/clients/auth/v1/client.py @@ -12,8 +12,8 @@ class AuthRESTClient(AbstractSyncRestClient): """ - A client class for handling pre-recorded audio data. - Provides methods for transcribing audio from URLs and files. + A client class for handling authentication endpoints. + Provides method for generating a temporary JWT token. """ _logger: verboselogs.VerboseLogger @@ -33,7 +33,7 @@ def grant_token(self): Generates a temporary JWT with a 30 second TTL. Returns: - GrantTokenResponse: An object containing the transcription result. + GrantTokenResponse: An object containing the authentication token and its expiration time. Raises: DeepgramTypeError: Raised for known API errors. @@ -42,7 +42,7 @@ def grant_token(self): url = f"{self._config.url}/{self._endpoint}" self._logger.info("url: %s", url) - result = self.post(url, headers={"auth": self._config.api_key}) + result = self.post(url, headers={"Authorization": f"Token {self._config.api_key}"}) self._logger.info("json: %s", result) res = GrantTokenResponse.from_json(result) self._logger.verbose("result: %s", res) diff --git a/examples/auth/async_token/main.py b/examples/auth/async_token/main.py index 5fbf2b82..b85a6ddd 100644 --- a/examples/auth/async_token/main.py +++ b/examples/auth/async_token/main.py @@ -27,7 +27,7 @@ async def main(): config = DeepgramClientOptions( verbose=verboselogs.SPAM, ) - deepgram: DeepgramClient = DeepgramClient("", config) + deepgram: DeepgramClient = DeepgramClient(os.getenv("DEEPGRAM_API_KEY", ""), config) # STEP 2 Call the grant_token method on the auth rest class response = await deepgram.asyncauth.v("1").grant_token() diff --git a/examples/auth/token/main.py b/examples/auth/token/main.py index 975464a0..e9c89a54 100644 --- a/examples/auth/token/main.py +++ b/examples/auth/token/main.py @@ -26,7 +26,7 @@ def main(): config = DeepgramClientOptions( verbose=verboselogs.SPAM, ) - deepgram: DeepgramClient = DeepgramClient("", config) + deepgram: DeepgramClient = DeepgramClient(os.getenv("DEEPGRAM_API_KEY", ""), config) # STEP 2 Call the grant_token method on the auth rest class response = deepgram.auth.v("1").grant_token() From ffbf3563538eb46b05a95ae103e49408dd4c0053 Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Fri, 11 Apr 2025 15:59:31 -0700 Subject: [PATCH 4/4] chore: clean up examples --- examples/auth/async_token/main.py | 7 ------- examples/auth/token/main.py | 7 ------- 2 files changed, 14 deletions(-) diff --git a/examples/auth/async_token/main.py b/examples/auth/async_token/main.py index b85a6ddd..9107f596 100644 --- a/examples/auth/async_token/main.py +++ b/examples/auth/async_token/main.py @@ -5,22 +5,15 @@ import asyncio import os from dotenv import load_dotenv -import logging from deepgram.utils import verboselogs from deepgram import ( DeepgramClient, - PrerecordedOptions, DeepgramClientOptions ) load_dotenv() -AUDIO_URL = { - "url": "https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav" -} - - async def main(): try: # STEP 1 Create a Deepgram client using the DEEPGRAM_API_KEY from your environment variables diff --git a/examples/auth/token/main.py b/examples/auth/token/main.py index e9c89a54..cb56cd4a 100644 --- a/examples/auth/token/main.py +++ b/examples/auth/token/main.py @@ -4,22 +4,15 @@ import os from dotenv import load_dotenv -import logging from deepgram.utils import verboselogs from deepgram import ( DeepgramClient, - PrerecordedOptions, DeepgramClientOptions ) load_dotenv() -AUDIO_URL = { - "url": "https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav" -} - - def main(): try: # STEP 1 Create a Deepgram client using the DEEPGRAM_API_KEY from your environment variables