assemblyai 4.34.5 → 4.35.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.
@@ -71,6 +71,22 @@ export type StreamingTranscriberParams = {
71
71
  websocketBaseUrl?: string;
72
72
  apiKey?: string;
73
73
  token?: string;
74
+ /**
75
+ * Milliseconds to wait for the streaming handshake (socket open + server
76
+ * `Begin`) before treating the attempt as failed. Defaults to 1000.
77
+ */
78
+ connectTimeout?: number;
79
+ /**
80
+ * Number of additional connection attempts after the first one fails on a
81
+ * transient error (timeout, network drop, unexpected close). 0 disables
82
+ * retries. Permanent failures (auth, insufficient funds, malformed config)
83
+ * are never retried. Defaults to 2.
84
+ */
85
+ maxConnectionRetries?: number;
86
+ /**
87
+ * Milliseconds to wait between connection attempts. Defaults to 500.
88
+ */
89
+ connectionRetryDelay?: number;
74
90
  sampleRate: number;
75
91
  encoding?: AudioEncoding;
76
92
  endOfTurnConfidenceThreshold?: number;
@@ -151,7 +167,7 @@ export type StreamingListeners = {
151
167
  vad?: (event: VadFrame) => void;
152
168
  error?: (error: Error) => void;
153
169
  };
154
- export type StreamingSpeechModel = "universal-streaming-english" | "universal-streaming-multilingual" | "u3-rt-pro" | "u3-rt-pro-beta-1" | "whisper-rt" | "u3-pro";
170
+ export type StreamingSpeechModel = "universal-streaming-english" | "universal-streaming-multilingual" | "u3-rt-pro" | "u3-rt-pro-beta-1" | "whisper-rt" | "universal-3-5-pro" | "u3-pro";
155
171
  export type StreamingDomain = "medical-v1";
156
172
  export type StreamingMode = "max_accuracy" | "min_latency" | "balanced";
157
173
  export type VoiceFocusModel = "near-field" | "far-field";
@@ -243,6 +259,9 @@ export type StreamingUpdateConfiguration = {
243
259
  export type StreamingForceEndpoint = {
244
260
  type: "ForceEndpoint";
245
261
  };
262
+ export type StreamingKeepAlive = {
263
+ type: "KeepAlive";
264
+ };
246
265
  export type ErrorEvent = {
247
266
  type: "Error";
248
267
  error_code?: number;
@@ -282,4 +301,4 @@ export type SpeakerRevisionEvent = {
282
301
  revisions: SpeakerRevisionItem[];
283
302
  };
284
303
  export type StreamingEventMessage = BeginEvent | TurnEvent | SpeechStartedEvent | TerminationEvent | LLMGatewayResponseEvent | SpeakerRevisionEvent | ErrorEvent | WarningEvent;
285
- export type StreamingOperationMessage = StreamingUpdateConfiguration | StreamingForceEndpoint | StreamingTerminateSession;
304
+ export type StreamingOperationMessage = StreamingUpdateConfiguration | StreamingForceEndpoint | StreamingKeepAlive | StreamingTerminateSession;
package/dist/workerd.mjs CHANGED
@@ -28,7 +28,7 @@ if (typeof navigator !== "undefined" && navigator.userAgent) {
28
28
  defaultUserAgentString += navigator.userAgent;
29
29
  }
30
30
  const defaultUserAgent = {
31
- sdk: { name: "JavaScript", version: "4.34.5" },
31
+ sdk: { name: "JavaScript", version: "4.35.0" },
32
32
  };
33
33
  if (typeof process !== "undefined") {
34
34
  if (process.versions.node && defaultUserAgentString.indexOf("Node") === -1) {
@@ -983,6 +983,24 @@ function toInt16View(audio) {
983
983
  }
984
984
  const defaultStreamingUrl$1 = "wss://streaming.assemblyai.com/v3/ws";
985
985
  const terminateSessionMessage = `{"type":"Terminate"}`;
986
+ const DEFAULT_CONNECT_TIMEOUT_MS = 1000;
987
+ const DEFAULT_MAX_CONNECTION_RETRIES = 2;
988
+ const DEFAULT_CONNECTION_RETRY_DELAY_MS = 500;
989
+ /**
990
+ * Close/error codes that signal a permanent client-side problem (auth,
991
+ * billing, malformed config). A retry would hit the same failure, so the
992
+ * connection is never retried on these.
993
+ */
994
+ const NON_RETRYABLE_CLOSE_CODES = new Set([
995
+ StreamingErrorType.BadSampleRate,
996
+ StreamingErrorType.AuthFailed,
997
+ StreamingErrorType.InsufficientFunds,
998
+ StreamingErrorType.FreeTierUser,
999
+ StreamingErrorType.BadSchema,
1000
+ ]);
1001
+ function isRetryableCloseCode(code) {
1002
+ return code !== 1000 && !NON_RETRYABLE_CLOSE_CODES.has(code);
1003
+ }
986
1004
  /**
987
1005
  * Per-send chunk cap in milliseconds for the dual-channel mixer. The streaming
988
1006
  * server rejects audio messages longer than 1000 ms (`Input Duration Error`).
@@ -1191,12 +1209,81 @@ class StreamingTranscriber {
1191
1209
  on(event, listener) {
1192
1210
  this.listeners[event] = listener;
1193
1211
  }
1194
- connect() {
1195
- return new Promise((resolve) => {
1196
- if (this.socket) {
1197
- throw new Error("Already connected");
1212
+ /**
1213
+ * Open the streaming session.
1214
+ *
1215
+ * Resolves with the server's `Begin` event once the handshake completes. A
1216
+ * single attempt is bounded by `connectTimeout` (default 1000ms); transient
1217
+ * failures (timeout, network drop, unexpected close) are retried up to
1218
+ * `maxConnectionRetries` times (default 2), waiting `connectionRetryDelay`
1219
+ * (default 500ms) between attempts. Permanent failures (auth, insufficient
1220
+ * funds, malformed config) are not retried.
1221
+ *
1222
+ * Unlike previously, a failed connection now rejects this promise rather
1223
+ * than only invoking the `error` listener — necessary for the caller (and
1224
+ * the retry loop) to observe the failure.
1225
+ */
1226
+ async connect() {
1227
+ if (this.socket) {
1228
+ throw new Error("Already connected");
1229
+ }
1230
+ const maxRetries = this.params.maxConnectionRetries ?? DEFAULT_MAX_CONNECTION_RETRIES;
1231
+ const retryDelay = this.params.connectionRetryDelay ?? DEFAULT_CONNECTION_RETRY_DELAY_MS;
1232
+ let lastError;
1233
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1234
+ try {
1235
+ return await this.connectOnce();
1236
+ }
1237
+ catch (err) {
1238
+ lastError = err;
1239
+ const retryable = err.retryable === true;
1240
+ if (!retryable || attempt === maxRetries) {
1241
+ throw err;
1242
+ }
1243
+ console.warn(`Streaming connect attempt ${attempt + 1}/${maxRetries + 1} failed (${err.message}); retrying`);
1244
+ if (retryDelay > 0) {
1245
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
1246
+ }
1198
1247
  }
1248
+ }
1249
+ // The loop above always returns or throws; this only satisfies the type
1250
+ // checker that a value is produced on every path.
1251
+ throw lastError ?? new Error("Failed to connect to streaming server");
1252
+ }
1253
+ connectOnce() {
1254
+ return new Promise((resolve, reject) => {
1199
1255
  const url = this.connectionUrl();
1256
+ const timeoutMs = this.params.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT_MS;
1257
+ // `settled` flips once this attempt has resolved (`Begin`) or rejected
1258
+ // (timeout / pre-`Begin` close / error). Before it flips the socket
1259
+ // handlers drive this promise; after it flips they revert to normal
1260
+ // runtime dispatch (close / error / message listeners).
1261
+ let settled = false;
1262
+ let timer;
1263
+ const failAttempt = (error) => {
1264
+ if (settled)
1265
+ return;
1266
+ settled = true;
1267
+ if (timer)
1268
+ clearTimeout(timer);
1269
+ this.discardPendingSocket();
1270
+ reject(error);
1271
+ };
1272
+ const succeed = (begin) => {
1273
+ if (settled)
1274
+ return;
1275
+ settled = true;
1276
+ if (timer)
1277
+ clearTimeout(timer);
1278
+ resolve(begin);
1279
+ };
1280
+ if (timeoutMs > 0) {
1281
+ timer = setTimeout(() => {
1282
+ const err = new StreamingError(`Streaming connection timed out after ${timeoutMs}ms`);
1283
+ err.retryable = true;
1284
+ failAttempt(err);
1285
+ }, timeoutMs);
1286
+ }
1200
1287
  if (this.token) {
1201
1288
  this.socket = factory(url.toString());
1202
1289
  }
@@ -1213,6 +1300,15 @@ class StreamingTranscriber {
1213
1300
  reason = StreamingErrorMessages[code];
1214
1301
  }
1215
1302
  }
1303
+ // A close before `Begin` is a failed connection attempt — reject so
1304
+ // connect() can retry (or surface a permanent failure).
1305
+ if (!settled) {
1306
+ const err = new StreamingError(reason || `Streaming connection closed (code=${code})`);
1307
+ err.code = code;
1308
+ err.retryable = isRetryableCloseCode(code);
1309
+ failAttempt(err);
1310
+ return;
1311
+ }
1216
1312
  // Stop the flush timer when the socket is gone (server-initiated close,
1217
1313
  // network drop, etc.) — otherwise subsequent ticks call send() on a
1218
1314
  // closed socket and spam the error listener.
@@ -1223,25 +1319,37 @@ class StreamingTranscriber {
1223
1319
  this.listeners.close?.(code, reason);
1224
1320
  };
1225
1321
  this.socket.onerror = (event) => {
1226
- if (event.error)
1227
- this.listeners.error?.(event.error);
1228
- else
1229
- this.listeners.error?.(new Error(event.message));
1322
+ const error = event.error ?? new Error(event.message);
1323
+ // A socket error before `Begin` is a failed attempt → reject/retry.
1324
+ if (!settled) {
1325
+ error.retryable = true;
1326
+ failAttempt(error);
1327
+ return;
1328
+ }
1329
+ this.listeners.error?.(error);
1230
1330
  };
1231
1331
  this.socket.onmessage = ({ data }) => {
1232
1332
  const message = JSON.parse(data.toString());
1233
1333
  if ("error" in message) {
1234
1334
  const err = new StreamingError(message.error);
1235
1335
  if ("error_code" in message) {
1236
- err.code =
1237
- message.error_code;
1336
+ err.code = message.error_code;
1337
+ }
1338
+ // A server error frame before `Begin` fails the attempt; the code
1339
+ // decides whether a retry is worthwhile.
1340
+ if (!settled) {
1341
+ const attemptErr = err;
1342
+ attemptErr.retryable =
1343
+ err.code === undefined ? true : isRetryableCloseCode(err.code);
1344
+ failAttempt(attemptErr);
1345
+ return;
1238
1346
  }
1239
1347
  this.listeners.error?.(err);
1240
1348
  return;
1241
1349
  }
1242
1350
  switch (message.type) {
1243
1351
  case "Begin": {
1244
- resolve(message);
1352
+ succeed(message);
1245
1353
  this.listeners.open?.(message);
1246
1354
  break;
1247
1355
  }
@@ -1288,6 +1396,20 @@ class StreamingTranscriber {
1288
1396
  };
1289
1397
  });
1290
1398
  }
1399
+ /** Tear down a half-open socket from a failed connection attempt. */
1400
+ discardPendingSocket() {
1401
+ if (!this.socket)
1402
+ return;
1403
+ try {
1404
+ if (this.socket.removeAllListeners)
1405
+ this.socket.removeAllListeners();
1406
+ this.socket.close();
1407
+ }
1408
+ catch {
1409
+ // Best-effort cleanup; a half-open socket may throw on close.
1410
+ }
1411
+ this.socket = undefined;
1412
+ }
1291
1413
  /**
1292
1414
  * Returns a WritableStream that pumps PCM chunks into `sendAudio`. Single-channel
1293
1415
  * only — in dual-channel mode use `sendAudio(pcm, { channel })` directly, since
@@ -1561,6 +1683,16 @@ class StreamingTranscriber {
1561
1683
  };
1562
1684
  this.send(JSON.stringify(message));
1563
1685
  }
1686
+ /**
1687
+ * Reset the server's inactivity timer. Only needed when the session was
1688
+ * created with `inactivityTimeout` and no audio is being sent.
1689
+ */
1690
+ keepAlive() {
1691
+ const message = {
1692
+ type: "KeepAlive",
1693
+ };
1694
+ this.send(JSON.stringify(message));
1695
+ }
1564
1696
  send(data) {
1565
1697
  if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
1566
1698
  throw new Error("Socket is not open for communication");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assemblyai",
3
- "version": "4.34.5",
3
+ "version": "4.35.0",
4
4
  "description": "The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription, as well as the latest LeMUR models.",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -18,12 +18,17 @@ import {
18
18
  SpeakerRevisionEvent,
19
19
  StreamingUpdateConfiguration,
20
20
  StreamingForceEndpoint,
21
+ StreamingKeepAlive,
21
22
  WarningEvent,
22
23
  } from "../..";
23
24
  import type { VadDetector, VadFrame } from "../../types/streaming/dual-channel";
24
25
  import { EnergyVad } from "./energy-vad";
25
26
  import { attributeTurn, rollUpTurnChannel, VadTimeline } from "./label-mapper";
26
- import { StreamingError, StreamingErrorMessages } from "../../utils/errors";
27
+ import {
28
+ StreamingError,
29
+ StreamingErrorMessages,
30
+ StreamingErrorType,
31
+ } from "../../utils/errors";
27
32
  import { StreamingErrorTypeCodes } from "../../utils/errors/streaming";
28
33
 
29
34
  /**
@@ -58,6 +63,30 @@ function toInt16View(audio: AudioData): Int16Array {
58
63
  const defaultStreamingUrl = "wss://streaming.assemblyai.com/v3/ws";
59
64
  const terminateSessionMessage = `{"type":"Terminate"}`;
60
65
 
66
+ const DEFAULT_CONNECT_TIMEOUT_MS = 1000;
67
+ const DEFAULT_MAX_CONNECTION_RETRIES = 2;
68
+ const DEFAULT_CONNECTION_RETRY_DELAY_MS = 500;
69
+
70
+ /**
71
+ * Close/error codes that signal a permanent client-side problem (auth,
72
+ * billing, malformed config). A retry would hit the same failure, so the
73
+ * connection is never retried on these.
74
+ */
75
+ const NON_RETRYABLE_CLOSE_CODES = new Set<number>([
76
+ StreamingErrorType.BadSampleRate,
77
+ StreamingErrorType.AuthFailed,
78
+ StreamingErrorType.InsufficientFunds,
79
+ StreamingErrorType.FreeTierUser,
80
+ StreamingErrorType.BadSchema,
81
+ ]);
82
+
83
+ /** Error from a single connection attempt, tagged for retry handling. */
84
+ type ConnectionAttemptError = Error & { code?: number; retryable: boolean };
85
+
86
+ function isRetryableCloseCode(code: number): boolean {
87
+ return code !== 1000 && !NON_RETRYABLE_CLOSE_CODES.has(code);
88
+ }
89
+
61
90
  /**
62
91
  * Per-send chunk cap in milliseconds for the dual-channel mixer. The streaming
63
92
  * server rejects audio messages longer than 1000 ms (`Input Duration Error`).
@@ -432,13 +461,90 @@ export class StreamingTranscriber {
432
461
  this.listeners[event] = listener;
433
462
  }
434
463
 
435
- connect() {
436
- return new Promise<BeginEvent>((resolve) => {
437
- if (this.socket) {
438
- throw new Error("Already connected");
464
+ /**
465
+ * Open the streaming session.
466
+ *
467
+ * Resolves with the server's `Begin` event once the handshake completes. A
468
+ * single attempt is bounded by `connectTimeout` (default 1000ms); transient
469
+ * failures (timeout, network drop, unexpected close) are retried up to
470
+ * `maxConnectionRetries` times (default 2), waiting `connectionRetryDelay`
471
+ * (default 500ms) between attempts. Permanent failures (auth, insufficient
472
+ * funds, malformed config) are not retried.
473
+ *
474
+ * Unlike previously, a failed connection now rejects this promise rather
475
+ * than only invoking the `error` listener — necessary for the caller (and
476
+ * the retry loop) to observe the failure.
477
+ */
478
+ async connect(): Promise<BeginEvent> {
479
+ if (this.socket) {
480
+ throw new Error("Already connected");
481
+ }
482
+
483
+ const maxRetries =
484
+ this.params.maxConnectionRetries ?? DEFAULT_MAX_CONNECTION_RETRIES;
485
+ const retryDelay =
486
+ this.params.connectionRetryDelay ?? DEFAULT_CONNECTION_RETRY_DELAY_MS;
487
+
488
+ let lastError: Error | undefined;
489
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
490
+ try {
491
+ return await this.connectOnce();
492
+ } catch (err) {
493
+ lastError = err as Error;
494
+ const retryable = (err as ConnectionAttemptError).retryable === true;
495
+ if (!retryable || attempt === maxRetries) {
496
+ throw err;
497
+ }
498
+ console.warn(
499
+ `Streaming connect attempt ${attempt + 1}/${maxRetries + 1} failed (${(err as Error).message}); retrying`,
500
+ );
501
+ if (retryDelay > 0) {
502
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
503
+ }
439
504
  }
505
+ }
506
+ // The loop above always returns or throws; this only satisfies the type
507
+ // checker that a value is produced on every path.
508
+ throw lastError ?? new Error("Failed to connect to streaming server");
509
+ }
440
510
 
511
+ private connectOnce(): Promise<BeginEvent> {
512
+ return new Promise<BeginEvent>((resolve, reject) => {
441
513
  const url = this.connectionUrl();
514
+ const timeoutMs =
515
+ this.params.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT_MS;
516
+
517
+ // `settled` flips once this attempt has resolved (`Begin`) or rejected
518
+ // (timeout / pre-`Begin` close / error). Before it flips the socket
519
+ // handlers drive this promise; after it flips they revert to normal
520
+ // runtime dispatch (close / error / message listeners).
521
+ let settled = false;
522
+ let timer: ReturnType<typeof setTimeout> | undefined;
523
+
524
+ const failAttempt = (error: ConnectionAttemptError) => {
525
+ if (settled) return;
526
+ settled = true;
527
+ if (timer) clearTimeout(timer);
528
+ this.discardPendingSocket();
529
+ reject(error);
530
+ };
531
+
532
+ const succeed = (begin: BeginEvent) => {
533
+ if (settled) return;
534
+ settled = true;
535
+ if (timer) clearTimeout(timer);
536
+ resolve(begin);
537
+ };
538
+
539
+ if (timeoutMs > 0) {
540
+ timer = setTimeout(() => {
541
+ const err = new StreamingError(
542
+ `Streaming connection timed out after ${timeoutMs}ms`,
543
+ ) as ConnectionAttemptError;
544
+ err.retryable = true;
545
+ failAttempt(err);
546
+ }, timeoutMs);
547
+ }
442
548
 
443
549
  if (this.token) {
444
550
  this.socket = polyfillWebSocketFactory(url.toString());
@@ -465,6 +571,17 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
465
571
  reason = StreamingErrorMessages[code as StreamingErrorTypeCodes];
466
572
  }
467
573
  }
574
+ // A close before `Begin` is a failed connection attempt — reject so
575
+ // connect() can retry (or surface a permanent failure).
576
+ if (!settled) {
577
+ const err = new StreamingError(
578
+ reason || `Streaming connection closed (code=${code})`,
579
+ ) as ConnectionAttemptError;
580
+ err.code = code;
581
+ err.retryable = isRetryableCloseCode(code);
582
+ failAttempt(err);
583
+ return;
584
+ }
468
585
  // Stop the flush timer when the socket is gone (server-initiated close,
469
586
  // network drop, etc.) — otherwise subsequent ticks call send() on a
470
587
  // closed socket and spam the error listener.
@@ -476,18 +593,34 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
476
593
  };
477
594
 
478
595
  this.socket.onerror = (event: ErrorEvent) => {
479
- if (event.error) this.listeners.error?.(event.error as Error);
480
- else this.listeners.error?.(new Error(event.message));
596
+ const error = (event.error as Error) ?? new Error(event.message);
597
+ // A socket error before `Begin` is a failed attempt → reject/retry.
598
+ if (!settled) {
599
+ (error as ConnectionAttemptError).retryable = true;
600
+ failAttempt(error as ConnectionAttemptError);
601
+ return;
602
+ }
603
+ this.listeners.error?.(error);
481
604
  };
482
605
 
483
606
  this.socket.onmessage = ({ data }: MessageEvent) => {
484
607
  const message = JSON.parse(data.toString()) as StreamingEventMessage;
485
608
 
486
609
  if ("error" in message) {
487
- const err = new StreamingError(message.error);
610
+ const err = new StreamingError(message.error) as StreamingError & {
611
+ code?: number;
612
+ };
488
613
  if ("error_code" in message) {
489
- (err as StreamingError & { code?: number }).code =
490
- message.error_code;
614
+ err.code = message.error_code;
615
+ }
616
+ // A server error frame before `Begin` fails the attempt; the code
617
+ // decides whether a retry is worthwhile.
618
+ if (!settled) {
619
+ const attemptErr = err as ConnectionAttemptError;
620
+ attemptErr.retryable =
621
+ err.code === undefined ? true : isRetryableCloseCode(err.code);
622
+ failAttempt(attemptErr);
623
+ return;
491
624
  }
492
625
  this.listeners.error?.(err);
493
626
  return;
@@ -495,7 +628,7 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
495
628
 
496
629
  switch (message.type) {
497
630
  case "Begin": {
498
- resolve(message);
631
+ succeed(message);
499
632
  this.listeners.open?.(message);
500
633
  break;
501
634
  }
@@ -548,6 +681,18 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
548
681
  });
549
682
  }
550
683
 
684
+ /** Tear down a half-open socket from a failed connection attempt. */
685
+ private discardPendingSocket(): void {
686
+ if (!this.socket) return;
687
+ try {
688
+ if (this.socket.removeAllListeners) this.socket.removeAllListeners();
689
+ this.socket.close();
690
+ } catch {
691
+ // Best-effort cleanup; a half-open socket may throw on close.
692
+ }
693
+ this.socket = undefined;
694
+ }
695
+
551
696
  /**
552
697
  * Returns a WritableStream that pumps PCM chunks into `sendAudio`. Single-channel
553
698
  * only — in dual-channel mode use `sendAudio(pcm, { channel })` directly, since
@@ -829,6 +974,17 @@ Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/c
829
974
  this.send(JSON.stringify(message));
830
975
  }
831
976
 
977
+ /**
978
+ * Reset the server's inactivity timer. Only needed when the session was
979
+ * created with `inactivityTimeout` and no audio is being sent.
980
+ */
981
+ keepAlive() {
982
+ const message: StreamingKeepAlive = {
983
+ type: "KeepAlive",
984
+ };
985
+ this.send(JSON.stringify(message));
986
+ }
987
+
832
988
  private send(data: BufferLike) {
833
989
  if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
834
990
  throw new Error("Socket is not open for communication");
@@ -76,6 +76,22 @@ export type StreamingTranscriberParams = {
76
76
  websocketBaseUrl?: string;
77
77
  apiKey?: string;
78
78
  token?: string;
79
+ /**
80
+ * Milliseconds to wait for the streaming handshake (socket open + server
81
+ * `Begin`) before treating the attempt as failed. Defaults to 1000.
82
+ */
83
+ connectTimeout?: number;
84
+ /**
85
+ * Number of additional connection attempts after the first one fails on a
86
+ * transient error (timeout, network drop, unexpected close). 0 disables
87
+ * retries. Permanent failures (auth, insufficient funds, malformed config)
88
+ * are never retried. Defaults to 2.
89
+ */
90
+ maxConnectionRetries?: number;
91
+ /**
92
+ * Milliseconds to wait between connection attempts. Defaults to 500.
93
+ */
94
+ connectionRetryDelay?: number;
79
95
  sampleRate: number;
80
96
  encoding?: AudioEncoding;
81
97
  endOfTurnConfidenceThreshold?: number;
@@ -172,6 +188,7 @@ export type StreamingSpeechModel =
172
188
  | "u3-rt-pro"
173
189
  | "u3-rt-pro-beta-1"
174
190
  | "whisper-rt"
191
+ | "universal-3-5-pro"
175
192
  | "u3-pro";
176
193
 
177
194
  export type StreamingDomain = "medical-v1";
@@ -348,6 +365,10 @@ export type StreamingForceEndpoint = {
348
365
  type: "ForceEndpoint";
349
366
  };
350
367
 
368
+ export type StreamingKeepAlive = {
369
+ type: "KeepAlive";
370
+ };
371
+
351
372
  export type ErrorEvent = {
352
373
  type: "Error";
353
374
  error_code?: number;
@@ -404,4 +425,5 @@ export type StreamingEventMessage =
404
425
  export type StreamingOperationMessage =
405
426
  | StreamingUpdateConfiguration
406
427
  | StreamingForceEndpoint
428
+ | StreamingKeepAlive
407
429
  | StreamingTerminateSession;