I have a Livekit project and I have 2 agents. I wa...
# ask-ai
a
I have a Livekit project and I have 2 agents. I want to deploy only on agent when a room is created based on conditions programatically. How do I do that?
t
Thanks for reaching out, Sanjeev. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
To deploy a specific agent programmatically when a room is created in LiveKit, you can use explicit agent dispatch. This gives you full control over when and how agents join rooms. Here’s a friendly walkthrough on how to do it: • Using the Agent Dispatch API: You can explicitly dispatch an agent to a room using the
AgentDispatchService
. Here’s an example:
Copy code
import asyncio
from livekit import api

room_name = "my-room"
agent_name = "test-agent"

async def create_explicit_dispatch():
    lkapi = api.LiveKitAPI()
    dispatch = await lkapi.agent_dispatch.create_dispatch(
        api.CreateAgentDispatchRequest(
            agent_name=agent_name, 
            room=room_name, 
            metadata='{"user_id": "12345"}'
        )
    )
    print("created dispatch", dispatch)
    
    dispatches = await lkapi.agent_dispatch.list_dispatch(room_name=room_name)
    print(f"there are {len(dispatches)} dispatches in {room_name}")
    
    await lkapi.aclose()

asyncio.run(create_explicit_dispatch())
Using Room Configuration in Access Tokens: Another way is to dispatch an agent by configuring the room in the access token when a participant connects. Check this out:
Copy code
from livekit.api import (
    AccessToken,
    RoomAgentDispatch,
    RoomConfiguration,
    VideoGrants,
)

room_name = "my-room"
agent_name = "test-agent"

def create_token_with_agent_dispatch() -> str:
    token = (
        AccessToken()
        .with_identity("my_participant")
        .with_grants(VideoGrants(room_join=True, room=room_name))
        .with_room_config(
            RoomConfiguration(
                agents=[RoomAgentDispatch(agent_name=agent_name, metadata='{"mydata": "myvalue"}')],
            ),
        )
        .to_jwt()
    )
    return token
