Skip to content

Commit fcadfc0

Browse files
committed
feat: add explicit failure when agent disconnects from the room
This should hopefully make the agents sdk more robust to cases like a local agent server being terminated, a hosted agent leaving a call due to it being connected longer than the drain threshold, etc. It still is an open question to a degree how we can / should disambiguate between purposeful agent disconnects and outright crashes. It doesn't seem like today there is enough metadata to do this so right now I am assuming all non user triggered disconnects to be of the "crash" variety.
1 parent 09b09eb commit fcadfc0

File tree

1 file changed

+41
-2
lines changed

1 file changed

+41
-2
lines changed

packages/react/src/hooks/useAgent.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ export function useAgent(session?: SessionStub): UseAgentReturn {
342342
emitter.emit(AgentEvent.MicrophoneChanged, audioTrack);
343343
}, [emitter, audioTrack]);
344344

345+
// Listen for room connection state updates
345346
const [roomConnectionState, setRoomConnectionState] = React.useState(room.state);
346347
React.useEffect(() => {
347348
const handleConnectionStateChanged = (connectionState: ConnectionState) => {
@@ -354,6 +355,37 @@ export function useAgent(session?: SessionStub): UseAgentReturn {
354355
};
355356
}, [room]);
356357

358+
// If the agent participant disconnects in the middle of a conversation unexpectedly, mark that as an explicit failure
359+
const [agentDisconnectedFailureReason, setAgentDisconnectedFailureReason] = React.useState<
360+
string | null
361+
>(null);
362+
React.useEffect(() => {
363+
if (!agentParticipant) {
364+
return;
365+
}
366+
367+
const onParticipantDisconnect = (participant: RemoteParticipant) => {
368+
if (participant.identity !== agentParticipant?.identity) {
369+
return;
370+
}
371+
setAgentDisconnectedFailureReason('Agent left the room unexpectedly.');
372+
};
373+
374+
room.on(RoomEvent.ParticipantDisconnected, onParticipantDisconnect);
375+
376+
return () => {
377+
room.off(RoomEvent.ParticipantDisconnected, onParticipantDisconnect);
378+
};
379+
}, [agentParticipant, room]);
380+
381+
React.useEffect(() => {
382+
if (roomConnectionState !== ConnectionState.Disconnected) {
383+
return;
384+
}
385+
// Clear the agent disconnect failure state when the room disconnects
386+
setAgentDisconnectedFailureReason(null);
387+
}, [roomConnectionState]);
388+
357389
const [localMicTrack, setLocalMicTrack] = React.useState<LocalTrackPublication | null>(
358390
() => room.localParticipant.getTrackPublication(Track.Source.Microphone) ?? null,
359391
);
@@ -386,8 +418,15 @@ export function useAgent(session?: SessionStub): UseAgentReturn {
386418
}, [room.localParticipant]);
387419

388420
const failureReasons = React.useMemo(() => {
389-
return agentTimeoutFailureReason ? [agentTimeoutFailureReason] : [];
390-
}, [agentTimeoutFailureReason]);
421+
const reasons = [];
422+
if (agentTimeoutFailureReason) {
423+
reasons.push(agentTimeoutFailureReason);
424+
}
425+
if (agentDisconnectedFailureReason) {
426+
reasons.push(agentDisconnectedFailureReason);
427+
}
428+
return reasons;
429+
}, [agentTimeoutFailureReason, agentDisconnectedFailureReason]);
391430

392431
const [state, isBufferingSpeech] = React.useMemo(() => {
393432
if (failureReasons.length > 0) {

0 commit comments

Comments
 (0)