@vapi-ai/web 1.0.140 → 1.0.142

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/player.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /// <reference types="node" />
2
2
  import EventEmitter from "events";
3
3
  export declare class ContinuousPlayer extends EventEmitter {
4
- private audioCtx;
5
- private source;
4
+ private atomic;
5
+ private audio;
6
6
  constructor();
7
7
  start(): void;
8
- playChunk(audioData: ArrayBuffer): Promise<void>;
8
+ playChunk(audioData: ArrayBuffer): void;
9
9
  clear(): void;
10
10
  }
package/dist/player.js CHANGED
@@ -1,37 +1,38 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
4
  };
14
5
  Object.defineProperty(exports, "__esModule", { value: true });
15
6
  exports.ContinuousPlayer = void 0;
7
+ const AtomicMediaSource_1 = __importDefault(require("./AtomicMediaSource"));
16
8
  const events_1 = __importDefault(require("events"));
17
9
  class ContinuousPlayer extends events_1.default {
18
10
  constructor() {
19
11
  super();
20
- this.source = null;
21
- this.audioCtx = new AudioContext();
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();
22
25
  }
23
- start() { }
24
26
  playChunk(audioData) {
25
- return __awaiter(this, void 0, void 0, function* () {
26
- const audioBuffer = yield this.audioCtx.decodeAudioData(audioData);
27
- this.source = this.audioCtx.createBufferSource();
28
- this.source.buffer = audioBuffer;
29
- this.source.connect(this.audioCtx.destination);
30
- this.source.start();
31
- this.emit("speech-start");
32
- this.source.onended = () => this.emit("speech-end");
33
- });
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();
34
36
  }
35
- clear() { }
36
37
  }
37
38
  exports.ContinuousPlayer = ContinuousPlayer;
package/dist/vapi.d.ts CHANGED
@@ -1,18 +1,10 @@
1
+ /// <reference types="node" />
1
2
  import { CreateAssistantDTO } from "./api";