Just a couple of things to keep in mind: • Make sure to set the
agent_name
in Worker Options when registering the agent. This turns off automatic dispatch and lets you take explicit control. • Agents need to be registered with the LiveKit server and set up to accept explicit dispatches. Explicit dispatch is great because it lets you decide exactly when agents join rooms—perfect for customizing your workflows. If you want me to walk you through anything else or share more examples, just let me know! Sources: Dispatching agents | LiveKit Docs | An open source stack for real-time multimodal AI
a
I have one project and 2 agents. When a call Comes in I want to deploy one agent automatically (and not the second) and then as part of that Agents code create a new room and deploy the other agent (without the first). CAn you show me the best way to do this
t
To help you deploy one agent automatically when a call comes in, and then create a new room programmatically to deploy another agent, here’s a friendly step-by-step guide: • Step 1: Automatic Agent Dispatch for the First Agent ◦ LiveKit automatically dispatches an agent when a room is created—pretty handy, right? Just make sure your first agent is set up to handle this automatic dispatch. ◦ So, when someone connects to a room that doesn’t exist yet, the room gets created, and the first agent is assigned automatically. • Step 2: Programmatically Create a New Room and Deploy the Second Agent ◦ In your first agent’s code, you’ll want to explicitly create a new room and assign the second agent to it. Here’s a simple example to get you started: `import asyncio from livekit import api room_name = "new-room" agent_name = "second-agent" async def create_explicit_dispatch(): lkapi = api.LiveKitAPI() dispatch = await lkapi.agent_dispatch.create_dispatch( api.CreateAgentDispatchRequest( agent_name=agent_name, room=room_name, metadata="custom_metadata" ) ) print("created dispatch", dispatch) dispatches = await lkapi.agent_dispatch.list_dispatch(room_name=room_name) print(f"there are {len(dispatches)} dispatches in {room_name}") await lkapi.aclose() asyncio.run(create_explicit_dispatch())`This snippet creates a new dispatch so your second agent can join the new room. Pretty neat, right? • Step 3: Configure Agents for Explicit Dispatch ◦ Set the
agent_name
in Worker Options for both agents. This helps prevent the second agent from being dispatched automatically, giving you full control. ◦ Also, make sure each agent has a unique
agent_name
to keep things running smoothly without conflicts. • Step 4: Manage Deployments ◦ Use handy LiveKit CLI commands like
lk agent create
and
lk agent deploy
to manage your agent deployments easily. With this approach, your first agent gets deployed automatically, and the second agent is programmatically deployed into a new room created by the first agent. If you want me to walk you through any part of this or share more examples, just let me know—I’m here to help! Sources: LiveKit CLI | livekit/livekit_composite | DeepWiki | Dispatching agents | LiveKit Docs
a
If I set agent_name in both how will first agent get deployed automatically?
t
When you set agent_name in the Worker Options, it turns off automatic agent dispatch. So, agents won’t be assigned to rooms automatically—you’ll need to assign them yourself using explicit dispatch. If you want the first agent to be assigned automatically but still want to use explicit dispatch for the second agent, just don’t set agent_name for the first one. This way, the first agent gets dispatched automatically when a room is created. For the second agent, you can set agent_name so you can assign them programmatically to a new room. Feel free to ask if you want me to walk you through this with some examples or if anything’s unclear! Sources: Dispatching agents | LiveKit Docs | Agents telephony integration | LiveKit Docs
a
When I create a the second Room and specify agent name - Will not deploy the first agent also which has no Name?
t
When you create a new room and specify an agent name for explicit dispatch, only that particular agent will be sent to the room. Agents without a name won’t be automatically assigned because explicit dispatch turns off automatic dispatch for that room. So, if you want to make sure only the second agent gets deployed to the new room, just include their name in the dispatch request. The first agent, who doesn’t have a name, won’t be assigned automatically. Feel free to reach out if you want me to walk you through this or if you have any other questions! Sources: Dispatching agents | LiveKit Docs | Agent dispatch | LiveKit Docs | packages/livekit-server-sdk/src/AgentDispatchClient.ts | Authentication | LiveKit Docs | Authentication | LiveKit Docs
a
I have 2 Agents defined in 2 files both registering to teh same livekit project. First File qual_agent.py with agent defined as follows. This is triggered When a call is recieved: if name == "__main__": cli.run_app(WorkerOptions( entrypoint_fnc=entrypoint, prewarm_fnc=prewarm, port=7881, shutdown_process_timeout=240.0 ) ) Second agent is defined as in agent.py: if name == "__main__": cli.run_app( WorkerOptions( entrypoint_fnc=entrypoint, agent_name="outbound-caller", ) ) In the first Agent (qual_aget.py) I do teh following: lkapi = api.LiveKitAPI() new_room_name = f"room_{uuid.uuid4().hex[:8]}" response = await lkapi.agent_dispatch.create_dispatch( api.CreateAgentDispatchRequest( room=new_room_name, agent_name="outbound-caller", metadata=metadata ) ) The new Room created - I get this error - {"message": "error creating SIP participant: twirp error unknown: unexpected status from INVITE response: sip status: 482 (LOOP_DETECTED), SIP status: 482 SIP_STATUS_LOOP_DETECTED", "level": "ERROR", "name": "outbound-caller", "pid": 37389, "job_id": "AJ_oTokhrtG2hwr", "timestamp": "2025-08-17T234437.297686+00:00"}
t
The error you’re seeing might be related to how your SIP routing or configuration is set up. If you could share a bit more about your SIP trunk setup, dispatch rules, or any specific settings around creating the SIP participant, that would be super helpful. It’ll make it easier to pinpoint what’s going on and get this sorted out for you. Sources: CHANGELOG.md | CHANGELOG.md | agent.py
a
Inbound Trunk: { "sipTrunkId": "ST_YYBW4eVVVVV", "name": "Twilio trunk", "numbers": [ "+1949771CCC" ], "headersToAttributes": { "x-User-to-User": "call-data" }, "krispEnabled": true } Dispatch Rule: { "sipDispatchRuleId": "SDR_rtHVVVVV", "rule": { "dispatchRuleIndividual": { "roomPrefix": "call" } }, "trunkIds": [ "ST_YYBW4eKVVVVV" ], "name": "twilio Dispatch Rule" } Outbound Trunk: { "sipTrunkId": "ST_s5HriNt4y9bs", "name": "Twilio_Trunk", "address": "maqualsdsfdh.pstn.twilio.com", "transport": "SIP_TRANSPORT_TCP", "numbers": [ "+1628767867876" ], "authUsername": "", "authPassword": "" }
t
The SIP status 482 (LOOP_DETECTED) error usually pops up because there’s a routing loop in your SIP setup. Don’t worry, though! Let’s walk through some key things to check and fix: • Dispatch Rule Configuration: Make sure your dispatch rules are set up correctly to avoid any circular routing. For instance, using
dispatchRuleIndividual
with a unique
roomPrefix
helps create unique rooms for each call dynamically:
Copy code
{
  "rule": {
    "dispatchRuleIndividual": {
      "roomPrefix": "call-"
    }
  },
  "trunkIds": [
    "ST_YYBW4eKVVVVV"
  ],
  "name": "twilio Dispatch Rule"
}
Trunk Address Configuration: Double-check that your inbound and outbound trunk addresses aren’t causing any loops. Here’s an example:
Copy code
{
  "outbound_address": "<http://maqualsdsfdh.pstn.twilio.com|maqualsdsfdh.pstn.twilio.com>",
  "inbound_addresses": [],
  "outbound_number": "+1628767867876",
  "inbound_numbers": [
    "+1949771CCC"
  ]
}
Room Existence and Naming: Make sure the room mentioned in your dispatch rule actually exists and is valid. If you’re using
dispatchRuleDirect
, the room needs to be there already to prevent re-routing issues. • Authentication Configuration: If your trunks require authentication, double-check that the credentials are spot on to avoid any misrouting. • Monitor Call Flow: Take a look at your LiveKit logs to spot any routing patterns that might hint at loops. This can really help pinpoint where things are going in circles. Tackling these areas should help clear up that 482 error caused by routing loops. If you want, I’m here to help with more details or examples tailored to your setup—just let me know! Sources: livekit/livekit_sip.pb.go | SIP overview | LiveKit Docs | README.md | src/setup-livekit.ts | Create and configure SIP trunk | LiveKit Docs
a
I think the problem is that the default agent is also getting deployed when i trigger the 2nd room
t
To make sure the default agent doesn’t jump in when you’re explicitly sending another agent to a new room, here’s what you’ll want to do: • Set
agent_name
in WorkerOptions:
When you specify the
agent_name
in the
WorkerOptions
, it stops the automatic agent dispatch. This way, the default agent won’t automatically show up in new rooms. For example:
Copy code
opts = WorkerOptions(
    ...
    agent_name="test-agent",
)
Use Explicit Dispatch: Go ahead and explicitly dispatch the agent you want to the new room using the
AgentDispatchClient
. Here’s how:
Copy code
lkapi = api.LiveKitAPI()
dispatch = await lkapi.agent_dispatch.create_dispatch(
    api.CreateAgentDispatchRequest(
        agent_name="outbound-caller", room="new-room", metadata="{\"user_id\": \"12345\"}"
    )
)
Optional: Configure Room in Access Token: If you’d like the agent to be dispatched right when a participant joins, you can set up the room in the access token. For example:
Copy code
token = (
    api.AccessToken()
    .with_identity("my_participant")
    .with_grants(api.VideoGrants(room_join=True, room="new-room"))
    .with_room_config(
        api.RoomConfiguration(
            agents=[
                api.RoomAgentDispatch(agent_name="outbound-caller", metadata="{\"user_id\": \"12345\"}")
            ],
        ),
    )
    .to_jwt()
)
So, by setting agent_name for your second agent and dispatching explicitly, you’re all set to have just the agent you want in the new room—no surprises from the default agent. If you want to chat more or need a hand with anything else, just let me know!
a
CAn uyou specify a agent when rooms are created?
t
Yes, you can totally specify an agent when creating rooms in LiveKit by using explicit dispatch methods. Here’s a friendly walkthrough for you: • Explicit Agent Dispatch via API: You can use the
AgentDispatchService
to dispatch an agent to a room. Here’s a quick example:
Copy code
async def create_explicit_dispatch():
    lkapi = api.LiveKitAPI()
    dispatch = await lkapi.agent_dispatch.create_dispatch(
        api.CreateAgentDispatchRequest(
            agent_name="outbound-caller", room="new-room", metadata="{\"user_id\": \"12345\"}"
        )
    )
    print("created dispatch", dispatch)
