@shogo-ai/voice 1.9.13 → 2.4.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 +89 -0
- package/dist/{chunk-S3S2RNO6.js → chunk-BI6SL35K.js} +1 -184
- package/dist/chunk-CLF25CZC.js +987 -0
- package/dist/chunk-KMSL5PWK.js +540 -0
- package/dist/chunk-ME3SIZZU.js +581 -0
- package/dist/chunk-TQOBJMEK.js +17 -0
- package/dist/{chunk-T3T4EBWY.js → chunk-UH75UVM2.js} +6 -2
- package/dist/chunk-UW7VQ3S6.js +148 -0
- package/dist/chunk-XE4LLC7Z.js +1067 -0
- package/dist/chunk-ZAW6F3YT.js +172 -0
- package/dist/index.cjs +735 -25
- package/dist/index.d.cts +373 -3
- package/dist/index.d.ts +373 -3
- package/dist/index.js +59 -3
- package/dist/live/client.cjs +173 -0
- package/dist/live/client.d.cts +132 -0
- package/dist/live/client.d.ts +132 -0
- package/dist/live/client.js +9 -0
- package/dist/native/index.d.cts +5 -2
- package/dist/native/index.d.ts +5 -2
- package/dist/native/index.js +15 -11
- package/dist/{organicParticlesShaders-DIva0vBO.d.ts → organicParticlesShaders-seZq7jYX.d.cts} +1 -421
- package/dist/{organicParticlesShaders-DIva0vBO.d.cts → organicParticlesShaders-seZq7jYX.d.ts} +1 -421
- package/dist/particlesConfig-0YbdPOdY.d.cts +223 -0
- package/dist/particlesConfig-0YbdPOdY.d.ts +223 -0
- package/dist/react/conversation.cjs +789 -0
- package/dist/react/conversation.d.cts +134 -0
- package/dist/react/conversation.d.ts +134 -0
- package/dist/react/conversation.js +13 -0
- package/dist/react/index.cjs +374 -36
- package/dist/react/index.d.cts +10 -141
- package/dist/react/index.d.ts +10 -141
- package/dist/react/index.js +23 -770
- package/dist/react/visuals.cjs +1048 -0
- package/dist/react/visuals.d.cts +70 -0
- package/dist/react/visuals.d.ts +70 -0
- package/dist/react/visuals.js +10 -0
- package/dist/route/agent.cjs +1038 -85
- package/dist/route/agent.js +3 -3
- package/dist/route/audio-tags.cjs +1038 -85
- package/dist/route/audio-tags.js +3 -3
- package/dist/route/index.cjs +1042 -85
- package/dist/route/index.d.cts +19 -3
- package/dist/route/index.d.ts +19 -3
- package/dist/route/index.js +3 -3
- package/dist/route/music.cjs +1038 -85
- package/dist/route/music.js +3 -3
- package/dist/route/op.cjs +2118 -0
- package/dist/route/op.d.cts +43 -0
- package/dist/route/op.d.ts +43 -0
- package/dist/route/op.js +27 -0
- package/dist/route/signed-url.cjs +1038 -85
- package/dist/route/signed-url.js +3 -3
- package/dist/route/tts-preview.cjs +1038 -85
- package/dist/route/tts-preview.d.cts +6 -4
- package/dist/route/tts-preview.d.ts +6 -4
- package/dist/route/tts-preview.js +3 -3
- package/dist/server.cjs +1038 -85
- package/dist/server.d.cts +40 -8
- package/dist/server.d.ts +40 -8
- package/dist/server.js +2 -2
- package/dist/types-Bfhu-aQH.d.cts +199 -0
- package/dist/types-Bfhu-aQH.d.ts +199 -0
- package/dist/{types-W9y6eMj3.d.ts → types-CadjTXNH.d.cts} +193 -12
- package/dist/{types-W9y6eMj3.d.cts → types-CadjTXNH.d.ts} +193 -12
- package/package.json +21 -1
- package/dist/chunk-B4JC3DK2.js +0 -529
- package/dist/chunk-O362EJQR.js +0 -524
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/react/conversation.ts
|
|
21
|
+
var conversation_exports = {};
|
|
22
|
+
__export(conversation_exports, {
|
|
23
|
+
ShogoVoiceProvider: () => ShogoVoiceProvider,
|
|
24
|
+
useLiveVoiceConversation: () => useLiveVoiceConversation,
|
|
25
|
+
useVoiceConversation: () => useVoiceConversation
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(conversation_exports);
|
|
28
|
+
|
|
29
|
+
// src/react/useVoiceConversation.ts
|
|
30
|
+
var import_react = require("react");
|
|
31
|
+
var import_react2 = require("@elevenlabs/react");
|
|
32
|
+
|
|
33
|
+
// src/shared/postJson.ts
|
|
34
|
+
function createPostJson({
|
|
35
|
+
authHeaders,
|
|
36
|
+
fetchCredentials
|
|
37
|
+
}) {
|
|
38
|
+
return async function postJson(path, body) {
|
|
39
|
+
return fetch(path, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: { "content-type": "application/json", ...authHeaders() },
|
|
42
|
+
credentials: fetchCredentials,
|
|
43
|
+
body: JSON.stringify(body)
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/shared/memory.ts
|
|
49
|
+
function createMemoryAddTool({
|
|
50
|
+
postJson,
|
|
51
|
+
memoryAddPath
|
|
52
|
+
}) {
|
|
53
|
+
return async (params) => {
|
|
54
|
+
const fact = typeof params.fact === "string" ? params.fact : "";
|
|
55
|
+
if (!fact.trim()) return "Memory save failed: empty fact.";
|
|
56
|
+
try {
|
|
57
|
+
const res = await postJson(memoryAddPath, { fact });
|
|
58
|
+
if (!res.ok) return "Failed to save memory.";
|
|
59
|
+
return "Memory saved.";
|
|
60
|
+
} catch {
|
|
61
|
+
return "Memory save failed.";
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function createMemoryContextInjector({
|
|
66
|
+
postJson,
|
|
67
|
+
memoryRetrievePath,
|
|
68
|
+
conversationRef,
|
|
69
|
+
enabled,
|
|
70
|
+
lastInjectedRef
|
|
71
|
+
}) {
|
|
72
|
+
return async function inject(userText) {
|
|
73
|
+
if (!enabled) return;
|
|
74
|
+
try {
|
|
75
|
+
const res = await postJson(memoryRetrievePath, { query: userText, limit: 4 });
|
|
76
|
+
if (!res.ok) return;
|
|
77
|
+
const data = await res.json();
|
|
78
|
+
const results = data.results ?? [];
|
|
79
|
+
if (!results.length) return;
|
|
80
|
+
const lines = results.map((r) => `- ${r.chunk.trim().replace(/\s+/g, " ").slice(0, 200)}`).join("\n");
|
|
81
|
+
if (lines === lastInjectedRef.current) return;
|
|
82
|
+
lastInjectedRef.current = lines;
|
|
83
|
+
const payload = `Relevant memory about this user:
|
|
84
|
+
${lines}`;
|
|
85
|
+
conversationRef.current?.sendContextualUpdate(payload);
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/audioTags.ts
|
|
92
|
+
var AUDIO_TAGS = [
|
|
93
|
+
// Emotions
|
|
94
|
+
{ tag: "happy", group: "emotion", label: "Happy", description: "Warm, upbeat delivery.", example: "[happy] Oh, I\u2019m so glad you stopped by." },
|
|
95
|
+
{ tag: "sad", group: "emotion", label: "Sad", description: "Subdued, heavy-hearted tone.", example: "[sad] I wish things had gone differently." },
|
|
96
|
+
{ tag: "excited", group: "emotion", label: "Excited", description: "Energized, eager delivery.", example: "[excited] Wait until you hear this!" },
|
|
97
|
+
{ tag: "curious", group: "emotion", label: "Curious", description: "Inquisitive, rising inflection.", example: "[curious] Hmm, what did they look like?" },
|
|
98
|
+
{ tag: "sarcastic", group: "emotion", label: "Sarcastic", description: "Dry, ironic tone.", example: "[sarcastic] Oh, fantastic. Another Monday." },
|
|
99
|
+
{ tag: "angry", group: "emotion", label: "Angry", description: "Sharp, irritated edge.", example: "[angry] I told you not to touch that." },
|
|
100
|
+
{ tag: "nervous", group: "emotion", label: "Nervous", description: "Uneasy, faltering delivery.", example: "[nervous] I\u2014 I didn\u2019t think you\u2019d actually come." },
|
|
101
|
+
{ tag: "confident", group: "emotion", label: "Confident", description: "Grounded, assured delivery.", example: "[confident] Trust me. I\u2019ve done this before." },
|
|
102
|
+
// Delivery
|
|
103
|
+
{ tag: "whispers", group: "delivery", label: "Whispers", description: "Hushed, close-mic delivery.", example: "[whispers] Don\u2019t say it too loud." },
|
|
104
|
+
{ tag: "shouts", group: "delivery", label: "Shouts", description: "Loud, projected delivery.", example: "[shouts] Look out behind you!" },
|
|
105
|
+
{ tag: "softly", group: "delivery", label: "Softly", description: "Gentle, quiet tone.", example: "[softly] It\u2019s okay. I\u2019m here." },
|
|
106
|
+
{ tag: "slowly", group: "delivery", label: "Slowly", description: "Deliberate pacing.", example: "[slowly] Tell me\u2026 exactly\u2026 what happened." },
|
|
107
|
+
{ tag: "quickly", group: "delivery", label: "Quickly", description: "Rapid, urgent pacing.", example: "[quickly] We need to go, right now." },
|
|
108
|
+
// Reactions (non-verbal sounds)
|
|
109
|
+
{ tag: "laughs", group: "reaction", label: "Laughs", description: "A genuine laugh.", example: "That\u2019s ridiculous [laughs]." },
|
|
110
|
+
{ tag: "chuckles", group: "reaction", label: "Chuckles", description: "A small, warm chuckle.", example: "[chuckles] You always say that." },
|
|
111
|
+
{ tag: "sighs", group: "reaction", label: "Sighs", description: "An audible sigh.", example: "[sighs] Alright, fine. Let\u2019s do it." },
|
|
112
|
+
{ tag: "gasps", group: "reaction", label: "Gasps", description: "A sharp inhale of surprise.", example: "[gasps] You didn\u2019t!" },
|
|
113
|
+
{ tag: "hesitates", group: "reaction", label: "Hesitates", description: "A pause with uncertainty.", example: "[hesitates] I\u2019m not sure I should say." },
|
|
114
|
+
{ tag: "clears throat", group: "reaction", label: "Clears throat", description: "Throat-clearing beat.", example: "[clears throat] As I was saying\u2026" }
|
|
115
|
+
];
|
|
116
|
+
var KNOWN_TAG_SET = new Set(AUDIO_TAGS.map((t) => t.tag.toLowerCase()));
|
|
117
|
+
function stripAudioTags(text) {
|
|
118
|
+
if (!text) return "";
|
|
119
|
+
return text.replace(/\[([a-z][a-z ]{0,24})\]/gi, (match, inner) => {
|
|
120
|
+
const key = inner.trim().toLowerCase();
|
|
121
|
+
return KNOWN_TAG_SET.has(key) ? "" : match;
|
|
122
|
+
}).replace(/[ \t]{2,}/g, " ").replace(/\s+([,.!?;:])/g, "$1").trim();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/shared/transcript.ts
|
|
126
|
+
function createTranscriptDisconnectHandler({
|
|
127
|
+
isRestartingRef,
|
|
128
|
+
weStartedSessionRef,
|
|
129
|
+
transcriptRef,
|
|
130
|
+
onTranscript,
|
|
131
|
+
transcriptIngestPath,
|
|
132
|
+
postJson
|
|
133
|
+
}) {
|
|
134
|
+
return function handleTranscriptOnDisconnect() {
|
|
135
|
+
if (isRestartingRef.current) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (!weStartedSessionRef.current) {
|
|
139
|
+
transcriptRef.current = [];
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
weStartedSessionRef.current = false;
|
|
143
|
+
const transcript = transcriptRef.current.join("\n");
|
|
144
|
+
transcriptRef.current = [];
|
|
145
|
+
if (onTranscript) {
|
|
146
|
+
void onTranscript(transcript);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const trimmed = transcript.trim();
|
|
150
|
+
if (trimmed.length < 20) return;
|
|
151
|
+
void postJson(transcriptIngestPath, { transcript: trimmed }).catch(() => {
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function appendTranscriptLine(msg, { transcriptRef, weStartedSessionRef }) {
|
|
156
|
+
if (!msg?.message || !msg?.source) return;
|
|
157
|
+
if (!weStartedSessionRef.current) return;
|
|
158
|
+
const speaker = msg.source === "user" ? "User" : "Agent";
|
|
159
|
+
const cleaned = msg.source === "user" ? msg.message : stripAudioTags(msg.message);
|
|
160
|
+
if (!cleaned) return;
|
|
161
|
+
transcriptRef.current.push(`${speaker}: ${cleaned}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/shared/sessionPayload.ts
|
|
165
|
+
function sanitizeDynamicVariables(raw) {
|
|
166
|
+
if (raw == null) return {};
|
|
167
|
+
const out = {};
|
|
168
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
169
|
+
if (v == null) continue;
|
|
170
|
+
out[k] = typeof v === "string" ? v : String(v);
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
function buildSessionPayload(opts) {
|
|
175
|
+
const dynamicVariables = {
|
|
176
|
+
...sanitizeDynamicVariables(opts.dynamicVariables),
|
|
177
|
+
character_name: opts.characterName,
|
|
178
|
+
user_context: opts.userContext
|
|
179
|
+
};
|
|
180
|
+
if (typeof opts.conversationId === "string" && opts.conversationId.length > 0) {
|
|
181
|
+
dynamicVariables.conversation_id = opts.conversationId;
|
|
182
|
+
}
|
|
183
|
+
const payload = {
|
|
184
|
+
signedUrl: opts.signedUrl,
|
|
185
|
+
dynamicVariables
|
|
186
|
+
};
|
|
187
|
+
const agentOverrides = {};
|
|
188
|
+
if (opts.suppressFirstMessage) {
|
|
189
|
+
agentOverrides.firstMessage = "";
|
|
190
|
+
}
|
|
191
|
+
if (typeof opts.agentPromptOverride === "string" && opts.agentPromptOverride.length > 0) {
|
|
192
|
+
agentOverrides.prompt = { prompt: opts.agentPromptOverride };
|
|
193
|
+
}
|
|
194
|
+
if (Object.keys(agentOverrides).length > 0) {
|
|
195
|
+
payload.overrides = { agent: agentOverrides };
|
|
196
|
+
}
|
|
197
|
+
return payload;
|
|
198
|
+
}
|
|
199
|
+
async function fetchSignedUrl({
|
|
200
|
+
path,
|
|
201
|
+
fetchCredentials,
|
|
202
|
+
authHeaders
|
|
203
|
+
}) {
|
|
204
|
+
const res = await fetch(path, {
|
|
205
|
+
credentials: fetchCredentials,
|
|
206
|
+
headers: authHeaders()
|
|
207
|
+
});
|
|
208
|
+
if (!res.ok) throw new Error(`Signed URL request failed: ${res.status}`);
|
|
209
|
+
return await res.json();
|
|
210
|
+
}
|
|
211
|
+
function withProjectQuery(signedUrlPath, params) {
|
|
212
|
+
const tail = [];
|
|
213
|
+
if (typeof params.projectId === "string" && params.projectId.length > 0) {
|
|
214
|
+
tail.push(`projectId=${encodeURIComponent(params.projectId)}`);
|
|
215
|
+
}
|
|
216
|
+
if (typeof params.agentName === "string" && params.agentName.length > 0) {
|
|
217
|
+
tail.push(`agentName=${encodeURIComponent(params.agentName)}`);
|
|
218
|
+
}
|
|
219
|
+
if (tail.length === 0) return signedUrlPath;
|
|
220
|
+
const sep = signedUrlPath.includes("?") ? "&" : "?";
|
|
221
|
+
return `${signedUrlPath}${sep}${tail.join("&")}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/react/mic-access.ts
|
|
225
|
+
var MIC_BLOCKED_MESSAGE = "Microphone access is blocked. Allow it in your system or browser settings (on macOS: System Settings > Privacy & Security > Microphone), then try again.";
|
|
226
|
+
var MIC_NOT_FOUND_MESSAGE = "No microphone was found. Connect one and try again.";
|
|
227
|
+
async function requestMicStream(constraints = { audio: true }) {
|
|
228
|
+
const desktop = globalThis.shogoDesktop;
|
|
229
|
+
if (desktop?.isDesktop && desktop.ensureMicAccess) {
|
|
230
|
+
const access = await desktop.ensureMicAccess().catch(() => "granted");
|
|
231
|
+
if (access !== "granted") throw new Error(MIC_BLOCKED_MESSAGE);
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
return await navigator.mediaDevices.getUserMedia(constraints);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
const name = err?.name;
|
|
237
|
+
if (name === "NotAllowedError" || name === "SecurityError") throw new Error(MIC_BLOCKED_MESSAGE);
|
|
238
|
+
if (name === "NotFoundError" || name === "OverconstrainedError") throw new Error(MIC_NOT_FOUND_MESSAGE);
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/react/useVoiceConversation.ts
|
|
244
|
+
function useVoiceConversation(options) {
|
|
245
|
+
const {
|
|
246
|
+
characterName,
|
|
247
|
+
signedUrlPath = "/api/voice/signed-url",
|
|
248
|
+
memoryAddPath = "/api/memory/add",
|
|
249
|
+
memoryRetrievePath = "/api/memory/retrieve",
|
|
250
|
+
transcriptIngestPath = "/api/memory/ingest",
|
|
251
|
+
autoInjectMemory = true,
|
|
252
|
+
includeMemoryTool = true,
|
|
253
|
+
clientTools = {},
|
|
254
|
+
onTranscript,
|
|
255
|
+
shogoApiKey,
|
|
256
|
+
projectId,
|
|
257
|
+
agentName,
|
|
258
|
+
fetchCredentials = shogoApiKey ? "omit" : "same-origin",
|
|
259
|
+
onError,
|
|
260
|
+
onMessage,
|
|
261
|
+
conversationId: optionConversationId,
|
|
262
|
+
dynamicVariables
|
|
263
|
+
} = options;
|
|
264
|
+
const authHeaders = (0, import_react.useCallback)(() => {
|
|
265
|
+
return shogoApiKey ? { authorization: `Bearer ${shogoApiKey}` } : {};
|
|
266
|
+
}, [shogoApiKey]);
|
|
267
|
+
const resolvedSignedUrlPath = withProjectQuery(signedUrlPath, { projectId, agentName });
|
|
268
|
+
const transcriptRef = (0, import_react.useRef)([]);
|
|
269
|
+
const lastInjectedRef = (0, import_react.useRef)("");
|
|
270
|
+
const weStartedSessionRef = (0, import_react.useRef)(false);
|
|
271
|
+
const [convaiConversationId, setConvaiConversationId] = (0, import_react.useState)(null);
|
|
272
|
+
const isRestartingRef = (0, import_react.useRef)(false);
|
|
273
|
+
const conversationRef = (0, import_react.useRef)(null);
|
|
274
|
+
const postJson = (0, import_react.useCallback)(
|
|
275
|
+
createPostJson({ authHeaders, fetchCredentials }),
|
|
276
|
+
[fetchCredentials, authHeaders]
|
|
277
|
+
);
|
|
278
|
+
const injectMemoryContext = (0, import_react.useCallback)(
|
|
279
|
+
createMemoryContextInjector({
|
|
280
|
+
postJson,
|
|
281
|
+
memoryRetrievePath,
|
|
282
|
+
conversationRef,
|
|
283
|
+
enabled: autoInjectMemory,
|
|
284
|
+
lastInjectedRef
|
|
285
|
+
}),
|
|
286
|
+
[autoInjectMemory, memoryRetrievePath, postJson]
|
|
287
|
+
);
|
|
288
|
+
const handleTranscriptOnDisconnect = (0, import_react.useCallback)(
|
|
289
|
+
createTranscriptDisconnectHandler({
|
|
290
|
+
isRestartingRef,
|
|
291
|
+
weStartedSessionRef,
|
|
292
|
+
transcriptRef,
|
|
293
|
+
onTranscript,
|
|
294
|
+
transcriptIngestPath,
|
|
295
|
+
postJson
|
|
296
|
+
}),
|
|
297
|
+
[onTranscript, postJson, transcriptIngestPath]
|
|
298
|
+
);
|
|
299
|
+
const builtInTools = includeMemoryTool ? { add_memory: createMemoryAddTool({ postJson, memoryAddPath }) } : {};
|
|
300
|
+
const mergedClientTools = { ...builtInTools, ...clientTools };
|
|
301
|
+
const conversation = (0, import_react2.useConversation)({
|
|
302
|
+
clientTools: mergedClientTools,
|
|
303
|
+
onConnect: (info) => {
|
|
304
|
+
const id = info?.conversationId;
|
|
305
|
+
if (typeof id === "string" && id.length > 0) {
|
|
306
|
+
setConvaiConversationId(id);
|
|
307
|
+
}
|
|
308
|
+
if (!isRestartingRef.current) {
|
|
309
|
+
transcriptRef.current = [];
|
|
310
|
+
lastInjectedRef.current = "";
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
onDisconnect: () => {
|
|
314
|
+
handleTranscriptOnDisconnect();
|
|
315
|
+
if (!isRestartingRef.current) {
|
|
316
|
+
setConvaiConversationId(null);
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
onError: (e) => {
|
|
320
|
+
onError?.(e);
|
|
321
|
+
},
|
|
322
|
+
onMessage: (m) => {
|
|
323
|
+
const msg = m;
|
|
324
|
+
if (!msg?.message || !msg?.source) return;
|
|
325
|
+
appendTranscriptLine(msg, { transcriptRef, weStartedSessionRef });
|
|
326
|
+
onMessage?.({ source: msg.source, message: msg.message });
|
|
327
|
+
if (autoInjectMemory && msg.source === "user" && msg.message.trim().length >= 3 && weStartedSessionRef.current) {
|
|
328
|
+
void injectMemoryContext(msg.message);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
const { status, isSpeaking, isListening, isMuted, setMuted, startSession, endSession } = conversation;
|
|
333
|
+
conversationRef.current = conversation;
|
|
334
|
+
(0, import_react.useEffect)(() => {
|
|
335
|
+
const onHide = () => {
|
|
336
|
+
if (!weStartedSessionRef.current || transcriptRef.current.length === 0) return;
|
|
337
|
+
const transcript = transcriptRef.current.join("\n").trim();
|
|
338
|
+
transcriptRef.current = [];
|
|
339
|
+
if (transcript.length < 20) return;
|
|
340
|
+
try {
|
|
341
|
+
const blob = new Blob([JSON.stringify({ transcript })], { type: "application/json" });
|
|
342
|
+
navigator.sendBeacon(transcriptIngestPath, blob);
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
window.addEventListener("pagehide", onHide);
|
|
347
|
+
return () => window.removeEventListener("pagehide", onHide);
|
|
348
|
+
}, [transcriptIngestPath]);
|
|
349
|
+
const startInternal = (0, import_react.useCallback)(
|
|
350
|
+
async (opts) => {
|
|
351
|
+
if (status === "connected" || status === "connecting") {
|
|
352
|
+
try {
|
|
353
|
+
endSession();
|
|
354
|
+
await new Promise((r) => setTimeout(r, 120));
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const micStream = await requestMicStream({ audio: true });
|
|
359
|
+
micStream.getTracks().forEach((track) => track.stop());
|
|
360
|
+
const data = await fetchSignedUrl({
|
|
361
|
+
path: resolvedSignedUrlPath,
|
|
362
|
+
fetchCredentials,
|
|
363
|
+
authHeaders
|
|
364
|
+
});
|
|
365
|
+
const ctx = data.userContext || "No prior memories yet.";
|
|
366
|
+
weStartedSessionRef.current = true;
|
|
367
|
+
const sessionPayload = buildSessionPayload({
|
|
368
|
+
signedUrl: data.signedUrl,
|
|
369
|
+
characterName,
|
|
370
|
+
userContext: ctx,
|
|
371
|
+
agentPromptOverride: data.agentPromptOverride,
|
|
372
|
+
suppressFirstMessage: opts?.suppressFirstMessage,
|
|
373
|
+
conversationId: optionConversationId,
|
|
374
|
+
dynamicVariables
|
|
375
|
+
});
|
|
376
|
+
await startSession(sessionPayload);
|
|
377
|
+
},
|
|
378
|
+
[
|
|
379
|
+
status,
|
|
380
|
+
endSession,
|
|
381
|
+
resolvedSignedUrlPath,
|
|
382
|
+
fetchCredentials,
|
|
383
|
+
dynamicVariables,
|
|
384
|
+
startSession,
|
|
385
|
+
characterName,
|
|
386
|
+
authHeaders,
|
|
387
|
+
optionConversationId
|
|
388
|
+
]
|
|
389
|
+
);
|
|
390
|
+
const start = (0, import_react.useCallback)(
|
|
391
|
+
async (options2) => {
|
|
392
|
+
await startInternal(options2);
|
|
393
|
+
},
|
|
394
|
+
[startInternal]
|
|
395
|
+
);
|
|
396
|
+
const end = (0, import_react.useCallback)(() => {
|
|
397
|
+
endSession();
|
|
398
|
+
}, [endSession]);
|
|
399
|
+
const restart = (0, import_react.useCallback)(
|
|
400
|
+
async (options2) => {
|
|
401
|
+
const suppress = options2?.suppressFirstMessage ?? true;
|
|
402
|
+
isRestartingRef.current = true;
|
|
403
|
+
try {
|
|
404
|
+
try {
|
|
405
|
+
endSession();
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
await new Promise((r) => setTimeout(r, 120));
|
|
409
|
+
await startInternal({ suppressFirstMessage: suppress });
|
|
410
|
+
} finally {
|
|
411
|
+
isRestartingRef.current = false;
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
[endSession, startInternal]
|
|
415
|
+
);
|
|
416
|
+
const sendContextualUpdate = (0, import_react.useCallback)((text) => {
|
|
417
|
+
conversationRef.current?.sendContextualUpdate(text);
|
|
418
|
+
}, []);
|
|
419
|
+
const sendUserMessage = (0, import_react.useCallback)((text) => {
|
|
420
|
+
const ref = conversationRef.current;
|
|
421
|
+
if (!ref) return;
|
|
422
|
+
if (typeof ref.sendUserMessage === "function") {
|
|
423
|
+
ref.sendUserMessage(text);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
ref.sendContextualUpdate(text);
|
|
427
|
+
try {
|
|
428
|
+
ref.sendUserActivity?.();
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
431
|
+
}, []);
|
|
432
|
+
const sendUserActivity = (0, import_react.useCallback)(() => {
|
|
433
|
+
conversationRef.current?.sendUserActivity?.();
|
|
434
|
+
}, []);
|
|
435
|
+
const getOutputByteFrequencyData = (0, import_react.useCallback)(() => {
|
|
436
|
+
try {
|
|
437
|
+
const fn = conversation.getOutputByteFrequencyData;
|
|
438
|
+
return fn ? fn() : null;
|
|
439
|
+
} catch {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
}, [conversation]);
|
|
443
|
+
return {
|
|
444
|
+
start,
|
|
445
|
+
end,
|
|
446
|
+
restart,
|
|
447
|
+
status,
|
|
448
|
+
isSpeaking,
|
|
449
|
+
isListening,
|
|
450
|
+
isMuted,
|
|
451
|
+
setMuted,
|
|
452
|
+
getOutputByteFrequencyData,
|
|
453
|
+
sendContextualUpdate,
|
|
454
|
+
sendUserMessage,
|
|
455
|
+
sendUserActivity,
|
|
456
|
+
conversationId: optionConversationId ?? convaiConversationId,
|
|
457
|
+
convaiConversationId
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/react/useLiveVoiceConversation.ts
|
|
462
|
+
var import_react3 = require("react");
|
|
463
|
+
function parseEvent(data) {
|
|
464
|
+
try {
|
|
465
|
+
const text = typeof data === "string" ? data : String(data);
|
|
466
|
+
const event = JSON.parse(text);
|
|
467
|
+
return event && typeof event === "object" ? event : null;
|
|
468
|
+
} catch {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function waitForIceGathering(pc) {
|
|
473
|
+
if (pc.iceGatheringState === "complete") return Promise.resolve();
|
|
474
|
+
return new Promise((resolve) => {
|
|
475
|
+
const onStateChange = () => {
|
|
476
|
+
if (pc.iceGatheringState === "complete") {
|
|
477
|
+
pc.removeEventListener("icegatheringstatechange", onStateChange);
|
|
478
|
+
resolve();
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
pc.addEventListener("icegatheringstatechange", onStateChange);
|
|
482
|
+
window.setTimeout(() => {
|
|
483
|
+
pc.removeEventListener("icegatheringstatechange", onStateChange);
|
|
484
|
+
resolve();
|
|
485
|
+
}, 2e3);
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
function splitContext(text) {
|
|
489
|
+
const chunks = [];
|
|
490
|
+
for (let offset = 0; offset < text.length; offset += 1800) {
|
|
491
|
+
chunks.push(text.slice(offset, offset + 1800));
|
|
492
|
+
}
|
|
493
|
+
return chunks.length ? chunks : [""];
|
|
494
|
+
}
|
|
495
|
+
var TRANSCRIPT_HISTORY_MAX = 30;
|
|
496
|
+
function useLiveVoiceConversation(options) {
|
|
497
|
+
const { mintSession } = options;
|
|
498
|
+
const [status, setStatus] = (0, import_react3.useState)("disconnected");
|
|
499
|
+
const [isSpeaking, setIsSpeaking] = (0, import_react3.useState)(false);
|
|
500
|
+
const [isListening, setIsListening] = (0, import_react3.useState)(false);
|
|
501
|
+
const [isMuted, setIsMuted] = (0, import_react3.useState)(false);
|
|
502
|
+
const [sessionId, setSessionId] = (0, import_react3.useState)(null);
|
|
503
|
+
const optionsRef = (0, import_react3.useRef)(options);
|
|
504
|
+
optionsRef.current = options;
|
|
505
|
+
const pcRef = (0, import_react3.useRef)(null);
|
|
506
|
+
const dataChannelRef = (0, import_react3.useRef)(null);
|
|
507
|
+
const mediaStreamRef = (0, import_react3.useRef)(null);
|
|
508
|
+
const audioRef = (0, import_react3.useRef)(null);
|
|
509
|
+
const analyserRef = (0, import_react3.useRef)(null);
|
|
510
|
+
const frequencyRef = (0, import_react3.useRef)(null);
|
|
511
|
+
const audioContextRef = (0, import_react3.useRef)(null);
|
|
512
|
+
const speakingFrameRef = (0, import_react3.useRef)(null);
|
|
513
|
+
const inputTranscriptRef = (0, import_react3.useRef)("");
|
|
514
|
+
const outputTranscriptRef = (0, import_react3.useRef)("");
|
|
515
|
+
const historyRef = (0, import_react3.useRef)([]);
|
|
516
|
+
const closingRef = (0, import_react3.useRef)(false);
|
|
517
|
+
const mutedRef = (0, import_react3.useRef)(false);
|
|
518
|
+
const emit = (0, import_react3.useCallback)((source, message) => {
|
|
519
|
+
const text = message.trim();
|
|
520
|
+
if (!text) return;
|
|
521
|
+
const history = historyRef.current;
|
|
522
|
+
history.push({ role: source === "user" ? "user" : "assistant", text });
|
|
523
|
+
if (history.length > TRANSCRIPT_HISTORY_MAX) history.splice(0, history.length - TRANSCRIPT_HISTORY_MAX);
|
|
524
|
+
optionsRef.current.onMessage?.({ source, message: text });
|
|
525
|
+
}, []);
|
|
526
|
+
const flushTranscripts = (0, import_react3.useCallback)(() => {
|
|
527
|
+
const input = inputTranscriptRef.current.trim();
|
|
528
|
+
const output = outputTranscriptRef.current.trim();
|
|
529
|
+
inputTranscriptRef.current = "";
|
|
530
|
+
outputTranscriptRef.current = "";
|
|
531
|
+
if (input) emit("user", input);
|
|
532
|
+
if (output) emit("agent", output);
|
|
533
|
+
}, [emit]);
|
|
534
|
+
const sendEvent = (0, import_react3.useCallback)((event) => {
|
|
535
|
+
const channel = dataChannelRef.current;
|
|
536
|
+
if (channel?.readyState === "open") channel.send(JSON.stringify(event));
|
|
537
|
+
}, []);
|
|
538
|
+
const updateSpeaking = (0, import_react3.useCallback)(() => {
|
|
539
|
+
const analyser = analyserRef.current;
|
|
540
|
+
if (analyser) {
|
|
541
|
+
const data = frequencyRef.current ?? new Uint8Array(analyser.frequencyBinCount);
|
|
542
|
+
frequencyRef.current = data;
|
|
543
|
+
analyser.getByteFrequencyData(data);
|
|
544
|
+
let sum = 0;
|
|
545
|
+
for (const value of data) sum += value;
|
|
546
|
+
setIsSpeaking(data.length > 0 && sum / data.length > 8);
|
|
547
|
+
}
|
|
548
|
+
if (pcRef.current) speakingFrameRef.current = requestAnimationFrame(updateSpeaking);
|
|
549
|
+
}, []);
|
|
550
|
+
const handleEvent = (0, import_react3.useCallback)(async (event) => {
|
|
551
|
+
const nested = event.type === "response.event" && event.event ? event.event : event;
|
|
552
|
+
const type = nested.type ?? "";
|
|
553
|
+
if (type === "session.started" || type === "session.created") {
|
|
554
|
+
setStatus("connected");
|
|
555
|
+
setIsListening(!mutedRef.current);
|
|
556
|
+
const id = typeof nested.session?.id === "string" ? nested.session.id : null;
|
|
557
|
+
if (id) setSessionId(id);
|
|
558
|
+
}
|
|
559
|
+
if (type === "session.closed") {
|
|
560
|
+
flushTranscripts();
|
|
561
|
+
setStatus("disconnected");
|
|
562
|
+
setIsListening(false);
|
|
563
|
+
}
|
|
564
|
+
if (type === "error") {
|
|
565
|
+
optionsRef.current.onError?.(new Error(event.error?.message || "GPT-Live error"));
|
|
566
|
+
}
|
|
567
|
+
if (type === "session.input_transcript.delta" || type === "input_audio_transcription.delta") {
|
|
568
|
+
inputTranscriptRef.current += event.delta ?? "";
|
|
569
|
+
}
|
|
570
|
+
if (type === "session.output_transcript.delta" || type === "response.audio_transcript.delta") {
|
|
571
|
+
outputTranscriptRef.current += event.delta ?? "";
|
|
572
|
+
}
|
|
573
|
+
if (type === "session.input_transcript.done" || type === "input_audio_transcription.completed" || type === "session.output_transcript.done" || type === "response.audio_transcript.done" || type === "response.done" || type === "response.completed") {
|
|
574
|
+
flushTranscripts();
|
|
575
|
+
}
|
|
576
|
+
if (type === "session.delegation.created" && nested.delegation?.target !== "responses" && typeof nested.delegation?.id === "string") {
|
|
577
|
+
const onDelegation = optionsRef.current.onDelegation;
|
|
578
|
+
if (!onDelegation) return;
|
|
579
|
+
const delegationId = nested.delegation.id;
|
|
580
|
+
flushTranscripts();
|
|
581
|
+
let result;
|
|
582
|
+
try {
|
|
583
|
+
result = await onDelegation({ delegationId, transcript: [...historyRef.current] });
|
|
584
|
+
} catch (error) {
|
|
585
|
+
optionsRef.current.onError?.(error);
|
|
586
|
+
result = "That request failed before it could finish. Nothing was confirmed.";
|
|
587
|
+
}
|
|
588
|
+
const text = typeof result === "string" ? result.trim() : "";
|
|
589
|
+
if (!text) return;
|
|
590
|
+
for (const content of splitContext(text)) {
|
|
591
|
+
sendEvent({
|
|
592
|
+
type: "session.commentary.append",
|
|
593
|
+
delegation_id: delegationId,
|
|
594
|
+
content
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const item = nested.item;
|
|
600
|
+
if (type === "response.output_item.done" && item?.type === "function_call") {
|
|
601
|
+
const tool = optionsRef.current.clientTools?.[item.name ?? ""];
|
|
602
|
+
if (!tool || !item.call_id) return;
|
|
603
|
+
let args = {};
|
|
604
|
+
try {
|
|
605
|
+
const parsed = JSON.parse(item.arguments || "{}");
|
|
606
|
+
if (parsed && typeof parsed === "object") args = parsed;
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
609
|
+
let output;
|
|
610
|
+
try {
|
|
611
|
+
output = String(await tool(args));
|
|
612
|
+
} catch (error) {
|
|
613
|
+
output = `Tool failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
614
|
+
}
|
|
615
|
+
sendEvent({
|
|
616
|
+
type: "response.item.create",
|
|
617
|
+
item: {
|
|
618
|
+
type: "function_call_output",
|
|
619
|
+
call_id: item.call_id,
|
|
620
|
+
output
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
sendEvent({ type: "response.create" });
|
|
624
|
+
}
|
|
625
|
+
}, [flushTranscripts, sendEvent]);
|
|
626
|
+
const end = (0, import_react3.useCallback)(() => {
|
|
627
|
+
closingRef.current = true;
|
|
628
|
+
flushTranscripts();
|
|
629
|
+
sendEvent({ type: "session.close" });
|
|
630
|
+
dataChannelRef.current?.close();
|
|
631
|
+
pcRef.current?.close();
|
|
632
|
+
mediaStreamRef.current?.getTracks().forEach((track) => track.stop());
|
|
633
|
+
audioRef.current?.pause();
|
|
634
|
+
if (speakingFrameRef.current !== null) cancelAnimationFrame(speakingFrameRef.current);
|
|
635
|
+
speakingFrameRef.current = null;
|
|
636
|
+
pcRef.current = null;
|
|
637
|
+
dataChannelRef.current = null;
|
|
638
|
+
mediaStreamRef.current = null;
|
|
639
|
+
analyserRef.current = null;
|
|
640
|
+
frequencyRef.current = null;
|
|
641
|
+
audioContextRef.current?.close().catch(() => {
|
|
642
|
+
});
|
|
643
|
+
audioContextRef.current = null;
|
|
644
|
+
setIsSpeaking(false);
|
|
645
|
+
setIsListening(false);
|
|
646
|
+
setSessionId(null);
|
|
647
|
+
setStatus("disconnected");
|
|
648
|
+
}, [flushTranscripts, sendEvent]);
|
|
649
|
+
const startInternal = (0, import_react3.useCallback)(async () => {
|
|
650
|
+
if (status === "connected" || status === "connecting") end();
|
|
651
|
+
closingRef.current = false;
|
|
652
|
+
setStatus("connecting");
|
|
653
|
+
try {
|
|
654
|
+
const stream = await requestMicStream({ audio: true });
|
|
655
|
+
mediaStreamRef.current = stream;
|
|
656
|
+
const pc = new RTCPeerConnection();
|
|
657
|
+
pcRef.current = pc;
|
|
658
|
+
stream.getTracks().forEach((track) => pc.addTrack(track, stream));
|
|
659
|
+
const channel = pc.createDataChannel("oai-events");
|
|
660
|
+
dataChannelRef.current = channel;
|
|
661
|
+
channel.onopen = () => {
|
|
662
|
+
if (!closingRef.current) setIsListening(!mutedRef.current);
|
|
663
|
+
};
|
|
664
|
+
channel.onmessage = (message) => {
|
|
665
|
+
const event = parseEvent(message.data);
|
|
666
|
+
if (event) void handleEvent(event);
|
|
667
|
+
};
|
|
668
|
+
channel.onerror = (event) => optionsRef.current.onError?.(event);
|
|
669
|
+
pc.ontrack = (event) => {
|
|
670
|
+
const remoteStream = event.streams[0];
|
|
671
|
+
if (!remoteStream) return;
|
|
672
|
+
const audio = new Audio();
|
|
673
|
+
audio.autoplay = true;
|
|
674
|
+
audio.srcObject = remoteStream;
|
|
675
|
+
audioRef.current = audio;
|
|
676
|
+
void audio.play().catch(() => {
|
|
677
|
+
});
|
|
678
|
+
try {
|
|
679
|
+
const AudioContextImpl = window.AudioContext || window.webkitAudioContext;
|
|
680
|
+
if (AudioContextImpl) {
|
|
681
|
+
const context = new AudioContextImpl();
|
|
682
|
+
const source = context.createMediaStreamSource(remoteStream);
|
|
683
|
+
const analyser = context.createAnalyser();
|
|
684
|
+
analyser.fftSize = 256;
|
|
685
|
+
source.connect(analyser);
|
|
686
|
+
audioContextRef.current = context;
|
|
687
|
+
analyserRef.current = analyser;
|
|
688
|
+
frequencyRef.current = new Uint8Array(analyser.frequencyBinCount);
|
|
689
|
+
if (speakingFrameRef.current === null) {
|
|
690
|
+
speakingFrameRef.current = requestAnimationFrame(updateSpeaking);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
pc.onconnectionstatechange = () => {
|
|
697
|
+
if (["failed", "disconnected", "closed"].includes(pc.connectionState) && !closingRef.current) {
|
|
698
|
+
optionsRef.current.onError?.(new Error(`GPT-Live connection ${pc.connectionState}`));
|
|
699
|
+
end();
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
const offer = await pc.createOffer();
|
|
703
|
+
await pc.setLocalDescription(offer);
|
|
704
|
+
await waitForIceGathering(pc);
|
|
705
|
+
const localSdp = pc.localDescription?.sdp;
|
|
706
|
+
if (!localSdp) throw new Error("Failed to create a WebRTC offer");
|
|
707
|
+
const answer = await mintSession(localSdp);
|
|
708
|
+
await pc.setRemoteDescription({ type: "answer", sdp: answer.sdp });
|
|
709
|
+
setSessionId(answer.sessionId);
|
|
710
|
+
setStatus("connected");
|
|
711
|
+
} catch (error) {
|
|
712
|
+
end();
|
|
713
|
+
optionsRef.current.onError?.(error);
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
716
|
+
}, [end, handleEvent, mintSession, status, updateSpeaking]);
|
|
717
|
+
const start = (0, import_react3.useCallback)(async () => {
|
|
718
|
+
await startInternal();
|
|
719
|
+
}, [startInternal]);
|
|
720
|
+
const restart = (0, import_react3.useCallback)(async () => {
|
|
721
|
+
end();
|
|
722
|
+
await new Promise((resolve) => window.setTimeout(resolve, 120));
|
|
723
|
+
await startInternal();
|
|
724
|
+
}, [end, startInternal]);
|
|
725
|
+
const setMuted = (0, import_react3.useCallback)((muted) => {
|
|
726
|
+
mutedRef.current = muted;
|
|
727
|
+
mediaStreamRef.current?.getAudioTracks().forEach((track) => {
|
|
728
|
+
track.enabled = !muted;
|
|
729
|
+
});
|
|
730
|
+
sendEvent({ type: muted ? "session.input_audio.mute" : "session.input_audio.unmute" });
|
|
731
|
+
setIsMuted(muted);
|
|
732
|
+
setIsListening(!muted && status === "connected");
|
|
733
|
+
}, [sendEvent, status]);
|
|
734
|
+
const sendContextualUpdate = (0, import_react3.useCallback)((text) => {
|
|
735
|
+
for (const content of splitContext(text)) {
|
|
736
|
+
sendEvent({
|
|
737
|
+
type: "session.thinking.append",
|
|
738
|
+
delegation_id: null,
|
|
739
|
+
content
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}, [sendEvent]);
|
|
743
|
+
const sendUserMessage = (0, import_react3.useCallback)((text) => {
|
|
744
|
+
sendEvent({
|
|
745
|
+
type: "session.commentary.append",
|
|
746
|
+
delegation_id: null,
|
|
747
|
+
content: text
|
|
748
|
+
});
|
|
749
|
+
}, [sendEvent]);
|
|
750
|
+
const sendUserActivity = (0, import_react3.useCallback)(() => {
|
|
751
|
+
sendEvent({ type: "session.input_audio.commit" });
|
|
752
|
+
}, [sendEvent]);
|
|
753
|
+
const getOutputByteFrequencyData = (0, import_react3.useCallback)(() => {
|
|
754
|
+
return frequencyRef.current;
|
|
755
|
+
}, []);
|
|
756
|
+
const getTranscript = (0, import_react3.useCallback)(() => [...historyRef.current], []);
|
|
757
|
+
(0, import_react3.useEffect)(() => () => end(), [end]);
|
|
758
|
+
return {
|
|
759
|
+
start,
|
|
760
|
+
end,
|
|
761
|
+
restart,
|
|
762
|
+
status,
|
|
763
|
+
isSpeaking,
|
|
764
|
+
isListening,
|
|
765
|
+
isMuted,
|
|
766
|
+
setMuted,
|
|
767
|
+
getOutputByteFrequencyData,
|
|
768
|
+
sendContextualUpdate,
|
|
769
|
+
sendUserMessage,
|
|
770
|
+
sendUserActivity,
|
|
771
|
+
getTranscript,
|
|
772
|
+
conversationId: sessionId,
|
|
773
|
+
convaiConversationId: sessionId
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// src/react/ShogoVoiceProvider.tsx
|
|
778
|
+
var import_react4 = require("@elevenlabs/react");
|
|
779
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
780
|
+
function ShogoVoiceProvider(props) {
|
|
781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.ConversationProvider, { children: props.children });
|
|
782
|
+
}
|
|
783
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
784
|
+
0 && (module.exports = {
|
|
785
|
+
ShogoVoiceProvider,
|
|
786
|
+
useLiveVoiceConversation,
|
|
787
|
+
useVoiceConversation
|
|
788
|
+
});
|
|
789
|
+
//# sourceMappingURL=conversation.cjs.map
|