@keyframelabs/elements 0.0.5 → 0.0.6
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 +3 -3
- package/dist/agents/index.d.ts +6 -2
- package/dist/agents/vapi.d.ts +30 -0
- package/dist/index.js +157 -69
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ view.disconnect();
|
|
|
71
71
|
|
|
72
72
|
## Supported agents and real-time LLMs
|
|
73
73
|
|
|
74
|
-
Supports ElevenLabs Agents, Cartesia Line,
|
|
74
|
+
Supports ElevenLabs Agents, Cartesia Line, Gemini Live, and Vapi.
|
|
75
75
|
|
|
76
76
|
For `PersonaEmbed`, this is determined by the values you set in the Keyframe platform dashboard.
|
|
77
77
|
|
|
@@ -145,10 +145,10 @@ type SessionDetails = {
|
|
|
145
145
|
};
|
|
146
146
|
|
|
147
147
|
type VoiceAgentDetails = {
|
|
148
|
-
type: 'gemini' | 'elevenlabs' | 'cartesia';
|
|
148
|
+
type: 'gemini' | 'elevenlabs' | 'cartesia' | 'vapi';
|
|
149
149
|
token?: string; // For gemini, cartesia
|
|
150
150
|
agent_id?: string; // For elevenlabs, cartesia
|
|
151
|
-
signed_url?: string; // For elevenlabs
|
|
151
|
+
signed_url?: string; // For elevenlabs, vapi
|
|
152
152
|
};
|
|
153
153
|
```
|
|
154
154
|
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GeminiLiveAgent, GeminiLiveConfig } from './gemini-live';
|
|
2
2
|
import { ElevenLabsAgent, ElevenLabsConfig } from './elevenlabs';
|
|
3
3
|
import { CartesiaAgent, CartesiaConfig } from './cartesia';
|
|
4
|
+
import { VapiAgent, VapiConfig } from './vapi';
|
|
4
5
|
/**
|
|
5
6
|
* Agent implementations for voice AI platforms.
|
|
6
7
|
*
|
|
@@ -12,9 +13,10 @@ export type { Agent, AgentConfig, AgentEventMap, AgentState } from './types';
|
|
|
12
13
|
export { GeminiLiveAgent, type GeminiLiveConfig };
|
|
13
14
|
export { ElevenLabsAgent, type ElevenLabsConfig };
|
|
14
15
|
export { CartesiaAgent, type CartesiaConfig };
|
|
16
|
+
export { VapiAgent, type VapiConfig };
|
|
15
17
|
export { SAMPLE_RATE, base64ToBytes, bytesToBase64, resamplePcm, createEventEmitter, floatTo16BitPCM } from './audio-utils';
|
|
16
18
|
/** Supported agent types */
|
|
17
|
-
export type AgentType = 'gemini' | 'elevenlabs' | 'cartesia';
|
|
19
|
+
export type AgentType = 'gemini' | 'elevenlabs' | 'cartesia' | 'vapi';
|
|
18
20
|
/** Agent type metadata */
|
|
19
21
|
export interface AgentTypeInfo {
|
|
20
22
|
id: AgentType;
|
|
@@ -28,9 +30,10 @@ export interface AgentConfigMap {
|
|
|
28
30
|
gemini: GeminiLiveConfig;
|
|
29
31
|
elevenlabs: ElevenLabsConfig;
|
|
30
32
|
cartesia: CartesiaConfig;
|
|
33
|
+
vapi: VapiConfig;
|
|
31
34
|
}
|
|
32
35
|
/** Union type of all agent instances */
|
|
33
|
-
export type AnyAgent = GeminiLiveAgent | ElevenLabsAgent | CartesiaAgent;
|
|
36
|
+
export type AnyAgent = GeminiLiveAgent | ElevenLabsAgent | CartesiaAgent | VapiAgent;
|
|
34
37
|
/**
|
|
35
38
|
* Create an agent instance by type.
|
|
36
39
|
*
|
|
@@ -43,6 +46,7 @@ export type AnyAgent = GeminiLiveAgent | ElevenLabsAgent | CartesiaAgent;
|
|
|
43
46
|
export declare function createAgent(type: 'gemini'): GeminiLiveAgent;
|
|
44
47
|
export declare function createAgent(type: 'elevenlabs'): ElevenLabsAgent;
|
|
45
48
|
export declare function createAgent(type: 'cartesia'): CartesiaAgent;
|
|
49
|
+
export declare function createAgent(type: 'vapi'): VapiAgent;
|
|
46
50
|
export declare function createAgent(type: AgentType): AnyAgent;
|
|
47
51
|
/**
|
|
48
52
|
* Get agent type metadata by ID.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AgentConfig } from './types';
|
|
2
|
+
import { BaseAgent } from './base';
|
|
3
|
+
/** Vapi specific configuration */
|
|
4
|
+
export interface VapiConfig extends AgentConfig {
|
|
5
|
+
/** Pre-authenticated WebSocket URL from backend (same concept as ElevenLabs signed_url) */
|
|
6
|
+
signedUrl: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Vapi WebSocket Transport agent implementation.
|
|
10
|
+
*
|
|
11
|
+
* Handles WebSocket connection to Vapi and converts
|
|
12
|
+
* audio responses to events that Persona SDK can consume.
|
|
13
|
+
*/
|
|
14
|
+
export declare class VapiAgent extends BaseAgent {
|
|
15
|
+
protected readonly agentName = "Vapi";
|
|
16
|
+
private vapiSampleRate;
|
|
17
|
+
connect(config: VapiConfig): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Handle binary audio data from Vapi.
|
|
20
|
+
* Vapi sends raw PCM 16-bit little-endian audio.
|
|
21
|
+
*/
|
|
22
|
+
private handleBinaryAudio;
|
|
23
|
+
protected handleParsedMessage(message: unknown): void;
|
|
24
|
+
sendAudio(pcmData: Uint8Array): void;
|
|
25
|
+
/**
|
|
26
|
+
* Send a control message to end the call.
|
|
27
|
+
*/
|
|
28
|
+
hangup(): void;
|
|
29
|
+
close(): void;
|
|
30
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { createClient as v } from "@keyframelabs/sdk";
|
|
2
|
-
const
|
|
3
|
-
function
|
|
2
|
+
const h = 24e3;
|
|
3
|
+
function g(i) {
|
|
4
4
|
const e = atob(i), t = new Uint8Array(e.length);
|
|
5
5
|
for (let s = 0; s < e.length; s++)
|
|
6
6
|
t[s] = e.charCodeAt(s);
|
|
7
7
|
return t;
|
|
8
8
|
}
|
|
9
|
-
function
|
|
9
|
+
function m(i) {
|
|
10
10
|
let e = "";
|
|
11
11
|
for (let t = 0; t < i.length; t++)
|
|
12
12
|
e += String.fromCharCode(i[t]);
|
|
13
13
|
return btoa(e);
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function c(i, e, t) {
|
|
16
16
|
if (e === t)
|
|
17
17
|
return i;
|
|
18
|
-
const s = new Int16Array(i.buffer, i.byteOffset, i.length / 2), n = e / t, a = Math.floor(s.length / n),
|
|
19
|
-
for (let
|
|
20
|
-
const
|
|
21
|
-
r
|
|
22
|
-
s[
|
|
18
|
+
const s = new Int16Array(i.buffer, i.byteOffset, i.length / 2), n = e / t, a = Math.floor(s.length / n), o = new Int16Array(a);
|
|
19
|
+
for (let r = 0; r < a; r++) {
|
|
20
|
+
const S = r * n, p = Math.floor(S), f = Math.min(p + 1, s.length - 1), _ = S - p;
|
|
21
|
+
o[r] = Math.round(
|
|
22
|
+
s[p] * (1 - _) + s[f] * _
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
|
-
return new Uint8Array(
|
|
25
|
+
return new Uint8Array(o.buffer);
|
|
26
26
|
}
|
|
27
27
|
function b() {
|
|
28
28
|
const i = /* @__PURE__ */ new Map();
|
|
@@ -41,7 +41,7 @@ function b() {
|
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
function
|
|
44
|
+
function w(i) {
|
|
45
45
|
const e = new Int16Array(i.length);
|
|
46
46
|
for (let t = 0; t < i.length; t++) {
|
|
47
47
|
const s = Math.max(-1, Math.min(1, i[t]));
|
|
@@ -50,7 +50,7 @@ function y(i) {
|
|
|
50
50
|
return new Uint8Array(e.buffer);
|
|
51
51
|
}
|
|
52
52
|
const I = 16e3;
|
|
53
|
-
class
|
|
53
|
+
class u {
|
|
54
54
|
ws = null;
|
|
55
55
|
_state = "idle";
|
|
56
56
|
events = b();
|
|
@@ -113,8 +113,8 @@ class m {
|
|
|
113
113
|
this.events.emit("closed", { code: e, reason: t });
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
const
|
|
117
|
-
class
|
|
116
|
+
const E = "gemini-2.5-flash-native-audio-preview-12-2025", C = "wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent", k = "wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained";
|
|
117
|
+
class A extends u {
|
|
118
118
|
agentName = "GeminiLive";
|
|
119
119
|
async connect(e) {
|
|
120
120
|
if (this.ws)
|
|
@@ -122,10 +122,10 @@ class k extends m {
|
|
|
122
122
|
if (!e.apiKey)
|
|
123
123
|
throw new Error("Gemini API key is required");
|
|
124
124
|
e.inputSampleRate && (this.inputSampleRate = e.inputSampleRate);
|
|
125
|
-
const t = e.model ??
|
|
126
|
-
return new Promise((a,
|
|
125
|
+
const t = e.model ?? E, n = (e.authType ?? "api_key") === "ephemeral_token" ? `${k}?access_token=${encodeURIComponent(e.apiKey)}` : `${C}?key=${encodeURIComponent(e.apiKey)}`;
|
|
126
|
+
return new Promise((a, o) => {
|
|
127
127
|
this.ws = new WebSocket(n), this.ws.onopen = () => {
|
|
128
|
-
const
|
|
128
|
+
const r = {
|
|
129
129
|
setup: {
|
|
130
130
|
model: `models/${t}`,
|
|
131
131
|
generationConfig: {
|
|
@@ -134,13 +134,13 @@ class k extends m {
|
|
|
134
134
|
systemInstruction: e.systemPrompt ? { parts: [{ text: e.systemPrompt }] } : void 0
|
|
135
135
|
}
|
|
136
136
|
};
|
|
137
|
-
this.ws.send(JSON.stringify(
|
|
137
|
+
this.ws.send(JSON.stringify(r)), this.setState("listening"), a();
|
|
138
138
|
}, this.ws.onerror = () => {
|
|
139
|
-
|
|
140
|
-
}, this.ws.onclose = (
|
|
141
|
-
this.ws = null, this.setState("idle"), this.emitClosed(
|
|
142
|
-
}, this.ws.onmessage = (
|
|
143
|
-
this.handleMessage(
|
|
139
|
+
o(new Error("Failed to connect to Gemini Live"));
|
|
140
|
+
}, this.ws.onclose = (r) => {
|
|
141
|
+
this.ws = null, this.setState("idle"), this.emitClosed(r.code, r.reason);
|
|
142
|
+
}, this.ws.onmessage = (r) => {
|
|
143
|
+
this.handleMessage(r.data);
|
|
144
144
|
};
|
|
145
145
|
});
|
|
146
146
|
}
|
|
@@ -159,7 +159,7 @@ class k extends m {
|
|
|
159
159
|
this._state !== "speaking" && (this.events.emit("turnStart", void 0), this.setState("speaking"));
|
|
160
160
|
for (const n of s.modelTurn.parts) {
|
|
161
161
|
if (n.inlineData?.data) {
|
|
162
|
-
const a =
|
|
162
|
+
const a = g(n.inlineData.data);
|
|
163
163
|
this.events.emit("audio", a);
|
|
164
164
|
}
|
|
165
165
|
n.text && this.events.emit("transcript", {
|
|
@@ -181,7 +181,7 @@ class k extends m {
|
|
|
181
181
|
mediaChunks: [
|
|
182
182
|
{
|
|
183
183
|
mimeType: `audio/pcm;rate=${this.inputSampleRate}`,
|
|
184
|
-
data:
|
|
184
|
+
data: m(e)
|
|
185
185
|
}
|
|
186
186
|
]
|
|
187
187
|
}
|
|
@@ -190,7 +190,7 @@ class k extends m {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
const R = "wss://api.elevenlabs.io/v1/convai/conversation";
|
|
193
|
-
class M extends
|
|
193
|
+
class M extends u {
|
|
194
194
|
agentName = "ElevenLabs";
|
|
195
195
|
outputSampleRate = 24e3;
|
|
196
196
|
// Default, updated from metadata
|
|
@@ -272,15 +272,15 @@ class M extends m {
|
|
|
272
272
|
if (!t?.audio_base_64 || (t.event_id ?? 0) <= this.lastInterruptId)
|
|
273
273
|
return;
|
|
274
274
|
this._state !== "speaking" && (this.events.emit("turnStart", void 0), this.setState("speaking"));
|
|
275
|
-
let n =
|
|
276
|
-
this.outputSampleRate !==
|
|
275
|
+
let n = g(t.audio_base_64);
|
|
276
|
+
this.outputSampleRate !== h && (n = c(n, this.outputSampleRate, h));
|
|
277
277
|
const a = 4800;
|
|
278
278
|
if (n.length <= a)
|
|
279
279
|
this.events.emit("audio", n);
|
|
280
280
|
else
|
|
281
|
-
for (let
|
|
282
|
-
const
|
|
283
|
-
this.events.emit("audio",
|
|
281
|
+
for (let o = 0; o < n.length; o += a) {
|
|
282
|
+
const r = n.slice(o, Math.min(o + a, n.length));
|
|
283
|
+
this.events.emit("audio", r);
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
286
|
handleUserTranscript(e) {
|
|
@@ -307,8 +307,8 @@ class M extends m {
|
|
|
307
307
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN || !this.initialized)
|
|
308
308
|
return;
|
|
309
309
|
let t = e;
|
|
310
|
-
this.sourceInputSampleRate !== this.expectedInputSampleRate && (t =
|
|
311
|
-
user_audio_chunk:
|
|
310
|
+
this.sourceInputSampleRate !== this.expectedInputSampleRate && (t = c(e, this.sourceInputSampleRate, this.expectedInputSampleRate)), this.ws.send(JSON.stringify({
|
|
311
|
+
user_audio_chunk: m(t)
|
|
312
312
|
}));
|
|
313
313
|
}
|
|
314
314
|
/**
|
|
@@ -342,7 +342,7 @@ class M extends m {
|
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
const x = "wss://api.cartesia.ai/agents/stream", P = "2025-04-16";
|
|
345
|
-
class T extends
|
|
345
|
+
class T extends u {
|
|
346
346
|
agentName = "Cartesia";
|
|
347
347
|
// Audio configuration
|
|
348
348
|
cartesiaInputFormat = "pcm_16000";
|
|
@@ -423,8 +423,8 @@ class T extends m {
|
|
|
423
423
|
handleMediaOutput(e) {
|
|
424
424
|
if (!e.media?.payload) return;
|
|
425
425
|
this._state !== "speaking" && (this.events.emit("turnStart", void 0), this.setState("speaking"));
|
|
426
|
-
let t =
|
|
427
|
-
this.cartesiaOutputRate !==
|
|
426
|
+
let t = g(e.media.payload);
|
|
427
|
+
this.cartesiaOutputRate !== h && (t = c(t, this.cartesiaOutputRate, h)), this.events.emit("audio", t);
|
|
428
428
|
}
|
|
429
429
|
handleClear() {
|
|
430
430
|
this.events.emit("interrupted", void 0), this.setState("listening");
|
|
@@ -434,11 +434,11 @@ class T extends m {
|
|
|
434
434
|
return;
|
|
435
435
|
let t = e;
|
|
436
436
|
const s = parseInt(this.cartesiaInputFormat.split("_")[1]);
|
|
437
|
-
this.inputSampleRate !== s && (t =
|
|
437
|
+
this.inputSampleRate !== s && (t = c(e, this.inputSampleRate, s)), this.ws.send(JSON.stringify({
|
|
438
438
|
event: "media_input",
|
|
439
439
|
stream_id: this.streamId,
|
|
440
440
|
media: {
|
|
441
|
-
payload:
|
|
441
|
+
payload: m(t)
|
|
442
442
|
}
|
|
443
443
|
}));
|
|
444
444
|
}
|
|
@@ -446,27 +446,109 @@ class T extends m {
|
|
|
446
446
|
this.stopHeartbeat(), this.isReady = !1, this.streamId = null, super.close();
|
|
447
447
|
}
|
|
448
448
|
}
|
|
449
|
-
|
|
449
|
+
class O extends u {
|
|
450
|
+
agentName = "Vapi";
|
|
451
|
+
// Audio configuration - Vapi uses 16kHz PCM by default
|
|
452
|
+
vapiSampleRate = 16e3;
|
|
453
|
+
async connect(e) {
|
|
454
|
+
if (this.ws)
|
|
455
|
+
throw new Error("Already connected");
|
|
456
|
+
if (!e.signedUrl)
|
|
457
|
+
throw new Error("Vapi signed URL is required");
|
|
458
|
+
return e.inputSampleRate && (this.inputSampleRate = e.inputSampleRate), new Promise((t, s) => {
|
|
459
|
+
this.ws = new WebSocket(e.signedUrl), this.ws.binaryType = "arraybuffer", this.ws.onopen = () => {
|
|
460
|
+
this.setState("listening"), t();
|
|
461
|
+
}, this.ws.onerror = () => {
|
|
462
|
+
s(new Error("Failed to connect to Vapi"));
|
|
463
|
+
}, this.ws.onclose = (n) => {
|
|
464
|
+
this.ws = null, this.setState("idle"), this.emitClosed(n.code, n.reason);
|
|
465
|
+
}, this.ws.onmessage = (n) => {
|
|
466
|
+
n.data instanceof ArrayBuffer ? this.handleBinaryAudio(n.data) : this.handleMessage(n.data);
|
|
467
|
+
};
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Handle binary audio data from Vapi.
|
|
472
|
+
* Vapi sends raw PCM 16-bit little-endian audio.
|
|
473
|
+
*/
|
|
474
|
+
handleBinaryAudio(e) {
|
|
475
|
+
this._state !== "speaking" && (this.events.emit("turnStart", void 0), this.setState("speaking"));
|
|
476
|
+
const t = new Uint8Array(e), s = this.vapiSampleRate !== h ? c(t, this.vapiSampleRate, h) : t;
|
|
477
|
+
this.events.emit("audio", s);
|
|
478
|
+
}
|
|
479
|
+
handleParsedMessage(e) {
|
|
480
|
+
const t = e;
|
|
481
|
+
switch (t.type) {
|
|
482
|
+
case "conversation-update":
|
|
483
|
+
t.role === "user" && t.transcript ? this.events.emit("transcript", {
|
|
484
|
+
role: "user",
|
|
485
|
+
text: t.transcript,
|
|
486
|
+
isFinal: !0
|
|
487
|
+
}) : t.role === "assistant" && t.transcript && this.events.emit("transcript", {
|
|
488
|
+
role: "assistant",
|
|
489
|
+
text: t.transcript,
|
|
490
|
+
isFinal: !0
|
|
491
|
+
});
|
|
492
|
+
break;
|
|
493
|
+
case "speech-update":
|
|
494
|
+
t.status === "started" ? (this.events.emit("turnStart", void 0), this.setState("speaking")) : t.status === "stopped" && (this.events.emit("turnEnd", void 0), this.setState("listening"));
|
|
495
|
+
break;
|
|
496
|
+
case "transcript":
|
|
497
|
+
this.events.emit("transcript", {
|
|
498
|
+
role: t.role === "user" ? "user" : "assistant",
|
|
499
|
+
text: t.transcript || "",
|
|
500
|
+
isFinal: t.transcriptType === "final"
|
|
501
|
+
});
|
|
502
|
+
break;
|
|
503
|
+
case "hang":
|
|
504
|
+
case "end-of-call-report":
|
|
505
|
+
this.events.emit("turnEnd", void 0), this.setState("idle");
|
|
506
|
+
break;
|
|
507
|
+
case "error":
|
|
508
|
+
console.error("[Vapi] Server error:", t);
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
sendAudio(e) {
|
|
513
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
|
514
|
+
return;
|
|
515
|
+
let t = e;
|
|
516
|
+
this.inputSampleRate !== this.vapiSampleRate && (t = c(e, this.inputSampleRate, this.vapiSampleRate)), this.ws.send(t.buffer);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Send a control message to end the call.
|
|
520
|
+
*/
|
|
521
|
+
hangup() {
|
|
522
|
+
this.ws && this.ws.readyState === WebSocket.OPEN && this.ws.send(JSON.stringify({ type: "end-call" }));
|
|
523
|
+
}
|
|
524
|
+
close() {
|
|
525
|
+
this.hangup(), super.close();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
const N = [
|
|
450
529
|
{ id: "gemini", name: "Gemini Live", description: "Google Gemini Live API" },
|
|
451
530
|
{ id: "elevenlabs", name: "ElevenLabs", description: "ElevenLabs Conversational AI" },
|
|
452
|
-
{ id: "cartesia", name: "Cartesia", description: "Cartesia Agents API" }
|
|
531
|
+
{ id: "cartesia", name: "Cartesia", description: "Cartesia Agents API" },
|
|
532
|
+
{ id: "vapi", name: "Vapi", description: "Vapi WebSocket Transport" }
|
|
453
533
|
];
|
|
454
|
-
function
|
|
534
|
+
function y(i) {
|
|
455
535
|
switch (i) {
|
|
456
536
|
case "gemini":
|
|
457
|
-
return new
|
|
537
|
+
return new A();
|
|
458
538
|
case "elevenlabs":
|
|
459
539
|
return new M();
|
|
460
540
|
case "cartesia":
|
|
461
541
|
return new T();
|
|
542
|
+
case "vapi":
|
|
543
|
+
return new O();
|
|
462
544
|
default:
|
|
463
545
|
throw new Error(`Unknown agent type: ${i}`);
|
|
464
546
|
}
|
|
465
547
|
}
|
|
466
548
|
function L(i) {
|
|
467
|
-
return
|
|
549
|
+
return N.find((e) => e.id === i);
|
|
468
550
|
}
|
|
469
|
-
class
|
|
551
|
+
class U extends Error {
|
|
470
552
|
status;
|
|
471
553
|
payload;
|
|
472
554
|
url;
|
|
@@ -475,7 +557,7 @@ class D extends Error {
|
|
|
475
557
|
}
|
|
476
558
|
}
|
|
477
559
|
const l = /* @__PURE__ */ new Set();
|
|
478
|
-
class
|
|
560
|
+
class F {
|
|
479
561
|
apiBaseUrl;
|
|
480
562
|
publishableKey;
|
|
481
563
|
callbacks;
|
|
@@ -568,7 +650,7 @@ class U {
|
|
|
568
650
|
s = await t.json();
|
|
569
651
|
} catch {
|
|
570
652
|
}
|
|
571
|
-
throw new
|
|
653
|
+
throw new U({
|
|
572
654
|
message: s?.message ?? "create_session failed",
|
|
573
655
|
status: t.status,
|
|
574
656
|
payload: s,
|
|
@@ -602,7 +684,7 @@ class U {
|
|
|
602
684
|
onClose: () => {
|
|
603
685
|
this.mounted && this.callbacks.onDisconnect?.();
|
|
604
686
|
}
|
|
605
|
-
}), this.agent =
|
|
687
|
+
}), this.agent = y(e.voice_agent_details.type), this.agent.on("audio", (t) => this.session?.sendAudio(t)), this.agent.on("interrupted", () => this.session?.interrupt()), this.agent.on("stateChange", (t) => this.setAgentState(t)), this.agent.on("closed", () => {
|
|
606
688
|
this.mounted && this.callbacks.onDisconnect?.();
|
|
607
689
|
}), await this.session.connect();
|
|
608
690
|
}
|
|
@@ -613,7 +695,7 @@ class U {
|
|
|
613
695
|
const e = this.audioContext.createMediaStreamSource(this.stream);
|
|
614
696
|
this.processor = this.audioContext.createScriptProcessor(4096, 1, 1), this.processor.onaudioprocess = (t) => {
|
|
615
697
|
if (!this._isMuted) {
|
|
616
|
-
const s =
|
|
698
|
+
const s = w(t.inputBuffer.getChannelData(0));
|
|
617
699
|
this.agent?.sendAudio(s);
|
|
618
700
|
}
|
|
619
701
|
}, e.connect(this.processor), this.processor.connect(this.audioContext.destination);
|
|
@@ -629,17 +711,20 @@ class U {
|
|
|
629
711
|
...t,
|
|
630
712
|
agentId: e.agent_id,
|
|
631
713
|
signedUrl: e.signed_url
|
|
632
|
-
}) : e.type === "cartesia"
|
|
714
|
+
}) : e.type === "cartesia" ? await this.agent.connect({
|
|
633
715
|
...t,
|
|
634
716
|
agentId: e.agent_id,
|
|
635
717
|
apiKey: e.token
|
|
718
|
+
}) : e.type === "vapi" && await this.agent.connect({
|
|
719
|
+
...t,
|
|
720
|
+
signedUrl: e.signed_url
|
|
636
721
|
});
|
|
637
722
|
}
|
|
638
723
|
cleanup() {
|
|
639
724
|
this.stream?.getTracks().forEach((e) => e.stop()), this.processor?.disconnect(), this.audioContext?.close(), this.agent?.close(), this.session?.close(), this.stream = null, this.processor = null, this.audioContext = null, this.agent = null, this.session = null;
|
|
640
725
|
}
|
|
641
726
|
}
|
|
642
|
-
const
|
|
727
|
+
const d = /* @__PURE__ */ new Set();
|
|
643
728
|
class K {
|
|
644
729
|
voiceAgentDetails;
|
|
645
730
|
sessionDetails;
|
|
@@ -684,24 +769,24 @@ class K {
|
|
|
684
769
|
}
|
|
685
770
|
/** Connect to the session */
|
|
686
771
|
async connect() {
|
|
687
|
-
if (
|
|
772
|
+
if (d.has(this.connectionId)) {
|
|
688
773
|
console.log("[PersonaView] Connection already in progress, skipping");
|
|
689
774
|
return;
|
|
690
775
|
}
|
|
691
|
-
|
|
776
|
+
d.add(this.connectionId), this.mounted = !0, this.setStatus("connecting");
|
|
692
777
|
try {
|
|
693
778
|
if (await this.initSession(), await this.initMicrophone(), await this.connectAgent(), !this.mounted) {
|
|
694
|
-
this.cleanup(),
|
|
779
|
+
this.cleanup(), d.delete(this.connectionId);
|
|
695
780
|
return;
|
|
696
781
|
}
|
|
697
782
|
this.setStatus("connected");
|
|
698
783
|
} catch (e) {
|
|
699
|
-
|
|
784
|
+
d.delete(this.connectionId), console.error("[PersonaView]", e), this.mounted && (this.setStatus("error"), this.callbacks.onError?.(e));
|
|
700
785
|
}
|
|
701
786
|
}
|
|
702
787
|
/** Disconnect and cleanup */
|
|
703
788
|
disconnect() {
|
|
704
|
-
this.mounted = !1,
|
|
789
|
+
this.mounted = !1, d.delete(this.connectionId), this.cleanup(), this.setStatus("disconnected");
|
|
705
790
|
}
|
|
706
791
|
/** Toggle microphone mute */
|
|
707
792
|
toggleMute() {
|
|
@@ -734,7 +819,7 @@ class K {
|
|
|
734
819
|
onClose: () => {
|
|
735
820
|
this.mounted && this.callbacks.onDisconnect?.();
|
|
736
821
|
}
|
|
737
|
-
}), this.agent =
|
|
822
|
+
}), this.agent = y(this.voiceAgentDetails.type), this.agent.on("audio", (e) => this.session?.sendAudio(e)), this.agent.on("interrupted", () => this.session?.interrupt()), this.agent.on("stateChange", (e) => this.setAgentState(e)), this.agent.on("closed", () => {
|
|
738
823
|
this.mounted && this.callbacks.onDisconnect?.();
|
|
739
824
|
}), await this.session.connect();
|
|
740
825
|
}
|
|
@@ -745,7 +830,7 @@ class K {
|
|
|
745
830
|
const e = this.audioContext.createMediaStreamSource(this.stream);
|
|
746
831
|
this.processor = this.audioContext.createScriptProcessor(4096, 1, 1), this.processor.onaudioprocess = (t) => {
|
|
747
832
|
if (!this._isMuted) {
|
|
748
|
-
const s =
|
|
833
|
+
const s = w(t.inputBuffer.getChannelData(0));
|
|
749
834
|
this.agent?.sendAudio(s);
|
|
750
835
|
}
|
|
751
836
|
}, e.connect(this.processor), this.processor.connect(this.audioContext.destination);
|
|
@@ -761,10 +846,13 @@ class K {
|
|
|
761
846
|
...t,
|
|
762
847
|
agentId: e.agent_id,
|
|
763
848
|
signedUrl: e.signed_url
|
|
764
|
-
}) : e.type === "cartesia"
|
|
849
|
+
}) : e.type === "cartesia" ? await this.agent.connect({
|
|
765
850
|
...t,
|
|
766
851
|
agentId: e.agent_id,
|
|
767
852
|
apiKey: e.token
|
|
853
|
+
}) : e.type === "vapi" && await this.agent.connect({
|
|
854
|
+
...t,
|
|
855
|
+
signedUrl: e.signed_url
|
|
768
856
|
});
|
|
769
857
|
}
|
|
770
858
|
cleanup() {
|
|
@@ -772,20 +860,20 @@ class K {
|
|
|
772
860
|
}
|
|
773
861
|
}
|
|
774
862
|
export {
|
|
775
|
-
|
|
776
|
-
|
|
863
|
+
N as AGENT_REGISTRY,
|
|
864
|
+
u as BaseAgent,
|
|
777
865
|
T as CartesiaAgent,
|
|
778
866
|
M as ElevenLabsAgent,
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
867
|
+
A as GeminiLiveAgent,
|
|
868
|
+
U as KeyframeApiError,
|
|
869
|
+
F as PersonaEmbed,
|
|
782
870
|
K as PersonaView,
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
871
|
+
h as SAMPLE_RATE,
|
|
872
|
+
g as base64ToBytes,
|
|
873
|
+
m as bytesToBase64,
|
|
874
|
+
y as createAgent,
|
|
787
875
|
b as createEventEmitter,
|
|
788
|
-
|
|
876
|
+
w as floatTo16BitPCM,
|
|
789
877
|
L as getAgentInfo,
|
|
790
|
-
|
|
878
|
+
c as resamplePcm
|
|
791
879
|
};
|