Skip to content

Send lk.agent.session messages with one-shot byte sends - #2769

Draft
rosetta-livekit-bot[bot] wants to merge 1 commit into
mainfrom
skated-already-palisade
Draft

rosetta-livekit-bot[bot] wants to merge 1 commit into
mainfrom
skated-already-palisade

Conversation

@rosetta-livekit-bot

@rosetta-livekit-bot rosetta-livekit-bot Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • send each lk.agent.session message through rtc-node's one-shot byte-stream path instead of an incremental streamBytes writer
  • serialize sends to preserve message order and remove owner-only temporary payload files after each send
  • port the source transport coverage for one-shot delivery, ordering, and send failures

Infrastructure gap

@livekit/rtc-node does not expose the in-memory sendBytes API used by the Python source PR, and its native FFI has no in-memory one-shot byte request. This port therefore uses rtc-node's existing native sendFile one-shot path with an owner-only temporary file. It preserves the intended data-stream-v2 inline/compression behavior and wire protocol, but adds local filesystem I/O until rtc-node gains an in-memory API.

Testing

  • pnpm test agents/src/voice/remote_session.test.ts (27 passed)
  • pnpm --filter @livekit/agents typecheck
  • pnpm build:agents
  • pnpm --filter @livekit/agents lint (passes with existing warnings)
  • pnpm exec prettier --check "agents/src/**/*.{ts,tsx,md,json}" ".changeset/calm-dodos-send.md"
  • pnpm test agents (3,683 passed, 5 skipped, 3 unrelated failures in telemetry/loop_monitor.test.ts; the container exposes process-level rather than thread-level CPU accounting)

Runtime cue-cli validation could not be run because cue-cli is not installed or publicly resolvable in this environment.

Source diff coverage

Source PR livekit/agents#7751
  • livekit-agents/livekit/agents/voice/remote_session.py: adapted to agents/src/voice/remote_session.ts. Replaced streamBytes/writer calls with rtc-node's file-backed one-shot sendFile, preserving payload bytes, generated name, topic, destination filtering, failure propagation, and serialized ordering. The file-backed adaptation is required because rtc-node lacks Python's in-memory sendBytes API.
  • tests/test_remote_session.py: adapted to agents/src/voice/remote_session.test.ts. Ported the modified concurrent-send coverage, the new assertion that streamBytes is not used and each message is sent in one call, exact payload/topic/destination checks, and the modified send-failure behavior.
  • livekit-agents/pyproject.toml: not applicable. This bumps Python livekit to the release that introduced send_bytes; the target's current @livekit/rtc-node already provides the file-backed one-shot sendFile primitive used by this adaptation, while no rtc-node release provides in-memory sendBytes.
  • uv.lock: not applicable. It only records the Python dependency bump above; no target dependency changed, so pnpm-lock.yaml remains unchanged.

Source: livekit/agents#7751


Ported from livekit/agents#7751

Original PR description

Summary

RoomSessionTransport.send_message now sends each lk.agent.session message with the one-shot LocalParticipant.send_bytes instead of opening an incremental stream_bytes stream per message.

With stream_bytes, an incremental stream always results in three packets being sent on the reliable data channel (header, chunk, trailer) and is never compressed.

With send_bytes the SDK knows the whole payload up front, so for receivers that support data streams v2 it can send a small message as a single inline packet, and compress a large one.

Why it matters

Session events are small and frequent, and today they go to every participant in the room, because the room session host is created with no remote identity. Measured against a JS client (clientProtocol of 3), one agent turn sent ~95 session messages: 89× session_usage_updated, 4× agent_state_changed, 2× conversation_item_added. On the client's data channel:

data channel ↓ per agent turn
stream_bytes (today) ~52 KB / 354 messages
send_bytes (this PR) ~37 KB / 170 messages (−29% bytes, −52% messages)
no session host at all ~11 KB / 83 messages

On constrained mobile links, that traffic queues ahead of RPCs and transcriptions on the reliable channel. That's the head-of-line blocking behind customer reports of agent → client RPC connection timeouts.

TODO

@rosetta-livekit-bot
rosetta-livekit-bot Bot requested a review from a team as a code owner October 18, 2026 23:18
@changeset-bot

changeset-bot Bot commented Oct 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 02c4deb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 41 packages
Name Type
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-anthropic Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-azure Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-krisp Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-meta Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-protoface Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-speechify Patch
@livekit/agents-plugin-synthesia Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugins-test Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@rosetta-livekit-bot
rosetta-livekit-bot Bot requested a review from 1egoman October 18, 2026 23:18

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

throw new Error('room session transport is closed');
}

const unlock = await this.sendLock.lock();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Queued room messages outlive closure

When close() runs while a send waits on sendLock, that send can transmit after closure. The closed check runs before the lock wait, and close() does not cancel queued sends.

Learn more

A room transport serializes outgoing messages with sendLock. A send checks closed before waiting for the lock, but close sets closed without draining or cancelling the queue. A queued call therefore resumes after closure and proceeds to sendFile if the room remains connected. This lets a closing session deliver stale events or responses after its transport has closed.

Example: Send A holds the lock while sendFile waits. Send B passes the initial check and waits for the lock. close() completes, then A finishes; B acquires the lock and sends even though closed is now true.

Recommended fix: Recheck closed and room connectivity after acquiring sendLock and before creating the file. Coordinate close() with any active send if its contract requires no sends after close returns.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@1egoman 1egoman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is not a good approach. Before this could be merged, a sendBytes method needs to be added to the node sdk as well. I thought I had done this but it seems not, I will move this to draft, make the requisite node sdk changes, and then rework this once there's a node sdk release with sendBytes.

@1egoman
1egoman marked this pull request as draft October 20, 2026 00:41
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