@vapi-ai/web 1.0.263 → 1.0.267
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/.prettierrc +5 -0
- package/README.md +7 -2
- package/api.ts +915 -525
- package/dist/api.d.ts +511 -215
- package/dist/api.js +83 -73
- package/dist/example.d.ts +1 -0
- package/dist/example.js +26 -0
- package/dist/vapi.d.ts +15 -11
- package/dist/vapi.js +29 -26
- package/package.json +2 -1
- package/vapi.ts +60 -71
- package/dist/AtomicMediaSource.d.ts +0 -17
- package/dist/AtomicMediaSource.js +0 -78
- package/dist/player.d.ts +0 -10
- package/dist/player.js +0 -38
package/vapi.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import { Call, CreateAssistantDTO } from
|
|
1
|
+
import { Call, CreateAssistantDTO } from './api';
|
|
2
2
|
import DailyIframe, {
|
|
3
3
|
DailyCall,
|
|
4
4
|
DailyEventObjectAppMessage,
|
|
5
5
|
DailyEventObjectParticipant,
|
|
6
6
|
DailyEventObjectRemoteParticipantsAudioLevel,
|
|
7
|
-
} from
|
|
7
|
+
} from '@daily-co/daily-js';
|
|
8
8
|
|
|
9
|
-
import type { ChatCompletionMessageParam } from
|
|
10
|
-
import EventEmitter from
|
|
11
|
-
import { client } from
|
|
9
|
+
import type { ChatCompletionMessageParam } from 'openai/resources';
|
|
10
|
+
import EventEmitter from 'events';
|
|
11
|
+
import { client } from './client';
|
|
12
12
|
|
|
13
13
|
function destroyAudioPlayer(participantId: string) {
|
|
14
|
-
const player = document.querySelector(
|
|
15
|
-
`audio[data-participant-id="${participantId}"]`
|
|
16
|
-
);
|
|
14
|
+
const player = document.querySelector(`audio[data-participant-id="${participantId}"]`);
|
|
17
15
|
player?.remove();
|
|
18
16
|
}
|
|
19
17
|
async function startPlayer(player: HTMLAudioElement, track: any) {
|
|
@@ -25,7 +23,7 @@ async function startPlayer(player: HTMLAudioElement, track: any) {
|
|
|
25
23
|
}
|
|
26
24
|
}
|
|
27
25
|
async function buildAudioPlayer(track: any, participantId: string) {
|
|
28
|
-
const player = document.createElement(
|
|
26
|
+
const player = document.createElement('audio');
|
|
29
27
|
player.dataset.participantId = participantId;
|
|
30
28
|
document.body.appendChild(player);
|
|
31
29
|
await startPlayer(player, track);
|
|
@@ -43,56 +41,49 @@ function subscribeToTracks(e: DailyEventObjectParticipant, call: DailyCall) {
|
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
export interface AddMessageMessage {
|
|
46
|
-
type:
|
|
44
|
+
type: 'add-message';
|
|
47
45
|
message: ChatCompletionMessageParam;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
export interface ControlMessages {
|
|
49
|
+
type: 'control';
|
|
50
|
+
control: 'mute-assistant' | 'unmute-assistant';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type VapiClientToServerMessage = AddMessageMessage | ControlMessages;
|
|
51
54
|
|
|
52
55
|
type VapiEventNames =
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
56
|
+
| 'call-end'
|
|
57
|
+
| 'call-start'
|
|
58
|
+
| 'volume-level'
|
|
59
|
+
| 'speech-start'
|
|
60
|
+
| 'speech-end'
|
|
61
|
+
| 'message'
|
|
62
|
+
| 'error';
|
|
60
63
|
|
|
61
64
|
type VapiEventListeners = {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
'call-end': () => void;
|
|
66
|
+
'call-start': () => void;
|
|
67
|
+
'volume-level': (volume: number) => void;
|
|
68
|
+
'speech-start': () => void;
|
|
69
|
+
'speech-end': () => void;
|
|
67
70
|
message: (message: any) => void;
|
|
68
71
|
error: (error: any) => void;
|
|
69
72
|
};
|
|
70
73
|
|
|
71
74
|
class VapiEventEmitter extends EventEmitter {
|
|
72
|
-
on<E extends VapiEventNames>(
|
|
73
|
-
event: E,
|
|
74
|
-
listener: VapiEventListeners[E]
|
|
75
|
-
): this {
|
|
75
|
+
on<E extends VapiEventNames>(event: E, listener: VapiEventListeners[E]): this {
|
|
76
76
|
super.on(event, listener);
|
|
77
77
|
return this;
|
|
78
78
|
}
|
|
79
|
-
once<E extends VapiEventNames>(
|
|
80
|
-
event: E,
|
|
81
|
-
listener: VapiEventListeners[E]
|
|
82
|
-
): this {
|
|
79
|
+
once<E extends VapiEventNames>(event: E, listener: VapiEventListeners[E]): this {
|
|
83
80
|
super.once(event, listener);
|
|
84
81
|
return this;
|
|
85
82
|
}
|
|
86
|
-
emit<E extends VapiEventNames>(
|
|
87
|
-
event: E,
|
|
88
|
-
...args: Parameters<VapiEventListeners[E]>
|
|
89
|
-
): boolean {
|
|
83
|
+
emit<E extends VapiEventNames>(event: E, ...args: Parameters<VapiEventListeners[E]>): boolean {
|
|
90
84
|
return super.emit(event, ...args);
|
|
91
85
|
}
|
|
92
|
-
removeListener<E extends VapiEventNames>(
|
|
93
|
-
event: E,
|
|
94
|
-
listener: VapiEventListeners[E]
|
|
95
|
-
): this {
|
|
86
|
+
removeListener<E extends VapiEventNames>(event: E, listener: VapiEventListeners[E]): this {
|
|
96
87
|
super.removeListener(event, listener);
|
|
97
88
|
return this;
|
|
98
89
|
}
|
|
@@ -110,7 +101,7 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
110
101
|
|
|
111
102
|
constructor(apiToken: string, apiBaseUrl?: string) {
|
|
112
103
|
super();
|
|
113
|
-
client.baseUrl = apiBaseUrl ??
|
|
104
|
+
client.baseUrl = apiBaseUrl ?? 'https://api.vapi.ai';
|
|
114
105
|
client.setSecurityData(apiToken);
|
|
115
106
|
}
|
|
116
107
|
|
|
@@ -131,43 +122,46 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
131
122
|
try {
|
|
132
123
|
const webCall = (
|
|
133
124
|
await client.call.callControllerCreateWebCall({
|
|
134
|
-
assistant: typeof assistant ===
|
|
135
|
-
assistantId: typeof assistant ===
|
|
125
|
+
assistant: typeof assistant === 'string' ? undefined : assistant,
|
|
126
|
+
assistantId: typeof assistant === 'string' ? assistant : undefined,
|
|
136
127
|
})
|
|
137
128
|
).data;
|
|
138
129
|
|
|
130
|
+
if (this.call) {
|
|
131
|
+
this.cleanup();
|
|
132
|
+
}
|
|
139
133
|
this.call = DailyIframe.createCallObject({
|
|
140
134
|
audioSource: true,
|
|
141
135
|
videoSource: false,
|
|
142
136
|
});
|
|
143
|
-
this.call.iframe()?.style.setProperty(
|
|
137
|
+
this.call.iframe()?.style.setProperty('display', 'none');
|
|
144
138
|
|
|
145
|
-
this.call.on(
|
|
146
|
-
this.emit(
|
|
139
|
+
this.call.on('left-meeting', () => {
|
|
140
|
+
this.emit('call-end');
|
|
147
141
|
this.cleanup();
|
|
148
142
|
});
|
|
149
143
|
|
|
150
|
-
this.call.on(
|
|
144
|
+
this.call.on('participant-left', (e) => {
|
|
151
145
|
if (!e) return;
|
|
152
146
|
destroyAudioPlayer(e.participant.session_id);
|
|
153
147
|
});
|
|
154
148
|
|
|
155
|
-
this.call.on(
|
|
149
|
+
this.call.on('error', () => {
|
|
156
150
|
// Ignore error
|
|
157
151
|
});
|
|
158
152
|
|
|
159
|
-
this.call.on(
|
|
153
|
+
this.call.on('track-started', async (e) => {
|
|
160
154
|
if (!e || !e.participant) return;
|
|
161
155
|
if (e.participant?.local) return;
|
|
162
|
-
if (e.track.kind !==
|
|
156
|
+
if (e.track.kind !== 'audio') return;
|
|
163
157
|
|
|
164
158
|
await buildAudioPlayer(e.track, e.participant.session_id);
|
|
165
159
|
|
|
166
|
-
if (e?.participant?.user_name !==
|
|
167
|
-
this.call?.sendAppMessage(
|
|
160
|
+
if (e?.participant?.user_name !== 'Vapi Speaker') return;
|
|
161
|
+
this.call?.sendAppMessage('playable');
|
|
168
162
|
});
|
|
169
163
|
|
|
170
|
-
this.call.on(
|
|
164
|
+
this.call.on('participant-joined', (e) => {
|
|
171
165
|
if (!e || !this.call) return;
|
|
172
166
|
subscribeToTracks(e, this.call);
|
|
173
167
|
});
|
|
@@ -179,16 +173,16 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
179
173
|
|
|
180
174
|
this.call.startRemoteParticipantsAudioLevelObserver(100);
|
|
181
175
|
|
|
182
|
-
this.call.on(
|
|
176
|
+
this.call.on('remote-participants-audio-level', (e) => {
|
|
183
177
|
if (e) this.handleRemoteParticipantsAudioLevel(e);
|
|
184
178
|
});
|
|
185
179
|
|
|
186
|
-
this.call.on(
|
|
180
|
+
this.call.on('app-message', (e) => this.onAppMessage(e));
|
|
187
181
|
|
|
188
182
|
this.call.updateInputSettings({
|
|
189
183
|
audio: {
|
|
190
184
|
processor: {
|
|
191
|
-
type:
|
|
185
|
+
type: 'noise-cancellation',
|
|
192
186
|
},
|
|
193
187
|
},
|
|
194
188
|
});
|
|
@@ -196,7 +190,7 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
196
190
|
return webCall;
|
|
197
191
|
} catch (e) {
|
|
198
192
|
console.error(e);
|
|
199
|
-
this.emit(
|
|
193
|
+
this.emit('error', e);
|
|
200
194
|
this.cleanup();
|
|
201
195
|
return null;
|
|
202
196
|
}
|
|
@@ -205,14 +199,14 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
205
199
|
private onAppMessage(e?: DailyEventObjectAppMessage) {
|
|
206
200
|
if (!e) return;
|
|
207
201
|
try {
|
|
208
|
-
if (e.data ===
|
|
209
|
-
return this.emit(
|
|
202
|
+
if (e.data === 'listening') {
|
|
203
|
+
return this.emit('call-start');
|
|
210
204
|
} else {
|
|
211
205
|
try {
|
|
212
206
|
const parsedMessage = JSON.parse(e.data);
|
|
213
|
-
this.emit(
|
|
207
|
+
this.emit('message', parsedMessage);
|
|
214
208
|
} catch (parseError) {
|
|
215
|
-
console.log(
|
|
209
|
+
console.log('Error parsing message data: ', parseError);
|
|
216
210
|
}
|
|
217
211
|
}
|
|
218
212
|
} catch (e) {
|
|
@@ -220,15 +214,10 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
220
214
|
}
|
|
221
215
|
}
|
|
222
216
|
|
|
223
|
-
private handleRemoteParticipantsAudioLevel(
|
|
224
|
-
e
|
|
225
|
-
) {
|
|
226
|
-
const speechLevel = Object.values(e.participantsAudioLevel).reduce(
|
|
227
|
-
(a, b) => a + b,
|
|
228
|
-
0
|
|
229
|
-
);
|
|
217
|
+
private handleRemoteParticipantsAudioLevel(e: DailyEventObjectRemoteParticipantsAudioLevel) {
|
|
218
|
+
const speechLevel = Object.values(e.participantsAudioLevel).reduce((a, b) => a + b, 0);
|
|
230
219
|
|
|
231
|
-
this.emit(
|
|
220
|
+
this.emit('volume-level', Math.min(1, speechLevel / 0.15));
|
|
232
221
|
|
|
233
222
|
const isSpeaking = speechLevel > 0.01;
|
|
234
223
|
|
|
@@ -238,11 +227,11 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
238
227
|
clearTimeout(this.speakingTimeout);
|
|
239
228
|
this.speakingTimeout = null;
|
|
240
229
|
} else {
|
|
241
|
-
this.emit(
|
|
230
|
+
this.emit('speech-start');
|
|
242
231
|
}
|
|
243
232
|
|
|
244
233
|
this.speakingTimeout = setTimeout(() => {
|
|
245
|
-
this.emit(
|
|
234
|
+
this.emit('speech-end');
|
|
246
235
|
this.speakingTimeout = null;
|
|
247
236
|
}, 1000);
|
|
248
237
|
}
|
|
@@ -260,7 +249,7 @@ export default class Vapi extends VapiEventEmitter {
|
|
|
260
249
|
public setMuted(mute: boolean) {
|
|
261
250
|
try {
|
|
262
251
|
if (!this.call) {
|
|
263
|
-
throw new Error(
|
|
252
|
+
throw new Error('Call object is not available.');
|
|
264
253
|
}
|
|
265
254
|
this.call.setLocalAudio(!mute);
|
|
266
255
|
} catch (error) {
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import EventEmitter from "events";
|
|
3
|
-
declare global {
|
|
4
|
-
interface Window {
|
|
5
|
-
ManagedMediaSource: any;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
declare class AtomicMediaSource extends EventEmitter {
|
|
9
|
-
mediaSource: any;
|
|
10
|
-
sourceBuffer: SourceBuffer | null;
|
|
11
|
-
private operationsQueue;
|
|
12
|
-
constructor();
|
|
13
|
-
appendBuffer(buffer: ArrayBuffer): void;
|
|
14
|
-
clearBuffer(): void;
|
|
15
|
-
private createOperationQueue;
|
|
16
|
-
}
|
|
17
|
-
export default AtomicMediaSource;
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const async_1 = __importDefault(require("async"));
|
|
7
|
-
const events_1 = __importDefault(require("events"));
|
|
8
|
-
// ManagedMediaSource needed for iOS Safari
|
|
9
|
-
function getMediaSource() {
|
|
10
|
-
if (window.ManagedMediaSource) {
|
|
11
|
-
return new window.ManagedMediaSource();
|
|
12
|
-
}
|
|
13
|
-
if (window.MediaSource) {
|
|
14
|
-
return new window.MediaSource();
|
|
15
|
-
}
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
class AtomicMediaSource extends events_1.default {
|
|
19
|
-
constructor() {
|
|
20
|
-
super();
|
|
21
|
-
this.sourceBuffer = null;
|
|
22
|
-
this.mediaSource = getMediaSource();
|
|
23
|
-
this.operationsQueue = this.createOperationQueue();
|
|
24
|
-
if (!this.mediaSource) {
|
|
25
|
-
console.error("MediaSource is not supported in your browser.");
|
|
26
|
-
// Display a message to the user, or handle this situation in another way
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
this.mediaSource.addEventListener("sourceopen", () => {
|
|
30
|
-
this.mediaSource.duration = Number.POSITIVE_INFINITY;
|
|
31
|
-
this.sourceBuffer = this.mediaSource.addSourceBuffer('audio/webm; codecs="opus"');
|
|
32
|
-
if (this.sourceBuffer) {
|
|
33
|
-
this.sourceBuffer.mode = "sequence";
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
appendBuffer(buffer) {
|
|
38
|
-
this.operationsQueue.push(() => {
|
|
39
|
-
return new Promise((resolve, reject) => {
|
|
40
|
-
if (this.sourceBuffer) {
|
|
41
|
-
this.sourceBuffer.addEventListener("updateend", () => {
|
|
42
|
-
resolve();
|
|
43
|
-
}, { once: true });
|
|
44
|
-
this.sourceBuffer.appendBuffer(buffer);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
reject();
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
clearBuffer() {
|
|
53
|
-
// this.operationsQueue.push(() => {
|
|
54
|
-
// return new Promise((resolve, reject) => {
|
|
55
|
-
// if (this.sourceBuffer) {
|
|
56
|
-
// this.sourceBuffer.addEventListener(
|
|
57
|
-
// "updateend",
|
|
58
|
-
// () => {
|
|
59
|
-
// resolve();
|
|
60
|
-
// },
|
|
61
|
-
// { once: true }
|
|
62
|
-
// );
|
|
63
|
-
// if (this.sourceBuffer.buffered.length > 0) {
|
|
64
|
-
// this.sourceBuffer.remove(0, this.sourceBuffer.buffered.end(0));
|
|
65
|
-
// }
|
|
66
|
-
// } else {
|
|
67
|
-
// reject();
|
|
68
|
-
// }
|
|
69
|
-
// });
|
|
70
|
-
// });
|
|
71
|
-
}
|
|
72
|
-
createOperationQueue() {
|
|
73
|
-
return async_1.default.queue((task, callback) => {
|
|
74
|
-
task().then(() => callback());
|
|
75
|
-
}, 1);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
exports.default = AtomicMediaSource;
|
package/dist/player.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import EventEmitter from "events";
|
|
3
|
-
export declare class ContinuousPlayer extends EventEmitter {
|
|
4
|
-
private atomic;
|
|
5
|
-
private audio;
|
|
6
|
-
constructor();
|
|
7
|
-
start(): void;
|
|
8
|
-
playChunk(audioData: ArrayBuffer): void;
|
|
9
|
-
clear(): void;
|
|
10
|
-
}
|
package/dist/player.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ContinuousPlayer = void 0;
|
|
7
|
-
const AtomicMediaSource_1 = __importDefault(require("./AtomicMediaSource"));
|
|
8
|
-
const events_1 = __importDefault(require("events"));
|
|
9
|
-
class ContinuousPlayer extends events_1.default {
|
|
10
|
-
constructor() {
|
|
11
|
-
super();
|
|
12
|
-
this.audio = document.createElement("audio");
|
|
13
|
-
this.audio.addEventListener("playing", () => this.emit("speech-start"));
|
|
14
|
-
this.audio.addEventListener("play", () => this.emit("speech-start"));
|
|
15
|
-
this.audio.addEventListener("canplay", () => {
|
|
16
|
-
this.audio.play();
|
|
17
|
-
});
|
|
18
|
-
this.audio.addEventListener("waiting", () => this.emit("speech-end"));
|
|
19
|
-
this.atomic = new AtomicMediaSource_1.default();
|
|
20
|
-
this.audio.src = URL.createObjectURL(this.atomic.mediaSource);
|
|
21
|
-
document.body.appendChild(this.audio);
|
|
22
|
-
}
|
|
23
|
-
start() {
|
|
24
|
-
this.audio.play();
|
|
25
|
-
}
|
|
26
|
-
playChunk(audioData) {
|
|
27
|
-
var _a;
|
|
28
|
-
(_a = this.atomic.sourceBuffer) === null || _a === void 0 ? void 0 : _a.addEventListener("updateend", () => {
|
|
29
|
-
this.audio.play().catch((error) => console.error("Play error:", error));
|
|
30
|
-
}, { once: true });
|
|
31
|
-
this.atomic.appendBuffer(audioData);
|
|
32
|
-
}
|
|
33
|
-
clear() {
|
|
34
|
-
this.audio.pause();
|
|
35
|
-
this.atomic.clearBuffer();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
exports.ContinuousPlayer = ContinuousPlayer;
|