happy-fall-6105
02/24/2026, 1:17 PMtall-airline-16756
02/24/2026, 1:17 PMtall-airline-16756
02/24/2026, 1:17 PMctx.add_shutdown_callback.
You can register as many shutdown callbacks as you need by calling ctx.add_shutdown_callback once per function. Each registered callback will be executed when the session shuts down (they run asynchronously). The framework waits up to 10 seconds by default for callbacks to finish.
Example:
async def entrypoint(ctx: JobContext):
async def cleanup_one():
<http://logger.info|logger.info>("Performing cleanup task one")
# cleanup logic here
async def cleanup_two():
<http://logger.info|logger.info>("Performing cleanup task two")
# cleanup logic here
ctx.add_shutdown_callback(cleanup_one)
ctx.add_shutdown_callback(cleanup_two)
If you expect callbacks might take longer than 10 seconds, or you need ordering guarantees, tell me which behavior you need and I can suggest patterns (for example, a single wrapper callback that awaits multiple tasks in sequence or with a timeout).
Sources: Job lifecycle | LiveKit Documentationhappy-fall-6105
02/24/2026, 1:20 PMtall-airline-16756
02/24/2026, 1:21 PMctx.add_shutdown_callback take longer than the default 10 seconds. Good question — here are practical options you can use depending on how critical the work is.
1) Increase the shutdown callback timeout
• Configure the agent server with a longer shutdown_process_timeout. Example:
server = AgentServer(
shutdown_process_timeout=60, # increase to 60 seconds
)
Use this when you need more time for callbacks to finish cleanly.
2) Keep callbacks short and offload heavy work
• Make the shutdown callback perform only quick, essential actions (persist critical state, mark job as finishing).
• Offload longer tasks to external systems (enqueue a job, call a background worker API, write a DB flag that a worker will pick up).
3) Fire-and-forget within the callback
• If the work is non-critical, start an asynchronous background task without awaiting it:
async def shutdown_hook():
asyncio.create_task(long_running_cleanup())
save_critical_state()
This returns quickly but note it won't guarantee completion before process exit unless you also increase the timeout.
4) Use a single wrapper callback to control ordering or parallelism
• If you need ordered or coordinated cleanup, register one shutdown callback that awaits multiple tasks in sequence or concurrently with a timeout:
async def wrapper():
await asyncio.gather(
asyncio.wait_for(cleanup_one(), 10),
asyncio.wait_for(cleanup_two(), 10),
)
ctx.add_shutdown_callback(wrapper)
This lets you enforce per-task timeouts and control total duration.
5) Consider process-level graceful-termination settings for long conversations
• For use cases such as voice or long-running sessions, extend the deployment’s termination grace period (e.g., Kubernetes terminationGracePeriodSeconds) so active jobs can finish before shutdown hooks run. The framework also supports draining active jobs for longer periods (drain_timeout) separate from the 10s shutdown callback allowance.
If you tell me:
• whether the long work is critical to complete before exit, and
• your deployment environment (local, Kubernetes, or other),
I can recommend a concrete pattern and show the exact code or server option changes to implement it.
Sources: Job lifecycle | LiveKit Documentation | Self-hosted deployments | LiveKit Documentation | Server options | LiveKit Documentationhappy-fall-6105
02/24/2026, 2:17 PMtall-airline-16756
02/24/2026, 2:17 PMNone or an empty payload) so the agent has nothing to echo back.
• Or implement the tool to perform its action but not emit any messages that the agent will surface.
• If you need ordering or additional control, wrap the function in a tool that explicitly suppresses agent output.
3) Combine silent tools with LLM instructions
• Add instruction-level guidance to the agent’s system prompt: tell the model not to produce any output after specific tool calls.
• This reduces the risk of the LLM generating commentary even if a tool returns something.
4) For critical guarantees (best-effort)
• Use the Silent tool configuration as the primary control point.
• As a fallback, have the tool start any long-running work as fire-and-forget and return quickly (or enqueue a job), so the agent has nothing to respond to.
If you tell me:
• whether you’re using the Agent Builder UI or the SDK, and
• whether the silence must be guaranteed (no spoken/text response ever) or just “no immediate acknowledgement,”
I’ll give exact configuration steps or a small code example tailored to your setup.
Sources: Agent Builder | LiveKit Documentation | Agent speech and audio | LiveKit Documentation