feat(regen): agent function cancellation and defer-until-EOT - #790
Conversation
|
dg-coreylweathers
left a comment
There was a problem hiding this comment.
Requesting changes — all small, no blockers in the code itself. The regen is sound: public surface is purely additive and the new setting works on production. The gaps are discoverability.
What I verified
defer_until_eotworks on production. Sent it towss://agent.deepgram.com/v1/agent/converse→SettingsApplied. To confirm the server actually understands it rather than ignoring it, I sent a bogus sibling field in the same position →Error UNPARSABLE_CLIENT_MESSAGE("Check the agent.think field against the API spec"). Unknown-field rejection is on foragent.think, so the acceptance is meaningful. Not server-gated, unlike last cycle's force-end-turn.- No breaking changes. Imported
__all__from 14 packages onmainvs this branch: 0 removed, 12 added. FunctionCallCancelleddoes not shadowFunctionCallRequestinV1SocketClientResponse— both resolve correctly, including Cancelled with an unknown nested field and with an emptyfunctionslist. Distincttypeconsts, so no discriminator collapse.- Gate:
pytest1054 passed / 1 skipped,mypy src tests/typecheckclean (908 files),ruff check src/deepgramclean,poetry check --lockclean. websockets.WebSocketExceptionresolves on the dependency floor (websockets==12.0), so the new narrowexceptis safe.- Version bump is fine — the generator patch-bumps and release-please recomputes from the squash title, per the "Version bump" note in bacd1b5. Just keep the squash title as
feat(regen): …or the feature ships labeled 7.9.1.
Please fix
1. FunctionCallCancelled is missing from reference.md
Both agent-connect import blocks still list the fourteen pre-existing event types. A developer working from the reference never learns the event exists, or that they must stop sending a FunctionCallResponse for a cancelled id.
Add after AgentV1FunctionCallRequest, at reference.md:6171 and reference.md:6279:
AgentV1FunctionCallCancelled,
reference.md is permanently frozen in .fernignore, so the generator can't do this — it's re-applied by hand each cycle, same as send_force_end_turn and SpeechInterrupted last time.
2. No example or live coverage for either feature
The only coverage is unit tests against fake sockets. tests/manual/agent/v1/connect/main.py still dispatches AgentV1FunctionCallRequest with no cancellation branch, and nothing demonstrates defer_until_eot. Last regen shipped examples/32-voice-agent-force-end-turn.py plus tests/manual/listen/v2/force_end_turn/ for a comparable feature.
Suggest extending tests/manual/agent/v1/connect/main.py: handle AgentV1FunctionCallCancelled, and add a defer_until_eot=True function to its settings.
3. Both except clauses are now present with identical bodies
Ten sites across all five socket clients now have:
except (websockets.WebSocketException, JSONDecodeError) as exc:
self._emit(EventType.ERROR, exc)
except Exception as exc:
self._emit(EventType.ERROR, exc)The first clause can never change behavior. More importantly, .fernignore describes this patch as "except Exception broad catch (supports custom transports, generator narrows to WebSocketException)" — which no longer matches the file, so the next regen reviewer will be misled about which line is the patch.
Either drop the narrow clause and the now-unused import websockets at agent/v1/socket_client.py:127,283, listen/v1/socket_client.py:72,179, listen/v2/socket_client.py:75,187, speak/v1/socket_client.py:73,180, speak/v2/socket_client.py:92,206 — or keep it and update the .fernignore comment to say both are deliberate.
Nits
.fernignore:219addstests/custom/test_defer_until_eot.pybut theAGENTS.mdfreeze ledger isn't updated; bacd1b5 updated both in step.agent/v1/socket_client.py:49dropsSee: internal-api-specs/issues/205from the_sanitize_numeric_typesdocstring — that was the only recorded reason the frozen patch exists..fern/metadata.jsonlost its trailing newline.tests/custom/test_defer_until_eot.py:9uses the Pydantic-v1.dict()API and duplicates an assertion already added attest_socket_client_shims.py:247.
One question
Does production emit FunctionCallCancelled yet? The client parses it and the paired setting is accepted on prod, but the event only fires when a speculative call is retracted mid-turn, which I couldn't force from a probe. Last cycle's force-end-turn turned out staging-only. If emission isn't live on prod yet, the release notes should carry that caveat.
|
Addressed the requested discoverability and maintenance follow-ups.
Validation: poetry run pytest (1,050 passed, 4 skipped), poetry run mypy src tests/typecheck, poetry run ruff check src/deepgram, and poetry check --lock all pass. |
dg-coreylweathers
left a comment
There was a problem hiding this comment.
Approving — all three requested changes are in, verified against the code rather than the summary.
Re-verified on 1f2ce25
reference.md—AgentV1FunctionCallCancelledis now in both agent-connect import blocks, matching the existing style (every event type there is listed to show what the connection can deliver).- Live coverage — I serialized the exact
ThinkSettingsV1FunctionsItemfrom the updated manual script through the SDK, got{"name","description","parameters","defer_until_eot":true}, and sent it towss://agent.deepgram.com/v1/agent/converse→SettingsApplied. The script will work when run. - Exception cleanup — all ten narrow clauses removed along with both now-unused imports; zero
WebSocketExceptionreferences remain insrc/deepgram/; the broadexcept Exceptionis still present twice per file;listen/v1,speak/v1andspeak/v2socket clients are byte-identical tomainagain, and.fernignore's description of the patch matches the files once more. - Deleting the duplicate test was the better call.
.fernignoreis now identical tomain, so no ledger row is owed. Coverage isn't lost —test_socket_client_shims.py:247,253assert the same field through the fullsend_settingspath on both sync and async clients, which is stronger than the model-level assertion that went away. - Gate:
pytest1053 passed / 1 skipped (exactly one fewer than before, matching the single deleted test),mypy src tests/typecheckclean across 908 files,ruff check src/deepgramclean. - Public surface: re-ran the 14-package
__all__comparison againstmain— 0 removed, 12 added, unchanged from the first pass.
Two things to carry into the merge
1. Keep the squash title as feat(regen): … — the patch version in the diff is only correct because release-please recomputes the bump from the commit type. If it lands as chore or fix, this ships as 7.9.1 and the new feature goes out labeled a patch.
2. Does production emit FunctionCallCancelled yet? Still open. The setting half is confirmed live on prod and the client parses the event on both sync and async paths, but nothing confirms the server actually sends it outside staging. If it is not live yet, the release notes should say so — the way last cycle's force-end-turn gate was documented.
Non-blocking leftovers
.fern/metadata.jsonstill has no trailing newline.- Only the sync manual script covers the new event;
async.pydoesn't, and the two agent socket clients are separate implementations with separate listen loops. The parse path is unit-tested in both directions, so this is completeness rather than a hole — and it was not in the original ask, which named onlymain.py. examples/has nothing for either feature, where the previous regen addedexamples/32-voice-agent-force-end-turn.pyfor a comparable addition. Worth a follow-up if we want parity.
Summary
fernapi/fern-python-sdk 5.27.1.defer_until_eotfunction setting.Validation
poetry run pytest: 1,036 passed, 4 skippedpoetry run mypy src tests/typecheck: passedpoetry run ruff check .reports 59 pre-existing violations in unrelated manual examples and tests; none are introduced by this regen.