We have an intermittent inbound SIP issue with Liv...
# ask-ai
b
We have an intermittent inbound SIP issue with LiveKit Agents in Python. Observed behavior: • Some calls work normally: user speech becomes conversation_item_added(role='user'), then Transcript [user/final], then LLM response. • Some calls fail: the agent greeting and inactivity prompts speak normally, but there is no user conversation_item_added, no Transcript [user/final], and the call ends with llm_calls=0, stt_calls=0, tts_calls=5. • STT is constructed successfully in both cases. • We are using AgentSession.start(..., room=ctx.room, room_input_options=room_options). • We separately discover the SIP participant from room.remote_participants, but we are not explicitly pinning the AgentSession input to that SIP participant. • The failures are intermittent, not permanent. Questions: 1. In Python Agents, if participant_identity is not explicitly set in RoomOptions/RoomInputOptions, does AgentSession/RoomIO bind to the first available remote participant by default? 2. Can that default participant-linking behavior be timing/order dependent for SIP calls, causing intermittent “no inbound STT/user transcript” failures? 3. Is explicit participant binding to the SIP participant identity considered the recommended approach / best practice for telephony agents? 4. In current Python Agents, what is the preferred way to do that: RoomOptions(participant_identity=...), RoomIO.set_participant(...), or something else? 5. Is RoomInputOptions deprecated enough that we should migrate to RoomOptions for this exact reason? If possible, please answer with the exact expected behavior of participant linking in current Python Agents.
✅ 1
t
Thanks for reaching out, Patrick. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — this is a clear report and a helpful set of questions. I’ll respond to each question directly and then give a short recommendation you can apply immediately to remove the timing-dependent behavior. Acknowledgement • I’m responding to the five questions about how Participant linking works for Python Agents (AgentSession / RoomIO), whether the default is timing/order dependent for SIP callers, best practice for telephony agents, how to explicitly bind a participant in current Python Agents, and whether you should migrate from RoomInputOptions to RoomOptions. Answers 1) Default binding behavior (when you do NOT set participant_identity) • The AgentSession / RoomIO will link to the “first participant” it chooses in the room by default. Agents auto-subscribe to eligible participant kinds (standard RTC and SIP participants), and if you do not explicitly set a participant identity the agent will use whatever participant the session links to (effectively the first participant it establishes for input). 2) Is that default behavior timing/order dependent for SIP calls? • Yes. Because the session links to whichever participant is selected when the RoomIO is created/started, race conditions between the SIP caller joining and the agent starting can make the linked participant change between calls. That timing/order dependency can produce intermittent cases where the agent is not linked to the SIP caller — resulting in no user conversation_item_added, no user final transcript, and 0 STT/LLM calls even though TTS runs (agent greetings/inactivity still played). This matches the intermittent failures you described. 3) Is explicit participant binding recommended for telephony agents? • Yes. Explicitly binding the AgentSession / RoomIO input to the SIP participant identity is the recommended best practice for telephony/one-to-one caller scenarios. This avoids race conditions in multi-participant rooms and ensures the agent always processes the intended caller’s audio. 4) Preferred current method in Python Agents to bind explicitly • Use RoomOptions(participant_identity=...) when starting the session — this is the recommended and straightforward approach for new code. Alternative options: ◦ RoomOptions(participant_identity="sip-participant-identity") passed to AgentSession.start(...) — recommended. ◦ Construct RoomIO with the participant identity explicitly (RoomIO(session, room=..., participant="...")) and start it before or as part of session start — acceptable for custom flows. ◦ RoomIO.set_participant(...) can be used to change the linked participant dynamically at runtime if you need to switch targets after start. • Practical note: The simplest and most robust pattern is to determine the SIP participant identity (from room.remote_participants or your SIP signaling) and pass it as
participant_identity
in RoomOptions when calling session.start. 5) Is RoomInputOptions deprecated / should you migrate to RoomOptions? • RoomOptions is the current unified approach and is used in current examples. RoomInputOptions is not the preferred interface for new Python Agent code; migrate to RoomOptions to manage participant binding and other input/output configuration to avoid unexpected differences or confusion. How this applies to your symptom (why you saw stt_calls=0 / llm_calls=0) • If the agent is linked to the wrong participant (or to no participant input) the STT/audio path for the SIP caller will not be processed even though TTS works (TTS is independent outbound). Explicitly binding the session to the SIP participant eliminates the race that leads to intermittent “no user transcript” cases. Suggested next steps (concise) 1. When you discover the SIP participant in room.remote_participants, capture its participant.identity immediately. 2. Start the agent with `RoomOptions(participant_identity=that-identity)`: a. Example pattern: ▪︎ Build room_options = RoomOptions(participant_identity="sip-identity") ▪︎ await session.start(agent=my_agent, room=ctx.room, room_options=room_options) 1. If you need to support callers who join after the agent starts, either: ◦ Start the agent only after the SIP participant is present and you’ve set RoomOptions, or ◦ Use RoomIO.set_participant(...) as soon as you detect the SIP participant to rebind the input dynamically. If you’d like, tell me: • How you currently detect the SIP participant (what field you use for identity), and • Whether the agent must be started before the SIP join or can wait until after — I can suggest the exact code pattern to integrate RoomOptions or a dynamic set_participant flow for your current startup sequence. Sources: Agent session | LiveKit Documentation | sip | livekit/livekit_composite | DeepWiki | How to set a fixed participant for agent audio processing | LiveKit, with interactive elements