@vox-ai/react 0.1.1

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.
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # Vox.ai React 라이브러리
2
+
3
+ React 애플리케이션에 음성 AI 기능을 통합하기 위한 SDK 라이브러리입니다.
4
+
5
+ ## 설치
6
+
7
+ 패키지 매니저를 통해 프로젝트에 라이브러리를 설치하세요.
8
+
9
+ ```bash
10
+ npm install @vox-ai/react
11
+ # 또는
12
+ yarn add @vox-ai/react
13
+ # 또는
14
+ pnpm install @vox-ai/react
15
+ ```
16
+
17
+ ## 사용법
18
+
19
+ ### useVoxAI
20
+
21
+ Vox.ai 플랫폼과의 음성 AI 상호작용을 관리하는 React 훅입니다.
22
+
23
+ #### 초기화
24
+
25
+ 먼저 useVoxAI 훅을 초기화합니다.
26
+
27
+ ```tsx
28
+ import { useVoxAI } from "@vox-ai/react";
29
+
30
+ function VoiceComponent() {
31
+ const { connect, disconnect, state, messages } = useVoxAI({
32
+ onConnect: () => console.log("Vox.ai에 연결됨"),
33
+ onDisconnect: () => console.log("Vox.ai 연결 해제됨"),
34
+ onError: (error) => console.error("오류:", error),
35
+ onMessage: (message) => console.log("새 메시지:", message),
36
+ });
37
+
38
+ // 컴포넌트의 나머지 부분
39
+ }
40
+ ```
41
+
42
+ #### 옵션
43
+
44
+ - **onConnect** - 음성 AI 연결이 설정되었을 때 호출되는 핸들러
45
+ - **onDisconnect** - 음성 AI 연결이 종료되었을 때 호출되는 핸들러
46
+ - **onMessage** - 새 메시지가 수신되었을 때 호출되는 핸들러
47
+ - **onError** - 오류가 발생했을 때 호출되는 핸들러
48
+
49
+ #### 메서드
50
+
51
+ ##### connect
52
+
53
+ Vox.ai 서비스에 연결을 설정합니다. 인증 매개변수가 필요합니다.
54
+
55
+ ```tsx
56
+ // 에이전트 ID와 API 키로 Vox.ai에 연결
57
+ connect({
58
+ agentId: "your-agent-id",
59
+ apiKey: "your-api-key",
60
+ metadata: {
61
+ // 대화 커스터마이징을 위한 선택적 메타데이터
62
+ userName: "홍길동",
63
+ context: "고객-지원",
64
+ },
65
+ });
66
+ ```
67
+
68
+ ##### disconnect
69
+
70
+ 음성 AI 세션을 수동으로 종료하는 메서드입니다.
71
+
72
+ ```tsx
73
+ disconnect();
74
+ ```
75
+
76
+ #### 상태 및 데이터
77
+
78
+ ##### state
79
+
80
+ 음성 AI 상호작용의 현재 상태를 포함하는 React 상태입니다.
81
+
82
+ ```tsx
83
+ const { state } = useVoxAI();
84
+ console.log(state); // "disconnected", "connecting", "initializing", "listening", "thinking", "speaking" 중 하나
85
+ ```
86
+
87
+ 이 상태를 사용하여 사용자에게 적절한 UI 표시기를 보여줄 수 있습니다.
88
+
89
+ ##### messages
90
+
91
+ 대화 기록을 포함하는 React 상태입니다.
92
+
93
+ ```tsx
94
+ const { messages } = useVoxAI();
95
+ console.log(messages); // 메시지 객체 배열
96
+ ```
97
+
98
+ 각 메시지는 다음 구조를 가집니다:
99
+
100
+ ```tsx
101
+ type VoxMessage = {
102
+ id?: string;
103
+ name: "agent" | "user" | "tool";
104
+ message?: string;
105
+ timestamp: number;
106
+ isFinal?: boolean;
107
+ };
108
+ ```
109
+
110
+ ## 예제
111
+
112
+ ```tsx
113
+ import React, { useState } from "react";
114
+ import { useVoxAI } from "@vox-ai/react";
115
+
116
+ function VoiceAssistant() {
117
+ const [isConnected, setIsConnected] = useState(false);
118
+ const { connect, disconnect, state, messages } = useVoxAI({
119
+ onConnect: () => setIsConnected(true),
120
+ onDisconnect: () => setIsConnected(false),
121
+ onError: (error) => console.error("오류:", error),
122
+ });
123
+
124
+ const handleConnect = () => {
125
+ connect({
126
+ agentId: "your-agent-id",
127
+ apiKey: "your-api-key",
128
+ });
129
+ };
130
+
131
+ return (
132
+ <div>
133
+ <h1>Vox.ai 음성 비서</h1>
134
+
135
+ <div>
136
+ <button onClick={handleConnect} disabled={isConnected}>
137
+ 연결
138
+ </button>
139
+ <button onClick={disconnect} disabled={!isConnected}>
140
+ 연결 해제
141
+ </button>
142
+ </div>
143
+
144
+ <div>
145
+ <p>현재 상태: {state}</p>
146
+ </div>
147
+
148
+ <div>
149
+ <h2>대화</h2>
150
+ <ul>
151
+ {messages.map((msg, index) => (
152
+ <li key={msg.id || index}>
153
+ <strong>{msg.name}:</strong> {msg.message}
154
+ </li>
155
+ ))}
156
+ </ul>
157
+ </div>
158
+ </div>
159
+ );
160
+ }
161
+ ```
162
+
163
+ ## 기여하기
164
+
165
+ 변경 사항을 제안하기 전에 먼저 이슈를 생성해 주세요. 모든 기여를 환영합니다!
166
+
167
+ Pull Request를 제출함으로써, 귀하는 코드가 MIT 라이센스 하에 이 라이브러리에 통합되는 것에 동의하는 것입니다.
@@ -0,0 +1,12 @@
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 {};
@@ -0,0 +1,6 @@
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 {};
@@ -0,0 +1 @@
1
+ export * from "./VoxAIConversation";
@@ -0,0 +1,16 @@
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 {};
@@ -0,0 +1,17 @@
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 {};
@@ -0,0 +1 @@
1
+ export * from "./VoxAIContext";
@@ -0,0 +1 @@
1
+ export * from "./useVoxAI";
@@ -0,0 +1,11 @@
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
+ };
@@ -0,0 +1,29 @@
1
+ type VoxAgentState = "disconnected" | "connecting" | "initializing" | "listening" | "thinking" | "speaking";
2
+ export type VoxMessage = {
3
+ id?: string;
4
+ name: "agent" | "user" | "tool";
5
+ message?: string;
6
+ timestamp: number;
7
+ isFinal?: boolean;
8
+ };
9
+ interface VoxAIOptions {
10
+ onConnect?: () => void;
11
+ onDisconnect?: () => void;
12
+ onError?: (error: Error) => void;
13
+ onMessage?: (message: VoxMessage) => void;
14
+ }
15
+ interface ConnectParams {
16
+ agentId: string;
17
+ apiKey: string;
18
+ metadata?: Record<string, any>;
19
+ }
20
+ /**
21
+ * Hook for integrating with VoxAI voice assistant
22
+ */
23
+ export declare function useVoxAI(options?: VoxAIOptions): {
24
+ connect: ({ agentId, apiKey, metadata }: ConnectParams) => Promise<void>;
25
+ disconnect: () => void;
26
+ state: VoxAgentState;
27
+ messages: VoxMessage[];
28
+ };
29
+ export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @voxai/react
3
+ * React UI component library for vox.ai
4
+ */
5
+ export { useVoxAI } from "./hooks";
package/dist/lib.cjs ADDED
@@ -0,0 +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: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&&k(t.transcriptions)},()=>{var e;null==(e=y.current)||e.port1.close()}),[]);const k=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 E=r.useCallback(function(e){let{agentId:t,apiKey:r,metadata:s}=e;try{return Promise.resolve(function(e,o){try{var a=(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){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 o(e)}return a&&a.then?a.then(void 0,o):a}(0,function(e){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 n;v.current&&v.current.render(a?e.jsxs(t.LiveKitRoom,{serverUrl:a.serverUrl,token:a.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:w,children:[e.jsx(t.RoomAudioRenderer,{}),e.jsx(o,{port:null==(n=y.current)?void 0:n.port2})]}):e.jsx(e.Fragment,{}))},[a,w]),{connect:E,disconnect:w,state:c,messages:d}};
2
+ //# sourceMappingURL=lib.cjs.map
@@ -0,0 +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 metadata\ninterface ConnectParams {\n agentId: string;\n apiKey: string;\n metadata?: Record<string, any>; // Allow any metadata 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 metadata\n const connect = useCallback(\n async ({ agentId, apiKey, 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: 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 setState(\"disconnected\");\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 >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect]);\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","metadata","Promise","resolve","fetch","method","headers","Authorization","agent_id","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","children","_jsx","RoomAudioRenderer","port2","_Fragment"],"mappings":"uJA0RA,SAASA,EAAYC,GAAC,IAAAC,KAAEA,GAAyCD,EAC/D,MAAME,MAAEA,EAAKC,MAAEA,GAAUC,EAAiBA,oBAGpCC,EAAkBC,EAAoBA,qBAC1C,CAACC,EAAAA,MAAMC,OAAOC,YACdP,MAAAA,OAAAA,EAAAA,EAAOQ,UACP,GACIC,EAAqBC,wBAAsBP,GAG3CQ,EAAmBC,wBACnBC,EAAgBH,EAAqBA,sBAAC,CAC1CI,YAAaH,EAAiBI,gBAC9BC,OAAQX,EAAKA,MAACC,OAAOC,WACrBU,YAAaN,EAAiBA,mBA8ChC,OA1CAO,YAAU,KACJnB,GACFA,EAAKoB,YAAY,CAAEC,KAAM,eAAgBnB,SAC3C,EACC,CAACA,EAAOF,IAGXmB,EAAAA,UAAU,KACR,GAAInB,GAAQU,EAAmBY,SAASC,OAAS,EAAG,CAClD,MAAMC,EAAiBd,EAAmBY,SAASG,IAAKC,IAAO,CAC7DC,GAAID,EAAQC,GACZC,KAAMF,EAAQE,KACdC,QAASH,EAAQI,MACjBC,UAAWC,KAAKC,MAChBC,QAAS,WAGXlC,EAAKoB,YAAY,CACfC,KAAM,uBACNG,kBAEJ,GACC,CAACd,EAAmBY,SAAUtB,IAGjCmB,EAASA,UAAC,KACR,GAAInB,GAAQc,EAAcQ,SAASC,OAAS,EAAG,CAC7C,MAAMC,EAAiBV,EAAcQ,SAASG,IAAKC,IAAa,CAC9DC,GAAID,EAAQC,GACZC,KAAMF,EAAQE,KACdC,QAASH,EAAQI,MACjBC,UAAWC,KAAKC,MAChBC,QAAS,UAGXlC,EAAKoB,YAAY,CACfC,KAAM,uBACNG,kBAEJ,GACC,CAACV,EAAcQ,SAAUtB,IAG9B,IAAA,kBAjRgB,SAASmC,QAAAA,IAAAA,IAAAA,EAAwB,CAAA,GAE/C,MAAOC,EAAkBC,GACvBC,WAAqC,OAChCpC,EAAOqC,GAAYD,EAAAA,SAAwB,iBAG3CE,EAAeC,GAAoBH,WACxC,IAAII,MAECC,EAAUC,GAAeN,EAAQA,SAAe,IACjDO,EAAkBC,SAAe,IAGjCC,EAAyBD,EAAMA,OAAc,IAAIE,KAGjDC,EAAgBH,EAAAA,OAA8B,MAC9CI,EAAUJ,EAAAA,OAAoB,MAG9BK,EAAaL,EAAMA,OAAwB,MAGjD3B,EAASA,UAAC,KACR,MAAMiC,EAAcC,MAAMC,KAAKd,EAAce,UAAUC,KACrD,CAACC,EAAGC,IAAMD,EAAE1B,UAAY2B,EAAE3B,WAItB4B,EAAiBC,KAAKC,UAAUT,GAClCO,IAAmBd,EAAgBiB,UACrCjB,EAAgBiB,QAAUH,EAC1Bf,EAAYQ,GAGRjB,EAAQ4B,WACVX,EACGY,OACEC,GACCA,EAAIpC,SACJoC,EAAItC,KACHoB,EAAuBe,QAAQI,IAAID,EAAItC,KAE3CwC,QAASF,IACJA,EAAItC,KAENoB,EAAuBe,QAAQM,IAAIH,EAAItC,IAEtB,MAAjBQ,EAAQ4B,WAAR5B,EAAQ4B,UAAYE,GACtB,GAGR,EACC,CAACzB,EAAeL,EAAQ4B,YAG3B5C,EAASA,UAAC,KACRgC,EAAWW,QAAU,IAAIO,eACzBlB,EAAWW,QAAQQ,MAAMC,UAAaC,IACpC,MAAMC,EAAOD,EAAEC,KAEG,iBAAdA,EAAKpD,KACPkB,EAASkC,EAAKvE,OACS,yBAAduE,EAAKpD,MACdqD,EAA0BD,EAAKjD,eACjC,EAGK,SAAKmD,EACVA,OAAAA,EAAAxB,EAAWW,UAAXa,EAAoBL,MAAMM,UAE3B,IAGH,MAAMF,EAA4BG,EAAAA,YAC/BrD,IACCiB,EAAkBqC,IAChB,MAAMC,EAAS,IAAIrC,IAAIoC,GAgBvB,OAdAtD,EAAe2C,QAASa,IAAK,IAAAC,EAC3B,MAAMC,EAA4B,UAAdF,EAAE9C,QAAsB,QAAU,OAEhDiD,GAAqC,OAAjBF,EAAAH,EAAQM,IAAIJ,EAAErD,UAAG,EAAjBsD,EAAmBlD,YAAaiD,EAAEjD,UAE5DgD,EAAOM,IAAIL,EAAErD,GAAI,CACfA,GAAIqD,EAAErD,GACN2D,KAAMJ,EACNK,QAASP,EAAEpD,KACXG,UAAWoD,EACXtD,QAASmD,EAAEnD,SACZ,GAGIkD,GAEX,EACA,IAIF5D,EAAAA,UAAU,KACR,MAAMqE,EAAMC,SAASC,cAAc,OAMnC,OALAF,EAAIG,MAAMC,QAAU,OACpBH,SAASI,KAAKC,YAAYN,GAC1BvC,EAAca,QAAU0B,EACxBtC,EAAQY,QAAUiC,EAAUA,WAACP,GAEtB,KACDtC,EAAQY,SACVZ,EAAQY,QAAQkC,UAEd/C,EAAca,SAChB2B,SAASI,KAAKI,YAAYhD,EAAca,QAC1C,CACF,EACC,IAGH,MAAMoC,EAAUrB,uBAAWsB,GAAA,IAClBC,QAAEA,EAAOC,OAAEA,EAAMC,SAAEA,GAAyBH,EAAI,IAAA,OAAAI,QAAAC,iCAEnDjE,EAAS,cAAcgE,QAAAC,QAEAC,MAtKN,sCAsK8B,CAC7CC,OAAQ,OACRC,QAAS,CACPC,cAAyBP,UAAAA,EACzB,eAAgB,oBAElBR,KAAMjC,KAAKC,UAAU,CACnBgD,SAAUT,EACVE,SAAU,CACRQ,SAAU,CACRC,kBAAmBT,GAAY,CAAA,SAIrCU,cAdIC,GAAQC,SAAAA,EAAAC,GAAAC,OAAAb,QAAAC,QAuBKS,EAASI,QAAML,KAA5BvC,SAAAA,GACNpC,EAAoBoC,GAEhBtC,EAAQmF,WACVnF,EAAQmF,WAAYC,EAAAA,CAAAA,MAAAA,iBAXjBN,EAASO,GAAE,OAAAjB,QAAAC,QACUS,EAASrF,QAAMoF,cAAjCS,GACN,MAAM,IAAIC,MAAK,sBACST,EAASU,aAAYF,EAC3C,EAAAF,IAAAA,OAAAA,GAAAA,EAAAP,KAAAO,EAAAP,KAAAE,GAAAA,gEAxB+CU,GAiCpD,SAAQC,GACPtF,EAAS,gBACT,MAAMuF,EAAQD,aAAeH,MAAQG,EAAM,IAAIH,MAAMK,OAAOF,IAExD1F,EAAQ6F,SACV7F,EAAQ6F,QAAQF,EAEpB,GACF,CAAC,MAAAtD,GAAA+B,OAAAA,QAAA0B,OAAAzD,EACD,CAAA,EAAA,CAACrC,IAIG+F,EAAarD,EAAAA,YAAY,KAC7BxC,EAAoB,MACpBI,EAAiB,IAAIC,KACrBE,EAAY,IACZL,EAAS,gBAELJ,EAAQgG,cACVhG,EAAQgG,cACV,EACC,CAAChG,IAyBJ,OAtBAhB,EAAAA,UAAU,KAGciH,IAAAA,EAFjBlF,EAAQY,SAGXZ,EAAQY,QAAQuE,OADdjG,EAEAkG,OAACC,EAAAA,YAAW,CACVC,UAAWpG,EAAiBoG,UAC5BC,MAAOrG,EAAiBsG,iBACxBC,OAAO,EACPC,OAAO,EACP1C,SAAS,EACT2C,eAAgBX,EAAUY,SAAA,CAE1BC,EAAAA,IAACC,EAAiBA,kBAAG,CAAA,GACrBD,MAACjJ,EAAY,CAACE,KAAwB,OAApBoI,EAAEjF,EAAWW,cAAO,EAAlBsE,EAAoBa,WAIrBF,MAAAG,EAAAA,SAAA,CAAA,GACzB,EACC,CAAC9G,EAAkB8F,IAEf,CACLhC,UACAgC,aACAhI,QACAyC,WAEJ"}
@@ -0,0 +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 a,useParticipantTracks as s,useTrackTranscription as i,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[s,i]=d(null),[c,p]=d("disconnected"),[f,h]=d(new Map),[w,v]=d([]),M=l(""),k=l(new Set),x=l(null),E=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&&!k.current.has(t.id)).forEach(t=>{t.id&&(k.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&&C(e.transcriptions)},()=>{var t;null==(t=_.current)||t.port1.close()}),[]);const C=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),x.current=t,E.current=g(t),()=>{E.current&&E.current.unmount(),x.current&&document.body.removeChild(x.current)}},[]);const S=m(async({agentId:t,apiKey:e,metadata: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();i(o),a.onConnect&&a.onConnect()}catch(t){p("disconnected");const e=t instanceof Error?t:new Error(String(t));a.onError&&a.onError(e)}},[a]),b=m(()=>{i(null),h(new Map),v([]),p("disconnected"),a.onDisconnect&&a.onDisconnect()},[a]);return u(()=>{var a;E.current&&E.current.render(s?t(r,{serverUrl:s.serverUrl,token:s.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:b,children:[e(o,{}),e(y,{port:null==(a=_.current)?void 0:a.port2})]}):e(n,{}))},[s,b]),{connect:S,disconnect:b,state:c,messages:w}}function y({port:t}){const{agent:e,state:n}=a(),r=s([p.Source.Microphone],null==e?void 0:e.identity)[0],o=i(r),d=c(),l=i({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
+ //# sourceMappingURL=lib.modern.js.map
@@ -0,0 +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 metadata\ninterface ConnectParams {\n agentId: string;\n apiKey: string;\n metadata?: Record<string, any>; // Allow any metadata 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 metadata\n const connect = useCallback(\n async ({ agentId, apiKey, 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: 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 setState(\"disconnected\");\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 >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect]);\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","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","children","_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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"}
@@ -0,0 +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 u}from"livekit-client";import{useState as p,useRef as l,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]=p(null),[c,u]=p("disconnected"),[f,v]=p(new Map),[y,w]=p([]),M=l(""),k=l(new Set),x=l(null),E=l(null),_=l(null);d(()=>{const t=Array.from(f.values()).sort((t,e)=>t.timestamp-e.timestamp),e=JSON.stringify(t);e!==M.current&&(M.current=e,w(t),i.onMessage&&t.filter(t=>t.isFinal&&t.id&&!k.current.has(t.id)).forEach(t=>{t.id&&(k.current.add(t.id),null==i.onMessage||i.onMessage(t))}))},[f,i.onMessage]),d(()=>(_.current=new MessageChannel,_.current.port1.onmessage=t=>{const e=t.data;"state_update"===e.type?u(e.state):"transcription_update"===e.type&&C(e.transcriptions)},()=>{var t;null==(t=_.current)||t.port1.close()}),[]);const C=m(t=>{v(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})},[]);d(()=>{const t=document.createElement("div");return t.style.display="none",document.body.appendChild(t),x.current=t,E.current=g(t),()=>{E.current&&E.current.unmount(),x.current&&document.body.removeChild(x.current)}},[]);const P=m(function(t){let{agentId:e,apiKey:n,metadata:r}=t;try{return Promise.resolve(function(t,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:e,metadata:{call_web:{dynamic_variables:r||{}}}})})).then(function(t){function e(e){return Promise.resolve(t.json()).then(function(t){a(t),i.onConnect&&i.onConnect()})}const n=function(){if(!t.ok)return Promise.resolve(t.text()).then(function(e){throw new Error("Connection failed ("+t.status+"): "+e)})}();return n&&n.then?n.then(e):e()}))}catch(t){return o(t)}return s&&s.then?s.then(void 0,o):s}(0,function(t){u("disconnected");const e=t instanceof Error?t:new Error(String(t));i.onError&&i.onError(e)}))}catch(t){return Promise.reject(t)}},[i]),S=m(()=>{a(null),v(new Map),w([]),u("disconnected"),i.onDisconnect&&i.onDisconnect()},[i]);return d(()=>{var i;E.current&&E.current.render(s?t(r,{serverUrl:s.serverUrl,token:s.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:S,children:[e(o,{}),e(h,{port:null==(i=_.current)?void 0:i.port2})]}):e(n,{}))},[s,S]),{connect:P,disconnect:S,state:c,messages:y}}function h(t){let{port:e}=t;const{agent:n,state:r}=i(),o=s([u.Source.Microphone],null==n?void 0:n.identity)[0],p=a(o),l=c(),m=a({publication:l.microphoneTrack,source:u.Source.Microphone,participant:l.localParticipant});return d(()=>{e&&e.postMessage({type:"state_update",state:r})},[r,e]),d(()=>{if(e&&p.segments.length>0){const t=p.segments.map(t=>({id:t.id,text:t.text,isFinal:t.final,timestamp:Date.now(),speaker:"agent"}));e.postMessage({type:"transcription_update",transcriptions:t})}},[p.segments,e]),d(()=>{if(e&&m.segments.length>0){const t=m.segments.map(t=>({id:t.id,text:t.text,isFinal:t.final,timestamp:Date.now(),speaker:"user"}));e.postMessage({type:"transcription_update",transcriptions:t})}},[m.segments,e]),null}export{f as useVoxAI};
2
+ //# sourceMappingURL=lib.module.js.map
@@ -0,0 +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 metadata\ninterface ConnectParams {\n agentId: string;\n apiKey: string;\n metadata?: Record<string, any>; // Allow any metadata 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 metadata\n const connect = useCallback(\n async ({ agentId, apiKey, 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: 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 setState(\"disconnected\");\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 >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect]);\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","metadata","Promise","resolve","fetch","method","headers","Authorization","agent_id","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","children","_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":"sZAuEgB,SAAAA,EAASC,QAAAA,IAAAA,IAAAA,EAAwB,CAAA,GAE/C,MAAOC,EAAkBC,GACvBC,EAAqC,OAChCC,EAAOC,GAAYF,EAAwB,iBAG3CG,EAAeC,GAAoBJ,EACxC,IAAIK,MAECC,EAAUC,GAAeP,EAAuB,IACjDQ,EAAkBC,EAAe,IAGjCC,EAAyBD,EAAoB,IAAIE,KAGjDC,EAAgBH,EAA8B,MAC9CI,EAAUJ,EAAoB,MAG9BK,EAAaL,EAA8B,MAGjDM,EAAU,KACR,MAAMC,EAAcC,MAAMC,KAAKf,EAAcgB,UAAUC,KACrD,CAACC,EAAGC,IAAMD,EAAEE,UAAYD,EAAEC,WAItBC,EAAiBC,KAAKC,UAAUV,GAClCQ,IAAmBhB,EAAgBmB,UACrCnB,EAAgBmB,QAAUH,EAC1BjB,EAAYS,GAGRnB,EAAQ+B,WACVZ,EACGa,OACEC,GACCA,EAAIC,SACJD,EAAIE,KACHtB,EAAuBiB,QAAQM,IAAIH,EAAIE,KAE3CE,QAASJ,IACJA,EAAIE,KAENtB,EAAuBiB,QAAQQ,IAAIL,EAAIE,IAEtB,MAAjBnC,EAAQ+B,WAAR/B,EAAQ+B,UAAYE,GACtB,GAGR,EACC,CAAC3B,EAAeN,EAAQ+B,YAG3Bb,EAAU,KACRD,EAAWa,QAAU,IAAIS,eACzBtB,EAAWa,QAAQU,MAAMC,UAAaC,IACpC,MAAMC,EAAOD,EAAEC,KAEG,iBAAdA,EAAKC,KACPvC,EAASsC,EAAKvC,OACS,yBAAduC,EAAKC,MACdC,EAA0BF,EAAKG,eACjC,EAGK,SAAKC,EACVA,OAAAA,EAAA9B,EAAWa,UAAXiB,EAAoBP,MAAMQ,UAE3B,IAGH,MAAMH,EAA4BI,EAC/BH,IACCvC,EAAkB2C,IAChB,MAAMC,EAAS,IAAI3C,IAAI0C,GAgBvB,OAdAJ,EAAeT,QAASe,IAAK,IAAAC,EAC3B,MAAMC,EAA4B,UAAdF,EAAEG,QAAsB,QAAU,OAEhDC,GAAqC,OAAjBH,EAAAH,EAAQO,IAAIL,EAAEjB,UAAG,EAAjBkB,EAAmB3B,YAAa0B,EAAE1B,UAE5DyB,EAAOO,IAAIN,EAAEjB,GAAI,CACfA,GAAIiB,EAAEjB,GACNwB,KAAML,EACNM,QAASR,EAAES,KACXnC,UAAW8B,EACXtB,QAASkB,EAAElB,SACZ,GAGIiB,GAEX,EACA,IAIFjC,EAAU,KACR,MAAM4C,EAAMC,SAASC,cAAc,OAMnC,OALAF,EAAIG,MAAMC,QAAU,OACpBH,SAASI,KAAKC,YAAYN,GAC1B/C,EAAce,QAAUgC,EACxB9C,EAAQc,QAAUuC,EAAWP,GAEtB,KACD9C,EAAQc,SACVd,EAAQc,QAAQwC,UAEdvD,EAAce,SAChBiC,SAASI,KAAKI,YAAYxD,EAAce,QAC1C,CACF,EACC,IAGH,MAAM0C,EAAUvB,WAAWwB,GAAA,IAClBC,QAAEA,EAAOC,OAAEA,EAAMC,SAAEA,GAAyBH,EAAI,IAAA,OAAAI,QAAAC,iCAEnDzE,EAAS,cAAcwE,QAAAC,QAEAC,MAtKN,sCAsK8B,CAC7CC,OAAQ,OACRC,QAAS,CACPC,cAAyBP,UAAAA,EACzB,eAAgB,oBAElBR,KAAMvC,KAAKC,UAAU,CACnBsD,SAAUT,EACVE,SAAU,CACRQ,SAAU,CACRC,kBAAmBT,GAAY,CAAA,SAIrCU,cAdIC,GAAQC,SAAAA,EAAAC,GAAAC,OAAAb,QAAAC,QAuBKS,EAASI,QAAML,KAA5B3C,SAAAA,GACNzC,EAAoByC,GAEhB3C,EAAQ4F,WACV5F,EAAQ4F,WAAYC,EAAAA,CAAAA,MAAAA,iBAXjBN,EAASO,GAAE,OAAAjB,QAAAC,QACUS,EAAS1B,QAAMyB,cAAjCS,GACN,MAAM,IAAIC,MAAK,sBACST,EAASU,aAAYF,EAC3C,EAAAF,IAAAA,OAAAA,GAAAA,EAAAP,KAAAO,EAAAP,KAAAE,GAAAA,gEAxB+CU,GAiCpD,SAAQC,GACP9F,EAAS,gBACT,MAAM+F,EAAQD,aAAeH,MAAQG,EAAM,IAAIH,MAAMK,OAAOF,IAExDnG,EAAQsG,SACVtG,EAAQsG,QAAQF,EAEpB,GACF,CAAC,MAAA1D,GAAAmC,OAAAA,QAAA0B,OAAA7D,EACD,CAAA,EAAA,CAAC1C,IAIGwG,EAAavD,EAAY,KAC7B/C,EAAoB,MACpBK,EAAiB,IAAIC,KACrBE,EAAY,IACZL,EAAS,gBAELL,EAAQyG,cACVzG,EAAQyG,cACV,EACC,CAACzG,IAyBJ,OAtBAkB,EAAU,KAGcwF,IAAAA,EAFjB1F,EAAQc,SAGXd,EAAQc,QAAQ6E,OADd1G,EAEA2G,EAACC,EAAW,CACVC,UAAW7G,EAAiB6G,UAC5BC,MAAO9G,EAAiB+G,iBACxBC,OAAO,EACPC,OAAO,EACP1C,SAAS,EACT2C,eAAgBX,EAAUY,SAAA,CAE1BC,EAACC,EAAoB,CAAA,GACrBD,EAACE,EAAY,CAACC,KAAwB,OAApBd,EAAEzF,EAAWa,cAAO,EAAlB4E,EAAoBe,WAIrBJ,EAAAK,EAAA,CAAA,GACzB,EACC,CAACzH,EAAkBuG,IAEf,CACLhC,UACAgC,aACApG,QACAK,WAEJ,CAKA,SAAS8G,EAAYI,GAAC,IAAAH,KAAEA,GAAyCG,EAC/D,MAAMC,MAAEA,EAAKxH,MAAEA,GAAUyH,IAGnBC,EAAkBC,EACtB,CAACC,EAAMC,OAAOC,YACdN,MAAAA,OAAAA,EAAAA,EAAOO,UACP,GACIC,EAAqBC,EAAsBP,GAG3CQ,EAAmBC,IACnBC,EAAgBH,EAAsB,CAC1CI,YAAaH,EAAiBI,gBAC9BC,OAAQX,EAAMC,OAAOC,WACrBU,YAAaN,EAAiBA,mBA8ChC,OA1CApH,EAAU,KACJsG,GACFA,EAAKqB,YAAY,CAAEjG,KAAM,eAAgBxC,SAC3C,EACC,CAACA,EAAOoH,IAGXtG,EAAU,KACR,GAAIsG,GAAQY,EAAmBU,SAASC,OAAS,EAAG,CAClD,MAAMjG,EAAiBsF,EAAmBU,SAASE,IAAKC,IAAO,CAC7D9G,GAAI8G,EAAQ9G,GACZ0B,KAAMoF,EAAQpF,KACd3B,QAAS+G,EAAQC,MACjBxH,UAAWyH,KAAKC,MAChB7F,QAAS,WAGXiE,EAAKqB,YAAY,CACfjG,KAAM,uBACNE,kBAEJ,GACC,CAACsF,EAAmBU,SAAUtB,IAGjCtG,EAAU,KACR,GAAIsG,GAAQgB,EAAcM,SAASC,OAAS,EAAG,CAC7C,MAAMjG,EAAiB0F,EAAcM,SAASE,IAAKC,IAAa,CAC9D9G,GAAI8G,EAAQ9G,GACZ0B,KAAMoF,EAAQpF,KACd3B,QAAS+G,EAAQC,MACjBxH,UAAWyH,KAAKC,MAChB7F,QAAS,UAGXiE,EAAKqB,YAAY,CACfjG,KAAM,uBACNE,kBAEJ,GACC,CAAC0F,EAAcM,SAAUtB,IAG9B,IAAA"}
@@ -0,0 +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,s,i){function o(e){let{port:t}=e;const{agent:i,state:o}=n.useVoiceAssistant(),a=n.useParticipantTracks([r.Track.Source.Microphone],null==i?void 0:i.identity)[0],c=n.useTrackTranscription(a),u=n.useLocalParticipant(),l=n.useTrackTranscription({publication:u.microphoneTrack,source:r.Track.Source.Microphone,participant:u.localParticipant});return s.useEffect(()=>{t&&t.postMessage({type:"state_update",state:o})},[o,t]),s.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]),s.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]=s.useState(null),[c,u]=s.useState("disconnected"),[l,d]=s.useState(new Map),[p,f]=s.useState([]),m=s.useRef(""),g=s.useRef(new Set),h=s.useRef(null),v=s.useRef(null),y=s.useRef(null);s.useEffect(()=>{const t=Array.from(l.values()).sort((e,t)=>e.timestamp-t.timestamp),n=JSON.stringify(t);n!==m.current&&(m.current=n,f(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]),s.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=s.useCallback(e=>{d(t=>{const n=new Map(t);return e.forEach(e=>{var r;const s="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:s,message:e.text,timestamp:i,isFinal:e.isFinal})}),n})},[]);s.useEffect(()=>{const e=document.createElement("div");return e.style.display="none",document.body.appendChild(e),h.current=e,v.current=i.createRoot(e),()=>{v.current&&v.current.unmount(),h.current&&document.body.removeChild(h.current)}},[]);const x=s.useCallback(function(t){let{agentId:n,apiKey:r,metadata:s}=t;try{return Promise.resolve(function(t,i){try{var o=(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: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 o&&o.then?o.then(void 0,i):o}(0,function(t){u("disconnected");const n=t instanceof Error?t:new Error(String(t));e.onError&&e.onError(n)}))}catch(e){return Promise.reject(e)}},[e]),E=s.useCallback(()=>{a(null),d(new Map),f([]),u("disconnected"),e.onDisconnect&&e.onDisconnect()},[e]);return s.useEffect(()=>{var e;v.current&&v.current.render(r?t.jsxs(n.LiveKitRoom,{serverUrl:r.serverUrl,token:r.participantToken,audio:!0,video:!1,connect:!0,onDisconnected:E,children:[t.jsx(n.RoomAudioRenderer,{}),t.jsx(o,{port:null==(e=y.current)?void 0:e.port2})]}):t.jsx(t.Fragment,{}))},[r,E]),{connect:x,disconnect:E,state:c,messages:p}}});
2
+ //# sourceMappingURL=lib.umd.js.map
@@ -0,0 +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 metadata\ninterface ConnectParams {\n agentId: string;\n apiKey: string;\n metadata?: Record<string, any>; // Allow any metadata 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 metadata\n const connect = useCallback(\n async ({ agentId, apiKey, 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: 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 setState(\"disconnected\");\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 >\n <RoomAudioRenderer />\n <StateMonitor port={channelRef.current?.port2} />\n </LiveKitRoom>\n );\n } else {\n rootRef.current.render(<></>);\n }\n }, [connectionDetail, disconnect]);\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","metadata","Promise","resolve","fetch","method","headers","Authorization","agent_id","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","children","_jsx","RoomAudioRenderer","port2","_Fragment"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@vox-ai/react",
3
+ "version": "0.1.1",
4
+ "description": "vox.ai React Library",
5
+ "main": "./dist/lib.umd.js",
6
+ "module": "./dist/lib.module.js",
7
+ "source": "src/index.ts",
8
+ "type": "module",
9
+ "unpkg": "./dist/lib.umd.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/lib.modern.js",
15
+ "require": "./dist/lib.cjs"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "BROWSERSLIST_ENV=modern microbundle src/index.ts",
23
+ "clean": "rm -rf ./dist",
24
+ "dev": "npm run clean && BROWSERSLIST_ENV=development microbundle --jsx React.createElement --jsxFragment React.Fragment --jsxImportSource react src/index.ts -w -f modern",
25
+ "lint": "npm run lint:ts && npm run lint:es",
26
+ "lint:ts": "tsc --noEmit --skipLibCheck",
27
+ "lint:es": "npx eslint .",
28
+ "lint:prettier": "prettier 'src/**/*.ts'",
29
+ "prepublishOnly": "npm run build",
30
+ "test": "jest"
31
+ },
32
+ "dependencies": {
33
+ "@livekit/components-react": "^2.8.1",
34
+ "@livekit/krisp-noise-filter": "^0.2.16",
35
+ "livekit-client": "^2.9.5"
36
+ },
37
+ "peerDependencies": {
38
+ "react": ">=16.8.0",
39
+ "react-dom": ">=16.8.0",
40
+ "@livekit/components-react": "^2.8.1",
41
+ "@livekit/krisp-noise-filter": "^0.2.16",
42
+ "livekit-client": "^2.9.5"
43
+ },
44
+ "devDependencies": {
45
+ "@types/jest": "^29.5.12",
46
+ "@types/react": "^18.3.3",
47
+ "@types/react-dom": "^18.3.0",
48
+ "eslint": "^9.8.0",
49
+ "jest": "^29.7.0",
50
+ "microbundle": "^0.15.1",
51
+ "react": "^18.2.0",
52
+ "react-dom": "^18.2.0",
53
+ "typescript": "^5.5.4"
54
+ },
55
+ "license": "MIT"
56
+ }