@vapi-ai/web 2.1.4 → 2.1.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/dist/vapi.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /// <reference types="node" />
2
- import { Call, CreateAssistantDTO, CreateSquadDTO, AssistantOverrides } from './api';
3
- import { DailyAdvancedConfig, DailyCall, DailyFactoryOptions } from '@daily-co/daily-js';
4
2
  import type { ChatCompletionMessageParam } from 'openai/resources';
3
+ import { DailyCall, DailyAdvancedConfig, DailyFactoryOptions } from '@daily-co/daily-js';
5
4
  import EventEmitter from 'events';
5
+ import { Call, CreateSquadDTO, CreateAssistantDTO, AssistantOverrides } from './api';
6
6
  export interface AddMessageMessage {
7
7
  type: 'add-message';
8
8
  message: ChatCompletionMessageParam;
@@ -18,13 +18,14 @@ export interface SayMessage {
18
18
  endCallAfterSpoken?: boolean;
19
19
  }
20
20
  type VapiClientToServerMessage = AddMessageMessage | ControlMessages | SayMessage;
21
- type VapiEventNames = 'call-end' | 'call-start' | 'volume-level' | 'speech-start' | 'speech-end' | 'message' | 'error';
21
+ type VapiEventNames = 'call-end' | 'call-start' | 'volume-level' | 'speech-start' | 'speech-end' | 'message' | 'video' | 'error';
22
22
  type VapiEventListeners = {
23
23
  'call-end': () => void;
24
24
  'call-start': () => void;
25
25
  'volume-level': (volume: number) => void;
26
26
  'speech-start': () => void;
27
27
  'speech-end': () => void;
28
+ video: (track: MediaStreamTrack) => void;
28
29
  message: (message: any) => void;
29
30
  error: (error: any) => void;
30
31
  };
package/dist/vapi.js CHANGED
@@ -6,11 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const daily_js_1 = __importDefault(require("@daily-co/daily-js"));
7
7
  const events_1 = __importDefault(require("events"));
8
8
  const client_1 = require("./client");
9
- function destroyAudioPlayer(participantId) {
10
- const player = document.querySelector(`audio[data-participant-id="${participantId}"]`);
11
- player?.remove();
12
- }
13
- async function startPlayer(player, track) {
9
+ async function startAudioPlayer(player, track) {
14
10
  player.muted = false;
15
11
  player.autoplay = true;
16
12
  if (track != null) {
@@ -22,16 +18,20 @@ async function buildAudioPlayer(track, participantId) {
22
18
  const player = document.createElement('audio');
23
19
  player.dataset.participantId = participantId;
24
20
  document.body.appendChild(player);
25
- await startPlayer(player, track);
21
+ await startAudioPlayer(player, track);
26
22
  return player;
27
23
  }
28
- function subscribeToTracks(e, call, isVideoRecordingEnabled) {
24
+ function destroyAudioPlayer(participantId) {
25
+ const player = document.querySelector(`audio[data-participant-id="${participantId}"]`);
26
+ player?.remove();
27
+ }
28
+ function subscribeToTracks(e, call, isVideoRecordingEnabled, isVideoEnabled) {
29
29
  if (e.participant.local)
30
30
  return;
31
31
  call.updateParticipant(e.participant.session_id, {
32
32
  setSubscribedTracks: {
33
33
  audio: true,
34
- video: isVideoRecordingEnabled,
34
+ video: isVideoRecordingEnabled || isVideoEnabled,
35
35
  },
36
36
  });
37
37
  }
@@ -95,10 +95,12 @@ class Vapi extends VapiEventEmitter {
95
95
  this.cleanup();
96
96
  }
97
97
  const isVideoRecordingEnabled = webCall?.artifactPlan?.videoRecordingEnabled ?? false;
98
+ // @ts-expect-error Tavus voice exists
99
+ const isVideoEnabled = webCall.assistant?.voice?.provider === 'tavus';
98
100
  this.call = daily_js_1.default.createCallObject({
99
101
  audioSource: this.dailyCallObject.audioSource ?? true,
100
- videoSource: isVideoRecordingEnabled,
101
- dailyConfig: this.dailyCallConfig
102
+ videoSource: this.dailyCallObject.videoSource ?? isVideoRecordingEnabled,
103
+ dailyConfig: this.dailyCallConfig,
102
104
  });
103
105
  this.call.iframe()?.style.setProperty('display', 'none');
104
106
  this.call.on('left-meeting', () => {
@@ -127,17 +129,20 @@ class Vapi extends VapiEventEmitter {
127
129
  return;
128
130
  if (e.participant?.local)
129
131
  return;
130
- if (e.track.kind !== 'audio')
131
- return;
132
- await buildAudioPlayer(e.track, e.participant.session_id);
133
- if (e?.participant?.user_name !== 'Vapi Speaker')
132
+ if (e.participant?.user_name !== 'Vapi Speaker')
134
133
  return;
134
+ if (e.track.kind === 'video') {
135
+ this.emit('video', e.track);
136
+ }
137
+ if (e.track.kind === 'audio') {
138
+ await buildAudioPlayer(e.track, e.participant.session_id);
139
+ }
135
140
  this.call?.sendAppMessage('playable');
136
141
  });
137
142
  this.call.on('participant-joined', (e) => {
138
143
  if (!e || !this.call)
139
144
  return;
140
- subscribeToTracks(e, this.call, isVideoRecordingEnabled);
145
+ subscribeToTracks(e, this.call, isVideoRecordingEnabled, isVideoEnabled);
141
146
  });
142
147
  await this.call.join({
143
148
  url: webCall.webCallUrl,
@@ -247,26 +252,16 @@ class Vapi extends VapiEventEmitter {
247
252
  this.call?.sendAppMessage(JSON.stringify(message));
248
253
  }
249
254
  setMuted(mute) {
250
- try {
251
- if (!this.call) {
252
- throw new Error('Call object is not available.');
253
- }
254
- this.call.setLocalAudio(!mute);
255
- }
256
- catch (error) {
257
- throw error;
255
+ if (!this.call) {
256
+ throw new Error('Call object is not available.');
258
257
  }
258
+ this.call.setLocalAudio(!mute);
259
259
  }
260
260
  isMuted() {
261
- try {
262
- if (!this.call) {
263
- return false;
264
- }
265
- return this.call.localAudio() === false;
266
- }
267
- catch (error) {
268
- throw error;
261
+ if (!this.call) {
262
+ return false;
269
263
  }
264
+ return this.call.localAudio() === false;
270
265
  }
271
266
  say(message, endCallAfterSpoken) {
272
267
  this.send({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vapi-ai/web",
3
- "version": "2.1.4",
3
+ "version": "2.1.6",
4
4
  "description": "",
5
5
  "main": "dist/vapi.js",
6
6
  "types": "dist/vapi.d.ts",
@@ -22,8 +22,8 @@
22
22
  },
23
23
  "homepage": "https://github.com/VapiAI/web#readme",
24
24
  "dependencies": {
25
- "@daily-co/daily-js": "^0.66.0",
26
- "@vapi-ai/web": "^1.0.263",
25
+ "@daily-co/daily-js": "^0.72.1",
26
+ "@vapi-ai/web": "^2.1.5",
27
27
  "events": "^3.3.0"
28
28
  },
29
29
  "devDependencies": {
package/vapi.ts CHANGED
@@ -1,22 +1,27 @@
1
- import { Call, CreateAssistantDTO, CreateSquadDTO, AssistantOverrides } from './api';
1
+ import type { ChatCompletionMessageParam } from 'openai/resources';
2
+
2
3
  import DailyIframe, {
3
- DailyAdvancedConfig,
4
4
  DailyCall,
5
+ DailyAdvancedConfig,
6
+ DailyFactoryOptions,
5
7
  DailyEventObjectAppMessage,
6
8
  DailyEventObjectParticipant,
7
9
  DailyEventObjectRemoteParticipantsAudioLevel,
8
- DailyFactoryOptions,
9
10
  } from '@daily-co/daily-js';
10
-
11
- import type { ChatCompletionMessageParam } from 'openai/resources';
12
11
  import EventEmitter from 'events';
12
+
13
+ import {
14
+ Call,
15
+ CreateSquadDTO,
16
+ CreateAssistantDTO,
17
+ AssistantOverrides,
18
+ } from './api';
13
19
  import { client } from './client';
14
20
 
15
- function destroyAudioPlayer(participantId: string) {
16
- const player = document.querySelector(`audio[data-participant-id="${participantId}"]`);
17
- player?.remove();
18
- }
19
- async function startPlayer(player: HTMLAudioElement, track: any) {
21
+ async function startAudioPlayer(
22
+ player: HTMLAudioElement,
23
+ track: MediaStreamTrack,
24
+ ) {
20
25
  player.muted = false;
21
26
  player.autoplay = true;
22
27
  if (track != null) {
@@ -24,24 +29,37 @@ async function startPlayer(player: HTMLAudioElement, track: any) {
24
29
  await player.play();
25
30
  }
26
31
  }
27
- async function buildAudioPlayer(track: any, participantId: string) {
32
+
33
+ async function buildAudioPlayer(
34
+ track: MediaStreamTrack,
35
+ participantId: string,
36
+ ) {
28
37
  const player = document.createElement('audio');
29
38
  player.dataset.participantId = participantId;
30
39
  document.body.appendChild(player);
31
- await startPlayer(player, track);
40
+ await startAudioPlayer(player, track);
32
41
  return player;
33
42
  }
43
+
44
+ function destroyAudioPlayer(participantId: string) {
45
+ const player = document.querySelector(
46
+ `audio[data-participant-id="${participantId}"]`,
47
+ );
48
+ player?.remove();
49
+ }
50
+
34
51
  function subscribeToTracks(
35
52
  e: DailyEventObjectParticipant,
36
53
  call: DailyCall,
37
54
  isVideoRecordingEnabled?: boolean,
55
+ isVideoEnabled?: boolean,
38
56
  ) {
39
57
  if (e.participant.local) return;
40
58
 
41
59
  call.updateParticipant(e.participant.session_id, {
42
60
  setSubscribedTracks: {
43
61
  audio: true,
44
- video: isVideoRecordingEnabled,
62
+ video: isVideoRecordingEnabled || isVideoEnabled,
45
63
  },
46
64
  });
47
65
  }
@@ -63,7 +81,10 @@ export interface SayMessage {
63
81
  endCallAfterSpoken?: boolean;
64
82
  }
65
83
 
66
- type VapiClientToServerMessage = AddMessageMessage | ControlMessages | SayMessage;
84
+ type VapiClientToServerMessage =
85
+ | AddMessageMessage
86
+ | ControlMessages
87
+ | SayMessage;
67
88
 
68
89
  type VapiEventNames =
69
90
  | 'call-end'
@@ -72,6 +93,7 @@ type VapiEventNames =
72
93
  | 'speech-start'
73
94
  | 'speech-end'
74
95
  | 'message'
96
+ | 'video'
75
97
  | 'error';
76
98
 
77
99
  type VapiEventListeners = {
@@ -80,23 +102,36 @@ type VapiEventListeners = {
80
102
  'volume-level': (volume: number) => void;
81
103
  'speech-start': () => void;
82
104
  'speech-end': () => void;
105
+ video: (track: MediaStreamTrack) => void;
83
106
  message: (message: any) => void;
84
107
  error: (error: any) => void;
85
108
  };
86
109
 
87
110
  class VapiEventEmitter extends EventEmitter {
88
- on<E extends VapiEventNames>(event: E, listener: VapiEventListeners[E]): this {
111
+ on<E extends VapiEventNames>(
112
+ event: E,
113
+ listener: VapiEventListeners[E],
114
+ ): this {
89
115
  super.on(event, listener);
90
116
  return this;
91
117
  }
92
- once<E extends VapiEventNames>(event: E, listener: VapiEventListeners[E]): this {
118
+ once<E extends VapiEventNames>(
119
+ event: E,
120
+ listener: VapiEventListeners[E],
121
+ ): this {
93
122
  super.once(event, listener);
94
123
  return this;
95
124
  }
96
- emit<E extends VapiEventNames>(event: E, ...args: Parameters<VapiEventListeners[E]>): boolean {
125
+ emit<E extends VapiEventNames>(
126
+ event: E,
127
+ ...args: Parameters<VapiEventListeners[E]>
128
+ ): boolean {
97
129
  return super.emit(event, ...args);
98
130
  }
99
- removeListener<E extends VapiEventNames>(event: E, listener: VapiEventListeners[E]): this {
131
+ removeListener<E extends VapiEventNames>(
132
+ event: E,
133
+ listener: VapiEventListeners[E],
134
+ ): this {
100
135
  super.removeListener(event, listener);
101
136
  return this;
102
137
  }
@@ -110,20 +145,23 @@ export default class Vapi extends VapiEventEmitter {
110
145
  private started: boolean = false;
111
146
  private call: DailyCall | null = null;
112
147
  private speakingTimeout: NodeJS.Timeout | null = null;
113
- private dailyCallConfig: DailyAdvancedConfig = {}
114
- private dailyCallObject: DailyFactoryOptions = {}
148
+ private dailyCallConfig: DailyAdvancedConfig = {};
149
+ private dailyCallObject: DailyFactoryOptions = {};
115
150
 
116
151
  constructor(
117
- apiToken: string,
118
- apiBaseUrl?: string,
119
- dailyCallConfig?: Pick<DailyAdvancedConfig, 'avoidEval' | 'alwaysIncludeMicInPermissionPrompt'>,
120
- dailyCallObject?: Pick<DailyFactoryOptions, 'audioSource'>
152
+ apiToken: string,
153
+ apiBaseUrl?: string,
154
+ dailyCallConfig?: Pick<
155
+ DailyAdvancedConfig,
156
+ 'avoidEval' | 'alwaysIncludeMicInPermissionPrompt'
157
+ >,
158
+ dailyCallObject?: Pick<DailyFactoryOptions, 'audioSource'>,
121
159
  ) {
122
160
  super();
123
161
  client.baseUrl = apiBaseUrl ?? 'https://api.vapi.ai';
124
162
  client.setSecurityData(apiToken);
125
- this.dailyCallConfig = dailyCallConfig ?? {}
126
- this.dailyCallObject = dailyCallObject ?? {}
163
+ this.dailyCallConfig = dailyCallConfig ?? {};
164
+ this.dailyCallObject = dailyCallObject ?? {};
127
165
  }
128
166
 
129
167
  private cleanup() {
@@ -161,12 +199,17 @@ export default class Vapi extends VapiEventEmitter {
161
199
  if (this.call) {
162
200
  this.cleanup();
163
201
  }
164
- const isVideoRecordingEnabled = webCall?.artifactPlan?.videoRecordingEnabled ?? false;
202
+
203
+ const isVideoRecordingEnabled =
204
+ webCall?.artifactPlan?.videoRecordingEnabled ?? false;
205
+
206
+ // @ts-expect-error Tavus voice exists
207
+ const isVideoEnabled = webCall.assistant?.voice?.provider === 'tavus';
165
208
 
166
209
  this.call = DailyIframe.createCallObject({
167
210
  audioSource: this.dailyCallObject.audioSource ?? true,
168
- videoSource: isVideoRecordingEnabled,
169
- dailyConfig: this.dailyCallConfig
211
+ videoSource: this.dailyCallObject.videoSource ?? isVideoRecordingEnabled,
212
+ dailyConfig: this.dailyCallConfig,
170
213
  });
171
214
  this.call.iframe()?.style.setProperty('display', 'none');
172
215
 
@@ -197,17 +240,27 @@ export default class Vapi extends VapiEventEmitter {
197
240
  this.call.on('track-started', async (e) => {
198
241
  if (!e || !e.participant) return;
199
242
  if (e.participant?.local) return;
200
- if (e.track.kind !== 'audio') return;
243
+ if (e.participant?.user_name !== 'Vapi Speaker') return;
244
+
245
+ if (e.track.kind === 'video') {
246
+ this.emit('video', e.track);
247
+ }
201
248
 
202
- await buildAudioPlayer(e.track, e.participant.session_id);
249
+ if (e.track.kind === 'audio') {
250
+ await buildAudioPlayer(e.track, e.participant.session_id);
251
+ }
203
252
 
204
- if (e?.participant?.user_name !== 'Vapi Speaker') return;
205
253
  this.call?.sendAppMessage('playable');
206
254
  });
207
255
 
208
256
  this.call.on('participant-joined', (e) => {
209
257
  if (!e || !this.call) return;
210
- subscribeToTracks(e, this.call, isVideoRecordingEnabled);
258
+ subscribeToTracks(
259
+ e,
260
+ this.call,
261
+ isVideoRecordingEnabled,
262
+ isVideoEnabled,
263
+ );
211
264
  });
212
265
 
213
266
  await this.call.join({
@@ -231,7 +284,8 @@ export default class Vapi extends VapiEventEmitter {
231
284
  this.send({
232
285
  type: 'control',
233
286
  control: 'say-first-message',
234
- videoRecordingStartDelaySeconds: (new Date().getTime() - recordingRequestedTime) / 1000,
287
+ videoRecordingStartDelaySeconds:
288
+ (new Date().getTime() - recordingRequestedTime) / 1000,
235
289
  });
236
290
  });
237
291
  }
@@ -296,8 +350,13 @@ export default class Vapi extends VapiEventEmitter {
296
350
  }
297
351
  }
298
352
 
299
- private handleRemoteParticipantsAudioLevel(e: DailyEventObjectRemoteParticipantsAudioLevel) {
300
- const speechLevel = Object.values(e.participantsAudioLevel).reduce((a, b) => a + b, 0);
353
+ private handleRemoteParticipantsAudioLevel(
354
+ e: DailyEventObjectRemoteParticipantsAudioLevel,
355
+ ) {
356
+ const speechLevel = Object.values(e.participantsAudioLevel).reduce(
357
+ (a, b) => a + b,
358
+ 0,
359
+ );
301
360
 
302
361
  this.emit('volume-level', Math.min(1, speechLevel / 0.15));
303
362
 
@@ -329,25 +388,17 @@ export default class Vapi extends VapiEventEmitter {
329
388
  }
330
389
 
331
390
  public setMuted(mute: boolean) {
332
- try {
333
- if (!this.call) {
334
- throw new Error('Call object is not available.');
335
- }
336
- this.call.setLocalAudio(!mute);
337
- } catch (error) {
338
- throw error;
391
+ if (!this.call) {
392
+ throw new Error('Call object is not available.');
339
393
  }
394
+ this.call.setLocalAudio(!mute);
340
395
  }
341
396
 
342
397
  public isMuted() {
343
- try {
344
- if (!this.call) {
345
- return false;
346
- }
347
- return this.call.localAudio() === false;
348
- } catch (error) {
349
- throw error;
398
+ if (!this.call) {
399
+ return false;
350
400
  }
401
+ return this.call.localAudio() === false;
351
402
  }
352
403
 
353
404
  public say(message: string, endCallAfterSpoken?: boolean) {
@@ -358,11 +409,15 @@ export default class Vapi extends VapiEventEmitter {
358
409
  });
359
410
  }
360
411
 
361
- public setInputDevicesAsync(options: Parameters<DailyCall['setInputDevicesAsync']>[0]) {
412
+ public setInputDevicesAsync(
413
+ options: Parameters<DailyCall['setInputDevicesAsync']>[0],
414
+ ) {
362
415
  this.call?.setInputDevicesAsync(options);
363
416
  }
364
417
 
365
- public setOutputDeviceAsync(options: Parameters<DailyCall['setOutputDeviceAsync']>[0]) {
418
+ public setOutputDeviceAsync(
419
+ options: Parameters<DailyCall['setOutputDeviceAsync']>[0],
420
+ ) {
366
421
  this.call?.setOutputDeviceAsync(options);
367
422
  }
368
423