assemblyai 4.5.0-beta.0 → 4.5.0

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +53 -9
  3. package/dist/assemblyai.streaming.umd.js +288 -0
  4. package/dist/assemblyai.streaming.umd.min.js +1 -0
  5. package/dist/assemblyai.umd.js +131 -12
  6. package/dist/assemblyai.umd.min.js +1 -1
  7. package/dist/browser.mjs +125 -11
  8. package/dist/bun.mjs +125 -11
  9. package/dist/deno.mjs +125 -11
  10. package/dist/exports/index.d.ts +2 -0
  11. package/dist/exports/streaming.d.ts +4 -0
  12. package/dist/index.cjs +131 -12
  13. package/dist/index.mjs +131 -12
  14. package/dist/node.cjs +125 -11
  15. package/dist/node.mjs +125 -11
  16. package/dist/polyfills/fetch/default.d.ts +1 -0
  17. package/dist/polyfills/fetch/workerd.d.ts +1 -0
  18. package/dist/services/base.d.ts +1 -0
  19. package/dist/services/lemur/index.d.ts +8 -1
  20. package/dist/services/transcripts/index.d.ts +16 -3
  21. package/dist/streaming.browser.mjs +239 -0
  22. package/dist/streaming.cjs +277 -0
  23. package/dist/streaming.mjs +274 -0
  24. package/dist/types/index.d.ts +7 -0
  25. package/dist/types/openapi.generated.d.ts +60 -23
  26. package/dist/types/services/index.d.ts +7 -0
  27. package/dist/types/transcripts/index.d.ts +9 -0
  28. package/dist/utils/userAgent.d.ts +2 -0
  29. package/dist/workerd.mjs +722 -0
  30. package/docs/reference-types-from-js.md +27 -0
  31. package/package.json +39 -19
  32. package/src/exports/index.ts +2 -0
  33. package/src/exports/streaming.ts +4 -0
  34. package/src/polyfills/fetch/default.ts +3 -0
  35. package/src/polyfills/fetch/workerd.ts +1 -0
  36. package/src/services/base.ts +27 -7
  37. package/src/services/files/index.ts +19 -2
  38. package/src/services/index.ts +2 -1
  39. package/src/services/lemur/index.ts +12 -0
  40. package/src/services/transcripts/index.ts +41 -4
  41. package/src/types/index.ts +9 -0
  42. package/src/types/openapi.generated.ts +198 -41
  43. package/src/types/services/index.ts +8 -0
  44. package/src/types/transcripts/index.ts +10 -0
  45. package/src/utils/path.ts +1 -0
  46. package/src/utils/userAgent.ts +51 -0
package/dist/node.mjs CHANGED
@@ -3,6 +3,45 @@ import ws from 'ws';
3
3
  import { createReadStream } from 'fs';
4
4
  import { Readable } from 'stream';
5
5
 
6
+ const DEFAULT_FETCH_INIT = {
7
+ cache: "no-store",
8
+ };
9
+
10
+ const buildUserAgent = (userAgent) => defaultUserAgentString +
11
+ (userAgent === false
12
+ ? ""
13
+ : " AssemblyAI/1.0 (" +
14
+ Object.entries({ ...defaultUserAgent, ...userAgent })
15
+ .map(([key, item]) => item ? `${key}=${item.name}/${item.version}` : "")
16
+ .join(" ") +
17
+ ")");
18
+ let defaultUserAgentString = "";
19
+ if (typeof navigator !== "undefined" && navigator.userAgent) {
20
+ defaultUserAgentString += navigator.userAgent;
21
+ }
22
+ const defaultUserAgent = {
23
+ sdk: { name: "JavaScript", version: "4.5.0" },
24
+ };
25
+ if (typeof process !== "undefined") {
26
+ if (process.versions.node && defaultUserAgentString.indexOf("Node") === -1) {
27
+ defaultUserAgent.runtime_env = {
28
+ name: "Node",
29
+ version: process.versions.node,
30
+ };
31
+ }
32
+ if (process.versions.bun && defaultUserAgentString.indexOf("Bun") === -1) {
33
+ defaultUserAgent.runtime_env = {
34
+ name: "Bun",
35
+ version: process.versions.bun,
36
+ };
37
+ }
38
+ }
39
+ if (typeof Deno !== "undefined") {
40
+ if (process.versions.bun && defaultUserAgentString.indexOf("Deno") === -1) {
41
+ defaultUserAgent.runtime_env = { name: "Deno", version: Deno.version.deno };
42
+ }
43
+ }
44
+
6
45
  /**
7
46
  * Base class for services that communicate with the API.
8
47
  */