Room Configuration in Access Token: When creating a room, you can include the agent configuration right in the access token. Check this out:
Copy code
def create_token_with_agent_dispatch() -> str:
    token = (
        api.AccessToken()
        .with_identity("my_participant")
        .with_grants(api.VideoGrants(room_join=True, room="new-room"))
        .with_room_config(
            api.RoomConfiguration(
                agents=[api.RoomAgentDispatch(agent_name="outbound-caller", metadata="{\"user_id\": \"12345\"}")],
            ),
        )
        .to_jwt()
    )
    return token
Just a few things to keep in mind: • Set
agent_name
in Worker Options if you want to turn off automatic dispatch and take explicit control. • Explicit dispatch makes sure that only the agent you specify gets assigned to the room. • You can pass metadata during dispatch to share extra context — super handy! If you want me to walk you through anything else or need more examples, just let me know. I’m here to help! Sources: Dispatching agents | LiveKit Docs | Agent dispatch | LiveKit Docs
a
CAn you havd 2 agents in teh same project?
t
""" Absolutely, you can have multiple agents within the same LiveKit project! The LiveKit Agents framework is designed to let you create as many agents as you need, each with their own unique roles and responsibilities. This way, you can easily manage different tasks and workflows all in one place. For instance, you can give each agent a unique agent_name in their WorkerOptions, which helps you keep their roles clear and lets you send them to specific rooms or tasks whenever you want. If you'd like, I’m here to help you set up or manage multiple agents in your project—just let me know! """
a
I wam writing a server side webhook in node.js to listen to room_cfreated event and explicitly dispatch qualifuer named agent based on metadata. CAn you give me the code?
t
Here is an example Node.js code to create a server-side webhook that listens for the room_created event and explicitly dispatches an agent based on metadata:
Copy code
const { AgentDispatchClient, WebhookReceiver } = require('livekit-server-sdk');
const http = require('http');

