From a2691584cfee8057ca7f88134f101e9c7bd583f7 Mon Sep 17 00:00:00 2001 From: Luke Oliff Date: Tue, 17 Feb 2026 16:48:46 +0000 Subject: [PATCH 1/3] refactor(sagemaker): move SageMaker transport to separate deepgram-sagemaker package The SageMaker transport's AWS dependencies require Python >=3.12, but the main SDK supports Python >=3.8. Extract it into a standalone deepgram-sagemaker package so the SDK remains compatible with older Python versions. - Delete src/deepgram/transports/sagemaker.py - Remove mypy overrides for AWS SDK modules from pyproject.toml - Update README Custom Transports section with deepgram-sagemaker example - Update example imports to use deepgram_sagemaker - Remove unused imports (JSONDecodeError, websockets) from socket clients - Update .fernignore comments to reflect changes --- .fernignore | 8 +- README.md | 29 +- examples/27-transcription-live-sagemaker.py | 4 +- pyproject.toml | 8 - src/deepgram/agent/v1/socket_client.py | 2 - src/deepgram/listen/v1/socket_client.py | 2 - src/deepgram/listen/v2/socket_client.py | 2 - src/deepgram/speak/v1/socket_client.py | 2 - src/deepgram/transports/__init__.py | 13 +- src/deepgram/transports/sagemaker.py | 363 -------------------- 10 files changed, 28 insertions(+), 405 deletions(-) delete mode 100644 src/deepgram/transports/sagemaker.py diff --git a/.fernignore b/.fernignore index 27f509c6..d8dd04aa 100644 --- a/.fernignore +++ b/.fernignore @@ -11,8 +11,10 @@ wiremock/wiremock-mappings.json # Wire test with manual fix: transcribe_file() requires request=bytes parameter tests/wire/test_listen_v1_media.py -# WebSocket socket clients: optional message parameter defaults for send_flush, -# send_close, send_clear, send_finalize, send_close_stream, send_keep_alive +# WebSocket socket clients: +# - Optional message parameter defaults for send_flush, send_close, send_clear, +# send_finalize, send_close_stream, send_keep_alive +# - Removed unused imports (JSONDecodeError, websockets) flagged by ruff F401 src/deepgram/speak/v1/socket_client.py src/deepgram/listen/v1/socket_client.py src/deepgram/listen/v2/socket_client.py @@ -41,7 +43,7 @@ src/deepgram/helpers # - transport_interface.py: Protocol definitions (SyncTransport, AsyncTransport) for # users implementing custom transports. This is the public-facing interface file. # - transport.py: Internal shims, install/restore helpers, and conflict guard. -# - transports/: Concrete transport implementations (e.g. SageMaker). +# - transports/: Module stub (SageMaker transport moved to separate deepgram-sagemaker package). # All are manually maintained and should not be regenerated. src/deepgram/transport_interface.py src/deepgram/transport.py diff --git a/README.md b/README.md index b5319599..126ee681 100644 --- a/README.md +++ b/README.md @@ -335,11 +335,15 @@ client = DeepgramClient( ) ``` -### Custom WebSocket Transport +### Custom Transports Replace the built-in `websockets` transport with your own implementation for WebSocket-based APIs (Listen, Speak, Agent). This enables alternative protocols (HTTP/2, SSE), test doubles, or proxied connections. -Implement the `SyncTransport` or `AsyncTransport` protocol from `deepgram.transport_interface` and pass your class as `transport_factory`: +Any class that implements the right methods can be used as a transport — no inheritance required. Pass your class (or a factory callable) as `transport_factory` when creating a client. + +#### Sync transports + +Implement `send()`, `recv()`, `__iter__()`, and `close()`, then pass the class to `DeepgramClient`: ```python from deepgram import DeepgramClient @@ -361,7 +365,9 @@ with client.listen.v1.connect(model="nova-3") as connection: connection.start_listening() ``` -For async transports, implement `async def send()`, `async def recv()`, `async def __aiter__()`, and `async def close()`, then use `AsyncDeepgramClient`: +#### Async transports + +Implement `async def send()`, `async def recv()`, `async def __aiter__()`, and `async def close()`, then use `AsyncDeepgramClient`: ```python from deepgram import AsyncDeepgramClient @@ -373,15 +379,19 @@ async with client.listen.v1.connect(model="nova-3") as connection: await connection.start_listening() ``` -See `src/deepgram/transport_interface.py` for the full protocol definitions. +See `src/deepgram/transport_interface.py` for the full protocol definitions (`SyncTransport` and `AsyncTransport`). -### SageMaker Transport +#### SageMaker transport -The SDK includes a built-in transport for running Deepgram models on [AWS SageMaker](https://aws.amazon.com/sagemaker/) endpoints. It uses HTTP/2 bidirectional streaming under the hood, but exposes the same SDK interface — just swap in a `transport_factory`: +The [`deepgram-sagemaker`](https://github.com/deepgram/deepgram-python-sdk-transport-sagemaker) package is a ready-made async transport for running Deepgram models on [AWS SageMaker](https://aws.amazon.com/sagemaker/) endpoints. It uses HTTP/2 bidirectional streaming under the hood, but exposes the same SDK interface — just install the package and swap in a `transport_factory`: + +```bash +pip install deepgram-sagemaker # requires Python 3.12+ +``` ```python from deepgram import AsyncDeepgramClient -from deepgram.transports.sagemaker import SageMakerTransportFactory +from deepgram_sagemaker import SageMakerTransportFactory factory = SageMakerTransportFactory( endpoint_name="my-deepgram-endpoint", @@ -398,11 +408,6 @@ async with client.listen.v1.connect(model="nova-3") as connection: > **Note:** The SageMaker transport is async-only and requires `AsyncDeepgramClient`. -Install the SageMaker transport dependencies (requires Python 3.12+): -```bash -pip install aws-sdk-sagemaker-runtime-http2 boto3 -``` - See [`examples/27-transcription-live-sagemaker.py`](./examples/27-transcription-live-sagemaker.py) for a complete working example. ### Retry Configuration diff --git a/examples/27-transcription-live-sagemaker.py b/examples/27-transcription-live-sagemaker.py index f307217b..03f06b02 100644 --- a/examples/27-transcription-live-sagemaker.py +++ b/examples/27-transcription-live-sagemaker.py @@ -13,7 +13,7 @@ Requirements:: - pip install aws-sdk-sagemaker-runtime-http2 boto3 + pip install deepgram-sagemaker Environment: AWS credentials must be configured (via environment variables, @@ -41,7 +41,7 @@ ListenV1SpeechStarted, ListenV1UtteranceEnd, ) -from deepgram.transports.sagemaker import SageMakerTransportFactory +from deepgram_sagemaker import SageMakerTransportFactory ListenV1SocketClientResponse = Union[ListenV1Results, ListenV1Metadata, ListenV1UtteranceEnd, ListenV1SpeechStarted] diff --git a/pyproject.toml b/pyproject.toml index 41b00ec2..52568feb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,14 +64,6 @@ asyncio_mode = "auto" [tool.mypy] plugins = ["pydantic.mypy"] -[[tool.mypy.overrides]] -module = [ - "aws_sdk_sagemaker_runtime_http2.*", - "smithy_aws_core.*", - "boto3.*", -] -ignore_missing_imports = true - [tool.ruff] line-length = 120 diff --git a/src/deepgram/agent/v1/socket_client.py b/src/deepgram/agent/v1/socket_client.py index 48130c9a..56dd49fe 100644 --- a/src/deepgram/agent/v1/socket_client.py +++ b/src/deepgram/agent/v1/socket_client.py @@ -2,9 +2,7 @@ import json import typing -from json.decoder import JSONDecodeError -import websockets import websockets.sync.connection as websockets_sync_connection from ...core.events import EventEmitterMixin, EventType from ...core.pydantic_utilities import parse_obj_as diff --git a/src/deepgram/listen/v1/socket_client.py b/src/deepgram/listen/v1/socket_client.py index 15f0ef9d..ce948284 100644 --- a/src/deepgram/listen/v1/socket_client.py +++ b/src/deepgram/listen/v1/socket_client.py @@ -2,9 +2,7 @@ import json import typing -from json.decoder import JSONDecodeError -import websockets import websockets.sync.connection as websockets_sync_connection from ...core.events import EventEmitterMixin, EventType from ...core.pydantic_utilities import parse_obj_as diff --git a/src/deepgram/listen/v2/socket_client.py b/src/deepgram/listen/v2/socket_client.py index 3daf61c4..09935ff8 100644 --- a/src/deepgram/listen/v2/socket_client.py +++ b/src/deepgram/listen/v2/socket_client.py @@ -2,9 +2,7 @@ import json import typing -from json.decoder import JSONDecodeError -import websockets import websockets.sync.connection as websockets_sync_connection from ...core.events import EventEmitterMixin, EventType from ...core.pydantic_utilities import parse_obj_as diff --git a/src/deepgram/speak/v1/socket_client.py b/src/deepgram/speak/v1/socket_client.py index bc242e0a..e04280bf 100644 --- a/src/deepgram/speak/v1/socket_client.py +++ b/src/deepgram/speak/v1/socket_client.py @@ -2,9 +2,7 @@ import json import typing -from json.decoder import JSONDecodeError -import websockets import websockets.sync.connection as websockets_sync_connection from ...core.events import EventEmitterMixin, EventType from ...core.pydantic_utilities import parse_obj_as diff --git a/src/deepgram/transports/__init__.py b/src/deepgram/transports/__init__.py index bbc36e3f..b983141d 100644 --- a/src/deepgram/transports/__init__.py +++ b/src/deepgram/transports/__init__.py @@ -1,14 +1,9 @@ """Concrete transport implementations for the Deepgram Python SDK. -Each module provides a transport factory that can be passed to -``AsyncDeepgramClient(transport_factory=...)`` or -``DeepgramClient(transport_factory=...)``. +Custom transports can be passed to ``AsyncDeepgramClient(transport_factory=...)`` +or ``DeepgramClient(transport_factory=...)``. -Available transports: - -- :mod:`deepgram.transports.sagemaker` — AWS SageMaker HTTP/2 BiDi streaming (async-only) +See ``deepgram.transport_interface`` for the protocol definitions. """ -from .sagemaker import SageMakerTransport, SageMakerTransportFactory - -__all__ = ["SageMakerTransport", "SageMakerTransportFactory"] +__all__: list = [] diff --git a/src/deepgram/transports/sagemaker.py b/src/deepgram/transports/sagemaker.py deleted file mode 100644 index 1f511a8f..00000000 --- a/src/deepgram/transports/sagemaker.py +++ /dev/null @@ -1,363 +0,0 @@ -""" -SageMaker transport for the Deepgram Python SDK. - -Uses AWS SageMaker's HTTP/2 bidirectional streaming API as an alternative to -WebSocket, allowing transparent switching between Deepgram Cloud and Deepgram -on SageMaker. - -**Async-only** — the underlying ``sagemaker-runtime-http2`` library is entirely -async, so this transport implements ``AsyncTransport`` and must be used with -``AsyncDeepgramClient``. It cannot be used with the sync ``DeepgramClient``. - -Requirements:: - - pip install aws-sdk-sagemaker-runtime-http2 boto3 - -Usage:: - - from deepgram import AsyncDeepgramClient - from deepgram.transports.sagemaker import SageMakerTransportFactory - - factory = SageMakerTransportFactory( - endpoint_name="my-deepgram-endpoint", - region="us-west-2", - ) - client = AsyncDeepgramClient( - api_key="unused", # SageMaker uses AWS credentials, not API keys - transport_factory=factory, - ) - - async with client.listen.v1.connect(model="nova-3") as connection: - connection.on(EventType.MESSAGE, handler) - await connection.start_listening() -""" - -import asyncio -import json -import logging -import os -from typing import Any -from urllib.parse import urlparse - -from ..transport_interface import AsyncTransport - - -def _import_sagemaker_deps() -> tuple: - """Lazily import SageMaker dependencies. - - These are optional — only needed when actually using the SageMaker - transport. Deferring them avoids mypy/import errors for users who - don't have ``aws-sdk-sagemaker-runtime-http2`` installed. - - Returns - ------- - tuple - (SageMakerRuntimeHTTP2Client, Config, HTTPAuthSchemeResolver, - InvokeEndpointWithBidirectionalStreamInput, RequestPayloadPart, - RequestStreamEventPayloadPart, SigV4AuthScheme, - EnvironmentCredentialsResolver) - - Raises - ------ - ImportError - If the required packages are not installed. - """ - try: - from aws_sdk_sagemaker_runtime_http2.client import SageMakerRuntimeHTTP2Client - from aws_sdk_sagemaker_runtime_http2.config import Config, HTTPAuthSchemeResolver - from aws_sdk_sagemaker_runtime_http2.models import ( - InvokeEndpointWithBidirectionalStreamInput, - RequestPayloadPart, - RequestStreamEventPayloadPart, - ) - from smithy_aws_core.auth.sigv4 import SigV4AuthScheme - from smithy_aws_core.identity import EnvironmentCredentialsResolver - except ImportError: - raise ImportError( - "SageMaker transport requires additional dependencies. " - "Install them with: pip install aws-sdk-sagemaker-runtime-http2 boto3" - ) from None - - return ( - SageMakerRuntimeHTTP2Client, - Config, - HTTPAuthSchemeResolver, - InvokeEndpointWithBidirectionalStreamInput, - RequestPayloadPart, - RequestStreamEventPayloadPart, - SigV4AuthScheme, - EnvironmentCredentialsResolver, - ) - -logger = logging.getLogger(__name__) - - -class SageMakerTransport(AsyncTransport): - """SageMaker BiDi streaming transport implementing ``AsyncTransport``. - - Connection is established lazily on the first ``send()`` or ``recv()`` call, - since the transport is constructed synchronously by the SDK's shim layer. - - Parameters - ---------- - endpoint_name : str - Name of the SageMaker endpoint running Deepgram. - region : str - AWS region where the endpoint is deployed. - invocation_path : str - Model invocation path (e.g. ``"v1/listen"``). Extracted from the - WebSocket URL by :class:`SageMakerTransportFactory`. - query_string : str - URL-encoded query parameters (e.g. ``"model=nova-3&interim_results=true"``). - Extracted from the WebSocket URL by :class:`SageMakerTransportFactory`. - """ - - def __init__( - self, - endpoint_name: str, - region: str, - invocation_path: str, - query_string: str, - ) -> None: - self.endpoint_name = endpoint_name - self.region = region - self.invocation_path = invocation_path - self.query_string = query_string - self._stream: Any = None - self._output_stream: Any = None - self._connected = False - self._closed = False - self._connect_lock: Any = None # created lazily (needs a running loop) - - self._setup_credentials() - - def _setup_credentials(self) -> None: - """Resolve AWS credentials via boto3 (if available) into env vars. - - The ``EnvironmentCredentialsResolver`` used by the SageMaker HTTP/2 - client reads ``AWS_ACCESS_KEY_ID``, ``AWS_SECRET_ACCESS_KEY``, and - optionally ``AWS_SESSION_TOKEN`` from the environment. This method - uses boto3's credential chain (env vars, shared credentials file, - IAM role, etc.) and writes the resolved values back to the - environment so the resolver can find them. - """ - try: - import boto3 - except ImportError: - return - try: - session = boto3.Session(region_name=self.region) - creds = session.get_credentials() - if creds is None: - return - frozen = creds.get_frozen_credentials() - os.environ["AWS_ACCESS_KEY_ID"] = frozen.access_key - os.environ["AWS_SECRET_ACCESS_KEY"] = frozen.secret_key - if frozen.token: - os.environ["AWS_SESSION_TOKEN"] = frozen.token - except Exception as exc: - logger.debug("Could not load boto3 credentials: %s", exc) - - async def _ensure_connected(self) -> None: - """Lazily establish the SageMaker BiDi stream on first use.""" - if self._connected: - return - - # Guard against concurrent callers (start_listening + send_media race) - if self._connect_lock is None: - self._connect_lock = asyncio.Lock() - - async with self._connect_lock: - if self._connected: - return - await self._do_connect() - - async def _do_connect(self) -> None: - """Establish the SageMaker BiDi stream (called under lock).""" - - logger.info("Connecting to SageMaker endpoint: %s in %s", self.endpoint_name, self.region) - - ( - SageMakerRuntimeHTTP2Client, - Config, - HTTPAuthSchemeResolver, - InvokeEndpointWithBidirectionalStreamInput, - _RequestPayloadPart, - _RequestStreamEventPayloadPart, - SigV4AuthScheme, - EnvironmentCredentialsResolver, - ) = _import_sagemaker_deps() - - config = Config( - endpoint_uri=f"https://runtime.sagemaker.{self.region}.amazonaws.com:8443", - region=self.region, - aws_credentials_identity_resolver=EnvironmentCredentialsResolver(), - auth_scheme_resolver=HTTPAuthSchemeResolver(), - auth_schemes={"aws.auth#sigv4": SigV4AuthScheme(service="sagemaker")}, - ) - client = SageMakerRuntimeHTTP2Client(config=config) - - stream_input = InvokeEndpointWithBidirectionalStreamInput( - endpoint_name=self.endpoint_name, - model_invocation_path=self.invocation_path, - model_query_string=self.query_string, - ) - - self._stream = await client.invoke_endpoint_with_bidirectional_stream(stream_input) - - # Await the output stream before sending any data (critical ordering) - output = await asyncio.wait_for(self._stream.await_output(), timeout=10.0) - self._output_stream = output[1] - self._connected = True - - logger.info("Connected to SageMaker endpoint: %s", self.endpoint_name) - - async def send(self, data: Any) -> None: - """Send text, bytes, or dict data to SageMaker. - - The SDK calls this with: - - ``bytes`` for audio media (from ``send_media``) - - ``str`` for JSON control messages (from ``_send_model`` after ``json.dumps``) - """ - await self._ensure_connected() - - if isinstance(data, (bytes, bytearray)): - raw = bytes(data) - elif isinstance(data, str): - raw = data.encode("utf-8") - elif isinstance(data, dict): - raw = json.dumps(data).encode("utf-8") - else: - raw = str(data).encode("utf-8") - - from aws_sdk_sagemaker_runtime_http2.models import ( - RequestPayloadPart, - RequestStreamEventPayloadPart, - ) - - payload = RequestPayloadPart(bytes_=raw) - event = RequestStreamEventPayloadPart(value=payload) - await self._stream.input_stream.send(event) - - async def recv(self) -> Any: - """Receive the next message from SageMaker. - - Returns a decoded UTF-8 string when possible (so the SDK can - JSON-parse it), or raw bytes for binary data. Returns ``None`` - when the stream ends. - """ - await self._ensure_connected() - - result = await self._output_stream.receive() - if result is None: - return None - - if result.value and result.value.bytes_: - raw = result.value.bytes_ - try: - return raw.decode("utf-8") - except UnicodeDecodeError: - return raw - - return None - - async def __aiter__(self): - """Async-iterate over messages until the stream ends. - - The SDK's ``start_listening()`` drives the event loop by calling - ``async for raw_message in self._websocket:``, which invokes this - method on the transport. - """ - while not self._closed: - msg = await self.recv() - if msg is None: - break - yield msg - - async def close(self) -> None: - """Close the SageMaker stream and release resources.""" - if self._closed: - return - self._closed = True - if self._stream: - try: - await self._stream.input_stream.close() - except Exception: - pass - logger.info("Closed SageMaker connection: %s", self.endpoint_name) - - -class SageMakerTransportFactory: - """Factory callable for ``AsyncDeepgramClient(transport_factory=...)``. - - **Async-only** — must be used with ``AsyncDeepgramClient``, not the sync - ``DeepgramClient``. Passing this factory to ``DeepgramClient`` will raise - a ``TypeError``. - - When the SDK calls ``factory(url, headers)``, this extracts the invocation - path and query string from the URL and creates a :class:`SageMakerTransport`. - - The URL is the WebSocket URL the SDK would normally connect to, e.g.:: - - wss://api.deepgram.com/v1/listen?model=nova-3&interim_results=true - - From this, the factory extracts: - - ``invocation_path`` = ``"v1/listen"`` - - ``query_string`` = ``"model=nova-3&interim_results=true"`` - - Parameters - ---------- - endpoint_name : str - Name of the SageMaker endpoint running Deepgram. - region : str - AWS region (default ``"us-west-2"``). - - Example - ------- - :: - - from deepgram import AsyncDeepgramClient - from deepgram.transports.sagemaker import SageMakerTransportFactory - - factory = SageMakerTransportFactory("my-endpoint", region="us-east-1") - client = AsyncDeepgramClient(api_key="unused", transport_factory=factory) - - async with client.listen.v1.connect(model="nova-3") as connection: - connection.on(EventType.MESSAGE, handler) - await connection.start_listening() - """ - - _SYNC_ERROR = ( - "SageMakerTransportFactory is async-only and cannot be used with the " - "sync DeepgramClient. Use AsyncDeepgramClient instead." - ) - - def __init__(self, endpoint_name: str, region: str = "us-west-2") -> None: - self.endpoint_name = endpoint_name - self.region = region - - def __call__(self, url: str, headers: dict) -> SageMakerTransport: - """Create a transport instance from the SDK-provided WebSocket URL. - - Raises - ------ - TypeError - If called outside an async context (i.e. from the sync - ``DeepgramClient``), since SageMaker streaming is async-only. - """ - try: - asyncio.get_running_loop() - except RuntimeError: - raise TypeError(self._SYNC_ERROR) from None - - parsed = urlparse(url) - # Strip leading slash — SageMaker expects "v1/listen" not "/v1/listen" - invocation_path = parsed.path.lstrip("/") - query_string = parsed.query # already URL-encoded - - return SageMakerTransport( - endpoint_name=self.endpoint_name, - region=self.region, - invocation_path=invocation_path, - query_string=query_string, - ) From 6e7c5fac71535ea37a4f905018d1f0f47118295c Mon Sep 17 00:00:00 2001 From: Luke Oliff Date: Tue, 17 Feb 2026 18:31:33 +0000 Subject: [PATCH 2/3] chore: remove changelog notify workflow --- .github/workflows/changelog-log.yml | 39 ----------------------------- 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/changelog-log.yml diff --git a/.github/workflows/changelog-log.yml b/.github/workflows/changelog-log.yml deleted file mode 100644 index f442b975..00000000 --- a/.github/workflows/changelog-log.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Changelog Log - -on: - push: - branches: [main] - paths: - - "**/CHANGELOG*.md" - - "**/changelog*.md" - -jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Send changelog to webhook - uses: lukeocodes/changelog-log@changelog-log-v0.1.7 - with: - webhook_url: ${{ secrets.CHANGELOG_WEBHOOK_URL }} - webhook_headers_json: '{"Content-Type":"application/json","X-DX-Logs-Key":"${{ secrets.CHANGELOG_SECRET }}"}' - extra_body_json: '{"route":"changelog-python"}' - # Project context is automatically inferred from GitHub context - # You can override with: - # project_name: "my-custom-project-name" - # project_owner: "my-org" - # repository_url: "https://github.com/owner/repo" - # include_github_context: "true" # includes ref, workflow, actor info - # - # Other optional overrides: - # file_globs: "CHANGELOG.md,**/CHANGELOG*.md" - # entry_separator_regex: "^##\\s+.*$" - # http_method: "POST" - # include_body_raw: "false" - # log_level: "warn" # trace, debug, info, warn, error, fatal - # extra_body_json: '{"custom":"field"}' # merge custom fields into payload - From 66bdec6057e4c5dfcee0af836d23ca06ab0772ab Mon Sep 17 00:00:00 2001 From: Luke Oliff Date: Tue, 17 Feb 2026 19:28:40 +0000 Subject: [PATCH 3/3] docs(sagemaker): update references for deepgram-sagemaker 0.2.0 on PyPI Update changelog and README to reflect that the SageMaker transport is now a separate package (deepgram-sagemaker) available on PyPI, not bundled in the SDK. --- CHANGELOG.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbc1ba82..9335f1d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Features * **helpers:** add TextBuilder class for TTS pronunciation and pause controls ([#660](https://github.com/deepgram/deepgram-python-sdk/issues/660)) ([4324120](https://github.com/deepgram/deepgram-python-sdk/commit/43241200a7e025bdc4633bdb47f6708921c82ad1)) -* **sagemaker:** add SageMaker transport for running Deepgram on AWS SageMaker endpoints ([#659](https://github.com/deepgram/deepgram-python-sdk/issues/659)) ([2046175](https://github.com/deepgram/deepgram-python-sdk/commit/204617538339b1958e2fe562dc94c8887de94a5d)) +* **sagemaker:** add SageMaker transport support via the separate [`deepgram-sagemaker`](https://pypi.org/project/deepgram-sagemaker/) package (`pip install deepgram-sagemaker`) ([#659](https://github.com/deepgram/deepgram-python-sdk/issues/659)) * v6 — fully generated SDK with latest APIs and WebSocket support ([#640](https://github.com/deepgram/deepgram-python-sdk/issues/640)) ([bc918fe](https://github.com/deepgram/deepgram-python-sdk/commit/bc918fe23e92eefb5e4c24cbfaad369d4e2818f3)) * **websockets:** add custom WebSocket transport support ([#658](https://github.com/deepgram/deepgram-python-sdk/issues/658)) ([f6cf0fb](https://github.com/deepgram/deepgram-python-sdk/commit/f6cf0fbc9aaaa844e475e014560cc377819ec1f9)) diff --git a/README.md b/README.md index 126ee681..5e325d51 100644 --- a/README.md +++ b/README.md @@ -383,7 +383,7 @@ See `src/deepgram/transport_interface.py` for the full protocol definitions (`Sy #### SageMaker transport -The [`deepgram-sagemaker`](https://github.com/deepgram/deepgram-python-sdk-transport-sagemaker) package is a ready-made async transport for running Deepgram models on [AWS SageMaker](https://aws.amazon.com/sagemaker/) endpoints. It uses HTTP/2 bidirectional streaming under the hood, but exposes the same SDK interface — just install the package and swap in a `transport_factory`: +The [`deepgram-sagemaker`](https://pypi.org/project/deepgram-sagemaker/) package ([source](https://github.com/deepgram/deepgram-python-sdk-transport-sagemaker)) is a ready-made async transport for running Deepgram models on [AWS SageMaker](https://aws.amazon.com/sagemaker/) endpoints. It uses HTTP/2 bidirectional streaming under the hood, but exposes the same SDK interface — just install the package and swap in a `transport_factory`: ```bash pip install deepgram-sagemaker # requires Python 3.12+