Skip to content

Commit f59d85c

Browse files
committed
feat: add isPreConnectBufferEnabled to useAgent to detect if listening is happening with preconnect buffer or not
1 parent daa029c commit f59d85c

File tree

1 file changed

+68
-22
lines changed

1 file changed

+68
-22
lines changed

packages/react/src/hooks/useAgent.ts

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,29 @@ type AgentInstanceCommon = {
6161
};
6262

6363
type AgentStateAvailable = AgentInstanceCommon & {
64-
state: "listening" | "thinking" | "speaking";
64+
state: "thinking" | "speaking";
6565
failureReasons: null;
6666

6767
/** Is the agent ready for user interaction? */
6868
isAvailable: true;
6969

70+
/** Is the audio preconnect buffer currently available? */
71+
isPreConnectBufferEnabled: false;
72+
73+
cameraTrack: TrackReference | null;
74+
microphoneTrack: TrackReference | null;
75+
};
76+
77+
type AgentStateAvailableListening = AgentInstanceCommon & {
78+
state: "listening";
79+
failureReasons: null;
80+
81+
/** Is the agent ready for user interaction? */
82+
isAvailable: true;
83+
84+
/** Is the audio preconnect buffer currently available? */
85+
isPreConnectBufferEnabled: boolean;
86+
7087
cameraTrack: TrackReference | null;
7188
microphoneTrack: TrackReference | null;
7289
};
@@ -78,17 +95,23 @@ type AgentStateUnAvailable = AgentInstanceCommon & {
7895
/** Is the agent ready for user interaction? */
7996
isAvailable: false;
8097

98+
/** Is the audio preconnect buffer currently available? */
99+
isPreConnectBufferEnabled: false;
100+
81101
cameraTrack: TrackReference | null;
82102
microphoneTrack: TrackReference | null;
83103
};
84104

85105
type AgentStateConnecting = AgentInstanceCommon & {
86-
state: "connecting";
106+
state: "disconnected" | "connecting";
87107
failureReasons: null;
88108

89109
/** Is the agent ready for user interaction? */
90110
isAvailable: false;
91111

112+
/** Is the audio preconnect buffer currently available? */
113+
isPreConnectBufferEnabled: false;
114+
92115
cameraTrack: null;
93116
microphoneTrack: null;
94117
};
@@ -100,6 +123,9 @@ type AgentStateFailed = AgentInstanceCommon & {
100123
/** Is the agent ready for user interaction? */
101124
isAvailable: false;
102125

126+
/** Is the audio preconnect buffer currently available? */
127+
isPreConnectBufferEnabled: false;
128+
103129
cameraTrack: null;
104130
microphoneTrack: null;
105131
};
@@ -115,7 +141,7 @@ type AgentActions = {
115141
waitUntilMicrophone: (signal?: AbortSignal) => Promise<TrackReference>;
116142
};
117143

118-
type AgentStateCases = AgentStateConnecting | AgentStateAvailable | AgentStateUnAvailable | AgentStateFailed;
144+
type AgentStateCases = AgentStateConnecting | AgentStateAvailable | AgentStateAvailableListening | AgentStateUnAvailable | AgentStateFailed;
119145
export type AgentInstance = AgentStateCases & AgentActions;
120146