// Webhook server setup
const host = 'localhost';
const port = 3000;

// Initialize WebhookReceiver with your API credentials
const receiver = new WebhookReceiver(
  process.env.LIVEKIT_API_KEY,
  process.env.LIVEKIT_API_SECRET
);

// Agent dispatch client
const agentDispatchClient = new AgentDispatchClient(process.env.LIVEKIT_URL);

// Create HTTP server
const server = http.createServer(async (req, res) => {
  let data = '';
  req.on('data', (chunk) => {
    data += chunk;
  });

  req.on('end', async () => {
    try {
      // Parse the webhook event
      const event = receiver.receive(data, req.headers.authorization);

      // Handle room_created event
      if (event.event === 'room_created') {
        const roomName = event.data.room.id;

        // Explicitly dispatch agent to the newly created room
        const agentName = 'your-agent-name'; // Replace with your agent name
        const dispatch = await agentDispatchClient.createDispatch(
          roomName,
          agentName,
          {
            metadata: '{"room_created": "true"}', // Optional metadata
          }
        );

        console.log(`Dispatched agent ${agentName} to room ${roomName}`);
        console.log('Dispatch response:', dispatch);
      }

      res.writeHead(200);
      res.end();
    } catch (error) {
      console.error('Error processing webhook:', error);
      res.writeHead(500);
      res.end();
    }
  });
});

