@livekit/agents 1.0.49 → 1.0.51

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 (105) hide show
  1. package/dist/index.cjs +12 -10
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +13 -13
  4. package/dist/index.d.ts +13 -13
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +11 -10
  7. package/dist/index.js.map +1 -1
  8. package/dist/inference/api_protos.d.cts +67 -67
  9. package/dist/inference/api_protos.d.ts +67 -67
  10. package/dist/inference/llm.cjs +10 -8
  11. package/dist/inference/llm.cjs.map +1 -1
  12. package/dist/inference/llm.d.cts +1 -1
  13. package/dist/inference/llm.d.ts +1 -1
  14. package/dist/inference/llm.d.ts.map +1 -1
  15. package/dist/inference/llm.js +3 -7
  16. package/dist/inference/llm.js.map +1 -1
  17. package/dist/inference/stt.cjs +20 -12
  18. package/dist/inference/stt.cjs.map +1 -1
  19. package/dist/inference/stt.d.cts +3 -2
  20. package/dist/inference/stt.d.ts +3 -2
  21. package/dist/inference/stt.d.ts.map +1 -1
  22. package/dist/inference/stt.js +20 -12
  23. package/dist/inference/stt.js.map +1 -1
  24. package/dist/inference/stt.test.cjs +14 -0
  25. package/dist/inference/stt.test.cjs.map +1 -1
  26. package/dist/inference/stt.test.js +14 -0
  27. package/dist/inference/stt.test.js.map +1 -1
  28. package/dist/inference/tts.cjs +13 -4
  29. package/dist/inference/tts.cjs.map +1 -1
  30. package/dist/inference/tts.d.cts +2 -1
  31. package/dist/inference/tts.d.ts +2 -1
  32. package/dist/inference/tts.d.ts.map +1 -1
  33. package/dist/inference/tts.js +13 -4
  34. package/dist/inference/tts.js.map +1 -1
  35. package/dist/inference/tts.test.cjs +10 -0
  36. package/dist/inference/tts.test.cjs.map +1 -1
  37. package/dist/inference/tts.test.js +10 -0
  38. package/dist/inference/tts.test.js.map +1 -1
  39. package/dist/inference/utils.cjs +5 -5
  40. package/dist/inference/utils.cjs.map +1 -1
  41. package/dist/inference/utils.js +1 -1
  42. package/dist/inference/utils.js.map +1 -1
  43. package/dist/ipc/job_proc_lazy_main.cjs +13 -4
  44. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  45. package/dist/ipc/job_proc_lazy_main.js +13 -4
  46. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  47. package/dist/language.cjs +394 -0
  48. package/dist/language.cjs.map +1 -0
  49. package/dist/language.d.cts +15 -0
  50. package/dist/language.d.ts +15 -0
  51. package/dist/language.d.ts.map +1 -0
  52. package/dist/language.js +363 -0
  53. package/dist/language.js.map +1 -0
  54. package/dist/language.test.cjs +43 -0
  55. package/dist/language.test.cjs.map +1 -0
  56. package/dist/language.test.js +49 -0
  57. package/dist/language.test.js.map +1 -0
  58. package/dist/stream/deferred_stream.cjs +6 -2
  59. package/dist/stream/deferred_stream.cjs.map +1 -1
  60. package/dist/stream/deferred_stream.d.ts.map +1 -1
  61. package/dist/stream/deferred_stream.js +6 -2
  62. package/dist/stream/deferred_stream.js.map +1 -1
  63. package/dist/stt/stt.cjs.map +1 -1
  64. package/dist/stt/stt.d.cts +2 -1
  65. package/dist/stt/stt.d.ts +2 -1
  66. package/dist/stt/stt.d.ts.map +1 -1
  67. package/dist/stt/stt.js.map +1 -1
  68. package/dist/version.cjs +1 -1
  69. package/dist/version.js +1 -1
  70. package/dist/voice/agent_activity.cjs +4 -1
  71. package/dist/voice/agent_activity.cjs.map +1 -1
  72. package/dist/voice/agent_activity.d.ts.map +1 -1
  73. package/dist/voice/agent_activity.js +4 -1
  74. package/dist/voice/agent_activity.js.map +1 -1
  75. package/dist/voice/agent_activity.test.cjs +135 -0
  76. package/dist/voice/agent_activity.test.cjs.map +1 -0
  77. package/dist/voice/agent_activity.test.js +134 -0
  78. package/dist/voice/agent_activity.test.js.map +1 -0
  79. package/dist/voice/audio_recognition.cjs.map +1 -1
  80. package/dist/voice/audio_recognition.d.cts +3 -2
  81. package/dist/voice/audio_recognition.d.ts +3 -2
  82. package/dist/voice/audio_recognition.d.ts.map +1 -1
  83. package/dist/voice/audio_recognition.js.map +1 -1
  84. package/dist/voice/events.cjs.map +1 -1
  85. package/dist/voice/events.d.cts +3 -2
  86. package/dist/voice/events.d.ts +3 -2
  87. package/dist/voice/events.d.ts.map +1 -1
  88. package/dist/voice/events.js.map +1 -1
  89. package/package.json +1 -1
  90. package/src/index.ts +13 -15
  91. package/src/inference/llm.ts +3 -8
  92. package/src/inference/stt.test.ts +17 -0
  93. package/src/inference/stt.ts +22 -14
  94. package/src/inference/tts.test.ts +12 -0
  95. package/src/inference/tts.ts +14 -5
  96. package/src/inference/utils.ts +1 -1
  97. package/src/ipc/job_proc_lazy_main.ts +15 -4
  98. package/src/language.test.ts +62 -0
  99. package/src/language.ts +380 -0
  100. package/src/stream/deferred_stream.ts +5 -1
  101. package/src/stt/stt.ts +2 -1
  102. package/src/voice/agent_activity.test.ts +194 -0
  103. package/src/voice/agent_activity.ts +11 -1
  104. package/src/voice/audio_recognition.ts +4 -3
  105. package/src/voice/events.ts +3 -2
