@vapi-ai/web 1.0.62 → 1.0.63
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 +2 -2
- package/api.ts +32 -95
- package/dist/api.d.ts +3 -3
- package/dist/api.js +4 -16
- package/dist/player.d.ts +7 -6
- package/dist/player.js +42 -51
- package/dist/vapi.d.ts +1 -1
- package/dist/vapi.js +4 -3
- package/package.json +1 -1
- package/player.ts +51 -59
- package/vapi.ts +4 -3
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Then, create a new instance of the Vapi class, passing your Client Token as a pa
|
|
|
24
24
|
const vapi = new Vapi('your-web-token');
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
You can start a new call by calling the `start` method and passing an `
|
|
27
|
+
You can start a new call by calling the `start` method and passing an `assistant` object or `assistantId`:
|
|
28
28
|
|
|
29
29
|
```javascript
|
|
30
30
|
vapi.start({
|
|
@@ -34,7 +34,7 @@ vapi.start({
|
|
|
34
34
|
});
|
|
35
35
|
```
|
|
36
36
|
```javascript
|
|
37
|
-
vapi.start('your-
|
|
37
|
+
vapi.start('your-assistant-id');
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
The `start` method will initiate a new call.
|
package/api.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
export interface PhoneCallParams {
|
|
13
13
|
callSid?: string;
|
|
14
|
-
status?: string
|
|
14
|
+
status?: string;
|
|
15
15
|
customerPhoneNumber: string;
|
|
16
16
|
twilioPhoneNumber: string;
|
|
17
17
|
twilioAccountSid: string;
|
|
@@ -227,8 +227,8 @@ export interface PhoneNumber {
|
|
|
227
227
|
stripeSubscriptionId?: string;
|
|
228
228
|
number: string;
|
|
229
229
|
assistantId?: string;
|
|
230
|
-
twilioAccountSid
|
|
231
|
-
twilioAuthToken
|
|
230
|
+
twilioAccountSid: string;
|
|
231
|
+
twilioAuthToken: string;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
export interface ImportTwilioPhoneNumberDto {
|
|
@@ -266,22 +266,16 @@ export interface FullRequestParams extends Omit<RequestInit, "body"> {
|
|
|
266
266
|
cancelToken?: CancelToken;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
export type RequestParams = Omit<
|
|
270
|
-
FullRequestParams,
|
|
271
|
-
"body" | "method" | "query" | "path"
|
|
272
|
-
>;
|
|
269
|
+
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
|
|
273
270
|
|
|
274
271
|
export interface ApiConfig<SecurityDataType = unknown> {
|
|
275
272
|
baseUrl?: string;
|
|
276
273
|
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
|
|
277
|
-
securityWorker?: (
|
|
278
|
-
securityData: SecurityDataType | null
|
|
279
|
-
) => Promise<RequestParams | void> | RequestParams | void;
|
|
274
|
+
securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
|
|
280
275
|
customFetch?: typeof fetch;
|
|
281
276
|
}
|
|
282
277
|
|
|
283
|
-
export interface HttpResponse<D extends unknown, E extends unknown = unknown>
|
|
284
|
-
extends Response {
|
|
278
|
+
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
|
|
285
279
|
data: D;
|
|
286
280
|
error: E;
|
|
287
281
|
}
|
|
@@ -300,8 +294,7 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
300
294
|
private securityData: SecurityDataType | null = null;
|
|
301
295
|
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
|
|
302
296
|
private abortControllers = new Map<CancelToken, AbortController>();
|
|
303
|
-
private customFetch = (...fetchParams: Parameters<typeof fetch>) =>
|
|
304
|
-
fetch(...fetchParams);
|
|
297
|
+
private customFetch = (...fetchParams: Parameters<typeof fetch>) => fetch(...fetchParams);
|
|
305
298
|
|
|
306
299
|
private baseApiParams: RequestParams = {
|
|
307
300
|
credentials: "same-origin",
|
|
@@ -320,9 +313,7 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
320
313
|
|
|
321
314
|
protected encodeQueryParam(key: string, value: any) {
|
|
322
315
|
const encodedKey = encodeURIComponent(key);
|
|
323
|
-
return `${encodedKey}=${encodeURIComponent(
|
|
324
|
-
typeof value === "number" ? value : `${value}`
|
|
325
|
-
)}`;
|
|
316
|
+
return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
|
|
326
317
|
}
|
|
327
318
|
|
|
328
319
|
protected addQueryParam(query: QueryParamsType, key: string) {
|
|
@@ -336,15 +327,9 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
336
327
|
|
|
337
328
|
protected toQueryString(rawQuery?: QueryParamsType): string {
|
|
338
329
|
const query = rawQuery || {};
|
|
339
|
-
const keys = Object.keys(query).filter(
|
|
340
|
-
(key) => "undefined" !== typeof query[key]
|
|
341
|
-
);
|
|
330
|
+
const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
|
|
342
331
|
return keys
|
|
343
|
-
.map((key) =>
|
|
344
|
-
Array.isArray(query[key])
|
|
345
|
-
? this.addArrayQueryParam(query, key)
|
|
346
|
-
: this.addQueryParam(query, key)
|
|
347
|
-
)
|
|
332
|
+
.map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)))
|
|
348
333
|
.join("&");
|
|
349
334
|
}
|
|
350
335
|
|
|
@@ -355,13 +340,8 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
355
340
|
|
|
356
341
|
private contentFormatters: Record<ContentType, (input: any) => any> = {
|
|
357
342
|
[ContentType.Json]: (input: any) =>
|
|
358
|
-
input !== null && (typeof input === "object" || typeof input === "string")
|
|
359
|
-
|
|
360
|
-
: input,
|
|
361
|
-
[ContentType.Text]: (input: any) =>
|
|
362
|
-
input !== null && typeof input !== "string"
|
|
363
|
-
? JSON.stringify(input)
|
|
364
|
-
: input,
|
|
343
|
+
input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
|
|
344
|
+
[ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input),
|
|
365
345
|
[ContentType.FormData]: (input: any) =>
|
|
366
346
|
Object.keys(input || {}).reduce((formData, key) => {
|
|
367
347
|
const property = input[key];
|
|
@@ -371,17 +351,14 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
371
351
|
? property
|
|
372
352
|
: typeof property === "object" && property !== null
|
|
373
353
|
? JSON.stringify(property)
|
|
374
|
-
: `${property}
|
|
354
|
+
: `${property}`,
|
|
375
355
|
);
|
|
376
356
|
return formData;
|
|
377
357
|
}, new FormData()),
|
|
378
358
|
[ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
|
|
379
359
|
};
|
|
380
360
|
|
|
381
|
-
protected mergeRequestParams(
|
|
382
|
-
params1: RequestParams,
|
|
383
|
-
params2?: RequestParams
|
|
384
|
-
): RequestParams {
|
|
361
|
+
protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
|
|
385
362
|
return {
|
|
386
363
|
...this.baseApiParams,
|
|
387
364
|
...params1,
|
|
@@ -394,9 +371,7 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
394
371
|
};
|
|
395
372
|
}
|
|
396
373
|
|
|
397
|
-
protected createAbortSignal = (
|
|
398
|
-
cancelToken: CancelToken
|
|
399
|
-
): AbortSignal | undefined => {
|
|
374
|
+
protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
|
|
400
375
|
if (this.abortControllers.has(cancelToken)) {
|
|
401
376
|
const abortController = this.abortControllers.get(cancelToken);
|
|
402
377
|
if (abortController) {
|
|
@@ -440,28 +415,15 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
440
415
|
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
|
|
441
416
|
const responseFormat = format || requestParams.format;
|
|
442
417
|
|
|
443
|
-
return this.customFetch(
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
? { "Content-Type": type }
|
|
453
|
-
: {}),
|
|
454
|
-
},
|
|
455
|
-
signal:
|
|
456
|
-
(cancelToken
|
|
457
|
-
? this.createAbortSignal(cancelToken)
|
|
458
|
-
: requestParams.signal) || null,
|
|
459
|
-
body:
|
|
460
|
-
typeof body === "undefined" || body === null
|
|
461
|
-
? null
|
|
462
|
-
: payloadFormatter(body),
|
|
463
|
-
}
|
|
464
|
-
).then(async (response) => {
|
|
418
|
+
return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, {
|
|
419
|
+
...requestParams,
|
|
420
|
+
headers: {
|
|
421
|
+
...(requestParams.headers || {}),
|
|
422
|
+
...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
|
|
423
|
+
},
|
|
424
|
+
signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
|
|
425
|
+
body: typeof body === "undefined" || body === null ? null : payloadFormatter(body),
|
|
426
|
+
}).then(async (response) => {
|
|
465
427
|
const r = response as HttpResponse<T, E>;
|
|
466
428
|
r.data = null as unknown as T;
|
|
467
429
|
r.error = null as unknown as E;
|
|
@@ -499,9 +461,7 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
499
461
|
*
|
|
500
462
|
* API for making voice assistants
|
|
501
463
|
*/
|
|
502
|
-
export class Api<
|
|
503
|
-
SecurityDataType extends unknown,
|
|
504
|
-
> extends HttpClient<SecurityDataType> {
|
|
464
|
+
export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
|
|
505
465
|
call = {
|
|
506
466
|
/**
|
|
507
467
|
* No description
|
|
@@ -543,10 +503,7 @@ export class Api<
|
|
|
543
503
|
* @summary Create Phone Call
|
|
544
504
|
* @request POST:/call/phone
|
|
545
505
|
*/
|
|
546
|
-
callControllerCreatePhoneCall: (
|
|
547
|
-
data: CreateOutboundCallDto,
|
|
548
|
-
params: RequestParams = {}
|
|
549
|
-
) =>
|
|
506
|
+
callControllerCreatePhoneCall: (data: CreateOutboundCallDto, params: RequestParams = {}) =>
|
|
550
507
|
this.request<Call, any>({
|
|
551
508
|
path: `/call/phone`,
|
|
552
509
|
method: "POST",
|
|
@@ -564,10 +521,7 @@ export class Api<
|
|
|
564
521
|
* @summary Create Web Call
|
|
565
522
|
* @request POST:/call/web
|
|
566
523
|
*/
|
|
567
|
-
callControllerCreateWebCall: (
|
|
568
|
-
data: CreateWebCallDto,
|
|
569
|
-
params: RequestParams = {}
|
|
570
|
-
) =>
|
|
524
|
+
callControllerCreateWebCall: (data: CreateWebCallDto, params: RequestParams = {}) =>
|
|
571
525
|
this.request<WebCallResponseDto, any>({
|
|
572
526
|
path: `/call/web`,
|
|
573
527
|
method: "POST",
|
|
@@ -586,10 +540,7 @@ export class Api<
|
|
|
586
540
|
* @summary Create Assistant
|
|
587
541
|
* @request POST:/assistant
|
|
588
542
|
*/
|
|
589
|
-
assistantControllerCreate: (
|
|
590
|
-
data: CreateAssistantDTO,
|
|
591
|
-
params: RequestParams = {}
|
|
592
|
-
) =>
|
|
543
|
+
assistantControllerCreate: (data: CreateAssistantDTO, params: RequestParams = {}) =>
|
|
593
544
|
this.request<Assistant, any>({
|
|
594
545
|
path: `/assistant`,
|
|
595
546
|
method: "POST",
|
|
@@ -639,11 +590,7 @@ export class Api<
|
|
|
639
590
|
* @summary Update Assistant
|
|
640
591
|
* @request PATCH:/assistant/{id}
|
|
641
592
|
*/
|
|
642
|
-
assistantControllerUpdate: (
|
|
643
|
-
id: string,
|
|
644
|
-
data: UpdateAssistantDTO,
|
|
645
|
-
params: RequestParams = {}
|
|
646
|
-
) =>
|
|
593
|
+
assistantControllerUpdate: (id: string, data: UpdateAssistantDTO, params: RequestParams = {}) =>
|
|
647
594
|
this.request<Assistant, any>({
|
|
648
595
|
path: `/assistant/${id}`,
|
|
649
596
|
method: "PATCH",
|
|
@@ -677,10 +624,7 @@ export class Api<
|
|
|
677
624
|
* @summary Buy Phone Number
|
|
678
625
|
* @request POST:/phone-number/buy
|
|
679
626
|
*/
|
|
680
|
-
phoneNumberControllerBuy: (
|
|
681
|
-
data: BuyPhoneNumberDto,
|
|
682
|
-
params: RequestParams = {}
|
|
683
|
-
) =>
|
|
627
|
+
phoneNumberControllerBuy: (data: BuyPhoneNumberDto, params: RequestParams = {}) =>
|
|
684
628
|
this.request<PhoneNumber, any>({
|
|
685
629
|
path: `/phone-number/buy`,
|
|
686
630
|
method: "POST",
|
|
@@ -698,10 +642,7 @@ export class Api<
|
|
|
698
642
|
* @summary Import Twilio Number
|
|
699
643
|
* @request POST:/phone-number/import
|
|
700
644
|
*/
|
|
701
|
-
phoneNumberControllerImport: (
|
|
702
|
-
data: ImportTwilioPhoneNumberDto,
|
|
703
|
-
params: RequestParams = {}
|
|
704
|
-
) =>
|
|
645
|
+
phoneNumberControllerImport: (data: ImportTwilioPhoneNumberDto, params: RequestParams = {}) =>
|
|
705
646
|
this.request<PhoneNumber, any>({
|
|
706
647
|
path: `/phone-number/import`,
|
|
707
648
|
method: "POST",
|
|
@@ -751,11 +692,7 @@ export class Api<
|
|
|
751
692
|
* @summary Update Phone Number
|
|
752
693
|
* @request PATCH:/phone-number/{id}
|
|
753
694
|
*/
|
|
754
|
-
phoneNumberControllerUpdate: (
|
|
755
|
-
id: string,
|
|
756
|
-
data: UpdatePhoneNumberDto,
|
|
757
|
-
params: RequestParams = {}
|
|
758
|
-
) =>
|
|
695
|
+
phoneNumberControllerUpdate: (id: string, data: UpdatePhoneNumberDto, params: RequestParams = {}) =>
|
|
759
696
|
this.request<PhoneNumber, any>({
|
|
760
697
|
path: `/phone-number/${id}`,
|
|
761
698
|
method: "PATCH",
|
package/dist/api.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export interface PhoneCallParams {
|
|
2
2
|
callSid?: string;
|
|
3
|
-
status?: string
|
|
3
|
+
status?: string;
|
|
4
4
|
customerPhoneNumber: string;
|
|
5
5
|
twilioPhoneNumber: string;
|
|
6
6
|
twilioAccountSid: string;
|
|
@@ -154,8 +154,8 @@ export interface PhoneNumber {
|
|
|
154
154
|
stripeSubscriptionId?: string;
|
|
155
155
|
number: string;
|
|
156
156
|
assistantId?: string;
|
|
157
|
-
twilioAccountSid
|
|
158
|
-
twilioAuthToken
|
|
157
|
+
twilioAccountSid: string;
|
|
158
|
+
twilioAuthToken: string;
|
|
159
159
|
}
|
|
160
160
|
export interface ImportTwilioPhoneNumberDto {
|
|
161
161
|
/** This is the area code of the phone number to purchase. */
|
package/dist/api.js
CHANGED
|
@@ -54,12 +54,8 @@ class HttpClient {
|
|
|
54
54
|
this.securityData = data;
|
|
55
55
|
};
|
|
56
56
|
this.contentFormatters = {
|
|
57
|
-
[ContentType.Json]: (input) => input !== null && (typeof input === "object" || typeof input === "string")
|
|
58
|
-
|
|
59
|
-
: input,
|
|
60
|
-
[ContentType.Text]: (input) => input !== null && typeof input !== "string"
|
|
61
|
-
? JSON.stringify(input)
|
|
62
|
-
: input,
|
|
57
|
+
[ContentType.Json]: (input) => input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
|
|
58
|
+
[ContentType.Text]: (input) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input),
|
|
63
59
|
[ContentType.FormData]: (input) => Object.keys(input || {}).reduce((formData, key) => {
|
|
64
60
|
const property = input[key];
|
|
65
61
|
formData.append(key, property instanceof Blob
|
|
@@ -100,13 +96,7 @@ class HttpClient {
|
|
|
100
96
|
const queryString = query && this.toQueryString(query);
|
|
101
97
|
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
|
|
102
98
|
const responseFormat = format || requestParams.format;
|
|
103
|
-
return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, Object.assign(Object.assign({}, requestParams), { headers: Object.assign(Object.assign({}, (requestParams.headers || {})), (type && type !== ContentType.FormData
|
|
104
|
-
? { "Content-Type": type }
|
|
105
|
-
: {})), signal: (cancelToken
|
|
106
|
-
? this.createAbortSignal(cancelToken)
|
|
107
|
-
: requestParams.signal) || null, body: typeof body === "undefined" || body === null
|
|
108
|
-
? null
|
|
109
|
-
: payloadFormatter(body) })).then((response) => __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, Object.assign(Object.assign({}, requestParams), { headers: Object.assign(Object.assign({}, (requestParams.headers || {})), (type && type !== ContentType.FormData ? { "Content-Type": type } : {})), signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null, body: typeof body === "undefined" || body === null ? null : payloadFormatter(body) })).then((response) => __awaiter(this, void 0, void 0, function* () {
|
|
110
100
|
const r = response;
|
|
111
101
|
r.data = null;
|
|
112
102
|
r.error = null;
|
|
@@ -151,9 +141,7 @@ class HttpClient {
|
|
|
151
141
|
const query = rawQuery || {};
|
|
152
142
|
const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
|
|
153
143
|
return keys
|
|
154
|
-
.map((key) => Array.isArray(query[key])
|
|
155
|
-
? this.addArrayQueryParam(query, key)
|
|
156
|
-
: this.addQueryParam(query, key))
|
|
144
|
+
.map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)))
|
|
157
145
|
.join("&");
|
|
158
146
|
}
|
|
159
147
|
addQueryParams(rawQuery) {
|
package/dist/player.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import EventEmitter from "events";
|
|
3
3
|
export declare class ContinuousPlayer extends EventEmitter {
|
|
4
|
-
private
|
|
5
|
-
private
|
|
4
|
+
private mediaSource;
|
|
5
|
+
private sourceBuffer;
|
|
6
|
+
private audio;
|
|
6
7
|
private audioQueue;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
private createQueue;
|
|
8
|
+
constructor();
|
|
9
|
+
start(): void;
|
|
10
10
|
playChunk(audioData: ArrayBuffer): void;
|
|
11
|
-
|
|
11
|
+
private appendNextChunk;
|
|
12
|
+
clear(): void;
|
|
12
13
|
}
|
package/dist/player.js
CHANGED
|
@@ -1,68 +1,59 @@
|
|
|
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;
|
|
16
|
-
const async_1 = require("async");
|
|
7
|
+
const async_1 = __importDefault(require("async"));
|
|
17
8
|
const events_1 = __importDefault(require("events"));
|
|
18
9
|
class ContinuousPlayer extends events_1.default {
|
|
19
|
-
constructor(
|
|
10
|
+
constructor() {
|
|
20
11
|
super();
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// Start the source at the next start time
|
|
37
|
-
source.start(this.nextStartTime);
|
|
38
|
-
// Schedule the crossfade
|
|
39
|
-
gainNode.gain.setValueAtTime(0, this.nextStartTime);
|
|
40
|
-
gainNode.gain.linearRampToValueAtTime(1, this.nextStartTime + this.crossFadeDuration);
|
|
41
|
-
gainNode.gain.setValueAtTime(1, this.nextStartTime + audioBuffer.duration - this.crossFadeDuration);
|
|
42
|
-
gainNode.gain.linearRampToValueAtTime(0, this.nextStartTime + audioBuffer.duration);
|
|
43
|
-
source.onended = () => {
|
|
44
|
-
this.nextStartTime += audioBuffer.duration;
|
|
45
|
-
callback();
|
|
46
|
-
};
|
|
47
|
-
}), 1);
|
|
48
|
-
q.drain(() => {
|
|
49
|
-
this.emit("speech-end");
|
|
50
|
-
});
|
|
51
|
-
q.saturated(() => {
|
|
52
|
-
this.emit("speech-start");
|
|
12
|
+
this.sourceBuffer = null;
|
|
13
|
+
this.mediaSource = new MediaSource();
|
|
14
|
+
this.audio = document.createElement("audio");
|
|
15
|
+
this.audio.src = URL.createObjectURL(this.mediaSource);
|
|
16
|
+
document.body.appendChild(this.audio);
|
|
17
|
+
this.audioQueue = async_1.default.queue((task, callback) => {
|
|
18
|
+
this.appendNextChunk(task, callback);
|
|
19
|
+
}, 1);
|
|
20
|
+
this.mediaSource.addEventListener("sourceopen", () => {
|
|
21
|
+
this.sourceBuffer = this.mediaSource.addSourceBuffer('audio/webm; codecs="opus"');
|
|
22
|
+
this.sourceBuffer.mode = "sequence";
|
|
23
|
+
this.sourceBuffer.addEventListener("updateend", () => {
|
|
24
|
+
this.audioQueue.resume();
|
|
25
|
+
console.log("RESUMING");
|
|
26
|
+
});
|
|
53
27
|
});
|
|
54
|
-
|
|
28
|
+
}
|
|
29
|
+
start() {
|
|
30
|
+
this.audio.play();
|
|
55
31
|
}
|
|
56
32
|
playChunk(audioData) {
|
|
57
|
-
|
|
58
|
-
|
|
33
|
+
this.audioQueue.push(audioData);
|
|
34
|
+
}
|
|
35
|
+
appendNextChunk(chunk, callback) {
|
|
36
|
+
if (this.sourceBuffer && !this.sourceBuffer.updating) {
|
|
37
|
+
this.audioQueue.pause();
|
|
38
|
+
this.sourceBuffer.appendBuffer(chunk);
|
|
39
|
+
}
|
|
40
|
+
callback();
|
|
59
41
|
}
|
|
60
42
|
clear() {
|
|
61
|
-
|
|
62
|
-
this.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
if (this.sourceBuffer) {
|
|
44
|
+
if (this.sourceBuffer.updating) {
|
|
45
|
+
this.sourceBuffer.addEventListener("updateend", () => this.clear(), {
|
|
46
|
+
once: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.sourceBuffer.abort();
|
|
51
|
+
while (this.sourceBuffer.buffered.length > 0) {
|
|
52
|
+
this.sourceBuffer.remove(0, this.sourceBuffer.buffered.end(0));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.sourceBuffer.timestampOffset = 0;
|
|
56
|
+
}
|
|
66
57
|
}
|
|
67
58
|
}
|
|
68
59
|
exports.ContinuousPlayer = ContinuousPlayer;
|
package/dist/vapi.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export default class Vapi extends EventEmitter {
|
|
|
7
7
|
private mediaRecorder;
|
|
8
8
|
private player;
|
|
9
9
|
constructor(apiToken: string);
|
|
10
|
-
start(
|
|
10
|
+
start(assistant: CreateAssistantDTO | string): void;
|
|
11
11
|
private startRecording;
|
|
12
12
|
private onMessage;
|
|
13
13
|
clear(): void;
|
package/dist/vapi.js
CHANGED
|
@@ -18,15 +18,16 @@ class Vapi extends events_1.default {
|
|
|
18
18
|
this.player.on("speech-start", () => this.emit("speech-start"));
|
|
19
19
|
this.player.on("speech-end", () => this.emit("speech-end"));
|
|
20
20
|
}
|
|
21
|
-
start(
|
|
21
|
+
start(assistant) {
|
|
22
22
|
if (this.started) {
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
this.started = true;
|
|
26
|
+
this.player.start();
|
|
26
27
|
client_1.client.call
|
|
27
28
|
.callControllerCreateWebCall({
|
|
28
|
-
assistant: typeof
|
|
29
|
-
assistantId: typeof
|
|
29
|
+
assistant: typeof assistant === "string" ? undefined : assistant,
|
|
30
|
+
assistantId: typeof assistant === "string" ? assistant : undefined,
|
|
30
31
|
})
|
|
31
32
|
.then(({ data }) => {
|
|
32
33
|
const { callId, url } = data;
|
package/package.json
CHANGED
package/player.ts
CHANGED
|
@@ -1,78 +1,70 @@
|
|
|
1
|
-
import { QueueObject
|
|
1
|
+
import async, { QueueObject } from "async";
|
|
2
2
|
|
|
3
3
|
import EventEmitter from "events";
|
|
4
4
|
|
|
5
5
|
export class ContinuousPlayer extends EventEmitter {
|
|
6
|
-
private
|
|
7
|
-
private
|
|
8
|
-
private
|
|
9
|
-
private
|
|
6
|
+
private mediaSource: MediaSource;
|
|
7
|
+
private sourceBuffer: SourceBuffer | null = null;
|
|
8
|
+
private audio: HTMLAudioElement;
|
|
9
|
+
private audioQueue: QueueObject<ArrayBuffer>;
|
|
10
10
|
|
|
11
|
-
constructor(
|
|
11
|
+
constructor() {
|
|
12
12
|
super();
|
|
13
|
-
this.
|
|
14
|
-
this.nextStartTime = 0;
|
|
15
|
-
this.audioQueue = this.createQueue();
|
|
16
|
-
this.crossFadeDuration = crossFadeDuration;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
private createQueue() {
|
|
20
|
-
const q = queue(
|
|
21
|
-
async (audioPromise: Promise<AudioBuffer>, callback: Function) => {
|
|
22
|
-
const audioBuffer = await audioPromise;
|
|
23
|
-
const source = this.audioCtx.createBufferSource();
|
|
24
|
-
source.buffer = audioBuffer;
|
|
25
|
-
source.connect(this.audioCtx.destination);
|
|
13
|
+
this.mediaSource = new MediaSource();
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
gainNode.connect(this.audioCtx.destination);
|
|
15
|
+
this.audio = document.createElement("audio");
|
|
16
|
+
this.audio.src = URL.createObjectURL(this.mediaSource);
|
|
17
|
+
document.body.appendChild(this.audio);
|
|
31
18
|
|
|
32
|
-
|
|
33
|
-
|
|
19
|
+
this.audioQueue = async.queue((task, callback) => {
|
|
20
|
+
this.appendNextChunk(task, callback);
|
|
21
|
+
}, 1);
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
-
gainNode.gain.setValueAtTime(
|
|
42
|
-
1,
|
|
43
|
-
this.nextStartTime + audioBuffer.duration - this.crossFadeDuration
|
|
44
|
-
);
|
|
45
|
-
gainNode.gain.linearRampToValueAtTime(
|
|
46
|
-
0,
|
|
47
|
-
this.nextStartTime + audioBuffer.duration
|
|
48
|
-
);
|
|
23
|
+
this.mediaSource.addEventListener("sourceopen", () => {
|
|
24
|
+
this.sourceBuffer = this.mediaSource.addSourceBuffer(
|
|
25
|
+
'audio/webm; codecs="opus"'
|
|
26
|
+
);
|
|
27
|
+
this.sourceBuffer.mode = "sequence";
|
|
49
28
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
1
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
q.drain(() => {
|
|
59
|
-
this.emit("speech-end");
|
|
60
|
-
});
|
|
61
|
-
q.saturated(() => {
|
|
62
|
-
this.emit("speech-start");
|
|
29
|
+
this.sourceBuffer.addEventListener("updateend", () => {
|
|
30
|
+
this.audioQueue.resume();
|
|
31
|
+
console.log("RESUMING");
|
|
32
|
+
});
|
|
63
33
|
});
|
|
34
|
+
}
|
|
64
35
|
|
|
65
|
-
|
|
36
|
+
start() {
|
|
37
|
+
this.audio.play();
|
|
66
38
|
}
|
|
67
39
|
|
|
68
40
|
playChunk(audioData: ArrayBuffer) {
|
|
69
|
-
|
|
70
|
-
|
|
41
|
+
this.audioQueue.push(audioData);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private appendNextChunk(
|
|
45
|
+
chunk: ArrayBuffer,
|
|
46
|
+
callback: async.ErrorCallback<Error>
|
|
47
|
+
): void {
|
|
48
|
+
if (this.sourceBuffer && !this.sourceBuffer.updating) {
|
|
49
|
+
this.audioQueue.pause();
|
|
50
|
+
this.sourceBuffer.appendBuffer(chunk);
|
|
51
|
+
}
|
|
52
|
+
callback();
|
|
71
53
|
}
|
|
72
54
|
|
|
73
|
-
|
|
74
|
-
this.
|
|
75
|
-
|
|
76
|
-
|
|
55
|
+
clear(): void {
|
|
56
|
+
if (this.sourceBuffer) {
|
|
57
|
+
if (this.sourceBuffer.updating) {
|
|
58
|
+
this.sourceBuffer.addEventListener("updateend", () => this.clear(), {
|
|
59
|
+
once: true,
|
|
60
|
+
});
|
|
61
|
+
} else {
|
|
62
|
+
this.sourceBuffer.abort();
|
|
63
|
+
while (this.sourceBuffer.buffered.length > 0) {
|
|
64
|
+
this.sourceBuffer.remove(0, this.sourceBuffer.buffered.end(0));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
this.sourceBuffer.timestampOffset = 0;
|
|
68
|
+
}
|
|
77
69
|
}
|
|
78
70
|
}
|
package/vapi.ts
CHANGED
|
@@ -19,17 +19,18 @@ export default class Vapi extends EventEmitter {
|
|
|
19
19
|
this.player.on("speech-end", () => this.emit("speech-end"));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
start(
|
|
22
|
+
start(assistant: CreateAssistantDTO | string): void {
|
|
23
23
|
if (this.started) {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
this.started = true;
|
|
28
|
+
this.player.start();
|
|
28
29
|
|
|
29
30
|
client.call
|
|
30
31
|
.callControllerCreateWebCall({
|
|
31
|
-
assistant: typeof
|
|
32
|
-
assistantId: typeof
|
|
32
|
+
assistant: typeof assistant === "string" ? undefined : assistant,
|
|
33
|
+
assistantId: typeof assistant === "string" ? assistant : undefined,
|
|
33
34
|
})
|
|
34
35
|
.then(({ data }) => {
|
|
35
36
|
const { callId, url } = data;
|