Repository navigation
feat(agent): add raw control sender - #789
Conversation
|
dg-coreylweathers
left a comment
There was a problem hiding this comment.
Verdict: approve with nits.
What this PR does
Adds public send_raw(message: Dict | str) on the sync and async Voice Agent socket clients so protocol-transparent bridges can forward any control frame without calling the private _send() (issue #785). Dicts are JSON-serialized; strings are sent byte-for-byte.
What I checked
- Does it send unchanged? Yes:
_senddoesjson.dumpsfor dicts and passes strings straight towebsocket.send; the four new tests capture exactly that, including that44100.0is not converted to an int. - Is the patch protected from regen? Yes:
agent/v1/socket_client.pywas already frozen; the PR adds the ledger bullet. - Gates in Docker: mypy clean, ruff clean on changed files, pytest 1038 passed.
Should-fix
send_rawappears only in the socket client, its tests,.fernignore, andAGENTS.md. Add asend_raw(message)line to the agent.v1 section ofreference.mdand a one-line mention in a voice-agent example or the skill file, otherwise bridge authors keep using_send().- State the number contract in the docstring: "Values are sent as given; whole-number floats are not converted to integers, so pass ints where the API expects them." That is what the tests pin and how it differs from the typed senders.
Nits: the Union[Dict, str] annotation is narrower than _send accepts (bytes also work), fine as the public contract. Needs a rebase: AGENTS.md/.fernignore conflict with main now, and after #797 lands agent/v1/socket_client.py is regenerated, so re-apply the two methods there.
589b545 to
fb06762
Compare
|
@dg-coreylweathers Rebased onto current |
fb06762 to
2b738dd
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dg-coreylweathers
left a comment
There was a problem hiding this comment.
Approving.
What this PR does
Adds a public send_raw() to the sync and async Voice Agent socket clients so relay apps can forward any JSON control message, including types the SDK does not model yet, without calling the private _send() (issue #785). A dict is converted to JSON; a string is sent exactly as given. No existing send_* method changes.
What I checked
- Were my earlier asks done? Yes:
send_rawis inreference.mdand the Voice Agent skill, both docstrings say whole-number floats are not converted to ints, and the branch is rebased on current main. - Does it send what it says? Yes: unit tests compare the exact text that reaches the socket, and a probe confirmed a string like
not json{goes out byte-for-byte while typed senders still convert1.0to1. - Does the live API accept it? Yes (request_id 01a1461b-33ab-7471-98ea-0325c23d0d34): Settings sent as a string got
SettingsApplied, a KeepAlive dict was accepted, an InjectAgentMessage dict made the agent speak; an unknown type gotError UNPARSABLE_CLIENT_MESSAGE. - Will the next Fern regen drop it? No: the file stays in
.fernignore, and a test merge with #803 (the open regen) keeps both methods with 46 socket tests passing and mypy clean.
Nits fixed in be28f5c: the reference.md entry now has an example line (agent.send_raw({"type": "KeepAlive"})) and says to prefer the typed method when one exists, and that the server answers an unknown type with Error UNPARSABLE_CLIENT_MESSAGE. The PR body now says this documents send_raw in the existing freeze entry.
Merge note: this conflicts with #788 in AGENTS.md (both edit the agent/v1/socket_client.py line). Keep this PR's line, then #788's StrictInt line below it.
🤖 I have created a release *beep* *boop* --- ## [7.14.0](v7.13.0...v7.14.0) (2026-10-20) ### Features * **Voice Agent:** Send protocol-transparent control frames with sync or async `connection.send_raw(...)`. Dictionary payloads are serialized without model validation; pre-serialized JSON strings are sent unchanged. * **Listen V1:** Receive typed `ListenV1ConfigureSuccess` and `ListenV1ConfigureFailure` responses after `connection.send_configure(ListenV1Configure(...))`, including failure codes for unsupported or over-limit keyterms. ### Bug Fixes * **Voice Agent:** Clarify ForceEndTurn availability for hosted and self-hosted deployments. The example now reports `FORCE_END_TURN_UNSUPPORTED` and waits for the final outcome before closing the connection. ## Validation - Source PR CI passed across Python 3.10 through 3.13. - #803: `poetry run pytest` passed with 1,075 tests and 4 skips; mypy and Ruff passed. - #789: targeted socket-client tests, mypy, and Ruff passed. - #788: ForceEndTurn regression coverage passed. --- 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> Co-authored-by: Greg Holmes <greg.holmes@deepgram.com>
Summary
send_raw()methods for protocol-transparent Agent control-frame bridges.send_rawin the existingagent/v1/socket_client.pyfreeze entry (.fernignore, AGENTS.md) and cover both input forms on both clients.Closes #785
Validation