server.listen(port, host, () => {
  console.log(`Webhook server running on <http://localhost:${port}`>);
});
Steps to Set Up: • Set the following environment variables in your
.env
file:
Copy code
LIVEKIT_URL=<YOUR_LIVEKIT_SERVER_URL>
LIVEKIT_API_KEY=<YOUR_API_KEY>
LIVEKIT_API_SECRET=<YOUR_API_SECRET>
• Install the required packages:
npm install livekit-server-sdk dotenv
• Make sure you have an agent worker running with the
agent_name
matching the
agentName
variable in the code. Notes: • Don't forget to replace
'your-agent-name'
with your actual agent's name. • For production, it's a good idea to secure your webhook server with authentication and HTTPS. • You can use the
metadata
field to send extra info to the agent if needed. If you have any questions or want me to help tweak this further, just let me know! I'm here to help.
a
can you make the above an azure function?
t
To create an Azure Function that listens to the room_created event and dispatches an agent in LiveKit, here’s a friendly step-by-step guide to help you get started: Step 1: Configure LiveKit WebhookFor LiveKit Cloud: ◦ Head over to your project dashboard, then go to Settings > Webhooks, and add your Azure Function URL as a new webhook endpoint. ◦ Make sure to select the
ROOM_CREATED
event and save your settings. • For Self-Hosted LiveKit: ◦ Update the
webhook
section in your
livekit.yaml
file like this:
Copy code
webhook:
  api_key: 'your-api-key'
  urls:
    - <YOUR_AZURE_FUNCTION_URL>/api/webhook-endpoint
◦ Don’t forget to restart your LiveKit server to apply these changes. Step 2: Create an Azure Function Here’s a simple example of an Azure Function that listens for the webhook and dispatches an agent when a room is created:
Copy code
const { WebhookReceiver, AgentDispatchClient } = require('livekit-server-sdk');

module.exports = async function (context, req) {
  try {
    const receiver = new WebhookReceiver(
      process.env.LIVEKIT_WEBHOOK_API_KEY,
      process.env.LIVEKIT_WEBHOOK_API_SECRET
    );

    // Validate the webhook event
    const event = receiver.receive(req.body, req.headers.authorization);

    // Handle room_created event
    if (event.event === 'room_created') {
      const roomName = event.data.room.id;

      // Initialize AgentDispatchClient
      const agentDispatchClient = new AgentDispatchClient(
        process.env.LIVEKIT_URL,
        process.env.LIVEKIT_API_KEY,
        process.env.LIVEKIT_API_SECRET
      );

      // Dispatch an agent to the newly created room
      const metadata = '{"context": "room_created"}';
      const dispatch = await agentDispatchClient.createDispatch(roomName, 'my-agent', { metadata });

      context.log(`Dispatched agent to room: ${roomName}`);
      context.res = {
        status: 200,
        body: `Agent dispatched to room ${roomName}`
      };
    } else {
      context.res = {
        status: 200,
        body: 'Event not handled'
      };
    }
  } catch (error) {
    context.log.error('Error processing webhook:', error);
    context.res = {
      status: 500,
      body: `Error: ${error.message}`
    };
  }
};
Step 3: Set Environment Variables • Make sure to set these environment variables in your Azure Function’s configuration:
Copy code
LIVEKIT_URL=<YOUR_LIVEKIT_SERVER_URL>
LIVEKIT_API_KEY=<YOUR_API_KEY>
LIVEKIT_API_SECRET=<YOUR_API_SECRET>
LIVEKIT_WEBHOOK_API_KEY=<YOUR_WEBHOOK_API_KEY>
LIVEKIT_WEBHOOK_API_SECRET=<YOUR_WEBHOOK_API_SECRET>
Step 4: Deploy and Test • Deploy your Azure Function and set the webhook URL in LiveKit. • Create a room to trigger the
ROOM_CREATED
event and check that your agent gets dispatched smoothly. Quick Tips: • Swap out
'my-agent'
with your agent’s actual name. • Ensure your webhook server uses HTTPS and proper authentication for security. • You can use the
metadata
field to send extra context to your agent. If you have any questions or want to tweak this setup, just let me know — I’m here to help! Sources: Webhooks | LiveKit Docs | livekit/livekit_analytics.pb.go | Dispatching agents | LiveKit Docs
a
Hello - How to stop and disconnect an agent but leave the romm open with 2 hu,man users still talking?
t
To disconnect an agent from a LiveKit room but keep the room open for your human users, you can simply use the ctx.shutdown() method inside the agent's entrypoint function. This lets the agent leave smoothly without closing the room or disturbing anyone else. Example (Python):
Copy code
async def entrypoint(ctx: JobContext):
    # Perform agent work
    ...

    # Disconnect the agent from the room
    ctx.shutdown(reason="Agent task completed")
