Skip to content

voice-agents-python: the TypeScript runtime holds frames during a resume, fails lost tool calls, contains a throwing handler - #4

Closed
sent-dm-sdk-pipeline[bot] wants to merge 1 commit into
mainfrom
voice-agents/render-b629252
Closed

sent-dm-sdk-pipeline[bot] wants to merge 1 commit into
mainfrom
voice-agents/render-b629252

Conversation

@sent-dm-sdk-pipeline

Copy link
Copy Markdown
Contributor

Rendered by the Voice Agents lane in sent-dm-sdk-pipeline from sent-dm-voice-protocol at v0.2.0, after https://github.com/sentdm/sent-dm-sdk-pipeline/commit/b62925299e2c1785890cb4dccd14822b712a4cf5.

What a reviewer checks: this repository's CI, the conformance job included, is green on this PR; the generated frames module and the runtime changed as the protocol or the template did; nothing release-please owns moved. Merging makes release-please open the release.

…lost tool calls, contains a throwing handler
@sent-dm-sdk-pipeline
sent-dm-sdk-pipeline Bot requested a review from a team as a code owner November 1, 2026 14:45
@sent-dm-sdk-pipeline

Copy link
Copy Markdown
Contributor Author

Superseded by the render on voice-agents/render-9269369, which carries this change and the lane's later ones.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Comment on lines +94 to +95
if self.ended:
raise SessionEnded(self.id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 (optional) Audio agents will now log a "a handler failed" traceback at the end of most calls. Any code after send_audio in the handler is also skipped. send_audio now raises SessionEnded once ended is true. Audio already queued in the session inbox still reaches on_audio after the reader has set end or lost. The repo's own conformance agent (conformance/main.py:63) does not catch it. Fix: handle the end of the call in every send_audio caller. Either drop audio silently after the end, as the resume hold already does, or keep the raise and catch SessionEnded in the conformance on_audio and in the README guidance.

Why this was flagged

The trigger is an audio session whose session.end arrives, or whose session is lost, while caller audio frames are still queued in session._inbox. Each 20 ms frame is its own item, so a backlog is common. agent.py:305-307 puts the SessionEnd in the inbox and then calls session._finish, which sets end at once. _lost at agent.py:350-353 does the same through _abandon, which sets lost. _run_session (agent.py:393-394) then delivers the earlier audio items to on_audio. The conformance agent's on_audio calls session.send_audio(samples) with no try (conformance/main.py:62-63), so the new check at session.py:94-95 raises SessionEnded. _safely (agent.py:418-419) logs it with log.exception("a handler failed"). On the base branch send_audio never raised SessionEnded. The bytes went to send_bytes, which swallows a closed socket. So existing handlers, including this repo's conformance agent, which catches SessionEnded only around say/tool, never expected it. Nothing in the diff updates those callers or the README's send_audio(samples) description.

Verification: Trigger: in an audio session, session.end arrives or the session is lost while caller audio frames are still queued in session._inbox. The new guard at the top of send_audio (session.py:94-95) is if self.ended: raise SessionEnded(self.id). On the base, send_audio had no ended check. sentdm_voice_agents/conformance/main.py:62-63 has no except SessionEnded.

Comment on lines 337 to 339
session._resuming = False
connection.sessions.pop(session.id, None)
if frame.error.code == "connection_full":

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 pre-existing, not blocking: Pre-existing: after a connection_full refusal, agent audio can still go to another caller. The new resume guard does not cover this case. _error clears _resuming but leaves session._connection on the full connection, so send_audio and _send still transmit there with the old stream number. Fix: keep the session marked detached (for example _resuming stays True, or a separate flag) from the refusal until session.resumed arrives on the new connection, so both audio and frames are held.
A small fix can ride a push you are already making; otherwise a short reply is enough.

Why this was flagged

The trigger is Sent answering a session.resume with code connection_full. agent.py:337 sets session._resuming = False and agent.py:341 opens a new connection, but session._connection still points at the connection that refused it (set at agent.py:279). Until the new connection is ready, session.py:98 no longer drops audio. send_audio sends audio_frame(self.stream, ...) on the full connection. self.stream belongs to the old, lost connection and can match another caller's stream there. That is the hazard the new comment at session.py:99-100 describes. Counted frames are also sent there at session.py:146 instead of waiting for the replay. The base branch has the same behaviour. The diff's guard only covers the time a resume is unanswered and misses this window.

Verification: Pre-existing. _error (agent.py:337-342) sets session._resuming = False and calls self._open([session], 0.0). It never detaches session._connection, so the session still points at the full connection. During that gap, the new guard in send_audio (session.py:98, if self._resuming: return) does not fire. On the base, send_audio had no _resuming check at all.

@arbianst
arbianst deleted the voice-agents/render-b629252 branch November 1, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant