Can you please explain the livekit lifecycle i hav...
# ask-ai
a
Can you please explain the livekit lifecycle i have 2 methods on_exit and add_shutdown_callback ?
✅ 1
t
Thanks for reaching out, Manish. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’ll explain how the LiveKit agent/session lifecycle works and precisely when to use
on_exit
vs
add_shutdown_callback
. Quick lifecycle summary • An agent server registers with LiveKit and waits for job requests. • When a user joins a room, the server assigns a job to an agent; that job runs your entrypoint code (the session). • A session ends when the job completes or the room closes (for example, when the last non-agent participant leaves). • The framework supports two kinds of shutdown hooks for different purposes and timing: one for agent handoffs and one for final session cleanup. When to use each method • on_exit() ◦ Scope: agent-level hook during a multi-agent workflow inside the same session. ◦ When it runs: right before the agent gives control to another agent in the same session (an agent-to-agent handoff). ◦ Typical use: let the departing agent perform agent-specific final actions (e.g., send a final message to the user, save transient agent state needed by the next agent, or tidy up agent-scoped resources). ◦ Example pattern:
Copy code
async def on_exit(self):
        await self.session.generate_reply(instructions="Tell the user a friendly goodbye before you exit.")
• add_shutdown_callback() ◦ Scope: session/job-level hook for final cleanup after the whole session ends. ◦ When it runs: when the session/job is terminating (regardless of how it terminated). ◦ Typical use: persist user state to a database, upload logs/artifacts, or other cleanup that must run once the job is fully finished. ◦ Example pattern:
Copy code
async def entrypoint(ctx: JobContext):
        async def my_shutdown_hook():
            # save user state
            ...
        ctx.add_shutdown_callback(my_shutdown_hook)
Key differences (short) • Purpose:
on_exit
= prepare for handing off to another agent;
add_shutdown_callback
= final cleanup after session end. • Timing:
on_exit
runs during intra-session agent transitions; shutdown callbacks run when the job/session is ending. • Scope:
on_exit
affects the agent workflow; shutdown callbacks affect persistent/session-level cleanup. Practical notes • Keep shutdown hooks short: the framework waits about 10 seconds before forcefully terminating the process, so long-running tasks should be designed accordingly. • Both hooks are complementary: use
on_exit
for graceful handoff behavior and
add_shutdown_callback
for final persistence/cleanup. If you want, tell me: • whether you’re implementing a multi-agent workflow (so I can give a concrete example of agent handoff), or • what cleanup steps you need to perform at session end (so I can show a safe pattern for a shutdown callback). Sources: Server lifecycle | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation | Job lifecycle | LiveKit Documentation, with interactive elements
a
i want to generate the call summary and present to use before hanging up the call
t
Thanks — that’s clear. You want to generate a call summary and present it to the caller right before the call ends. You can do that using the agent lifecycle hooks in two main ways; I’ll highlight the recommended approach and give a concise example pattern you can adapt. Recommended approach • Use the agent-level hook
on_exit()
to generate and present the summary just before the agent exits the session or hands off. This runs while the session is still active, so you can speak the summary to the user. • After presenting the summary, call
session.shutdown(drain=True)
to ensure any spoken audio finishes playing before the session actually closes. Why this works •
on_exit()
is intended for agent-to-agent handoff or when an agent is leaving a session — exactly the right timing to say a final summary. •
add_shutdown_callback()
is for final session-level cleanup after the session finishes; it runs too late if you want the user to hear the summary before hangup. • Use
session.shutdown(drain=True)
so the framework waits for pending speech (the summary) to complete. Concise example pattern (pseudo/Python-style) 1. Inside your agent class, implement `on_exit()`:
Copy code
async def on_exit(self):
    # 1) Build a summary from the conversation history
    convo = self.session.history  # access history
    summary_prompt = f"Create a short call summary from this conversation:\n{convo}"

    # 2) Generate summary with your LLM method (example)
    summary = await self.session.generate_reply(instructions=summary_prompt)

    # 3) Present the summary to the user (speak/send)
    await self.session.say(summary)  # or generate_reply/send as appropriate

    # 4) Gracefully shutdown, waiting for speech to finish
    await self.session.shutdown(drain=True)
Notes and best practices • Keep the summary short so it finishes within the shutdown grace period. The runtime will only wait a short time (about 10s) before force-termination, so long runs should be avoided. • If summary generation might take longer, consider: ◦ Precomputing partial summaries during the call and finalizing in
on_exit()
, or ◦ Generating a short spoken summary in
on_exit()
and saving a fuller transcript/summary via
add_shutdown_callback()
for backend persistence. • If you prefer a ready-made tool, the EndCallTool can generate a closing response controlled by
end_instructions
and then end the call; it encapsulates the generate-then-shutdown flow if that fits your needs. If you want, tell me: • which API you use to generate LLM replies (so I can tailor the code snippet), and • whether you want the summary both spoken and posted to chat or saved to a database (so I can show the pattern for both). Sources: Pipeline nodes and hooks | LiveKit Documentation | EndCallTool | LiveKit Documentation | Agent Builder | LiveKit Documentation | Building multi-agent architectures with LiveKit agents | LiveKit | Agents and handoffs | LiveKit Documentation, with interactive elements
a
Will on_exit called even if User disconnects the call
t
Short answer: No —
on_exit()
is not called when the user disconnects unexpectedly. Use shutdown callbacks for cleanup and summary generation when a session ends. More detail and recommended pattern • Purpose difference (recap): ◦
on_exit()
runs only for agent-to-agent handoffs inside an active session. It’s meant for the departing agent to do final agent-scoped actions before control transfers. ◦
ctx.add_shutdown_callback()
registers a session/job-level callback that runs when the session ends, regardless of how it ends (user disconnects, manual shutdown, job completion). • What happens on user disconnect: ◦ If the user drops or leaves the call, the session can close automatically (depending on room options). ◦
on_exit()
will not be invoked for that event. ◦ Any callbacks added with
ctx.add_shutdown_callback()
will run — this is where you should do final persistence, reporting, and any non-interactive summary generation. How to present a summary to the caller and still handle disconnects • If you want the caller to hear a spoken summary before hangup: ◦ Generate and speak the summary from
on_exit()
only when an agent is intentionally exiting while the session is still active (e.g., you are handing off but the user remains). ◦ After speaking, call
session.shutdown(drain=True)
so the system waits for speech to finish before disconnecting. • If the user might disconnect unexpectedly: ◦ Use
ctx.add_shutdown_callback()
to generate the summary and store/send it (database, webhook, email, etc.). Media output may not be possible if the user already left, but the summary will still be generated and persisted. ◦ To improve chances the user hears a summary before disconnect, consider generating partial/near-real-time summaries during the call (e.g., every N minutes or after key moments) so a short final summary is cheap to produce in the shutdown path. Practical patterns • Best-effort spoken summary + reliable persistence: a. During the call, keep lightweight rolling summary state (update every few minutes). b. On planned agent exit: in
on_exit()
assemble a short spoken summary from the rolling state,
session.say()
or
session.generate_reply()
, then
await session.shutdown(drain=True)
. c. Always register a
ctx.add_shutdown_callback()
to persist the full summary and transcript to your backend (so you don’t lose it if the user disconnects). • If you prefer a tool-based approach: the EndCallTool can generate a closing message from
end_instructions
and handle the shutdown flow for you. If you like, I can: • Show a concrete code snippet combining a short spoken summary in
on_exit()
with a persistent save in
add_shutdown_callback()
, or • Help design a rolling-summary approach that minimizes final-generation time so you can reliably speak a summary before hangup. Which would you prefer? Sources: Pipeline nodes and hooks | LiveKit Documentation | Job lifecycle | LiveKit Documentation | Why is my Agent insights data missing? | LiveKit | Agent session | LiveKit Documentation | Agent Builder | LiveKit Documentation, with interactive elements
a
My usecase is that after call ends the i want to use agent llm to generate the summary and store in DB
t
Thanks — that use case fits cleanly into the session shutdown flow. Below I’ll show a concise pattern you can implement: register a shutdown callback with
ctx.add_shutdown_callback()
that (1) builds the conversation prompt from
session.history
, (2) calls the agent/LLM to generate a summary, and (3) persists that summary to your database. Why use a shutdown callback •
add_shutdown_callback()
runs when the session/job ends (including user disconnects), so your summary will be generated and saved even if media output is no longer possible. • Do not rely on
on_exit()
here, because it is only called for agent-to-agent handoffs and not on user disconnect. Example pattern (Python-style pseudocode)
Copy code
async def entrypoint(ctx: JobContext):
    session = ctx.session

    async def persist_summary():
        # 1) Build a concise prompt from conversation history
        convo = session.history  # access recorded messages/events
        prompt = f"Generate a short call summary (3-4 bullet points) from this conversation:\n{convo}"

        # 2) Generate summary via agent/LLM
        # Use the session/agent LLM call available in your runtime
        summary_resp = await session.generate_reply(instructions=prompt)
        summary_text = summary_resp.text if hasattr(summary_resp, "text") else str(summary_resp)

        # 3) Persist to DB
        # Replace with your DB client/ORM call
        await save_summary_to_db(
            call_id=session.call_id,
            summary=summary_text,
            generated_at=datetime.utcnow(),
        )

    # Register the shutdown callback so it runs when the session ends
    ctx.add_shutdown_callback(persist_summary)

    # ... your normal session logic here ...
Practical notes and best practices • Keep the shutdown task reasonably short. The runtime has a graceful timeout (about 10 seconds) before forcefully terminating. If your DB write or LLM call may take longer: ◦ Make the shutdown callback enqueue a background job (e.g., push to a queue like Redis/RabbitMQ or a serverless function) and return quickly. ◦ Or precompute a partial summary during the call and finalize it in shutdown. • If the user disconnects abruptly, media output won’t be possible but the summary generation and DB persistence will still run. • Use structured summary prompts (e.g., ask for bullets, key decisions, action items) to make the stored summary more useful downstream. • Consider storing both the summary text and metadata (call_id, participants, timestamps, prompt used, model/version) for traceability. • If you want higher reliability, persist a rough draft periodically (every N minutes) during the call so the final shutdown step only needs to refine and store the final version. If you want, I can: • Provide a concrete example for a specific database (Postgres with asyncpg or SQLAlchemy, MongoDB, etc.). • Show how to offload longer tasks to a background queue within the shutdown callback. Which database or persistence method are you using?, with interactive elements