Key Points:Room Continuity: Using
ctx.shutdown()
disconnects the agent but keeps the room alive until the last human participant leaves. • Alternative: You could also use the
RemoveParticipant
API to kick out the agent, but
ctx.shutdown()
is usually the nicer, more graceful way to go. • Egress Consideration: If you’re recording the session, just double-check that egress is handled properly when everything wraps up. Feel free to reach out if you want more examples or have any questions — I’m here to help! Sources: Inside a session | LiveKit Docs | Job lifecycle | LiveKit Docs | Anatomy of an Agent | LiveKit Docs | Managing participants | LiveKit Docs
a
ctx.shutdown is closing the room
t
The ctx.shutdown method is here to help you disconnect the agent from the room without shutting down the room for everyone else. If you notice the room closing right when you call ctx.shutdown, it usually means there aren’t any other non-agent participants left in the room—because LiveKit automatically closes a room once the last non-agent participant leaves. Key Points:Agent Disconnection:
ctx.shutdown
just disconnects the agent and runs any shutdown hooks you’ve set up. It won’t close the room unless no one else is there. • Room Closure: If you want to close a room on purpose, you’ll need to use the server API
deleteRoom
. This isn’t done by
ctx.shutdown
. • Automatic Room Closure: LiveKit closes a room automatically when the last non-agent participant leaves. If the room is closing when you didn’t expect it to, it’s a good idea to double-check that there are still active non-agent participants in the room when you call ctx.shutdown. And of course, feel free to reach out if you want a hand with anything else! Sources: Inside a session | LiveKit Docs | Managing rooms | LiveKit Docs | Anatomy of an Agent | LiveKit Docs | Worker lifecycle | LiveKit Docs | Job lifecycle | LiveKit Docs
a
This is working now However, The session is ending because it says agent did not join
t
The message "agent did not join" pops up when the agent can’t connect to the room or doesn’t finish initializing within the usual timeout period (usually 10-20 seconds). No worries—here’s how you can fix it: • Verify Agent Name: Double-check that the
agent_name
is correctly set both when creating the agent (using
WorkerOptions
) and in the dispatch rule (
RoomAgentDispatch
). • Check Agent Registration: Make sure the agent worker is properly registered with the LiveKit server and is in standby mode, ready to take on job requests. • Review Initialization Logic: Confirm that the agent moves from the "connecting" state to an active state (like "listening") within the timeout. The agent is only considered available if it’s in "listening," "thinking," or "speaking" state. If you’re still running into trouble, just let me know—I’m here to help you troubleshoot your setup! Sources: SIP troubleshooting guide | LiveKit Docs | Worker lifecycle | LiveKit Docs | complex-agents/role-playing/role_playing_frontend/components/session-view.t... | components/session-view.tsx
a
I want the agent to disconect when another human joins. The human joined and We can speak but Session si ending
t
To disconnect an agent when another human joins a LiveKit room without ending the session, you can use the ctx.shutdown() method inside the participant_connected event listener. Here’s a friendly example to help you get started:
Copy code
import asyncio
from livekit import agents, rtc, JobContext, AutoSubscribe

