fix: route Speak V2 websocket through custom transport_factory - #766
Open
mayankbohradev wants to merge 2 commits into
Open
fix: route Speak V2 websocket through custom transport_factory#766mayankbohradev wants to merge 2 commits into
mayankbohradev wants to merge 2 commits into
Conversation
mayankbohradev
requested review from
GregHolmes,
deepgram-kiley and
dg-coreylweathers
as code owners
August 13, 2026 15:09
`_TARGET_MODULES` in transport.py lists the modules that `install_transport()` patches so a user-supplied `transport_factory` replaces the default `websockets` transport. The Speak V2 websocket client shipped in 7.7.0 and binds the same patched symbols, but its two modules were never added to the list. Effect: a caller passing `transport_factory` gets custom-transport routing for Listen v1/v2, Speak v1 and Agent v1, while Speak V2 opens a direct connection to api.deepgram.com. There is no error or warning, so traffic intended to flow through a proxied or custom-hosted transport silently bypasses it. The existing transport tests could not catch this. They iterate `_TARGET_MODULES` itself, so they verify the list is internally consistent but cannot detect a module missing from it -- the missing entry is never iterated. Left as-is, the next regen that adds a websocket client reintroduces the same silent bypass. Changes: - add deepgram.speak.v2.raw_client and deepgram.speak.v2.client to `_TARGET_MODULES` (and correct the now-stale count in the comment) - add a completeness guard that discovers websocket modules from the package source, independently of `_TARGET_MODULES`, and asserts every discovered module is registered Verified: the new guard fails on the unpatched tree naming both Speak V2 modules, and passes after the fix. 319 passed, 1 skipped across tests/custom and tests/utils; `mypy src/` and `mypy tests/typecheck` clean. WireMock-backed tests under tests/wire require Docker and were not run locally.
mayankbohradev
force-pushed
the
fix/speak-v2-transport-factory
branch
from
August 13, 2026 15:23
d6d879e to
67b7341
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A
transport_factorypassed toDeepgramClientorAsyncDeepgramClientwas not applied to the Speak V2 WebSocket client. Speak V2 connected through the defaultwebsocketstransport instead, with no error or warning.Who this affects
transport_factoryexists so callers can route WebSocket traffic through their own transport, including proxies, test doubles, and custom-hosted endpoints. Before this fix, Listen v1/v2, Speak v1, and Agent v1 used the custom transport while Speak V2 silently connected directly toapi.deepgram.com.This is especially important for callers relying on controlled network egress, auditing, or custom-hosted transports: accepting the factory but bypassing it can send traffic to an unintended endpoint without any signal.
Root cause
install_transport()patches the generated modules listed in_TARGET_MODULESinsrc/deepgram/transport.py. Speak V2 shipped after that hand-maintained list was created, anddeepgram.speak.v2.raw_clientanddeepgram.speak.v2.clientwere never added.The existing tests iterated
_TARGET_MODULESitself, so they could verify registered modules were patched but could not detect a real WebSocket module missing from the list.Changes
deepgram.speak.v2.raw_clientanddeepgram.speak.v2.clientto_TARGET_MODULES_TARGET_MODULESregeneration invariant inAGENTS.mdThe guard intentionally uses a static source scan rather than
pkgutil.walk_packagesto avoid import side effects and still detect modules that fail to import. It asserts a subset rather than exact equality because the runtime registry may intentionally contain a module not present in a particular generated tree; the behavior-breaking case is a real WebSocket module absent from the registry.Verification
speak.v2.connect()invoke the supplied factory with the expected/v2/speakURL and authorization headers380 passed, 1 skipped36 passedmypy src/: clean across 882 source filessrc/deepgram/transport.pycheck: cleangit diff --check: cleanwebsocketsdeprecation warnings