fix(websockets): restore optional message param on control send_ methods#680
Conversation
Control messages (keep-alive, close-stream, finalize, flush, clear, close) carry no meaningful payload — the type field is the whole message. Making the message param optional with a typed default means users can call e.g. send_keep_alive() without constructing the type themselves. Payload-carrying methods (send_text, send_media, send_settings, etc.) remain required. This restores behaviour lost in the last regen and fixes a breaking change shipped in a minor patch.
There was a problem hiding this comment.
Pull request overview
This PR restores backward-compatible WebSocket control send_* method signatures by reintroducing an optional message parameter (defaulting to None) and constructing a default control payload when omitted, avoiding TypeError regressions from the last regen.
Changes:
- Make control
send_*methods acceptmessage: Optional[T] = Noneacross Speak v1, Listen v1/v2, and Agent v1 socket clients. - When
messageis omitted, send a default control model instance (e.g.,type="CloseStream"). - Document the generator freeze rationale in
.fernignorewith an added note about this patch.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/deepgram/speak/v1/socket_client.py | Restores optional params for send_flush/send_clear/send_close and sends default typed control messages when omitted. |
| src/deepgram/listen/v1/socket_client.py | Restores optional params for send_finalize/send_close_stream/send_keep_alive and sends default typed control messages when omitted. |
| src/deepgram/listen/v2/socket_client.py | Restores optional param for send_close_stream and sends a default typed control message when omitted. |
| src/deepgram/agent/v1/socket_client.py | Restores optional param for send_keep_alive and attempts to send a default keep-alive message when omitted. |
| .fernignore | Adds a comment documenting this patch as part of the generator freeze notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| The message will be sent as a AgentV1KeepAlive. | ||
| """ | ||
| await self._send_model(message) | ||
| await self._send_model(message or AgentV1KeepAlive()) |
There was a problem hiding this comment.
AgentV1KeepAlive requires a type field (no default in the model), so AgentV1KeepAlive() will raise a validation error. This means send_keep_alive() is still not callable with no args. Construct the default message with the required type (e.g., the "KeepAlive" literal) when message is omitted.
| await self._send_model(message or AgentV1KeepAlive()) | |
| if message is None: | |
| message = AgentV1KeepAlive(type="KeepAlive") | |
| await self._send_model(message) |
| The message will be sent as a AgentV1KeepAlive. | ||
| """ | ||
| self._send_model(message) | ||
| self._send_model(message or AgentV1KeepAlive()) |
There was a problem hiding this comment.
Same issue for the sync client: AgentV1KeepAlive() is missing the required type field, so this default will raise at runtime. Please set the type field when constructing the default keep-alive message.
| self._send_model(message or AgentV1KeepAlive()) | |
| self._send_model(message or AgentV1KeepAlive(type="keep_alive")) |
| async def send_finalize(self, message: typing.Optional[ListenV1Finalize] = None) -> None: | ||
| """ | ||
| Send a message to the websocket connection. | ||
| The message will be sent as a ListenV1Finalize. | ||
| """ | ||
| await self._send_model(message) | ||
| await self._send_model(message or ListenV1Finalize(type="Finalize")) | ||
|
|
||
| async def send_close_stream(self, message: ListenV1CloseStream) -> None: | ||
| async def send_close_stream(self, message: typing.Optional[ListenV1CloseStream] = None) -> None: | ||
| """ | ||
| Send a message to the websocket connection. | ||
| The message will be sent as a ListenV1CloseStream. | ||
| """ | ||
| await self._send_model(message) | ||
| await self._send_model(message or ListenV1CloseStream(type="CloseStream")) | ||
|
|
||
| async def send_keep_alive(self, message: ListenV1KeepAlive) -> None: | ||
| async def send_keep_alive(self, message: typing.Optional[ListenV1KeepAlive] = None) -> None: | ||
| """ | ||
| Send a message to the websocket connection. | ||
| The message will be sent as a ListenV1KeepAlive. | ||
| """ | ||
| await self._send_model(message) | ||
| await self._send_model(message or ListenV1KeepAlive(type="KeepAlive")) |
There was a problem hiding this comment.
This PR restores optional control-message params, but there are no tests asserting that these send_*() control methods can be called with no arguments (and that they send the expected default payload). Since the repo already has coverage for listen.v1.socket_client behavior in tests/custom/test_transport.py, please add a small unit test that instantiates the socket client with a mock transport and verifies send_keep_alive()/send_close_stream()/send_finalize() work without args.
Also fixes AgentV1KeepAlive default to include type='KeepAlive' (bare AgentV1KeepAlive() fails Pydantic validation since type is required).
🤖 I have created a release *beep* *boop* --- ## [6.1.1](v6.1.0...v6.1.1) (2026-03-27) ### Bug Fixes * **websockets:** restore optional message param on control send_ methods ([#680](#680)) ([0018fc4](0018fc4)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
send_methods (send_keep_alive,send_close_stream,send_finalize,send_flush,send_clear,send_close) lost their optionalmessageparameter in the last regensend_keep_alive()etc. without args started gettingTypeErrormessage: Optional[T] = Nonewith a typed default on all control methods across all 4 WebSocket clientssend_text,send_media,send_settings, etc.) remain requiredAffected files
src/deepgram/speak/v1/socket_client.py—send_flush,send_clear,send_closesrc/deepgram/listen/v1/socket_client.py—send_finalize,send_close_stream,send_keep_alivesrc/deepgram/listen/v2/socket_client.py—send_close_streamsrc/deepgram/agent/v1/socket_client.py—send_keep_alive.fernignore— updated comment to document this patchTest plan
send_keep_alive(),send_close_stream(),send_finalize(),send_flush(),send_clear(),send_close()can be called with no argumentspytestpasses