121147
const generateDerivedStateValues = <State extends AgentState>(state: State) => ({
@@ -314,42 +340,44 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
314340
agentTimeoutFailureReason,
315341
startAgentTimeout,
316342
clearAgentTimeout,
317-
updateAgentTimeoutState: updateAgentTimeoutConversationalState,
343+
updateAgentTimeoutState,
318344
updateAgentTimeoutParticipantExists,
319345
} = useAgentTimeoutIdStore();
320346

321347
const failureReasons = useMemo(() => {
322348
return agentTimeoutFailureReason ? [ agentTimeoutFailureReason ] : [];
323349
}, [agentTimeoutFailureReason]);
324350

325-
const state = useMemo(() => {
351+
const [state, isPreConnectBufferEnabled] = useMemo(() => {
326352
if (failureReasons.length > 0) {
327-
return 'failed';
353+
return ['failed' as const, false];
328354
}
329355

330-
let newState: AgentState = 'connecting';
356+
let state: AgentState = 'disconnected';
357+
let preconnectBufferEnabled = false;
331358

332359
if (roomConnectionState !== ConnectionState.Disconnected) {
333-
newState = 'initializing';
360+
state = 'connecting';
334361
}
335362

336363
// If the microphone preconnect buffer is active, then the state should be "listening" rather
337364
// than "initializing"
338365
if (localMicTrack) {
339-
newState = 'listening';
366+
state = 'listening';
367+
preconnectBufferEnabled = true;
340368
}
341369

342370
if (agentParticipant && agentParticipantAttributes[ParticipantAgentAttributes.AgentState]) {
343-
const agentState = agentParticipantAttributes[ParticipantAgentAttributes.AgentState] as AgentSdkStates;
344-
newState = agentState;
371+
state = agentParticipantAttributes[ParticipantAgentAttributes.AgentState] as AgentSdkStates;
372+
preconnectBufferEnabled = false;
345373
}
346374

347-
return newState;
375+
return [state, preconnectBufferEnabled] as [AgentState, boolean];
348376
}, [failureReasons, roomConnectionState, localMicTrack, agentParticipant, agentParticipantAttributes]);
349377

350378
useEffect(() => {
351379
emitter.emit(AgentEvent.StateChanged, state);
352-
updateAgentTimeoutConversationalState(state);
380+
updateAgentTimeoutState(state);
353381
}, [emitter, state]);
354382
useEffect(() => {
355383
updateAgentTimeoutParticipantExists(agentParticipant !== null);
@@ -382,18 +410,19 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
382410
};
383411

384412
switch (state) {
385-
case 'listening':
386-
case 'thinking':
387-
case 'speaking':
413+
case 'disconnected':
414+
case 'connecting':
388415
return {
389416
...common,
390417

391418
state,
392419
...generateDerivedStateValues(state),
420+
isPreConnectBufferEnabled: false,
393421
failureReasons: null,
394422

395-
cameraTrack: videoTrack,
396-
microphoneTrack: audioTrack,
423+
// Clear inner values if no longer connected
424+
cameraTrack: null,
425+
microphoneTrack: null,
397426
};
398427

399428
case 'initializing':
@@ -403,23 +432,38 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
403432

404433
state,
405434
...generateDerivedStateValues(state),
435+
isPreConnectBufferEnabled: false,
406436
failureReasons: null,
407437

408438
cameraTrack: videoTrack,
409439
microphoneTrack: audioTrack,
410440
};
411441

412-
case 'connecting':
442+
case 'listening':
413443
return {
414444
...common,
415445

416446
state,
417447
...generateDerivedStateValues(state),
448+
isPreConnectBufferEnabled,
418449
failureReasons: null,
419450

420-
// Clear inner values if no longer connected
421-
cameraTrack: null,
422-
microphoneTrack: null,
451+
cameraTrack: videoTrack,
452+
microphoneTrack: audioTrack,
453+
};
454+
455+
case 'thinking':
456+
case 'speaking':
457+
return {
458+
...common,
459+
460+
state,
461+
...generateDerivedStateValues(state),
462+
isPreConnectBufferEnabled: false,
463+
failureReasons: null,
464+
465+
cameraTrack: videoTrack,
466+
microphoneTrack: audioTrack,
423467
};
424468

425469
case 'failed':
@@ -428,6 +472,7 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
428472

429473
state: 'failed',
430474
...generateDerivedStateValues('failed'),
475+
isPreConnectBufferEnabled: false,
431476
failureReasons,
432477

433478
// Clear inner values if no longer connected
@@ -443,6 +488,7 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
443488
state,
444489
videoTrack,
445490
audioTrack,
491+
isPreConnectBufferEnabled,
446492
]);
447493

448494
const waitUntilAvailable = useCallback(async (signal?: AbortSignal) => {

0 commit comments

Comments
 (0)