2
- import async from "async";
3
- export default class Vapi {
4
- private audioContext;
5
- private source;
3
+ import EventEmitter from "events";
4
+ export default class Vapi extends EventEmitter {
6
5
  private started;
7
- private ws;
8
- private mediaRecorder;
9
- private nextClipTime;
10
- private decodeQueue;
11
- constructor(apiToken: string, apiUrl?: string);
12
- createQueue(): async.QueueObject<ArrayBuffer>;
6
+ private call;
7
+ constructor(apiToken: string, apiBaseUrl?: string);
13
8
  start(assistant: CreateAssistantDTO | string): void;
14
- private startRecording;
15
- private onMessage;
16
- clear(): void;
17
9
  stop(): void;
18
10
  }
package/dist/vapi.js CHANGED
@@ -3,39 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const standardized_audio_context_1 = require("standardized-audio-context");
7
- const async_1 = __importDefault(require("async"));
6
+ const daily_js_1 = __importDefault(require("@daily-co/daily-js"));
7
+ const events_1 = __importDefault(require("events"));
8
8
  const client_1 = require("./client");
9
- const base64_arraybuffer_1 = require("base64-arraybuffer");
10
- class Vapi {
11
- constructor(apiToken, apiUrl) {
12
- this.audioContext = new standardized_audio_context_1.AudioContext();
13
- this.source = null;
9
+ class Vapi extends events_1.default {
10
+ constructor(apiToken, apiBaseUrl) {
11
+ super();
14
12
  this.started = false;
15
- this.ws = null;
16
- this.mediaRecorder = null;
17
- this.nextClipTime = 0;
13
+ this.call = null;
14
+ client_1.client.baseUrl = apiBaseUrl !== null && apiBaseUrl !== void 0 ? apiBaseUrl : "https://api.vapi.ai";
18
15
  client_1.client.setSecurityData(apiToken);
19
- client_1.client.baseUrl = apiUrl !== null && apiUrl !== void 0 ? apiUrl : "https://api.vapi.ai";
20
- this.decodeQueue = this.createQueue();
21
- }
22
- createQueue() {
23
- return async_1.default.queue((task, callback) => {
24
- this.source = this.audioContext.createBufferSource();
25
- this.audioContext.decodeAudioData(task, (buffer) => {
26
- if (!this.source)
27
- return;
28
- this.source.buffer = buffer;
29
- this.source.connect(this.audioContext.destination);
30
- // If the next clip time is in the past, set it to the current time
31
- this.nextClipTime = Math.max(this.nextClipTime, this.audioContext.currentTime);
32
- // Start the source at the next clip time
33
- this.source.start(this.nextClipTime);
34
- // Schedule the next clip time
35
- this.nextClipTime += buffer.duration;
36
- callback();
37
- });
38
- }, 1);
39
16
  }
40
17
  start(assistant) {
41
18
  if (this.started) {
@@ -44,78 +21,26 @@ class Vapi {
44
21
  this.started = true;
45
22
  client_1.client.call
46
23
  .callControllerCreateWebCall({
24
+ assistant: typeof assistant === "string" ? undefined : assistant,
47
25
  assistantId: typeof assistant === "string" ? assistant : undefined,
48
- assistant: typeof assistant === "object" ? assistant : undefined,
49
26
  })
50
27
  .then(({ data }) => {
51
- const { callId, url } = data;
52
- const socket = new WebSocket(url);
53
- this.ws = socket;
54
- socket.onopen = () => {
55
- socket.send(JSON.stringify({ event: "start", callId }));
56
- this.startRecording();
57
- };
58
- socket.onmessage = (event) => {
59
- if (!socket)
60
- return;
61
- this.onMessage(socket, event);
62
- };
63
- socket.onclose = () => {
64
- this.stop();
65
- };
28
+ const { url } = data;
29
+ this.call = daily_js_1.default.createCallObject({
30
+ url,
31
+ audioSource: true,
32
+ videoSource: false,
33
+ });
66
34
  })
67
35
  .catch((error) => {
68
36
  console.error(error);
69
37
  });
70
38
  }
71
- startRecording() {
72
- navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
73
- this.mediaRecorder = new MediaRecorder(stream, {
74
- mimeType: "audio/webm;codecs=opus",
75
- });
76
- this.mediaRecorder.start(100);
77
- this.mediaRecorder.ondataavailable = (event) => {
78
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
79
- this.ws.send(event.data);
80
- }
81
- };
82
- });
83
- }
84
- onMessage(ws, event) {
85
- const data = JSON.parse(event.data);
86
- if (data.event === "media") {
87
- const audioData = (0, base64_arraybuffer_1.decode)(data.media.payload);
88
- this.decodeQueue.push(audioData);
89
- }
90
- if (data.event === "clear") {
91
- this.clear();
92
- }
93
- }
94
- clear() {
95
- this.decodeQueue.kill();
96
- this.decodeQueue = this.createQueue();
97
- // Stop the source if it exists
98
- if (this.source) {
99
- this.source.stop();
100
- this.source.disconnect();
101
- this.source = null;
102
- }
103
- // Reset the next clip time
104
- this.nextClipTime = 0;
105
- }
106
39
  stop() {
107
- if (this.source) {
108
- this.source.stop();
109
- }
40
+ var _a;
110
41
  this.started = false;
111
- if (this.ws) {
112
- this.ws.close();
113
- }
114
- this.ws = null;
115
- if (this.mediaRecorder) {
116
- this.mediaRecorder.stop();
117
- }
118
- this.mediaRecorder = null;
42
+ (_a = this.call) === null || _a === void 0 ? void 0 : _a.destroy();
43
+ this.call = null;
119
44
  }
120
45
  }
121
46
  exports.default = Vapi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vapi-ai/web",
3
- "version": "1.0.140",
3
+ "version": "1.0.142",
4
4
  "description": "",
5
5
  "main": "dist/vapi.js",
6
6
  "types": "dist/vapi.d.ts",
@@ -22,15 +22,11 @@
22
22
  },
23
23
  "homepage": "https://github.com/VapiAI/snippet#readme",
24
24
  "dependencies": {
25
- "async": "^3.2.4",
26
- "base64-arraybuffer": "^1.0.2",
27
- "events": "^3.3.0",
28
- "standardized-audio-context": "^25.3.57"
25
+ "@daily-co/daily-js": "^0.52.0",
26
+ "events": "^3.3.0"
29
27
  },
30
28
  "devDependencies": {
31
- "@types/async": "^3.2.21",
32
29
  "@types/node": "^20.6.0",
33
- "@types/ws": "^8.5.5",
34
30
  "swagger-typescript-api": "^13.0.3"
35
31
  }
36
32
  }
package/vapi.ts CHANGED
@@ -1,57 +1,17 @@
1
- import { Assistant, CreateAssistantDTO } from "./api";
2
- import {
3
- AudioContext,
4
- IAudioBufferSourceNode,
5
- IAudioContext,
6
- } from "standardized-audio-context";
7
- import async, { QueueObject } from "async";
1
+ import DailyIframe, { DailyCall } from "@daily-co/daily-js";
8
2
 
