feat: add declarative reconnect flag with transport-factory auto-disable by GregHolmes · Pull Request #720 · deepgram/deepgram-python-sdk · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .fernignore
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ How to identify:
- The file lives **outside `src/deepgram/`** in a hand-maintained location (e.g., `.claude/`, `docs/`)

Current permanently frozen files:
- `src/deepgram/client.py` — entirely custom (Bearer auth, session ID); no Fern equivalent
- `src/deepgram/client.py` — entirely custom (Bearer auth, session ID, `transport_factory`, `reconnect` parity flag); no Fern equivalent
- `src/deepgram/helpers/` — hand-written TextBuilder helpers
- `src/deepgram/agent/v1/types/agent_v1history_content.py`, `src/deepgram/agent/v1/types/agent_v1history_function_calls.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item_content.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item_content_role.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item_function_calls.py`, `src/deepgram/agent/v1/types/agent_v1settings_agent_context_messages_item_function_calls_function_calls_item.py` — hand-written compatibility aliases preserving old public Agent History type imports after regen renames
- `src/deepgram/agent/v1/requests/agent_v1history_content.py`, `src/deepgram/agent/v1/requests/agent_v1history_function_calls.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_messages_item.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_messages_item_content.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_messages_item_function_calls.py`, `src/deepgram/agent/v1/requests/agent_v1settings_agent_context_messages_item_function_calls_function_calls_item.py` — hand-written compatibility aliases preserving old public Agent History request-param imports after regen renames
Expand Down
34 changes: 32 additions & 2 deletions src/deepgram/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
- `transport_factory` to replace the default `websockets` transport with a custom one:
- A callable: ``factory(url, headers) -> transport`` returning an object with
``send()``, ``recv()``, iteration, and ``close()`` support.
- `reconnect` flag (default `True`) declaring whether the SDK is expected to
manage WebSocket reconnects for this client. When a custom
``transport_factory`` is set, ``reconnect`` is auto-disabled because the
custom transport owns its own retry/reconnect lifecycle; double-stacking
retries on top would cause storm-on-storm under burst load. The Python
SDK has no wrapper reconnect layer today, so this flag is declarative
only -- it documents intent and is reserved for any future SDK-side
reconnect logic.
"""

import types
Expand Down Expand Up @@ -57,6 +65,12 @@ class DeepgramClient(BaseClient):
- `transport_factory`: Custom sync WebSocket transport factory. A callable
``factory(url, headers) -> transport`` whose return value must support
``send()``, ``recv()``, iteration, and ``close()``.
- `reconnect`: Declarative flag (default ``True``) signalling whether the SDK is
expected to manage WebSocket reconnects for this client. Auto-disabled
when ``transport_factory`` is set, since the custom transport owns its
retry/reconnect lifecycle. The Python SDK has no wrapper reconnect layer
today, so this flag is declarative only -- it documents intent and is
reserved for any future SDK-side reconnect logic.
- `telemetry_opt_out`: Telemetry opt-out flag (maintained for backwards compatibility, no-op).
- `telemetry_handler`: Telemetry handler (maintained for backwards compatibility, no-op).
"""
Expand All @@ -65,6 +79,7 @@ def __init__(self, *args, **kwargs) -> None:
access_token: Optional[str] = kwargs.pop("access_token", None)
session_id: Optional[str] = kwargs.pop("session_id", None)
transport_factory: Optional[Callable] = kwargs.pop("transport_factory", None)
reconnect: bool = bool(kwargs.pop("reconnect", True))
telemetry_opt_out: bool = bool(kwargs.pop("telemetry_opt_out", True))
telemetry_handler: Optional[Any] = kwargs.pop("telemetry_handler", None)

Expand Down Expand Up @@ -95,9 +110,13 @@ def __init__(self, *args, **kwargs) -> None:
if access_token is not None:
_apply_bearer_authorization_override(self._client_wrapper, access_token)

# Install custom WebSocket transport if provided
# Install custom WebSocket transport if provided. Auto-disable
# `reconnect`: a custom transport owns its retry lifecycle, so flip
# the flag off even if the caller left it at the default.
if transport_factory is not None:
install_transport(sync_factory=transport_factory)
reconnect = False
self.reconnect = reconnect

# Store telemetry handler for backwards compatibility (no-op, telemetry not implemented)
self._telemetry_handler = None
Expand All @@ -114,6 +133,12 @@ class AsyncDeepgramClient(AsyncBaseClient):
- `transport_factory`: Custom async WebSocket transport factory. A callable
``factory(url, headers) -> transport`` whose return value must support
``send()``, ``recv()``, async iteration, and ``close()``.
- `reconnect`: Declarative flag (default ``True``) signalling whether the SDK is
expected to manage WebSocket reconnects for this client. Auto-disabled
when ``transport_factory`` is set, since the custom transport owns its
retry/reconnect lifecycle. The Python SDK has no wrapper reconnect layer
today, so this flag is declarative only -- it documents intent and is
reserved for any future SDK-side reconnect logic.
- `telemetry_opt_out`: Telemetry opt-out flag (maintained for backwards compatibility, no-op).
- `telemetry_handler`: Telemetry handler (maintained for backwards compatibility, no-op).
"""
Expand All @@ -122,6 +147,7 @@ def __init__(self, *args, **kwargs) -> None:
access_token: Optional[str] = kwargs.pop("access_token", None)
session_id: Optional[str] = kwargs.pop("session_id", None)
transport_factory: Optional[Callable] = kwargs.pop("transport_factory", None)
reconnect: bool = bool(kwargs.pop("reconnect", True))
telemetry_opt_out: bool = bool(kwargs.pop("telemetry_opt_out", True))
telemetry_handler: Optional[Any] = kwargs.pop("telemetry_handler", None)

Expand Down Expand Up @@ -152,9 +178,13 @@ def __init__(self, *args, **kwargs) -> None:
if access_token is not None:
_apply_bearer_authorization_override(self._client_wrapper, access_token)

# Install custom WebSocket transport if provided
# Install custom WebSocket transport if provided. Auto-disable
# `reconnect`: a custom transport owns its retry lifecycle, so flip
# the flag off even if the caller left it at the default.
if transport_factory is not None:
install_transport(async_factory=transport_factory)
reconnect = False
self.reconnect = reconnect

# Store telemetry handler for backwards compatibility (no-op, telemetry not implemented)
self._telemetry_handler = None
51 changes: 51 additions & 0 deletions tests/custom/test_transport.py
Loading