@@ -7,14 +7,30 @@ import { ParticipantAgentAttributes, TrackReference } from '@livekit/components-
77
88import { useParticipantTracks } from './useParticipantTracks' ;
99import { useRemoteParticipants } from './useRemoteParticipants' ;
10- import { AgentState as LegacyAgentState } from './useVoiceAssistant' ;
1110import { ConversationInstance } from './useConversationWith' ;
1211
1312// FIXME: make this 10 seconds once room dispatch booting info is discoverable
1413const DEFAULT_AGENT_CONNECT_TIMEOUT_MILLISECONDS = 20_000 ;
1514
16- /** State representing the current status of the agent, whether it is ready for speach, etc */
17- export type AgentStateNew = 'unset' | 'initializing' | 'failed' | 'idle' | 'listening' | 'thinking' | 'speaking' ;
15+ /** @see https://github.com/livekit/agents/blob/65170238db197f62f479eb7aaef1c0e18bfad6e7/livekit-agents/livekit/agents/voice/events.py#L97 */
16+ type AgentSdkStates = 'initializing' | 'idle' | 'listening' | 'thinking' | 'speaking' ;
17+
18+ /**
19+ * State representing the current status of the agent, whether it is ready for speach, etc
20+ *
21+ * For most agents (which have the preconnect audio buffer feature enabled), this is the lifecycle:
22+ * connecting -> listening -> listening/thinking/speaking
23+ *
24+ * For agents without the preconnect audio feature enabled:
25+ * connecting -> initializing -> idle/listening/thinking/speaking
26+ *
27+ * If an agent fails to connect:
28+ * connecting -> listening/initializing -> failed
29+ *
30+ * Legacy useVoiceAssistant hook:
31+ * disconnected -> connecting -> initializing -> listening/thinking/speaking
32+ * */
33+ export type AgentState = 'disconnected' | 'connecting' | 'failed' | AgentSdkStates ;
1834
1935export enum AgentEvent {
2036 CameraChanged = 'cameraChanged' ,
@@ -27,7 +43,7 @@ export type AgentCallbacks = {
2743 [ AgentEvent . CameraChanged ] : ( newTrack : TrackReference | null ) => void ;
2844 [ AgentEvent . MicrophoneChanged ] : ( newTrack : TrackReference | null ) => void ;
2945 [ AgentEvent . AttributesChanged ] : ( newAttributes : Record < string , string > ) => void ;
30- [ AgentEvent . StateChanged ] : ( newAgentState : AgentStateNew ) => void ;
46+ [ AgentEvent . StateChanged ] : ( newAgentState : AgentState ) => void ;
3147} ;
3248
3349type AgentInstanceCommon = {
@@ -41,11 +57,6 @@ type AgentInstanceCommon = {
4157
4258 agentParticipant : RemoteParticipant | null ;
4359 workerParticipant : RemoteParticipant | null ;
44-
45- /** A computed version of the old {@link AgentState} value returned by {@link useVoiceAssistant}
46- * @deprecated Use conversation.connectionState / agent.lifecycleState if at all possible
47- */
48- legacyAgentState : LegacyAgentState ;
4960 } ;
5061} ;
5162
@@ -61,7 +72,18 @@ type AgentStateAvailable = AgentInstanceCommon & {
6172} ;
6273
6374type AgentStateUnAvailable = AgentInstanceCommon & {
64- state : "unset" | "initializing" | "idle" ;
75+ state : "initializing" | "idle" ;
76+ failureReasons : null ;
77+
78+ /** Is the agent ready for user interaction? */
79+ isAvailable : false ;
80+
81+ cameraTrack : TrackReference | null ;
82+ microphoneTrack : TrackReference | null ;
83+ } ;
84+
85+ type AgentStateConnecting = AgentInstanceCommon & {
86+ state : "connecting" ;
6587 failureReasons : null ;
6688
6789 /** Is the agent ready for user interaction? */
@@ -93,10 +115,10 @@ type AgentActions = {
93115 waitUntilMicrophone : ( signal ?: AbortSignal ) => Promise < TrackReference > ;
94116} ;
95117
96- type AgentStateCases = AgentStateAvailable | AgentStateUnAvailable | AgentStateFailed ;
118+ type AgentStateCases = AgentStateConnecting | AgentStateAvailable | AgentStateUnAvailable | AgentStateFailed ;
97119export type AgentInstance = AgentStateCases & AgentActions ;
98120
99- const generateDerivedStateValues = < State extends AgentStateNew > ( state : State ) => ( {
121+ const generateDerivedStateValues = < State extends AgentState > ( state : State ) => ( {
100122 isAvailable : (
101123 state === 'listening' ||
102124 state === 'thinking' ||
@@ -110,11 +132,11 @@ const useAgentTimeoutIdStore = create<{
110132 agentTimeoutFailureReason : string | null ,
111133 startAgentTimeout : ( agentConnectTimeoutMilliseconds ?: number ) => void ;
112134 clearAgentTimeout : ( ) => void ;
113- updateAgentTimeoutState : ( agentState : AgentStateNew ) => void ;
135+ updateAgentTimeoutState : ( agentState : AgentState ) => void ;
114136 updateAgentTimeoutParticipantExists : ( agentParticipantExists : boolean ) => void ;
115137 subtle : {
116138 agentTimeoutId : ReturnType < typeof setTimeout > | null ;
117- agentState : AgentStateNew ;
139+ agentState : AgentState ;
118140 agentParticipantExists : boolean ;
119141 } ;
120142} > ( ( set , get ) => {
@@ -148,7 +170,7 @@ const useAgentTimeoutIdStore = create<{
148170 subtle : {
149171 ...old . subtle ,
150172 agentTimeoutId : startAgentConnectedTimeout ( agentConnectTimeoutMilliseconds ) ,
151- agentState : 'unset ' ,
173+ agentState : 'connecting ' ,
152174 agentParticipantExists : false ,
153175 } ,
154176 } ;
@@ -165,14 +187,14 @@ const useAgentTimeoutIdStore = create<{
165187 subtle : {
166188 ...old . subtle ,
167189 agentTimeoutId : null ,
168- agentState : 'unset ' ,
190+ agentState : 'connecting ' ,
169191 agentParticipantExists : false ,
170192 } ,
171193 } ;
172194 } ) ;
173195 } ,
174196
175- updateAgentTimeoutState : ( agentState : AgentStateNew ) => {
197+ updateAgentTimeoutState : ( agentState : AgentState ) => {
176198 set ( ( old ) => ( { ...old , subtle : { ...old . subtle , agentState : agentState } } ) ) ;
177199 } ,
178200 updateAgentTimeoutParticipantExists : ( agentParticipantExists : boolean ) => {
@@ -181,7 +203,7 @@ const useAgentTimeoutIdStore = create<{
181203
182204 subtle : {
183205 agentTimeoutId : null ,
184- agentState : 'unset ' ,
206+ agentState : 'connecting ' ,
185207 agentParticipantExists : false ,
186208 } ,
187209 } ;
@@ -305,7 +327,7 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
305327 return 'failed' ;
306328 }
307329
308- let newState : AgentStateNew = 'unset ' ;
330+ let newState : AgentState = 'connecting ' ;
309331
310332 if ( roomConnectionState !== ConnectionState . Disconnected ) {
311333 newState = 'initializing' ;
@@ -318,8 +340,7 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
318340 }
319341
320342 if ( agentParticipant && agentParticipantAttributes [ ParticipantAgentAttributes . AgentState ] ) {
321- // ref: https://github.com/livekit/agents/blob/65170238db197f62f479eb7aaef1c0e18bfad6e7/livekit-agents/livekit/agents/voice/events.py#L97
322- const agentState = agentParticipantAttributes [ ParticipantAgentAttributes . AgentState ] as 'initializing' | 'idle' | 'listening' | 'thinking' | 'speaking' ;
343+ const agentState = agentParticipantAttributes [ ParticipantAgentAttributes . AgentState ] as AgentSdkStates ;
323344 newState = agentState ;
324345 }
325346
@@ -347,31 +368,6 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
347368 } ;
348369 } , [ isConversationDisconnected , conversation . subtle . agentConnectTimeoutMilliseconds ] ) ;
349370
350- const legacyAgentState : LegacyAgentState = useMemo ( ( ) => {
351- switch ( conversation . connectionState ) {
352- case ConnectionState . Disconnected :
353- case ConnectionState . Connecting :
354- return conversation . connectionState ;
355-
356- case ConnectionState . Connected :
357- case ConnectionState . Reconnecting :
358- case ConnectionState . SignalReconnecting :
359- switch ( state ) {
360- case 'speaking' :
361- case 'listening' :
362- case 'initializing' :
363- case 'thinking' :
364- return state ;
365-
366- case 'idle' :
367- case 'unset' :
368- case 'failed' :
369- // There's not really a good direct correlation for either of these...
370- return 'disconnected' ;
371- }
372- }
373- } , [ conversation . connectionState , state ] ) ;
374-
375371 const agentState : AgentStateCases = useMemo ( ( ) => {
376372 const common : AgentInstanceCommon = {
377373 [ Symbol . toStringTag ] : "AgentInstance" ,
@@ -382,7 +378,6 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
382378 emitter,
383379 agentParticipant,
384380 workerParticipant,
385- legacyAgentState,
386381 } ,
387382 } ;
388383
@@ -401,9 +396,20 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
401396 microphoneTrack : audioTrack ,
402397 } ;
403398
404- case 'unset' :
405399 case 'initializing' :
406400 case 'idle' :
401+ return {
402+ ...common ,
403+
404+ state,
405+ ...generateDerivedStateValues ( state ) ,
406+ failureReasons : null ,
407+
408+ cameraTrack : videoTrack ,
409+ microphoneTrack : audioTrack ,
410+ } ;
411+
412+ case 'connecting' :
407413 return {
408414 ...common ,
409415
@@ -446,7 +452,7 @@ export function useAgent(conversation: ConversationStub, _name?: string): AgentI
446452 }
447453
448454 return new Promise < void > ( ( resolve , reject ) => {
449- const stateChangedHandler = ( state : AgentStateNew ) => {
455+ const stateChangedHandler = ( state : AgentState ) => {
450456 const { isAvailable } = generateDerivedStateValues ( state ) ;
451457 if ( ! isAvailable ) {
452458 return ;
0 commit comments