async def entrypoint(ctx: JobContext):
    room = ctx.room
    agent_shutdown_initiated = False  # To make sure we don't trigger shutdown multiple times

    async def safe_shutdown(reason: str):
        nonlocal agent_shutdown_initiated
        if agent_shutdown_initiated:
            return
        agent_shutdown_initiated = True
        ctx.shutdown(reason=reason)

    @room.on("participant_connected")
    def on_participant_connected(participant: rtc.Participant):
        # If we're already shutting down, no need to do anything
        if agent_shutdown_initiated:
            return

        # Ignore non-human participants (like ingress, egress, or agents)
        if participant.kind != rtc.ParticipantKind.PARTICIPANT_KIND_STANDARD:
            return

        # Skip if participant disconnected due to duplicate identity
        if participant.disconnect_reason == rtc.DisconnectReason.DUPLICATE_IDENTITY:
            return

        # When a human joins, disconnect the agent gracefully
        print(f"Human participant {participant.identity} joined. Agent exiting.")
        asyncio.create_task(safe_shutdown("Human participant joined"))

    # Connect with some error handling to keep things smooth
    try:
        await ctx.connect(auto_subscribe=AutoSubscribe.AUDIO_ONLY)
    except Exception as e:
        print(f"Oops, failed to connect agent to room: {e}")
        ctx.shutdown(reason="Connection failed")
What’s going on here: • Smart Human Detection: We check for
PARTICIPANT_KIND_STANDARD
to spot real human participants and skip system-related disconnects like
DUPLICATE_IDENTITY
. • One-Time Shutdown: A simple flag keeps
ctx.shutdown()
from running more than once, even if multiple people join quickly. • Non-Blocking Shutdown: Using
asyncio.create_task()
keeps the event loop happy while shutting down. Just a couple of friendly reminders: • The room stays open as long as there’s at least one
STANDARD
or
INGRESS
participant around. • You might want to add some cleanup steps with
ctx.add_shutdown_callback()
to tidy things up after disconnecting. If you want me to walk you through anything else or tweak this example, just let me know! I'm here to help. Sources: Connecting to LiveKit | LiveKit Docs | Inside a session | LiveKit Docs | agent/game_host.py | Managing participants | LiveKit Docs | Job lifecycle | LiveKit Docs
a
I am connected through teh sandbox. But As soon as agent gets disconnected - The Sandbox session ends
t
In LiveKit Sandbox, disconnecting an agent using ctx.shutdown() shouldn’t end the session or room—unless there aren’t any other non-agent participants still around. If you’re seeing the session end when the agent disconnects, it might mean the Sandbox environment is treating the agent as a required participant. Key Points: • Agent Disconnection: Use
ctx.shutdown(reason="Session ended")
to disconnect the agent but keep the room active for others. • Room Continuity: Make sure there are still active non-agent participants in the room when the agent leaves, since LiveKit closes the room automatically once the last non-agent participant exits. • Sandbox Behavior: The Sandbox might be set up to require an agent to stay connected. If that’s the case, you might want to tweak your workflow or try testing in an environment closer to production. If you’re still running into issues, just let me know—I’m here to help you debug your setup! Sources: Worker lifecycle | LiveKit Docs | examples/voice_agents/realtime_joke_teller.py