wonderful-wolf-51568
01/02/2026, 8:46 AMtall-airline-16756
01/02/2026, 8:46 AMtall-airline-16756
01/02/2026, 8:46 AMctx.add_shutdown_callback() inside your agent entrypoint so it runs after the room disconnects but before the worker shuts down. Start the long task in the background (e.g., with asyncio.create_task) so it won’t block shutdown housekeeping.
Minimal example:
@server.rtc_session()
async def entrypoint(ctx: JobContext):
session = AgentSession(...)
await session.start(agent=agent, room=ctx.room)
await ctx.connect()
async def on_shutdown():
# run long-running post-processing in background
asyncio.create_task(summarize_call(session, ctx.room))
ctx.add_shutdown_callback(on_shutdown)
Notes and best practices:
• Place the callback in the entrypoint where ctx is available.
• Access transcripts/metrics from session inside your callback before it’s torn down.
• Wrap background work in try/except and clean up any external clients when finished.
• Use background tasks so the agent cleanup isn’t blocked.
If you want, tell me:
• how you store transcripts (in-session or external), and
• whether the summarization must finish before other downstream steps — I’ll suggest patterns for guaranteed completion vs. fire-and-forget. Sources: Collect recording consent with tasks | LiveKit docswonderful-wolf-51568
01/02/2026, 8:50 AMtall-airline-16756
01/02/2026, 8:50 AMctx.add_shutdown_callback() so post‑call work runs after disconnect.
2. Start the summarization in the background inside that callback — do NOT await it. Use asyncio.create_task() so the callback returns immediately.
Example pattern
async def on_shutdown():
# non-blocking: fire-and-forget
asyncio.create_task(summarize_call(session.transcript))
ctx.add_shutdown_callback(on_shutdown)
1. Keep the shutdown callback itself short and non-blocking. Never await the long task there.
2. Make the background task resilient:
◦ Catch asyncio.CancelledError to handle mid-shutdown termination.
◦ Save partial results or enqueue work to an external queue if you need retries.
◦ Close any external clients only after starting the background task, or use separate clients inside the background task so shutdown cleanup doesn’t cancel it prematurely.
1. If you must guarantee completion, switch from fire‑and‑forget to a reliable handoff: push the transcript to a queue (e.g., SQS, Pub/Sub, or a webhook) in the shutdown callback and process it externally — this avoids process timeout entirely.
If you want, share a short snippet of your current shutdown handler (or confirm you’re already using ctx.add_shutdown_callback()), and I’ll point out the exact change you need.
Sources: How to detect and handle silence during calls | examples/frontdesk | livekit/livekit_composite | DeepWiki | Collect recording consent with tasks | LiveKit docs | examples/voice_agents | livekit/livekit_composite | DeepWiki | STT Metrics | LiveKit docssteep-shampoo-86436
01/02/2026, 9:32 AMtall-airline-16756
01/02/2026, 9:33 AMctx.add_shutdown_callback() in the entrypoint to start a background task with asyncio.create_task() so the callback returns immediately.
• Ensure the shutdown callback does not await long tasks.
• Consider increasing the agent process grace period with --shutdown-grace-period=<seconds> if you need more time.
• For guaranteed processing, push the transcript to a queue (SQS/PubSub) in the shutdown callback and process it externally.
If you share how you currently persist transcripts (in-session only, or also saved to storage) I’ll give an exact webhook handler pattern or the precise change to your shutdown callback to avoid the timeout.
Sources: Collect recording consent with tasks | LiveKit docs | How to detect when an agent has finished speaking | server-sdk-js | livekit/livekit_composite | DeepWiki