@livekit/agents-plugin-azure 1.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.
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var import_agents = require("@livekit/agents");
3
+ var import_rtc_node = require("@livekit/rtc-node");
4
+ var import_vitest = require("vitest");
5
+ var import_stt = require("./stt.cjs");
6
+ const azureHarness = import_vitest.vi.hoisted(() => ({
7
+ activeReaders: 0,
8
+ cancellationErrors: 0,
9
+ deadStreamWrites: 0,
10
+ maxActiveReaders: 0,
11
+ recognizers: [],
12
+ streams: []
13
+ }));
14
+ import_vitest.vi.mock("microsoft-cognitiveservices-speech-sdk", async (importOriginal) => {
15
+ const actual = await importOriginal();
16
+ class FakePushStream {
17
+ closed = false;
18
+ frames = [];
19
+ write(buffer) {
20
+ if (this.closed) {
21
+ azureHarness.deadStreamWrites += 1;
22
+ return;
23
+ }
24
+ this.frames.push(new Int16Array(buffer)[0] ?? 0);
25
+ if (azureHarness.streams[0] === this && this.frames.length === 1) {
26
+ queueMicrotask(() => {
27
+ var _a, _b;
28
+ azureHarness.cancellationErrors += 1;
29
+ (_b = (_a = azureHarness.recognizers[0]) == null ? void 0 : _a.canceled) == null ? void 0 : _b.call(_a, void 0, {
30
+ reason: actual.CancellationReason.Error,
31
+ errorCode: actual.CancellationErrorCode.ServiceTimeout,
32
+ errorDetails: "timeout"
33
+ });
34
+ });
35
+ }
36
+ }
37
+ close() {
38
+ this.closed = true;
39
+ const index = azureHarness.streams.indexOf(this);
40
+ queueMicrotask(() => {
41
+ var _a, _b;
42
+ (_b = (_a = azureHarness.recognizers[index]) == null ? void 0 : _a.sessionStopped) == null ? void 0 : _b.call(_a, void 0, {});
43
+ });
44
+ }
45
+ }
46
+ class FakeRecognizer {
47
+ recognizing;
48
+ recognized;
49
+ speechStartDetected;
50
+ speechEndDetected;
51
+ sessionStarted;
52
+ sessionStopped;
53
+ canceled;
54
+ constructor() {
55
+ azureHarness.recognizers.push(this);
56
+ }
57
+ startContinuousRecognitionAsync(resolve) {
58
+ resolve();
59
+ queueMicrotask(() => {
60
+ var _a, _b;
61
+ (_a = this.sessionStarted) == null ? void 0 : _a.call(this, void 0, {});
62
+ (_b = this.speechStartDetected) == null ? void 0 : _b.call(this, void 0, {});
63
+ });
64
+ }
65
+ stopContinuousRecognitionAsync(resolve) {
66
+ resolve();
67
+ }
68
+ close() {
69
+ }
70
+ }
71
+ return {
72
+ ...actual,
73
+ AudioConfig: {
74
+ ...actual.AudioConfig,
75
+ fromStreamInput: () => ({})
76
+ },
77
+ AudioInputStream: {
78
+ ...actual.AudioInputStream,
79
+ createPushStream: () => {
80
+ const stream = new FakePushStream();
81
+ azureHarness.streams.push(stream);
82
+ return stream;
83
+ }
84
+ },
85
+ SpeechRecognizer: FakeRecognizer
86
+ };
87
+ });
88
+ function canceledEvent(reason, errorCode, errorDetails = "") {
89
+ return { reason, errorCode, errorDetails };
90
+ }
91
+ (0, import_vitest.describe)("Azure STT cancellation handling", () => {
92
+ (0, import_vitest.beforeEach)(() => {
93
+ azureHarness.activeReaders = 0;
94
+ azureHarness.cancellationErrors = 0;
95
+ azureHarness.deadStreamWrites = 0;
96
+ azureHarness.maxActiveReaders = 0;
97
+ azureHarness.recognizers.length = 0;
98
+ azureHarness.streams.length = 0;
99
+ });
100
+ (0, import_vitest.it)("unblocks run on canceled error", () => {
101
+ const stream = import_stt.SpeechStream.prototype;
102
+ const testStream = Object.create(stream);
103
+ testStream._sessionStoppedEvent = {
104
+ isSet: false,
105
+ set() {
106
+ this.isSet = true;
107
+ },
108
+ clear() {
109
+ this.isSet = false;
110
+ },
111
+ wait: () => Promise.resolve()
112
+ };
113
+ testStream._cancellationError = null;
114
+ const event = canceledEvent(
115
+ import_stt.speechsdk.CancellationReason.Error,
116
+ import_stt.speechsdk.CancellationErrorCode.ServiceTimeout,
117
+ "timeout"
118
+ );
119
+ testStream._onCanceled(event);
120
+ (0, import_vitest.expect)(testStream._sessionStoppedEvent.isSet).toBe(true);
121
+ (0, import_vitest.expect)(testStream._cancellationError).toBe(event);
122
+ });
123
+ (0, import_vitest.it)("ignores cancellations without error", () => {
124
+ const stream = import_stt.SpeechStream.prototype;
125
+ const testStream = Object.create(stream);
126
+ testStream._sessionStoppedEvent = {
127
+ isSet: false,
128
+ set() {
129
+ this.isSet = true;
130
+ },
131
+ clear() {
132
+ this.isSet = false;
133
+ },
134
+ wait: () => Promise.resolve()
135
+ };
136
+ testStream._cancellationError = null;
137
+ testStream._onCanceled(canceledEvent(import_stt.speechsdk.CancellationReason.EndOfStream));
138
+ (0, import_vitest.expect)(testStream._sessionStoppedEvent.isSet).toBe(false);
139
+ (0, import_vitest.expect)(testStream._cancellationError).toBeNull();
140
+ });
141
+ (0, import_vitest.it)("replaces a canceled recognizer without leaving its input consumer alive", async () => {
142
+ var _a;
143
+ const stt2 = new import_stt.STT({ speechHost: "wss://azure.test" });
144
+ const stream = stt2.stream({
145
+ connOptions: { maxRetry: 1, retryIntervalMs: 0, timeoutMs: 1e3 }
146
+ });
147
+ const internal = stream;
148
+ const originalNext = internal.input.next.bind(internal.input);
149
+ internal.input.next = async (options = {}) => {
150
+ azureHarness.activeReaders += 1;
151
+ azureHarness.maxActiveReaders = Math.max(
152
+ azureHarness.maxActiveReaders,
153
+ azureHarness.activeReaders
154
+ );
155
+ try {
156
+ return await originalNext(options);
157
+ } finally {
158
+ azureHarness.activeReaders -= 1;
159
+ }
160
+ };
161
+ stream.pushFrame(frame(1));
162
+ await import_vitest.vi.waitFor(() => (0, import_vitest.expect)(azureHarness.recognizers).toHaveLength(2));
163
+ stream.pushFrame(frame(2));
164
+ stream.pushFrame(frame(3));
165
+ await import_vitest.vi.waitFor(() => {
166
+ var _a2;
167
+ return (0, import_vitest.expect)((_a2 = azureHarness.streams[1]) == null ? void 0 : _a2.frames).toEqual([2, 3]);
168
+ });
169
+ (0, import_vitest.expect)(azureHarness.cancellationErrors).toBe(1);
170
+ (0, import_vitest.expect)(azureHarness.maxActiveReaders).toBe(1);
171
+ (0, import_vitest.expect)((_a = azureHarness.streams[0]) == null ? void 0 : _a.closed).toBe(true);
172
+ (0, import_vitest.expect)(azureHarness.deadStreamWrites).toBe(0);
173
+ await new Promise((resolve) => setTimeout(resolve, 100));
174
+ (0, import_vitest.expect)(azureHarness.recognizers).toHaveLength(2);
175
+ stream.close();
176
+ });
177
+ (0, import_vitest.it)("emits speech start after replacing a canceled recognizer mid-speech", async () => {
178
+ const azureStt = new import_stt.STT({ speechHost: "wss://azure.test" });
179
+ const stream = azureStt.stream({
180
+ connOptions: { maxRetry: 1, retryIntervalMs: 0, timeoutMs: 1e3 }
181
+ });
182
+ const startEvents = [];
183
+ const collectEvents = (async () => {
184
+ for await (const event of stream) {
185
+ if (event.type === import_agents.stt.SpeechEventType.START_OF_SPEECH) {
186
+ startEvents.push(event.type);
187
+ }
188
+ }
189
+ })();
190
+ try {
191
+ await import_vitest.vi.waitFor(() => (0, import_vitest.expect)(startEvents).toEqual([import_agents.stt.SpeechEventType.START_OF_SPEECH]));
192
+ stream.pushFrame(frame(1));
193
+ await import_vitest.vi.waitFor(() => (0, import_vitest.expect)(azureHarness.recognizers).toHaveLength(2));
194
+ await import_vitest.vi.waitFor(
195
+ () => (0, import_vitest.expect)(startEvents).toEqual([
196
+ import_agents.stt.SpeechEventType.START_OF_SPEECH,
197
+ import_agents.stt.SpeechEventType.START_OF_SPEECH
198
+ ])
199
+ );
200
+ } finally {
201
+ stream.close();
202
+ await collectEvents;
203
+ }
204
+ });
205
+ });
206
+ function frame(value) {
207
+ return new import_rtc_node.AudioFrame(new Int16Array([value]), 16e3, 1, 1);
208
+ }
209
+ //# sourceMappingURL=stt.test.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stt.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { stt } from '@livekit/agents';\nimport { AudioFrame } from '@livekit/rtc-node';\nimport { beforeEach, describe, expect, it, vi } from 'vitest';\nimport { STT, SpeechStream, speechsdk } from './stt.js';\n\nconst azureHarness = vi.hoisted(() => ({\n activeReaders: 0,\n cancellationErrors: 0,\n deadStreamWrites: 0,\n maxActiveReaders: 0,\n recognizers: [] as Array<{\n canceled?: (_sender: unknown, event: unknown) => void;\n sessionStarted?: (_sender: unknown, event: unknown) => void;\n sessionStopped?: (_sender: unknown, event: unknown) => void;\n speechStartDetected?: (_sender: unknown, event: unknown) => void;\n }>,\n streams: [] as Array<{\n closed: boolean;\n frames: number[];\n }>,\n}));\n\nvi.mock('microsoft-cognitiveservices-speech-sdk', async (importOriginal) => {\n const actual = await importOriginal<typeof import('microsoft-cognitiveservices-speech-sdk')>();\n\n class FakePushStream {\n closed = false;\n frames: number[] = [];\n\n write(buffer: ArrayBuffer): void {\n if (this.closed) {\n azureHarness.deadStreamWrites += 1;\n return;\n }\n this.frames.push(new Int16Array(buffer)[0] ?? 0);\n if (azureHarness.streams[0] === this && this.frames.length === 1) {\n queueMicrotask(() => {\n azureHarness.cancellationErrors += 1;\n azureHarness.recognizers[0]?.canceled?.(undefined, {\n reason: actual.CancellationReason.Error,\n errorCode: actual.CancellationErrorCode.ServiceTimeout,\n errorDetails: 'timeout',\n });\n });\n }\n }\n\n close(): void {\n this.closed = true;\n const index = azureHarness.streams.indexOf(this);\n queueMicrotask(() => {\n azureHarness.recognizers[index]?.sessionStopped?.(undefined, {});\n });\n }\n }\n\n class FakeRecognizer {\n recognizing?: (_sender: unknown, event: unknown) => void;\n recognized?: (_sender: unknown, event: unknown) => void;\n speechStartDetected?: (_sender: unknown, event: unknown) => void;\n speechEndDetected?: (_sender: unknown, event: unknown) => void;\n sessionStarted?: (_sender: unknown, event: unknown) => void;\n sessionStopped?: (_sender: unknown, event: unknown) => void;\n canceled?: (_sender: unknown, event: unknown) => void;\n\n constructor() {\n azureHarness.recognizers.push(this);\n }\n\n startContinuousRecognitionAsync(resolve: () => void): void {\n resolve();\n queueMicrotask(() => {\n this.sessionStarted?.(undefined, {});\n this.speechStartDetected?.(undefined, {});\n });\n }\n\n stopContinuousRecognitionAsync(resolve: () => void): void {\n resolve();\n }\n\n close(): void {}\n }\n\n return {\n ...actual,\n AudioConfig: {\n ...actual.AudioConfig,\n fromStreamInput: () => ({}),\n },\n AudioInputStream: {\n ...actual.AudioInputStream,\n createPushStream: () => {\n const stream = new FakePushStream();\n azureHarness.streams.push(stream);\n return stream;\n },\n },\n SpeechRecognizer: FakeRecognizer,\n };\n});\n\nfunction canceledEvent(\n reason: speechsdk.CancellationReason,\n errorCode?: speechsdk.CancellationErrorCode,\n errorDetails = '',\n) {\n return { reason, errorCode, errorDetails };\n}\n\ndescribe('Azure STT cancellation handling', () => {\n beforeEach(() => {\n azureHarness.activeReaders = 0;\n azureHarness.cancellationErrors = 0;\n azureHarness.deadStreamWrites = 0;\n azureHarness.maxActiveReaders = 0;\n azureHarness.recognizers.length = 0;\n azureHarness.streams.length = 0;\n });\n\n it('unblocks run on canceled error', () => {\n const stream = SpeechStream.prototype as SpeechStream;\n const testStream = Object.create(stream) as SpeechStream;\n testStream._sessionStoppedEvent = {\n isSet: false,\n set() {\n this.isSet = true;\n },\n clear() {\n this.isSet = false;\n },\n wait: () => Promise.resolve(),\n } as SpeechStream['_sessionStoppedEvent'];\n testStream._cancellationError = null;\n\n const event = canceledEvent(\n speechsdk.CancellationReason.Error,\n speechsdk.CancellationErrorCode.ServiceTimeout,\n 'timeout',\n );\n testStream._onCanceled(event);\n\n expect(testStream._sessionStoppedEvent.isSet).toBe(true);\n expect(testStream._cancellationError).toBe(event);\n });\n\n it('ignores cancellations without error', () => {\n const stream = SpeechStream.prototype as SpeechStream;\n const testStream = Object.create(stream) as SpeechStream;\n testStream._sessionStoppedEvent = {\n isSet: false,\n set() {\n this.isSet = true;\n },\n clear() {\n this.isSet = false;\n },\n wait: () => Promise.resolve(),\n } as SpeechStream['_sessionStoppedEvent'];\n testStream._cancellationError = null;\n\n testStream._onCanceled(canceledEvent(speechsdk.CancellationReason.EndOfStream));\n\n expect(testStream._sessionStoppedEvent.isSet).toBe(false);\n expect(testStream._cancellationError).toBeNull();\n });\n\n it('replaces a canceled recognizer without leaving its input consumer alive', async () => {\n const stt = new STT({ speechHost: 'wss://azure.test' });\n const stream = stt.stream({\n connOptions: { maxRetry: 1, retryIntervalMs: 0, timeoutMs: 1000 },\n });\n const internal = stream as unknown as {\n input: {\n next(options?: { signal?: AbortSignal }): Promise<IteratorResult<AudioFrame | symbol>>;\n };\n };\n const originalNext = internal.input.next.bind(internal.input);\n internal.input.next = async (options = {}) => {\n azureHarness.activeReaders += 1;\n azureHarness.maxActiveReaders = Math.max(\n azureHarness.maxActiveReaders,\n azureHarness.activeReaders,\n );\n try {\n return await originalNext(options);\n } finally {\n azureHarness.activeReaders -= 1;\n }\n };\n\n stream.pushFrame(frame(1));\n await vi.waitFor(() => expect(azureHarness.recognizers).toHaveLength(2));\n stream.pushFrame(frame(2));\n stream.pushFrame(frame(3));\n await vi.waitFor(() => expect(azureHarness.streams[1]?.frames).toEqual([2, 3]));\n\n expect(azureHarness.cancellationErrors).toBe(1);\n expect(azureHarness.maxActiveReaders).toBe(1);\n expect(azureHarness.streams[0]?.closed).toBe(true);\n expect(azureHarness.deadStreamWrites).toBe(0);\n await new Promise((resolve) => setTimeout(resolve, 100));\n expect(azureHarness.recognizers).toHaveLength(2);\n\n stream.close();\n });\n\n it('emits speech start after replacing a canceled recognizer mid-speech', async () => {\n const azureStt = new STT({ speechHost: 'wss://azure.test' });\n const stream = azureStt.stream({\n connOptions: { maxRetry: 1, retryIntervalMs: 0, timeoutMs: 1000 },\n });\n const startEvents: stt.SpeechEventType[] = [];\n const collectEvents = (async () => {\n for await (const event of stream) {\n if (event.type === stt.SpeechEventType.START_OF_SPEECH) {\n startEvents.push(event.type);\n }\n }\n })();\n\n try {\n await vi.waitFor(() => expect(startEvents).toEqual([stt.SpeechEventType.START_OF_SPEECH]));\n stream.pushFrame(frame(1));\n await vi.waitFor(() => expect(azureHarness.recognizers).toHaveLength(2));\n\n await vi.waitFor(() =>\n expect(startEvents).toEqual([\n stt.SpeechEventType.START_OF_SPEECH,\n stt.SpeechEventType.START_OF_SPEECH,\n ]),\n );\n } finally {\n stream.close();\n await collectEvents;\n }\n });\n});\n\nfunction frame(value: number): AudioFrame {\n return new AudioFrame(new Int16Array([value]), 16000, 1, 1);\n}\n"],"mappings":";AAGA,oBAAoB;AACpB,sBAA2B;AAC3B,oBAAqD;AACrD,iBAA6C;AAE7C,MAAM,eAAe,iBAAG,QAAQ,OAAO;AAAA,EACrC,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,aAAa,CAAC;AAAA,EAMd,SAAS,CAAC;AAIZ,EAAE;AAEF,iBAAG,KAAK,0CAA0C,OAAO,mBAAmB;AAC1E,QAAM,SAAS,MAAM,eAAwE;AAAA,EAE7F,MAAM,eAAe;AAAA,IACnB,SAAS;AAAA,IACT,SAAmB,CAAC;AAAA,IAEpB,MAAM,QAA2B;AAC/B,UAAI,KAAK,QAAQ;AACf,qBAAa,oBAAoB;AACjC;AAAA,MACF;AACA,WAAK,OAAO,KAAK,IAAI,WAAW,MAAM,EAAE,CAAC,KAAK,CAAC;AAC/C,UAAI,aAAa,QAAQ,CAAC,MAAM,QAAQ,KAAK,OAAO,WAAW,GAAG;AAChE,uBAAe,MAAM;AAvC7B;AAwCU,uBAAa,sBAAsB;AACnC,mCAAa,YAAY,CAAC,MAA1B,mBAA6B,aAA7B,4BAAwC,QAAW;AAAA,YACjD,QAAQ,OAAO,mBAAmB;AAAA,YAClC,WAAW,OAAO,sBAAsB;AAAA,YACxC,cAAc;AAAA,UAChB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IAEA,QAAc;AACZ,WAAK,SAAS;AACd,YAAM,QAAQ,aAAa,QAAQ,QAAQ,IAAI;AAC/C,qBAAe,MAAM;AArD3B;AAsDQ,iCAAa,YAAY,KAAK,MAA9B,mBAAiC,mBAAjC,4BAAkD,QAAW,CAAC;AAAA,MAChE,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IAEA,cAAc;AACZ,mBAAa,YAAY,KAAK,IAAI;AAAA,IACpC;AAAA,IAEA,gCAAgC,SAA2B;AACzD,cAAQ;AACR,qBAAe,MAAM;AA1E3B;AA2EQ,mBAAK,mBAAL,8BAAsB,QAAW,CAAC;AAClC,mBAAK,wBAAL,8BAA2B,QAAW,CAAC;AAAA,MACzC,CAAC;AAAA,IACH;AAAA,IAEA,+BAA+B,SAA2B;AACxD,cAAQ;AAAA,IACV;AAAA,IAEA,QAAc;AAAA,IAAC;AAAA,EACjB;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,aAAa;AAAA,MACX,GAAG,OAAO;AAAA,MACV,iBAAiB,OAAO,CAAC;AAAA,IAC3B;AAAA,IACA,kBAAkB;AAAA,MAChB,GAAG,OAAO;AAAA,MACV,kBAAkB,MAAM;AACtB,cAAM,SAAS,IAAI,eAAe;AAClC,qBAAa,QAAQ,KAAK,MAAM;AAChC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA,EACpB;AACF,CAAC;AAED,SAAS,cACP,QACA,WACA,eAAe,IACf;AACA,SAAO,EAAE,QAAQ,WAAW,aAAa;AAC3C;AAAA,IAEA,wBAAS,mCAAmC,MAAM;AAChD,gCAAW,MAAM;AACf,iBAAa,gBAAgB;AAC7B,iBAAa,qBAAqB;AAClC,iBAAa,mBAAmB;AAChC,iBAAa,mBAAmB;AAChC,iBAAa,YAAY,SAAS;AAClC,iBAAa,QAAQ,SAAS;AAAA,EAChC,CAAC;AAED,wBAAG,kCAAkC,MAAM;AACzC,UAAM,SAAS,wBAAa;AAC5B,UAAM,aAAa,OAAO,OAAO,MAAM;AACvC,eAAW,uBAAuB;AAAA,MAChC,OAAO;AAAA,MACP,MAAM;AACJ,aAAK,QAAQ;AAAA,MACf;AAAA,MACA,QAAQ;AACN,aAAK,QAAQ;AAAA,MACf;AAAA,MACA,MAAM,MAAM,QAAQ,QAAQ;AAAA,IAC9B;AACA,eAAW,qBAAqB;AAEhC,UAAM,QAAQ;AAAA,MACZ,qBAAU,mBAAmB;AAAA,MAC7B,qBAAU,sBAAsB;AAAA,MAChC;AAAA,IACF;AACA,eAAW,YAAY,KAAK;AAE5B,8BAAO,WAAW,qBAAqB,KAAK,EAAE,KAAK,IAAI;AACvD,8BAAO,WAAW,kBAAkB,EAAE,KAAK,KAAK;AAAA,EAClD,CAAC;AAED,wBAAG,uCAAuC,MAAM;AAC9C,UAAM,SAAS,wBAAa;AAC5B,UAAM,aAAa,OAAO,OAAO,MAAM;AACvC,eAAW,uBAAuB;AAAA,MAChC,OAAO;AAAA,MACP,MAAM;AACJ,aAAK,QAAQ;AAAA,MACf;AAAA,MACA,QAAQ;AACN,aAAK,QAAQ;AAAA,MACf;AAAA,MACA,MAAM,MAAM,QAAQ,QAAQ;AAAA,IAC9B;AACA,eAAW,qBAAqB;AAEhC,eAAW,YAAY,cAAc,qBAAU,mBAAmB,WAAW,CAAC;AAE9E,8BAAO,WAAW,qBAAqB,KAAK,EAAE,KAAK,KAAK;AACxD,8BAAO,WAAW,kBAAkB,EAAE,SAAS;AAAA,EACjD,CAAC;AAED,wBAAG,2EAA2E,YAAY;AA1K5F;AA2KI,UAAMA,OAAM,IAAI,eAAI,EAAE,YAAY,mBAAmB,CAAC;AACtD,UAAM,SAASA,KAAI,OAAO;AAAA,MACxB,aAAa,EAAE,UAAU,GAAG,iBAAiB,GAAG,WAAW,IAAK;AAAA,IAClE,CAAC;AACD,UAAM,WAAW;AAKjB,UAAM,eAAe,SAAS,MAAM,KAAK,KAAK,SAAS,KAAK;AAC5D,aAAS,MAAM,OAAO,OAAO,UAAU,CAAC,MAAM;AAC5C,mBAAa,iBAAiB;AAC9B,mBAAa,mBAAmB,KAAK;AAAA,QACnC,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AACA,UAAI;AACF,eAAO,MAAM,aAAa,OAAO;AAAA,MACnC,UAAE;AACA,qBAAa,iBAAiB;AAAA,MAChC;AAAA,IACF;AAEA,WAAO,UAAU,MAAM,CAAC,CAAC;AACzB,UAAM,iBAAG,QAAQ,UAAM,sBAAO,aAAa,WAAW,EAAE,aAAa,CAAC,CAAC;AACvE,WAAO,UAAU,MAAM,CAAC,CAAC;AACzB,WAAO,UAAU,MAAM,CAAC,CAAC;AACzB,UAAM,iBAAG,QAAQ,MAAG;AAtMxB,UAAAC;AAsM2B,wCAAOA,MAAA,aAAa,QAAQ,CAAC,MAAtB,gBAAAA,IAAyB,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;AAAA,KAAC;AAE9E,8BAAO,aAAa,kBAAkB,EAAE,KAAK,CAAC;AAC9C,8BAAO,aAAa,gBAAgB,EAAE,KAAK,CAAC;AAC5C,+BAAO,kBAAa,QAAQ,CAAC,MAAtB,mBAAyB,MAAM,EAAE,KAAK,IAAI;AACjD,8BAAO,aAAa,gBAAgB,EAAE,KAAK,CAAC;AAC5C,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG,CAAC;AACvD,8BAAO,aAAa,WAAW,EAAE,aAAa,CAAC;AAE/C,WAAO,MAAM;AAAA,EACf,CAAC;AAED,wBAAG,uEAAuE,YAAY;AACpF,UAAM,WAAW,IAAI,eAAI,EAAE,YAAY,mBAAmB,CAAC;AAC3D,UAAM,SAAS,SAAS,OAAO;AAAA,MAC7B,aAAa,EAAE,UAAU,GAAG,iBAAiB,GAAG,WAAW,IAAK;AAAA,IAClE,CAAC;AACD,UAAM,cAAqC,CAAC;AAC5C,UAAM,iBAAiB,YAAY;AACjC,uBAAiB,SAAS,QAAQ;AAChC,YAAI,MAAM,SAAS,kBAAI,gBAAgB,iBAAiB;AACtD,sBAAY,KAAK,MAAM,IAAI;AAAA,QAC7B;AAAA,MACF;AAAA,IACF,GAAG;AAEH,QAAI;AACF,YAAM,iBAAG,QAAQ,UAAM,sBAAO,WAAW,EAAE,QAAQ,CAAC,kBAAI,gBAAgB,eAAe,CAAC,CAAC;AACzF,aAAO,UAAU,MAAM,CAAC,CAAC;AACzB,YAAM,iBAAG,QAAQ,UAAM,sBAAO,aAAa,WAAW,EAAE,aAAa,CAAC,CAAC;AAEvE,YAAM,iBAAG;AAAA,QAAQ,UACf,sBAAO,WAAW,EAAE,QAAQ;AAAA,UAC1B,kBAAI,gBAAgB;AAAA,UACpB,kBAAI,gBAAgB;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF,UAAE;AACA,aAAO,MAAM;AACb,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AACH,CAAC;AAED,SAAS,MAAM,OAA2B;AACxC,SAAO,IAAI,2BAAW,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,MAAO,GAAG,CAAC;AAC5D;","names":["stt","_a"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stt.test.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stt.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stt.test.d.ts","sourceRoot":"","sources":["../src/stt.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,208 @@
1
+ import { stt } from "@livekit/agents";
2
+ import { AudioFrame } from "@livekit/rtc-node";
3
+ import { beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { STT, SpeechStream, speechsdk } from "./stt.js";
5
+ const azureHarness = vi.hoisted(() => ({
6
+ activeReaders: 0,
7
+ cancellationErrors: 0,
8
+ deadStreamWrites: 0,
9
+ maxActiveReaders: 0,
10
+ recognizers: [],
11
+ streams: []
12
+ }));
13
+ vi.mock("microsoft-cognitiveservices-speech-sdk", async (importOriginal) => {
14
+ const actual = await importOriginal();
15
+ class FakePushStream {
16
+ closed = false;
17
+ frames = [];
18
+ write(buffer) {
19
+ if (this.closed) {
20
+ azureHarness.deadStreamWrites += 1;
21
+ return;
22
+ }
23
+ this.frames.push(new Int16Array(buffer)[0] ?? 0);
24
+ if (azureHarness.streams[0] === this && this.frames.length === 1) {
25
+ queueMicrotask(() => {
26
+ var _a, _b;
27
+ azureHarness.cancellationErrors += 1;
28
+ (_b = (_a = azureHarness.recognizers[0]) == null ? void 0 : _a.canceled) == null ? void 0 : _b.call(_a, void 0, {
29
+ reason: actual.CancellationReason.Error,
30
+ errorCode: actual.CancellationErrorCode.ServiceTimeout,
31
+ errorDetails: "timeout"
32
+ });
33
+ });
34
+ }
35
+ }
36
+ close() {
37
+ this.closed = true;
38
+ const index = azureHarness.streams.indexOf(this);
39
+ queueMicrotask(() => {
40
+ var _a, _b;
41
+ (_b = (_a = azureHarness.recognizers[index]) == null ? void 0 : _a.sessionStopped) == null ? void 0 : _b.call(_a, void 0, {});
42
+ });
43
+ }
44
+ }
45
+ class FakeRecognizer {
46
+ recognizing;
47
+ recognized;
48
+ speechStartDetected;
49
+ speechEndDetected;
50
+ sessionStarted;
51
+ sessionStopped;
52
+ canceled;
53
+ constructor() {
54
+ azureHarness.recognizers.push(this);
55
+ }
56
+ startContinuousRecognitionAsync(resolve) {
57
+ resolve();
58
+ queueMicrotask(() => {
59
+ var _a, _b;
60
+ (_a = this.sessionStarted) == null ? void 0 : _a.call(this, void 0, {});
61
+ (_b = this.speechStartDetected) == null ? void 0 : _b.call(this, void 0, {});
62
+ });
63
+ }
64
+ stopContinuousRecognitionAsync(resolve) {
65
+ resolve();
66
+ }
67
+ close() {
68
+ }
69
+ }
70
+ return {
71
+ ...actual,
72
+ AudioConfig: {
73
+ ...actual.AudioConfig,
74
+ fromStreamInput: () => ({})
75
+ },
76
+ AudioInputStream: {
77
+ ...actual.AudioInputStream,
78
+ createPushStream: () => {
79
+ const stream = new FakePushStream();
80
+ azureHarness.streams.push(stream);
81
+ return stream;
82
+ }
83
+ },
84
+ SpeechRecognizer: FakeRecognizer
85
+ };
86
+ });
87
+ function canceledEvent(reason, errorCode, errorDetails = "") {
88
+ return { reason, errorCode, errorDetails };
89
+ }
90
+ describe("Azure STT cancellation handling", () => {
91
+ beforeEach(() => {
92
+ azureHarness.activeReaders = 0;
93
+ azureHarness.cancellationErrors = 0;
94
+ azureHarness.deadStreamWrites = 0;
95
+ azureHarness.maxActiveReaders = 0;
96
+ azureHarness.recognizers.length = 0;
97
+ azureHarness.streams.length = 0;
98
+ });
99
+ it("unblocks run on canceled error", () => {
100
+ const stream = SpeechStream.prototype;
101
+ const testStream = Object.create(stream);
102
+ testStream._sessionStoppedEvent = {
103
+ isSet: false,
104
+ set() {
105
+ this.isSet = true;
106
+ },
107
+ clear() {
108
+ this.isSet = false;
109
+ },
110
+ wait: () => Promise.resolve()
111
+ };
112
+ testStream._cancellationError = null;
113
+ const event = canceledEvent(
114
+ speechsdk.CancellationReason.Error,
115
+ speechsdk.CancellationErrorCode.ServiceTimeout,
116
+ "timeout"
117
+ );
118
+ testStream._onCanceled(event);
119
+ expect(testStream._sessionStoppedEvent.isSet).toBe(true);
120
+ expect(testStream._cancellationError).toBe(event);
121
+ });
122
+ it("ignores cancellations without error", () => {
123
+ const stream = SpeechStream.prototype;
124
+ const testStream = Object.create(stream);
125
+ testStream._sessionStoppedEvent = {
126
+ isSet: false,
127
+ set() {
128
+ this.isSet = true;
129
+ },
130
+ clear() {
131
+ this.isSet = false;
132
+ },
133
+ wait: () => Promise.resolve()
134
+ };
135
+ testStream._cancellationError = null;
136
+ testStream._onCanceled(canceledEvent(speechsdk.CancellationReason.EndOfStream));
137
+ expect(testStream._sessionStoppedEvent.isSet).toBe(false);
138
+ expect(testStream._cancellationError).toBeNull();
139
+ });
140
+ it("replaces a canceled recognizer without leaving its input consumer alive", async () => {
141
+ var _a;
142
+ const stt2 = new STT({ speechHost: "wss://azure.test" });
143
+ const stream = stt2.stream({
144
+ connOptions: { maxRetry: 1, retryIntervalMs: 0, timeoutMs: 1e3 }
145
+ });
146
+ const internal = stream;
147
+ const originalNext = internal.input.next.bind(internal.input);
148
+ internal.input.next = async (options = {}) => {
149
+ azureHarness.activeReaders += 1;
150
+ azureHarness.maxActiveReaders = Math.max(
151
+ azureHarness.maxActiveReaders,
152
+ azureHarness.activeReaders
153
+ );
154
+ try {
155
+ return await originalNext(options);
156
+ } finally {
157
+ azureHarness.activeReaders -= 1;
158
+ }
159
+ };
160
+ stream.pushFrame(frame(1));
161
+ await vi.waitFor(() => expect(azureHarness.recognizers).toHaveLength(2));
162
+ stream.pushFrame(frame(2));
163
+ stream.pushFrame(frame(3));
164
+ await vi.waitFor(() => {
165
+ var _a2;
166
+ return expect((_a2 = azureHarness.streams[1]) == null ? void 0 : _a2.frames).toEqual([2, 3]);
167
+ });
168
+ expect(azureHarness.cancellationErrors).toBe(1);
169
+ expect(azureHarness.maxActiveReaders).toBe(1);
170
+ expect((_a = azureHarness.streams[0]) == null ? void 0 : _a.closed).toBe(true);
171
+ expect(azureHarness.deadStreamWrites).toBe(0);
172
+ await new Promise((resolve) => setTimeout(resolve, 100));
173
+ expect(azureHarness.recognizers).toHaveLength(2);
174
+ stream.close();
175
+ });
176
+ it("emits speech start after replacing a canceled recognizer mid-speech", async () => {
177
+ const azureStt = new STT({ speechHost: "wss://azure.test" });
178
+ const stream = azureStt.stream({
179
+ connOptions: { maxRetry: 1, retryIntervalMs: 0, timeoutMs: 1e3 }
180
+ });
181
+ const startEvents = [];
182
+ const collectEvents = (async () => {
183
+ for await (const event of stream) {
184
+ if (event.type === stt.SpeechEventType.START_OF_SPEECH) {
185
+ startEvents.push(event.type);
186
+ }
187
+ }
188
+ })();
189
+ try {
190
+ await vi.waitFor(() => expect(startEvents).toEqual([stt.SpeechEventType.START_OF_SPEECH]));
191
+ stream.pushFrame(frame(1));
192
+ await vi.waitFor(() => expect(azureHarness.recognizers).toHaveLength(2));
193
+ await vi.waitFor(
194
+ () => expect(startEvents).toEqual([
195
+ stt.SpeechEventType.START_OF_SPEECH,
196
+ stt.SpeechEventType.START_OF_SPEECH
197
+ ])
198
+ );
199
+ } finally {
200
+ stream.close();
201
+ await collectEvents;
202
+ }
203
+ });
204
+ });
205
+ function frame(value) {
206
+ return new AudioFrame(new Int16Array([value]), 16e3, 1, 1);
207
+ }
208
+ //# sourceMappingURL=stt.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stt.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { stt } from '@livekit/agents';\nimport { AudioFrame } from '@livekit/rtc-node';\nimport { beforeEach, describe, expect, it, vi } from 'vitest';\nimport { STT, SpeechStream, speechsdk } from './stt.js';\n\nconst azureHarness = vi.hoisted(() => ({\n activeReaders: 0,\n cancellationErrors: 0,\n deadStreamWrites: 0,\n maxActiveReaders: 0,\n recognizers: [] as Array<{\n canceled?: (_sender: unknown, event: unknown) => void;\n sessionStarted?: (_sender: unknown, event: unknown) => void;\n sessionStopped?: (_sender: unknown, event: unknown) => void;\n speechStartDetected?: (_sender: unknown, event: unknown) => void;\n }>,\n streams: [] as Array<{\n closed: boolean;\n frames: number[];\n }>,\n}));\n\nvi.mock('microsoft-cognitiveservices-speech-sdk', async (importOriginal) => {\n const actual = await importOriginal<typeof import('microsoft-cognitiveservices-speech-sdk')>();\n\n class FakePushStream {\n closed = false;\n frames: number[] = [];\n\n write(buffer: ArrayBuffer): void {\n if (this.closed) {\n azureHarness.deadStreamWrites += 1;\n return;\n }\n this.frames.push(new Int16Array(buffer)[0] ?? 0);\n if (azureHarness.streams[0] === this && this.frames.length === 1) {\n queueMicrotask(() => {\n azureHarness.cancellationErrors += 1;\n azureHarness.recognizers[0]?.canceled?.(undefined, {\n reason: actual.CancellationReason.Error,\n errorCode: actual.CancellationErrorCode.ServiceTimeout,\n errorDetails: 'timeout',\n });\n });\n }\n }\n\n close(): void {\n this.closed = true;\n const index = azureHarness.streams.indexOf(this);\n queueMicrotask(() => {\n azureHarness.recognizers[index]?.sessionStopped?.(undefined, {});\n });\n }\n }\n\n class FakeRecognizer {\n recognizing?: (_sender: unknown, event: unknown) => void;\n recognized?: (_sender: unknown, event: unknown) => void;\n speechStartDetected?: (_sender: unknown, event: unknown) => void;\n speechEndDetected?: (_sender: unknown, event: unknown) => void;\n sessionStarted?: (_sender: unknown, event: unknown) => void;\n sessionStopped?: (_sender: unknown, event: unknown) => void;\n canceled?: (_sender: unknown, event: unknown) => void;\n\n constructor() {\n azureHarness.recognizers.push(this);\n }\n\n startContinuousRecognitionAsync(resolve: () => void): void {\n resolve();\n queueMicrotask(() => {\n this.sessionStarted?.(undefined, {});\n this.speechStartDetected?.(undefined, {});\n });\n }\n\n stopContinuousRecognitionAsync(resolve: () => void): void {\n resolve();\n }\n\n close(): void {}\n }\n\n return {\n ...actual,\n AudioConfig: {\n ...actual.AudioConfig,\n fromStreamInput: () => ({}),\n },\n AudioInputStream: {\n ...actual.AudioInputStream,\n createPushStream: () => {\n const stream = new FakePushStream();\n azureHarness.streams.push(stream);\n return stream;\n },\n },\n SpeechRecognizer: FakeRecognizer,\n };\n});\n\nfunction canceledEvent(\n reason: speechsdk.CancellationReason,\n errorCode?: speechsdk.CancellationErrorCode,\n errorDetails = '',\n) {\n return { reason, errorCode, errorDetails };\n}\n\ndescribe('Azure STT cancellation handling', () => {\n beforeEach(() => {\n azureHarness.activeReaders = 0;\n azureHarness.cancellationErrors = 0;\n azureHarness.deadStreamWrites = 0;\n azureHarness.maxActiveReaders = 0;\n azureHarness.recognizers.length = 0;\n azureHarness.streams.length = 0;\n });\n\n it('unblocks run on canceled error', () => {\n const stream = SpeechStream.prototype as SpeechStream;\n const testStream = Object.create(stream) as SpeechStream;\n testStream._sessionStoppedEvent = {\n isSet: false,\n set() {\n this.isSet = true;\n },\n clear() {\n this.isSet = false;\n },\n wait: () => Promise.resolve(),\n } as SpeechStream['_sessionStoppedEvent'];\n testStream._cancellationError = null;\n\n const event = canceledEvent(\n speechsdk.CancellationReason.Error,\n speechsdk.CancellationErrorCode.ServiceTimeout,\n 'timeout',\n );\n testStream._onCanceled(event);\n\n expect(testStream._sessionStoppedEvent.isSet).toBe(true);\n expect(testStream._cancellationError).toBe(event);\n });\n\n it('ignores cancellations without error', () => {\n const stream = SpeechStream.prototype as SpeechStream;\n const testStream = Object.create(stream) as SpeechStream;\n testStream._sessionStoppedEvent = {\n isSet: false,\n set() {\n this.isSet = true;\n },\n clear() {\n this.isSet = false;\n },\n wait: () => Promise.resolve(),\n } as SpeechStream['_sessionStoppedEvent'];\n testStream._cancellationError = null;\n\n testStream._onCanceled(canceledEvent(speechsdk.CancellationReason.EndOfStream));\n\n expect(testStream._sessionStoppedEvent.isSet).toBe(false);\n expect(testStream._cancellationError).toBeNull();\n });\n\n it('replaces a canceled recognizer without leaving its input consumer alive', async () => {\n const stt = new STT({ speechHost: 'wss://azure.test' });\n const stream = stt.stream({\n connOptions: { maxRetry: 1, retryIntervalMs: 0, timeoutMs: 1000 },\n });\n const internal = stream as unknown as {\n input: {\n next(options?: { signal?: AbortSignal }): Promise<IteratorResult<AudioFrame | symbol>>;\n };\n };\n const originalNext = internal.input.next.bind(internal.input);\n internal.input.next = async (options = {}) => {\n azureHarness.activeReaders += 1;\n azureHarness.maxActiveReaders = Math.max(\n azureHarness.maxActiveReaders,\n azureHarness.activeReaders,\n );\n try {\n return await originalNext(options);\n } finally {\n azureHarness.activeReaders -= 1;\n }\n };\n\n stream.pushFrame(frame(1));\n await vi.waitFor(() => expect(azureHarness.recognizers).toHaveLength(2));\n stream.pushFrame(frame(2));\n stream.pushFrame(frame(3));\n await vi.waitFor(() => expect(azureHarness.streams[1]?.frames).toEqual([2, 3]));\n\n expect(azureHarness.cancellationErrors).toBe(1);\n expect(azureHarness.maxActiveReaders).toBe(1);\n expect(azureHarness.streams[0]?.closed).toBe(true);\n expect(azureHarness.deadStreamWrites).toBe(0);\n await new Promise((resolve) => setTimeout(resolve, 100));\n expect(azureHarness.recognizers).toHaveLength(2);\n\n stream.close();\n });\n\n it('emits speech start after replacing a canceled recognizer mid-speech', async () => {\n const azureStt = new STT({ speechHost: 'wss://azure.test' });\n const stream = azureStt.stream({\n connOptions: { maxRetry: 1, retryIntervalMs: 0, timeoutMs: 1000 },\n });\n const startEvents: stt.SpeechEventType[] = [];\n const collectEvents = (async () => {\n for await (const event of stream) {\n if (event.type === stt.SpeechEventType.START_OF_SPEECH) {\n startEvents.push(event.type);\n }\n }\n })();\n\n try {\n await vi.waitFor(() => expect(startEvents).toEqual([stt.SpeechEventType.START_OF_SPEECH]));\n stream.pushFrame(frame(1));\n await vi.waitFor(() => expect(azureHarness.recognizers).toHaveLength(2));\n\n await vi.waitFor(() =>\n expect(startEvents).toEqual([\n stt.SpeechEventType.START_OF_SPEECH,\n stt.SpeechEventType.START_OF_SPEECH,\n ]),\n );\n } finally {\n stream.close();\n await collectEvents;\n }\n });\n});\n\nfunction frame(value: number): AudioFrame {\n return new AudioFrame(new Int16Array([value]), 16000, 1, 1);\n}\n"],"mappings":"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","names":["stt","_a"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@livekit/agents-plugin-azure",
3
+ "version": "1.5.0",
4
+ "description": "Azure plugin for LiveKit Agents for Node.js",
5
+ "main": "dist/index.js",
6
+ "require": "dist/index.cjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ "import": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "require": {
14
+ "types": "./dist/index.d.cts",
15
+ "default": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "author": "LiveKit",
19
+ "type": "module",
20
+ "repository": "git@github.com:livekit/agents-js.git",
21
+ "license": "Apache-2.0",
22
+ "files": [
23
+ "dist",
24
+ "src",
25
+ "README.md"
26
+ ],
27
+ "devDependencies": {
28
+ "@livekit/rtc-node": "^0.13.31",
29
+ "@microsoft/api-extractor": "^7.35.0",
30
+ "tsup": "^8.3.5",
31
+ "typescript": "^5.0.0",
32
+ "@livekit/agents": "1.5.2"
33
+ },
34
+ "dependencies": {
35
+ "microsoft-cognitiveservices-speech-sdk": "^1.50.0"
36
+ },
37
+ "peerDependencies": {
38
+ "@livekit/rtc-node": "^0.13.31",
39
+ "@livekit/agents": "1.5.2"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup --onSuccess \"pnpm build:types\"",
43
+ "build:types": "tsc --declaration --emitDeclarationOnly && node ../../scripts/copyDeclarationOutput.js",
44
+ "clean": "rm -rf dist",
45
+ "clean:build": "pnpm clean && pnpm build",
46
+ "lint": "eslint -f unix \"src/**/*.{ts,js}\"",
47
+ "api:check": "api-extractor run --typescript-compiler-folder ../../node_modules/typescript",
48
+ "api:update": "api-extractor run --local --typescript-compiler-folder ../../node_modules/typescript --verbose"
49
+ }
50
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ // SPDX-FileCopyrightText: 2026 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import { Plugin } from '@livekit/agents';
5
+
6
+ export * from './stt.js';
7
+
8
+ class AzurePlugin extends Plugin {
9
+ constructor() {
10
+ super({
11
+ title: 'azure',
12
+ version: __PACKAGE_VERSION__,
13
+ package: __PACKAGE_NAME__,
14
+ });
15
+ }
16
+ }
17
+
18
+ Plugin.registerPlugin(new AzurePlugin());