@jchaffin/voicekit 0.2.2 → 0.2.3
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/dist/adapters/deepgram.js +15 -12
- package/dist/adapters/deepgram.mjs +15 -12
- package/dist/adapters/elevenlabs.js +5 -11
- package/dist/adapters/elevenlabs.mjs +5 -11
- package/dist/adapters/livekit.js +3 -9
- package/dist/adapters/livekit.mjs +3 -9
- package/dist/index.js +12 -4
- package/dist/index.mjs +12 -4
- package/package.json +10 -6
|
@@ -73,18 +73,21 @@ var DeepgramSession = class extends EventEmitter {
|
|
|
73
73
|
if (this.options.model) url.searchParams.set("model", this.options.model);
|
|
74
74
|
if (this.options.language) url.searchParams.set("language", this.options.language);
|
|
75
75
|
this.ws = new WebSocket(url.toString());
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
await new Promise((resolve, reject) => {
|
|
77
|
+
const ws = this.ws;
|
|
78
|
+
let opened = false;
|
|
79
|
+
ws.onopen = () => {
|
|
80
|
+
opened = true;
|
|
81
|
+
resolve();
|
|
82
|
+
};
|
|
83
|
+
ws.onerror = () => {
|
|
84
|
+
if (!opened) reject(new Error("WebSocket connection failed"));
|
|
85
|
+
};
|
|
86
|
+
ws.onclose = () => {
|
|
87
|
+
this.emit("status_change", "DISCONNECTED");
|
|
88
|
+
if (!opened) reject(new Error("WebSocket closed before connection opened"));
|
|
89
|
+
};
|
|
90
|
+
});
|
|
88
91
|
this.ws.onmessage = (event) => {
|
|
89
92
|
try {
|
|
90
93
|
const msg = JSON.parse(event.data);
|
|
@@ -19,18 +19,21 @@ var DeepgramSession = class extends EventEmitter {
|
|
|
19
19
|
if (this.options.model) url.searchParams.set("model", this.options.model);
|
|
20
20
|
if (this.options.language) url.searchParams.set("language", this.options.language);
|
|
21
21
|
this.ws = new WebSocket(url.toString());
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
await new Promise((resolve, reject) => {
|
|
23
|
+
const ws = this.ws;
|
|
24
|
+
let opened = false;
|
|
25
|
+
ws.onopen = () => {
|
|
26
|
+
opened = true;
|
|
27
|
+
resolve();
|
|
28
|
+
};
|
|
29
|
+
ws.onerror = () => {
|
|
30
|
+
if (!opened) reject(new Error("WebSocket connection failed"));
|
|
31
|
+
};
|
|
32
|
+
ws.onclose = () => {
|
|
33
|
+
this.emit("status_change", "DISCONNECTED");
|
|
34
|
+
if (!opened) reject(new Error("WebSocket closed before connection opened"));
|
|
35
|
+
};
|
|
36
|
+
});
|
|
34
37
|
this.ws.onmessage = (event) => {
|
|
35
38
|
try {
|
|
36
39
|
const msg = JSON.parse(event.data);
|
|
@@ -73,17 +73,11 @@ var ElevenLabsSession = class extends EventEmitter {
|
|
|
73
73
|
async connect(config) {
|
|
74
74
|
const wsUrl = config.authToken?.startsWith("wss://") ? config.authToken : `${ELEVENLABS_WS_BASE}?agent_id=${this.agentId}`;
|
|
75
75
|
this.ws = new WebSocket(wsUrl);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
82
|
-
} catch (err) {
|
|
83
|
-
this.ws?.close();
|
|
84
|
-
this.ws = null;
|
|
85
|
-
throw err;
|
|
86
|
-
}
|
|
76
|
+
await new Promise((resolve, reject) => {
|
|
77
|
+
const ws = this.ws;
|
|
78
|
+
ws.onopen = () => resolve();
|
|
79
|
+
ws.onerror = () => reject(new Error("ElevenLabs WebSocket connection failed"));
|
|
80
|
+
});
|
|
87
81
|
this.ws.onmessage = (event) => {
|
|
88
82
|
try {
|
|
89
83
|
const msg = JSON.parse(event.data);
|
|
@@ -19,17 +19,11 @@ var ElevenLabsSession = class extends EventEmitter {
|
|
|
19
19
|
async connect(config) {
|
|
20
20
|
const wsUrl = config.authToken?.startsWith("wss://") ? config.authToken : `${ELEVENLABS_WS_BASE}?agent_id=${this.agentId}`;
|
|
21
21
|
this.ws = new WebSocket(wsUrl);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
} catch (err) {
|
|
29
|
-
this.ws?.close();
|
|
30
|
-
this.ws = null;
|
|
31
|
-
throw err;
|
|
32
|
-
}
|
|
22
|
+
await new Promise((resolve, reject) => {
|
|
23
|
+
const ws = this.ws;
|
|
24
|
+
ws.onopen = () => resolve();
|
|
25
|
+
ws.onerror = () => reject(new Error("ElevenLabs WebSocket connection failed"));
|
|
26
|
+
});
|
|
33
27
|
this.ws.onmessage = (event) => {
|
|
34
28
|
try {
|
|
35
29
|
const msg = JSON.parse(event.data);
|
package/dist/adapters/livekit.js
CHANGED
|
@@ -106,15 +106,9 @@ var LiveKitSession = class extends EventEmitter {
|
|
|
106
106
|
this.room.on(RoomEvent.Reconnected, () => {
|
|
107
107
|
this.emit("status_change", "CONNECTED");
|
|
108
108
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.emit("status_change", "CONNECTED");
|
|
113
|
-
} catch (err) {
|
|
114
|
-
await this.room.disconnect();
|
|
115
|
-
this.room = null;
|
|
116
|
-
throw err;
|
|
117
|
-
}
|
|
109
|
+
await this.room.connect(this.serverUrl, config.authToken);
|
|
110
|
+
await this.room.localParticipant.setMicrophoneEnabled(true);
|
|
111
|
+
this.emit("status_change", "CONNECTED");
|
|
118
112
|
}
|
|
119
113
|
async disconnect() {
|
|
120
114
|
if (this.room) {
|
|
@@ -42,15 +42,9 @@ var LiveKitSession = class extends EventEmitter {
|
|
|
42
42
|
this.room.on(RoomEvent.Reconnected, () => {
|
|
43
43
|
this.emit("status_change", "CONNECTED");
|
|
44
44
|
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.emit("status_change", "CONNECTED");
|
|
49
|
-
} catch (err) {
|
|
50
|
-
await this.room.disconnect();
|
|
51
|
-
this.room = null;
|
|
52
|
-
throw err;
|
|
53
|
-
}
|
|
45
|
+
await this.room.connect(this.serverUrl, config.authToken);
|
|
46
|
+
await this.room.localParticipant.setMicrophoneEnabled(true);
|
|
47
|
+
this.emit("status_change", "CONNECTED");
|
|
54
48
|
}
|
|
55
49
|
async disconnect() {
|
|
56
50
|
if (this.room) {
|
package/dist/index.js
CHANGED
|
@@ -217,7 +217,12 @@ function VoiceProvider({
|
|
|
217
217
|
}, 500);
|
|
218
218
|
} catch (error) {
|
|
219
219
|
console.error("VoiceKit connection failed:", error);
|
|
220
|
+
try {
|
|
221
|
+
await sessionRef.current?.disconnect();
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
220
224
|
sessionRef.current = null;
|
|
225
|
+
currentMsgIdRef.current = null;
|
|
221
226
|
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
222
227
|
updateStatus("DISCONNECTED");
|
|
223
228
|
}
|
|
@@ -824,7 +829,6 @@ function useAudioRecorder() {
|
|
|
824
829
|
if (mediaRecorderRef.current?.state === "recording") {
|
|
825
830
|
return;
|
|
826
831
|
}
|
|
827
|
-
recordedChunksRef.current = [];
|
|
828
832
|
try {
|
|
829
833
|
const mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
|
|
830
834
|
mediaRecorder.ondataavailable = (event) => {
|
|
@@ -1107,6 +1111,8 @@ function useSessionHistory() {
|
|
|
1107
1111
|
updateTranscriptMessage,
|
|
1108
1112
|
updateTranscriptItem
|
|
1109
1113
|
} = useTranscript();
|
|
1114
|
+
const transcriptItemsRef = (0, import_react7.useRef)(transcriptItems);
|
|
1115
|
+
transcriptItemsRef.current = transcriptItems;
|
|
1110
1116
|
const { logServerEvent } = useEvent();
|
|
1111
1117
|
const accumulatedTextRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
1112
1118
|
const pendingDeltasRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
@@ -1174,7 +1180,11 @@ function useSessionHistory() {
|
|
|
1174
1180
|
const { itemId, role, content = [] } = item;
|
|
1175
1181
|
if (itemId && role) {
|
|
1176
1182
|
let text = extractMessageText(content);
|
|
1177
|
-
if (!text)
|
|
1183
|
+
if (role === "assistant" && !text) {
|
|
1184
|
+
text = "";
|
|
1185
|
+
} else if (role === "user" && !text) {
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1178
1188
|
const guardrailMessage = sketchilyDetectGuardrailMessage(text);
|
|
1179
1189
|
if (guardrailMessage) {
|
|
1180
1190
|
const failureDetails = JSON.parse(guardrailMessage);
|
|
@@ -1268,8 +1278,6 @@ function useSessionHistory() {
|
|
|
1268
1278
|
});
|
|
1269
1279
|
}
|
|
1270
1280
|
}
|
|
1271
|
-
const transcriptItemsRef = (0, import_react7.useRef)(transcriptItems);
|
|
1272
|
-
transcriptItemsRef.current = transcriptItems;
|
|
1273
1281
|
const handlersRef = (0, import_react7.useRef)({
|
|
1274
1282
|
handleAgentToolStart,
|
|
1275
1283
|
handleAgentToolEnd,
|
package/dist/index.mjs
CHANGED
|
@@ -159,7 +159,12 @@ function VoiceProvider({
|
|
|
159
159
|
}, 500);
|
|
160
160
|
} catch (error) {
|
|
161
161
|
console.error("VoiceKit connection failed:", error);
|
|
162
|
+
try {
|
|
163
|
+
await sessionRef.current?.disconnect();
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
162
166
|
sessionRef.current = null;
|
|
167
|
+
currentMsgIdRef.current = null;
|
|
163
168
|
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
164
169
|
updateStatus("DISCONNECTED");
|
|
165
170
|
}
|
|
@@ -605,7 +610,6 @@ function useAudioRecorder() {
|
|
|
605
610
|
if (mediaRecorderRef.current?.state === "recording") {
|
|
606
611
|
return;
|
|
607
612
|
}
|
|
608
|
-
recordedChunksRef.current = [];
|
|
609
613
|
try {
|
|
610
614
|
const mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
|
|
611
615
|
mediaRecorder.ondataavailable = (event) => {
|
|
@@ -893,6 +897,8 @@ function useSessionHistory() {
|
|
|
893
897
|
updateTranscriptMessage,
|
|
894
898
|
updateTranscriptItem
|
|
895
899
|
} = useTranscript();
|
|
900
|
+
const transcriptItemsRef = useRef5(transcriptItems);
|
|
901
|
+
transcriptItemsRef.current = transcriptItems;
|
|
896
902
|
const { logServerEvent } = useEvent();
|
|
897
903
|
const accumulatedTextRef = useRef5(/* @__PURE__ */ new Map());
|
|
898
904
|
const pendingDeltasRef = useRef5(/* @__PURE__ */ new Map());
|
|
@@ -960,7 +966,11 @@ function useSessionHistory() {
|
|
|
960
966
|
const { itemId, role, content = [] } = item;
|
|
961
967
|
if (itemId && role) {
|
|
962
968
|
let text = extractMessageText(content);
|
|
963
|
-
if (!text)
|
|
969
|
+
if (role === "assistant" && !text) {
|
|
970
|
+
text = "";
|
|
971
|
+
} else if (role === "user" && !text) {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
964
974
|
const guardrailMessage = sketchilyDetectGuardrailMessage(text);
|
|
965
975
|
if (guardrailMessage) {
|
|
966
976
|
const failureDetails = JSON.parse(guardrailMessage);
|
|
@@ -1054,8 +1064,6 @@ function useSessionHistory() {
|
|
|
1054
1064
|
});
|
|
1055
1065
|
}
|
|
1056
1066
|
}
|
|
1057
|
-
const transcriptItemsRef = useRef5(transcriptItems);
|
|
1058
|
-
transcriptItemsRef.current = transcriptItems;
|
|
1059
1067
|
const handlersRef = useRef5({
|
|
1060
1068
|
handleAgentToolStart,
|
|
1061
1069
|
handleAgentToolEnd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jchaffin/voicekit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "A provider-agnostic React library for building voice-enabled AI agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -42,11 +42,12 @@
|
|
|
42
42
|
"README.md"
|
|
43
43
|
],
|
|
44
44
|
"scripts": {
|
|
45
|
-
"build": "tsup
|
|
46
|
-
"dev": "tsup
|
|
45
|
+
"build": "tsup",
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"prepare": "tsup",
|
|
47
48
|
"lint": "eslint src/",
|
|
48
49
|
"typecheck": "tsc --noEmit",
|
|
49
|
-
"prepublishOnly": "
|
|
50
|
+
"prepublishOnly": "tsup"
|
|
50
51
|
},
|
|
51
52
|
"keywords": [
|
|
52
53
|
"voice",
|
|
@@ -59,11 +60,13 @@
|
|
|
59
60
|
"react",
|
|
60
61
|
"agent",
|
|
61
62
|
"webrtc",
|
|
62
|
-
"speech"
|
|
63
|
-
"provider-agnostic"
|
|
63
|
+
"speech"
|
|
64
64
|
],
|
|
65
65
|
"author": "Jacob Chaffin",
|
|
66
66
|
"license": "MIT",
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public"
|
|
69
|
+
},
|
|
67
70
|
"peerDependencies": {
|
|
68
71
|
"react": ">=18.0.0",
|
|
69
72
|
"react-dom": ">=18.0.0",
|
|
@@ -88,6 +91,7 @@
|
|
|
88
91
|
},
|
|
89
92
|
"devDependencies": {
|
|
90
93
|
"@openai/agents": ">=0.0.15",
|
|
94
|
+
"@types/node": "^25.6.0",
|
|
91
95
|
"@types/react": "^18.2.0",
|
|
92
96
|
"@types/react-dom": "^18.2.0",
|
|
93
97
|
"tsup": "^8.0.0",
|