@@ -13,15 +52,32 @@ class BaseService {
13
52
  */
14
53
  constructor(params) {
15
54
  this.params = params;
55
+ if (params.userAgent === false) {
56
+ this.userAgent = undefined;
57
+ }
58
+ else {
59
+ this.userAgent = buildUserAgent(params.userAgent || {});
60
+ }
16
61
  }
17
62
  async fetch(input, init) {
18
- init = init ?? {};
19
- init.headers = init.headers ?? {};
20
- init.headers = {
63
+ init = { ...DEFAULT_FETCH_INIT, ...init };
64
+ let headers = {
21
65
  Authorization: this.params.apiKey,
22
66
  "Content-Type": "application/json",
23
- ...init.headers,
24
67
  };
68
+ if (DEFAULT_FETCH_INIT?.headers)
69
+ headers = { ...headers, ...DEFAULT_FETCH_INIT.headers };
70
+ if (init?.headers)
71
+ headers = { ...headers, ...init.headers };
72
+ if (this.userAgent) {
73
+ headers["User-Agent"] = this.userAgent;
74
+ // chromium browsers have a bug where the user agent can't be modified
75
+ if (typeof window !== "undefined" && "chrome" in window) {
76
+ headers["AssemblyAI-Agent"] =
77
+ this.userAgent;
78
+ }
79
+ }
80
+ init.headers = headers;
25
81
  if (!input.startsWith("http"))
26
82
  input = this.params.baseUrl + input;
27
83
  const response = await fetch(input, init);
@@ -74,6 +130,9 @@ class LemurService extends BaseService {
74
130
  body: JSON.stringify(params),
75
131
  });
76
132
  }
133
+ getResponse(id) {
134
+ return this.fetchJson(`/lemur/v3/${id}`);
135
+ }
77
136
  /**
78
137
  * Delete the data for a previously submitted LeMUR request.
79
138
  * @param id - ID of the LeMUR request
@@ -348,6 +407,8 @@ function getPath(path) {
348
407
  return null;
349
408
  if (path.startsWith("https"))
350
409
  return null;
410
+ if (path.startsWith("data:"))
411
+ return null;
351
412
  if (path.startsWith("file://"))
352
413
  return path.substring(7);
353
414
  if (path.startsWith("file:"))
@@ -389,8 +450,13 @@ class TranscriptService extends BaseService {
389
450
  audioUrl = await this.files.upload(path);
390
451
  }
391
452
  else {
392
- // audio is not a local path, assume it's a URL
393
- audioUrl = audio;
453
+ if (audio.startsWith("data:")) {
454
+ audioUrl = await this.files.upload(audio);
455
+ }
456
+ else {
457
+ // audio is not a local path, and not a data-URI, assume it's a normal URL
458
+ audioUrl = audio;
459
+ }
394
460
  }
395
461
  }
396
462
  else {
@@ -541,13 +607,43 @@ class TranscriptService extends BaseService {
541
607
  return await response.text();
542
608
  }
543
609
  /**
544
- * Retrieve redactions of a transcript.
610
+ * Retrieve the redacted audio URL of a transcript.
545
611
  * @param id - The identifier of the transcript.
546
- * @returns A promise that resolves to the subtitles text.
612
+ * @returns A promise that resolves to the details of the redacted audio.
613
+ * @deprecated Use `redactedAudio` instead.
547
614
  */
548
615
  redactions(id) {
616
+ return this.redactedAudio(id);
617
+ }
618
+ /**
619
+ * Retrieve the redacted audio URL of a transcript.
620
+ * @param id - The identifier of the transcript.
621
+ * @returns A promise that resolves to the details of the redacted audio.
622
+ */
623
+ redactedAudio(id) {
549
624
  return this.fetchJson(`/v2/transcript/${id}/redacted-audio`);
550
625
  }
626
+ /**
627
+ * Retrieve the redacted audio file of a transcript.
628
+ * @param id - The identifier of the transcript.
629
+ * @returns A promise that resolves to the fetch HTTP response of the redacted audio file.
630
+ */
631
+ async redactedAudioFile(id) {
632
+ const { redacted_audio_url, status } = await this.redactedAudio(id);
633
+ if (status !== "redacted_audio_ready") {
634
+ throw new Error(`Redacted audio status is ${status}`);
635
+ }
636
+ const response = await fetch(redacted_audio_url);
637
+ if (!response.ok) {
638
+ throw new Error(`Failed to fetch redacted audio: ${response.statusText}`);
639
+ }
640
+ return {
641
+ arrayBuffer: response.arrayBuffer.bind(response),
642
+ blob: response.blob.bind(response),
643
+ body: response.body,
644
+ bodyUsed: response.bodyUsed,
645
+ };
646
+ }
551
647
  }
