@wix/legends-platform-sdk 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +3 -2
  2. package/dist/cjs/hooks/index.js +6 -0
  3. package/dist/cjs/hooks/index.js.map +1 -1
  4. package/dist/cjs/hooks/useConversation/livekit/constants.js +3 -1
  5. package/dist/cjs/hooks/useConversation/livekit/constants.js.map +1 -1
  6. package/dist/cjs/hooks/useConversation/livekit/useConversation.js +82 -35
  7. package/dist/cjs/hooks/useConversation/livekit/useConversation.js.map +1 -1
  8. package/dist/cjs/hooks/useCreateConversation/useCreateConversation.js +16 -8
  9. package/dist/cjs/hooks/useCreateConversation/useCreateConversation.js.map +1 -1
  10. package/dist/cjs/hooks/useSpeechActivityDetector/helpers.js +87 -0
  11. package/dist/cjs/hooks/useSpeechActivityDetector/helpers.js.map +1 -0
  12. package/dist/cjs/hooks/useSpeechActivityDetector/index.js +16 -0
  13. package/dist/cjs/hooks/useSpeechActivityDetector/index.js.map +1 -0
  14. package/dist/cjs/hooks/useSpeechActivityDetector/speechActivityProcessorSource.js +55 -0
  15. package/dist/cjs/hooks/useSpeechActivityDetector/speechActivityProcessorSource.js.map +1 -0
  16. package/dist/cjs/hooks/useSpeechActivityDetector/types.js +4 -0
  17. package/dist/cjs/hooks/useSpeechActivityDetector/types.js.map +1 -0
  18. package/dist/cjs/hooks/useSpeechActivityDetector/useSpeechActivityDetector.js +52 -0
  19. package/dist/cjs/hooks/useSpeechActivityDetector/useSpeechActivityDetector.js.map +1 -0
  20. package/dist/cjs/sdk.js +6 -0
  21. package/dist/cjs/sdk.js.map +1 -1
  22. package/dist/cjs/types/service.js.map +1 -1
  23. package/dist/esm/hooks/index.js +1 -0
  24. package/dist/esm/hooks/index.js.map +1 -1
  25. package/dist/esm/hooks/useConversation/livekit/constants.js +2 -0
  26. package/dist/esm/hooks/useConversation/livekit/constants.js.map +1 -1
  27. package/dist/esm/hooks/useConversation/livekit/useConversation.js +82 -35
  28. package/dist/esm/hooks/useConversation/livekit/useConversation.js.map +1 -1
  29. package/dist/esm/hooks/useCreateConversation/useCreateConversation.js +16 -8
  30. package/dist/esm/hooks/useCreateConversation/useCreateConversation.js.map +1 -1
  31. package/dist/esm/hooks/useSpeechActivityDetector/helpers.js +83 -0
  32. package/dist/esm/hooks/useSpeechActivityDetector/helpers.js.map +1 -0
  33. package/dist/esm/hooks/useSpeechActivityDetector/index.js +3 -0
  34. package/dist/esm/hooks/useSpeechActivityDetector/index.js.map +1 -0
  35. package/dist/esm/hooks/useSpeechActivityDetector/speechActivityProcessorSource.js +51 -0
  36. package/dist/esm/hooks/useSpeechActivityDetector/speechActivityProcessorSource.js.map +1 -0
  37. package/dist/esm/hooks/useSpeechActivityDetector/types.js +2 -0
  38. package/dist/esm/hooks/useSpeechActivityDetector/types.js.map +1 -0
  39. package/dist/esm/hooks/useSpeechActivityDetector/useSpeechActivityDetector.js +44 -0
  40. package/dist/esm/hooks/useSpeechActivityDetector/useSpeechActivityDetector.js.map +1 -0
  41. package/dist/esm/sdk.js +1 -0
  42. package/dist/esm/sdk.js.map +1 -1
  43. package/dist/esm/types/service.js.map +1 -1
  44. package/dist/types/hooks/index.d.ts +1 -0
  45. package/dist/types/hooks/index.d.ts.map +1 -1
  46. package/dist/types/hooks/useConversation/livekit/constants.d.ts +2 -0
  47. package/dist/types/hooks/useConversation/livekit/constants.d.ts.map +1 -1
  48. package/dist/types/hooks/useConversation/livekit/useConversation.d.ts.map +1 -1
  49. package/dist/types/hooks/useCreateConversation/useCreateConversation.d.ts +4 -0
  50. package/dist/types/hooks/useCreateConversation/useCreateConversation.d.ts.map +1 -1
  51. package/dist/types/hooks/useSpeechActivityDetector/helpers.d.ts +19 -0
  52. package/dist/types/hooks/useSpeechActivityDetector/helpers.d.ts.map +1 -0
  53. package/dist/types/hooks/useSpeechActivityDetector/index.d.ts +3 -0
  54. package/dist/types/hooks/useSpeechActivityDetector/index.d.ts.map +1 -0
  55. package/dist/types/hooks/useSpeechActivityDetector/speechActivityProcessorSource.d.ts +3 -0
  56. package/dist/types/hooks/useSpeechActivityDetector/speechActivityProcessorSource.d.ts.map +1 -0
  57. package/dist/types/hooks/useSpeechActivityDetector/types.d.ts +14 -0
  58. package/dist/types/hooks/useSpeechActivityDetector/types.d.ts.map +1 -0
  59. package/dist/types/hooks/useSpeechActivityDetector/useSpeechActivityDetector.d.ts +10 -0
  60. package/dist/types/hooks/useSpeechActivityDetector/useSpeechActivityDetector.d.ts.map +1 -0
  61. package/dist/types/sdk.d.ts +1 -0
  62. package/dist/types/sdk.d.ts.map +1 -1
  63. package/dist/types/types/service.d.ts +4 -0
  64. package/dist/types/types/service.d.ts.map +1 -1
  65. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"names":["useEffect","useRef","DisconnectReason","setLogLevel","ParticipantKind","LogLevel","invariant","useTimer","useLatest","useUpdateEffect","useEnsureRoom","useMediaDeviceSelect","useAtom","useAtomValue","useSetAtom","state","useCreateConversation","useEmitConversationEvent","useToolCallingRuntime","checkIsTurnDetectionData","checkIsInactivityWarningData","checkIsInactivityTimeoutData","checkIsToolCallsData","createLivekitToolAdapter","decodeDataPayload","findJoinedAgentParticipant","mergeTranscriptions","publishConversationDataMessage","safeStringify","shouldHandleParticipantConnected","useConversation","_ref","_conversation$data2","_ref$autoJoin","autoJoin","type","config","_ref$startWithMicroph","startWithMicrophone","_ref$startWithCamera","startWithCamera","silent","conversation","room","speaker","_useAtom","microphone","setMicrophone","_useAtom2","camera","setCamera","toolRuntime","toolAdapter","conversationRef","data","toolRuntimeRef","_useAtom3","conversationStatus","setConversationStatus","_useAtom4","userConnectionState","setUserConnectionState","_useAtom5","joinedAt","setJoinedAt","_useMediaDeviceSelect","kind","setMicrophoneDeviceId","setActiveMediaDevice","_useMediaDeviceSelect2","setSpeakerDeviceId","_useMediaDeviceSelect3","requestPermissions","enabled","setCameraDeviceId","setTranscriptions","transcriptions","_useAtom6","agentConnectionState","setAgentConnectionState","setAgentActivityState","agentActivityState","setUserActivityState","userActivityState","emitEvent","userStopRef","inactivityTimeoutRef","timer","autoStart","onExpire","handleExpire","expiryTimestamp","Date","registerRoomEvents","unregisterRoomEvents","deviceId","localParticipant","setMicrophoneEnabled","setCameraEnabled","resolution","width","height","join","_conversation$data","limit","undefined","restart","getTime","_conversation$data3","url","URL","accessToken","searchParams","get","connect","remoteParticipant","handleAgentJoin","enableMediaDevices","error","console","handleTranscriptionReceivedEvent","segments","participant","map","s","id","message","text","source","AGENT","old","handleTurnDetectionEvent","_conversationRef$curr","conversationId","current","participantKind","eventState","ts","performance","now","turnDetectionStrategy","sequence","speechId","latencyMs","handleInactivityWarningEvent","_conversationRef$curr2","gracePeriodSec","grace_period_sec","handleInactivityTimeoutEvent","_data","_conversationRef$curr3","handleToolCallsEvent","resolveIncomingToolCalls","dataChannelHandlers","guard","handle","handleDataReceivedEvent","payload","decoded","_ref2","Promise","resolve","catch","handleConnectedEvent","_conversationRef$curr4","handleEncryptionErrorEvent","handleDisconnectedEvent","reason","CLIENT_INITIATED","ROOM_CLOSED","handleParticipantConnectedEvent","_conversationRef$curr5","handleParticipantDisconnectedEvent","_conversationRef$curr6","leave","_conversationRef$curr7","disconnect","_conversationRef$curr8","_conversationRef$curr9","interrupt","_conversationRef$curr0","dismissInactivity","send","attachments","_conversationRef$curr1","sendText","topic","attributes","on","off","status"],"sources":["../../../../../src/hooks/useConversation/livekit/useConversation.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport {\n DisconnectReason,\n Participant,\n setLogLevel,\n ParticipantKind,\n RemoteParticipant,\n TranscriptionSegment,\n LogLevel,\n} from 'livekit-client';\nimport invariant from 'invariant';\nimport { useTimer } from 'react-timer-hook';\nimport { useLatest, useUpdateEffect } from 'react-use';\nimport { useEnsureRoom, useMediaDeviceSelect } from '@livekit/components-react';\nimport { useAtom, useAtomValue, useSetAtom } from 'jotai';\nimport {\n Conversation,\n state,\n type ConversationTranscription,\n} from '../../../conversation';\nimport type {\n UseConversationResult,\n UseConversationOptions,\n Attachment,\n} from '../types';\nimport type {\n InactivityWarningData,\n InactivityTimeoutData,\n TurnDetectionData,\n ToolCallsData,\n} from './types';\nimport { useCreateConversation } from '../../useCreateConversation';\n\nimport { useEmitConversationEvent } from '../../useConversationEvents/useEmitConversationEvent';\nimport { useToolCallingRuntime } from '../../useTools/runtime/useToolCallingRuntime';\nimport {\n checkIsTurnDetectionData,\n checkIsInactivityWarningData,\n checkIsInactivityTimeoutData,\n checkIsToolCallsData,\n createLivekitToolAdapter,\n decodeDataPayload,\n findJoinedAgentParticipant,\n mergeTranscriptions,\n publishConversationDataMessage,\n safeStringify,\n shouldHandleParticipantConnected,\n} from './utils';\n\nexport function useConversation({\n autoJoin = true,\n type,\n config,\n startWithMicrophone = false,\n startWithCamera = false,\n}: UseConversationOptions): UseConversationResult {\n setLogLevel(LogLevel.silent);\n\n const conversation = useCreateConversation({ type, config });\n const room = useEnsureRoom();\n const speaker = useAtomValue(state.speaker);\n const [microphone, setMicrophone] = useAtom(state.microphone);\n const [camera, setCamera] = useAtom(state.camera);\n const toolRuntime = useToolCallingRuntime();\n const toolAdapter = createLivekitToolAdapter(room);\n\n // for use in register/unregister room events\n const conversationRef = useLatest<Conversation | undefined>(\n conversation.data,\n );\n const toolRuntimeRef = useLatest(toolRuntime);\n\n const [conversationStatus, setConversationStatus] = useAtom(\n state.conversationStatus,\n );\n\n const [userConnectionState, setUserConnectionState] = useAtom(\n state.userConnectionState,\n );\n\n const [joinedAt, setJoinedAt] = useAtom(state.joinedAt);\n\n const { setActiveMediaDevice: setMicrophoneDeviceId } = useMediaDeviceSelect({\n kind: 'audioinput',\n });\n\n const { setActiveMediaDevice: setSpeakerDeviceId } = useMediaDeviceSelect({\n kind: 'audiooutput',\n });\n\n const { setActiveMediaDevice: setCameraDeviceId } = useMediaDeviceSelect({\n kind: 'videoinput',\n requestPermissions: camera.enabled,\n });\n\n const setTranscriptions = useSetAtom(state.transcriptions);\n const [agentConnectionState, setAgentConnectionState] = useAtom(\n state.agentConnectionState,\n );\n const setAgentActivityState = useSetAtom(state.agentActivityState);\n const setUserActivityState = useSetAtom(state.userActivityState);\n const emitEvent = useEmitConversationEvent();\n\n const userStopRef = useRef<{\n ts: number;\n turnDetectionStrategy: string;\n sequence?: number;\n speechId?: string;\n } | null>(null);\n const inactivityTimeoutRef = useRef(false);\n\n const timer = useTimer({\n autoStart: false,\n onExpire: handleExpire,\n expiryTimestamp: new Date(),\n });\n\n useEffect(() => {\n if (!room) {\n return;\n }\n registerRoomEvents();\n\n return () => {\n unregisterRoomEvents();\n };\n }, [room]);\n\n useUpdateEffect(() => {\n setMicrophoneDeviceId(microphone.deviceId);\n }, [microphone.deviceId]);\n\n useUpdateEffect(() => {\n setSpeakerDeviceId(speaker.deviceId);\n }, [speaker.deviceId]);\n\n useUpdateEffect(() => {\n setCameraDeviceId(camera.deviceId);\n }, [camera.deviceId]);\n\n useUpdateEffect(() => {\n if (agentConnectionState !== 'joined') {\n return;\n }\n room.localParticipant.setMicrophoneEnabled(microphone.enabled);\n }, [microphone.enabled, agentConnectionState]);\n\n useUpdateEffect(() => {\n if (agentConnectionState !== 'joined') {\n return;\n }\n room.localParticipant.setCameraEnabled(camera.enabled, {\n resolution: { width: 1920, height: 1080 },\n });\n }, [camera.enabled, agentConnectionState]);\n\n useEffect(() => {\n if (!autoJoin) {\n return;\n }\n\n if (conversationStatus === 'created') {\n void join();\n }\n }, [conversationStatus, autoJoin]);\n\n useEffect(() => {\n if (!joinedAt || conversation.data?.limit === undefined) {\n return;\n }\n\n timer.restart(new Date(joinedAt.getTime() + conversation.data!.limit!));\n }, [joinedAt, conversation.data?.limit]);\n\n async function join() {\n invariant(userConnectionState === 'idle', 'Already joined');\n invariant(conversation.data, 'Conversation is not ready');\n const url = new URL(conversation?.data?.url || '');\n const accessToken = url.searchParams.get('token');\n try {\n setUserConnectionState('joining');\n await room?.connect(conversation.data.url, accessToken!, {});\n\n // sometimes the agent is already joined before the user joins the conversation\n const remoteParticipant = findJoinedAgentParticipant(room, type);\n if (remoteParticipant && agentConnectionState !== 'joined') {\n handleAgentJoin(remoteParticipant);\n }\n\n enableMediaDevices();\n } catch (error) {\n console.error('Failed to join', error);\n setUserConnectionState('join-error');\n }\n }\n\n function enableMediaDevices() {\n if (startWithMicrophone) {\n setMicrophone({ ...microphone, enabled: true });\n }\n if (startWithCamera && type === 'video') {\n setCamera({ ...camera, enabled: true });\n }\n }\n\n function handleTranscriptionReceivedEvent(\n segments: TranscriptionSegment[],\n participant?: Participant,\n ) {\n const transcriptions: ConversationTranscription[] = segments.map((s) => ({\n id: s.id,\n message: s.text,\n source: participant?.kind === ParticipantKind.AGENT ? 'agent' : 'user',\n }));\n\n setTranscriptions((old) => mergeTranscriptions(transcriptions, old));\n }\n\n function handleTurnDetectionEvent(data: TurnDetectionData) {\n const conversationId = conversationRef.current?.id;\n const { participantKind, state: eventState } = data;\n switch (participantKind) {\n case 'STANDARD':\n if (eventState === 'listening') {\n userStopRef.current = {\n ts: performance.now(),\n turnDetectionStrategy: data.turnDetectionStrategy,\n sequence: data.sequence,\n speechId: data.speechId,\n };\n } else {\n userStopRef.current = null;\n }\n emitEvent(\n eventState === 'speaking' ? 'userSpeakStart' : 'userSpeakStop',\n { conversationId: conversationId! },\n );\n setUserActivityState(eventState);\n break;\n case 'AGENT':\n if (eventState === 'speaking' && userStopRef.current) {\n const latencyMs = performance.now() - userStopRef.current.ts;\n emitEvent('responseLatency', {\n conversationId: conversationId!,\n latencyMs,\n turnDetectionStrategy: userStopRef.current.turnDetectionStrategy,\n sequence: userStopRef.current.sequence,\n speechId: data.speechId ?? userStopRef.current.speechId,\n });\n userStopRef.current = null;\n }\n emitEvent(\n eventState === 'speaking' ? 'agentSpeakStart' : 'agentSpeakStop',\n { conversationId: conversationId! },\n );\n setAgentActivityState(eventState);\n break;\n }\n }\n\n function handleInactivityWarningEvent(data: InactivityWarningData) {\n emitEvent('inactivityWarning', {\n conversationId: conversationRef.current?.id!,\n gracePeriodSec: data.grace_period_sec,\n });\n }\n\n function handleInactivityTimeoutEvent(_data: InactivityTimeoutData) {\n inactivityTimeoutRef.current = true;\n emitEvent('inactivityTimeout', {\n conversationId: conversationRef.current?.id!,\n });\n }\n\n async function handleToolCallsEvent(data: ToolCallsData) {\n await toolRuntimeRef.current.resolveIncomingToolCalls(data, toolAdapter);\n }\n\n const dataChannelHandlers: {\n guard: (data: unknown) => boolean;\n handle: (data: unknown) => void | Promise<void>;\n }[] = [\n {\n guard: checkIsToolCallsData,\n handle: handleToolCallsEvent as (data: unknown) => Promise<void>,\n },\n {\n guard: checkIsTurnDetectionData,\n handle: handleTurnDetectionEvent as (data: unknown) => void,\n },\n {\n guard: checkIsInactivityWarningData,\n handle: handleInactivityWarningEvent as (data: unknown) => void,\n },\n {\n guard: checkIsInactivityTimeoutData,\n handle: handleInactivityTimeoutEvent as (data: unknown) => void,\n },\n ];\n\n function handleDataReceivedEvent(payload: Uint8Array) {\n const decoded = decodeDataPayload(payload);\n if (decoded === null) {\n return;\n }\n for (const { guard, handle } of dataChannelHandlers) {\n if (guard(decoded)) {\n void Promise.resolve(handle(decoded)).catch((error) => {\n console.error('Failed to handle data channel payload', error);\n });\n return;\n }\n }\n }\n\n function handleConnectedEvent() {\n setUserConnectionState('joined');\n setAgentConnectionState('joining');\n emitEvent('callStarted', {\n conversationId: conversationRef.current?.id!,\n });\n }\n\n async function handleEncryptionErrorEvent(error: Error) {\n console.error('Encryption error', error);\n setConversationStatus('error');\n }\n\n function handleDisconnectedEvent(reason?: DisconnectReason) {\n userStopRef.current = null;\n setAgentActivityState('idle');\n setAgentConnectionState('idle');\n setUserActivityState('idle');\n setUserConnectionState('idle');\n\n if (inactivityTimeoutRef.current) {\n inactivityTimeoutRef.current = false;\n setConversationStatus('inactivity-timeout');\n return;\n }\n\n switch (reason) {\n case DisconnectReason.CLIENT_INITIATED:\n break;\n case DisconnectReason.ROOM_CLOSED:\n setConversationStatus('destroyed');\n break;\n default:\n console.error('Conversation disconnected with reason', reason);\n setConversationStatus('error');\n break;\n }\n }\n\n function handleParticipantConnectedEvent(participant: RemoteParticipant) {\n if (shouldHandleParticipantConnected(participant, type)) {\n handleAgentJoin(participant);\n }\n }\n\n function handleAgentJoin(participant: RemoteParticipant) {\n setAgentConnectionState('joined');\n emitEvent('agentJoined', {\n conversationId: conversationRef.current?.id!,\n participant,\n });\n }\n\n function handleParticipantDisconnectedEvent(participant: RemoteParticipant) {\n setAgentConnectionState('left');\n emitEvent('agentLeft', {\n conversationId: conversationRef.current?.id!,\n participant,\n });\n }\n\n async function leave() {\n if (userConnectionState !== 'joined' && userConnectionState !== 'joining') {\n return;\n }\n\n try {\n setUserConnectionState('leaving');\n await room?.disconnect(true);\n setConversationStatus('destroyed');\n setJoinedAt(undefined);\n emitEvent('callEnded', {\n conversationId: conversationRef.current?.id!,\n });\n } catch (error) {\n console.error('Failed to leave', error);\n setConversationStatus('error');\n }\n }\n\n async function handleExpire() {\n emitEvent('timeExceeded', {\n conversationId: conversationRef.current?.id!,\n limit: conversationRef.current?.limit!,\n });\n await leave();\n setConversationStatus('exceeded');\n }\n\n async function interrupt() {\n emitEvent('interrupt', {\n conversationId: conversationRef.current?.id!,\n });\n\n await publishConversationDataMessage(room, {}, 'interrupt');\n }\n\n async function dismissInactivity() {\n await publishConversationDataMessage(\n room,\n { type: 'inactivity_dismiss' },\n 'inactivity_dismiss',\n );\n }\n\n async function send(message: string, attachments: Attachment[] = []) {\n await room?.localParticipant.sendText(message, {\n topic: 'lk.chat',\n attributes: {\n attachments: safeStringify(attachments),\n },\n });\n emitEvent('userSendMessage', {\n conversationId: conversationRef.current?.id!,\n message,\n });\n }\n\n function registerRoomEvents() {\n room\n .on('connected', handleConnectedEvent)\n .on('encryptionError', handleEncryptionErrorEvent)\n .on('disconnected', handleDisconnectedEvent)\n .on('transcriptionReceived', handleTranscriptionReceivedEvent)\n .on('participantConnected', handleParticipantConnectedEvent)\n .on('participantDisconnected', handleParticipantDisconnectedEvent)\n .on('dataReceived', handleDataReceivedEvent);\n }\n\n function unregisterRoomEvents() {\n room.off('connected', handleConnectedEvent);\n room.off('encryptionError', handleEncryptionErrorEvent);\n room.off('disconnected', handleDisconnectedEvent);\n room.off('transcriptionReceived', handleTranscriptionReceivedEvent);\n room.off('participantConnected', handleParticipantConnectedEvent);\n room.off('participantDisconnected', handleParticipantDisconnectedEvent);\n room.off('dataReceived', handleDataReceivedEvent);\n }\n\n return {\n status: conversationStatus,\n conversation: conversation.data,\n join,\n send,\n interrupt,\n dismissInactivity,\n leave,\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useEffect","useRef","DisconnectReason","setLogLevel","ParticipantKind","LogLevel","invariant","useTimer","useLatest","useUpdateEffect","useEnsureRoom","useMediaDeviceSelect","useAtom","useAtomValue","useSetAtom","state","useCreateConversation","useSpeechActivityDetector","useEmitConversationEvent","useToolCallingRuntime","checkIsTurnDetectionData","checkIsInactivityWarningData","checkIsInactivityTimeoutData","checkIsToolCallsData","createLivekitToolAdapter","decodeDataPayload","findJoinedAgentParticipant","mergeTranscriptions","publishConversationDataMessage","safeStringify","shouldHandleParticipantConnected","ACOUSTIC_USER_HANGOVER_MS","ACOUSTIC_USER_THRESHOLD_RMS","useConversation","_ref","_conversation$data2","_ref$autoJoin","autoJoin","type","config","_ref$startWithMicroph","startWithMicrophone","_ref$startWithCamera","startWithCamera","silent","conversation","room","speaker","_useAtom","microphone","setMicrophone","_useAtom2","camera","setCamera","toolRuntime","toolAdapter","conversationRef","data","toolRuntimeRef","_useAtom3","conversationStatus","setConversationStatus","_useAtom4","userConnectionState","setUserConnectionState","_useAtom5","joinedAt","setJoinedAt","_useMediaDeviceSelect","kind","setMicrophoneDeviceId","setActiveMediaDevice","_useMediaDeviceSelect2","setSpeakerDeviceId","_useMediaDeviceSelect3","requestPermissions","enabled","setCameraDeviceId","setTranscriptions","transcriptions","_useAtom6","agentConnectionState","setAgentConnectionState","setAgentActivityState","agentActivityState","setUserActivityState","userActivityState","emitEvent","inactivityTimeoutRef","acousticPhaseRef","acousticT0Ref","latestTurnMetadataRef","isConnectedRef","timer","autoStart","onExpire","handleExpire","expiryTimestamp","Date","registerRoomEvents","unregisterRoomEvents","deviceId","localParticipant","setMicrophoneEnabled","setCameraEnabled","resolution","width","height","join","_conversation$data","limit","undefined","restart","getTime","_conversation$data3","url","URL","accessToken","searchParams","get","connect","remoteParticipant","handleAgentJoin","enableMediaDevices","error","console","handleTranscriptionReceivedEvent","segments","participant","map","s","id","message","text","source","AGENT","old","handleTurnDetectionEvent","_conversationRef$curr","conversationId","current","participantKind","eventState","turnDetectionStrategy","sequence","speechId","handleAcousticUserOnset","handleAcousticUserOffset","timestampMs","handleAcousticAgentOnset","_conversationRef$curr2","latencyMs","onSpeechStart","onSpeechEnd","thresholdRms","hangoverMs","handleInactivityWarningEvent","_conversationRef$curr3","gracePeriodSec","grace_period_sec","handleInactivityTimeoutEvent","_data","_conversationRef$curr4","handleToolCallsEvent","resolveIncomingToolCalls","dataChannelHandlers","guard","handle","handleDataReceivedEvent","payload","decoded","_ref2","Promise","resolve","catch","handleConnectedEvent","_conversationRef$curr5","handleEncryptionErrorEvent","handleDisconnectedEvent","reason","CLIENT_INITIATED","ROOM_CLOSED","handleParticipantConnectedEvent","_conversationRef$curr6","handleParticipantDisconnectedEvent","_conversationRef$curr7","leave","_conversationRef$curr8","disconnect","_conversationRef$curr9","_conversationRef$curr0","interrupt","_conversationRef$curr1","dismissInactivity","send","attachments","_conversationRef$curr10","sendText","topic","attributes","on","off","status"],"sources":["../../../../../src/hooks/useConversation/livekit/useConversation.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport {\n DisconnectReason,\n Participant,\n setLogLevel,\n ParticipantKind,\n RemoteParticipant,\n TranscriptionSegment,\n LogLevel,\n} from 'livekit-client';\nimport invariant from 'invariant';\nimport { useTimer } from 'react-timer-hook';\nimport { useLatest, useUpdateEffect } from 'react-use';\nimport { useEnsureRoom, useMediaDeviceSelect } from '@livekit/components-react';\nimport { useAtom, useAtomValue, useSetAtom } from 'jotai';\nimport {\n Conversation,\n state,\n type ConversationTranscription,\n} from '../../../conversation';\nimport type {\n UseConversationResult,\n UseConversationOptions,\n Attachment,\n} from '../types';\nimport type {\n InactivityWarningData,\n InactivityTimeoutData,\n TurnDetectionData,\n ToolCallsData,\n} from './types';\nimport { useCreateConversation } from '../../useCreateConversation';\nimport { useSpeechActivityDetector } from '../../useSpeechActivityDetector';\n\nimport { useEmitConversationEvent } from '../../useConversationEvents/useEmitConversationEvent';\nimport { useToolCallingRuntime } from '../../useTools/runtime/useToolCallingRuntime';\nimport {\n checkIsTurnDetectionData,\n checkIsInactivityWarningData,\n checkIsInactivityTimeoutData,\n checkIsToolCallsData,\n createLivekitToolAdapter,\n decodeDataPayload,\n findJoinedAgentParticipant,\n mergeTranscriptions,\n publishConversationDataMessage,\n safeStringify,\n shouldHandleParticipantConnected,\n} from './utils';\nimport {\n ACOUSTIC_USER_HANGOVER_MS,\n ACOUSTIC_USER_THRESHOLD_RMS,\n} from './constants';\n\nexport function useConversation({\n autoJoin = true,\n type,\n config,\n startWithMicrophone = false,\n startWithCamera = false,\n}: UseConversationOptions): UseConversationResult {\n setLogLevel(LogLevel.silent);\n\n const conversation = useCreateConversation({ type, config });\n const room = useEnsureRoom();\n const speaker = useAtomValue(state.speaker);\n const [microphone, setMicrophone] = useAtom(state.microphone);\n const [camera, setCamera] = useAtom(state.camera);\n const toolRuntime = useToolCallingRuntime();\n const toolAdapter = createLivekitToolAdapter(room);\n\n // for use in register/unregister room events\n const conversationRef = useLatest<Conversation | undefined>(\n conversation.data,\n );\n const toolRuntimeRef = useLatest(toolRuntime);\n\n const [conversationStatus, setConversationStatus] = useAtom(\n state.conversationStatus,\n );\n\n const [userConnectionState, setUserConnectionState] = useAtom(\n state.userConnectionState,\n );\n\n const [joinedAt, setJoinedAt] = useAtom(state.joinedAt);\n\n const { setActiveMediaDevice: setMicrophoneDeviceId } = useMediaDeviceSelect({\n kind: 'audioinput',\n });\n const { setActiveMediaDevice: setSpeakerDeviceId } = useMediaDeviceSelect({\n kind: 'audiooutput',\n });\n const { setActiveMediaDevice: setCameraDeviceId } = useMediaDeviceSelect({\n kind: 'videoinput',\n requestPermissions: camera.enabled,\n });\n\n const setTranscriptions = useSetAtom(state.transcriptions);\n const [agentConnectionState, setAgentConnectionState] = useAtom(\n state.agentConnectionState,\n );\n const setAgentActivityState = useSetAtom(state.agentActivityState);\n const setUserActivityState = useSetAtom(state.userActivityState);\n const emitEvent = useEmitConversationEvent();\n\n const inactivityTimeoutRef = useRef(false);\n const acousticPhaseRef = useRef<'idle' | 'waitingForAgent' | 'busy'>('idle');\n const acousticT0Ref = useRef<number | null>(null);\n const latestTurnMetadataRef = useRef<{\n turnDetectionStrategy?: string;\n sequence?: number;\n speechId?: string;\n }>({});\n const isConnectedRef = useRef(false);\n\n const timer = useTimer({\n autoStart: false,\n onExpire: handleExpire,\n expiryTimestamp: new Date(),\n });\n\n useEffect(() => {\n if (!room) {\n return;\n }\n registerRoomEvents();\n\n return () => {\n unregisterRoomEvents();\n };\n }, [room]);\n\n useUpdateEffect(() => {\n setMicrophoneDeviceId(microphone.deviceId);\n }, [microphone.deviceId]);\n\n useUpdateEffect(() => {\n setSpeakerDeviceId(speaker.deviceId);\n }, [speaker.deviceId]);\n\n useUpdateEffect(() => {\n setCameraDeviceId(camera.deviceId);\n }, [camera.deviceId]);\n\n useUpdateEffect(() => {\n if (agentConnectionState !== 'joined') {\n return;\n }\n room.localParticipant.setMicrophoneEnabled(microphone.enabled);\n }, [microphone.enabled, agentConnectionState]);\n\n useUpdateEffect(() => {\n if (agentConnectionState !== 'joined') {\n return;\n }\n room.localParticipant.setCameraEnabled(camera.enabled, {\n resolution: { width: 1920, height: 1080 },\n });\n }, [camera.enabled, agentConnectionState]);\n\n useEffect(() => {\n if (!autoJoin) {\n return;\n }\n\n if (conversationStatus === 'created') {\n void join();\n }\n }, [conversationStatus, autoJoin]);\n\n useEffect(() => {\n if (!joinedAt || conversation.data?.limit === undefined) {\n return;\n }\n\n timer.restart(new Date(joinedAt.getTime() + conversation.data!.limit!));\n }, [joinedAt, conversation.data?.limit]);\n\n async function join() {\n invariant(userConnectionState === 'idle', 'Already joined');\n invariant(conversation.data, 'Conversation is not ready');\n const url = new URL(conversation?.data?.url || '');\n const accessToken = url.searchParams.get('token');\n try {\n setUserConnectionState('joining');\n await room?.connect(conversation.data.url, accessToken!, {});\n\n // sometimes the agent is already joined before the user joins the conversation\n const remoteParticipant = findJoinedAgentParticipant(room, type);\n if (remoteParticipant && agentConnectionState !== 'joined') {\n handleAgentJoin(remoteParticipant);\n }\n\n enableMediaDevices();\n } catch (error) {\n console.error('Failed to join', error);\n setUserConnectionState('join-error');\n }\n }\n\n function enableMediaDevices() {\n if (startWithMicrophone) {\n setMicrophone({ ...microphone, enabled: true });\n }\n if (startWithCamera && type === 'video') {\n setCamera({ ...camera, enabled: true });\n }\n }\n\n function handleTranscriptionReceivedEvent(\n segments: TranscriptionSegment[],\n participant?: Participant,\n ) {\n const transcriptions: ConversationTranscription[] = segments.map((s) => ({\n id: s.id,\n message: s.text,\n source: participant?.kind === ParticipantKind.AGENT ? 'agent' : 'user',\n }));\n\n setTranscriptions((old) => mergeTranscriptions(transcriptions, old));\n }\n\n function handleTurnDetectionEvent(data: TurnDetectionData) {\n const conversationId = conversationRef.current?.id;\n const { participantKind, state: eventState } = data;\n switch (participantKind) {\n case 'STANDARD':\n if (eventState === 'listening') {\n latestTurnMetadataRef.current = {\n turnDetectionStrategy: data.turnDetectionStrategy,\n sequence: data.sequence,\n speechId: data.speechId,\n };\n }\n emitEvent(\n eventState === 'speaking' ? 'userSpeakStart' : 'userSpeakStop',\n { conversationId: conversationId! },\n );\n setUserActivityState(eventState);\n break;\n case 'AGENT':\n if (data.speechId) {\n latestTurnMetadataRef.current = {\n ...latestTurnMetadataRef.current,\n speechId: data.speechId,\n };\n }\n emitEvent(\n eventState === 'speaking' ? 'agentSpeakStart' : 'agentSpeakStop',\n { conversationId: conversationId! },\n );\n setAgentActivityState(eventState);\n break;\n }\n }\n\n function handleAcousticUserOnset() {\n if (!isConnectedRef.current) {\n return;\n }\n if (acousticPhaseRef.current === 'waitingForAgent') {\n acousticPhaseRef.current = 'idle';\n acousticT0Ref.current = null;\n }\n }\n\n function handleAcousticUserOffset(timestampMs: number) {\n if (!isConnectedRef.current || acousticPhaseRef.current !== 'idle') {\n return;\n }\n acousticT0Ref.current = timestampMs - ACOUSTIC_USER_HANGOVER_MS;\n acousticPhaseRef.current = 'waitingForAgent';\n }\n\n function handleAcousticAgentOnset(timestampMs: number) {\n if (\n !isConnectedRef.current ||\n acousticPhaseRef.current !== 'waitingForAgent' ||\n acousticT0Ref.current == null\n ) {\n return;\n }\n\n const latencyMs = timestampMs - acousticT0Ref.current;\n acousticT0Ref.current = null;\n acousticPhaseRef.current = 'busy';\n\n if (latencyMs < 0 || latencyMs > 15000) {\n return;\n }\n\n emitEvent('responseLatency', {\n conversationId: conversationRef.current?.id!,\n latencyMs,\n turnDetectionStrategy:\n latestTurnMetadataRef.current.turnDetectionStrategy!,\n sequence: latestTurnMetadataRef.current.sequence,\n speechId: latestTurnMetadataRef.current.speechId,\n });\n }\n\n useSpeechActivityDetector({\n source: 'user',\n onSpeechStart: handleAcousticUserOnset,\n onSpeechEnd: handleAcousticUserOffset,\n thresholdRms: ACOUSTIC_USER_THRESHOLD_RMS,\n hangoverMs: ACOUSTIC_USER_HANGOVER_MS,\n });\n useSpeechActivityDetector({\n source: 'agent',\n onSpeechStart: handleAcousticAgentOnset,\n });\n\n function handleInactivityWarningEvent(data: InactivityWarningData) {\n emitEvent('inactivityWarning', {\n conversationId: conversationRef.current?.id!,\n gracePeriodSec: data.grace_period_sec,\n });\n }\n\n function handleInactivityTimeoutEvent(_data: InactivityTimeoutData) {\n inactivityTimeoutRef.current = true;\n emitEvent('inactivityTimeout', {\n conversationId: conversationRef.current?.id!,\n });\n }\n\n async function handleToolCallsEvent(data: ToolCallsData) {\n await toolRuntimeRef.current.resolveIncomingToolCalls(data, toolAdapter);\n }\n\n const dataChannelHandlers: {\n guard: (data: unknown) => boolean;\n handle: (data: unknown) => void | Promise<void>;\n }[] = [\n {\n guard: checkIsToolCallsData,\n handle: handleToolCallsEvent as (data: unknown) => Promise<void>,\n },\n {\n guard: checkIsTurnDetectionData,\n handle: handleTurnDetectionEvent as (data: unknown) => void,\n },\n {\n guard: checkIsInactivityWarningData,\n handle: handleInactivityWarningEvent as (data: unknown) => void,\n },\n {\n guard: checkIsInactivityTimeoutData,\n handle: handleInactivityTimeoutEvent as (data: unknown) => void,\n },\n ];\n\n function handleDataReceivedEvent(payload: Uint8Array) {\n const decoded = decodeDataPayload(payload);\n if (decoded === null) {\n return;\n }\n for (const { guard, handle } of dataChannelHandlers) {\n if (guard(decoded)) {\n void Promise.resolve(handle(decoded)).catch((error) => {\n console.error('Failed to handle data channel payload', error);\n });\n return;\n }\n }\n }\n\n function handleConnectedEvent() {\n isConnectedRef.current = true;\n setUserConnectionState('joined');\n setAgentConnectionState('joining');\n emitEvent('callStarted', {\n conversationId: conversationRef.current?.id!,\n });\n }\n\n async function handleEncryptionErrorEvent(error: Error) {\n console.error('Encryption error', error);\n setConversationStatus('error');\n }\n\n function handleDisconnectedEvent(reason?: DisconnectReason) {\n isConnectedRef.current = false;\n acousticPhaseRef.current = 'idle';\n acousticT0Ref.current = null;\n latestTurnMetadataRef.current = {};\n setAgentActivityState('idle');\n setAgentConnectionState('idle');\n setUserActivityState('idle');\n setUserConnectionState('idle');\n\n if (inactivityTimeoutRef.current) {\n inactivityTimeoutRef.current = false;\n setConversationStatus('inactivity-timeout');\n return;\n }\n\n switch (reason) {\n case DisconnectReason.CLIENT_INITIATED:\n break;\n case DisconnectReason.ROOM_CLOSED:\n setConversationStatus('destroyed');\n break;\n default:\n console.error('Conversation disconnected with reason', reason);\n setConversationStatus('error');\n break;\n }\n }\n\n function handleParticipantConnectedEvent(participant: RemoteParticipant) {\n if (shouldHandleParticipantConnected(participant, type)) {\n handleAgentJoin(participant);\n }\n }\n\n function handleAgentJoin(participant: RemoteParticipant) {\n setAgentConnectionState('joined');\n emitEvent('agentJoined', {\n conversationId: conversationRef.current?.id!,\n participant,\n });\n }\n\n function handleParticipantDisconnectedEvent(participant: RemoteParticipant) {\n setAgentConnectionState('left');\n emitEvent('agentLeft', {\n conversationId: conversationRef.current?.id!,\n participant,\n });\n }\n\n async function leave() {\n if (userConnectionState !== 'joined' && userConnectionState !== 'joining') {\n return;\n }\n\n try {\n setUserConnectionState('leaving');\n await room?.disconnect(true);\n setConversationStatus('destroyed');\n setJoinedAt(undefined);\n emitEvent('callEnded', {\n conversationId: conversationRef.current?.id!,\n });\n } catch (error) {\n console.error('Failed to leave', error);\n setConversationStatus('error');\n }\n }\n\n async function handleExpire() {\n emitEvent('timeExceeded', {\n conversationId: conversationRef.current?.id!,\n limit: conversationRef.current?.limit!,\n });\n await leave();\n setConversationStatus('exceeded');\n }\n\n async function interrupt() {\n emitEvent('interrupt', {\n conversationId: conversationRef.current?.id!,\n });\n\n await publishConversationDataMessage(room, {}, 'interrupt');\n }\n\n async function dismissInactivity() {\n await publishConversationDataMessage(\n room,\n { type: 'inactivity_dismiss' },\n 'inactivity_dismiss',\n );\n }\n\n async function send(message: string, attachments: Attachment[] = []) {\n await room?.localParticipant.sendText(message, {\n topic: 'lk.chat',\n attributes: {\n attachments: safeStringify(attachments),\n },\n });\n emitEvent('userSendMessage', {\n conversationId: conversationRef.current?.id!,\n message,\n });\n }\n\n function registerRoomEvents() {\n room\n .on('connected', handleConnectedEvent)\n .on('encryptionError', handleEncryptionErrorEvent)\n .on('disconnected', handleDisconnectedEvent)\n .on('transcriptionReceived', handleTranscriptionReceivedEvent)\n .on('participantConnected', handleParticipantConnectedEvent)\n .on('participantDisconnected', handleParticipantDisconnectedEvent)\n .on('dataReceived', handleDataReceivedEvent);\n }\n\n function unregisterRoomEvents() {\n room.off('connected', handleConnectedEvent);\n room.off('encryptionError', handleEncryptionErrorEvent);\n room.off('disconnected', handleDisconnectedEvent);\n room.off('transcriptionReceived', handleTranscriptionReceivedEvent);\n room.off('participantConnected', handleParticipantConnectedEvent);\n room.off('participantDisconnected', handleParticipantDisconnectedEvent);\n room.off('dataReceived', handleDataReceivedEvent);\n }\n\n return {\n status: conversationStatus,\n conversation: conversation.data,\n join,\n send,\n interrupt,\n dismissInactivity,\n leave,\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -7,7 +7,9 @@ import { useSdkConsumer } from '../../sdk-consumer';
7
7
  import { limit } from './helpers';
8
8
  export function useCreateConversation(options) {
9
9
  const type = options.type,
10
- chatConfig = options.config;
10
+ chatConfig = options.config,
11
+ firstConversation = options.firstConversation,
12
+ lastConversationTopic = options.lastConversationTopic;
11
13
  const _useSdkConsumer = useSdkConsumer(),
12
14
  chatId = _useSdkConsumer.chatId,
13
15
  namespace = _useSdkConsumer.namespace,
@@ -53,7 +55,9 @@ export function useCreateConversation(options) {
53
55
  const result = await personaChatService.createInteractiveConversation({
54
56
  chatId,
55
57
  namespace,
56
- chatConfig
58
+ chatConfig,
59
+ firstConversation,
60
+ lastConversationTopic
57
61
  });
58
62
  setConversation({
59
63
  type: 'video',
@@ -68,7 +72,9 @@ export function useCreateConversation(options) {
68
72
  const result = await personaChatService.createVoiceConversation({
69
73
  chatId,
70
74
  namespace,
71
- chatConfig
75
+ chatConfig,
76
+ firstConversation,
77
+ lastConversationTopic
72
78
  });
73
79
  setConversation({
74
80
  type: 'audio',
@@ -86,11 +92,13 @@ export function useCreateConversation(options) {
86
92
  invariant(conversation, 'Conversation is not ready');
87
93
  invariant(personaChatService, 'personaChatService is required.');
88
94
  setConversationStatus('destroying');
89
- await personaChatService.endInteractiveConversation({
90
- chatId,
91
- conversationId: conversation.id,
92
- namespace
93
- }).catch(() => {});
95
+ if (conversation.type === 'video') {
96
+ await personaChatService.endInteractiveConversation({
97
+ chatId,
98
+ conversationId: conversation.id,
99
+ namespace
100
+ }).catch(() => {});
101
+ }
94
102
  setConversationStatus('destroyed');
95
103
  setConversation(undefined);
96
104
  }
@@ -1 +1 @@
1
- {"version":3,"names":["invariant","useEffect","useAtom","useSetAtom","useUnmount","state","useSdkConsumer","limit","useCreateConversation","options","type","chatConfig","config","_useSdkConsumer","chatId","namespace","personaChatService","setConversationStatus","conversationStatus","_useAtom","conversation","setConversation","create","then","catch","error","_err$message","err","status","message","includes","destroy","data","result","createInteractiveConversation","url","id","createVoiceConversation","Error","endInteractiveConversation","conversationId","undefined"],"sources":["../../../../src/hooks/useCreateConversation/useCreateConversation.ts"],"sourcesContent":["import invariant from 'invariant';\nimport { useEffect } from 'react';\nimport { useAtom, useSetAtom } from 'jotai';\nimport { useUnmount } from 'react-use';\n\nimport {\n state,\n type ConversationType,\n type Conversation,\n} from '../../conversation';\nimport { useSdkConsumer } from '../../sdk-consumer';\nimport { limit } from './helpers';\n\nexport type UseCreateConversationOptions = {\n type: ConversationType;\n config?: object;\n};\n\nexport type UseCreateConversationResult = {\n data?: Conversation;\n destroy(): Promise<void>;\n};\n\nexport function useCreateConversation(\n options: UseCreateConversationOptions,\n): UseCreateConversationResult {\n const { type, config: chatConfig } = options;\n\n const { chatId, namespace, personaChatService } = useSdkConsumer();\n\n const setConversationStatus = useSetAtom(state.conversationStatus);\n const [conversation, setConversation] = useAtom(state.conversation);\n\n useEffect(() => {\n void create()\n .then(() => setConversationStatus('created'))\n .catch((error: unknown) => {\n const err = error as { status?: number; message?: string };\n if (err.status === 404 && err.message?.includes('not found')) {\n setConversationStatus('voice-not-found');\n } else {\n setConversationStatus('initialization-error');\n }\n });\n }, [type, chatId]);\n\n useUnmount(() => {\n if (!conversation) {\n return;\n }\n void destroy();\n });\n\n useEffect(() => {\n if (!conversation) {\n return;\n }\n if (conversation.limit === 0) {\n setConversationStatus('exceeded');\n }\n }, [conversation]);\n\n return { data: conversation, destroy };\n\n async function create() {\n invariant(\n personaChatService,\n 'personaChatService is required. Pass it as a prop to LegendsPlatform.',\n );\n\n switch (type) {\n case 'video': {\n const result = await personaChatService.createInteractiveConversation({\n chatId,\n namespace,\n chatConfig,\n });\n setConversation({\n type: 'video',\n url: result.url,\n id: result.id,\n limit: limit(result.limit),\n });\n break;\n }\n case 'audio': {\n const result = await personaChatService.createVoiceConversation({\n chatId,\n namespace,\n chatConfig,\n });\n setConversation({\n type: 'audio',\n url: result.url,\n id: result.id,\n limit: limit(result.limit),\n });\n break;\n }\n default:\n throw new Error(`Unsupported conversation type: ${type}`);\n }\n }\n\n async function destroy() {\n invariant(conversation, 'Conversation is not ready');\n invariant(personaChatService, 'personaChatService is required.');\n\n setConversationStatus('destroying');\n\n await personaChatService\n .endInteractiveConversation({\n chatId,\n conversationId: conversation.id,\n namespace,\n })\n .catch(() => {});\n\n setConversationStatus('destroyed');\n setConversation(undefined);\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["invariant","useEffect","useAtom","useSetAtom","useUnmount","state","useSdkConsumer","limit","useCreateConversation","options","type","chatConfig","config","firstConversation","lastConversationTopic","_useSdkConsumer","chatId","namespace","personaChatService","setConversationStatus","conversationStatus","_useAtom","conversation","setConversation","create","then","catch","error","_err$message","err","status","message","includes","destroy","data","result","createInteractiveConversation","url","id","createVoiceConversation","Error","endInteractiveConversation","conversationId","undefined"],"sources":["../../../../src/hooks/useCreateConversation/useCreateConversation.ts"],"sourcesContent":["import invariant from 'invariant';\nimport { useEffect } from 'react';\nimport { useAtom, useSetAtom } from 'jotai';\nimport { useUnmount } from 'react-use';\n\nimport {\n state,\n type ConversationType,\n type Conversation,\n} from '../../conversation';\nimport { useSdkConsumer } from '../../sdk-consumer';\nimport { limit } from './helpers';\n\nexport type UseCreateConversationOptions = {\n type: ConversationType;\n config?: object;\n /** Whether this is the visitor's first-ever conversation with the persona (drives the opening message). */\n firstConversation?: boolean;\n /** Title of the visitor's last meaningful conversation, for a returning-visitor greeting. */\n lastConversationTopic?: string;\n};\n\nexport type UseCreateConversationResult = {\n data?: Conversation;\n destroy(): Promise<void>;\n};\n\nexport function useCreateConversation(\n options: UseCreateConversationOptions,\n): UseCreateConversationResult {\n const {\n type,\n config: chatConfig,\n firstConversation,\n lastConversationTopic,\n } = options;\n\n const { chatId, namespace, personaChatService } = useSdkConsumer();\n\n const setConversationStatus = useSetAtom(state.conversationStatus);\n const [conversation, setConversation] = useAtom(state.conversation);\n\n useEffect(() => {\n void create()\n .then(() => setConversationStatus('created'))\n .catch((error: unknown) => {\n const err = error as { status?: number; message?: string };\n if (err.status === 404 && err.message?.includes('not found')) {\n setConversationStatus('voice-not-found');\n } else {\n setConversationStatus('initialization-error');\n }\n });\n }, [type, chatId]);\n\n useUnmount(() => {\n if (!conversation) {\n return;\n }\n void destroy();\n });\n\n useEffect(() => {\n if (!conversation) {\n return;\n }\n if (conversation.limit === 0) {\n setConversationStatus('exceeded');\n }\n }, [conversation]);\n\n return { data: conversation, destroy };\n\n async function create() {\n invariant(\n personaChatService,\n 'personaChatService is required. Pass it as a prop to LegendsPlatform.',\n );\n\n switch (type) {\n case 'video': {\n const result = await personaChatService.createInteractiveConversation({\n chatId,\n namespace,\n chatConfig,\n firstConversation,\n lastConversationTopic,\n });\n setConversation({\n type: 'video',\n url: result.url,\n id: result.id,\n limit: limit(result.limit),\n });\n break;\n }\n case 'audio': {\n const result = await personaChatService.createVoiceConversation({\n chatId,\n namespace,\n chatConfig,\n firstConversation,\n lastConversationTopic,\n });\n setConversation({\n type: 'audio',\n url: result.url,\n id: result.id,\n limit: limit(result.limit),\n });\n break;\n }\n default:\n throw new Error(`Unsupported conversation type: ${type}`);\n }\n }\n\n async function destroy() {\n invariant(conversation, 'Conversation is not ready');\n invariant(personaChatService, 'personaChatService is required.');\n\n setConversationStatus('destroying');\n\n if (conversation.type === 'video') {\n await personaChatService\n .endInteractiveConversation({\n chatId,\n conversationId: conversation.id,\n namespace,\n })\n .catch(() => {});\n }\n\n setConversationStatus('destroyed');\n setConversation(undefined);\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,83 @@
1
+ import { SPEECH_ACTIVITY_PROCESSOR_NAME, SPEECH_ACTIVITY_PROCESSOR_SOURCE } from './speechActivityProcessorSource';
2
+ const DEFAULT_THRESHOLD_RMS = 0.015;
3
+ const DEFAULT_HANGOVER_MS = 300;
4
+ const WORKLET_BLOCK_SIZE = 128;
5
+ /**
6
+ * Attaches an AudioWorkletNode-based RMS threshold+hangover detector to a
7
+ * LiveKit track's underlying MediaStreamTrack. Runs on the dedicated audio
8
+ * rendering thread — unlike requestAnimationFrame+AnalyserNode, it keeps
9
+ * running when the tab is backgrounded.
10
+ */
11
+ export function setupSpeechActivityDetector(track, options) {
12
+ if (!track) {
13
+ return {
14
+ cleanup: () => {}
15
+ };
16
+ }
17
+ let cancelled = false;
18
+ let audioContext = null;
19
+ let mediaStreamSource = null;
20
+ let node = null;
21
+ const thresholdRms = options.thresholdRms ?? DEFAULT_THRESHOLD_RMS;
22
+ const hangoverMs = options.hangoverMs ?? DEFAULT_HANGOVER_MS;
23
+ const setup = async () => {
24
+ const ctx = new AudioContext();
25
+ audioContext = ctx;
26
+ if (ctx.state === 'suspended') {
27
+ await ctx.resume();
28
+ }
29
+
30
+ // Hangover is expressed in ms; convert to a block count using this
31
+ // context's actual sample rate (block size is fixed at 128 samples by
32
+ // the Web Audio spec, but wall-clock duration per block varies with
33
+ // sample rate across devices/browsers).
34
+ const hangoverBlocks = Math.max(1, Math.round(hangoverMs / 1000 * (ctx.sampleRate / WORKLET_BLOCK_SIZE)));
35
+ const blob = new Blob([SPEECH_ACTIVITY_PROCESSOR_SOURCE], {
36
+ type: 'application/javascript'
37
+ });
38
+ const blobUrl = URL.createObjectURL(blob);
39
+ try {
40
+ await ctx.audioWorklet.addModule(blobUrl);
41
+ } finally {
42
+ URL.revokeObjectURL(blobUrl);
43
+ }
44
+ if (cancelled) {
45
+ await ctx.close();
46
+ return;
47
+ }
48
+ mediaStreamSource = ctx.createMediaStreamSource(new MediaStream([track.mediaStreamTrack]));
49
+ node = new AudioWorkletNode(ctx, SPEECH_ACTIVITY_PROCESSOR_NAME, {
50
+ processorOptions: {
51
+ thresholdRms,
52
+ hangoverBlocks
53
+ }
54
+ });
55
+ node.port.onmessage = event => {
56
+ var _event$data, _event$data2;
57
+ const timestampMs = performance.now();
58
+ if (((_event$data = event.data) == null ? void 0 : _event$data.type) === 'onset') {
59
+ options.onSpeechStart == null || options.onSpeechStart(timestampMs);
60
+ } else if (((_event$data2 = event.data) == null ? void 0 : _event$data2.type) === 'offset') {
61
+ options.onSpeechEnd == null || options.onSpeechEnd(timestampMs);
62
+ }
63
+ };
64
+ mediaStreamSource.connect(node);
65
+ };
66
+ setup().catch(error => {
67
+ // eslint-disable-next-line no-console
68
+ console.error('[useSpeechActivityDetector] failed to set up AudioWorklet', error);
69
+ });
70
+ return {
71
+ cleanup: () => {
72
+ var _mediaStreamSource, _audioContext;
73
+ cancelled = true;
74
+ if (node) {
75
+ node.port.onmessage = null;
76
+ node.disconnect();
77
+ }
78
+ (_mediaStreamSource = mediaStreamSource) == null || _mediaStreamSource.disconnect();
79
+ void ((_audioContext = audioContext) == null ? void 0 : _audioContext.close());
80
+ }
81
+ };
82
+ }
83
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SPEECH_ACTIVITY_PROCESSOR_NAME","SPEECH_ACTIVITY_PROCESSOR_SOURCE","DEFAULT_THRESHOLD_RMS","DEFAULT_HANGOVER_MS","WORKLET_BLOCK_SIZE","setupSpeechActivityDetector","track","options","cleanup","cancelled","audioContext","mediaStreamSource","node","thresholdRms","hangoverMs","setup","ctx","AudioContext","state","resume","hangoverBlocks","Math","max","round","sampleRate","blob","Blob","type","blobUrl","URL","createObjectURL","audioWorklet","addModule","revokeObjectURL","close","createMediaStreamSource","MediaStream","mediaStreamTrack","AudioWorkletNode","processorOptions","port","onmessage","event","_event$data","_event$data2","timestampMs","performance","now","data","onSpeechStart","onSpeechEnd","connect","catch","error","console","_mediaStreamSource","_audioContext","disconnect"],"sources":["../../../../src/hooks/useSpeechActivityDetector/helpers.ts"],"sourcesContent":["import { Track } from 'livekit-client';\nimport {\n SPEECH_ACTIVITY_PROCESSOR_NAME,\n SPEECH_ACTIVITY_PROCESSOR_SOURCE,\n} from './speechActivityProcessorSource';\n\nconst DEFAULT_THRESHOLD_RMS = 0.015;\nconst DEFAULT_HANGOVER_MS = 300;\nconst WORKLET_BLOCK_SIZE = 128;\n\nexport type SetupSpeechActivityDetectorOptions = {\n thresholdRms?: number;\n hangoverMs?: number;\n minSpeechMs?: number;\n onSpeechStart?: (timestampMs: number) => void;\n onSpeechEnd?: (timestampMs: number) => void;\n onMisfire?: () => void;\n};\n\n/**\n * Attaches an AudioWorkletNode-based RMS threshold+hangover detector to a\n * LiveKit track's underlying MediaStreamTrack. Runs on the dedicated audio\n * rendering thread — unlike requestAnimationFrame+AnalyserNode, it keeps\n * running when the tab is backgrounded.\n */\nexport function setupSpeechActivityDetector(\n track: Track | undefined,\n options: SetupSpeechActivityDetectorOptions,\n): { cleanup: () => void } {\n if (!track) {\n return { cleanup: () => {} };\n }\n\n let cancelled = false;\n let audioContext: AudioContext | null = null;\n let mediaStreamSource: MediaStreamAudioSourceNode | null = null;\n let node: AudioWorkletNode | null = null;\n\n const thresholdRms = options.thresholdRms ?? DEFAULT_THRESHOLD_RMS;\n const hangoverMs = options.hangoverMs ?? DEFAULT_HANGOVER_MS;\n\n const setup = async () => {\n const ctx = new AudioContext();\n audioContext = ctx;\n if (ctx.state === 'suspended') {\n await ctx.resume();\n }\n\n // Hangover is expressed in ms; convert to a block count using this\n // context's actual sample rate (block size is fixed at 128 samples by\n // the Web Audio spec, but wall-clock duration per block varies with\n // sample rate across devices/browsers).\n const hangoverBlocks = Math.max(\n 1,\n Math.round((hangoverMs / 1000) * (ctx.sampleRate / WORKLET_BLOCK_SIZE)),\n );\n\n const blob = new Blob([SPEECH_ACTIVITY_PROCESSOR_SOURCE], {\n type: 'application/javascript',\n });\n const blobUrl = URL.createObjectURL(blob);\n try {\n await ctx.audioWorklet.addModule(blobUrl);\n } finally {\n URL.revokeObjectURL(blobUrl);\n }\n\n if (cancelled) {\n await ctx.close();\n return;\n }\n\n mediaStreamSource = ctx.createMediaStreamSource(\n new MediaStream([track.mediaStreamTrack]),\n );\n node = new AudioWorkletNode(ctx, SPEECH_ACTIVITY_PROCESSOR_NAME, {\n processorOptions: { thresholdRms, hangoverBlocks },\n });\n node.port.onmessage = (event: MessageEvent) => {\n const timestampMs = performance.now();\n if (event.data?.type === 'onset') {\n options.onSpeechStart?.(timestampMs);\n } else if (event.data?.type === 'offset') {\n options.onSpeechEnd?.(timestampMs);\n }\n };\n mediaStreamSource.connect(node);\n };\n\n setup().catch((error) => {\n // eslint-disable-next-line no-console\n console.error(\n '[useSpeechActivityDetector] failed to set up AudioWorklet',\n error,\n );\n });\n\n return {\n cleanup: () => {\n cancelled = true;\n if (node) {\n node.port.onmessage = null;\n node.disconnect();\n }\n mediaStreamSource?.disconnect();\n void audioContext?.close();\n },\n };\n}\n"],"mappings":"AACA,SACEA,8BAA8B,EAC9BC,gCAAgC,QAC3B,iCAAiC;AAExC,MAAMC,qBAAqB,GAAG,KAAK;AACnC,MAAMC,mBAAmB,GAAG,GAAG;AAC/B,MAAMC,kBAAkB,GAAG,GAAG;AAW9B;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,2BAA2BA,CACzCC,KAAwB,EACxBC,OAA2C,EAClB;EACzB,IAAI,CAACD,KAAK,EAAE;IACV,OAAO;MAAEE,OAAO,EAAEA,CAAA,KAAM,CAAC;IAAE,CAAC;EAC9B;EAEA,IAAIC,SAAS,GAAG,KAAK;EACrB,IAAIC,YAAiC,GAAG,IAAI;EAC5C,IAAIC,iBAAoD,GAAG,IAAI;EAC/D,IAAIC,IAA6B,GAAG,IAAI;EAExC,MAAMC,YAAY,GAAGN,OAAO,CAACM,YAAY,IAAIX,qBAAqB;EAClE,MAAMY,UAAU,GAAGP,OAAO,CAACO,UAAU,IAAIX,mBAAmB;EAE5D,MAAMY,KAAK,GAAG,MAAAA,CAAA,KAAY;IACxB,MAAMC,GAAG,GAAG,IAAIC,YAAY,CAAC,CAAC;IAC9BP,YAAY,GAAGM,GAAG;IAClB,IAAIA,GAAG,CAACE,KAAK,KAAK,WAAW,EAAE;MAC7B,MAAMF,GAAG,CAACG,MAAM,CAAC,CAAC;IACpB;;IAEA;IACA;IACA;IACA;IACA,MAAMC,cAAc,GAAGC,IAAI,CAACC,GAAG,CAC7B,CAAC,EACDD,IAAI,CAACE,KAAK,CAAET,UAAU,GAAG,IAAI,IAAKE,GAAG,CAACQ,UAAU,GAAGpB,kBAAkB,CAAC,CACxE,CAAC;IAED,MAAMqB,IAAI,GAAG,IAAIC,IAAI,CAAC,CAACzB,gCAAgC,CAAC,EAAE;MACxD0B,IAAI,EAAE;IACR,CAAC,CAAC;IACF,MAAMC,OAAO,GAAGC,GAAG,CAACC,eAAe,CAACL,IAAI,CAAC;IACzC,IAAI;MACF,MAAMT,GAAG,CAACe,YAAY,CAACC,SAAS,CAACJ,OAAO,CAAC;IAC3C,CAAC,SAAS;MACRC,GAAG,CAACI,eAAe,CAACL,OAAO,CAAC;IAC9B;IAEA,IAAInB,SAAS,EAAE;MACb,MAAMO,GAAG,CAACkB,KAAK,CAAC,CAAC;MACjB;IACF;IAEAvB,iBAAiB,GAAGK,GAAG,CAACmB,uBAAuB,CAC7C,IAAIC,WAAW,CAAC,CAAC9B,KAAK,CAAC+B,gBAAgB,CAAC,CAC1C,CAAC;IACDzB,IAAI,GAAG,IAAI0B,gBAAgB,CAACtB,GAAG,EAAEhB,8BAA8B,EAAE;MAC/DuC,gBAAgB,EAAE;QAAE1B,YAAY;QAAEO;MAAe;IACnD,CAAC,CAAC;IACFR,IAAI,CAAC4B,IAAI,CAACC,SAAS,GAAIC,KAAmB,IAAK;MAAA,IAAAC,WAAA,EAAAC,YAAA;MAC7C,MAAMC,WAAW,GAAGC,WAAW,CAACC,GAAG,CAAC,CAAC;MACrC,IAAI,EAAAJ,WAAA,GAAAD,KAAK,CAACM,IAAI,qBAAVL,WAAA,CAAYhB,IAAI,MAAK,OAAO,EAAE;QAChCpB,OAAO,CAAC0C,aAAa,YAArB1C,OAAO,CAAC0C,aAAa,CAAGJ,WAAW,CAAC;MACtC,CAAC,MAAM,IAAI,EAAAD,YAAA,GAAAF,KAAK,CAACM,IAAI,qBAAVJ,YAAA,CAAYjB,IAAI,MAAK,QAAQ,EAAE;QACxCpB,OAAO,CAAC2C,WAAW,YAAnB3C,OAAO,CAAC2C,WAAW,CAAGL,WAAW,CAAC;MACpC;IACF,CAAC;IACDlC,iBAAiB,CAACwC,OAAO,CAACvC,IAAI,CAAC;EACjC,CAAC;EAEDG,KAAK,CAAC,CAAC,CAACqC,KAAK,CAAEC,KAAK,IAAK;IACvB;IACAC,OAAO,CAACD,KAAK,CACX,2DAA2D,EAC3DA,KACF,CAAC;EACH,CAAC,CAAC;EAEF,OAAO;IACL7C,OAAO,EAAEA,CAAA,KAAM;MAAA,IAAA+C,kBAAA,EAAAC,aAAA;MACb/C,SAAS,GAAG,IAAI;MAChB,IAAIG,IAAI,EAAE;QACRA,IAAI,CAAC4B,IAAI,CAACC,SAAS,GAAG,IAAI;QAC1B7B,IAAI,CAAC6C,UAAU,CAAC,CAAC;MACnB;MACA,CAAAF,kBAAA,GAAA5C,iBAAiB,aAAjB4C,kBAAA,CAAmBE,UAAU,CAAC,CAAC;MAC/B,OAAAD,aAAA,GAAK9C,YAAY,qBAAZ8C,aAAA,CAActB,KAAK,CAAC,CAAC;IAC5B;EACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,3 @@
1
+ export * from './useSpeechActivityDetector';
2
+ export * from './types';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["../../../../src/hooks/useSpeechActivityDetector/index.ts"],"sourcesContent":["export * from './useSpeechActivityDetector';\nexport * from './types';\n"],"mappings":"AAAA,cAAc,6BAA6B;AAC3C,cAAc,SAAS","ignoreList":[]}
@@ -0,0 +1,51 @@
1
+ // AudioWorkletProcessor source, kept as a plain string and loaded via a Blob
2
+ // URL (see helpers.ts). AudioWorkletGlobalScope runs isolated code on the
3
+ // dedicated audio rendering thread — it is not throttled when the tab is
4
+ // backgrounded, unlike requestAnimationFrame-driven analysis. Threshold
5
+ // crossings are posted back to the main thread via `port.postMessage`.
6
+ export const SPEECH_ACTIVITY_PROCESSOR_NAME = 'speech-activity-processor';
7
+ export const SPEECH_ACTIVITY_PROCESSOR_SOURCE = `
8
+ class SpeechActivityProcessor extends AudioWorkletProcessor {
9
+ constructor(options) {
10
+ super();
11
+ const opts = (options && options.processorOptions) || {};
12
+ this.threshold = typeof opts.thresholdRms === 'number' ? opts.thresholdRms : 0.015;
13
+ this.hangoverBlocks = typeof opts.hangoverBlocks === 'number' ? opts.hangoverBlocks : 8;
14
+ this.speaking = false;
15
+ this.silentBlockCount = 0;
16
+ }
17
+
18
+ process(inputs) {
19
+ const input = inputs[0];
20
+ const channel = input && input[0];
21
+ if (!channel || channel.length === 0) {
22
+ return true;
23
+ }
24
+
25
+ let sumSquares = 0;
26
+ for (let i = 0; i < channel.length; i++) {
27
+ sumSquares += channel[i] * channel[i];
28
+ }
29
+ const rms = Math.sqrt(sumSquares / channel.length);
30
+ const isLoud = rms > this.threshold;
31
+
32
+ if (isLoud) {
33
+ this.silentBlockCount = 0;
34
+ if (!this.speaking) {
35
+ this.speaking = true;
36
+ this.port.postMessage({ type: 'onset', rms: rms });
37
+ }
38
+ } else if (this.speaking) {
39
+ this.silentBlockCount += 1;
40
+ if (this.silentBlockCount >= this.hangoverBlocks) {
41
+ this.speaking = false;
42
+ this.port.postMessage({ type: 'offset', rms: rms });
43
+ }
44
+ }
45
+ return true;
46
+ }
47
+ }
48
+
49
+ registerProcessor('${SPEECH_ACTIVITY_PROCESSOR_NAME}', SpeechActivityProcessor);
50
+ `;
51
+ //# sourceMappingURL=speechActivityProcessorSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SPEECH_ACTIVITY_PROCESSOR_NAME","SPEECH_ACTIVITY_PROCESSOR_SOURCE"],"sources":["../../../../src/hooks/useSpeechActivityDetector/speechActivityProcessorSource.ts"],"sourcesContent":["// AudioWorkletProcessor source, kept as a plain string and loaded via a Blob\n// URL (see helpers.ts). AudioWorkletGlobalScope runs isolated code on the\n// dedicated audio rendering thread — it is not throttled when the tab is\n// backgrounded, unlike requestAnimationFrame-driven analysis. Threshold\n// crossings are posted back to the main thread via `port.postMessage`.\nexport const SPEECH_ACTIVITY_PROCESSOR_NAME = 'speech-activity-processor';\n\nexport const SPEECH_ACTIVITY_PROCESSOR_SOURCE = `\nclass SpeechActivityProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n const opts = (options && options.processorOptions) || {};\n this.threshold = typeof opts.thresholdRms === 'number' ? opts.thresholdRms : 0.015;\n this.hangoverBlocks = typeof opts.hangoverBlocks === 'number' ? opts.hangoverBlocks : 8;\n this.speaking = false;\n this.silentBlockCount = 0;\n }\n\n process(inputs) {\n const input = inputs[0];\n const channel = input && input[0];\n if (!channel || channel.length === 0) {\n return true;\n }\n\n let sumSquares = 0;\n for (let i = 0; i < channel.length; i++) {\n sumSquares += channel[i] * channel[i];\n }\n const rms = Math.sqrt(sumSquares / channel.length);\n const isLoud = rms > this.threshold;\n\n if (isLoud) {\n this.silentBlockCount = 0;\n if (!this.speaking) {\n this.speaking = true;\n this.port.postMessage({ type: 'onset', rms: rms });\n }\n } else if (this.speaking) {\n this.silentBlockCount += 1;\n if (this.silentBlockCount >= this.hangoverBlocks) {\n this.speaking = false;\n this.port.postMessage({ type: 'offset', rms: rms });\n }\n }\n return true;\n }\n}\n\nregisterProcessor('${SPEECH_ACTIVITY_PROCESSOR_NAME}', SpeechActivityProcessor);\n`;\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,8BAA8B,GAAG,2BAA2B;AAEzE,OAAO,MAAMC,gCAAgC,GAAG;AAChD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqBD,8BAA8B;AACnD,CAAC","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["../../../../src/hooks/useSpeechActivityDetector/types.ts"],"sourcesContent":["export type UseSpeechActivityDetectorOptions = {\n source: 'user' | 'agent';\n onSpeechStart?: (timestampMs: number) => void;\n onSpeechEnd?: (timestampMs: number) => void;\n /** Fired instead of onSpeechEnd when a segment was shorter than minSpeechMs. */\n onMisfire?: () => void;\n /** RMS threshold (0-1) above which a block counts as \"loud\". Default 0.015. */\n thresholdRms?: number;\n /** Consecutive silence duration before an \"offset\" fires. Default 300ms. */\n hangoverMs?: number;\n /** Minimum onset-to-offset duration to count as real speech, not a blip/echo. Default 0 (no filtering). */\n minSpeechMs?: number;\n};\n"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,44 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { useParticipantTrack, Source } from '../useParticipantTrack';
3
+ import { setupSpeechActivityDetector } from './helpers';
4
+ /**
5
+ * Acoustic speech onset/offset detector for a participant's microphone
6
+ * track, driven by AudioWorkletNode rather than LiveKit's speaking-state
7
+ * events. Useful when the caller needs a timestamp tied to the actual audio
8
+ * signal (e.g. perceived end-to-end latency measurement) rather than a
9
+ * VAD/turn-detection-derived event.
10
+ */
11
+ export function useSpeechActivityDetector(options) {
12
+ const source = options.source,
13
+ onSpeechStart = options.onSpeechStart,
14
+ onSpeechEnd = options.onSpeechEnd,
15
+ thresholdRms = options.thresholdRms,
16
+ hangoverMs = options.hangoverMs;
17
+ const _useParticipantTrack = useParticipantTrack({
18
+ type: source,
19
+ source: Source.Microphone
20
+ }),
21
+ track = _useParticipantTrack.track;
22
+
23
+ // Keep latest callbacks available to the effect without retriggering
24
+ // AudioContext/AudioWorkletNode setup on every render.
25
+ const callbacksRef = useRef({
26
+ onSpeechStart,
27
+ onSpeechEnd
28
+ });
29
+ callbacksRef.current = {
30
+ onSpeechStart,
31
+ onSpeechEnd
32
+ };
33
+ useEffect(() => {
34
+ const _setupSpeechActivityD = setupSpeechActivityDetector(track, {
35
+ thresholdRms,
36
+ hangoverMs,
37
+ onSpeechStart: timestampMs => callbacksRef.current.onSpeechStart == null ? void 0 : callbacksRef.current.onSpeechStart(timestampMs),
38
+ onSpeechEnd: timestampMs => callbacksRef.current.onSpeechEnd == null ? void 0 : callbacksRef.current.onSpeechEnd(timestampMs)
39
+ }),
40
+ cleanup = _setupSpeechActivityD.cleanup;
41
+ return cleanup;
42
+ }, [track == null ? void 0 : track.mediaStreamTrack, thresholdRms, hangoverMs]);
43
+ }
44
+ //# sourceMappingURL=useSpeechActivityDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useEffect","useRef","useParticipantTrack","Source","setupSpeechActivityDetector","useSpeechActivityDetector","options","source","onSpeechStart","onSpeechEnd","thresholdRms","hangoverMs","_useParticipantTrack","type","Microphone","track","callbacksRef","current","_setupSpeechActivityD","timestampMs","cleanup","mediaStreamTrack"],"sources":["../../../../src/hooks/useSpeechActivityDetector/useSpeechActivityDetector.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport { useParticipantTrack, Source } from '../useParticipantTrack';\nimport { setupSpeechActivityDetector } from './helpers';\nimport { type UseSpeechActivityDetectorOptions } from './types';\n\n/**\n * Acoustic speech onset/offset detector for a participant's microphone\n * track, driven by AudioWorkletNode rather than LiveKit's speaking-state\n * events. Useful when the caller needs a timestamp tied to the actual audio\n * signal (e.g. perceived end-to-end latency measurement) rather than a\n * VAD/turn-detection-derived event.\n */\nexport function useSpeechActivityDetector(\n options: UseSpeechActivityDetectorOptions,\n): void {\n const { source, onSpeechStart, onSpeechEnd, thresholdRms, hangoverMs } =\n options;\n const { track } = useParticipantTrack({\n type: source,\n source: Source.Microphone,\n });\n\n // Keep latest callbacks available to the effect without retriggering\n // AudioContext/AudioWorkletNode setup on every render.\n const callbacksRef = useRef({ onSpeechStart, onSpeechEnd });\n callbacksRef.current = { onSpeechStart, onSpeechEnd };\n\n useEffect(() => {\n const { cleanup } = setupSpeechActivityDetector(track, {\n thresholdRms,\n hangoverMs,\n onSpeechStart: (timestampMs) =>\n callbacksRef.current.onSpeechStart?.(timestampMs),\n onSpeechEnd: (timestampMs) =>\n callbacksRef.current.onSpeechEnd?.(timestampMs),\n });\n return cleanup;\n }, [track?.mediaStreamTrack, thresholdRms, hangoverMs]);\n}\n"],"mappings":"AAAA,SAASA,SAAS,EAAEC,MAAM,QAAQ,OAAO;AACzC,SAASC,mBAAmB,EAAEC,MAAM,QAAQ,wBAAwB;AACpE,SAASC,2BAA2B,QAAQ,WAAW;AAGvD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,yBAAyBA,CACvCC,OAAyC,EACnC;EACN,MAAQC,MAAM,GACZD,OAAO,CADDC,MAAM;IAAEC,aAAa,GAC3BF,OAAO,CADOE,aAAa;IAAEC,WAAW,GACxCH,OAAO,CADsBG,WAAW;IAAEC,YAAY,GACtDJ,OAAO,CADmCI,YAAY;IAAEC,UAAU,GAClEL,OAAO,CADiDK,UAAU;EAEpE,MAAAC,oBAAA,GAAkBV,mBAAmB,CAAC;MACpCW,IAAI,EAAEN,MAAM;MACZA,MAAM,EAAEJ,MAAM,CAACW;IACjB,CAAC,CAAC;IAHMC,KAAK,GAAAH,oBAAA,CAALG,KAAK;;EAKb;EACA;EACA,MAAMC,YAAY,GAAGf,MAAM,CAAC;IAAEO,aAAa;IAAEC;EAAY,CAAC,CAAC;EAC3DO,YAAY,CAACC,OAAO,GAAG;IAAET,aAAa;IAAEC;EAAY,CAAC;EAErDT,SAAS,CAAC,MAAM;IACd,MAAAkB,qBAAA,GAAoBd,2BAA2B,CAACW,KAAK,EAAE;QACrDL,YAAY;QACZC,UAAU;QACVH,aAAa,EAAGW,WAAW,IACzBH,YAAY,CAACC,OAAO,CAACT,aAAa,oBAAlCQ,YAAY,CAACC,OAAO,CAACT,aAAa,CAAGW,WAAW,CAAC;QACnDV,WAAW,EAAGU,WAAW,IACvBH,YAAY,CAACC,OAAO,CAACR,WAAW,oBAAhCO,YAAY,CAACC,OAAO,CAACR,WAAW,CAAGU,WAAW;MAClD,CAAC,CAAC;MAPMC,OAAO,GAAAF,qBAAA,CAAPE,OAAO;IAQf,OAAOA,OAAO;EAChB,CAAC,EAAE,CAACL,KAAK,oBAALA,KAAK,CAAEM,gBAAgB,EAAEX,YAAY,EAAEC,UAAU,CAAC,CAAC;AACzD","ignoreList":[]}
package/dist/esm/sdk.js CHANGED
@@ -21,5 +21,6 @@ export * from './hooks/usePictureInPicture';
21
21
  export * from './hooks/useMediaPermissions';
22
22
  export * from './hooks/useTools';
23
23
  export * from './hooks/useParticipantTrack';
24
+ export * from './hooks/useSpeechActivityDetector';
24
25
  export * from './conversation/types';
25
26
  //# sourceMappingURL=sdk.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["../../src/sdk.ts"],"sourcesContent":["export * from './types';\nexport * from './components/LegendsPlatform';\nexport * from './components/LegendsPlatformQLess';\n\nexport * from './hooks/useTextChat';\nexport * from './hooks/useConversation';\nexport * from './hooks/useTranscriptions';\nexport * from './hooks/useConversationStatus';\nexport * from './hooks/useMicrophone';\nexport * from './hooks/useSpeaker';\nexport * from './hooks/useCamera';\nexport * from './hooks/useMediaRecorder';\nexport * from './hooks/useConversationTimer';\nexport * from './hooks/useAudioLevel';\nexport * from './hooks/useConversationEvents';\nexport * from './hooks/useUserActivity';\nexport * from './hooks/useAudio';\nexport * from './hooks/useScreenShare';\nexport * from './hooks/useConnectionState';\nexport * from './hooks/useActivityState';\nexport * from './hooks/usePictureInPicture';\nexport * from './hooks/useMediaPermissions';\nexport * from './hooks/useTools';\nexport * from './hooks/useParticipantTrack';\n\nexport * from './conversation/types';\n"],"mappings":"AAAA,cAAc,SAAS;AACvB,cAAc,8BAA8B;AAC5C,cAAc,mCAAmC;AAEjD,cAAc,qBAAqB;AACnC,cAAc,yBAAyB;AACvC,cAAc,2BAA2B;AACzC,cAAc,+BAA+B;AAC7C,cAAc,uBAAuB;AACrC,cAAc,oBAAoB;AAClC,cAAc,mBAAmB;AACjC,cAAc,0BAA0B;AACxC,cAAc,8BAA8B;AAC5C,cAAc,uBAAuB;AACrC,cAAc,+BAA+B;AAC7C,cAAc,yBAAyB;AACvC,cAAc,kBAAkB;AAChC,cAAc,wBAAwB;AACtC,cAAc,4BAA4B;AAC1C,cAAc,0BAA0B;AACxC,cAAc,6BAA6B;AAC3C,cAAc,6BAA6B;AAC3C,cAAc,kBAAkB;AAChC,cAAc,6BAA6B;AAE3C,cAAc,sBAAsB","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["../../src/sdk.ts"],"sourcesContent":["export * from './types';\nexport * from './components/LegendsPlatform';\nexport * from './components/LegendsPlatformQLess';\n\nexport * from './hooks/useTextChat';\nexport * from './hooks/useConversation';\nexport * from './hooks/useTranscriptions';\nexport * from './hooks/useConversationStatus';\nexport * from './hooks/useMicrophone';\nexport * from './hooks/useSpeaker';\nexport * from './hooks/useCamera';\nexport * from './hooks/useMediaRecorder';\nexport * from './hooks/useConversationTimer';\nexport * from './hooks/useAudioLevel';\nexport * from './hooks/useConversationEvents';\nexport * from './hooks/useUserActivity';\nexport * from './hooks/useAudio';\nexport * from './hooks/useScreenShare';\nexport * from './hooks/useConnectionState';\nexport * from './hooks/useActivityState';\nexport * from './hooks/usePictureInPicture';\nexport * from './hooks/useMediaPermissions';\nexport * from './hooks/useTools';\nexport * from './hooks/useParticipantTrack';\nexport * from './hooks/useSpeechActivityDetector';\n\nexport * from './conversation/types';\n"],"mappings":"AAAA,cAAc,SAAS;AACvB,cAAc,8BAA8B;AAC5C,cAAc,mCAAmC;AAEjD,cAAc,qBAAqB;AACnC,cAAc,yBAAyB;AACvC,cAAc,2BAA2B;AACzC,cAAc,+BAA+B;AAC7C,cAAc,uBAAuB;AACrC,cAAc,oBAAoB;AAClC,cAAc,mBAAmB;AACjC,cAAc,0BAA0B;AACxC,cAAc,8BAA8B;AAC5C,cAAc,uBAAuB;AACrC,cAAc,+BAA+B;AAC7C,cAAc,yBAAyB;AACvC,cAAc,kBAAkB;AAChC,cAAc,wBAAwB;AACtC,cAAc,4BAA4B;AAC1C,cAAc,0BAA0B;AACxC,cAAc,6BAA6B;AAC3C,cAAc,6BAA6B;AAC3C,cAAc,kBAAkB;AAChC,cAAc,6BAA6B;AAC3C,cAAc,mCAAmC;AAEjD,cAAc,sBAAsB","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["../../../src/types/service.ts"],"sourcesContent":["export type FunctionCall = {\n functionCallId: string;\n name: string;\n arguments: string;\n};\n\nexport type FunctionCallResult = {\n functionCallId: string;\n result: string;\n error?: string;\n};\n\nexport type LimitationType = 'MINUTES' | 'SECONDS' | 'TOKENS';\n\nexport interface Limitation {\n type?: LimitationType;\n limit?: number;\n}\n\nexport interface ConversationResult {\n url: string;\n id: string;\n limit?: number;\n}\n\nexport interface CreateConversationParams {\n chatId: string;\n namespace: string;\n chatConfig?: object;\n}\n\nexport interface EndConversationParams {\n chatId: string;\n conversationId: string;\n namespace: string;\n}\n\nexport interface ChatMessageAttachment {\n fileName?: string;\n url?: string;\n mimeType?: string;\n textFileContent?: string;\n}\n\nexport interface SendMessageParams {\n chatId: string;\n namespace: string;\n message: string;\n functionCallResults?: FunctionCallResult[];\n chatConfig?: object;\n attachments?: ChatMessageAttachment[];\n}\n\nexport interface ChatMessage {\n id: string;\n content: string;\n role: 'user' | 'assistant';\n createdAt?: string;\n attachments?: ChatMessageAttachment[];\n}\n\nexport interface QueryMessagesParams {\n chatId: string;\n namespace: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface QueryMessagesResult {\n messages: ChatMessage[];\n nextCursor?: string;\n}\n\nexport interface IPersonaChatService {\n createVoiceConversation(\n params: CreateConversationParams,\n ): Promise<ConversationResult>;\n createInteractiveConversation(\n params: CreateConversationParams,\n ): Promise<ConversationResult>;\n endInteractiveConversation(params: EndConversationParams): Promise<void>;\n sendChatMessage(\n params: SendMessageParams,\n ): Promise<{ functionCalls?: FunctionCall[]; messages?: ChatMessage[] }>;\n queryChatMessages(params: QueryMessagesParams): Promise<QueryMessagesResult>;\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["../../../src/types/service.ts"],"sourcesContent":["export type FunctionCall = {\n functionCallId: string;\n name: string;\n arguments: string;\n};\n\nexport type FunctionCallResult = {\n functionCallId: string;\n result: string;\n error?: string;\n};\n\nexport type LimitationType = 'MINUTES' | 'SECONDS' | 'TOKENS';\n\nexport interface Limitation {\n type?: LimitationType;\n limit?: number;\n}\n\nexport interface ConversationResult {\n url: string;\n id: string;\n limit?: number;\n}\n\nexport interface CreateConversationParams {\n chatId: string;\n namespace: string;\n chatConfig?: object;\n /** Whether this is the visitor's first-ever conversation with the persona (drives the opening message). */\n firstConversation?: boolean;\n /** Title of the visitor's last meaningful conversation, for a returning-visitor greeting. */\n lastConversationTopic?: string;\n}\n\nexport interface EndConversationParams {\n chatId: string;\n conversationId: string;\n namespace: string;\n}\n\nexport interface ChatMessageAttachment {\n fileName?: string;\n url?: string;\n mimeType?: string;\n textFileContent?: string;\n}\n\nexport interface SendMessageParams {\n chatId: string;\n namespace: string;\n message: string;\n functionCallResults?: FunctionCallResult[];\n chatConfig?: object;\n attachments?: ChatMessageAttachment[];\n}\n\nexport interface ChatMessage {\n id: string;\n content: string;\n role: 'user' | 'assistant';\n createdAt?: string;\n attachments?: ChatMessageAttachment[];\n}\n\nexport interface QueryMessagesParams {\n chatId: string;\n namespace: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface QueryMessagesResult {\n messages: ChatMessage[];\n nextCursor?: string;\n}\n\nexport interface IPersonaChatService {\n createVoiceConversation(\n params: CreateConversationParams,\n ): Promise<ConversationResult>;\n createInteractiveConversation(\n params: CreateConversationParams,\n ): Promise<ConversationResult>;\n endInteractiveConversation(params: EndConversationParams): Promise<void>;\n sendChatMessage(\n params: SendMessageParams,\n ): Promise<{ functionCalls?: FunctionCall[]; messages?: ChatMessage[] }>;\n queryChatMessages(params: QueryMessagesParams): Promise<QueryMessagesResult>;\n}\n"],"mappings":"","ignoreList":[]}
@@ -7,4 +7,5 @@ export * from './useUserActivity';
7
7
  export * from './useScreenShare';
8
8
  export * from './usePictureInPicture';
9
9
  export * from './useTools';
10
+ export * from './useSpeechActivityDetector';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,cAAc,6BAA6B,CAAC"}
@@ -1,4 +1,6 @@
1
1
  import type { TurnDetectionData } from './types';
2
2
  import type { ConversationEvent } from '../../useConversationEvents';
3
3
  export declare const SPEAKING_STATE_EVENT_MAP: Record<TurnDetectionData['participantKind'], Record<TurnDetectionData['state'], ConversationEvent>>;
4
+ export declare const ACOUSTIC_USER_THRESHOLD_RMS = 0.025;
5
+ export declare const ACOUSTIC_USER_HANGOVER_MS = 400;
4
6
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/useConversation/livekit/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,iBAAiB,CAAC,iBAAiB,CAAC,EACpC,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAUtD,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/useConversation/livekit/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,iBAAiB,CAAC,iBAAiB,CAAC,EACpC,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAUtD,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAAQ,CAAC;AACjD,eAAO,MAAM,yBAAyB,MAAM,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useConversation.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/useConversation/livekit/useConversation.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EAEvB,MAAM,UAAU,CAAC;AAyBlB,wBAAgB,eAAe,CAAC,EAC9B,QAAe,EACf,IAAI,EACJ,MAAM,EACN,mBAA2B,EAC3B,eAAuB,GACxB,EAAE,sBAAsB,GAAG,qBAAqB,CAwZhD"}
1
+ {"version":3,"file":"useConversation.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/useConversation/livekit/useConversation.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EAEvB,MAAM,UAAU,CAAC;AA8BlB,wBAAgB,eAAe,CAAC,EAC9B,QAAe,EACf,IAAI,EACJ,MAAM,EACN,mBAA2B,EAC3B,eAAuB,GACxB,EAAE,sBAAsB,GAAG,qBAAqB,CA6chD"}
@@ -2,6 +2,10 @@ import { type ConversationType, type Conversation } from '../../conversation';
2
2
  export type UseCreateConversationOptions = {
3
3
  type: ConversationType;
4
4
  config?: object;
5
+ /** Whether this is the visitor's first-ever conversation with the persona (drives the opening message). */
6
+ firstConversation?: boolean;
7
+ /** Title of the visitor's last meaningful conversation, for a returning-visitor greeting. */
8
+ lastConversationTopic?: string;
5
9
  };
6
10
  export type UseCreateConversationResult = {
7
11
  data?: Conversation;
@@ -1 +1 @@
1
- {"version":3,"file":"useCreateConversation.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useCreateConversation/useCreateConversation.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,2BAA2B,CAgG7B"}
1
+ {"version":3,"file":"useCreateConversation.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useCreateConversation/useCreateConversation.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2GAA2G;IAC3G,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,6FAA6F;IAC7F,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,2BAA2B,CA2G7B"}
@@ -0,0 +1,19 @@
1
+ import { Track } from 'livekit-client';
2
+ export type SetupSpeechActivityDetectorOptions = {
3
+ thresholdRms?: number;
4
+ hangoverMs?: number;
5
+ minSpeechMs?: number;
6
+ onSpeechStart?: (timestampMs: number) => void;
7
+ onSpeechEnd?: (timestampMs: number) => void;
8
+ onMisfire?: () => void;
9
+ };
10
+ /**
11
+ * Attaches an AudioWorkletNode-based RMS threshold+hangover detector to a
12
+ * LiveKit track's underlying MediaStreamTrack. Runs on the dedicated audio
13
+ * rendering thread — unlike requestAnimationFrame+AnalyserNode, it keeps
14
+ * running when the tab is backgrounded.
15
+ */
16
+ export declare function setupSpeechActivityDetector(track: Track | undefined, options: SetupSpeechActivityDetectorOptions): {
17
+ cleanup: () => void;
18
+ };
19
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useSpeechActivityDetector/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAUvC,MAAM,MAAM,kCAAkC,GAAG;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,KAAK,GAAG,SAAS,EACxB,OAAO,EAAE,kCAAkC,GAC1C;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAgFzB"}
@@ -0,0 +1,3 @@
1
+ export * from './useSpeechActivityDetector';
2
+ export * from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useSpeechActivityDetector/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,SAAS,CAAC"}