3
+ import { CreateAssistantDTO } from "./api";
4
+ import EventEmitter from "events";
9
5
  import { client } from "./client";
10
- import { decode } from "base64-arraybuffer";
11
6
 
12
- export default class Vapi {
13
- private audioContext: AudioContext = new AudioContext();
14
- private source: IAudioBufferSourceNode<IAudioContext> | null = null;
7
+ export default class Vapi extends EventEmitter {
15
8
  private started: boolean = false;
16
- private ws: WebSocket | null = null;
17
- private mediaRecorder: MediaRecorder | null = null;
9
+ private call: DailyCall | null = null;
18
10
 
19
- private nextClipTime: number = 0;
20
-
21
- private decodeQueue: QueueObject<ArrayBuffer>;
22
-
23
- constructor(apiToken: string, apiUrl?: string) {
11
+ constructor(apiToken: string, apiBaseUrl?: string) {
12
+ super();
13
+ client.baseUrl = apiBaseUrl ?? "https://api.vapi.ai";
24
14
  client.setSecurityData(apiToken);
25
- client.baseUrl = apiUrl ?? "https://api.vapi.ai";
26
-
27
- this.decodeQueue = this.createQueue();
28
- }
29
-
30
- createQueue() {
31
- return async.queue<ArrayBuffer>((task, callback) => {
32
- this.source = this.audioContext.createBufferSource();
33
-
34
- this.audioContext.decodeAudioData(task, (buffer) => {
35
- if (!this.source) return;
36
-
37
- this.source.buffer = buffer;
38
- this.source.connect(this.audioContext.destination);
39
-
40
- // If the next clip time is in the past, set it to the current time
41
- this.nextClipTime = Math.max(
42
- this.nextClipTime,
43
- this.audioContext.currentTime
44
- );
45
-
46
- // Start the source at the next clip time
47
- this.source.start(this.nextClipTime);
48
-
49
- // Schedule the next clip time
50
- this.nextClipTime += buffer.duration;
51
-
52
- callback();
53
- });
54
- }, 1);
55
15
  }
56
16
 
57
17
  start(assistant: CreateAssistantDTO | string): void {
@@ -63,85 +23,26 @@ export default class Vapi {
63
23
 
64
24
  client.call
65
25
  .callControllerCreateWebCall({
26
+ assistant: typeof assistant === "string" ? undefined : assistant,
66
27
  assistantId: typeof assistant === "string" ? assistant : undefined,
67
- assistant: typeof assistant === "object" ? assistant : undefined,
68
28
  })
69
29
  .then(({ data }) => {
70
- const { callId, url } = data;
71
- const socket = new WebSocket(url);
30
+ const { url } = data;
72
31
 
73
- this.ws = socket;
74
-
75
- socket.onopen = () => {
76
- socket.send(JSON.stringify({ event: "start", callId }));
77
- this.startRecording();
78
- };
79
- socket.onmessage = (event) => {
80
- if (!socket) return;
81
- this.onMessage(socket, event);
82
- };
83
- socket.onclose = () => {
84
- this.stop();
85
- };
32
+ this.call = DailyIframe.createCallObject({
33
+ url,
34
+ audioSource: true,
35
+ videoSource: false,
36
+ });
86
37
  })
87
38
  .catch((error) => {
88
39
  console.error(error);
89
40
  });
90
41
  }
91
42
 
92
- private startRecording(): void {
93
- navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
94
- this.mediaRecorder = new MediaRecorder(stream, {
95
- mimeType: "audio/webm;codecs=opus",
96
- });
97
-
98
- this.mediaRecorder.start(100);
99
- this.mediaRecorder.ondataavailable = (event: BlobEvent) => {
100
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
101
- this.ws.send(event.data);
102
- }
103
- };
104
- });
105
- }
106
-
107
- private onMessage(ws: WebSocket, event: MessageEvent): void {
108
- const data = JSON.parse(event.data);
109
- if (data.event === "media") {
110
- const audioData = decode(data.media.payload);
111
- this.decodeQueue.push(audioData);
112
- }
113
- if (data.event === "clear") {
114
- this.clear();
115
- }
116
- }
117
-
118
- clear(): void {
119
- this.decodeQueue.kill();
120
- this.decodeQueue = this.createQueue();
121
-
122
- // Stop the source if it exists
123
- if (this.source) {
124
- this.source.stop();
125
- this.source.disconnect();
126
- this.source = null;
127
- }
128
-
129
- // Reset the next clip time
130
- this.nextClipTime = 0;
131
- }
132
-
133
43
  stop(): void {
134
- if (this.source) {
135
- this.source.stop();
136
- }
137
44
  this.started = false;
138
- if (this.ws) {
139
- this.ws.close();
140
- }
141
- this.ws = null;
142
- if (this.mediaRecorder) {
143
- this.mediaRecorder.stop();
144
- }
145
- this.mediaRecorder = null;
45
+ this.call?.destroy();
46
+ this.call = null;
146
47
  }
147
48
  }