552
648
  function deprecateConformer2(params) {
553
649
  if (!params)
@@ -567,8 +663,14 @@ class FileService extends BaseService {
567
663
  */
568
664
  async upload(input) {
569
665
  let fileData;
570
- if (typeof input === "string")
571
- fileData = await readFile(input);
666
+ if (typeof input === "string") {
667
+ if (input.startsWith("data:")) {
668
+ fileData = dataUrlToBlob(input);
669
+ }
670
+ else {
671
+ fileData = await readFile(input);
672
+ }
673
+ }
572
674
  else
573
675
  fileData = input;
574
676
  const data = await this.fetchJson("/v2/upload", {
@@ -582,6 +684,17 @@ class FileService extends BaseService {
582
684
  return data.upload_url;
583
685
  }
584
686
  }
687
+ function dataUrlToBlob(dataUrl) {
688
+ const arr = dataUrl.split(",");
689
+ const mime = arr[0].match(/:(.*?);/)[1];
690
+ const bstr = atob(arr[1]);
691
+ let n = bstr.length;
692
+ const u8arr = new Uint8Array(n);
693
+ while (n--) {
694
+ u8arr[n] = bstr.charCodeAt(n);
695
+ }
696
+ return new Blob([u8arr], { type: mime });
697
+ }
585
698
 
586
699
  const defaultBaseUrl = "https://api.assemblyai.com";
587
700
  class AssemblyAI {
@@ -591,8 +704,9 @@ class AssemblyAI {
591
704
  */
592
705
  constructor(params) {
593
706
  params.baseUrl = params.baseUrl || defaultBaseUrl;
594
- if (params.baseUrl && params.baseUrl.endsWith("/"))
707
+ if (params.baseUrl && params.baseUrl.endsWith("/")) {
595
708
  params.baseUrl = params.baseUrl.slice(0, -1);
709
+ }
596
710
  this.files = new FileService(params);
597
711
  this.transcripts = new TranscriptService(params, this.files);
598
712
  this.lemur = new LemurService(params);
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_FETCH_INIT: Record<string, unknown>;
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_FETCH_INIT: Record<string, unknown>;
@@ -4,6 +4,7 @@ import { BaseServiceParams } from "..";
4
4
  */
5
5
  export declare abstract class BaseService {
6
6
  private params;
7
+ private userAgent;
7
8
  /**
8
9
  * Create a new service.
9
10
  * @param params - The parameters to use for the service.
@@ -1,10 +1,17 @@
1
- import { LemurSummaryParams, LemurActionItemsParams, LemurQuestionAnswerParams, LemurTaskParams, LemurSummaryResponse, LemurQuestionAnswerResponse, LemurActionItemsResponse, LemurTaskResponse, PurgeLemurRequestDataResponse } from "../..";
1
+ import { LemurSummaryParams, LemurActionItemsParams, LemurQuestionAnswerParams, LemurTaskParams, LemurSummaryResponse, LemurQuestionAnswerResponse, LemurActionItemsResponse, LemurTaskResponse, PurgeLemurRequestDataResponse, LemurResponse } from "../..";
2
2
  import { BaseService } from "../base";
3
3
  export declare class LemurService extends BaseService {
4
4
  summary(params: LemurSummaryParams): Promise<LemurSummaryResponse>;
5
5
  questionAnswer(params: LemurQuestionAnswerParams): Promise<LemurQuestionAnswerResponse>;
6
6
  actionItems(params: LemurActionItemsParams): Promise<LemurActionItemsResponse>;
7
7
  task(params: LemurTaskParams): Promise<LemurTaskResponse>;
8
+ /**
9
+ * Retrieve a LeMUR response that was previously generated.
10
+ * @param id - The ID of the LeMUR request you previously made. This would be found in the response of the original request.
11
+ * @returns The LeMUR response.
12
+ */
13
+ getResponse<T extends LemurResponse>(id: string): Promise<T>;
14
+ getResponse(id: string): Promise<LemurResponse>;
8
15
  /**
9
16
  * Delete the data for a previously submitted LeMUR request.
10
17
  * @param id - ID of the LeMUR request
@@ -1,5 +1,5 @@
1
1
  import { BaseService } from "../base";
2
- import { ParagraphsResponse, SentencesResponse, Transcript, TranscriptList, TranscriptParams, CreateTranscriptOptions, SubtitleFormat, RedactedAudioResponse, ListTranscriptParams, WordSearchResponse, BaseServiceParams, PollingOptions, TranscribeParams, TranscribeOptions, SubmitParams } from "../..";
2
+ import { ParagraphsResponse, SentencesResponse, Transcript, TranscriptList, TranscriptParams, CreateTranscriptOptions, SubtitleFormat, RedactedAudioResponse, ListTranscriptParams, WordSearchResponse, BaseServiceParams, PollingOptions, TranscribeParams, TranscribeOptions, SubmitParams, RedactedAudioFile } from "../..";
3
3
  import { FileService } from "../files";
4
4
  export declare class TranscriptService extends BaseService {
5
5
  private files;
@@ -78,9 +78,22 @@ export declare class TranscriptService extends BaseService {
78
78
  */
79
79
  subtitles(id: string, format?: SubtitleFormat, chars_per_caption?: number): Promise<string>;
80
80
  /**
81
- * Retrieve redactions of a transcript.
81
+ * Retrieve the redacted audio URL of a transcript.
82
82
  * @param id - The identifier of the transcript.
83
- * @returns A promise that resolves to the subtitles text.
83
+ * @returns A promise that resolves to the details of the redacted audio.
84
+ * @deprecated Use `redactedAudio` instead.
84
85
  */
85
86
  redactions(id: string): Promise<RedactedAudioResponse>;
87
+ /**
88
+ * Retrieve the redacted audio URL of a transcript.
89
+ * @param id - The identifier of the transcript.
90
+ * @returns A promise that resolves to the details of the redacted audio.
91
+ */
92
+ redactedAudio(id: string): Promise<RedactedAudioResponse>;
93
+ /**
94
+ * Retrieve the redacted audio file of a transcript.
95
+ * @param id - The identifier of the transcript.
96
+ * @returns A promise that resolves to the fetch HTTP response of the redacted audio file.
97
+ */
98
+ redactedAudioFile(id: string): Promise<RedactedAudioFile>;
86
99
  }
@@ -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 };