@@ -6,6 +6,7 @@ import { WebSocket } from 'ws';
6
6
  import { APIError, APIStatusError } from '../_exceptions.js';
7
7
  import { AudioByteStream } from '../audio.js';
8
8
  import { ConnectionPool } from '../connection_pool.js';
9
+ import { type LanguageCode, normalizeLanguage } from '../language.js';
9
10
  import { log } from '../log.js';
10
11
  import { createStreamChannel } from '../stream/stream_channel.js';
11
12
  import { basic as tokenizeBasic } from '../tokenize/index.js';
@@ -150,7 +151,7 @@ const DEFAULT_LANGUAGE = 'en';
150
151
  export interface InferenceTTSOptions<TModel extends TTSModels> {
151
152
  model?: TModel;
152
153
  voice?: string;
153
- language?: string;
154
+ language?: LanguageCode;
154
155
  encoding: TTSEncoding;
155
156
  sampleRate: number;
156
157
  baseURL: string;
@@ -236,7 +237,7 @@ export class TTS<TModel extends TTSModels> extends BaseTTS {
236
237
  this.opts = {
237
238
  model: nextModel,
238
239
  voice: nextVoice,
239
- language,
240
+ language: normalizeLanguage(language),
240
241
  encoding,
241
242
  sampleRate,
242
243
  baseURL: lkBaseURL,
@@ -267,9 +268,13 @@ export class TTS<TModel extends TTSModels> extends BaseTTS {
267
268
  }
268
269
 
269
270
  updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {
270
- this.opts = { ...this.opts, ...opts };
271
+ this.opts = {
272
+ ...this.opts,
273
+ ...opts,
274
+ language: opts.language !== undefined ? normalizeLanguage(opts.language) : this.opts.language,
275
+ };
271
276
  for (const stream of this.streams) {
272
- stream.updateOptions(opts);
277
+ stream.updateOptions(this.opts);
273
278
  }
274
279
  }
275
280
 
@@ -362,7 +367,11 @@ export class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeSt
362
367
  }
363
368
 
364
369
  updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {
365
- this.opts = { ...this.opts, ...opts };
370
+ this.opts = {
371
+ ...this.opts,
372
+ ...opts,
373
+ language: opts.language !== undefined ? normalizeLanguage(opts.language) : this.opts.language,
374
+ };
366
375
  }
367
376
 
368
377
  protected async run(): Promise<void> {
@@ -3,7 +3,7 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { AccessToken } from 'livekit-server-sdk';
5
5
  import { WebSocket } from 'ws';
6
- import { APIConnectionError, APIStatusError } from '../index.js';
6
+ import { APIConnectionError, APIStatusError } from '../_exceptions.js';
7
7
 
8
8
  export type AnyString = string & NonNullable<unknown>;
9
9
 
@@ -16,11 +16,22 @@ import type { IPCMessage } from './message.js';
16
16
  const ORPHANED_TIMEOUT = 15 * 1000;
17
17
 
18
18
  const safeSend = (msg: IPCMessage): boolean => {
19
- if (process.connected && process.send) {
20
- process.send(msg);
21
- return true;
19
+ try {
20
+ if (process.connected && process.send) {
21
+ process.send(msg);
22
+ return true;
23
+ }
24
+ return false;
25
+ } catch (error) {
26
+ // Channel closed is expected during graceful shutdown
27
+ // Log at debug level to avoid noise in production logs
28
+ if (error instanceof Error && error.message.includes('Channel closed')) {
29
+ log().debug({ msgCase: msg.case }, 'IPC channel closed, message not sent');
30
+ } else {
31
+ log().error({ error, msgCase: msg.case }, 'IPC send failed unexpectedly');
32
+ }
33
+ return false;
22
34
  }
23
- return false;
24
35
  };
25
36
 
26
37
  type JobTask = {
@@ -0,0 +1,62 @@
1
+ // SPDX-FileCopyrightText: 2025 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import { describe, expect, it } from 'vitest';
5
+ import {
6
+ areLanguagesEquivalent,
7
+ getBaseLanguage,
8
+ getIsoLanguage,
9
+ getLanguageRegion,
10
+ normalizeLanguage,
11
+ toLanguageName,
12
+ } from './language.js';
13
+
14
+ describe('normalizeLanguage', () => {
15
+ it('normalizes language names', () => {
16
+ expect(normalizeLanguage('english')).toBe('en');
17
+ });
18
+
19
+ it('normalizes iso 639-3 codes', () => {
20
+ expect(normalizeLanguage('eng')).toBe('en');
21
+ });
22
+
23
+ it('normalizes bcp-47 casing and separators', () => {
24
+ expect(normalizeLanguage('en_us')).toBe('en-US');
25
+ expect(normalizeLanguage('zh_hans_cn')).toBe('zh-Hans-CN');
26
+ });
27
+
28
+ it('preserves iso 639-3 in compound tags', () => {
29
+ expect(normalizeLanguage('cmn_hans_cn')).toBe('cmn-Hans-CN');
30
+ });
31
+
32
+ it('passes unknown codes through in lowercase', () => {
33
+ expect(normalizeLanguage('MULTI')).toBe('multi');
34
+ });
35
+
36
+ it('preserves empty string sentinel', () => {
37
+ expect(normalizeLanguage('')).toBe('');
38
+ });
39
+ });
40
+
41
+ describe('language helpers', () => {
42
+ it('extracts base language', () => {
43
+ expect(getBaseLanguage('cmn-Hans-CN')).toBe('zh');
44
+ });
45
+
46
+ it('builds iso language tag', () => {
47
+ expect(getIsoLanguage('cmn-Hans-CN')).toBe('zh-CN');
48
+ });
49
+
50
+ it('extracts region', () => {
51
+ expect(getLanguageRegion('en-US')).toBe('US');
52
+ });
53
+
54
+ it('maps normalized code back to language name', () => {
55
+ expect(toLanguageName('eng')).toBe('english');
56
+ });
57
+
58
+ it('compares equivalent representations', () => {
59
+ expect(areLanguagesEquivalent('english', 'en')).toBe(true);
60
+ expect(areLanguagesEquivalent('en_us', 'en-US')).toBe(true);
61
+ });
62
+ });
@@ -0,0 +1,380 @@
1
+ // SPDX-FileCopyrightText: 2026 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ export const KNOWN_LANGUAGE_CODES = [
6
+ 'af',
7
+ 'am',
8
+ 'ar',
9
+ 'as',
10
+ 'az',
11
+ 'be',
12
+ 'bg',
13
+ 'bn',
14
+ 'bs',
15
+ 'ca',
16
+ 'cs',
17
+ 'cy',
18
+ 'da',
19
+ 'de',
20
+ 'el',
21
+ 'en',
22
+ 'es',
23
+ 'et',
24
+ 'eu',
25
+ 'fa',
26
+ 'ff',
27
+ 'fi',
28
+ 'fr',
29
+ 'ga',
30
+ 'gl',
31
+ 'gu',
32
+ 'ha',
33
+ 'he',
34
+ 'hi',
35
+ 'hr',
36
+ 'hu',
37
+ 'hy',
38
+ 'id',
39
+ 'ig',
40
+ 'is',
41
+ 'it',
42
+ 'ja',
43
+ 'jv',
44
+ 'ka',
45
+ 'kk',
46
+ 'km',
47
+ 'kn',
48
+ 'ko',
49
+ 'ku',
50
+ 'ky',
51
+ 'lb',
52
+ 'lg',
53
+ 'ln',
54
+ 'lo',
55
+ 'lt',
56
+ 'lv',
57
+ 'mi',
58
+ 'mk',
59
+ 'ml',
60
+ 'mn',
61
+ 'mr',
62
+ 'ms',
63
+ 'mt',
64
+ 'my',
65
+ 'ne',
66
+ 'nl',
67
+ 'no',
68
+ 'ny',
69
+ 'oc',
70
+ 'or',
71
+ 'pa',
72
+ 'pl',
73
+ 'ps',
74
+ 'pt',
75
+ 'ro',
76
+ 'ru',
77
+ 'sd',
78
+ 'sk',
79
+ 'sl',
80
+ 'sn',
81
+ 'so',
82
+ 'sq',
83
+ 'sr',
84
+ 'sv',
85
+ 'sw',
86
+ 'ta',
87
+ 'te',
88
+ 'tg',
89
+ 'th',
90
+ 'tl',
91
+ 'tr',
92
+ 'uk',
93
+ 'ur',
94
+ 'uz',
95
+ 'vi',
96
+ 'wo',
97
+ 'xh',
98
+ 'yo',
99
+ 'zh',
100
+ 'zu',
101
+ ] as const;
102
+
103
+ export type KnownLanguageCode = (typeof KNOWN_LANGUAGE_CODES)[number];
104
+
105
+ declare const languageCodeBrand: unique symbol;
106
+
107
+ export type LanguageCode = string & { readonly [languageCodeBrand]: 'LanguageCode' };
108
+
109
+ export function asLanguageCode(language: string): LanguageCode {
110
+ return language as LanguageCode;
111
+ }
112
+
113
+ const ISO_639_3_TO_1: Record<string, string | undefined> = {
114
+ afr: 'af',
115
+ amh: 'am',
116
+ ara: 'ar',
117
+ hye: 'hy',
118
+ asm: 'as',
119
+ ast: undefined,
120
+ aze: 'az',
121
+ bel: 'be',
122
+ ben: 'bn',
123
+ bos: 'bs',
124
+ bul: 'bg',
125
+ mya: 'my',
126
+ yue: undefined,
127
+ cat: 'ca',
128
+ ceb: undefined,
129
+ cmn: 'zh',
130
+ nya: 'ny',
131
+ hrv: 'hr',
132
+ ces: 'cs',
133
+ dan: 'da',
134
+ nld: 'nl',
135
+ eng: 'en',
136
+ est: 'et',
137
+ fil: undefined,
138
+ fin: 'fi',
139
+ fra: 'fr',
140
+ ful: 'ff',
141
+ glg: 'gl',
142
+ lug: 'lg',
143
+ kat: 'ka',
144
+ deu: 'de',
145
+ ell: 'el',
146
+ guj: 'gu',
147
+ hau: 'ha',
148
+ heb: 'he',
149
+ hin: 'hi',
150
+ hun: 'hu',
151
+ isl: 'is',
152
+ ibo: 'ig',
153
+ ind: 'id',
154
+ gle: 'ga',
155
+ ita: 'it',
156
+ jpn: 'ja',
157
+ jav: 'jv',
158
+ kea: undefined,
159
+ kan: 'kn',
160
+ kaz: 'kk',
161
+ khm: 'km',
162
+ kor: 'ko',
163
+ kur: 'ku',
164
+ kir: 'ky',
165
+ lao: 'lo',
166
+ lav: 'lv',
167
+ lin: 'ln',
168
+ lit: 'lt',
169
+ luo: undefined,
170
+ ltz: 'lb',
171
+ mkd: 'mk',
172
+ msa: 'ms',
173
+ mal: 'ml',
174
+ mlt: 'mt',
175
+ zho: 'zh',
176
+ mri: 'mi',
177
+ mar: 'mr',
178
+ mon: 'mn',
179
+ nep: 'ne',
180
+ nso: undefined,
181
+ nor: 'no',
182
+ oci: 'oc',
183
+ ori: 'or',
184
+ pus: 'ps',
185
+ fas: 'fa',
186
+ pol: 'pl',
187
+ por: 'pt',
188
+ pan: 'pa',
189
+ ron: 'ro',
190
+ rus: 'ru',
191
+ srp: 'sr',
192
+ sna: 'sn',
193
+ snd: 'sd',
194
+ slk: 'sk',
195
+ slv: 'sl',
196
+ som: 'so',
197
+ spa: 'es',
198
+ swa: 'sw',
199
+ swe: 'sv',
200
+ tam: 'ta',
201
+ tgk: 'tg',
202
+ tel: 'te',
203
+ tha: 'th',
204
+ tur: 'tr',
205
+ ukr: 'uk',
206
+ umb: undefined,
207
+ urd: 'ur',
208
+ uzb: 'uz',
209
+ vie: 'vi',
210
+ cym: 'cy',
211
+ wol: 'wo',
212
+ xho: 'xh',
213
+ zul: 'zu',
214
+ };
215
+
216
+ const LANGUAGE_NAMES_TO_CODE: Record<string, string> = {
217
+ afrikaans: 'af',
218
+ albanian: 'sq',
219
+ amharic: 'am',
220
+ arabic: 'ar',
221
+ armenian: 'hy',
222
+ azerbaijani: 'az',
223
+ basque: 'eu',
224
+ belarusian: 'be',
225
+ bengali: 'bn',
226
+ bosnian: 'bs',
227
+ bulgarian: 'bg',
228
+ burmese: 'my',
229
+ catalan: 'ca',
230
+ chinese: 'zh',
231
+ croatian: 'hr',
232
+ czech: 'cs',
233
+ danish: 'da',
234
+ dutch: 'nl',
235
+ english: 'en',
236
+ estonian: 'et',
237
+ finnish: 'fi',
238
+ french: 'fr',
239
+ galician: 'gl',
240
+ georgian: 'ka',
241
+ german: 'de',
242
+ greek: 'el',
243
+ gujarati: 'gu',
244
+ hausa: 'ha',
245
+ hebrew: 'he',
246
+ hindi: 'hi',
247
+ hungarian: 'hu',
248
+ icelandic: 'is',
249
+ indonesian: 'id',
250
+ irish: 'ga',
251
+ italian: 'it',
252
+ japanese: 'ja',
253
+ javanese: 'jv',
254
+ kannada: 'kn',
255
+ kazakh: 'kk',
256
+ khmer: 'km',
257
+ korean: 'ko',
258
+ kurdish: 'ku',
259
+ kyrgyz: 'ky',
260
+ lao: 'lo',
261
+ latvian: 'lv',
262
+ lingala: 'ln',
263
+ lithuanian: 'lt',
264
+ luxembourgish: 'lb',
265
+ macedonian: 'mk',
266
+ malay: 'ms',
267
+ malayalam: 'ml',
268
+ maltese: 'mt',
269
+ maori: 'mi',
270
+ marathi: 'mr',
271
+ mongolian: 'mn',
272
+ nepali: 'ne',
273
+ norwegian: 'no',
274
+ occitan: 'oc',
275
+ oriya: 'or',
276
+ pashto: 'ps',
277
+ persian: 'fa',
278
+ polish: 'pl',
279
+ portuguese: 'pt',
280
+ punjabi: 'pa',
281
+ romanian: 'ro',
282
+ russian: 'ru',
283
+ serbian: 'sr',
284
+ shona: 'sn',
285
+ sindhi: 'sd',
286
+ slovak: 'sk',
287
+ slovene: 'sl',
288
+ slovenian: 'sl',
289
+ somali: 'so',
290
+ spanish: 'es',
291
+ swahili: 'sw',
292
+ swedish: 'sv',
293
+ tagalog: 'tl',
294
+ tamil: 'ta',
295
+ tajik: 'tg',
296
+ telugu: 'te',
297
+ thai: 'th',
298
+ turkish: 'tr',
299
+ ukrainian: 'uk',
300
+ urdu: 'ur',
301
+ uzbek: 'uz',
302
+ vietnamese: 'vi',
303
+ welsh: 'cy',
304
+ wolof: 'wo',
305
+ xhosa: 'xh',
306
+ yoruba: 'yo',
307
+ zulu: 'zu',
308
+ };
309
+
310
+ const CODE_TO_LANGUAGE_NAME: Record<string, string> = Object.fromEntries(
311
+ Object.entries(LANGUAGE_NAMES_TO_CODE).map(([name, code]) => [code, name]),
312
+ );
313
+
314
+ CODE_TO_LANGUAGE_NAME.sl = 'slovene';
315
+
316
+ export function normalizeLanguage(language: string): LanguageCode {
317
+ const lowered = language.trim().toLowerCase();
318
+ if (lowered === '') {
319
+ return asLanguageCode('');
320
+ }
321
+
322
+ if (lowered in LANGUAGE_NAMES_TO_CODE) {
323
+ return asLanguageCode(LANGUAGE_NAMES_TO_CODE[lowered]!);
324
+ }
325
+
326
+ if (lowered in ISO_639_3_TO_1) {
327
+ return asLanguageCode(ISO_639_3_TO_1[lowered] ?? lowered);
328
+ }
329
+
330
+ const parts = lowered.replaceAll('_', '-').split('-');
331
+ if (parts.length >= 2) {
332
+ const [base, ...rest] = parts;
333
+ return asLanguageCode(
334
+ [
335
+ base,
336
+ ...rest.map((part) => {
337
+ if (part.length === 4) {
338
+ return part.charAt(0).toUpperCase() + part.slice(1);
339
+ }
340
+ return part.toUpperCase();
341
+ }),
342
+ ].join('-'),
343
+ );
344
+ }
345
+
346
+ return asLanguageCode(lowered);
347
+ }
348
+
349
+ export function getBaseLanguage(language: string): string {
350
+ const normalized = normalizeLanguage(language);
351
+ const [base = ''] = normalized.split('-');
352
+ return ISO_639_3_TO_1[base] ?? base;
353
+ }
354
+
355
+ export function getIsoLanguage(language: string): string {
356
+ const normalized = normalizeLanguage(language);
357
+ const region = getLanguageRegion(normalized);
358
+ const baseLanguage = getBaseLanguage(normalized);
359
+ return region ? `${baseLanguage}-${region}` : baseLanguage;
360
+ }
361
+
362
+ export function getLanguageRegion(language: string): string | undefined {
363
+ const normalized = normalizeLanguage(language);
364
+ const [, ...parts] = normalized.split('-');
365
+ return parts.find((part) => part.length === 2);
366
+ }
367
+
368
+ export function toLanguageName(language: string): string | undefined {
369
+ return CODE_TO_LANGUAGE_NAME[getBaseLanguage(language)];
370
+ }
371
+
372
+ export function areLanguagesEquivalent(
373
+ left: string | null | undefined,
374
+ right: string | null | undefined,
375
+ ): boolean {
376
+ if (left == null || right == null) {
377
+ return left === right;
378
+ }
379
+ return normalizeLanguage(left) === normalizeLanguage(right);
380
+ }
@@ -25,9 +25,13 @@ export function isStreamReaderReleaseError(e: unknown) {
25
25
  'Invalid state: The reader is not attached to a stream',
26
26
  'Controller is already closed',
27
27
  'WritableStream is closed',
28
+ // bun
29
+ 'Stream reader cancelled via releaseLock()',
30
+ // deno
31
+ 'The reader was released.',
28
32
  ];
29
33
 
30
- if (e instanceof TypeError) {
34
+ if (e instanceof TypeError || (e instanceof Error && e.name === 'AbortError')) {
31
35
  return allowedMessages.some((message) => e.message.includes(message));
32
36
  }
33
37
 
package/src/stt/stt.ts CHANGED
@@ -7,6 +7,7 @@ import { EventEmitter } from 'node:events';
7
7
  import type { ReadableStream } from 'node:stream/web';
8
8
  import { APIConnectionError, APIError } from '../_exceptions.js';
9
9
  import { calculateAudioDurationSeconds } from '../audio.js';
10
+ import type { LanguageCode } from '../language.js';
10
11
  import { log } from '../log.js';
11
12
  import type { STTMetrics } from '../metrics/base.js';
12
13
  import { DeferredReadableStream } from '../stream/deferred_stream.js';
@@ -50,7 +51,7 @@ export enum SpeechEventType {
50
51
  /** SpeechData contains metadata about this {@link SpeechEvent}. */
51
52
  export interface SpeechData {
52
53
  /** Language code of the speech. */
53
- language: string;
54
+ language: LanguageCode;
54
55
  /** Transcribed text. */
55
56
  text: string;
56
57
  /** Start time of the speech segment in seconds. */