@@ -1,97 +0,0 @@
1
- import async, { QueueObject } from "async";
2
-
3
- import EventEmitter from "events";
4
-
5
- declare global {
6
- interface Window {
7
- ManagedMediaSource: any;
8
- }
9
- }
10
-
11
- // ManagedMediaSource needed for iOS Safari
12
- function getMediaSource() {
13
- if (window.ManagedMediaSource) {
14
- return new window.ManagedMediaSource();
15
- }
16
- if (window.MediaSource) {
17
- return new window.MediaSource();
18
- }
19
-
20
- return null;
21
- }
22
-
23
- class AtomicMediaSource extends EventEmitter {
24
- mediaSource: any;
25
- sourceBuffer: SourceBuffer | null = null;
26
- private operationsQueue: QueueObject<() => Promise<void>>;
27
-
28
- constructor() {
29
- super();
30
- this.mediaSource = getMediaSource();
31
- this.operationsQueue = this.createOperationQueue();
32
-
33
- if (!this.mediaSource) {
34
- console.error("MediaSource is not supported in your browser.");
35
- // Display a message to the user, or handle this situation in another way
36
- return;
37
- }
38
-
39
- this.mediaSource.addEventListener("sourceopen", () => {
40
- this.mediaSource.duration = Number.POSITIVE_INFINITY;
41
- this.sourceBuffer = this.mediaSource.addSourceBuffer(
42
- 'audio/webm; codecs="opus"'
43
- );
44
- if (this.sourceBuffer) {
45
- this.sourceBuffer.mode = "sequence";
46
- }
47
- });
48
- }
49
-
50
- public appendBuffer(buffer: ArrayBuffer) {
51
- this.operationsQueue.push(() => {
52
- return new Promise((resolve, reject) => {
53
- if (this.sourceBuffer) {
54
- this.sourceBuffer.addEventListener(
55
- "updateend",
56
- () => {
57
- resolve();
58
- },
59
- { once: true }
60
- );
61
- this.sourceBuffer.appendBuffer(buffer);
62
- } else {
63
- reject();
64
- }
65
- });
66
- });
67
- }
68
-
69
- public clearBuffer() {
70
- // this.operationsQueue.push(() => {
71
- // return new Promise((resolve, reject) => {
72
- // if (this.sourceBuffer) {
73
- // this.sourceBuffer.addEventListener(
74
- // "updateend",
75
- // () => {
76
- // resolve();
77
- // },
78
- // { once: true }
79
- // );
80
- // if (this.sourceBuffer.buffered.length > 0) {
81
- // this.sourceBuffer.remove(0, this.sourceBuffer.buffered.end(0));
82
- // }
83
- // } else {
84
- // reject();
85
- // }
86
- // });
87
- // });
88
- }
89
-
90
- private createOperationQueue() {
91
- return async.queue<() => Promise<void>>((task, callback) => {
92
- task().then(() => callback());
93
- }, 1);
94
- }
95
- }
96
-
97
- export default AtomicMediaSource;
package/player.ts DELETED
@@ -1,27 +0,0 @@
1
- import AtomicMediaSource from "./AtomicMediaSource";
2
- import EventEmitter from "events";
3
-
4
- export class ContinuousPlayer extends EventEmitter {
5
- private audioCtx: AudioContext;
6
- private source: AudioBufferSourceNode | null = null;
7
-
8
- constructor() {
9
- super();
10
-
11
- this.audioCtx = new AudioContext();
12
- }
13
-
14
- start() {}
15
-
16
- async playChunk(audioData: ArrayBuffer) {
17
- const audioBuffer = await this.audioCtx.decodeAudioData(audioData);
18
- this.source = this.audioCtx.createBufferSource();
19
- this.source.buffer = audioBuffer;
20
- this.source.connect(this.audioCtx.destination);
21
- this.source.start();
22
- this.emit("speech-start");
23
- this.source.onended = () => this.emit("speech-end");
24
- }
25
-
26
- clear() {}
27
- }
package/silent.mp3 DELETED
Binary file