cool-planet-88641
12/30/2025, 3:36 PMtall-airline-16756
12/30/2025, 3:36 PMtall-airline-16756
12/30/2025, 3:37 PMpublishData() / sendText() on the participant — for the most reliable, low‑latency, secure delivery.
• For request/response patterns use LiveKit RPC (bidirectional RPC over data channels).
• For large or streaming outputs (LLM partial results, progressive updates) use LiveKit’s text/byte streaming handlers (chunked streams with cancellation support).
When to use each
• Small messages / chat / status updates: publishData() or sendText() with reliable mode.
• Structured commands or sync-style calls: RPC (frontend calls, agent replies).
• Large or incremental data (streaming): text stream or byte stream handlers to send chunks progressively.
• Broadcast vs targeted: you can send to the whole room or target specific participants by identity/SID.
Key practical notes
• Reliable delivery is supported (retransmit/TCP-like) — choose reliable mode for important messages.
• Message size: keep messages under ~15KB per message (chunk if larger).
• Security: messages are secured via DTLS; enable E2EE if you need end-to-end encryption beyond transport.
• Handlers: frontend listens for RoomEvent.DataReceived / stream handlers or RPC responses.
If you want, tell me:
• Which SDK(s) you’re using (JS, Python, Swift, etc.), and
• Whether you need broadcast, targeted delivery, RPC, or streaming — and I’ll give exact code snippets for that SDK. Sources: client-sdk-unity | livekit/livekit_composite | DeepWiki | Client SDKs | livekit/livekit_composite | DeepWiki | examples/other | livekit/livekit_composite | DeepWiki | Job lifecycle | LiveKit docs | External data and RAG | LiveKit docscool-planet-88641
12/30/2025, 3:37 PMtall-airline-16756
12/30/2025, 3:38 PMpublish_data, send_text, or perform_rpc.
Recommendation summary
• Use perform_rpc when you need structured request/response semantics (frontend expects a reply, timeouts, or remote procedure behavior).
• Use send_text for simple, small text messages or human-readable notifications where you don’t need RPC semantics.
• Use publish_data for arbitrary binary payloads, topic-tagged messages, or when you need to send structured data (JSON/binary) with reliability/control over reliability mode.
When to choose each (short)
• perform_rpc
◦ Use when calls require a direct response, retries/timeouts, or you want a clear request/response flow.
◦ Good for commands like “start process”, “fetch result”, or synchronous workflows.
• send_text
◦ Use for chat messages, status lines, or small human-readable strings.
◦ Simple API, text-focused, easy to consume on clients.
• publish_data
◦ Use for binary blobs, JSON payloads, topic-based routing, or when you need to choose reliable vs unreliable delivery.
◦ Good for telemetry, structured events, or targeting specific participants.
Other practical notes
• Reliability: all operate over WebRTC data channels; choose reliable mode for guaranteed delivery.
• Size: chunk messages if > ~15KB. For large/streaming data, use streaming handlers.
• Targeting: publish_data supports topic/targeting semantics; RPC naturally targets the caller/handler.
If you tell me your SDK (JS, Python, etc.) and whether you need reply semantics or streaming, I’ll give the exact code snippet to implement the recommended method.cool-planet-88641
12/30/2025, 3:38 PMtall-airline-16756
12/30/2025, 3:38 PMpublish_data, send_text, and perform_rpc, plus examples.
Quick recommendation
• If you need a request → reply flow with timeouts and clear semantics: use perform_rpc.
• For simple human-readable messages (chat/status): use send_text.
• For arbitrary structured/binary payloads, topic-based routing, or control over reliability: use publish_data.
Python examples
1) perform_rpc (recommended for request/response)
# agent side: define RPC handler
from livekit import Room, rtcx
@rtcx.rpc()
async def do_work(self, payload: str):
# process payload
return {"status": "done", "result": payload.upper()}
# make sure handler is registered when room connects
Frontend (JS) example of calling RPC (for context):
const res = await room.localParticipant.performRpc({
method: "do_work",
payload: JSON.stringify({ text: "hello" }),
responseTimeout: 30000
});
console.log(res); // agent reply
2) send_text (simple text messages)
# agent sends a text message to the room
await room.local_participant.send_text("processing started", topic="agent-status")
Frontend listens for text/data events via RoomEvent.DataReceived or text stream handlers.
3) publish_data (structured/binary/topic/targeted)
import json
payload = json.dumps({"type": "progress", "percent": 42}).encode()
await room.local_participant.publish_data(payload, reliable=True, topic="agent-events")
• To target a specific participant, include the participant SID/identity in the send call if SDK supports it (or the frontend can filter by topic/metadata).
Practical notes
• Use reliable=True for guaranteed delivery.
• Keep messages < ~15KB; chunk via streaming handlers for larger/streaming data.
• For streaming large outputs (LLM tokens), use text/byte stream handlers rather than single large messages.
• RPC is best when the frontend expects a response and you want built-in timeout/cancellation semantics.
If you tell me which exact Python SDK/classes you’re using (server-side LiveKit Room client or a custom agent framework) and whether you need broadcast vs targeted delivery or streaming, I’ll provide exact runnable snippets for your setup.cool-planet-88641
12/30/2025, 3:39 PMtall-airline-16756
12/30/2025, 3:40 PMpublish_data often doesn’t reach the frontend unless you add an asyncio.sleep().
Likely cause
• This typically indicates a timing/race issue: you’re sending data before the participant/room data channel is fully negotiated and ready on both sides. Adding asyncio.sleep() hides the race by delaying the send until channels are up.
To diagnose quickly, please tell me:
• which Python SDK & exact version you’re using,
• how/when you call publish_data (share the short code snippet around connection and the send),
• whether you send immediately after connect or after any RoomEvent handlers,
• how the frontend listens for data (SDK + listener code).
Immediate fixes to try
1. Wait for room/participant connection before sending:
await room.connect(...) # or however you join
# wait for local participant to be published / settled
await asyncio.sleep(0.1) # temporary; replace with event-based wait below
await room.local_participant.publish_data(b"payload", reliable=True)
1. Prefer event-driven send (recommended): send only after the room reports it’s connected or after the participant is published. Example pattern:
# pseudo-code: register handler when room connects
@room.on("connected") # adapt to your SDK event API
async def on_connected():
await room.local_participant.publish_data(b"hello", reliable=True)
1. Confirm frontend is ready to receive:
• Ensure the frontend has registered the RoomEvent.DataReceived (or text/stream handler) before or immediately after joining the room.
• Log data receipt and drop cases.
1. If you must send right after join, poll for data channel readiness instead of blind sleep:
• Check for participant state or room state that indicates data channel open (SDK may expose participant publishing state or events).
If trying the event-driven approach doesn’t fix it, paste:
• the minimal Python snippet that joins and calls publish_data, and
• the frontend code that listens for data.
I’ll review those and give a precise fix.cool-planet-88641
12/30/2025, 3:41 PMtall-airline-16756
12/30/2025, 3:41 PMpublish_data reliably reaches the frontend, including rejoin/reconnect scenarios.
Short answer
• Send only after the local participant’s data channel is fully established — i.e., after the room/participant is “connected” and the participant is published. Use event-driven checks rather than sleeps. Re-establish sends after any reconnect/rejoin event.
Recommended approach (event-driven)
1. Wait for the room/participant connected/published event before sending.
2. On reconnect/rejoin, wait for the same event and resend or resume sending.
3. Ensure the frontend registers its data handler early (on join) so it’s ready when the data arrives.
4. Use reliable mode and consider idempotency or acknowledgements if delivery must be confirmed.
Concrete Python pattern (pseudo/adjust to your SDK)
• Register handlers for connection and participant state, and send only from those handlers.
# Pseudocode — adapt to your LiveKit Python SDK event API
async def on_room_connected(room):
# local participant and data channels should be ready
await room.local_participant.publish_data(b"hello", reliable=True)
async def on_participant_published(room, participant):
if participant.is_local:
# safe to send now
await room.local_participant.publish_data(b"ready", reliable=True)
# handle rejoin/reconnect
async def on_reconnected(room):
# re-register/resend necessary state
await room.local_participant.publish_data(b"resuming", reliable=True)
# wiring (SDK-specific)
room.on("connected", on_room_connected)
room.on("participant_published", on_participant_published)
room.on("reconnected", on_reconnected)
Additional robustness tips
• Frontend readiness: Register RoomEvent.DataReceived (or text/stream handlers) as soon as the client begins connecting so it won’t miss the first messages.
• ACKs: If you need delivery confirmation, implement an application-level ACK: agent sends message with ID; frontend replies (via RPC or publish_data) to confirm receipt. Retry on no ACK.
• Idempotency: Make messages idempotent or include sequence IDs so re-sent messages on reconnect won’t cause problems.
• Streaming/large payloads: Use text/byte stream handlers rather than single large publish_data calls.
• Diagnostics: Log participant state, send timestamps, and any send errors. On the client, log when handlers are registered and when data events are received.
If you share the minimal Python join/send snippet and the frontend listener code, I’ll point out the exact place to attach the send logic and provide a runnable example tailored to your SDK version.cool-planet-88641
12/30/2025, 3:45 PMtall-airline-16756
12/30/2025, 3:45 PMpublish_data, including reconnect/rejoin handling.
Use these events in the LiveKit Python SDK:
• RoomEvent.CONNECTED — initial safe point: signaling + SDP negotiation complete and data channels ready.
• RoomEvent.RECONNECTED — safe to resume sends after a network reconnect.
Exact runnable example (adapt token/url):
import asyncio
from livekit import rtc
from livekit.room import RoomEvent
async def main():
room = rtc.Room()
async def send_ready_data():
try:
await room.local_participant.publish_data(
data=b"hello from agent",
reliable=True,
topic="agent-greeting"
)
print("Data sent")
except Exception as e:
print("Send failed:", e)
@room.on(RoomEvent.CONNECTED)
def on_connected():
print("Room CONNECTED")
asyncio.create_task(send_ready_data())
@room.on(RoomEvent.RECONNECTED)
def on_reconnected():
print("Room RECONNECTED")
asyncio.create_task(send_ready_data())
@room.on(RoomEvent.DISCONNECTED)
def on_disconnected():
print("Room DISCONNECTED")
await room.connect(
url="<wss://your-livekit-server>",
token="YOUR_JWT"
)
try:
while True:
await asyncio.sleep(1)
except KeyboardInterrupt:
await room.disconnect()
if __name__ == "__main__":
asyncio.run(main())
Notes:
• Register your frontend data handler early so it’s ready when messages arrive.
• For critical messages, implement an app-level ACK (agent sends ID → frontend replies) and retry on no ACK.
• If you paste your minimal join/send snippet, I’ll point out exactly where to attach this logic for your code. Sources: client-sdk-unity | livekit/livekit_composite | DeepWiki | SDKs | livekit/livekit_composite | DeepWiki