@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.
- package/dist/index.cjs +12 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -13
- package/dist/index.d.ts +13 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/inference/api_protos.d.cts +67 -67
- package/dist/inference/api_protos.d.ts +67 -67
- package/dist/inference/llm.cjs +10 -8
- package/dist/inference/llm.cjs.map +1 -1
- package/dist/inference/llm.d.cts +1 -1
- package/dist/inference/llm.d.ts +1 -1
- package/dist/inference/llm.d.ts.map +1 -1
- package/dist/inference/llm.js +3 -7
- package/dist/inference/llm.js.map +1 -1
- package/dist/inference/stt.cjs +20 -12
- package/dist/inference/stt.cjs.map +1 -1
- package/dist/inference/stt.d.cts +3 -2
- package/dist/inference/stt.d.ts +3 -2
- package/dist/inference/stt.d.ts.map +1 -1
- package/dist/inference/stt.js +20 -12
- package/dist/inference/stt.js.map +1 -1
- package/dist/inference/stt.test.cjs +14 -0
- package/dist/inference/stt.test.cjs.map +1 -1
- package/dist/inference/stt.test.js +14 -0
- package/dist/inference/stt.test.js.map +1 -1
- package/dist/inference/tts.cjs +13 -4
- package/dist/inference/tts.cjs.map +1 -1
- package/dist/inference/tts.d.cts +2 -1
- package/dist/inference/tts.d.ts +2 -1
- package/dist/inference/tts.d.ts.map +1 -1
- package/dist/inference/tts.js +13 -4
- package/dist/inference/tts.js.map +1 -1
- package/dist/inference/tts.test.cjs +10 -0
- package/dist/inference/tts.test.cjs.map +1 -1
- package/dist/inference/tts.test.js +10 -0
- package/dist/inference/tts.test.js.map +1 -1
- package/dist/inference/utils.cjs +5 -5
- package/dist/inference/utils.cjs.map +1 -1
- package/dist/inference/utils.js +1 -1
- package/dist/inference/utils.js.map +1 -1
- package/dist/ipc/job_proc_lazy_main.cjs +13 -4
- package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
- package/dist/ipc/job_proc_lazy_main.js +13 -4
- package/dist/ipc/job_proc_lazy_main.js.map +1 -1
- package/dist/language.cjs +394 -0
- package/dist/language.cjs.map +1 -0
- package/dist/language.d.cts +15 -0
- package/dist/language.d.ts +15 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +363 -0
- package/dist/language.js.map +1 -0
- package/dist/language.test.cjs +43 -0
- package/dist/language.test.cjs.map +1 -0
- package/dist/language.test.js +49 -0
- package/dist/language.test.js.map +1 -0
- package/dist/stream/deferred_stream.cjs +6 -2
- package/dist/stream/deferred_stream.cjs.map +1 -1
- package/dist/stream/deferred_stream.d.ts.map +1 -1
- package/dist/stream/deferred_stream.js +6 -2
- package/dist/stream/deferred_stream.js.map +1 -1
- package/dist/stt/stt.cjs.map +1 -1
- package/dist/stt/stt.d.cts +2 -1
- package/dist/stt/stt.d.ts +2 -1
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist/voice/agent_activity.cjs +4 -1
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +4 -1
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_activity.test.cjs +135 -0
- package/dist/voice/agent_activity.test.cjs.map +1 -0
- package/dist/voice/agent_activity.test.js +134 -0
- package/dist/voice/agent_activity.test.js.map +1 -0
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +3 -2
- package/dist/voice/audio_recognition.d.ts +3 -2
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/events.cjs.map +1 -1
- package/dist/voice/events.d.cts +3 -2
- package/dist/voice/events.d.ts +3 -2
- package/dist/voice/events.d.ts.map +1 -1
- package/dist/voice/events.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +13 -15
- package/src/inference/llm.ts +3 -8
- package/src/inference/stt.test.ts +17 -0
- package/src/inference/stt.ts +22 -14
- package/src/inference/tts.test.ts +12 -0
- package/src/inference/tts.ts +14 -5
- package/src/inference/utils.ts +1 -1
- package/src/ipc/job_proc_lazy_main.ts +15 -4
- package/src/language.test.ts +62 -0
- package/src/language.ts +380 -0
- package/src/stream/deferred_stream.ts +5 -1
- package/src/stt/stt.ts +2 -1
- package/src/voice/agent_activity.test.ts +194 -0
- package/src/voice/agent_activity.ts +11 -1
- package/src/voice/audio_recognition.ts +4 -3
- package/src/voice/events.ts +3 -2
package/src/inference/tts.ts
CHANGED
|
@@ -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?:
|
|
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 = {
|
|
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 = {
|
|
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> {
|
package/src/inference/utils.ts
CHANGED
|
@@ -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 '../
|
|
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
|
-
|
|
20
|
-
process.send
|
|
21
|
-
|
|
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
|
+
});
|
package/src/language.ts
ADDED
|
@@ -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:
|
|
54
|
+
language: LanguageCode;
|
|
54
55
|
/** Transcribed text. */
|
|
55
56
|
text: string;
|
|
56
57
|
/** Start time of the speech segment in seconds. */
|