@vox-ai/react 0.1.3 → 0.1.4

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.
@@ -1,4 +1,12 @@
1
- type VoxAgentState = "disconnected" | "connecting" | "initializing" | "listening" | "thinking" | "speaking";
1
+ /**
2
+ * VoxAgentState
3
+ * @description The state of the agent
4
+ */
5
+ export type VoxAgentState = "disconnected" | "connecting" | "initializing" | "listening" | "thinking" | "speaking";
6
+ /**
7
+ * VoxMessage
8
+ * @description The message type between the agent and the user
9
+ */
2
10
  export type VoxMessage = {
3
11
  id?: string;
4
12
  name: "agent" | "user" | "tool";
@@ -6,24 +14,45 @@ export type VoxMessage = {
6
14
  timestamp: number;
7
15
  isFinal?: boolean;
8
16
  };
9
- interface VoxAIOptions {
17
+ /**
18
+ * VoxAIOptions
19
+ * @description The callback functions for the useVoxAI hook
20
+ */
21
+ export interface VoxAIOptions {
10
22
  onConnect?: () => void;
11
23
  onDisconnect?: () => void;
12
24
  onError?: (error: Error) => void;
13
25
  onMessage?: (message: VoxMessage) => void;
14
26
  }
15
- interface ConnectParams {
27
+ /**
28
+ * ConnectParams
29
+ * @param agentId - The agent ID
30
+ * @param apiKey - The API key
31
+ * @param dynamicVariables - The dynamic variables
32
+ * @param metadata - 이 메타데이터는 아웃바운드 웹훅, 통화 기록에 포함됩니다.
33
+ */
34
+ export interface ConnectParams {
16
35
  agentId: string;
17
36
  apiKey: string;
18
37
  dynamicVariables?: Record<string, any>;
38
+ metadata?: Record<string, any>;
19
39
  }
20
40
  /**
21
- * Hook for integrating with VoxAI voice assistant
41
+ * useVoxAI
42
+ * @description The hook for integrating with vox.ai voice assistant
43
+ * @param options - The options for the useVoxAI hook
44
+ * @returns The useVoxAI hook
45
+ * @example
46
+ * const { connect, disconnect, state, messages } = useVoxAI({
47
+ * onConnect: () => console.log("Connected"),
48
+ * onDisconnect: () => console.log("Disconnected"),
49
+ * onError: (error) => console.error("Error:", error),
50
+ * onMessage: (message) => console.log("Message:", message),
51
+ * });
22
52
  */
23
53
  export declare function useVoxAI(options?: VoxAIOptions): {
24
- connect: ({ agentId, apiKey, dynamicVariables }: ConnectParams) => Promise<void>;
54
+ connect: ({ agentId, apiKey, dynamicVariables, metadata }: ConnectParams) => Promise<void>;
25
55
  disconnect: () => void;
26
56
  state: VoxAgentState;
27
57
  messages: VoxMessage[];
28
58
  };
29
- export {};
package/dist/index.d.ts CHANGED
@@ -3,3 +3,4 @@
3
3
  * React UI component library for vox.ai
4
4
  */
5
5
  export { useVoxAI } from "./hooks";
6
+ export type { VoxAgentState, VoxMessage, VoxAIOptions, ConnectParams, } from "./hooks";
package/dist/lib.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=require("react/jsx-runtime"),t=require("@livekit/components-react"),n=require("livekit-client"),r=require("react"),s=require("react-dom/client");function o(e){let{port:s}=e;const{agent:o,state:i}=t.useVoiceAssistant(),a=t.useParticipantTracks([n.Track.Source.Microphone],null==o?void 0:o.identity)[0],c=t.useTrackTranscription(a),u=t.useLocalParticipant(),l=t.useTrackTranscription({publication:u.microphoneTrack,source:n.Track.Source.Microphone,participant:u.localParticipant});return r.useEffect(()=>{s&&s.postMessage({type:"state_update",state:i})},[i,s]),r.useEffect(()=>{if(s&&c.segments.length>0){const e=c.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"agent"}));s.postMessage({type:"transcription_update",transcriptions:e})}},[c.segments,s]),r.useEffect(()=>{if(s&&l.segments.length>0){const e=l.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"user"}));s.postMessage({type:"transcription_update",transcriptions:e})}},[l.segments,s]),null}exports.useVoxAI=function(n){void 0===n&&(n={});const[i,a]=r.useState(null),[c,u]=r.useState("disconnected"),[l,p]=r.useState(new Map),[d,m]=r.useState([]),f=r.useRef(""),g=r.useRef(new Set),h=r.useRef(null),v=r.useRef(null),y=r.useRef(null);r.useEffect(()=>{const e=Array.from(l.values()).sort((e,t)=>e.timestamp-t.timestamp),t=JSON.stringify(e);t!==f.current&&(f.current=t,m(e),n.onMessage&&e.filter(e=>e.isFinal&&e.id&&!g.current.has(e.id)).forEach(e=>{e.id&&(g.current.add(e.id),null==n.onMessage||n.onMessage(e))}))},[l,n.onMessage]),r.useEffect(()=>(y.current=new MessageChannel,y.current.port1.onmessage=e=>{const t=e.data;"state_update"===t.type?u(t.state):"transcription_update"===t.type&&E(t.transcriptions)},()=>{var e;null==(e=y.current)||e.port1.close()}),[]);const E=r.useCallback(e=>{p(t=>{const n=new Map(t);return e.forEach(e=>{var r;const s="agent"===e.speaker?"agent":"user",o=(null==(r=t.get(e.id))?void 0:r.timestamp)||e.timestamp;n.set(e.id,{id:e.id,name:s,message:e.text,timestamp:o,isFinal:e.isFinal})}),n})},[]);r.useEffect(()=>{const e=document.createElement("div");return e.style.display="none",document.body.appendChild(e),h.current=e,v.current=s.createRoot(e),()=>{v.current&&v.current.unmount(),h.current&&document.body.removeChild(h.current)}},[]);const k=r.useCallback(function(e){let{agentId:t,apiKey:r,dynamicVariables:s}=e;try{return Promise.resolve(function(e,o){try{var i=(u("connecting"),Promise.resolve(fetch("https://www.tryvox.co/api/agent/sdk",{method:"POST",headers:{Authorization:"Bearer "+r,"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,metadata:{call_web:{dynamic_variables:s||{}}}})})).then(function(e){function t(t){return Promise.resolve(e.json()).then(function(e){a(e),n.onConnect&&n.onConnect()})}const r=function(){if(!e.ok)return Promise.resolve(e.text()).then(function(t){throw new Error("Connection failed ("+e.status+"): "+t)})}();return r&&r.then?r.then(t):t()}))}catch(e){return o(e)}return i&&i.then?i.then(void 0,o):i}(0,function(e){a(null),p(new Map),m([]),u("disconnected");const t=e instanceof Error?e:new Error(String(e));n.onError&&n.onError(t)}))}catch(e){return Promise.reject(e)}},[n]),w=r.useCallback(()=>{a(null),p(new Map),m([]),u("disconnected"),n.onDisconnect&&n.onDisconnect()},[n]);return r.useEffect(()=>{var r;v.current&&v.current.render(i?e.jsxs(t.LiveKitRoom,{serverUrl:i.serverUrl,token:i.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:w,onError:e=>{console.error("LiveKit connection error:",e),w(),n.onError&&n.onError(new Error("LiveKit connection error: "+e.message))},children:[e.jsx(t.RoomAudioRenderer,{}),e.jsx(o,{port:null==(r=y.current)?void 0:r.port2})]}):e.jsx(e.Fragment,{}))},[i,w,n.onError]),{connect:k,disconnect:w,state:c,messages:d}};
1
+ var e=require("react/jsx-runtime"),t=require("@livekit/components-react"),n=require("livekit-client"),r=require("react"),s=require("react-dom/client");function o(e){let{port:s}=e;const{agent:o,state:a}=t.useVoiceAssistant(),i=t.useParticipantTracks([n.Track.Source.Microphone],null==o?void 0:o.identity)[0],c=t.useTrackTranscription(i),u=t.useLocalParticipant(),l=t.useTrackTranscription({publication:u.microphoneTrack,source:n.Track.Source.Microphone,participant:u.localParticipant});return r.useEffect(()=>{s&&s.postMessage({type:"state_update",state:a})},[a,s]),r.useEffect(()=>{if(s&&c.segments.length>0){const e=c.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"agent"}));s.postMessage({type:"transcription_update",transcriptions:e})}},[c.segments,s]),r.useEffect(()=>{if(s&&l.segments.length>0){const e=l.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"user"}));s.postMessage({type:"transcription_update",transcriptions:e})}},[l.segments,s]),null}exports.useVoxAI=function(n){void 0===n&&(n={});const[a,i]=r.useState(null),[c,u]=r.useState("disconnected"),[l,p]=r.useState(new Map),[d,m]=r.useState([]),f=r.useRef(""),g=r.useRef(new Set),h=r.useRef(null),v=r.useRef(null),y=r.useRef(null);r.useEffect(()=>{const e=Array.from(l.values()).sort((e,t)=>e.timestamp-t.timestamp),t=JSON.stringify(e);t!==f.current&&(f.current=t,m(e),n.onMessage&&e.filter(e=>e.isFinal&&e.id&&!g.current.has(e.id)).forEach(e=>{e.id&&(g.current.add(e.id),null==n.onMessage||n.onMessage(e))}))},[l,n.onMessage]),r.useEffect(()=>(y.current=new MessageChannel,y.current.port1.onmessage=e=>{const t=e.data;"state_update"===t.type?u(t.state):"transcription_update"===t.type&&E(t.transcriptions)},()=>{var e;null==(e=y.current)||e.port1.close()}),[]);const E=r.useCallback(e=>{p(t=>{const n=new Map(t);return e.forEach(e=>{var r;const s="agent"===e.speaker?"agent":"user",o=(null==(r=t.get(e.id))?void 0:r.timestamp)||e.timestamp;n.set(e.id,{id:e.id,name:s,message:e.text,timestamp:o,isFinal:e.isFinal})}),n})},[]);r.useEffect(()=>{const e=document.createElement("div");return e.style.display="none",document.body.appendChild(e),h.current=e,v.current=s.createRoot(e),()=>{v.current&&v.current.unmount(),h.current&&document.body.removeChild(h.current)}},[]);const k=r.useCallback(function(e){let{agentId:t,apiKey:r,dynamicVariables:s,metadata:o}=e;try{return Promise.resolve(function(e,a){try{var c=(u("connecting"),Promise.resolve(fetch("https://www.tryvox.co/api/agent/sdk",{method:"POST",headers:{Authorization:"Bearer "+r,"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,metadata:{call_web:{dynamic_variables:s||{},metadata:o||{}}}})})).then(function(e){function t(t){return Promise.resolve(e.json()).then(function(e){i(e),n.onConnect&&n.onConnect()})}const r=function(){if(!e.ok)return Promise.resolve(e.text()).then(function(t){throw new Error("Connection failed ("+e.status+"): "+t)})}();return r&&r.then?r.then(t):t()}))}catch(e){return a(e)}return c&&c.then?c.then(void 0,a):c}(0,function(e){i(null),p(new Map),m([]),u("disconnected");const t=e instanceof Error?e:new Error(String(e));n.onError&&n.onError(t)}))}catch(e){return Promise.reject(e)}},[n]),w=r.useCallback(()=>{i(null),p(new Map),m([]),u("disconnected"),n.onDisconnect&&n.onDisconnect()},[n]);return r.useEffect(()=>{var r;v.current&&v.current.render(a?e.jsxs(t.LiveKitRoom,{serverUrl:a.serverUrl,token:a.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:w,onError:e=>{console.error("LiveKit connection error:",e),w(),n.onError&&n.onError(new Error("LiveKit connection error: "+e.message))},children:[e.jsx(t.RoomAudioRenderer,{}),e.jsx(o,{port:null==(r=y.current)?void 0:r.port2})]}):e.jsx(e.Fragment,{}))},[a,w,n.onError]),{connect:k,disconnect:w,state:c,messages:d}};
2
2
  //# sourceMappingURL=lib.cjs.map
package/dist/lib.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lib.cjs","sources":["../src/hooks/useVoxAI.tsx"],"sourcesContent":["import {\n LiveKitRoom,\n RoomAudioRenderer,\n useLocalParticipant,\n useParticipantTracks,\n useTrackTranscription,\n useVoiceAssistant,\n} from \"@livekit/components-react\";\nimport { Track } from \"livekit-client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\n\n// Connection types\ntype VoxConnectionDetail = {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n};\n\ntype VoxAgentState =\n | \"disconnected\"\n | \"connecting\"\n | \"initializing\"\n | \"listening\"\n | \"thinking\"\n | \"speaking\";\n\n// API endpoint\nconst HTTPS_API_ORIGIN = \"https://www.tryvox.co/api/agent/sdk\";\n// const HTTPS_API_ORIGIN = \"http://localhost:3000/api/agent/sdk\";\n\nexport type VoxMessage = {\n id?: string;\n name: \"agent\" | \"user\" | \"tool\";\n message?: string;\n timestamp: number;\n isFinal?: boolean;\n};\n\n// Hook configuration\ninterface VoxAIOptions {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n onMessage?: (message: VoxMessage) => void;\n}\n\n// Message channel event types\ntype MessageChannelEvent =\n | { type: \"state_update\"; state: VoxAgentState }\n | { type: \"transcription_update\"; transcriptions: TranscriptionSegment[] };\n\ntype TranscriptionSegment = {\n id: string;\n text: string;\n isFinal: boolean;\n timestamp: number;\n speaker: \"agent\" | \"user\";\n};\n\n// Update the connection parameter type to include dynamicVariables\ninterface ConnectParams {\n agentId: string;\n apiKey: string;\n dynamicVariables?: Record<string, any>; // Allow any dynamicVariables fields\n}\n\n/**\n * Hook for integrating with VoxAI voice assistant\n */\nexport function useVoxAI(options: VoxAIOptions = {}) {\n // Connection state\n const [connectionDetail, setConnectionDetail] =\n useState<VoxConnectionDetail | null>(null);\n const [state, setState] = useState<VoxAgentState>(\"disconnected\");\n\n // Message handling\n const [transcriptMap, setTranscriptMap] = useState<Map<string, VoxMessage>>(\n new Map()\n );\n const [messages, setMessages] = useState<VoxMessage[]>([]);\n const prevMessagesRef = useRef<string>(\"\");\n\n // Track which messages we've already sent to the onMessage callback\n const processedMessageIdsRef = useRef<Set<string>>(new Set());\n\n // DOM manipulation for LiveKit portal\n const portalRootRef = useRef<HTMLDivElement | null>(null);\n const rootRef = useRef<Root | null>(null);\n\n // Communication channel\n const channelRef = useRef<MessageChannel | null>(null);\n\n // Update messages whenever transcriptMap changes\n useEffect(() => {\n const allMessages = Array.from(transcriptMap.values()).sort(\n (a, b) => a.timestamp - b.timestamp\n );\n\n // Only update if the messages have actually changed\n const messagesString = JSON.stringify(allMessages);\n if (messagesString !== prevMessagesRef.current) {\n prevMessagesRef.current = messagesString;\n setMessages(allMessages);\n\n // Only trigger onMessage for new final messages that haven't been processed yet\n if (options.onMessage) {\n allMessages\n .filter(\n (msg) =>\n msg.isFinal &&\n msg.id &&\n !processedMessageIdsRef.current.has(msg.id)\n )\n .forEach((msg) => {\n if (msg.id) {\n // Mark this message as processed\n processedMessageIdsRef.current.add(msg.id);\n // Call the callback\n options.onMessage?.(msg);\n }\n });\n }\n }\n }, [transcriptMap, options.onMessage]);\n\n // Initialize message channel\n useEffect(() => {\n channelRef.current = new MessageChannel();\n channelRef.current.port1.onmessage = (e) => {\n const data = e.data as MessageChannelEvent;\n\n if (data.type === \"state_update\") {\n setState(data.state);\n } else if (data.type === \"transcription_update\") {\n handleTranscriptionUpdate(data.transcriptions);\n }\n };\n\n return () => {\n channelRef.current?.port1.close();\n };\n }, []);\n\n // Process incoming transcriptions\n const handleTranscriptionUpdate = useCallback(\n (transcriptions: TranscriptionSegment[]) => {\n setTranscriptMap((prevMap) => {\n const newMap = new Map(prevMap);\n\n transcriptions.forEach((t) => {\n const messageType = t.speaker === \"agent\" ? \"agent\" : \"user\";\n // Use existing timestamp if we already have this segment\n const existingTimestamp = prevMap.get(t.id)?.timestamp || t.timestamp;\n\n newMap.set(t.id, {\n id: t.id,\n name: messageType,\n message: t.text,\n timestamp: existingTimestamp,\n isFinal: t.isFinal,\n });\n });\n\n return newMap;\n });\n },\n []\n );\n\n // Set up DOM portal for LiveKit\n useEffect(() => {\n const div = document.createElement(\"div\");\n div.style.display = \"none\";\n document.body.appendChild(div);\n portalRootRef.current = div;\n rootRef.current = createRoot(div);\n\n return () => {\n if (rootRef.current) {\n rootRef.current.unmount();\n }\n if (portalRootRef.current) {\n document.body.removeChild(portalRootRef.current);\n }\n };\n }, []);\n\n // Connect to VoxAI service - updated to include dynamicVariables\n const connect = useCallback(\n async ({ agentId, apiKey, dynamicVariables }: ConnectParams) => {\n try {\n setState(\"connecting\");\n\n const response = await fetch(HTTPS_API_ORIGIN, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n agent_id: agentId,\n metadata: {\n call_web: {\n dynamic_variables: dynamicVariables || {},\n },\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Connection failed (${response.status}): ${errorText}`\n );\n }\n\n const data = await response.json();\n setConnectionDetail(data);\n\n if (options.onConnect) {\n options.onConnect();\n }\n } catch (err) {\n // Reset state on error\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n const error = err instanceof Error ? err : new Error(String(err));\n\n if (options.onError) {\n options.onError(error);\n }\n }\n },\n [options]\n );\n\n // Disconnect from VoxAI service\n const disconnect = useCallback(() => {\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n if (options.onDisconnect) {\n options.onDisconnect();\n }\n }, [options]);\n\n // Render LiveKit components when connection details are available\n useEffect(() => {\n if (!rootRef.current) return;\n\n if (connectionDetail) {\n rootRef.current.render(\n <LiveKitRoom\n serverUrl={connectionDetail.serverUrl}\n token={connectionDetail.participantToken}\n audio={true}\n video={false}\n connect={true}\n onDisconnected={disconnect}\n // Add error handling for LiveKit connection\n onError={(error) => {\n console.error(\"LiveKit connection error:\", error);\n disconnect();\n if (options.onError) {\n options.onError(\n new Error(`LiveKit connection error: ${error.message}`)\n );\n }\n }}\n >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect, options.onError]);\n\n return {\n connect,\n disconnect,\n state,\n messages,\n };\n}\n\n/**\n * Component that monitors LiveKit state and communicates back to the main hook\n */\nfunction StateMonitor({ port }: { port: MessagePort | undefined }) {\n const { agent, state } = useVoiceAssistant();\n\n // Agent transcriptions\n const agentAudioTrack = useParticipantTracks(\n [Track.Source.Microphone],\n agent?.identity\n )[0];\n const agentTranscription = useTrackTranscription(agentAudioTrack);\n\n // User transcriptions\n const localParticipant = useLocalParticipant();\n const localMessages = useTrackTranscription({\n publication: localParticipant.microphoneTrack,\n source: Track.Source.Microphone,\n participant: localParticipant.localParticipant,\n });\n\n // Send agent state updates\n useEffect(() => {\n if (port) {\n port.postMessage({ type: \"state_update\", state });\n }\n }, [state, port]);\n\n // Send agent transcriptions\n useEffect(() => {\n if (port && agentTranscription.segments.length > 0) {\n const transcriptions = agentTranscription.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"agent\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [agentTranscription.segments, port]);\n\n // Send user transcriptions\n useEffect(() => {\n if (port && localMessages.segments.length > 0) {\n const transcriptions = localMessages.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"user\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [localMessages.segments, port]);\n\n return null;\n}\n"],"names":["StateMonitor","_ref2","port","agent","state","useVoiceAssistant","agentAudioTrack","useParticipantTracks","Track","Source","Microphone","identity","agentTranscription","useTrackTranscription","localParticipant","useLocalParticipant","localMessages","publication","microphoneTrack","source","participant","useEffect","postMessage","type","segments","length","transcriptions","map","segment","id","text","isFinal","final","timestamp","Date","now","speaker","options","connectionDetail","setConnectionDetail","useState","setState","transcriptMap","setTranscriptMap","Map","messages","setMessages","prevMessagesRef","useRef","processedMessageIdsRef","Set","portalRootRef","rootRef","channelRef","allMessages","Array","from","values","sort","a","b","messagesString","JSON","stringify","current","onMessage","filter","msg","has","forEach","add","MessageChannel","port1","onmessage","e","data","handleTranscriptionUpdate","_channelRef$current","close","useCallback","prevMap","newMap","t","_prevMap$get","messageType","existingTimestamp","get","set","name","message","div","document","createElement","style","display","body","appendChild","createRoot","unmount","removeChild","connect","_ref","agentId","apiKey","dynamicVariables","Promise","resolve","fetch","method","headers","Authorization","agent_id","metadata","call_web","dynamic_variables","then","response","_temp2","_result2","_exit","json","onConnect","_temp","ok","errorText","Error","status","_catch","err","error","String","onError","reject","disconnect","onDisconnect","_channelRef$current2","render","_jsxs","jsxs","LiveKitRoom","serverUrl","token","participantToken","audio","video","onDisconnected","console","_jsx","RoomAudioRenderer","jsx","port2","_Fragment","Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"lib.cjs","sources":["../src/hooks/useVoxAI.tsx"],"sourcesContent":["import {\n LiveKitRoom,\n RoomAudioRenderer,\n useLocalParticipant,\n useParticipantTracks,\n useTrackTranscription,\n useVoiceAssistant,\n} from \"@livekit/components-react\";\nimport { Track } from \"livekit-client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\n\ntype VoxConnectionDetail = {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n};\n\n/**\n * VoxAgentState\n * @description The state of the agent\n */\nexport type VoxAgentState =\n | \"disconnected\"\n | \"connecting\"\n | \"initializing\"\n | \"listening\"\n | \"thinking\"\n | \"speaking\";\n\n// API endpoint\nconst HTTPS_API_ORIGIN = \"https://www.tryvox.co/api/agent/sdk\";\n// const HTTPS_API_ORIGIN = \"http://localhost:3000/api/agent/sdk\";\n\n/**\n * VoxMessage\n * @description The message type between the agent and the user\n */\nexport type VoxMessage = {\n id?: string;\n name: \"agent\" | \"user\" | \"tool\";\n message?: string;\n timestamp: number;\n isFinal?: boolean;\n};\n\n/**\n * VoxAIOptions\n * @description The callback functions for the useVoxAI hook\n */\nexport interface VoxAIOptions {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n onMessage?: (message: VoxMessage) => void;\n}\n\n// Message channel event types\ntype MessageChannelEvent =\n | { type: \"state_update\"; state: VoxAgentState }\n | { type: \"transcription_update\"; transcriptions: TranscriptionSegment[] };\n\ntype TranscriptionSegment = {\n id: string;\n text: string;\n isFinal: boolean;\n timestamp: number;\n speaker: \"agent\" | \"user\";\n};\n\n/**\n * ConnectParams\n * @param agentId - The agent ID\n * @param apiKey - The API key\n * @param dynamicVariables - The dynamic variables\n * @param metadata - 이 메타데이터는 아웃바운드 웹훅, 통화 기록에 포함됩니다.\n */\nexport interface ConnectParams {\n agentId: string;\n apiKey: string;\n dynamicVariables?: Record<string, any>;\n metadata?: Record<string, any>;\n}\n\n/**\n * useVoxAI\n * @description The hook for integrating with vox.ai voice assistant\n * @param options - The options for the useVoxAI hook\n * @returns The useVoxAI hook\n * @example\n * const { connect, disconnect, state, messages } = useVoxAI({\n * onConnect: () => console.log(\"Connected\"),\n * onDisconnect: () => console.log(\"Disconnected\"),\n * onError: (error) => console.error(\"Error:\", error),\n * onMessage: (message) => console.log(\"Message:\", message),\n * });\n */\nexport function useVoxAI(options: VoxAIOptions = {}) {\n // Connection state\n const [connectionDetail, setConnectionDetail] =\n useState<VoxConnectionDetail | null>(null);\n const [state, setState] = useState<VoxAgentState>(\"disconnected\");\n\n // Message handling\n const [transcriptMap, setTranscriptMap] = useState<Map<string, VoxMessage>>(\n new Map()\n );\n const [messages, setMessages] = useState<VoxMessage[]>([]);\n const prevMessagesRef = useRef<string>(\"\");\n\n // Track which messages we've already sent to the onMessage callback\n const processedMessageIdsRef = useRef<Set<string>>(new Set());\n\n // DOM manipulation for LiveKit portal\n const portalRootRef = useRef<HTMLDivElement | null>(null);\n const rootRef = useRef<Root | null>(null);\n\n // Communication channel\n const channelRef = useRef<MessageChannel | null>(null);\n\n // Update messages whenever transcriptMap changes\n useEffect(() => {\n const allMessages = Array.from(transcriptMap.values()).sort(\n (a, b) => a.timestamp - b.timestamp\n );\n\n // Only update if the messages have actually changed\n const messagesString = JSON.stringify(allMessages);\n if (messagesString !== prevMessagesRef.current) {\n prevMessagesRef.current = messagesString;\n setMessages(allMessages);\n\n // Only trigger onMessage for new final messages that haven't been processed yet\n if (options.onMessage) {\n allMessages\n .filter(\n (msg) =>\n msg.isFinal &&\n msg.id &&\n !processedMessageIdsRef.current.has(msg.id)\n )\n .forEach((msg) => {\n if (msg.id) {\n // Mark this message as processed\n processedMessageIdsRef.current.add(msg.id);\n // Call the callback\n options.onMessage?.(msg);\n }\n });\n }\n }\n }, [transcriptMap, options.onMessage]);\n\n // Initialize message channel\n useEffect(() => {\n channelRef.current = new MessageChannel();\n channelRef.current.port1.onmessage = (e) => {\n const data = e.data as MessageChannelEvent;\n\n if (data.type === \"state_update\") {\n setState(data.state);\n } else if (data.type === \"transcription_update\") {\n handleTranscriptionUpdate(data.transcriptions);\n }\n };\n\n return () => {\n channelRef.current?.port1.close();\n };\n }, []);\n\n // Process incoming transcriptions\n const handleTranscriptionUpdate = useCallback(\n (transcriptions: TranscriptionSegment[]) => {\n setTranscriptMap((prevMap) => {\n const newMap = new Map(prevMap);\n\n transcriptions.forEach((t) => {\n const messageType = t.speaker === \"agent\" ? \"agent\" : \"user\";\n // Use existing timestamp if we already have this segment\n const existingTimestamp = prevMap.get(t.id)?.timestamp || t.timestamp;\n\n newMap.set(t.id, {\n id: t.id,\n name: messageType,\n message: t.text,\n timestamp: existingTimestamp,\n isFinal: t.isFinal,\n });\n });\n\n return newMap;\n });\n },\n []\n );\n\n // Set up DOM portal for LiveKit\n useEffect(() => {\n const div = document.createElement(\"div\");\n div.style.display = \"none\";\n document.body.appendChild(div);\n portalRootRef.current = div;\n rootRef.current = createRoot(div);\n\n return () => {\n if (rootRef.current) {\n rootRef.current.unmount();\n }\n if (portalRootRef.current) {\n document.body.removeChild(portalRootRef.current);\n }\n };\n }, []);\n\n // Connect to VoxAI service - updated to include dynamicVariables\n const connect = useCallback(\n async ({ agentId, apiKey, dynamicVariables, metadata }: ConnectParams) => {\n try {\n setState(\"connecting\");\n\n const response = await fetch(HTTPS_API_ORIGIN, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n agent_id: agentId,\n metadata: {\n call_web: {\n dynamic_variables: dynamicVariables || {},\n metadata: metadata || {},\n },\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Connection failed (${response.status}): ${errorText}`\n );\n }\n\n const data = await response.json();\n setConnectionDetail(data);\n\n if (options.onConnect) {\n options.onConnect();\n }\n } catch (err) {\n // Reset state on error\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n const error = err instanceof Error ? err : new Error(String(err));\n\n if (options.onError) {\n options.onError(error);\n }\n }\n },\n [options]\n );\n\n // Disconnect from VoxAI service\n const disconnect = useCallback(() => {\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n if (options.onDisconnect) {\n options.onDisconnect();\n }\n }, [options]);\n\n // Render LiveKit components when connection details are available\n useEffect(() => {\n if (!rootRef.current) return;\n\n if (connectionDetail) {\n rootRef.current.render(\n <LiveKitRoom\n serverUrl={connectionDetail.serverUrl}\n token={connectionDetail.participantToken}\n audio={true}\n video={false}\n connect={true}\n onDisconnected={disconnect}\n // Add error handling for LiveKit connection\n onError={(error) => {\n console.error(\"LiveKit connection error:\", error);\n disconnect();\n if (options.onError) {\n options.onError(\n new Error(`LiveKit connection error: ${error.message}`)\n );\n }\n }}\n >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect, options.onError]);\n\n return {\n connect,\n disconnect,\n state,\n messages,\n };\n}\n\n/**\n * Component that monitors LiveKit state and communicates back to the main hook\n */\nfunction StateMonitor({ port }: { port: MessagePort | undefined }) {\n const { agent, state } = useVoiceAssistant();\n\n // Agent transcriptions\n const agentAudioTrack = useParticipantTracks(\n [Track.Source.Microphone],\n agent?.identity\n )[0];\n const agentTranscription = useTrackTranscription(agentAudioTrack);\n\n // User transcriptions\n const localParticipant = useLocalParticipant();\n const localMessages = useTrackTranscription({\n publication: localParticipant.microphoneTrack,\n source: Track.Source.Microphone,\n participant: localParticipant.localParticipant,\n });\n\n // Send agent state updates\n useEffect(() => {\n if (port) {\n port.postMessage({ type: \"state_update\", state });\n }\n }, [state, port]);\n\n // Send agent transcriptions\n useEffect(() => {\n if (port && agentTranscription.segments.length > 0) {\n const transcriptions = agentTranscription.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"agent\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [agentTranscription.segments, port]);\n\n // Send user transcriptions\n useEffect(() => {\n if (port && localMessages.segments.length > 0) {\n const transcriptions = localMessages.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"user\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [localMessages.segments, port]);\n\n return null;\n}\n"],"names":["StateMonitor","_ref2","port","agent","state","useVoiceAssistant","agentAudioTrack","useParticipantTracks","Track","Source","Microphone","identity","agentTranscription","useTrackTranscription","localParticipant","useLocalParticipant","localMessages","publication","microphoneTrack","source","participant","useEffect","postMessage","type","segments","length","transcriptions","map","segment","id","text","isFinal","final","timestamp","Date","now","speaker","options","connectionDetail","setConnectionDetail","useState","setState","transcriptMap","setTranscriptMap","Map","messages","setMessages","prevMessagesRef","useRef","processedMessageIdsRef","Set","portalRootRef","rootRef","channelRef","allMessages","Array","from","values","sort","a","b","messagesString","JSON","stringify","current","onMessage","filter","msg","has","forEach","add","MessageChannel","port1","onmessage","e","data","handleTranscriptionUpdate","_channelRef$current","close","useCallback","prevMap","newMap","t","_prevMap$get","messageType","existingTimestamp","get","set","name","message","div","document","createElement","style","display","body","appendChild","createRoot","unmount","removeChild","connect","_ref","agentId","apiKey","dynamicVariables","metadata","Promise","resolve","fetch","method","headers","Authorization","agent_id","call_web","dynamic_variables","then","response","_temp2","_result2","json","onConnect","_temp","ok","errorText","Error","status","_catch","err","error","String","onError","reject","disconnect","onDisconnect","_channelRef$current2","render","_jsxs","jsxs","LiveKitRoom","serverUrl","token","participantToken","audio","video","onDisconnected","console","_jsx","RoomAudioRenderer","port2","jsx","_Fragment","Fragment"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{jsxs as t,jsx as e,Fragment as n}from"react/jsx-runtime";import{LiveKitRoom as r,RoomAudioRenderer as o,useVoiceAssistant as i,useParticipantTracks as s,useTrackTranscription as a,useLocalParticipant as c}from"@livekit/components-react";import{Track as p}from"livekit-client";import{useState as l,useRef as d,useEffect as u,useCallback as m}from"react";import{createRoot as g}from"react-dom/client";function f(i={}){const[s,a]=l(null),[c,p]=l("disconnected"),[f,h]=l(new Map),[w,v]=l([]),M=d(""),E=d(new Set),k=d(null),x=d(null),_=d(null);u(()=>{const t=Array.from(f.values()).sort((t,e)=>t.timestamp-e.timestamp),e=JSON.stringify(t);e!==M.current&&(M.current=e,v(t),i.onMessage&&t.filter(t=>t.isFinal&&t.id&&!E.current.has(t.id)).forEach(t=>{t.id&&(E.current.add(t.id),null==i.onMessage||i.onMessage(t))}))},[f,i.onMessage]),u(()=>(_.current=new MessageChannel,_.current.port1.onmessage=t=>{const e=t.data;"state_update"===e.type?p(e.state):"transcription_update"===e.type&&b(e.transcriptions)},()=>{var t;null==(t=_.current)||t.port1.close()}),[]);const b=m(t=>{h(e=>{const n=new Map(e);return t.forEach(t=>{var r;const o="agent"===t.speaker?"agent":"user",i=(null==(r=e.get(t.id))?void 0:r.timestamp)||t.timestamp;n.set(t.id,{id:t.id,name:o,message:t.text,timestamp:i,isFinal:t.isFinal})}),n})},[]);u(()=>{const t=document.createElement("div");return t.style.display="none",document.body.appendChild(t),k.current=t,x.current=g(t),()=>{x.current&&x.current.unmount(),k.current&&document.body.removeChild(k.current)}},[]);const C=m(async({agentId:t,apiKey:e,dynamicVariables:n})=>{try{p("connecting");const r=await fetch("https://www.tryvox.co/api/agent/sdk",{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,metadata:{call_web:{dynamic_variables:n||{}}}})});if(!r.ok){const t=await r.text();throw new Error(`Connection failed (${r.status}): ${t}`)}const o=await r.json();a(o),i.onConnect&&i.onConnect()}catch(t){a(null),h(new Map),v([]),p("disconnected");const e=t instanceof Error?t:new Error(String(t));i.onError&&i.onError(e)}},[i]),S=m(()=>{a(null),h(new Map),v([]),p("disconnected"),i.onDisconnect&&i.onDisconnect()},[i]);return u(()=>{var a;x.current&&x.current.render(s?t(r,{serverUrl:s.serverUrl,token:s.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:S,onError:t=>{console.error("LiveKit connection error:",t),S(),i.onError&&i.onError(new Error(`LiveKit connection error: ${t.message}`))},children:[e(o,{}),e(y,{port:null==(a=_.current)?void 0:a.port2})]}):e(n,{}))},[s,S,i.onError]),{connect:C,disconnect:S,state:c,messages:w}}function y({port:t}){const{agent:e,state:n}=i(),r=s([p.Source.Microphone],null==e?void 0:e.identity)[0],o=a(r),l=c(),d=a({publication:l.microphoneTrack,source:p.Source.Microphone,participant:l.localParticipant});return u(()=>{t&&t.postMessage({type:"state_update",state:n})},[n,t]),u(()=>{if(t&&o.segments.length>0){const e=o.segments.map(t=>({id:t.id,text:t.text,isFinal:t.final,timestamp:Date.now(),speaker:"agent"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[o.segments,t]),u(()=>{if(t&&d.segments.length>0){const e=d.segments.map(t=>({id:t.id,text:t.text,isFinal:t.final,timestamp:Date.now(),speaker:"user"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[d.segments,t]),null}export{f as useVoxAI};
1
+ import{jsxs as t,jsx as e,Fragment as n}from"react/jsx-runtime";import{LiveKitRoom as r,RoomAudioRenderer as o,useVoiceAssistant as a,useParticipantTracks as i,useTrackTranscription as s,useLocalParticipant as c}from"@livekit/components-react";import{Track as p}from"livekit-client";import{useState as d,useRef as l,useEffect as u,useCallback as m}from"react";import{createRoot as g}from"react-dom/client";function f(a={}){const[i,s]=d(null),[c,p]=d("disconnected"),[f,h]=d(new Map),[w,v]=d([]),M=l(""),E=l(new Set),k=l(null),x=l(null),_=l(null);u(()=>{const t=Array.from(f.values()).sort((t,e)=>t.timestamp-e.timestamp),e=JSON.stringify(t);e!==M.current&&(M.current=e,v(t),a.onMessage&&t.filter(t=>t.isFinal&&t.id&&!E.current.has(t.id)).forEach(t=>{t.id&&(E.current.add(t.id),null==a.onMessage||a.onMessage(t))}))},[f,a.onMessage]),u(()=>(_.current=new MessageChannel,_.current.port1.onmessage=t=>{const e=t.data;"state_update"===e.type?p(e.state):"transcription_update"===e.type&&b(e.transcriptions)},()=>{var t;null==(t=_.current)||t.port1.close()}),[]);const b=m(t=>{h(e=>{const n=new Map(e);return t.forEach(t=>{var r;const o="agent"===t.speaker?"agent":"user",a=(null==(r=e.get(t.id))?void 0:r.timestamp)||t.timestamp;n.set(t.id,{id:t.id,name:o,message:t.text,timestamp:a,isFinal:t.isFinal})}),n})},[]);u(()=>{const t=document.createElement("div");return t.style.display="none",document.body.appendChild(t),k.current=t,x.current=g(t),()=>{x.current&&x.current.unmount(),k.current&&document.body.removeChild(k.current)}},[]);const C=m(async({agentId:t,apiKey:e,dynamicVariables:n,metadata:r})=>{try{p("connecting");const o=await fetch("https://www.tryvox.co/api/agent/sdk",{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,metadata:{call_web:{dynamic_variables:n||{},metadata:r||{}}}})});if(!o.ok){const t=await o.text();throw new Error(`Connection failed (${o.status}): ${t}`)}const i=await o.json();s(i),a.onConnect&&a.onConnect()}catch(t){s(null),h(new Map),v([]),p("disconnected");const e=t instanceof Error?t:new Error(String(t));a.onError&&a.onError(e)}},[a]),S=m(()=>{s(null),h(new Map),v([]),p("disconnected"),a.onDisconnect&&a.onDisconnect()},[a]);return u(()=>{var s;x.current&&x.current.render(i?t(r,{serverUrl:i.serverUrl,token:i.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:S,onError:t=>{console.error("LiveKit connection error:",t),S(),a.onError&&a.onError(new Error(`LiveKit connection error: ${t.message}`))},children:[e(o,{}),e(y,{port:null==(s=_.current)?void 0:s.port2})]}):e(n,{}))},[i,S,a.onError]),{connect:C,disconnect:S,state:c,messages:w}}function y({port:t}){const{agent:e,state:n}=a(),r=i([p.Source.Microphone],null==e?void 0:e.identity)[0],o=s(r),d=c(),l=s({publication:d.microphoneTrack,source:p.Source.Microphone,participant:d.localParticipant});return u(()=>{t&&t.postMessage({type:"state_update",state:n})},[n,t]),u(()=>{if(t&&o.segments.length>0){const e=o.segments.map(t=>({id:t.id,text:t.text,isFinal:t.final,timestamp:Date.now(),speaker:"agent"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[o.segments,t]),u(()=>{if(t&&l.segments.length>0){const e=l.segments.map(t=>({id:t.id,text:t.text,isFinal:t.final,timestamp:Date.now(),speaker:"user"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[l.segments,t]),null}export{f as useVoxAI};
2
2
  //# sourceMappingURL=lib.modern.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lib.modern.js","sources":["../src/hooks/useVoxAI.tsx"],"sourcesContent":["import {\n LiveKitRoom,\n RoomAudioRenderer,\n useLocalParticipant,\n useParticipantTracks,\n useTrackTranscription,\n useVoiceAssistant,\n} from \"@livekit/components-react\";\nimport { Track } from \"livekit-client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\n\n// Connection types\ntype VoxConnectionDetail = {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n};\n\ntype VoxAgentState =\n | \"disconnected\"\n | \"connecting\"\n | \"initializing\"\n | \"listening\"\n | \"thinking\"\n | \"speaking\";\n\n// API endpoint\nconst HTTPS_API_ORIGIN = \"https://www.tryvox.co/api/agent/sdk\";\n// const HTTPS_API_ORIGIN = \"http://localhost:3000/api/agent/sdk\";\n\nexport type VoxMessage = {\n id?: string;\n name: \"agent\" | \"user\" | \"tool\";\n message?: string;\n timestamp: number;\n isFinal?: boolean;\n};\n\n// Hook configuration\ninterface VoxAIOptions {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n onMessage?: (message: VoxMessage) => void;\n}\n\n// Message channel event types\ntype MessageChannelEvent =\n | { type: \"state_update\"; state: VoxAgentState }\n | { type: \"transcription_update\"; transcriptions: TranscriptionSegment[] };\n\ntype TranscriptionSegment = {\n id: string;\n text: string;\n isFinal: boolean;\n timestamp: number;\n speaker: \"agent\" | \"user\";\n};\n\n// Update the connection parameter type to include dynamicVariables\ninterface ConnectParams {\n agentId: string;\n apiKey: string;\n dynamicVariables?: Record<string, any>; // Allow any dynamicVariables fields\n}\n\n/**\n * Hook for integrating with VoxAI voice assistant\n */\nexport function useVoxAI(options: VoxAIOptions = {}) {\n // Connection state\n const [connectionDetail, setConnectionDetail] =\n useState<VoxConnectionDetail | null>(null);\n const [state, setState] = useState<VoxAgentState>(\"disconnected\");\n\n // Message handling\n const [transcriptMap, setTranscriptMap] = useState<Map<string, VoxMessage>>(\n new Map()\n );\n const [messages, setMessages] = useState<VoxMessage[]>([]);\n const prevMessagesRef = useRef<string>(\"\");\n\n // Track which messages we've already sent to the onMessage callback\n const processedMessageIdsRef = useRef<Set<string>>(new Set());\n\n // DOM manipulation for LiveKit portal\n const portalRootRef = useRef<HTMLDivElement | null>(null);\n const rootRef = useRef<Root | null>(null);\n\n // Communication channel\n const channelRef = useRef<MessageChannel | null>(null);\n\n // Update messages whenever transcriptMap changes\n useEffect(() => {\n const allMessages = Array.from(transcriptMap.values()).sort(\n (a, b) => a.timestamp - b.timestamp\n );\n\n // Only update if the messages have actually changed\n const messagesString = JSON.stringify(allMessages);\n if (messagesString !== prevMessagesRef.current) {\n prevMessagesRef.current = messagesString;\n setMessages(allMessages);\n\n // Only trigger onMessage for new final messages that haven't been processed yet\n if (options.onMessage) {\n allMessages\n .filter(\n (msg) =>\n msg.isFinal &&\n msg.id &&\n !processedMessageIdsRef.current.has(msg.id)\n )\n .forEach((msg) => {\n if (msg.id) {\n // Mark this message as processed\n processedMessageIdsRef.current.add(msg.id);\n // Call the callback\n options.onMessage?.(msg);\n }\n });\n }\n }\n }, [transcriptMap, options.onMessage]);\n\n // Initialize message channel\n useEffect(() => {\n channelRef.current = new MessageChannel();\n channelRef.current.port1.onmessage = (e) => {\n const data = e.data as MessageChannelEvent;\n\n if (data.type === \"state_update\") {\n setState(data.state);\n } else if (data.type === \"transcription_update\") {\n handleTranscriptionUpdate(data.transcriptions);\n }\n };\n\n return () => {\n channelRef.current?.port1.close();\n };\n }, []);\n\n // Process incoming transcriptions\n const handleTranscriptionUpdate = useCallback(\n (transcriptions: TranscriptionSegment[]) => {\n setTranscriptMap((prevMap) => {\n const newMap = new Map(prevMap);\n\n transcriptions.forEach((t) => {\n const messageType = t.speaker === \"agent\" ? \"agent\" : \"user\";\n // Use existing timestamp if we already have this segment\n const existingTimestamp = prevMap.get(t.id)?.timestamp || t.timestamp;\n\n newMap.set(t.id, {\n id: t.id,\n name: messageType,\n message: t.text,\n timestamp: existingTimestamp,\n isFinal: t.isFinal,\n });\n });\n\n return newMap;\n });\n },\n []\n );\n\n // Set up DOM portal for LiveKit\n useEffect(() => {\n const div = document.createElement(\"div\");\n div.style.display = \"none\";\n document.body.appendChild(div);\n portalRootRef.current = div;\n rootRef.current = createRoot(div);\n\n return () => {\n if (rootRef.current) {\n rootRef.current.unmount();\n }\n if (portalRootRef.current) {\n document.body.removeChild(portalRootRef.current);\n }\n };\n }, []);\n\n // Connect to VoxAI service - updated to include dynamicVariables\n const connect = useCallback(\n async ({ agentId, apiKey, dynamicVariables }: ConnectParams) => {\n try {\n setState(\"connecting\");\n\n const response = await fetch(HTTPS_API_ORIGIN, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n agent_id: agentId,\n metadata: {\n call_web: {\n dynamic_variables: dynamicVariables || {},\n },\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Connection failed (${response.status}): ${errorText}`\n );\n }\n\n const data = await response.json();\n setConnectionDetail(data);\n\n if (options.onConnect) {\n options.onConnect();\n }\n } catch (err) {\n // Reset state on error\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n const error = err instanceof Error ? err : new Error(String(err));\n\n if (options.onError) {\n options.onError(error);\n }\n }\n },\n [options]\n );\n\n // Disconnect from VoxAI service\n const disconnect = useCallback(() => {\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n if (options.onDisconnect) {\n options.onDisconnect();\n }\n }, [options]);\n\n // Render LiveKit components when connection details are available\n useEffect(() => {\n if (!rootRef.current) return;\n\n if (connectionDetail) {\n rootRef.current.render(\n <LiveKitRoom\n serverUrl={connectionDetail.serverUrl}\n token={connectionDetail.participantToken}\n audio={true}\n video={false}\n connect={true}\n onDisconnected={disconnect}\n // Add error handling for LiveKit connection\n onError={(error) => {\n console.error(\"LiveKit connection error:\", error);\n disconnect();\n if (options.onError) {\n options.onError(\n new Error(`LiveKit connection error: ${error.message}`)\n );\n }\n }}\n >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect, options.onError]);\n\n return {\n connect,\n disconnect,\n state,\n messages,\n };\n}\n\n/**\n * Component that monitors LiveKit state and communicates back to the main hook\n */\nfunction StateMonitor({ port }: { port: MessagePort | undefined }) {\n const { agent, state } = useVoiceAssistant();\n\n // Agent transcriptions\n const agentAudioTrack = useParticipantTracks(\n [Track.Source.Microphone],\n agent?.identity\n )[0];\n const agentTranscription = useTrackTranscription(agentAudioTrack);\n\n // User transcriptions\n const localParticipant = useLocalParticipant();\n const localMessages = useTrackTranscription({\n publication: localParticipant.microphoneTrack,\n source: Track.Source.Microphone,\n participant: localParticipant.localParticipant,\n });\n\n // Send agent state updates\n useEffect(() => {\n if (port) {\n port.postMessage({ type: \"state_update\", state });\n }\n }, [state, port]);\n\n // Send agent transcriptions\n useEffect(() => {\n if (port && agentTranscription.segments.length > 0) {\n const transcriptions = agentTranscription.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"agent\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [agentTranscription.segments, port]);\n\n // Send user transcriptions\n useEffect(() => {\n if (port && localMessages.segments.length > 0) {\n const transcriptions = localMessages.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"user\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [localMessages.segments, port]);\n\n return null;\n}\n"],"names":["useVoxAI","options","connectionDetail","setConnectionDetail","useState","state","setState","transcriptMap","setTranscriptMap","Map","messages","setMessages","prevMessagesRef","useRef","processedMessageIdsRef","Set","portalRootRef","rootRef","channelRef","useEffect","allMessages","Array","from","values","sort","a","b","timestamp","messagesString","JSON","stringify","current","onMessage","filter","msg","isFinal","id","has","forEach","add","MessageChannel","port1","onmessage","e","data","type","handleTranscriptionUpdate","transcriptions","_channelRef$current","close","useCallback","prevMap","newMap","t","_prevMap$get","messageType","speaker","existingTimestamp","get","set","name","message","text","div","document","createElement","style","display","body","appendChild","createRoot","unmount","removeChild","connect","async","agentId","apiKey","dynamicVariables","response","fetch","method","headers","Authorization","agent_id","metadata","call_web","dynamic_variables","ok","errorText","Error","status","json","onConnect","err","error","String","onError","disconnect","onDisconnect","_channelRef$current2","render","_jsxs","LiveKitRoom","serverUrl","token","participantToken","audio","video","onDisconnected","console","_jsx","RoomAudioRenderer","StateMonitor","port","port2","_Fragment","agent","useVoiceAssistant","agentAudioTrack","useParticipantTracks","Track","Source","Microphone","identity","agentTranscription","useTrackTranscription","localParticipant","useLocalParticipant","localMessages","publication","microphoneTrack","source","participant","postMessage","segments","length","map","segment","final","Date","now"],"mappings":"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"}
1
+ {"version":3,"file":"lib.modern.js","sources":["../src/hooks/useVoxAI.tsx"],"sourcesContent":["import {\n LiveKitRoom,\n RoomAudioRenderer,\n useLocalParticipant,\n useParticipantTracks,\n useTrackTranscription,\n useVoiceAssistant,\n} from \"@livekit/components-react\";\nimport { Track } from \"livekit-client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\n\ntype VoxConnectionDetail = {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n};\n\n/**\n * VoxAgentState\n * @description The state of the agent\n */\nexport type VoxAgentState =\n | \"disconnected\"\n | \"connecting\"\n | \"initializing\"\n | \"listening\"\n | \"thinking\"\n | \"speaking\";\n\n// API endpoint\nconst HTTPS_API_ORIGIN = \"https://www.tryvox.co/api/agent/sdk\";\n// const HTTPS_API_ORIGIN = \"http://localhost:3000/api/agent/sdk\";\n\n/**\n * VoxMessage\n * @description The message type between the agent and the user\n */\nexport type VoxMessage = {\n id?: string;\n name: \"agent\" | \"user\" | \"tool\";\n message?: string;\n timestamp: number;\n isFinal?: boolean;\n};\n\n/**\n * VoxAIOptions\n * @description The callback functions for the useVoxAI hook\n */\nexport interface VoxAIOptions {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n onMessage?: (message: VoxMessage) => void;\n}\n\n// Message channel event types\ntype MessageChannelEvent =\n | { type: \"state_update\"; state: VoxAgentState }\n | { type: \"transcription_update\"; transcriptions: TranscriptionSegment[] };\n\ntype TranscriptionSegment = {\n id: string;\n text: string;\n isFinal: boolean;\n timestamp: number;\n speaker: \"agent\" | \"user\";\n};\n\n/**\n * ConnectParams\n * @param agentId - The agent ID\n * @param apiKey - The API key\n * @param dynamicVariables - The dynamic variables\n * @param metadata - 이 메타데이터는 아웃바운드 웹훅, 통화 기록에 포함됩니다.\n */\nexport interface ConnectParams {\n agentId: string;\n apiKey: string;\n dynamicVariables?: Record<string, any>;\n metadata?: Record<string, any>;\n}\n\n/**\n * useVoxAI\n * @description The hook for integrating with vox.ai voice assistant\n * @param options - The options for the useVoxAI hook\n * @returns The useVoxAI hook\n * @example\n * const { connect, disconnect, state, messages } = useVoxAI({\n * onConnect: () => console.log(\"Connected\"),\n * onDisconnect: () => console.log(\"Disconnected\"),\n * onError: (error) => console.error(\"Error:\", error),\n * onMessage: (message) => console.log(\"Message:\", message),\n * });\n */\nexport function useVoxAI(options: VoxAIOptions = {}) {\n // Connection state\n const [connectionDetail, setConnectionDetail] =\n useState<VoxConnectionDetail | null>(null);\n const [state, setState] = useState<VoxAgentState>(\"disconnected\");\n\n // Message handling\n const [transcriptMap, setTranscriptMap] = useState<Map<string, VoxMessage>>(\n new Map()\n );\n const [messages, setMessages] = useState<VoxMessage[]>([]);\n const prevMessagesRef = useRef<string>(\"\");\n\n // Track which messages we've already sent to the onMessage callback\n const processedMessageIdsRef = useRef<Set<string>>(new Set());\n\n // DOM manipulation for LiveKit portal\n const portalRootRef = useRef<HTMLDivElement | null>(null);\n const rootRef = useRef<Root | null>(null);\n\n // Communication channel\n const channelRef = useRef<MessageChannel | null>(null);\n\n // Update messages whenever transcriptMap changes\n useEffect(() => {\n const allMessages = Array.from(transcriptMap.values()).sort(\n (a, b) => a.timestamp - b.timestamp\n );\n\n // Only update if the messages have actually changed\n const messagesString = JSON.stringify(allMessages);\n if (messagesString !== prevMessagesRef.current) {\n prevMessagesRef.current = messagesString;\n setMessages(allMessages);\n\n // Only trigger onMessage for new final messages that haven't been processed yet\n if (options.onMessage) {\n allMessages\n .filter(\n (msg) =>\n msg.isFinal &&\n msg.id &&\n !processedMessageIdsRef.current.has(msg.id)\n )\n .forEach((msg) => {\n if (msg.id) {\n // Mark this message as processed\n processedMessageIdsRef.current.add(msg.id);\n // Call the callback\n options.onMessage?.(msg);\n }\n });\n }\n }\n }, [transcriptMap, options.onMessage]);\n\n // Initialize message channel\n useEffect(() => {\n channelRef.current = new MessageChannel();\n channelRef.current.port1.onmessage = (e) => {\n const data = e.data as MessageChannelEvent;\n\n if (data.type === \"state_update\") {\n setState(data.state);\n } else if (data.type === \"transcription_update\") {\n handleTranscriptionUpdate(data.transcriptions);\n }\n };\n\n return () => {\n channelRef.current?.port1.close();\n };\n }, []);\n\n // Process incoming transcriptions\n const handleTranscriptionUpdate = useCallback(\n (transcriptions: TranscriptionSegment[]) => {\n setTranscriptMap((prevMap) => {\n const newMap = new Map(prevMap);\n\n transcriptions.forEach((t) => {\n const messageType = t.speaker === \"agent\" ? \"agent\" : \"user\";\n // Use existing timestamp if we already have this segment\n const existingTimestamp = prevMap.get(t.id)?.timestamp || t.timestamp;\n\n newMap.set(t.id, {\n id: t.id,\n name: messageType,\n message: t.text,\n timestamp: existingTimestamp,\n isFinal: t.isFinal,\n });\n });\n\n return newMap;\n });\n },\n []\n );\n\n // Set up DOM portal for LiveKit\n useEffect(() => {\n const div = document.createElement(\"div\");\n div.style.display = \"none\";\n document.body.appendChild(div);\n portalRootRef.current = div;\n rootRef.current = createRoot(div);\n\n return () => {\n if (rootRef.current) {\n rootRef.current.unmount();\n }\n if (portalRootRef.current) {\n document.body.removeChild(portalRootRef.current);\n }\n };\n }, []);\n\n // Connect to VoxAI service - updated to include dynamicVariables\n const connect = useCallback(\n async ({ agentId, apiKey, dynamicVariables, metadata }: ConnectParams) => {\n try {\n setState(\"connecting\");\n\n const response = await fetch(HTTPS_API_ORIGIN, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n agent_id: agentId,\n metadata: {\n call_web: {\n dynamic_variables: dynamicVariables || {},\n metadata: metadata || {},\n },\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Connection failed (${response.status}): ${errorText}`\n );\n }\n\n const data = await response.json();\n setConnectionDetail(data);\n\n if (options.onConnect) {\n options.onConnect();\n }\n } catch (err) {\n // Reset state on error\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n const error = err instanceof Error ? err : new Error(String(err));\n\n if (options.onError) {\n options.onError(error);\n }\n }\n },\n [options]\n );\n\n // Disconnect from VoxAI service\n const disconnect = useCallback(() => {\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n if (options.onDisconnect) {\n options.onDisconnect();\n }\n }, [options]);\n\n // Render LiveKit components when connection details are available\n useEffect(() => {\n if (!rootRef.current) return;\n\n if (connectionDetail) {\n rootRef.current.render(\n <LiveKitRoom\n serverUrl={connectionDetail.serverUrl}\n token={connectionDetail.participantToken}\n audio={true}\n video={false}\n connect={true}\n onDisconnected={disconnect}\n // Add error handling for LiveKit connection\n onError={(error) => {\n console.error(\"LiveKit connection error:\", error);\n disconnect();\n if (options.onError) {\n options.onError(\n new Error(`LiveKit connection error: ${error.message}`)\n );\n }\n }}\n >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect, options.onError]);\n\n return {\n connect,\n disconnect,\n state,\n messages,\n };\n}\n\n/**\n * Component that monitors LiveKit state and communicates back to the main hook\n */\nfunction StateMonitor({ port }: { port: MessagePort | undefined }) {\n const { agent, state } = useVoiceAssistant();\n\n // Agent transcriptions\n const agentAudioTrack = useParticipantTracks(\n [Track.Source.Microphone],\n agent?.identity\n )[0];\n const agentTranscription = useTrackTranscription(agentAudioTrack);\n\n // User transcriptions\n const localParticipant = useLocalParticipant();\n const localMessages = useTrackTranscription({\n publication: localParticipant.microphoneTrack,\n source: Track.Source.Microphone,\n participant: localParticipant.localParticipant,\n });\n\n // Send agent state updates\n useEffect(() => {\n if (port) {\n port.postMessage({ type: \"state_update\", state });\n }\n }, [state, port]);\n\n // Send agent transcriptions\n useEffect(() => {\n if (port && agentTranscription.segments.length > 0) {\n const transcriptions = agentTranscription.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"agent\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [agentTranscription.segments, port]);\n\n // Send user transcriptions\n useEffect(() => {\n if (port && localMessages.segments.length > 0) {\n const transcriptions = localMessages.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"user\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [localMessages.segments, port]);\n\n return null;\n}\n"],"names":["useVoxAI","options","connectionDetail","setConnectionDetail","useState","state","setState","transcriptMap","setTranscriptMap","Map","messages","setMessages","prevMessagesRef","useRef","processedMessageIdsRef","Set","portalRootRef","rootRef","channelRef","useEffect","allMessages","Array","from","values","sort","a","b","timestamp","messagesString","JSON","stringify","current","onMessage","filter","msg","isFinal","id","has","forEach","add","MessageChannel","port1","onmessage","e","data","type","handleTranscriptionUpdate","transcriptions","_channelRef$current","close","useCallback","prevMap","newMap","t","_prevMap$get","messageType","speaker","existingTimestamp","get","set","name","message","text","div","document","createElement","style","display","body","appendChild","createRoot","unmount","removeChild","connect","async","agentId","apiKey","dynamicVariables","metadata","response","fetch","method","headers","Authorization","agent_id","call_web","dynamic_variables","ok","errorText","Error","status","json","onConnect","err","error","String","onError","disconnect","onDisconnect","_channelRef$current2","render","_jsxs","LiveKitRoom","serverUrl","token","participantToken","audio","video","onDisconnected","console","_jsx","RoomAudioRenderer","StateMonitor","port","port2","_Fragment","agent","useVoiceAssistant","agentAudioTrack","useParticipantTracks","Track","Source","Microphone","identity","agentTranscription","useTrackTranscription","localParticipant","useLocalParticipant","localMessages","publication","microphoneTrack","source","participant","postMessage","segments","length","map","segment","final","Date","now"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{jsxs as e,jsx as t,Fragment as n}from"react/jsx-runtime";import{LiveKitRoom as r,RoomAudioRenderer as o,useVoiceAssistant as i,useParticipantTracks as s,useTrackTranscription as a,useLocalParticipant as c}from"@livekit/components-react";import{Track as u}from"livekit-client";import{useState as l,useRef as p,useEffect as d,useCallback as m}from"react";import{createRoot as g}from"react-dom/client";function f(i){void 0===i&&(i={});const[s,a]=l(null),[c,u]=l("disconnected"),[f,v]=l(new Map),[y,w]=l([]),M=p(""),E=p(new Set),k=p(null),x=p(null),_=p(null);d(()=>{const e=Array.from(f.values()).sort((e,t)=>e.timestamp-t.timestamp),t=JSON.stringify(e);t!==M.current&&(M.current=t,w(e),i.onMessage&&e.filter(e=>e.isFinal&&e.id&&!E.current.has(e.id)).forEach(e=>{e.id&&(E.current.add(e.id),null==i.onMessage||i.onMessage(e))}))},[f,i.onMessage]),d(()=>(_.current=new MessageChannel,_.current.port1.onmessage=e=>{const t=e.data;"state_update"===t.type?u(t.state):"transcription_update"===t.type&&b(t.transcriptions)},()=>{var e;null==(e=_.current)||e.port1.close()}),[]);const b=m(e=>{v(t=>{const n=new Map(t);return e.forEach(e=>{var r;const o="agent"===e.speaker?"agent":"user",i=(null==(r=t.get(e.id))?void 0:r.timestamp)||e.timestamp;n.set(e.id,{id:e.id,name:o,message:e.text,timestamp:i,isFinal:e.isFinal})}),n})},[]);d(()=>{const e=document.createElement("div");return e.style.display="none",document.body.appendChild(e),k.current=e,x.current=g(e),()=>{x.current&&x.current.unmount(),k.current&&document.body.removeChild(k.current)}},[]);const C=m(function(e){let{agentId:t,apiKey:n,dynamicVariables:r}=e;try{return Promise.resolve(function(e,o){try{var s=(u("connecting"),Promise.resolve(fetch("https://www.tryvox.co/api/agent/sdk",{method:"POST",headers:{Authorization:"Bearer "+n,"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,metadata:{call_web:{dynamic_variables:r||{}}}})})).then(function(e){function t(t){return Promise.resolve(e.json()).then(function(e){a(e),i.onConnect&&i.onConnect()})}const n=function(){if(!e.ok)return Promise.resolve(e.text()).then(function(t){throw new Error("Connection failed ("+e.status+"): "+t)})}();return n&&n.then?n.then(t):t()}))}catch(e){return o(e)}return s&&s.then?s.then(void 0,o):s}(0,function(e){a(null),v(new Map),w([]),u("disconnected");const t=e instanceof Error?e:new Error(String(e));i.onError&&i.onError(t)}))}catch(e){return Promise.reject(e)}},[i]),P=m(()=>{a(null),v(new Map),w([]),u("disconnected"),i.onDisconnect&&i.onDisconnect()},[i]);return d(()=>{var a;x.current&&x.current.render(s?e(r,{serverUrl:s.serverUrl,token:s.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:P,onError:e=>{console.error("LiveKit connection error:",e),P(),i.onError&&i.onError(new Error("LiveKit connection error: "+e.message))},children:[t(o,{}),t(h,{port:null==(a=_.current)?void 0:a.port2})]}):t(n,{}))},[s,P,i.onError]),{connect:C,disconnect:P,state:c,messages:y}}function h(e){let{port:t}=e;const{agent:n,state:r}=i(),o=s([u.Source.Microphone],null==n?void 0:n.identity)[0],l=a(o),p=c(),m=a({publication:p.microphoneTrack,source:u.Source.Microphone,participant:p.localParticipant});return d(()=>{t&&t.postMessage({type:"state_update",state:r})},[r,t]),d(()=>{if(t&&l.segments.length>0){const e=l.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"agent"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[l.segments,t]),d(()=>{if(t&&m.segments.length>0){const e=m.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"user"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[m.segments,t]),null}export{f as useVoxAI};
1
+ import{jsxs as e,jsx as t,Fragment as n}from"react/jsx-runtime";import{LiveKitRoom as r,RoomAudioRenderer as o,useVoiceAssistant as i,useParticipantTracks as s,useTrackTranscription as a,useLocalParticipant as c}from"@livekit/components-react";import{Track as u}from"livekit-client";import{useState as l,useRef as p,useEffect as d,useCallback as m}from"react";import{createRoot as g}from"react-dom/client";function f(i){void 0===i&&(i={});const[s,a]=l(null),[c,u]=l("disconnected"),[f,v]=l(new Map),[y,w]=l([]),M=p(""),E=p(new Set),k=p(null),x=p(null),_=p(null);d(()=>{const e=Array.from(f.values()).sort((e,t)=>e.timestamp-t.timestamp),t=JSON.stringify(e);t!==M.current&&(M.current=t,w(e),i.onMessage&&e.filter(e=>e.isFinal&&e.id&&!E.current.has(e.id)).forEach(e=>{e.id&&(E.current.add(e.id),null==i.onMessage||i.onMessage(e))}))},[f,i.onMessage]),d(()=>(_.current=new MessageChannel,_.current.port1.onmessage=e=>{const t=e.data;"state_update"===t.type?u(t.state):"transcription_update"===t.type&&b(t.transcriptions)},()=>{var e;null==(e=_.current)||e.port1.close()}),[]);const b=m(e=>{v(t=>{const n=new Map(t);return e.forEach(e=>{var r;const o="agent"===e.speaker?"agent":"user",i=(null==(r=t.get(e.id))?void 0:r.timestamp)||e.timestamp;n.set(e.id,{id:e.id,name:o,message:e.text,timestamp:i,isFinal:e.isFinal})}),n})},[]);d(()=>{const e=document.createElement("div");return e.style.display="none",document.body.appendChild(e),k.current=e,x.current=g(e),()=>{x.current&&x.current.unmount(),k.current&&document.body.removeChild(k.current)}},[]);const C=m(function(e){let{agentId:t,apiKey:n,dynamicVariables:r,metadata:o}=e;try{return Promise.resolve(function(e,s){try{var c=(u("connecting"),Promise.resolve(fetch("https://www.tryvox.co/api/agent/sdk",{method:"POST",headers:{Authorization:"Bearer "+n,"Content-Type":"application/json"},body:JSON.stringify({agent_id:t,metadata:{call_web:{dynamic_variables:r||{},metadata:o||{}}}})})).then(function(e){function t(t){return Promise.resolve(e.json()).then(function(e){a(e),i.onConnect&&i.onConnect()})}const n=function(){if(!e.ok)return Promise.resolve(e.text()).then(function(t){throw new Error("Connection failed ("+e.status+"): "+t)})}();return n&&n.then?n.then(t):t()}))}catch(e){return s(e)}return c&&c.then?c.then(void 0,s):c}(0,function(e){a(null),v(new Map),w([]),u("disconnected");const t=e instanceof Error?e:new Error(String(e));i.onError&&i.onError(t)}))}catch(e){return Promise.reject(e)}},[i]),P=m(()=>{a(null),v(new Map),w([]),u("disconnected"),i.onDisconnect&&i.onDisconnect()},[i]);return d(()=>{var a;x.current&&x.current.render(s?e(r,{serverUrl:s.serverUrl,token:s.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:P,onError:e=>{console.error("LiveKit connection error:",e),P(),i.onError&&i.onError(new Error("LiveKit connection error: "+e.message))},children:[t(o,{}),t(h,{port:null==(a=_.current)?void 0:a.port2})]}):t(n,{}))},[s,P,i.onError]),{connect:C,disconnect:P,state:c,messages:y}}function h(e){let{port:t}=e;const{agent:n,state:r}=i(),o=s([u.Source.Microphone],null==n?void 0:n.identity)[0],l=a(o),p=c(),m=a({publication:p.microphoneTrack,source:u.Source.Microphone,participant:p.localParticipant});return d(()=>{t&&t.postMessage({type:"state_update",state:r})},[r,t]),d(()=>{if(t&&l.segments.length>0){const e=l.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"agent"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[l.segments,t]),d(()=>{if(t&&m.segments.length>0){const e=m.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"user"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[m.segments,t]),null}export{f as useVoxAI};
2
2
  //# sourceMappingURL=lib.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lib.module.js","sources":["../src/hooks/useVoxAI.tsx"],"sourcesContent":["import {\n LiveKitRoom,\n RoomAudioRenderer,\n useLocalParticipant,\n useParticipantTracks,\n useTrackTranscription,\n useVoiceAssistant,\n} from \"@livekit/components-react\";\nimport { Track } from \"livekit-client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\n\n// Connection types\ntype VoxConnectionDetail = {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n};\n\ntype VoxAgentState =\n | \"disconnected\"\n | \"connecting\"\n | \"initializing\"\n | \"listening\"\n | \"thinking\"\n | \"speaking\";\n\n// API endpoint\nconst HTTPS_API_ORIGIN = \"https://www.tryvox.co/api/agent/sdk\";\n// const HTTPS_API_ORIGIN = \"http://localhost:3000/api/agent/sdk\";\n\nexport type VoxMessage = {\n id?: string;\n name: \"agent\" | \"user\" | \"tool\";\n message?: string;\n timestamp: number;\n isFinal?: boolean;\n};\n\n// Hook configuration\ninterface VoxAIOptions {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n onMessage?: (message: VoxMessage) => void;\n}\n\n// Message channel event types\ntype MessageChannelEvent =\n | { type: \"state_update\"; state: VoxAgentState }\n | { type: \"transcription_update\"; transcriptions: TranscriptionSegment[] };\n\ntype TranscriptionSegment = {\n id: string;\n text: string;\n isFinal: boolean;\n timestamp: number;\n speaker: \"agent\" | \"user\";\n};\n\n// Update the connection parameter type to include dynamicVariables\ninterface ConnectParams {\n agentId: string;\n apiKey: string;\n dynamicVariables?: Record<string, any>; // Allow any dynamicVariables fields\n}\n\n/**\n * Hook for integrating with VoxAI voice assistant\n */\nexport function useVoxAI(options: VoxAIOptions = {}) {\n // Connection state\n const [connectionDetail, setConnectionDetail] =\n useState<VoxConnectionDetail | null>(null);\n const [state, setState] = useState<VoxAgentState>(\"disconnected\");\n\n // Message handling\n const [transcriptMap, setTranscriptMap] = useState<Map<string, VoxMessage>>(\n new Map()\n );\n const [messages, setMessages] = useState<VoxMessage[]>([]);\n const prevMessagesRef = useRef<string>(\"\");\n\n // Track which messages we've already sent to the onMessage callback\n const processedMessageIdsRef = useRef<Set<string>>(new Set());\n\n // DOM manipulation for LiveKit portal\n const portalRootRef = useRef<HTMLDivElement | null>(null);\n const rootRef = useRef<Root | null>(null);\n\n // Communication channel\n const channelRef = useRef<MessageChannel | null>(null);\n\n // Update messages whenever transcriptMap changes\n useEffect(() => {\n const allMessages = Array.from(transcriptMap.values()).sort(\n (a, b) => a.timestamp - b.timestamp\n );\n\n // Only update if the messages have actually changed\n const messagesString = JSON.stringify(allMessages);\n if (messagesString !== prevMessagesRef.current) {\n prevMessagesRef.current = messagesString;\n setMessages(allMessages);\n\n // Only trigger onMessage for new final messages that haven't been processed yet\n if (options.onMessage) {\n allMessages\n .filter(\n (msg) =>\n msg.isFinal &&\n msg.id &&\n !processedMessageIdsRef.current.has(msg.id)\n )\n .forEach((msg) => {\n if (msg.id) {\n // Mark this message as processed\n processedMessageIdsRef.current.add(msg.id);\n // Call the callback\n options.onMessage?.(msg);\n }\n });\n }\n }\n }, [transcriptMap, options.onMessage]);\n\n // Initialize message channel\n useEffect(() => {\n channelRef.current = new MessageChannel();\n channelRef.current.port1.onmessage = (e) => {\n const data = e.data as MessageChannelEvent;\n\n if (data.type === \"state_update\") {\n setState(data.state);\n } else if (data.type === \"transcription_update\") {\n handleTranscriptionUpdate(data.transcriptions);\n }\n };\n\n return () => {\n channelRef.current?.port1.close();\n };\n }, []);\n\n // Process incoming transcriptions\n const handleTranscriptionUpdate = useCallback(\n (transcriptions: TranscriptionSegment[]) => {\n setTranscriptMap((prevMap) => {\n const newMap = new Map(prevMap);\n\n transcriptions.forEach((t) => {\n const messageType = t.speaker === \"agent\" ? \"agent\" : \"user\";\n // Use existing timestamp if we already have this segment\n const existingTimestamp = prevMap.get(t.id)?.timestamp || t.timestamp;\n\n newMap.set(t.id, {\n id: t.id,\n name: messageType,\n message: t.text,\n timestamp: existingTimestamp,\n isFinal: t.isFinal,\n });\n });\n\n return newMap;\n });\n },\n []\n );\n\n // Set up DOM portal for LiveKit\n useEffect(() => {\n const div = document.createElement(\"div\");\n div.style.display = \"none\";\n document.body.appendChild(div);\n portalRootRef.current = div;\n rootRef.current = createRoot(div);\n\n return () => {\n if (rootRef.current) {\n rootRef.current.unmount();\n }\n if (portalRootRef.current) {\n document.body.removeChild(portalRootRef.current);\n }\n };\n }, []);\n\n // Connect to VoxAI service - updated to include dynamicVariables\n const connect = useCallback(\n async ({ agentId, apiKey, dynamicVariables }: ConnectParams) => {\n try {\n setState(\"connecting\");\n\n const response = await fetch(HTTPS_API_ORIGIN, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n agent_id: agentId,\n metadata: {\n call_web: {\n dynamic_variables: dynamicVariables || {},\n },\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Connection failed (${response.status}): ${errorText}`\n );\n }\n\n const data = await response.json();\n setConnectionDetail(data);\n\n if (options.onConnect) {\n options.onConnect();\n }\n } catch (err) {\n // Reset state on error\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n const error = err instanceof Error ? err : new Error(String(err));\n\n if (options.onError) {\n options.onError(error);\n }\n }\n },\n [options]\n );\n\n // Disconnect from VoxAI service\n const disconnect = useCallback(() => {\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n if (options.onDisconnect) {\n options.onDisconnect();\n }\n }, [options]);\n\n // Render LiveKit components when connection details are available\n useEffect(() => {\n if (!rootRef.current) return;\n\n if (connectionDetail) {\n rootRef.current.render(\n <LiveKitRoom\n serverUrl={connectionDetail.serverUrl}\n token={connectionDetail.participantToken}\n audio={true}\n video={false}\n connect={true}\n onDisconnected={disconnect}\n // Add error handling for LiveKit connection\n onError={(error) => {\n console.error(\"LiveKit connection error:\", error);\n disconnect();\n if (options.onError) {\n options.onError(\n new Error(`LiveKit connection error: ${error.message}`)\n );\n }\n }}\n >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect, options.onError]);\n\n return {\n connect,\n disconnect,\n state,\n messages,\n };\n}\n\n/**\n * Component that monitors LiveKit state and communicates back to the main hook\n */\nfunction StateMonitor({ port }: { port: MessagePort | undefined }) {\n const { agent, state } = useVoiceAssistant();\n\n // Agent transcriptions\n const agentAudioTrack = useParticipantTracks(\n [Track.Source.Microphone],\n agent?.identity\n )[0];\n const agentTranscription = useTrackTranscription(agentAudioTrack);\n\n // User transcriptions\n const localParticipant = useLocalParticipant();\n const localMessages = useTrackTranscription({\n publication: localParticipant.microphoneTrack,\n source: Track.Source.Microphone,\n participant: localParticipant.localParticipant,\n });\n\n // Send agent state updates\n useEffect(() => {\n if (port) {\n port.postMessage({ type: \"state_update\", state });\n }\n }, [state, port]);\n\n // Send agent transcriptions\n useEffect(() => {\n if (port && agentTranscription.segments.length > 0) {\n const transcriptions = agentTranscription.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"agent\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [agentTranscription.segments, port]);\n\n // Send user transcriptions\n useEffect(() => {\n if (port && localMessages.segments.length > 0) {\n const transcriptions = localMessages.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"user\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [localMessages.segments, port]);\n\n return null;\n}\n"],"names":["useVoxAI","options","connectionDetail","setConnectionDetail","useState","state","setState","transcriptMap","setTranscriptMap","Map","messages","setMessages","prevMessagesRef","useRef","processedMessageIdsRef","Set","portalRootRef","rootRef","channelRef","useEffect","allMessages","Array","from","values","sort","a","b","timestamp","messagesString","JSON","stringify","current","onMessage","filter","msg","isFinal","id","has","forEach","add","MessageChannel","port1","onmessage","e","data","type","handleTranscriptionUpdate","transcriptions","_channelRef$current","close","useCallback","prevMap","newMap","t","_prevMap$get","messageType","speaker","existingTimestamp","get","set","name","message","text","div","document","createElement","style","display","body","appendChild","createRoot","unmount","removeChild","connect","_ref","agentId","apiKey","dynamicVariables","Promise","resolve","fetch","method","headers","Authorization","agent_id","metadata","call_web","dynamic_variables","then","response","_temp2","_result2","_exit","json","onConnect","_temp","ok","errorText","Error","status","_catch","err","error","String","onError","reject","disconnect","onDisconnect","_channelRef$current2","render","_jsxs","LiveKitRoom","serverUrl","token","participantToken","audio","video","onDisconnected","console","_jsx","RoomAudioRenderer","StateMonitor","port","port2","_Fragment","_ref2","agent","useVoiceAssistant","agentAudioTrack","useParticipantTracks","Track","Source","Microphone","identity","agentTranscription","useTrackTranscription","localParticipant","useLocalParticipant","localMessages","publication","microphoneTrack","source","participant","postMessage","segments","length","map","segment","final","Date","now"],"mappings":"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"}
1
+ {"version":3,"file":"lib.module.js","sources":["../src/hooks/useVoxAI.tsx"],"sourcesContent":["import {\n LiveKitRoom,\n RoomAudioRenderer,\n useLocalParticipant,\n useParticipantTracks,\n useTrackTranscription,\n useVoiceAssistant,\n} from \"@livekit/components-react\";\nimport { Track } from \"livekit-client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\n\ntype VoxConnectionDetail = {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n};\n\n/**\n * VoxAgentState\n * @description The state of the agent\n */\nexport type VoxAgentState =\n | \"disconnected\"\n | \"connecting\"\n | \"initializing\"\n | \"listening\"\n | \"thinking\"\n | \"speaking\";\n\n// API endpoint\nconst HTTPS_API_ORIGIN = \"https://www.tryvox.co/api/agent/sdk\";\n// const HTTPS_API_ORIGIN = \"http://localhost:3000/api/agent/sdk\";\n\n/**\n * VoxMessage\n * @description The message type between the agent and the user\n */\nexport type VoxMessage = {\n id?: string;\n name: \"agent\" | \"user\" | \"tool\";\n message?: string;\n timestamp: number;\n isFinal?: boolean;\n};\n\n/**\n * VoxAIOptions\n * @description The callback functions for the useVoxAI hook\n */\nexport interface VoxAIOptions {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n onMessage?: (message: VoxMessage) => void;\n}\n\n// Message channel event types\ntype MessageChannelEvent =\n | { type: \"state_update\"; state: VoxAgentState }\n | { type: \"transcription_update\"; transcriptions: TranscriptionSegment[] };\n\ntype TranscriptionSegment = {\n id: string;\n text: string;\n isFinal: boolean;\n timestamp: number;\n speaker: \"agent\" | \"user\";\n};\n\n/**\n * ConnectParams\n * @param agentId - The agent ID\n * @param apiKey - The API key\n * @param dynamicVariables - The dynamic variables\n * @param metadata - 이 메타데이터는 아웃바운드 웹훅, 통화 기록에 포함됩니다.\n */\nexport interface ConnectParams {\n agentId: string;\n apiKey: string;\n dynamicVariables?: Record<string, any>;\n metadata?: Record<string, any>;\n}\n\n/**\n * useVoxAI\n * @description The hook for integrating with vox.ai voice assistant\n * @param options - The options for the useVoxAI hook\n * @returns The useVoxAI hook\n * @example\n * const { connect, disconnect, state, messages } = useVoxAI({\n * onConnect: () => console.log(\"Connected\"),\n * onDisconnect: () => console.log(\"Disconnected\"),\n * onError: (error) => console.error(\"Error:\", error),\n * onMessage: (message) => console.log(\"Message:\", message),\n * });\n */\nexport function useVoxAI(options: VoxAIOptions = {}) {\n // Connection state\n const [connectionDetail, setConnectionDetail] =\n useState<VoxConnectionDetail | null>(null);\n const [state, setState] = useState<VoxAgentState>(\"disconnected\");\n\n // Message handling\n const [transcriptMap, setTranscriptMap] = useState<Map<string, VoxMessage>>(\n new Map()\n );\n const [messages, setMessages] = useState<VoxMessage[]>([]);\n const prevMessagesRef = useRef<string>(\"\");\n\n // Track which messages we've already sent to the onMessage callback\n const processedMessageIdsRef = useRef<Set<string>>(new Set());\n\n // DOM manipulation for LiveKit portal\n const portalRootRef = useRef<HTMLDivElement | null>(null);\n const rootRef = useRef<Root | null>(null);\n\n // Communication channel\n const channelRef = useRef<MessageChannel | null>(null);\n\n // Update messages whenever transcriptMap changes\n useEffect(() => {\n const allMessages = Array.from(transcriptMap.values()).sort(\n (a, b) => a.timestamp - b.timestamp\n );\n\n // Only update if the messages have actually changed\n const messagesString = JSON.stringify(allMessages);\n if (messagesString !== prevMessagesRef.current) {\n prevMessagesRef.current = messagesString;\n setMessages(allMessages);\n\n // Only trigger onMessage for new final messages that haven't been processed yet\n if (options.onMessage) {\n allMessages\n .filter(\n (msg) =>\n msg.isFinal &&\n msg.id &&\n !processedMessageIdsRef.current.has(msg.id)\n )\n .forEach((msg) => {\n if (msg.id) {\n // Mark this message as processed\n processedMessageIdsRef.current.add(msg.id);\n // Call the callback\n options.onMessage?.(msg);\n }\n });\n }\n }\n }, [transcriptMap, options.onMessage]);\n\n // Initialize message channel\n useEffect(() => {\n channelRef.current = new MessageChannel();\n channelRef.current.port1.onmessage = (e) => {\n const data = e.data as MessageChannelEvent;\n\n if (data.type === \"state_update\") {\n setState(data.state);\n } else if (data.type === \"transcription_update\") {\n handleTranscriptionUpdate(data.transcriptions);\n }\n };\n\n return () => {\n channelRef.current?.port1.close();\n };\n }, []);\n\n // Process incoming transcriptions\n const handleTranscriptionUpdate = useCallback(\n (transcriptions: TranscriptionSegment[]) => {\n setTranscriptMap((prevMap) => {\n const newMap = new Map(prevMap);\n\n transcriptions.forEach((t) => {\n const messageType = t.speaker === \"agent\" ? \"agent\" : \"user\";\n // Use existing timestamp if we already have this segment\n const existingTimestamp = prevMap.get(t.id)?.timestamp || t.timestamp;\n\n newMap.set(t.id, {\n id: t.id,\n name: messageType,\n message: t.text,\n timestamp: existingTimestamp,\n isFinal: t.isFinal,\n });\n });\n\n return newMap;\n });\n },\n []\n );\n\n // Set up DOM portal for LiveKit\n useEffect(() => {\n const div = document.createElement(\"div\");\n div.style.display = \"none\";\n document.body.appendChild(div);\n portalRootRef.current = div;\n rootRef.current = createRoot(div);\n\n return () => {\n if (rootRef.current) {\n rootRef.current.unmount();\n }\n if (portalRootRef.current) {\n document.body.removeChild(portalRootRef.current);\n }\n };\n }, []);\n\n // Connect to VoxAI service - updated to include dynamicVariables\n const connect = useCallback(\n async ({ agentId, apiKey, dynamicVariables, metadata }: ConnectParams) => {\n try {\n setState(\"connecting\");\n\n const response = await fetch(HTTPS_API_ORIGIN, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n agent_id: agentId,\n metadata: {\n call_web: {\n dynamic_variables: dynamicVariables || {},\n metadata: metadata || {},\n },\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Connection failed (${response.status}): ${errorText}`\n );\n }\n\n const data = await response.json();\n setConnectionDetail(data);\n\n if (options.onConnect) {\n options.onConnect();\n }\n } catch (err) {\n // Reset state on error\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n const error = err instanceof Error ? err : new Error(String(err));\n\n if (options.onError) {\n options.onError(error);\n }\n }\n },\n [options]\n );\n\n // Disconnect from VoxAI service\n const disconnect = useCallback(() => {\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n if (options.onDisconnect) {\n options.onDisconnect();\n }\n }, [options]);\n\n // Render LiveKit components when connection details are available\n useEffect(() => {\n if (!rootRef.current) return;\n\n if (connectionDetail) {\n rootRef.current.render(\n <LiveKitRoom\n serverUrl={connectionDetail.serverUrl}\n token={connectionDetail.participantToken}\n audio={true}\n video={false}\n connect={true}\n onDisconnected={disconnect}\n // Add error handling for LiveKit connection\n onError={(error) => {\n console.error(\"LiveKit connection error:\", error);\n disconnect();\n if (options.onError) {\n options.onError(\n new Error(`LiveKit connection error: ${error.message}`)\n );\n }\n }}\n >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect, options.onError]);\n\n return {\n connect,\n disconnect,\n state,\n messages,\n };\n}\n\n/**\n * Component that monitors LiveKit state and communicates back to the main hook\n */\nfunction StateMonitor({ port }: { port: MessagePort | undefined }) {\n const { agent, state } = useVoiceAssistant();\n\n // Agent transcriptions\n const agentAudioTrack = useParticipantTracks(\n [Track.Source.Microphone],\n agent?.identity\n )[0];\n const agentTranscription = useTrackTranscription(agentAudioTrack);\n\n // User transcriptions\n const localParticipant = useLocalParticipant();\n const localMessages = useTrackTranscription({\n publication: localParticipant.microphoneTrack,\n source: Track.Source.Microphone,\n participant: localParticipant.localParticipant,\n });\n\n // Send agent state updates\n useEffect(() => {\n if (port) {\n port.postMessage({ type: \"state_update\", state });\n }\n }, [state, port]);\n\n // Send agent transcriptions\n useEffect(() => {\n if (port && agentTranscription.segments.length > 0) {\n const transcriptions = agentTranscription.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"agent\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [agentTranscription.segments, port]);\n\n // Send user transcriptions\n useEffect(() => {\n if (port && localMessages.segments.length > 0) {\n const transcriptions = localMessages.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"user\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [localMessages.segments, port]);\n\n return null;\n}\n"],"names":["useVoxAI","options","connectionDetail","setConnectionDetail","useState","state","setState","transcriptMap","setTranscriptMap","Map","messages","setMessages","prevMessagesRef","useRef","processedMessageIdsRef","Set","portalRootRef","rootRef","channelRef","useEffect","allMessages","Array","from","values","sort","a","b","timestamp","messagesString","JSON","stringify","current","onMessage","filter","msg","isFinal","id","has","forEach","add","MessageChannel","port1","onmessage","e","data","type","handleTranscriptionUpdate","transcriptions","_channelRef$current","close","useCallback","prevMap","newMap","t","_prevMap$get","messageType","speaker","existingTimestamp","get","set","name","message","text","div","document","createElement","style","display","body","appendChild","createRoot","unmount","removeChild","connect","_ref","agentId","apiKey","dynamicVariables","metadata","Promise","resolve","fetch","method","headers","Authorization","agent_id","call_web","dynamic_variables","then","response","_temp2","_result2","json","onConnect","_temp","ok","errorText","Error","status","_catch","err","error","String","onError","reject","disconnect","onDisconnect","_channelRef$current2","render","_jsxs","LiveKitRoom","serverUrl","token","participantToken","audio","video","onDisconnected","console","_jsx","RoomAudioRenderer","StateMonitor","port","port2","_Fragment","_ref2","agent","useVoiceAssistant","agentAudioTrack","useParticipantTracks","Track","Source","Microphone","identity","agentTranscription","useTrackTranscription","localParticipant","useLocalParticipant","localMessages","publication","microphoneTrack","source","participant","postMessage","segments","length","map","segment","final","Date","now"],"mappings":"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"}
package/dist/lib.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react/jsx-runtime"),require("@livekit/components-react"),require("livekit-client"),require("react"),require("react-dom/client")):"function"==typeof define&&define.amd?define(["exports","react/jsx-runtime","@livekit/components-react","livekit-client","react","react-dom/client"],t):t((e||self).react={},e.jsxRuntime,e.componentsReact,e.livekitClient,e.react,e.client)}(this,function(e,t,n,r,o,s){function i(e){let{port:t}=e;const{agent:s,state:i}=n.useVoiceAssistant(),c=n.useParticipantTracks([r.Track.Source.Microphone],null==s?void 0:s.identity)[0],a=n.useTrackTranscription(c),u=n.useLocalParticipant(),l=n.useTrackTranscription({publication:u.microphoneTrack,source:r.Track.Source.Microphone,participant:u.localParticipant});return o.useEffect(()=>{t&&t.postMessage({type:"state_update",state:i})},[i,t]),o.useEffect(()=>{if(t&&a.segments.length>0){const e=a.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"agent"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[a.segments,t]),o.useEffect(()=>{if(t&&l.segments.length>0){const e=l.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"user"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[l.segments,t]),null}e.useVoxAI=function(e){void 0===e&&(e={});const[r,c]=o.useState(null),[a,u]=o.useState("disconnected"),[l,p]=o.useState(new Map),[d,m]=o.useState([]),f=o.useRef(""),g=o.useRef(new Set),h=o.useRef(null),v=o.useRef(null),y=o.useRef(null);o.useEffect(()=>{const t=Array.from(l.values()).sort((e,t)=>e.timestamp-t.timestamp),n=JSON.stringify(t);n!==f.current&&(f.current=n,m(t),e.onMessage&&t.filter(e=>e.isFinal&&e.id&&!g.current.has(e.id)).forEach(t=>{t.id&&(g.current.add(t.id),null==e.onMessage||e.onMessage(t))}))},[l,e.onMessage]),o.useEffect(()=>(y.current=new MessageChannel,y.current.port1.onmessage=e=>{const t=e.data;"state_update"===t.type?u(t.state):"transcription_update"===t.type&&k(t.transcriptions)},()=>{var e;null==(e=y.current)||e.port1.close()}),[]);const k=o.useCallback(e=>{p(t=>{const n=new Map(t);return e.forEach(e=>{var r;const o="agent"===e.speaker?"agent":"user",s=(null==(r=t.get(e.id))?void 0:r.timestamp)||e.timestamp;n.set(e.id,{id:e.id,name:o,message:e.text,timestamp:s,isFinal:e.isFinal})}),n})},[]);o.useEffect(()=>{const e=document.createElement("div");return e.style.display="none",document.body.appendChild(e),h.current=e,v.current=s.createRoot(e),()=>{v.current&&v.current.unmount(),h.current&&document.body.removeChild(h.current)}},[]);const E=o.useCallback(function(t){let{agentId:n,apiKey:r,dynamicVariables:o}=t;try{return Promise.resolve(function(t,s){try{var i=(u("connecting"),Promise.resolve(fetch("https://www.tryvox.co/api/agent/sdk",{method:"POST",headers:{Authorization:"Bearer "+r,"Content-Type":"application/json"},body:JSON.stringify({agent_id:n,metadata:{call_web:{dynamic_variables:o||{}}}})})).then(function(t){function n(n){return Promise.resolve(t.json()).then(function(t){c(t),e.onConnect&&e.onConnect()})}const r=function(){if(!t.ok)return Promise.resolve(t.text()).then(function(e){throw new Error("Connection failed ("+t.status+"): "+e)})}();return r&&r.then?r.then(n):n()}))}catch(e){return s(e)}return i&&i.then?i.then(void 0,s):i}(0,function(t){c(null),p(new Map),m([]),u("disconnected");const n=t instanceof Error?t:new Error(String(t));e.onError&&e.onError(n)}))}catch(e){return Promise.reject(e)}},[e]),x=o.useCallback(()=>{c(null),p(new Map),m([]),u("disconnected"),e.onDisconnect&&e.onDisconnect()},[e]);return o.useEffect(()=>{var o;v.current&&v.current.render(r?t.jsxs(n.LiveKitRoom,{serverUrl:r.serverUrl,token:r.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:x,onError:t=>{console.error("LiveKit connection error:",t),x(),e.onError&&e.onError(new Error("LiveKit connection error: "+t.message))},children:[t.jsx(n.RoomAudioRenderer,{}),t.jsx(i,{port:null==(o=y.current)?void 0:o.port2})]}):t.jsx(t.Fragment,{}))},[r,x,e.onError]),{connect:E,disconnect:x,state:a,messages:d}}});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react/jsx-runtime"),require("@livekit/components-react"),require("livekit-client"),require("react"),require("react-dom/client")):"function"==typeof define&&define.amd?define(["exports","react/jsx-runtime","@livekit/components-react","livekit-client","react","react-dom/client"],t):t((e||self).react={},e.jsxRuntime,e.componentsReact,e.livekitClient,e.react,e.client)}(this,function(e,t,n,r,o,s){function i(e){let{port:t}=e;const{agent:s,state:i}=n.useVoiceAssistant(),a=n.useParticipantTracks([r.Track.Source.Microphone],null==s?void 0:s.identity)[0],c=n.useTrackTranscription(a),u=n.useLocalParticipant(),l=n.useTrackTranscription({publication:u.microphoneTrack,source:r.Track.Source.Microphone,participant:u.localParticipant});return o.useEffect(()=>{t&&t.postMessage({type:"state_update",state:i})},[i,t]),o.useEffect(()=>{if(t&&c.segments.length>0){const e=c.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"agent"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[c.segments,t]),o.useEffect(()=>{if(t&&l.segments.length>0){const e=l.segments.map(e=>({id:e.id,text:e.text,isFinal:e.final,timestamp:Date.now(),speaker:"user"}));t.postMessage({type:"transcription_update",transcriptions:e})}},[l.segments,t]),null}e.useVoxAI=function(e){void 0===e&&(e={});const[r,a]=o.useState(null),[c,u]=o.useState("disconnected"),[l,d]=o.useState(new Map),[p,m]=o.useState([]),f=o.useRef(""),g=o.useRef(new Set),h=o.useRef(null),v=o.useRef(null),y=o.useRef(null);o.useEffect(()=>{const t=Array.from(l.values()).sort((e,t)=>e.timestamp-t.timestamp),n=JSON.stringify(t);n!==f.current&&(f.current=n,m(t),e.onMessage&&t.filter(e=>e.isFinal&&e.id&&!g.current.has(e.id)).forEach(t=>{t.id&&(g.current.add(t.id),null==e.onMessage||e.onMessage(t))}))},[l,e.onMessage]),o.useEffect(()=>(y.current=new MessageChannel,y.current.port1.onmessage=e=>{const t=e.data;"state_update"===t.type?u(t.state):"transcription_update"===t.type&&k(t.transcriptions)},()=>{var e;null==(e=y.current)||e.port1.close()}),[]);const k=o.useCallback(e=>{d(t=>{const n=new Map(t);return e.forEach(e=>{var r;const o="agent"===e.speaker?"agent":"user",s=(null==(r=t.get(e.id))?void 0:r.timestamp)||e.timestamp;n.set(e.id,{id:e.id,name:o,message:e.text,timestamp:s,isFinal:e.isFinal})}),n})},[]);o.useEffect(()=>{const e=document.createElement("div");return e.style.display="none",document.body.appendChild(e),h.current=e,v.current=s.createRoot(e),()=>{v.current&&v.current.unmount(),h.current&&document.body.removeChild(h.current)}},[]);const E=o.useCallback(function(t){let{agentId:n,apiKey:r,dynamicVariables:o,metadata:s}=t;try{return Promise.resolve(function(t,i){try{var c=(u("connecting"),Promise.resolve(fetch("https://www.tryvox.co/api/agent/sdk",{method:"POST",headers:{Authorization:"Bearer "+r,"Content-Type":"application/json"},body:JSON.stringify({agent_id:n,metadata:{call_web:{dynamic_variables:o||{},metadata:s||{}}}})})).then(function(t){function n(n){return Promise.resolve(t.json()).then(function(t){a(t),e.onConnect&&e.onConnect()})}const r=function(){if(!t.ok)return Promise.resolve(t.text()).then(function(e){throw new Error("Connection failed ("+t.status+"): "+e)})}();return r&&r.then?r.then(n):n()}))}catch(e){return i(e)}return c&&c.then?c.then(void 0,i):c}(0,function(t){a(null),d(new Map),m([]),u("disconnected");const n=t instanceof Error?t:new Error(String(t));e.onError&&e.onError(n)}))}catch(e){return Promise.reject(e)}},[e]),x=o.useCallback(()=>{a(null),d(new Map),m([]),u("disconnected"),e.onDisconnect&&e.onDisconnect()},[e]);return o.useEffect(()=>{var o;v.current&&v.current.render(r?t.jsxs(n.LiveKitRoom,{serverUrl:r.serverUrl,token:r.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:x,onError:t=>{console.error("LiveKit connection error:",t),x(),e.onError&&e.onError(new Error("LiveKit connection error: "+t.message))},children:[t.jsx(n.RoomAudioRenderer,{}),t.jsx(i,{port:null==(o=y.current)?void 0:o.port2})]}):t.jsx(t.Fragment,{}))},[r,x,e.onError]),{connect:E,disconnect:x,state:c,messages:p}}});
2
2
  //# sourceMappingURL=lib.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lib.umd.js","sources":["../src/hooks/useVoxAI.tsx"],"sourcesContent":["import {\n LiveKitRoom,\n RoomAudioRenderer,\n useLocalParticipant,\n useParticipantTracks,\n useTrackTranscription,\n useVoiceAssistant,\n} from \"@livekit/components-react\";\nimport { Track } from \"livekit-client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\n\n// Connection types\ntype VoxConnectionDetail = {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n};\n\ntype VoxAgentState =\n | \"disconnected\"\n | \"connecting\"\n | \"initializing\"\n | \"listening\"\n | \"thinking\"\n | \"speaking\";\n\n// API endpoint\nconst HTTPS_API_ORIGIN = \"https://www.tryvox.co/api/agent/sdk\";\n// const HTTPS_API_ORIGIN = \"http://localhost:3000/api/agent/sdk\";\n\nexport type VoxMessage = {\n id?: string;\n name: \"agent\" | \"user\" | \"tool\";\n message?: string;\n timestamp: number;\n isFinal?: boolean;\n};\n\n// Hook configuration\ninterface VoxAIOptions {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n onMessage?: (message: VoxMessage) => void;\n}\n\n// Message channel event types\ntype MessageChannelEvent =\n | { type: \"state_update\"; state: VoxAgentState }\n | { type: \"transcription_update\"; transcriptions: TranscriptionSegment[] };\n\ntype TranscriptionSegment = {\n id: string;\n text: string;\n isFinal: boolean;\n timestamp: number;\n speaker: \"agent\" | \"user\";\n};\n\n// Update the connection parameter type to include dynamicVariables\ninterface ConnectParams {\n agentId: string;\n apiKey: string;\n dynamicVariables?: Record<string, any>; // Allow any dynamicVariables fields\n}\n\n/**\n * Hook for integrating with VoxAI voice assistant\n */\nexport function useVoxAI(options: VoxAIOptions = {}) {\n // Connection state\n const [connectionDetail, setConnectionDetail] =\n useState<VoxConnectionDetail | null>(null);\n const [state, setState] = useState<VoxAgentState>(\"disconnected\");\n\n // Message handling\n const [transcriptMap, setTranscriptMap] = useState<Map<string, VoxMessage>>(\n new Map()\n );\n const [messages, setMessages] = useState<VoxMessage[]>([]);\n const prevMessagesRef = useRef<string>(\"\");\n\n // Track which messages we've already sent to the onMessage callback\n const processedMessageIdsRef = useRef<Set<string>>(new Set());\n\n // DOM manipulation for LiveKit portal\n const portalRootRef = useRef<HTMLDivElement | null>(null);\n const rootRef = useRef<Root | null>(null);\n\n // Communication channel\n const channelRef = useRef<MessageChannel | null>(null);\n\n // Update messages whenever transcriptMap changes\n useEffect(() => {\n const allMessages = Array.from(transcriptMap.values()).sort(\n (a, b) => a.timestamp - b.timestamp\n );\n\n // Only update if the messages have actually changed\n const messagesString = JSON.stringify(allMessages);\n if (messagesString !== prevMessagesRef.current) {\n prevMessagesRef.current = messagesString;\n setMessages(allMessages);\n\n // Only trigger onMessage for new final messages that haven't been processed yet\n if (options.onMessage) {\n allMessages\n .filter(\n (msg) =>\n msg.isFinal &&\n msg.id &&\n !processedMessageIdsRef.current.has(msg.id)\n )\n .forEach((msg) => {\n if (msg.id) {\n // Mark this message as processed\n processedMessageIdsRef.current.add(msg.id);\n // Call the callback\n options.onMessage?.(msg);\n }\n });\n }\n }\n }, [transcriptMap, options.onMessage]);\n\n // Initialize message channel\n useEffect(() => {\n channelRef.current = new MessageChannel();\n channelRef.current.port1.onmessage = (e) => {\n const data = e.data as MessageChannelEvent;\n\n if (data.type === \"state_update\") {\n setState(data.state);\n } else if (data.type === \"transcription_update\") {\n handleTranscriptionUpdate(data.transcriptions);\n }\n };\n\n return () => {\n channelRef.current?.port1.close();\n };\n }, []);\n\n // Process incoming transcriptions\n const handleTranscriptionUpdate = useCallback(\n (transcriptions: TranscriptionSegment[]) => {\n setTranscriptMap((prevMap) => {\n const newMap = new Map(prevMap);\n\n transcriptions.forEach((t) => {\n const messageType = t.speaker === \"agent\" ? \"agent\" : \"user\";\n // Use existing timestamp if we already have this segment\n const existingTimestamp = prevMap.get(t.id)?.timestamp || t.timestamp;\n\n newMap.set(t.id, {\n id: t.id,\n name: messageType,\n message: t.text,\n timestamp: existingTimestamp,\n isFinal: t.isFinal,\n });\n });\n\n return newMap;\n });\n },\n []\n );\n\n // Set up DOM portal for LiveKit\n useEffect(() => {\n const div = document.createElement(\"div\");\n div.style.display = \"none\";\n document.body.appendChild(div);\n portalRootRef.current = div;\n rootRef.current = createRoot(div);\n\n return () => {\n if (rootRef.current) {\n rootRef.current.unmount();\n }\n if (portalRootRef.current) {\n document.body.removeChild(portalRootRef.current);\n }\n };\n }, []);\n\n // Connect to VoxAI service - updated to include dynamicVariables\n const connect = useCallback(\n async ({ agentId, apiKey, dynamicVariables }: ConnectParams) => {\n try {\n setState(\"connecting\");\n\n const response = await fetch(HTTPS_API_ORIGIN, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n agent_id: agentId,\n metadata: {\n call_web: {\n dynamic_variables: dynamicVariables || {},\n },\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Connection failed (${response.status}): ${errorText}`\n );\n }\n\n const data = await response.json();\n setConnectionDetail(data);\n\n if (options.onConnect) {\n options.onConnect();\n }\n } catch (err) {\n // Reset state on error\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n const error = err instanceof Error ? err : new Error(String(err));\n\n if (options.onError) {\n options.onError(error);\n }\n }\n },\n [options]\n );\n\n // Disconnect from VoxAI service\n const disconnect = useCallback(() => {\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n if (options.onDisconnect) {\n options.onDisconnect();\n }\n }, [options]);\n\n // Render LiveKit components when connection details are available\n useEffect(() => {\n if (!rootRef.current) return;\n\n if (connectionDetail) {\n rootRef.current.render(\n <LiveKitRoom\n serverUrl={connectionDetail.serverUrl}\n token={connectionDetail.participantToken}\n audio={true}\n video={false}\n connect={true}\n onDisconnected={disconnect}\n // Add error handling for LiveKit connection\n onError={(error) => {\n console.error(\"LiveKit connection error:\", error);\n disconnect();\n if (options.onError) {\n options.onError(\n new Error(`LiveKit connection error: ${error.message}`)\n );\n }\n }}\n >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect, options.onError]);\n\n return {\n connect,\n disconnect,\n state,\n messages,\n };\n}\n\n/**\n * Component that monitors LiveKit state and communicates back to the main hook\n */\nfunction StateMonitor({ port }: { port: MessagePort | undefined }) {\n const { agent, state } = useVoiceAssistant();\n\n // Agent transcriptions\n const agentAudioTrack = useParticipantTracks(\n [Track.Source.Microphone],\n agent?.identity\n )[0];\n const agentTranscription = useTrackTranscription(agentAudioTrack);\n\n // User transcriptions\n const localParticipant = useLocalParticipant();\n const localMessages = useTrackTranscription({\n publication: localParticipant.microphoneTrack,\n source: Track.Source.Microphone,\n participant: localParticipant.localParticipant,\n });\n\n // Send agent state updates\n useEffect(() => {\n if (port) {\n port.postMessage({ type: \"state_update\", state });\n }\n }, [state, port]);\n\n // Send agent transcriptions\n useEffect(() => {\n if (port && agentTranscription.segments.length > 0) {\n const transcriptions = agentTranscription.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"agent\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [agentTranscription.segments, port]);\n\n // Send user transcriptions\n useEffect(() => {\n if (port && localMessages.segments.length > 0) {\n const transcriptions = localMessages.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"user\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [localMessages.segments, port]);\n\n return null;\n}\n"],"names":["StateMonitor","_ref2","port","agent","state","useVoiceAssistant","agentAudioTrack","useParticipantTracks","Track","Source","Microphone","identity","agentTranscription","useTrackTranscription","localParticipant","useLocalParticipant","localMessages","publication","microphoneTrack","source","participant","useEffect","postMessage","type","segments","length","transcriptions","map","segment","id","text","isFinal","final","timestamp","Date","now","speaker","options","connectionDetail","setConnectionDetail","useState","setState","transcriptMap","setTranscriptMap","Map","messages","setMessages","prevMessagesRef","useRef","processedMessageIdsRef","Set","portalRootRef","rootRef","channelRef","allMessages","Array","from","values","sort","a","b","messagesString","JSON","stringify","current","onMessage","filter","msg","has","forEach","add","MessageChannel","port1","onmessage","e","data","handleTranscriptionUpdate","_channelRef$current","close","useCallback","prevMap","newMap","t","_prevMap$get","messageType","existingTimestamp","get","set","name","message","div","document","createElement","style","display","body","appendChild","createRoot","unmount","removeChild","connect","_ref","agentId","apiKey","dynamicVariables","Promise","resolve","fetch","method","headers","Authorization","agent_id","metadata","call_web","dynamic_variables","then","response","_temp2","_result2","_exit","json","onConnect","_temp","ok","errorText","Error","status","_catch","err","error","String","onError","reject","disconnect","onDisconnect","_channelRef$current2","render","_jsxs","jsxs","LiveKitRoom","serverUrl","token","participantToken","audio","video","onDisconnected","console","_jsx","RoomAudioRenderer","jsx","port2","_Fragment","Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"lib.umd.js","sources":["../src/hooks/useVoxAI.tsx"],"sourcesContent":["import {\n LiveKitRoom,\n RoomAudioRenderer,\n useLocalParticipant,\n useParticipantTracks,\n useTrackTranscription,\n useVoiceAssistant,\n} from \"@livekit/components-react\";\nimport { Track } from \"livekit-client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\n\ntype VoxConnectionDetail = {\n serverUrl: string;\n roomName: string;\n participantName: string;\n participantToken: string;\n};\n\n/**\n * VoxAgentState\n * @description The state of the agent\n */\nexport type VoxAgentState =\n | \"disconnected\"\n | \"connecting\"\n | \"initializing\"\n | \"listening\"\n | \"thinking\"\n | \"speaking\";\n\n// API endpoint\nconst HTTPS_API_ORIGIN = \"https://www.tryvox.co/api/agent/sdk\";\n// const HTTPS_API_ORIGIN = \"http://localhost:3000/api/agent/sdk\";\n\n/**\n * VoxMessage\n * @description The message type between the agent and the user\n */\nexport type VoxMessage = {\n id?: string;\n name: \"agent\" | \"user\" | \"tool\";\n message?: string;\n timestamp: number;\n isFinal?: boolean;\n};\n\n/**\n * VoxAIOptions\n * @description The callback functions for the useVoxAI hook\n */\nexport interface VoxAIOptions {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n onMessage?: (message: VoxMessage) => void;\n}\n\n// Message channel event types\ntype MessageChannelEvent =\n | { type: \"state_update\"; state: VoxAgentState }\n | { type: \"transcription_update\"; transcriptions: TranscriptionSegment[] };\n\ntype TranscriptionSegment = {\n id: string;\n text: string;\n isFinal: boolean;\n timestamp: number;\n speaker: \"agent\" | \"user\";\n};\n\n/**\n * ConnectParams\n * @param agentId - The agent ID\n * @param apiKey - The API key\n * @param dynamicVariables - The dynamic variables\n * @param metadata - 이 메타데이터는 아웃바운드 웹훅, 통화 기록에 포함됩니다.\n */\nexport interface ConnectParams {\n agentId: string;\n apiKey: string;\n dynamicVariables?: Record<string, any>;\n metadata?: Record<string, any>;\n}\n\n/**\n * useVoxAI\n * @description The hook for integrating with vox.ai voice assistant\n * @param options - The options for the useVoxAI hook\n * @returns The useVoxAI hook\n * @example\n * const { connect, disconnect, state, messages } = useVoxAI({\n * onConnect: () => console.log(\"Connected\"),\n * onDisconnect: () => console.log(\"Disconnected\"),\n * onError: (error) => console.error(\"Error:\", error),\n * onMessage: (message) => console.log(\"Message:\", message),\n * });\n */\nexport function useVoxAI(options: VoxAIOptions = {}) {\n // Connection state\n const [connectionDetail, setConnectionDetail] =\n useState<VoxConnectionDetail | null>(null);\n const [state, setState] = useState<VoxAgentState>(\"disconnected\");\n\n // Message handling\n const [transcriptMap, setTranscriptMap] = useState<Map<string, VoxMessage>>(\n new Map()\n );\n const [messages, setMessages] = useState<VoxMessage[]>([]);\n const prevMessagesRef = useRef<string>(\"\");\n\n // Track which messages we've already sent to the onMessage callback\n const processedMessageIdsRef = useRef<Set<string>>(new Set());\n\n // DOM manipulation for LiveKit portal\n const portalRootRef = useRef<HTMLDivElement | null>(null);\n const rootRef = useRef<Root | null>(null);\n\n // Communication channel\n const channelRef = useRef<MessageChannel | null>(null);\n\n // Update messages whenever transcriptMap changes\n useEffect(() => {\n const allMessages = Array.from(transcriptMap.values()).sort(\n (a, b) => a.timestamp - b.timestamp\n );\n\n // Only update if the messages have actually changed\n const messagesString = JSON.stringify(allMessages);\n if (messagesString !== prevMessagesRef.current) {\n prevMessagesRef.current = messagesString;\n setMessages(allMessages);\n\n // Only trigger onMessage for new final messages that haven't been processed yet\n if (options.onMessage) {\n allMessages\n .filter(\n (msg) =>\n msg.isFinal &&\n msg.id &&\n !processedMessageIdsRef.current.has(msg.id)\n )\n .forEach((msg) => {\n if (msg.id) {\n // Mark this message as processed\n processedMessageIdsRef.current.add(msg.id);\n // Call the callback\n options.onMessage?.(msg);\n }\n });\n }\n }\n }, [transcriptMap, options.onMessage]);\n\n // Initialize message channel\n useEffect(() => {\n channelRef.current = new MessageChannel();\n channelRef.current.port1.onmessage = (e) => {\n const data = e.data as MessageChannelEvent;\n\n if (data.type === \"state_update\") {\n setState(data.state);\n } else if (data.type === \"transcription_update\") {\n handleTranscriptionUpdate(data.transcriptions);\n }\n };\n\n return () => {\n channelRef.current?.port1.close();\n };\n }, []);\n\n // Process incoming transcriptions\n const handleTranscriptionUpdate = useCallback(\n (transcriptions: TranscriptionSegment[]) => {\n setTranscriptMap((prevMap) => {\n const newMap = new Map(prevMap);\n\n transcriptions.forEach((t) => {\n const messageType = t.speaker === \"agent\" ? \"agent\" : \"user\";\n // Use existing timestamp if we already have this segment\n const existingTimestamp = prevMap.get(t.id)?.timestamp || t.timestamp;\n\n newMap.set(t.id, {\n id: t.id,\n name: messageType,\n message: t.text,\n timestamp: existingTimestamp,\n isFinal: t.isFinal,\n });\n });\n\n return newMap;\n });\n },\n []\n );\n\n // Set up DOM portal for LiveKit\n useEffect(() => {\n const div = document.createElement(\"div\");\n div.style.display = \"none\";\n document.body.appendChild(div);\n portalRootRef.current = div;\n rootRef.current = createRoot(div);\n\n return () => {\n if (rootRef.current) {\n rootRef.current.unmount();\n }\n if (portalRootRef.current) {\n document.body.removeChild(portalRootRef.current);\n }\n };\n }, []);\n\n // Connect to VoxAI service - updated to include dynamicVariables\n const connect = useCallback(\n async ({ agentId, apiKey, dynamicVariables, metadata }: ConnectParams) => {\n try {\n setState(\"connecting\");\n\n const response = await fetch(HTTPS_API_ORIGIN, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n agent_id: agentId,\n metadata: {\n call_web: {\n dynamic_variables: dynamicVariables || {},\n metadata: metadata || {},\n },\n },\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Connection failed (${response.status}): ${errorText}`\n );\n }\n\n const data = await response.json();\n setConnectionDetail(data);\n\n if (options.onConnect) {\n options.onConnect();\n }\n } catch (err) {\n // Reset state on error\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n const error = err instanceof Error ? err : new Error(String(err));\n\n if (options.onError) {\n options.onError(error);\n }\n }\n },\n [options]\n );\n\n // Disconnect from VoxAI service\n const disconnect = useCallback(() => {\n setConnectionDetail(null);\n setTranscriptMap(new Map());\n setMessages([]);\n setState(\"disconnected\");\n\n if (options.onDisconnect) {\n options.onDisconnect();\n }\n }, [options]);\n\n // Render LiveKit components when connection details are available\n useEffect(() => {\n if (!rootRef.current) return;\n\n if (connectionDetail) {\n rootRef.current.render(\n <LiveKitRoom\n serverUrl={connectionDetail.serverUrl}\n token={connectionDetail.participantToken}\n audio={true}\n video={false}\n connect={true}\n onDisconnected={disconnect}\n // Add error handling for LiveKit connection\n onError={(error) => {\n console.error(\"LiveKit connection error:\", error);\n disconnect();\n if (options.onError) {\n options.onError(\n new Error(`LiveKit connection error: ${error.message}`)\n );\n }\n }}\n >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect, options.onError]);\n\n return {\n connect,\n disconnect,\n state,\n messages,\n };\n}\n\n/**\n * Component that monitors LiveKit state and communicates back to the main hook\n */\nfunction StateMonitor({ port }: { port: MessagePort | undefined }) {\n const { agent, state } = useVoiceAssistant();\n\n // Agent transcriptions\n const agentAudioTrack = useParticipantTracks(\n [Track.Source.Microphone],\n agent?.identity\n )[0];\n const agentTranscription = useTrackTranscription(agentAudioTrack);\n\n // User transcriptions\n const localParticipant = useLocalParticipant();\n const localMessages = useTrackTranscription({\n publication: localParticipant.microphoneTrack,\n source: Track.Source.Microphone,\n participant: localParticipant.localParticipant,\n });\n\n // Send agent state updates\n useEffect(() => {\n if (port) {\n port.postMessage({ type: \"state_update\", state });\n }\n }, [state, port]);\n\n // Send agent transcriptions\n useEffect(() => {\n if (port && agentTranscription.segments.length > 0) {\n const transcriptions = agentTranscription.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"agent\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [agentTranscription.segments, port]);\n\n // Send user transcriptions\n useEffect(() => {\n if (port && localMessages.segments.length > 0) {\n const transcriptions = localMessages.segments.map((segment) => ({\n id: segment.id,\n text: segment.text,\n isFinal: segment.final,\n timestamp: Date.now(),\n speaker: \"user\" as const,\n }));\n\n port.postMessage({\n type: \"transcription_update\",\n transcriptions,\n });\n }\n }, [localMessages.segments, port]);\n\n return null;\n}\n"],"names":["StateMonitor","_ref2","port","agent","state","useVoiceAssistant","agentAudioTrack","useParticipantTracks","Track","Source","Microphone","identity","agentTranscription","useTrackTranscription","localParticipant","useLocalParticipant","localMessages","publication","microphoneTrack","source","participant","useEffect","postMessage","type","segments","length","transcriptions","map","segment","id","text","isFinal","final","timestamp","Date","now","speaker","options","connectionDetail","setConnectionDetail","useState","setState","transcriptMap","setTranscriptMap","Map","messages","setMessages","prevMessagesRef","useRef","processedMessageIdsRef","Set","portalRootRef","rootRef","channelRef","allMessages","Array","from","values","sort","a","b","messagesString","JSON","stringify","current","onMessage","filter","msg","has","forEach","add","MessageChannel","port1","onmessage","e","data","handleTranscriptionUpdate","_channelRef$current","close","useCallback","prevMap","newMap","t","_prevMap$get","messageType","existingTimestamp","get","set","name","message","div","document","createElement","style","display","body","appendChild","createRoot","unmount","removeChild","connect","_ref","agentId","apiKey","dynamicVariables","metadata","Promise","resolve","fetch","method","headers","Authorization","agent_id","call_web","dynamic_variables","then","response","_temp2","_result2","json","onConnect","_temp","ok","errorText","Error","status","_catch","err","error","String","onError","reject","disconnect","onDisconnect","_channelRef$current2","render","_jsxs","jsxs","LiveKitRoom","serverUrl","token","participantToken","audio","video","onDisconnected","console","_jsx","RoomAudioRenderer","port2","jsx","_Fragment","Fragment"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vox-ai/react",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "vox.ai React Library",
5
5
  "main": "./dist/lib.umd.js",
6
6
  "module": "./dist/lib.module.js",
@@ -1,12 +0,0 @@
1
- import React from "react";
2
- interface VoxAIConversationProps {
3
- children: React.ReactNode;
4
- agentId?: string;
5
- apiKey?: string;
6
- autoConnect?: boolean;
7
- onConnect?: () => void;
8
- onDisconnect?: () => void;
9
- onError?: (error: Error) => void;
10
- }
11
- export declare function VoxAIConversation({ children, agentId, apiKey, autoConnect, onConnect, onDisconnect, onError, }: VoxAIConversationProps): import("react/jsx-runtime").JSX.Element;
12
- export {};
@@ -1,6 +0,0 @@
1
- import React from "react";
2
- interface VoxAIRoomProps {
3
- children?: React.ReactNode;
4
- }
5
- export declare function VoxAIRoom({ children }: VoxAIRoomProps): import("react/jsx-runtime").JSX.Element;
6
- export {};
@@ -1 +0,0 @@
1
- export * from "./VoxAIConversation";
@@ -1,16 +0,0 @@
1
- type VoxConnectionDetail = {
2
- serverUrl: string;
3
- roomName: string;
4
- participantName: string;
5
- participantToken: string;
6
- };
7
- type VoxConnection = {
8
- connectionDetail: VoxConnectionDetail | null;
9
- connect: () => void;
10
- disconnect: () => void;
11
- };
12
- export declare const useAgentConnection: ({ agentId, apiKey, }: {
13
- agentId: string;
14
- apiKey: string;
15
- }) => VoxConnection;
16
- export {};
@@ -1,17 +0,0 @@
1
- import React from "react";
2
- type VoxConnectionDetail = {
3
- serverUrl: string;
4
- roomName: string;
5
- participantName: string;
6
- participantToken: string;
7
- };
8
- type VoxAIContextType = {
9
- connectionDetail: VoxConnectionDetail | null;
10
- connect: (agentId: string, apiKey: string) => Promise<void>;
11
- disconnect: () => void;
12
- };
13
- export declare const VoxAIProvider: React.FC<{
14
- children: React.ReactNode;
15
- }>;
16
- export declare const useVoxAIContext: () => VoxAIContextType;
17
- export {};
@@ -1 +0,0 @@
1
- export * from "./VoxAIContext";
@@ -1,11 +0,0 @@
1
- export declare const useAgentConnection: () => {
2
- connectionDetail: {
3
- serverUrl: string;
4
- roomName: string;
5
- participantName: string;
6
- participantToken: string;
7
- } | null;
8
- connect: (agentId: string, apiKey: string) => Promise<void>;
9
- disconnect: () => void;
10
- isConnected: boolean;
11
- };