assemblyai 4.4.3 → 4.4.5
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/CHANGELOG.md +11 -0
- package/README.md +9 -1
- package/dist/assemblyai.streaming.umd.js +288 -0
- package/dist/assemblyai.streaming.umd.min.js +1 -0
- package/dist/assemblyai.umd.js +54 -3
- package/dist/assemblyai.umd.min.js +1 -1
- package/dist/browser.mjs +54 -3
- package/dist/bun.mjs +54 -3
- package/dist/deno.mjs +54 -3
- package/dist/exports/index.d.ts +2 -0
- package/dist/exports/streaming.d.ts +4 -0
- package/dist/index.cjs +54 -3
- package/dist/index.mjs +54 -3
- package/dist/node.cjs +54 -3
- package/dist/node.mjs +54 -3
- package/dist/services/base.d.ts +1 -0
- package/dist/streaming.browser.mjs +239 -0
- package/dist/streaming.cjs +277 -0
- package/dist/streaming.mjs +274 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/openapi.generated.d.ts +5 -2
- package/dist/types/services/index.d.ts +7 -0
- package/dist/utils/userAgent.d.ts +2 -0
- package/package.json +21 -8
- package/src/exports/index.ts +2 -0
- package/src/exports/streaming.ts +4 -0
- package/src/services/base.ts +22 -3
- package/src/services/index.ts +2 -1
- package/src/types/index.ts +9 -0
- package/src/types/openapi.generated.ts +53 -18
- package/src/types/services/index.ts +8 -0
- package/src/utils/userAgent.ts +51 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
const { WritableStream } = typeof window !== "undefined"
|
|
2
|
+
? window
|
|
3
|
+
: typeof global !== "undefined"
|
|
4
|
+
? global
|
|
5
|
+
: globalThis;
|
|
6
|
+
|
|
7
|
+
const PolyfillWebSocket = WebSocket ?? global?.WebSocket ?? window?.WebSocket ?? self?.WebSocket;
|
|
8
|
+
const factory = (url, params) => {
|
|
9
|
+
if (params) {
|
|
10
|
+
return new PolyfillWebSocket(url, params);
|
|
11
|
+
}
|
|
12
|
+
return new PolyfillWebSocket(url);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
var RealtimeErrorType;
|
|
16
|
+
(function (RealtimeErrorType) {
|
|
17
|
+
RealtimeErrorType[RealtimeErrorType["BadSampleRate"] = 4000] = "BadSampleRate";
|
|
18
|
+
RealtimeErrorType[RealtimeErrorType["AuthFailed"] = 4001] = "AuthFailed";
|
|
19
|
+
// Both InsufficientFunds and FreeAccount error use 4002
|
|
20
|
+
RealtimeErrorType[RealtimeErrorType["InsufficientFundsOrFreeAccount"] = 4002] = "InsufficientFundsOrFreeAccount";
|
|
21
|
+
RealtimeErrorType[RealtimeErrorType["NonexistentSessionId"] = 4004] = "NonexistentSessionId";
|
|
22
|
+
RealtimeErrorType[RealtimeErrorType["SessionExpired"] = 4008] = "SessionExpired";
|
|
23
|
+
RealtimeErrorType[RealtimeErrorType["ClosedSession"] = 4010] = "ClosedSession";
|
|
24
|
+
RealtimeErrorType[RealtimeErrorType["RateLimited"] = 4029] = "RateLimited";
|
|
25
|
+
RealtimeErrorType[RealtimeErrorType["UniqueSessionViolation"] = 4030] = "UniqueSessionViolation";
|
|
26
|
+
RealtimeErrorType[RealtimeErrorType["SessionTimeout"] = 4031] = "SessionTimeout";
|
|
27
|
+
RealtimeErrorType[RealtimeErrorType["AudioTooShort"] = 4032] = "AudioTooShort";
|
|
28
|
+
RealtimeErrorType[RealtimeErrorType["AudioTooLong"] = 4033] = "AudioTooLong";
|
|
29
|
+
RealtimeErrorType[RealtimeErrorType["BadJson"] = 4100] = "BadJson";
|
|
30
|
+
RealtimeErrorType[RealtimeErrorType["BadSchema"] = 4101] = "BadSchema";
|
|
31
|
+
RealtimeErrorType[RealtimeErrorType["TooManyStreams"] = 4102] = "TooManyStreams";
|
|
32
|
+
RealtimeErrorType[RealtimeErrorType["Reconnected"] = 4103] = "Reconnected";
|
|
33
|
+
RealtimeErrorType[RealtimeErrorType["ReconnectAttemptsExhausted"] = 1013] = "ReconnectAttemptsExhausted";
|
|
34
|
+
})(RealtimeErrorType || (RealtimeErrorType = {}));
|
|
35
|
+
const RealtimeErrorMessages = {
|
|
36
|
+
[RealtimeErrorType.BadSampleRate]: "Sample rate must be a positive integer",
|
|
37
|
+
[RealtimeErrorType.AuthFailed]: "Not Authorized",
|
|
38
|
+
[RealtimeErrorType.InsufficientFundsOrFreeAccount]: "Insufficient funds or you are using a free account. This feature is paid-only and requires you to add a credit card. Please visit https://assemblyai.com/dashboard/ to add a credit card to your account.",
|
|
39
|
+
[RealtimeErrorType.NonexistentSessionId]: "Session ID does not exist",
|
|
40
|
+
[RealtimeErrorType.SessionExpired]: "Session has expired",
|
|
41
|
+
[RealtimeErrorType.ClosedSession]: "Session is closed",
|
|
42
|
+
[RealtimeErrorType.RateLimited]: "Rate limited",
|
|
43
|
+
[RealtimeErrorType.UniqueSessionViolation]: "Unique session violation",
|
|
44
|
+
[RealtimeErrorType.SessionTimeout]: "Session Timeout",
|
|
45
|
+
[RealtimeErrorType.AudioTooShort]: "Audio too short",
|
|
46
|
+
[RealtimeErrorType.AudioTooLong]: "Audio too long",
|
|
47
|
+
[RealtimeErrorType.BadJson]: "Bad JSON",
|
|
48
|
+
[RealtimeErrorType.BadSchema]: "Bad schema",
|
|
49
|
+
[RealtimeErrorType.TooManyStreams]: "Too many streams",
|
|
50
|
+
[RealtimeErrorType.Reconnected]: "Reconnected",
|
|
51
|
+
[RealtimeErrorType.ReconnectAttemptsExhausted]: "Reconnect attempts exhausted",
|
|
52
|
+
};
|
|
53
|
+
class RealtimeError extends Error {
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
57
|
+
const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
|
|
58
|
+
const terminateSessionMessage = `{"terminate_session":true}`;
|
|
59
|
+
class RealtimeTranscriber {
|
|
60
|
+
constructor(params) {
|
|
61
|
+
this.listeners = {};
|
|
62
|
+
this.realtimeUrl = params.realtimeUrl ?? defaultRealtimeUrl;
|
|
63
|
+
this.sampleRate = params.sampleRate ?? 16_000;
|
|
64
|
+
this.wordBoost = params.wordBoost;
|
|
65
|
+
this.encoding = params.encoding;
|
|
66
|
+
this.endUtteranceSilenceThreshold = params.endUtteranceSilenceThreshold;
|
|
67
|
+
this.disablePartialTranscripts = params.disablePartialTranscripts;
|
|
68
|
+
if ("token" in params && params.token)
|
|
69
|
+
this.token = params.token;
|
|
70
|
+
if ("apiKey" in params && params.apiKey)
|
|
71
|
+
this.apiKey = params.apiKey;
|
|
72
|
+
if (!(this.token || this.apiKey)) {
|
|
73
|
+
throw new Error("API key or temporary token is required.");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
connectionUrl() {
|
|
77
|
+
const url = new URL(this.realtimeUrl);
|
|
78
|
+
if (url.protocol !== "wss:") {
|
|
79
|
+
throw new Error("Invalid protocol, must be wss");
|
|
80
|
+
}
|
|
81
|
+
const searchParams = new URLSearchParams();
|
|
82
|
+
if (this.token) {
|
|
83
|
+
searchParams.set("token", this.token);
|
|
84
|
+
}
|
|
85
|
+
searchParams.set("sample_rate", this.sampleRate.toString());
|
|
86
|
+
if (this.wordBoost && this.wordBoost.length > 0) {
|
|
87
|
+
searchParams.set("word_boost", JSON.stringify(this.wordBoost));
|
|
88
|
+
}
|
|
89
|
+
if (this.encoding) {
|
|
90
|
+
searchParams.set("encoding", this.encoding);
|
|
91
|
+
}
|
|
92
|
+
searchParams.set("enable_extra_session_information", "true");
|
|
93
|
+
if (this.disablePartialTranscripts) {
|
|
94
|
+
searchParams.set("disable_partial_transcripts", this.disablePartialTranscripts.toString());
|
|
95
|
+
}
|
|
96
|
+
url.search = searchParams.toString();
|
|
97
|
+
return url;
|
|
98
|
+
}
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
+
on(event, listener) {
|
|
101
|
+
this.listeners[event] = listener;
|
|
102
|
+
}
|
|
103
|
+
connect() {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
if (this.socket) {
|
|
106
|
+
throw new Error("Already connected");
|
|
107
|
+
}
|
|
108
|
+
const url = this.connectionUrl();
|
|
109
|
+
if (this.token) {
|
|
110
|
+
this.socket = factory(url.toString());
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
this.socket = factory(url.toString(), {
|
|
114
|
+
headers: { Authorization: this.apiKey },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
this.socket.binaryType = "arraybuffer";
|
|
118
|
+
this.socket.onopen = () => {
|
|
119
|
+
if (this.endUtteranceSilenceThreshold === undefined ||
|
|
120
|
+
this.endUtteranceSilenceThreshold === null) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
this.configureEndUtteranceSilenceThreshold(this.endUtteranceSilenceThreshold);
|
|
124
|
+
};
|
|
125
|
+
this.socket.onclose = ({ code, reason }) => {
|
|
126
|
+
if (!reason) {
|
|
127
|
+
if (code in RealtimeErrorType) {
|
|
128
|
+
reason = RealtimeErrorMessages[code];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
this.listeners.close?.(code, reason);
|
|
132
|
+
};
|
|
133
|
+
this.socket.onerror = (event) => {
|
|
134
|
+
if (event.error)
|
|
135
|
+
this.listeners.error?.(event.error);
|
|
136
|
+
else
|
|
137
|
+
this.listeners.error?.(new Error(event.message));
|
|
138
|
+
};
|
|
139
|
+
this.socket.onmessage = ({ data }) => {
|
|
140
|
+
const message = JSON.parse(data.toString());
|
|
141
|
+
if ("error" in message) {
|
|
142
|
+
this.listeners.error?.(new RealtimeError(message.error));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
switch (message.message_type) {
|
|
146
|
+
case "SessionBegins": {
|
|
147
|
+
const openObject = {
|
|
148
|
+
sessionId: message.session_id,
|
|
149
|
+
expiresAt: new Date(message.expires_at),
|
|
150
|
+
};
|
|
151
|
+
resolve(openObject);
|
|
152
|
+
this.listeners.open?.(openObject);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "PartialTranscript": {
|
|
156
|
+
// message.created is actually a string when coming from the socket
|
|
157
|
+
message.created = new Date(message.created);
|
|
158
|
+
this.listeners.transcript?.(message);
|
|
159
|
+
this.listeners["transcript.partial"]?.(message);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case "FinalTranscript": {
|
|
163
|
+
// message.created is actually a string when coming from the socket
|
|
164
|
+
message.created = new Date(message.created);
|
|
165
|
+
this.listeners.transcript?.(message);
|
|
166
|
+
this.listeners["transcript.final"]?.(message);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
case "SessionInformation": {
|
|
170
|
+
this.listeners.session_information?.(message);
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case "SessionTerminated": {
|
|
174
|
+
this.sessionTerminatedResolve?.();
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
sendAudio(audio) {
|
|
182
|
+
this.send(audio);
|
|
183
|
+
}
|
|
184
|
+
stream() {
|
|
185
|
+
return new WritableStream({
|
|
186
|
+
write: (chunk) => {
|
|
187
|
+
this.sendAudio(chunk);
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Manually end an utterance
|
|
193
|
+
*/
|
|
194
|
+
forceEndUtterance() {
|
|
195
|
+
this.send(forceEndOfUtteranceMessage);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
|
|
199
|
+
* @param threshold - The duration of the end utterance silence threshold in milliseconds.
|
|
200
|
+
* This value must be an integer between 0 and 20_000.
|
|
201
|
+
*/
|
|
202
|
+
configureEndUtteranceSilenceThreshold(threshold) {
|
|
203
|
+
this.send(`{"end_utterance_silence_threshold":${threshold}}`);
|
|
204
|
+
}
|
|
205
|
+
send(data) {
|
|
206
|
+
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
|
|
207
|
+
throw new Error("Socket is not open for communication");
|
|
208
|
+
}
|
|
209
|
+
this.socket.send(data);
|
|
210
|
+
}
|
|
211
|
+
async close(waitForSessionTermination = true) {
|
|
212
|
+
if (this.socket) {
|
|
213
|
+
if (this.socket.readyState === this.socket.OPEN) {
|
|
214
|
+
if (waitForSessionTermination) {
|
|
215
|
+
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
216
|
+
this.sessionTerminatedResolve = resolve;
|
|
217
|
+
});
|
|
218
|
+
this.socket.send(terminateSessionMessage);
|
|
219
|
+
await sessionTerminatedPromise;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
this.socket.send(terminateSessionMessage);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (this.socket?.removeAllListeners)
|
|
226
|
+
this.socket.removeAllListeners();
|
|
227
|
+
this.socket.close();
|
|
228
|
+
}
|
|
229
|
+
this.listeners = {};
|
|
230
|
+
this.socket = undefined;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* @deprecated Use RealtimeTranscriber instead
|
|
235
|
+
*/
|
|
236
|
+
class RealtimeService extends RealtimeTranscriber {
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export { RealtimeService, RealtimeTranscriber };
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ws = require('ws');
|
|
4
|
+
|
|
5
|
+
/******************************************************************************
|
|
6
|
+
Copyright (c) Microsoft Corporation.
|
|
7
|
+
|
|
8
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
9
|
+
purpose with or without fee is hereby granted.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
12
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
13
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
14
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
15
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
16
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
17
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
18
|
+
***************************************************************************** */
|
|
19
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
23
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
24
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
25
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
26
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
27
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
28
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
33
|
+
var e = new Error(message);
|
|
34
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const { WritableStream } = typeof window !== "undefined"
|
|
38
|
+
? window
|
|
39
|
+
: typeof global !== "undefined"
|
|
40
|
+
? global
|
|
41
|
+
: globalThis;
|
|
42
|
+
|
|
43
|
+
const factory = (url, params) => new ws(url, params);
|
|
44
|
+
|
|
45
|
+
var RealtimeErrorType;
|
|
46
|
+
(function (RealtimeErrorType) {
|
|
47
|
+
RealtimeErrorType[RealtimeErrorType["BadSampleRate"] = 4000] = "BadSampleRate";
|
|
48
|
+
RealtimeErrorType[RealtimeErrorType["AuthFailed"] = 4001] = "AuthFailed";
|
|
49
|
+
// Both InsufficientFunds and FreeAccount error use 4002
|
|
50
|
+
RealtimeErrorType[RealtimeErrorType["InsufficientFundsOrFreeAccount"] = 4002] = "InsufficientFundsOrFreeAccount";
|
|
51
|
+
RealtimeErrorType[RealtimeErrorType["NonexistentSessionId"] = 4004] = "NonexistentSessionId";
|
|
52
|
+
RealtimeErrorType[RealtimeErrorType["SessionExpired"] = 4008] = "SessionExpired";
|
|
53
|
+
RealtimeErrorType[RealtimeErrorType["ClosedSession"] = 4010] = "ClosedSession";
|
|
54
|
+
RealtimeErrorType[RealtimeErrorType["RateLimited"] = 4029] = "RateLimited";
|
|
55
|
+
RealtimeErrorType[RealtimeErrorType["UniqueSessionViolation"] = 4030] = "UniqueSessionViolation";
|
|
56
|
+
RealtimeErrorType[RealtimeErrorType["SessionTimeout"] = 4031] = "SessionTimeout";
|
|
57
|
+
RealtimeErrorType[RealtimeErrorType["AudioTooShort"] = 4032] = "AudioTooShort";
|
|
58
|
+
RealtimeErrorType[RealtimeErrorType["AudioTooLong"] = 4033] = "AudioTooLong";
|
|
59
|
+
RealtimeErrorType[RealtimeErrorType["BadJson"] = 4100] = "BadJson";
|
|
60
|
+
RealtimeErrorType[RealtimeErrorType["BadSchema"] = 4101] = "BadSchema";
|
|
61
|
+
RealtimeErrorType[RealtimeErrorType["TooManyStreams"] = 4102] = "TooManyStreams";
|
|
62
|
+
RealtimeErrorType[RealtimeErrorType["Reconnected"] = 4103] = "Reconnected";
|
|
63
|
+
RealtimeErrorType[RealtimeErrorType["ReconnectAttemptsExhausted"] = 1013] = "ReconnectAttemptsExhausted";
|
|
64
|
+
})(RealtimeErrorType || (RealtimeErrorType = {}));
|
|
65
|
+
const RealtimeErrorMessages = {
|
|
66
|
+
[RealtimeErrorType.BadSampleRate]: "Sample rate must be a positive integer",
|
|
67
|
+
[RealtimeErrorType.AuthFailed]: "Not Authorized",
|
|
68
|
+
[RealtimeErrorType.InsufficientFundsOrFreeAccount]: "Insufficient funds or you are using a free account. This feature is paid-only and requires you to add a credit card. Please visit https://assemblyai.com/dashboard/ to add a credit card to your account.",
|
|
69
|
+
[RealtimeErrorType.NonexistentSessionId]: "Session ID does not exist",
|
|
70
|
+
[RealtimeErrorType.SessionExpired]: "Session has expired",
|
|
71
|
+
[RealtimeErrorType.ClosedSession]: "Session is closed",
|
|
72
|
+
[RealtimeErrorType.RateLimited]: "Rate limited",
|
|
73
|
+
[RealtimeErrorType.UniqueSessionViolation]: "Unique session violation",
|
|
74
|
+
[RealtimeErrorType.SessionTimeout]: "Session Timeout",
|
|
75
|
+
[RealtimeErrorType.AudioTooShort]: "Audio too short",
|
|
76
|
+
[RealtimeErrorType.AudioTooLong]: "Audio too long",
|
|
77
|
+
[RealtimeErrorType.BadJson]: "Bad JSON",
|
|
78
|
+
[RealtimeErrorType.BadSchema]: "Bad schema",
|
|
79
|
+
[RealtimeErrorType.TooManyStreams]: "Too many streams",
|
|
80
|
+
[RealtimeErrorType.Reconnected]: "Reconnected",
|
|
81
|
+
[RealtimeErrorType.ReconnectAttemptsExhausted]: "Reconnect attempts exhausted",
|
|
82
|
+
};
|
|
83
|
+
class RealtimeError extends Error {
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
87
|
+
const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
|
|
88
|
+
const terminateSessionMessage = `{"terminate_session":true}`;
|
|
89
|
+
class RealtimeTranscriber {
|
|
90
|
+
constructor(params) {
|
|
91
|
+
var _a, _b;
|
|
92
|
+
this.listeners = {};
|
|
93
|
+
this.realtimeUrl = (_a = params.realtimeUrl) !== null && _a !== void 0 ? _a : defaultRealtimeUrl;
|
|
94
|
+
this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
|
|
95
|
+
this.wordBoost = params.wordBoost;
|
|
96
|
+
this.encoding = params.encoding;
|
|
97
|
+
this.endUtteranceSilenceThreshold = params.endUtteranceSilenceThreshold;
|
|
98
|
+
this.disablePartialTranscripts = params.disablePartialTranscripts;
|
|
99
|
+
if ("token" in params && params.token)
|
|
100
|
+
this.token = params.token;
|
|
101
|
+
if ("apiKey" in params && params.apiKey)
|
|
102
|
+
this.apiKey = params.apiKey;
|
|
103
|
+
if (!(this.token || this.apiKey)) {
|
|
104
|
+
throw new Error("API key or temporary token is required.");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
connectionUrl() {
|
|
108
|
+
const url = new URL(this.realtimeUrl);
|
|
109
|
+
if (url.protocol !== "wss:") {
|
|
110
|
+
throw new Error("Invalid protocol, must be wss");
|
|
111
|
+
}
|
|
112
|
+
const searchParams = new URLSearchParams();
|
|
113
|
+
if (this.token) {
|
|
114
|
+
searchParams.set("token", this.token);
|
|
115
|
+
}
|
|
116
|
+
searchParams.set("sample_rate", this.sampleRate.toString());
|
|
117
|
+
if (this.wordBoost && this.wordBoost.length > 0) {
|
|
118
|
+
searchParams.set("word_boost", JSON.stringify(this.wordBoost));
|
|
119
|
+
}
|
|
120
|
+
if (this.encoding) {
|
|
121
|
+
searchParams.set("encoding", this.encoding);
|
|
122
|
+
}
|
|
123
|
+
searchParams.set("enable_extra_session_information", "true");
|
|
124
|
+
if (this.disablePartialTranscripts) {
|
|
125
|
+
searchParams.set("disable_partial_transcripts", this.disablePartialTranscripts.toString());
|
|
126
|
+
}
|
|
127
|
+
url.search = searchParams.toString();
|
|
128
|
+
return url;
|
|
129
|
+
}
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
+
on(event, listener) {
|
|
132
|
+
this.listeners[event] = listener;
|
|
133
|
+
}
|
|
134
|
+
connect() {
|
|
135
|
+
return new Promise((resolve) => {
|
|
136
|
+
if (this.socket) {
|
|
137
|
+
throw new Error("Already connected");
|
|
138
|
+
}
|
|
139
|
+
const url = this.connectionUrl();
|
|
140
|
+
if (this.token) {
|
|
141
|
+
this.socket = factory(url.toString());
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.socket = factory(url.toString(), {
|
|
145
|
+
headers: { Authorization: this.apiKey },
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
this.socket.binaryType = "arraybuffer";
|
|
149
|
+
this.socket.onopen = () => {
|
|
150
|
+
if (this.endUtteranceSilenceThreshold === undefined ||
|
|
151
|
+
this.endUtteranceSilenceThreshold === null) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.configureEndUtteranceSilenceThreshold(this.endUtteranceSilenceThreshold);
|
|
155
|
+
};
|
|
156
|
+
this.socket.onclose = ({ code, reason }) => {
|
|
157
|
+
var _a, _b;
|
|
158
|
+
if (!reason) {
|
|
159
|
+
if (code in RealtimeErrorType) {
|
|
160
|
+
reason = RealtimeErrorMessages[code];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
(_b = (_a = this.listeners).close) === null || _b === void 0 ? void 0 : _b.call(_a, code, reason);
|
|
164
|
+
};
|
|
165
|
+
this.socket.onerror = (event) => {
|
|
166
|
+
var _a, _b, _c, _d;
|
|
167
|
+
if (event.error)
|
|
168
|
+
(_b = (_a = this.listeners).error) === null || _b === void 0 ? void 0 : _b.call(_a, event.error);
|
|
169
|
+
else
|
|
170
|
+
(_d = (_c = this.listeners).error) === null || _d === void 0 ? void 0 : _d.call(_c, new Error(event.message));
|
|
171
|
+
};
|
|
172
|
+
this.socket.onmessage = ({ data }) => {
|
|
173
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
174
|
+
const message = JSON.parse(data.toString());
|
|
175
|
+
if ("error" in message) {
|
|
176
|
+
(_b = (_a = this.listeners).error) === null || _b === void 0 ? void 0 : _b.call(_a, new RealtimeError(message.error));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
switch (message.message_type) {
|
|
180
|
+
case "SessionBegins": {
|
|
181
|
+
const openObject = {
|
|
182
|
+
sessionId: message.session_id,
|
|
183
|
+
expiresAt: new Date(message.expires_at),
|
|
184
|
+
};
|
|
185
|
+
resolve(openObject);
|
|
186
|
+
(_d = (_c = this.listeners).open) === null || _d === void 0 ? void 0 : _d.call(_c, openObject);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
case "PartialTranscript": {
|
|
190
|
+
// message.created is actually a string when coming from the socket
|
|
191
|
+
message.created = new Date(message.created);
|
|
192
|
+
(_f = (_e = this.listeners).transcript) === null || _f === void 0 ? void 0 : _f.call(_e, message);
|
|
193
|
+
(_h = (_g = this.listeners)["transcript.partial"]) === null || _h === void 0 ? void 0 : _h.call(_g, message);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case "FinalTranscript": {
|
|
197
|
+
// message.created is actually a string when coming from the socket
|
|
198
|
+
message.created = new Date(message.created);
|
|
199
|
+
(_k = (_j = this.listeners).transcript) === null || _k === void 0 ? void 0 : _k.call(_j, message);
|
|
200
|
+
(_m = (_l = this.listeners)["transcript.final"]) === null || _m === void 0 ? void 0 : _m.call(_l, message);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case "SessionInformation": {
|
|
204
|
+
(_p = (_o = this.listeners).session_information) === null || _p === void 0 ? void 0 : _p.call(_o, message);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case "SessionTerminated": {
|
|
208
|
+
(_q = this.sessionTerminatedResolve) === null || _q === void 0 ? void 0 : _q.call(this);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
sendAudio(audio) {
|
|
216
|
+
this.send(audio);
|
|
217
|
+
}
|
|
218
|
+
stream() {
|
|
219
|
+
return new WritableStream({
|
|
220
|
+
write: (chunk) => {
|
|
221
|
+
this.sendAudio(chunk);
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Manually end an utterance
|
|
227
|
+
*/
|
|
228
|
+
forceEndUtterance() {
|
|
229
|
+
this.send(forceEndOfUtteranceMessage);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
|
|
233
|
+
* @param threshold - The duration of the end utterance silence threshold in milliseconds.
|
|
234
|
+
* This value must be an integer between 0 and 20_000.
|
|
235
|
+
*/
|
|
236
|
+
configureEndUtteranceSilenceThreshold(threshold) {
|
|
237
|
+
this.send(`{"end_utterance_silence_threshold":${threshold}}`);
|
|
238
|
+
}
|
|
239
|
+
send(data) {
|
|
240
|
+
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
|
|
241
|
+
throw new Error("Socket is not open for communication");
|
|
242
|
+
}
|
|
243
|
+
this.socket.send(data);
|
|
244
|
+
}
|
|
245
|
+
close() {
|
|
246
|
+
return __awaiter(this, arguments, void 0, function* (waitForSessionTermination = true) {
|
|
247
|
+
var _a;
|
|
248
|
+
if (this.socket) {
|
|
249
|
+
if (this.socket.readyState === this.socket.OPEN) {
|
|
250
|
+
if (waitForSessionTermination) {
|
|
251
|
+
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
252
|
+
this.sessionTerminatedResolve = resolve;
|
|
253
|
+
});
|
|
254
|
+
this.socket.send(terminateSessionMessage);
|
|
255
|
+
yield sessionTerminatedPromise;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
this.socket.send(terminateSessionMessage);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.removeAllListeners)
|
|
262
|
+
this.socket.removeAllListeners();
|
|
263
|
+
this.socket.close();
|
|
264
|
+
}
|
|
265
|
+
this.listeners = {};
|
|
266
|
+
this.socket = undefined;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* @deprecated Use RealtimeTranscriber instead
|
|
272
|
+
*/
|
|
273
|
+
class RealtimeService extends RealtimeTranscriber {
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
exports.RealtimeService = RealtimeService;
|
|
277
|
+
exports.RealtimeTranscriber = RealtimeTranscriber;
|