@livekit/agents 1.0.50 → 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/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 +1 -1
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.js +1 -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/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 +1 -1
- package/src/voice/audio_recognition.ts +4 -3
- package/src/voice/events.ts +3 -2
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var import_heap_js = require("heap-js");
|
|
3
|
+
var import_vitest = require("vitest");
|
|
4
|
+
var import_utils = require("../utils.cjs");
|
|
5
|
+
var import_agent_activity = require("./agent_activity.cjs");
|
|
6
|
+
var import_speech_handle = require("./speech_handle.cjs");
|
|
7
|
+
import_vitest.vi.mock("./agent.js", () => {
|
|
8
|
+
class Agent {
|
|
9
|
+
}
|
|
10
|
+
class AgentTask extends Agent {
|
|
11
|
+
}
|
|
12
|
+
class StopResponse {
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
Agent,
|
|
16
|
+
AgentTask,
|
|
17
|
+
StopResponse,
|
|
18
|
+
_getActivityTaskInfo: () => null,
|
|
19
|
+
_setActivityTaskInfo: () => {
|
|
20
|
+
},
|
|
21
|
+
functionCallStorage: {
|
|
22
|
+
getStore: () => void 0,
|
|
23
|
+
enterWith: () => {
|
|
24
|
+
},
|
|
25
|
+
run: (_, fn) => fn()
|
|
26
|
+
},
|
|
27
|
+
speechHandleStorage: {
|
|
28
|
+
getStore: () => void 0,
|
|
29
|
+
enterWith: () => {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
import_vitest.vi.mock("../version.js", () => ({ version: "0.0.0-test" }));
|
|
35
|
+
async function raceTimeout(promise, ms) {
|
|
36
|
+
let timer;
|
|
37
|
+
const timeout = new Promise((resolve) => {
|
|
38
|
+
timer = setTimeout(() => resolve("timeout"), ms);
|
|
39
|
+
});
|
|
40
|
+
return Promise.race([promise.then(() => "resolved"), timeout]).finally(
|
|
41
|
+
() => clearTimeout(timer)
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
function buildMainTaskRunner() {
|
|
45
|
+
const q_updated = new import_utils.Future();
|
|
46
|
+
const speechQueue = new import_heap_js.Heap((a, b) => b[0] - a[0] || a[1] - b[1]);
|
|
47
|
+
const fakeActivity = {
|
|
48
|
+
q_updated,
|
|
49
|
+
speechQueue,
|
|
50
|
+
_currentSpeech: void 0,
|
|
51
|
+
_schedulingPaused: false,
|
|
52
|
+
getDrainPendingSpeechTasks: () => [],
|
|
53
|
+
logger: {
|
|
54
|
+
info: () => {
|
|
55
|
+
},
|
|
56
|
+
debug: () => {
|
|
57
|
+
},
|
|
58
|
+
warn: () => {
|
|
59
|
+
},
|
|
60
|
+
error: () => {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const mainTask = import_agent_activity.AgentActivity.prototype.mainTask;
|
|
65
|
+
return {
|
|
66
|
+
fakeActivity,
|
|
67
|
+
mainTask: mainTask.bind(fakeActivity),
|
|
68
|
+
speechQueue,
|
|
69
|
+
q_updated
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
(0, import_vitest.describe)("AgentActivity - mainTask", () => {
|
|
73
|
+
(0, import_vitest.it)("should recover when speech handle is interrupted after authorization", async () => {
|
|
74
|
+
const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();
|
|
75
|
+
const handle = import_speech_handle.SpeechHandle.create({ allowInterruptions: true });
|
|
76
|
+
speechQueue.push([import_speech_handle.SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handle]);
|
|
77
|
+
handle._markScheduled();
|
|
78
|
+
q_updated.resolve();
|
|
79
|
+
const ac = new AbortController();
|
|
80
|
+
const mainTaskPromise = mainTask(ac.signal);
|
|
81
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
82
|
+
handle.interrupt();
|
|
83
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
84
|
+
fakeActivity._schedulingPaused = true;
|
|
85
|
+
fakeActivity.q_updated = new import_utils.Future();
|
|
86
|
+
fakeActivity.q_updated.resolve();
|
|
87
|
+
ac.abort();
|
|
88
|
+
const result = await raceTimeout(mainTaskPromise, 2e3);
|
|
89
|
+
(0, import_vitest.expect)(result).toBe("resolved");
|
|
90
|
+
});
|
|
91
|
+
(0, import_vitest.it)("should process next queued handle after an interrupted one", async () => {
|
|
92
|
+
const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();
|
|
93
|
+
const handleA = import_speech_handle.SpeechHandle.create({ allowInterruptions: true });
|
|
94
|
+
const handleB = import_speech_handle.SpeechHandle.create({ allowInterruptions: true });
|
|
95
|
+
speechQueue.push([import_speech_handle.SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handleA]);
|
|
96
|
+
handleA._markScheduled();
|
|
97
|
+
speechQueue.push([import_speech_handle.SpeechHandle.SPEECH_PRIORITY_NORMAL, 2, handleB]);
|
|
98
|
+
handleB._markScheduled();
|
|
99
|
+
q_updated.resolve();
|
|
100
|
+
const ac = new AbortController();
|
|
101
|
+
const mainTaskPromise = mainTask(ac.signal);
|
|
102
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
103
|
+
handleA.interrupt();
|
|
104
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
105
|
+
try {
|
|
106
|
+
handleB._markGenerationDone();
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
110
|
+
fakeActivity._schedulingPaused = true;
|
|
111
|
+
fakeActivity.q_updated = new import_utils.Future();
|
|
112
|
+
fakeActivity.q_updated.resolve();
|
|
113
|
+
ac.abort();
|
|
114
|
+
const result = await raceTimeout(mainTaskPromise, 2e3);
|
|
115
|
+
(0, import_vitest.expect)(result).toBe("resolved");
|
|
116
|
+
});
|
|
117
|
+
(0, import_vitest.it)("should skip handles that were interrupted before being popped", async () => {
|
|
118
|
+
const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();
|
|
119
|
+
const handle = import_speech_handle.SpeechHandle.create({ allowInterruptions: true });
|
|
120
|
+
handle.interrupt();
|
|
121
|
+
speechQueue.push([import_speech_handle.SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handle]);
|
|
122
|
+
handle._markScheduled();
|
|
123
|
+
q_updated.resolve();
|
|
124
|
+
const ac = new AbortController();
|
|
125
|
+
const mainTaskPromise = mainTask(ac.signal);
|
|
126
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
127
|
+
fakeActivity._schedulingPaused = true;
|
|
128
|
+
fakeActivity.q_updated = new import_utils.Future();
|
|
129
|
+
fakeActivity.q_updated.resolve();
|
|
130
|
+
ac.abort();
|
|
131
|
+
const result = await raceTimeout(mainTaskPromise, 2e3);
|
|
132
|
+
(0, import_vitest.expect)(result).toBe("resolved");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
//# sourceMappingURL=agent_activity.test.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/voice/agent_activity.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Regression tests for mainTask speech handle processing.\n *\n * When a speech handle is interrupted after _authorizeGeneration() but before the\n * reply task calls _markGenerationDone(), mainTask hangs on _waitForGeneration()\n * indefinitely. All subsequent speech handles queue behind it and the agent becomes\n * unresponsive.\n *\n * Fix: race _waitForGeneration() against the interrupt future via waitIfNotInterrupted().\n *\n * Related: #1124, #1089, #836\n */\nimport { Heap } from 'heap-js';\nimport { describe, expect, it, vi } from 'vitest';\nimport { Future } from '../utils.js';\nimport { AgentActivity } from './agent_activity.js';\nimport { SpeechHandle } from './speech_handle.js';\n\n// Break circular dependency: agent_activity.ts → agent.js → beta/workflows/task_group.ts\nvi.mock('./agent.js', () => {\n class Agent {}\n class AgentTask extends Agent {}\n class StopResponse {}\n return {\n Agent,\n AgentTask,\n StopResponse,\n _getActivityTaskInfo: () => null,\n _setActivityTaskInfo: () => {},\n functionCallStorage: {\n getStore: () => undefined,\n enterWith: () => {},\n run: (_: unknown, fn: () => unknown) => fn(),\n },\n speechHandleStorage: {\n getStore: () => undefined,\n enterWith: () => {},\n },\n };\n});\n\nvi.mock('../version.js', () => ({ version: '0.0.0-test' }));\n\nasync function raceTimeout(promise: Promise<unknown>, ms: number): Promise<'resolved' | 'timeout'> {\n let timer: ReturnType<typeof setTimeout>;\n const timeout = new Promise<'timeout'>((resolve) => {\n timer = setTimeout(() => resolve('timeout'), ms);\n });\n return Promise.race([promise.then(() => 'resolved' as const), timeout]).finally(() =>\n clearTimeout(timer),\n );\n}\n\n/**\n * Build a minimal stand-in with just enough state for mainTask to run.\n *\n * mainTask accesses: q_updated, speechQueue, _currentSpeech, _schedulingPaused,\n * getDrainPendingSpeechTasks(), and logger. We provide stubs for all of these,\n * then bind the real AgentActivity.prototype.mainTask to this object.\n */\nfunction buildMainTaskRunner() {\n const q_updated = new Future<void>();\n type HeapItem = [number, number, SpeechHandle];\n const speechQueue = new Heap<HeapItem>((a: HeapItem, b: HeapItem) => b[0] - a[0] || a[1] - b[1]);\n\n const fakeActivity = {\n q_updated,\n speechQueue,\n _currentSpeech: undefined as SpeechHandle | undefined,\n _schedulingPaused: false,\n getDrainPendingSpeechTasks: () => [],\n logger: {\n info: () => {},\n debug: () => {},\n warn: () => {},\n error: () => {},\n },\n };\n\n const mainTask = (AgentActivity.prototype as Record<string, unknown>).mainTask as (\n signal: AbortSignal,\n ) => Promise<void>;\n\n return {\n fakeActivity,\n mainTask: mainTask.bind(fakeActivity),\n speechQueue,\n q_updated,\n };\n}\n\ndescribe('AgentActivity - mainTask', () => {\n it('should recover when speech handle is interrupted after authorization', async () => {\n const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();\n\n const handle = SpeechHandle.create({ allowInterruptions: true });\n\n speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handle]);\n handle._markScheduled();\n q_updated.resolve();\n\n const ac = new AbortController();\n const mainTaskPromise = mainTask(ac.signal);\n\n // Give mainTask time to pop the handle and call _authorizeGeneration\n await new Promise((r) => setTimeout(r, 50));\n\n // Interrupt while waiting for generation\n handle.interrupt();\n\n // Let mainTask react to the interrupt, then signal exit\n await new Promise((r) => setTimeout(r, 50));\n fakeActivity._schedulingPaused = true;\n fakeActivity.q_updated = new Future();\n fakeActivity.q_updated.resolve();\n ac.abort();\n\n const result = await raceTimeout(mainTaskPromise, 2000);\n expect(result).toBe('resolved');\n });\n\n it('should process next queued handle after an interrupted one', async () => {\n const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();\n\n const handleA = SpeechHandle.create({ allowInterruptions: true });\n const handleB = SpeechHandle.create({ allowInterruptions: true });\n\n speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handleA]);\n handleA._markScheduled();\n speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 2, handleB]);\n handleB._markScheduled();\n q_updated.resolve();\n\n const ac = new AbortController();\n const mainTaskPromise = mainTask(ac.signal);\n\n // Wait for mainTask to pick up handle A\n await new Promise((r) => setTimeout(r, 50));\n\n // Interrupt handle A\n handleA.interrupt();\n\n // Wait for mainTask to move to handle B and authorize it\n await new Promise((r) => setTimeout(r, 50));\n\n // Resolve handle B's generation (simulating normal reply task completion).\n // If mainTask is stuck on handle A (bug), handle B was never authorized and this\n // throws — we catch it and let the timeout assert the real failure.\n try {\n handleB._markGenerationDone();\n } catch {\n // Expected when fix is absent: handle B has no active generation\n }\n\n // Let mainTask finish\n await new Promise((r) => setTimeout(r, 50));\n fakeActivity._schedulingPaused = true;\n fakeActivity.q_updated = new Future();\n fakeActivity.q_updated.resolve();\n ac.abort();\n\n const result = await raceTimeout(mainTaskPromise, 2000);\n expect(result).toBe('resolved');\n });\n\n it('should skip handles that were interrupted before being popped', async () => {\n const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();\n\n const handle = SpeechHandle.create({ allowInterruptions: true });\n\n // Interrupt before mainTask ever sees it\n handle.interrupt();\n\n speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handle]);\n handle._markScheduled();\n q_updated.resolve();\n\n const ac = new AbortController();\n const mainTaskPromise = mainTask(ac.signal);\n\n await new Promise((r) => setTimeout(r, 50));\n fakeActivity._schedulingPaused = true;\n fakeActivity.q_updated = new Future();\n fakeActivity.q_updated.resolve();\n ac.abort();\n\n const result = await raceTimeout(mainTaskPromise, 2000);\n expect(result).toBe('resolved');\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Heap } from "heap-js";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { Future } from "../utils.js";
|
|
4
|
+
import { AgentActivity } from "./agent_activity.js";
|
|
5
|
+
import { SpeechHandle } from "./speech_handle.js";
|
|
6
|
+
vi.mock("./agent.js", () => {
|
|
7
|
+
class Agent {
|
|
8
|
+
}
|
|
9
|
+
class AgentTask extends Agent {
|
|
10
|
+
}
|
|
11
|
+
class StopResponse {
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
Agent,
|
|
15
|
+
AgentTask,
|
|
16
|
+
StopResponse,
|
|
17
|
+
_getActivityTaskInfo: () => null,
|
|
18
|
+
_setActivityTaskInfo: () => {
|
|
19
|
+
},
|
|
20
|
+
functionCallStorage: {
|
|
21
|
+
getStore: () => void 0,
|
|
22
|
+
enterWith: () => {
|
|
23
|
+
},
|
|
24
|
+
run: (_, fn) => fn()
|
|
25
|
+
},
|
|
26
|
+
speechHandleStorage: {
|
|
27
|
+
getStore: () => void 0,
|
|
28
|
+
enterWith: () => {
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
vi.mock("../version.js", () => ({ version: "0.0.0-test" }));
|
|
34
|
+
async function raceTimeout(promise, ms) {
|
|
35
|
+
let timer;
|
|
36
|
+
const timeout = new Promise((resolve) => {
|
|
37
|
+
timer = setTimeout(() => resolve("timeout"), ms);
|
|
38
|
+
});
|
|
39
|
+
return Promise.race([promise.then(() => "resolved"), timeout]).finally(
|
|
40
|
+
() => clearTimeout(timer)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
function buildMainTaskRunner() {
|
|
44
|
+
const q_updated = new Future();
|
|
45
|
+
const speechQueue = new Heap((a, b) => b[0] - a[0] || a[1] - b[1]);
|
|
46
|
+
const fakeActivity = {
|
|
47
|
+
q_updated,
|
|
48
|
+
speechQueue,
|
|
49
|
+
_currentSpeech: void 0,
|
|
50
|
+
_schedulingPaused: false,
|
|
51
|
+
getDrainPendingSpeechTasks: () => [],
|
|
52
|
+
logger: {
|
|
53
|
+
info: () => {
|
|
54
|
+
},
|
|
55
|
+
debug: () => {
|
|
56
|
+
},
|
|
57
|
+
warn: () => {
|
|
58
|
+
},
|
|
59
|
+
error: () => {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const mainTask = AgentActivity.prototype.mainTask;
|
|
64
|
+
return {
|
|
65
|
+
fakeActivity,
|
|
66
|
+
mainTask: mainTask.bind(fakeActivity),
|
|
67
|
+
speechQueue,
|
|
68
|
+
q_updated
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
describe("AgentActivity - mainTask", () => {
|
|
72
|
+
it("should recover when speech handle is interrupted after authorization", async () => {
|
|
73
|
+
const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();
|
|
74
|
+
const handle = SpeechHandle.create({ allowInterruptions: true });
|
|
75
|
+
speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handle]);
|
|
76
|
+
handle._markScheduled();
|
|
77
|
+
q_updated.resolve();
|
|
78
|
+
const ac = new AbortController();
|
|
79
|
+
const mainTaskPromise = mainTask(ac.signal);
|
|
80
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
81
|
+
handle.interrupt();
|
|
82
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
83
|
+
fakeActivity._schedulingPaused = true;
|
|
84
|
+
fakeActivity.q_updated = new Future();
|
|
85
|
+
fakeActivity.q_updated.resolve();
|
|
86
|
+
ac.abort();
|
|
87
|
+
const result = await raceTimeout(mainTaskPromise, 2e3);
|
|
88
|
+
expect(result).toBe("resolved");
|
|
89
|
+
});
|
|
90
|
+
it("should process next queued handle after an interrupted one", async () => {
|
|
91
|
+
const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();
|
|
92
|
+
const handleA = SpeechHandle.create({ allowInterruptions: true });
|
|
93
|
+
const handleB = SpeechHandle.create({ allowInterruptions: true });
|
|
94
|
+
speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handleA]);
|
|
95
|
+
handleA._markScheduled();
|
|
96
|
+
speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 2, handleB]);
|
|
97
|
+
handleB._markScheduled();
|
|
98
|
+
q_updated.resolve();
|
|
99
|
+
const ac = new AbortController();
|
|
100
|
+
const mainTaskPromise = mainTask(ac.signal);
|
|
101
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
102
|
+
handleA.interrupt();
|
|
103
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
104
|
+
try {
|
|
105
|
+
handleB._markGenerationDone();
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
109
|
+
fakeActivity._schedulingPaused = true;
|
|
110
|
+
fakeActivity.q_updated = new Future();
|
|
111
|
+
fakeActivity.q_updated.resolve();
|
|
112
|
+
ac.abort();
|
|
113
|
+
const result = await raceTimeout(mainTaskPromise, 2e3);
|
|
114
|
+
expect(result).toBe("resolved");
|
|
115
|
+
});
|
|
116
|
+
it("should skip handles that were interrupted before being popped", async () => {
|
|
117
|
+
const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();
|
|
118
|
+
const handle = SpeechHandle.create({ allowInterruptions: true });
|
|
119
|
+
handle.interrupt();
|
|
120
|
+
speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handle]);
|
|
121
|
+
handle._markScheduled();
|
|
122
|
+
q_updated.resolve();
|
|
123
|
+
const ac = new AbortController();
|
|
124
|
+
const mainTaskPromise = mainTask(ac.signal);
|
|
125
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
126
|
+
fakeActivity._schedulingPaused = true;
|
|
127
|
+
fakeActivity.q_updated = new Future();
|
|
128
|
+
fakeActivity.q_updated.resolve();
|
|
129
|
+
ac.abort();
|
|
130
|
+
const result = await raceTimeout(mainTaskPromise, 2e3);
|
|
131
|
+
expect(result).toBe("resolved");
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=agent_activity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/voice/agent_activity.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Regression tests for mainTask speech handle processing.\n *\n * When a speech handle is interrupted after _authorizeGeneration() but before the\n * reply task calls _markGenerationDone(), mainTask hangs on _waitForGeneration()\n * indefinitely. All subsequent speech handles queue behind it and the agent becomes\n * unresponsive.\n *\n * Fix: race _waitForGeneration() against the interrupt future via waitIfNotInterrupted().\n *\n * Related: #1124, #1089, #836\n */\nimport { Heap } from 'heap-js';\nimport { describe, expect, it, vi } from 'vitest';\nimport { Future } from '../utils.js';\nimport { AgentActivity } from './agent_activity.js';\nimport { SpeechHandle } from './speech_handle.js';\n\n// Break circular dependency: agent_activity.ts → agent.js → beta/workflows/task_group.ts\nvi.mock('./agent.js', () => {\n class Agent {}\n class AgentTask extends Agent {}\n class StopResponse {}\n return {\n Agent,\n AgentTask,\n StopResponse,\n _getActivityTaskInfo: () => null,\n _setActivityTaskInfo: () => {},\n functionCallStorage: {\n getStore: () => undefined,\n enterWith: () => {},\n run: (_: unknown, fn: () => unknown) => fn(),\n },\n speechHandleStorage: {\n getStore: () => undefined,\n enterWith: () => {},\n },\n };\n});\n\nvi.mock('../version.js', () => ({ version: '0.0.0-test' }));\n\nasync function raceTimeout(promise: Promise<unknown>, ms: number): Promise<'resolved' | 'timeout'> {\n let timer: ReturnType<typeof setTimeout>;\n const timeout = new Promise<'timeout'>((resolve) => {\n timer = setTimeout(() => resolve('timeout'), ms);\n });\n return Promise.race([promise.then(() => 'resolved' as const), timeout]).finally(() =>\n clearTimeout(timer),\n );\n}\n\n/**\n * Build a minimal stand-in with just enough state for mainTask to run.\n *\n * mainTask accesses: q_updated, speechQueue, _currentSpeech, _schedulingPaused,\n * getDrainPendingSpeechTasks(), and logger. We provide stubs for all of these,\n * then bind the real AgentActivity.prototype.mainTask to this object.\n */\nfunction buildMainTaskRunner() {\n const q_updated = new Future<void>();\n type HeapItem = [number, number, SpeechHandle];\n const speechQueue = new Heap<HeapItem>((a: HeapItem, b: HeapItem) => b[0] - a[0] || a[1] - b[1]);\n\n const fakeActivity = {\n q_updated,\n speechQueue,\n _currentSpeech: undefined as SpeechHandle | undefined,\n _schedulingPaused: false,\n getDrainPendingSpeechTasks: () => [],\n logger: {\n info: () => {},\n debug: () => {},\n warn: () => {},\n error: () => {},\n },\n };\n\n const mainTask = (AgentActivity.prototype as Record<string, unknown>).mainTask as (\n signal: AbortSignal,\n ) => Promise<void>;\n\n return {\n fakeActivity,\n mainTask: mainTask.bind(fakeActivity),\n speechQueue,\n q_updated,\n };\n}\n\ndescribe('AgentActivity - mainTask', () => {\n it('should recover when speech handle is interrupted after authorization', async () => {\n const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();\n\n const handle = SpeechHandle.create({ allowInterruptions: true });\n\n speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handle]);\n handle._markScheduled();\n q_updated.resolve();\n\n const ac = new AbortController();\n const mainTaskPromise = mainTask(ac.signal);\n\n // Give mainTask time to pop the handle and call _authorizeGeneration\n await new Promise((r) => setTimeout(r, 50));\n\n // Interrupt while waiting for generation\n handle.interrupt();\n\n // Let mainTask react to the interrupt, then signal exit\n await new Promise((r) => setTimeout(r, 50));\n fakeActivity._schedulingPaused = true;\n fakeActivity.q_updated = new Future();\n fakeActivity.q_updated.resolve();\n ac.abort();\n\n const result = await raceTimeout(mainTaskPromise, 2000);\n expect(result).toBe('resolved');\n });\n\n it('should process next queued handle after an interrupted one', async () => {\n const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();\n\n const handleA = SpeechHandle.create({ allowInterruptions: true });\n const handleB = SpeechHandle.create({ allowInterruptions: true });\n\n speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handleA]);\n handleA._markScheduled();\n speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 2, handleB]);\n handleB._markScheduled();\n q_updated.resolve();\n\n const ac = new AbortController();\n const mainTaskPromise = mainTask(ac.signal);\n\n // Wait for mainTask to pick up handle A\n await new Promise((r) => setTimeout(r, 50));\n\n // Interrupt handle A\n handleA.interrupt();\n\n // Wait for mainTask to move to handle B and authorize it\n await new Promise((r) => setTimeout(r, 50));\n\n // Resolve handle B's generation (simulating normal reply task completion).\n // If mainTask is stuck on handle A (bug), handle B was never authorized and this\n // throws — we catch it and let the timeout assert the real failure.\n try {\n handleB._markGenerationDone();\n } catch {\n // Expected when fix is absent: handle B has no active generation\n }\n\n // Let mainTask finish\n await new Promise((r) => setTimeout(r, 50));\n fakeActivity._schedulingPaused = true;\n fakeActivity.q_updated = new Future();\n fakeActivity.q_updated.resolve();\n ac.abort();\n\n const result = await raceTimeout(mainTaskPromise, 2000);\n expect(result).toBe('resolved');\n });\n\n it('should skip handles that were interrupted before being popped', async () => {\n const { fakeActivity, mainTask, speechQueue, q_updated } = buildMainTaskRunner();\n\n const handle = SpeechHandle.create({ allowInterruptions: true });\n\n // Interrupt before mainTask ever sees it\n handle.interrupt();\n\n speechQueue.push([SpeechHandle.SPEECH_PRIORITY_NORMAL, 1, handle]);\n handle._markScheduled();\n q_updated.resolve();\n\n const ac = new AbortController();\n const mainTaskPromise = mainTask(ac.signal);\n\n await new Promise((r) => setTimeout(r, 50));\n fakeActivity._schedulingPaused = true;\n fakeActivity.q_updated = new Future();\n fakeActivity.q_updated.resolve();\n ac.abort();\n\n const result = await raceTimeout(mainTaskPromise, 2000);\n expect(result).toBe('resolved');\n });\n});\n"],"mappings":"AAgBA,SAAS,YAAY;AACrB,SAAS,UAAU,QAAQ,IAAI,UAAU;AACzC,SAAS,cAAc;AACvB,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAG7B,GAAG,KAAK,cAAc,MAAM;AAAA,EAC1B,MAAM,MAAM;AAAA,EAAC;AAAA,EACb,MAAM,kBAAkB,MAAM;AAAA,EAAC;AAAA,EAC/B,MAAM,aAAa;AAAA,EAAC;AACpB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,sBAAsB,MAAM;AAAA,IAC5B,sBAAsB,MAAM;AAAA,IAAC;AAAA,IAC7B,qBAAqB;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MAAC;AAAA,MAClB,KAAK,CAAC,GAAY,OAAsB,GAAG;AAAA,IAC7C;AAAA,IACA,qBAAqB;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MAAC;AAAA,IACpB;AAAA,EACF;AACF,CAAC;AAED,GAAG,KAAK,iBAAiB,OAAO,EAAE,SAAS,aAAa,EAAE;AAE1D,eAAe,YAAY,SAA2B,IAA6C;AACjG,MAAI;AACJ,QAAM,UAAU,IAAI,QAAmB,CAAC,YAAY;AAClD,YAAQ,WAAW,MAAM,QAAQ,SAAS,GAAG,EAAE;AAAA,EACjD,CAAC;AACD,SAAO,QAAQ,KAAK,CAAC,QAAQ,KAAK,MAAM,UAAmB,GAAG,OAAO,CAAC,EAAE;AAAA,IAAQ,MAC9E,aAAa,KAAK;AAAA,EACpB;AACF;AASA,SAAS,sBAAsB;AAC7B,QAAM,YAAY,IAAI,OAAa;AAEnC,QAAM,cAAc,IAAI,KAAe,CAAC,GAAa,MAAgB,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAE/F,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,4BAA4B,MAAM,CAAC;AAAA,IACnC,QAAQ;AAAA,MACN,MAAM,MAAM;AAAA,MAAC;AAAA,MACb,OAAO,MAAM;AAAA,MAAC;AAAA,MACd,MAAM,MAAM;AAAA,MAAC;AAAA,MACb,OAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,WAAY,cAAc,UAAsC;AAItE,SAAO;AAAA,IACL;AAAA,IACA,UAAU,SAAS,KAAK,YAAY;AAAA,IACpC;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,4BAA4B,MAAM;AACzC,KAAG,wEAAwE,YAAY;AACrF,UAAM,EAAE,cAAc,UAAU,aAAa,UAAU,IAAI,oBAAoB;AAE/E,UAAM,SAAS,aAAa,OAAO,EAAE,oBAAoB,KAAK,CAAC;AAE/D,gBAAY,KAAK,CAAC,aAAa,wBAAwB,GAAG,MAAM,CAAC;AACjE,WAAO,eAAe;AACtB,cAAU,QAAQ;AAElB,UAAM,KAAK,IAAI,gBAAgB;AAC/B,UAAM,kBAAkB,SAAS,GAAG,MAAM;AAG1C,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAG1C,WAAO,UAAU;AAGjB,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC1C,iBAAa,oBAAoB;AACjC,iBAAa,YAAY,IAAI,OAAO;AACpC,iBAAa,UAAU,QAAQ;AAC/B,OAAG,MAAM;AAET,UAAM,SAAS,MAAM,YAAY,iBAAiB,GAAI;AACtD,WAAO,MAAM,EAAE,KAAK,UAAU;AAAA,EAChC,CAAC;AAED,KAAG,8DAA8D,YAAY;AAC3E,UAAM,EAAE,cAAc,UAAU,aAAa,UAAU,IAAI,oBAAoB;AAE/E,UAAM,UAAU,aAAa,OAAO,EAAE,oBAAoB,KAAK,CAAC;AAChE,UAAM,UAAU,aAAa,OAAO,EAAE,oBAAoB,KAAK,CAAC;AAEhE,gBAAY,KAAK,CAAC,aAAa,wBAAwB,GAAG,OAAO,CAAC;AAClE,YAAQ,eAAe;AACvB,gBAAY,KAAK,CAAC,aAAa,wBAAwB,GAAG,OAAO,CAAC;AAClE,YAAQ,eAAe;AACvB,cAAU,QAAQ;AAElB,UAAM,KAAK,IAAI,gBAAgB;AAC/B,UAAM,kBAAkB,SAAS,GAAG,MAAM;AAG1C,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAG1C,YAAQ,UAAU;AAGlB,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAK1C,QAAI;AACF,cAAQ,oBAAoB;AAAA,IAC9B,QAAQ;AAAA,IAER;AAGA,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC1C,iBAAa,oBAAoB;AACjC,iBAAa,YAAY,IAAI,OAAO;AACpC,iBAAa,UAAU,QAAQ;AAC/B,OAAG,MAAM;AAET,UAAM,SAAS,MAAM,YAAY,iBAAiB,GAAI;AACtD,WAAO,MAAM,EAAE,KAAK,UAAU;AAAA,EAChC,CAAC;AAED,KAAG,iEAAiE,YAAY;AAC9E,UAAM,EAAE,cAAc,UAAU,aAAa,UAAU,IAAI,oBAAoB;AAE/E,UAAM,SAAS,aAAa,OAAO,EAAE,oBAAoB,KAAK,CAAC;AAG/D,WAAO,UAAU;AAEjB,gBAAY,KAAK,CAAC,aAAa,wBAAwB,GAAG,MAAM,CAAC;AACjE,WAAO,eAAe;AACtB,cAAU,QAAQ;AAElB,UAAM,KAAK,IAAI,gBAAgB;AAC/B,UAAM,kBAAkB,SAAS,GAAG,MAAM;AAE1C,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC1C,iBAAa,oBAAoB;AACjC,iBAAa,YAAY,IAAI,OAAO;AACpC,iBAAa,UAAU,QAAQ;AAC/B,OAAG,MAAM;AAET,UAAM,SAAS,MAAM,YAAY,iBAAiB,GAAI;AACtD,WAAO,MAAM,EAAE,KAAK,UAAU;AAAA,EAChC,CAAC;AACH,CAAC;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/voice/audio_recognition.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ParticipantKind } from '@livekit/rtc-node';\nimport { AudioFrame } from '@livekit/rtc-node';\nimport {\n type Context,\n ROOT_CONTEXT,\n type Span,\n context as otelContext,\n trace,\n} from '@opentelemetry/api';\nimport type { WritableStreamDefaultWriter } from 'node:stream/web';\nimport { ReadableStream } from 'node:stream/web';\nimport { type ChatContext } from '../llm/chat_context.js';\nimport { log } from '../log.js';\nimport { DeferredReadableStream, isStreamReaderReleaseError } from '../stream/deferred_stream.js';\nimport { IdentityTransform } from '../stream/identity_transform.js';\nimport { mergeReadableStreams } from '../stream/merge_readable_streams.js';\nimport { type SpeechEvent, SpeechEventType } from '../stt/stt.js';\nimport { traceTypes, tracer } from '../telemetry/index.js';\nimport { Task, delay } from '../utils.js';\nimport { type VAD, type VADEvent, VADEventType } from '../vad.js';\nimport type { TurnDetectionMode } from './agent_session.js';\nimport type { STTNode } from './io.js';\nimport { setParticipantSpanAttributes } from './utils.js';\n\nexport interface EndOfTurnInfo {\n /** The new transcript text from the user's speech. */\n newTranscript: string;\n /** Confidence score of the transcript (0-1). */\n transcriptConfidence: number;\n /** Delay from speech stop to final transcription in milliseconds. */\n transcriptionDelay: number;\n /** Delay from speech stop to end of utterance detection in milliseconds. */\n endOfUtteranceDelay: number;\n /** Timestamp when user started speaking (milliseconds since epoch). */\n startedSpeakingAt: number | undefined;\n /** Timestamp when user stopped speaking (milliseconds since epoch). */\n stoppedSpeakingAt: number | undefined;\n}\n\nexport interface PreemptiveGenerationInfo {\n newTranscript: string;\n transcriptConfidence: number;\n}\n\nexport interface RecognitionHooks {\n onStartOfSpeech: (ev: VADEvent) => void;\n onVADInferenceDone: (ev: VADEvent) => void;\n onEndOfSpeech: (ev: VADEvent) => void;\n onInterimTranscript: (ev: SpeechEvent) => void;\n onFinalTranscript: (ev: SpeechEvent) => void;\n onEndOfTurn: (info: EndOfTurnInfo) => Promise<boolean>;\n onPreemptiveGeneration: (info: PreemptiveGenerationInfo) => void;\n\n retrieveChatCtx: () => ChatContext;\n}\n\nexport interface _TurnDetector {\n unlikelyThreshold: (language?: string) => Promise<number | undefined>;\n supportsLanguage: (language?: string) => Promise<boolean>;\n predictEndOfTurn(chatCtx: ChatContext): Promise<number>;\n}\n\nexport interface AudioRecognitionOptions {\n /** Hooks for recognition events. */\n recognitionHooks: RecognitionHooks;\n /** Speech-to-text node. */\n stt?: STTNode;\n /** Voice activity detection. */\n vad?: VAD;\n /** Turn detector for end-of-turn prediction. */\n turnDetector?: _TurnDetector;\n /** Turn detection mode. */\n turnDetectionMode?: Exclude<TurnDetectionMode, _TurnDetector>;\n /** Minimum endpointing delay in milliseconds. */\n minEndpointingDelay: number;\n /** Maximum endpointing delay in milliseconds. */\n maxEndpointingDelay: number;\n /** Root span context for tracing. */\n rootSpanContext?: Context;\n /** STT model name for tracing */\n sttModel?: string;\n /** STT provider name for tracing */\n sttProvider?: string;\n /** Getter for linked participant for span attribution */\n getLinkedParticipant?: () => ParticipantLike | undefined;\n}\n\n/**\n * Minimal participant shape for span attribution.\n * Compatible with both `LocalParticipant` and `RemoteParticipant` from `@livekit/rtc-node`.\n */\nexport interface ParticipantLike {\n sid: string | undefined;\n identity: string;\n kind: ParticipantKind;\n}\n\nexport class AudioRecognition {\n private hooks: RecognitionHooks;\n private stt?: STTNode;\n private vad?: VAD;\n private turnDetector?: _TurnDetector;\n private turnDetectionMode?: Exclude<TurnDetectionMode, _TurnDetector>;\n private minEndpointingDelay: number;\n private maxEndpointingDelay: number;\n private lastLanguage?: string;\n private rootSpanContext?: Context;\n private sttModel?: string;\n private sttProvider?: string;\n private getLinkedParticipant?: () => ParticipantLike | undefined;\n\n private deferredInputStream: DeferredReadableStream<AudioFrame>;\n private logger = log();\n private lastFinalTranscriptTime = 0;\n private audioTranscript = '';\n private audioInterimTranscript = '';\n private audioPreflightTranscript = '';\n private finalTranscriptConfidence: number[] = [];\n private lastSpeakingTime: number | undefined;\n private speechStartTime: number | undefined;\n private userTurnCommitted = false;\n private speaking = false;\n private sampleRate?: number;\n\n private userTurnSpan?: Span;\n\n private vadInputStream: ReadableStream<AudioFrame>;\n private sttInputStream: ReadableStream<AudioFrame>;\n private silenceAudioTransform = new IdentityTransform<AudioFrame>();\n private silenceAudioWriter: WritableStreamDefaultWriter<AudioFrame>;\n\n // all cancellable tasks\n private bounceEOUTask?: Task<void>;\n private commitUserTurnTask?: Task<void>;\n private vadTask?: Task<void>;\n private sttTask?: Task<void>;\n\n constructor(opts: AudioRecognitionOptions) {\n this.hooks = opts.recognitionHooks;\n this.stt = opts.stt;\n this.vad = opts.vad;\n this.turnDetector = opts.turnDetector;\n this.turnDetectionMode = opts.turnDetectionMode;\n this.minEndpointingDelay = opts.minEndpointingDelay;\n this.maxEndpointingDelay = opts.maxEndpointingDelay;\n this.lastLanguage = undefined;\n this.rootSpanContext = opts.rootSpanContext;\n this.sttModel = opts.sttModel;\n this.sttProvider = opts.sttProvider;\n this.getLinkedParticipant = opts.getLinkedParticipant;\n\n this.deferredInputStream = new DeferredReadableStream<AudioFrame>();\n const [vadInputStream, sttInputStream] = this.deferredInputStream.stream.tee();\n this.vadInputStream = vadInputStream;\n this.sttInputStream = mergeReadableStreams(sttInputStream, this.silenceAudioTransform.readable);\n this.silenceAudioWriter = this.silenceAudioTransform.writable.getWriter();\n }\n\n /**\n * Current transcript of the user's speech, including interim transcript if available.\n */\n get currentTranscript(): string {\n if (this.audioInterimTranscript) {\n return `${this.audioTranscript} ${this.audioInterimTranscript}`.trim();\n }\n return this.audioTranscript;\n }\n\n async start() {\n this.vadTask = Task.from(({ signal }) => this.createVadTask(this.vad, signal));\n this.vadTask.result.catch((err) => {\n this.logger.error(`Error running VAD task: ${err}`);\n });\n\n this.sttTask = Task.from(({ signal }) => this.createSttTask(this.stt, signal));\n this.sttTask.result.catch((err) => {\n this.logger.error(`Error running STT task: ${err}`);\n });\n }\n\n private ensureUserTurnSpan(startTime?: number): Span {\n if (this.userTurnSpan && this.userTurnSpan.isRecording()) {\n return this.userTurnSpan;\n }\n\n this.userTurnSpan = tracer.startSpan({\n name: 'user_turn',\n context: this.rootSpanContext,\n startTime,\n });\n\n const participant = this.getLinkedParticipant?.();\n if (participant) {\n setParticipantSpanAttributes(this.userTurnSpan, participant);\n }\n\n if (this.sttModel) {\n this.userTurnSpan.setAttribute(traceTypes.ATTR_GEN_AI_REQUEST_MODEL, this.sttModel);\n }\n if (this.sttProvider) {\n this.userTurnSpan.setAttribute(traceTypes.ATTR_GEN_AI_PROVIDER_NAME, this.sttProvider);\n }\n\n return this.userTurnSpan;\n }\n\n private userTurnContext(span: Span): Context {\n const base = this.rootSpanContext ?? ROOT_CONTEXT;\n return trace.setSpan(base, span);\n }\n\n private async onSTTEvent(ev: SpeechEvent) {\n if (\n this.turnDetectionMode === 'manual' &&\n this.userTurnCommitted &&\n (this.bounceEOUTask === undefined ||\n this.bounceEOUTask.done ||\n ev.type == SpeechEventType.INTERIM_TRANSCRIPT)\n ) {\n // ignore stt event if user turn already committed and EOU task is done\n // or it's an interim transcript\n this.logger.debug(\n {\n userTurnCommitted: this.userTurnCommitted,\n eouTaskDone: this.bounceEOUTask?.done,\n evType: ev.type,\n turnDetectionMode: this.turnDetectionMode,\n },\n 'ignoring stt event',\n );\n return;\n }\n\n switch (ev.type) {\n case SpeechEventType.FINAL_TRANSCRIPT:\n const transcript = ev.alternatives?.[0]?.text;\n const confidence = ev.alternatives?.[0]?.confidence ?? 0;\n this.lastLanguage = ev.alternatives?.[0]?.language;\n\n if (!transcript) {\n // stt final transcript received but no transcript\n return;\n }\n\n this.hooks.onFinalTranscript(ev);\n\n this.logger.debug(\n {\n user_transcript: transcript,\n language: this.lastLanguage,\n },\n 'received user transcript',\n );\n\n this.lastFinalTranscriptTime = Date.now();\n this.audioTranscript += ` ${transcript}`;\n this.audioTranscript = this.audioTranscript.trimStart();\n this.finalTranscriptConfidence.push(confidence);\n const transcriptChanged = this.audioTranscript !== this.audioPreflightTranscript;\n this.audioInterimTranscript = '';\n this.audioPreflightTranscript = '';\n\n if (!this.vad || this.lastSpeakingTime === undefined) {\n // vad disabled, use stt timestamp\n // TODO: this would screw up transcription latency metrics\n // but we'll live with it for now.\n // the correct way is to ensure STT fires SpeechEventType.END_OF_SPEECH\n // and using that timestamp for lastSpeakingTime\n this.lastSpeakingTime = Date.now();\n }\n\n if (this.vadBaseTurnDetection || this.userTurnCommitted) {\n if (transcriptChanged) {\n this.logger.debug(\n { transcript: this.audioTranscript },\n 'triggering preemptive generation (FINAL_TRANSCRIPT)',\n );\n this.hooks.onPreemptiveGeneration({\n newTranscript: this.audioTranscript,\n transcriptConfidence:\n this.finalTranscriptConfidence.length > 0\n ? this.finalTranscriptConfidence.reduce((a, b) => a + b, 0) /\n this.finalTranscriptConfidence.length\n : 0,\n });\n }\n\n if (!this.speaking) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on stt FINAL_TRANSCRIPT');\n this.runEOUDetection(chatCtx);\n }\n }\n break;\n case SpeechEventType.PREFLIGHT_TRANSCRIPT:\n this.hooks.onInterimTranscript(ev);\n const preflightTranscript = ev.alternatives?.[0]?.text ?? '';\n const preflightConfidence = ev.alternatives?.[0]?.confidence ?? 0;\n const preflightLanguage = ev.alternatives?.[0]?.language;\n\n const MIN_LANGUAGE_DETECTION_LENGTH = 5;\n if (\n !this.lastLanguage ||\n (preflightLanguage && preflightTranscript.length > MIN_LANGUAGE_DETECTION_LENGTH)\n ) {\n this.lastLanguage = preflightLanguage;\n }\n\n if (!preflightTranscript) {\n return;\n }\n\n this.logger.debug(\n {\n user_transcript: preflightTranscript,\n language: this.lastLanguage,\n },\n 'received user preflight transcript',\n );\n\n // still need to increment it as it's used for turn detection,\n this.lastFinalTranscriptTime = Date.now();\n // preflight transcript includes all pre-committed transcripts (including final transcript from the previous STT run)\n this.audioPreflightTranscript =\n `${this.audioTranscript} ${preflightTranscript}`.trimStart();\n this.audioInterimTranscript = preflightTranscript;\n\n if (!this.vad || this.lastSpeakingTime === undefined) {\n // vad disabled, use stt timestamp\n this.lastSpeakingTime = Date.now();\n }\n\n if (this.turnDetectionMode !== 'manual' || this.userTurnCommitted) {\n const confidenceVals = [...this.finalTranscriptConfidence, preflightConfidence];\n this.logger.debug(\n {\n transcript:\n this.audioPreflightTranscript.length > 100\n ? this.audioPreflightTranscript.slice(0, 100) + '...'\n : this.audioPreflightTranscript,\n },\n 'triggering preemptive generation (PREFLIGHT_TRANSCRIPT)',\n );\n this.hooks.onPreemptiveGeneration({\n newTranscript: this.audioPreflightTranscript,\n transcriptConfidence:\n confidenceVals.length > 0\n ? confidenceVals.reduce((a, b) => a + b, 0) / confidenceVals.length\n : 0,\n });\n }\n break;\n case SpeechEventType.INTERIM_TRANSCRIPT:\n this.logger.debug({ transcript: ev.alternatives?.[0]?.text }, 'interim transcript');\n this.hooks.onInterimTranscript(ev);\n this.audioInterimTranscript = ev.alternatives?.[0]?.text ?? '';\n break;\n case SpeechEventType.START_OF_SPEECH:\n if (this.turnDetectionMode !== 'stt') break;\n {\n const span = this.ensureUserTurnSpan(Date.now());\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => {\n this.hooks.onStartOfSpeech({\n type: VADEventType.START_OF_SPEECH,\n samplesIndex: 0,\n timestamp: Date.now(),\n speechDuration: 0,\n silenceDuration: 0,\n frames: [],\n probability: 0,\n inferenceDuration: 0,\n speaking: true,\n rawAccumulatedSilence: 0,\n rawAccumulatedSpeech: 0,\n });\n });\n }\n this.speaking = true;\n this.lastSpeakingTime = Date.now();\n\n this.bounceEOUTask?.cancel();\n break;\n case SpeechEventType.END_OF_SPEECH:\n if (this.turnDetectionMode !== 'stt') break;\n {\n const span = this.ensureUserTurnSpan();\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => {\n this.hooks.onEndOfSpeech({\n type: VADEventType.END_OF_SPEECH,\n samplesIndex: 0,\n timestamp: Date.now(),\n speechDuration: 0,\n silenceDuration: 0,\n frames: [],\n probability: 0,\n inferenceDuration: 0,\n speaking: false,\n rawAccumulatedSilence: 0,\n rawAccumulatedSpeech: 0,\n });\n });\n }\n this.speaking = false;\n this.userTurnCommitted = true;\n this.lastSpeakingTime = Date.now();\n\n if (!this.speaking) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on stt END_OF_SPEECH');\n this.runEOUDetection(chatCtx);\n }\n }\n }\n\n private runEOUDetection(chatCtx: ChatContext) {\n this.logger.debug(\n {\n stt: this.stt,\n audioTranscript: this.audioTranscript,\n turnDetectionMode: this.turnDetectionMode,\n },\n 'running EOU detection',\n );\n\n if (this.stt && !this.audioTranscript && this.turnDetectionMode !== 'manual') {\n // stt enabled but no transcript yet\n this.logger.debug('skipping EOU detection');\n return;\n }\n\n chatCtx = chatCtx.copy();\n chatCtx.addMessage({ role: 'user', content: this.audioTranscript });\n\n const turnDetector =\n // disable EOU model if manual turn detection enabled\n this.audioTranscript && this.turnDetectionMode !== 'manual' ? this.turnDetector : undefined;\n\n const bounceEOUTask =\n (\n lastSpeakingTime: number | undefined,\n lastFinalTranscriptTime: number,\n speechStartTime: number | undefined,\n ) =>\n async (controller: AbortController) => {\n let endpointingDelay = this.minEndpointingDelay;\n\n const userTurnSpan = this.ensureUserTurnSpan();\n const userTurnCtx = this.userTurnContext(userTurnSpan);\n\n if (turnDetector) {\n await tracer.startActiveSpan(\n async (span) => {\n this.logger.debug('Running turn detector model');\n\n let endOfTurnProbability = 0.0;\n let unlikelyThreshold: number | undefined;\n\n if (!(await turnDetector.supportsLanguage(this.lastLanguage))) {\n this.logger.debug(`Turn detector does not support language ${this.lastLanguage}`);\n } else {\n try {\n endOfTurnProbability = await turnDetector.predictEndOfTurn(chatCtx);\n unlikelyThreshold = await turnDetector.unlikelyThreshold(this.lastLanguage);\n\n this.logger.debug(\n { endOfTurnProbability, unlikelyThreshold, language: this.lastLanguage },\n 'end of turn probability',\n );\n\n if (unlikelyThreshold && endOfTurnProbability < unlikelyThreshold) {\n endpointingDelay = this.maxEndpointingDelay;\n }\n } catch (error) {\n this.logger.error(error, 'Error predicting end of turn');\n }\n }\n\n span.setAttribute(\n traceTypes.ATTR_CHAT_CTX,\n JSON.stringify(chatCtx.toJSON({ excludeTimestamp: false })),\n );\n span.setAttribute(traceTypes.ATTR_EOU_PROBABILITY, endOfTurnProbability);\n span.setAttribute(traceTypes.ATTR_EOU_UNLIKELY_THRESHOLD, unlikelyThreshold ?? 0);\n span.setAttribute(traceTypes.ATTR_EOU_DELAY, endpointingDelay);\n span.setAttribute(traceTypes.ATTR_EOU_LANGUAGE, this.lastLanguage ?? '');\n },\n {\n name: 'eou_detection',\n context: userTurnCtx,\n },\n );\n }\n\n let extraSleep = endpointingDelay;\n if (lastSpeakingTime !== undefined) {\n extraSleep += lastSpeakingTime - Date.now();\n }\n\n if (extraSleep > 0) {\n // add delay to see if there's a potential upcoming EOU task that cancels this one\n await delay(Math.max(extraSleep, 0), { signal: controller.signal });\n }\n\n this.logger.debug({ transcript: this.audioTranscript }, 'end of user turn');\n\n const confidenceAvg =\n this.finalTranscriptConfidence.length > 0\n ? this.finalTranscriptConfidence.reduce((a, b) => a + b, 0) /\n this.finalTranscriptConfidence.length\n : 0;\n\n let startedSpeakingAt: number | undefined;\n let stoppedSpeakingAt: number | undefined;\n let transcriptionDelay: number | undefined;\n let endOfUtteranceDelay: number | undefined;\n\n // sometimes, we can't calculate the metrics because VAD was unreliable.\n // in this case, we just ignore the calculation, it's better than providing likely wrong values\n if (\n lastFinalTranscriptTime !== 0 &&\n lastSpeakingTime !== undefined &&\n speechStartTime !== undefined\n ) {\n startedSpeakingAt = speechStartTime;\n stoppedSpeakingAt = lastSpeakingTime;\n transcriptionDelay = Math.max(lastFinalTranscriptTime - lastSpeakingTime, 0);\n endOfUtteranceDelay = Date.now() - lastSpeakingTime;\n }\n\n const committed = await this.hooks.onEndOfTurn({\n newTranscript: this.audioTranscript,\n transcriptConfidence: confidenceAvg,\n transcriptionDelay: transcriptionDelay ?? 0,\n endOfUtteranceDelay: endOfUtteranceDelay ?? 0,\n startedSpeakingAt,\n stoppedSpeakingAt,\n });\n\n if (committed) {\n this._endUserTurnSpan({\n transcript: this.audioTranscript,\n confidence: confidenceAvg,\n transcriptionDelay: transcriptionDelay ?? 0,\n endOfUtteranceDelay: endOfUtteranceDelay ?? 0,\n });\n\n // clear the transcript if the user turn was committed\n this.audioTranscript = '';\n this.finalTranscriptConfidence = [];\n this.lastSpeakingTime = undefined;\n this.lastFinalTranscriptTime = 0;\n this.speechStartTime = undefined;\n }\n\n this.userTurnCommitted = false;\n };\n\n // cancel any existing EOU task\n this.bounceEOUTask?.cancel();\n // copy the values before awaiting (the values can change)\n this.bounceEOUTask = Task.from(\n bounceEOUTask(this.lastSpeakingTime, this.lastFinalTranscriptTime, this.speechStartTime),\n );\n\n this.bounceEOUTask.result\n .then(() => {\n this.logger.debug('EOU detection task completed');\n })\n .catch((err: unknown) => {\n if (err instanceof Error && err.message.includes('This operation was aborted')) {\n // ignore aborted errors\n return;\n }\n this.logger.error(err, 'Error in EOU detection task:');\n });\n }\n\n private async createSttTask(stt: STTNode | undefined, signal: AbortSignal) {\n if (!stt) return;\n\n this.logger.debug('createSttTask: create stt stream from stt node');\n\n const sttStream = await stt(this.sttInputStream, {});\n\n if (signal.aborted || sttStream === null) return;\n\n if (sttStream instanceof ReadableStream) {\n const reader = sttStream.getReader();\n\n signal.addEventListener('abort', async () => {\n try {\n reader.releaseLock();\n await sttStream?.cancel();\n } catch (e) {\n this.logger.debug('createSttTask: error during abort handler:', e);\n }\n });\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value: ev } = await reader.read();\n if (done) break;\n\n if (typeof ev === 'string') {\n throw new Error('STT node must yield SpeechEvent');\n } else {\n await this.onSTTEvent(ev);\n }\n }\n } catch (e) {\n if (isStreamReaderReleaseError(e)) {\n return;\n }\n this.logger.error({ error: e }, 'createSttTask: error reading sttStream');\n } finally {\n reader.releaseLock();\n try {\n await sttStream.cancel();\n } catch (e) {\n this.logger.debug(\n 'createSttTask: error cancelling sttStream (may already be cancelled):',\n e,\n );\n }\n }\n }\n }\n\n private async createVadTask(vad: VAD | undefined, signal: AbortSignal) {\n if (!vad) return;\n\n const vadStream = vad.stream();\n vadStream.updateInputStream(this.vadInputStream);\n\n const abortHandler = () => {\n vadStream.detachInputStream();\n vadStream.close();\n signal.removeEventListener('abort', abortHandler);\n };\n signal.addEventListener('abort', abortHandler);\n\n try {\n for await (const ev of vadStream) {\n if (signal.aborted) break;\n\n switch (ev.type) {\n case VADEventType.START_OF_SPEECH:\n this.logger.debug('VAD task: START_OF_SPEECH');\n {\n const startTime = Date.now() - ev.speechDuration;\n const span = this.ensureUserTurnSpan(startTime);\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => this.hooks.onStartOfSpeech(ev));\n }\n this.speaking = true;\n\n // Capture sample rate from the first VAD event if not already set\n if (ev.frames.length > 0 && ev.frames[0]) {\n this.sampleRate = ev.frames[0].sampleRate;\n }\n\n this.bounceEOUTask?.cancel();\n break;\n case VADEventType.INFERENCE_DONE:\n this.hooks.onVADInferenceDone(ev);\n // for metrics, get the \"earliest\" signal of speech as possible\n if (ev.rawAccumulatedSpeech > 0.0) {\n this.lastSpeakingTime = Date.now();\n\n if (this.speechStartTime === undefined) {\n this.speechStartTime = Date.now();\n }\n }\n break;\n case VADEventType.END_OF_SPEECH:\n this.logger.debug('VAD task: END_OF_SPEECH');\n {\n const span = this.ensureUserTurnSpan();\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => this.hooks.onEndOfSpeech(ev));\n }\n\n // when VAD fires END_OF_SPEECH, it already waited for the silence_duration\n this.speaking = false;\n\n if (\n this.vadBaseTurnDetection ||\n (this.turnDetectionMode === 'stt' && this.userTurnCommitted)\n ) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.runEOUDetection(chatCtx);\n }\n break;\n }\n }\n } catch (e) {\n this.logger.error(e, 'Error in VAD task');\n } finally {\n this.logger.debug('VAD task closed');\n }\n }\n\n setInputAudioStream(audioStream: ReadableStream<AudioFrame>) {\n this.deferredInputStream.setSource(audioStream);\n }\n\n detachInputAudioStream() {\n this.deferredInputStream.detachSource();\n }\n\n clearUserTurn() {\n this.audioTranscript = '';\n this.audioInterimTranscript = '';\n this.audioPreflightTranscript = '';\n this.finalTranscriptConfidence = [];\n this.userTurnCommitted = false;\n\n this.sttTask?.cancelAndWait().finally(() => {\n this.sttTask = Task.from(({ signal }) => this.createSttTask(this.stt, signal));\n this.sttTask.result.catch((err) => {\n this.logger.error(`Error running STT task: ${err}`);\n });\n });\n }\n\n commitUserTurn(audioDetached: boolean) {\n const commitUserTurnTask =\n (delayDuration: number = 500) =>\n async (controller: AbortController) => {\n if (Date.now() - this.lastFinalTranscriptTime > delayDuration) {\n // flush the stt by pushing silence\n if (audioDetached && this.sampleRate !== undefined) {\n const numSamples = Math.floor(this.sampleRate * 0.5);\n const silence = new Int16Array(numSamples * 2);\n const silenceFrame = new AudioFrame(silence, this.sampleRate, 1, numSamples);\n this.silenceAudioWriter.write(silenceFrame);\n }\n\n // wait for the final transcript to be available\n await delay(delayDuration, { signal: controller.signal });\n }\n\n if (this.audioInterimTranscript) {\n // append interim transcript in case the final transcript is not ready\n this.audioTranscript = `${this.audioTranscript} ${this.audioInterimTranscript}`.trim();\n }\n this.audioInterimTranscript = '';\n\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on commitUserTurn');\n this.runEOUDetection(chatCtx);\n this.userTurnCommitted = true;\n };\n\n // cancel any existing commit user turn task\n this.commitUserTurnTask?.cancel();\n this.commitUserTurnTask = Task.from(commitUserTurnTask());\n\n this.commitUserTurnTask.result\n .then(() => {\n this.logger.debug('User turn committed');\n })\n .catch((err: unknown) => {\n if (err instanceof Error && err.name === 'AbortError') {\n this.logger.debug('User turn commit task cancelled');\n return;\n }\n this.logger.error(err, 'Error in user turn commit task:');\n });\n }\n\n async close() {\n this.detachInputAudioStream();\n this.silenceAudioWriter.releaseLock();\n await this.commitUserTurnTask?.cancelAndWait();\n await this.sttTask?.cancelAndWait();\n await this.vadTask?.cancelAndWait();\n await this.bounceEOUTask?.cancelAndWait();\n }\n\n private _endUserTurnSpan({\n transcript,\n confidence,\n transcriptionDelay,\n endOfUtteranceDelay,\n }: {\n transcript: string;\n confidence: number;\n transcriptionDelay: number;\n endOfUtteranceDelay: number;\n }): void {\n if (this.userTurnSpan) {\n this.userTurnSpan.setAttributes({\n [traceTypes.ATTR_USER_TRANSCRIPT]: transcript,\n [traceTypes.ATTR_TRANSCRIPT_CONFIDENCE]: confidence,\n [traceTypes.ATTR_TRANSCRIPTION_DELAY]: transcriptionDelay,\n [traceTypes.ATTR_END_OF_TURN_DELAY]: endOfUtteranceDelay,\n });\n this.userTurnSpan.end();\n this.userTurnSpan = undefined;\n }\n }\n\n private get vadBaseTurnDetection() {\n return ['vad', undefined].includes(this.turnDetectionMode);\n }\n}\n"],"mappings":"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","names":["import_utils","otelContext"]}
|
|
1
|
+
{"version":3,"sources":["../../src/voice/audio_recognition.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ParticipantKind } from '@livekit/rtc-node';\nimport { AudioFrame } from '@livekit/rtc-node';\nimport {\n type Context,\n ROOT_CONTEXT,\n type Span,\n context as otelContext,\n trace,\n} from '@opentelemetry/api';\nimport type { WritableStreamDefaultWriter } from 'node:stream/web';\nimport { ReadableStream } from 'node:stream/web';\nimport type { LanguageCode } from '../language.js';\nimport { type ChatContext } from '../llm/chat_context.js';\nimport { log } from '../log.js';\nimport { DeferredReadableStream, isStreamReaderReleaseError } from '../stream/deferred_stream.js';\nimport { IdentityTransform } from '../stream/identity_transform.js';\nimport { mergeReadableStreams } from '../stream/merge_readable_streams.js';\nimport { type SpeechEvent, SpeechEventType } from '../stt/stt.js';\nimport { traceTypes, tracer } from '../telemetry/index.js';\nimport { Task, delay } from '../utils.js';\nimport { type VAD, type VADEvent, VADEventType } from '../vad.js';\nimport type { TurnDetectionMode } from './agent_session.js';\nimport type { STTNode } from './io.js';\nimport { setParticipantSpanAttributes } from './utils.js';\n\nexport interface EndOfTurnInfo {\n /** The new transcript text from the user's speech. */\n newTranscript: string;\n /** Confidence score of the transcript (0-1). */\n transcriptConfidence: number;\n /** Delay from speech stop to final transcription in milliseconds. */\n transcriptionDelay: number;\n /** Delay from speech stop to end of utterance detection in milliseconds. */\n endOfUtteranceDelay: number;\n /** Timestamp when user started speaking (milliseconds since epoch). */\n startedSpeakingAt: number | undefined;\n /** Timestamp when user stopped speaking (milliseconds since epoch). */\n stoppedSpeakingAt: number | undefined;\n}\n\nexport interface PreemptiveGenerationInfo {\n newTranscript: string;\n transcriptConfidence: number;\n}\n\nexport interface RecognitionHooks {\n onStartOfSpeech: (ev: VADEvent) => void;\n onVADInferenceDone: (ev: VADEvent) => void;\n onEndOfSpeech: (ev: VADEvent) => void;\n onInterimTranscript: (ev: SpeechEvent) => void;\n onFinalTranscript: (ev: SpeechEvent) => void;\n onEndOfTurn: (info: EndOfTurnInfo) => Promise<boolean>;\n onPreemptiveGeneration: (info: PreemptiveGenerationInfo) => void;\n\n retrieveChatCtx: () => ChatContext;\n}\n\nexport interface _TurnDetector {\n unlikelyThreshold: (language?: LanguageCode) => Promise<number | undefined>;\n supportsLanguage: (language?: LanguageCode) => Promise<boolean>;\n predictEndOfTurn(chatCtx: ChatContext): Promise<number>;\n}\n\nexport interface AudioRecognitionOptions {\n /** Hooks for recognition events. */\n recognitionHooks: RecognitionHooks;\n /** Speech-to-text node. */\n stt?: STTNode;\n /** Voice activity detection. */\n vad?: VAD;\n /** Turn detector for end-of-turn prediction. */\n turnDetector?: _TurnDetector;\n /** Turn detection mode. */\n turnDetectionMode?: Exclude<TurnDetectionMode, _TurnDetector>;\n /** Minimum endpointing delay in milliseconds. */\n minEndpointingDelay: number;\n /** Maximum endpointing delay in milliseconds. */\n maxEndpointingDelay: number;\n /** Root span context for tracing. */\n rootSpanContext?: Context;\n /** STT model name for tracing */\n sttModel?: string;\n /** STT provider name for tracing */\n sttProvider?: string;\n /** Getter for linked participant for span attribution */\n getLinkedParticipant?: () => ParticipantLike | undefined;\n}\n\n/**\n * Minimal participant shape for span attribution.\n * Compatible with both `LocalParticipant` and `RemoteParticipant` from `@livekit/rtc-node`.\n */\nexport interface ParticipantLike {\n sid: string | undefined;\n identity: string;\n kind: ParticipantKind;\n}\n\nexport class AudioRecognition {\n private hooks: RecognitionHooks;\n private stt?: STTNode;\n private vad?: VAD;\n private turnDetector?: _TurnDetector;\n private turnDetectionMode?: Exclude<TurnDetectionMode, _TurnDetector>;\n private minEndpointingDelay: number;\n private maxEndpointingDelay: number;\n private lastLanguage?: LanguageCode;\n private rootSpanContext?: Context;\n private sttModel?: string;\n private sttProvider?: string;\n private getLinkedParticipant?: () => ParticipantLike | undefined;\n\n private deferredInputStream: DeferredReadableStream<AudioFrame>;\n private logger = log();\n private lastFinalTranscriptTime = 0;\n private audioTranscript = '';\n private audioInterimTranscript = '';\n private audioPreflightTranscript = '';\n private finalTranscriptConfidence: number[] = [];\n private lastSpeakingTime: number | undefined;\n private speechStartTime: number | undefined;\n private userTurnCommitted = false;\n private speaking = false;\n private sampleRate?: number;\n\n private userTurnSpan?: Span;\n\n private vadInputStream: ReadableStream<AudioFrame>;\n private sttInputStream: ReadableStream<AudioFrame>;\n private silenceAudioTransform = new IdentityTransform<AudioFrame>();\n private silenceAudioWriter: WritableStreamDefaultWriter<AudioFrame>;\n\n // all cancellable tasks\n private bounceEOUTask?: Task<void>;\n private commitUserTurnTask?: Task<void>;\n private vadTask?: Task<void>;\n private sttTask?: Task<void>;\n\n constructor(opts: AudioRecognitionOptions) {\n this.hooks = opts.recognitionHooks;\n this.stt = opts.stt;\n this.vad = opts.vad;\n this.turnDetector = opts.turnDetector;\n this.turnDetectionMode = opts.turnDetectionMode;\n this.minEndpointingDelay = opts.minEndpointingDelay;\n this.maxEndpointingDelay = opts.maxEndpointingDelay;\n this.lastLanguage = undefined;\n this.rootSpanContext = opts.rootSpanContext;\n this.sttModel = opts.sttModel;\n this.sttProvider = opts.sttProvider;\n this.getLinkedParticipant = opts.getLinkedParticipant;\n\n this.deferredInputStream = new DeferredReadableStream<AudioFrame>();\n const [vadInputStream, sttInputStream] = this.deferredInputStream.stream.tee();\n this.vadInputStream = vadInputStream;\n this.sttInputStream = mergeReadableStreams(sttInputStream, this.silenceAudioTransform.readable);\n this.silenceAudioWriter = this.silenceAudioTransform.writable.getWriter();\n }\n\n /**\n * Current transcript of the user's speech, including interim transcript if available.\n */\n get currentTranscript(): string {\n if (this.audioInterimTranscript) {\n return `${this.audioTranscript} ${this.audioInterimTranscript}`.trim();\n }\n return this.audioTranscript;\n }\n\n async start() {\n this.vadTask = Task.from(({ signal }) => this.createVadTask(this.vad, signal));\n this.vadTask.result.catch((err) => {\n this.logger.error(`Error running VAD task: ${err}`);\n });\n\n this.sttTask = Task.from(({ signal }) => this.createSttTask(this.stt, signal));\n this.sttTask.result.catch((err) => {\n this.logger.error(`Error running STT task: ${err}`);\n });\n }\n\n private ensureUserTurnSpan(startTime?: number): Span {\n if (this.userTurnSpan && this.userTurnSpan.isRecording()) {\n return this.userTurnSpan;\n }\n\n this.userTurnSpan = tracer.startSpan({\n name: 'user_turn',\n context: this.rootSpanContext,\n startTime,\n });\n\n const participant = this.getLinkedParticipant?.();\n if (participant) {\n setParticipantSpanAttributes(this.userTurnSpan, participant);\n }\n\n if (this.sttModel) {\n this.userTurnSpan.setAttribute(traceTypes.ATTR_GEN_AI_REQUEST_MODEL, this.sttModel);\n }\n if (this.sttProvider) {\n this.userTurnSpan.setAttribute(traceTypes.ATTR_GEN_AI_PROVIDER_NAME, this.sttProvider);\n }\n\n return this.userTurnSpan;\n }\n\n private userTurnContext(span: Span): Context {\n const base = this.rootSpanContext ?? ROOT_CONTEXT;\n return trace.setSpan(base, span);\n }\n\n private async onSTTEvent(ev: SpeechEvent) {\n if (\n this.turnDetectionMode === 'manual' &&\n this.userTurnCommitted &&\n (this.bounceEOUTask === undefined ||\n this.bounceEOUTask.done ||\n ev.type == SpeechEventType.INTERIM_TRANSCRIPT)\n ) {\n // ignore stt event if user turn already committed and EOU task is done\n // or it's an interim transcript\n this.logger.debug(\n {\n userTurnCommitted: this.userTurnCommitted,\n eouTaskDone: this.bounceEOUTask?.done,\n evType: ev.type,\n turnDetectionMode: this.turnDetectionMode,\n },\n 'ignoring stt event',\n );\n return;\n }\n\n switch (ev.type) {\n case SpeechEventType.FINAL_TRANSCRIPT:\n const transcript = ev.alternatives?.[0]?.text;\n const confidence = ev.alternatives?.[0]?.confidence ?? 0;\n this.lastLanguage = ev.alternatives?.[0]?.language;\n\n if (!transcript) {\n // stt final transcript received but no transcript\n return;\n }\n\n this.hooks.onFinalTranscript(ev);\n\n this.logger.debug(\n {\n user_transcript: transcript,\n language: this.lastLanguage,\n },\n 'received user transcript',\n );\n\n this.lastFinalTranscriptTime = Date.now();\n this.audioTranscript += ` ${transcript}`;\n this.audioTranscript = this.audioTranscript.trimStart();\n this.finalTranscriptConfidence.push(confidence);\n const transcriptChanged = this.audioTranscript !== this.audioPreflightTranscript;\n this.audioInterimTranscript = '';\n this.audioPreflightTranscript = '';\n\n if (!this.vad || this.lastSpeakingTime === undefined) {\n // vad disabled, use stt timestamp\n // TODO: this would screw up transcription latency metrics\n // but we'll live with it for now.\n // the correct way is to ensure STT fires SpeechEventType.END_OF_SPEECH\n // and using that timestamp for lastSpeakingTime\n this.lastSpeakingTime = Date.now();\n }\n\n if (this.vadBaseTurnDetection || this.userTurnCommitted) {\n if (transcriptChanged) {\n this.logger.debug(\n { transcript: this.audioTranscript },\n 'triggering preemptive generation (FINAL_TRANSCRIPT)',\n );\n this.hooks.onPreemptiveGeneration({\n newTranscript: this.audioTranscript,\n transcriptConfidence:\n this.finalTranscriptConfidence.length > 0\n ? this.finalTranscriptConfidence.reduce((a, b) => a + b, 0) /\n this.finalTranscriptConfidence.length\n : 0,\n });\n }\n\n if (!this.speaking) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on stt FINAL_TRANSCRIPT');\n this.runEOUDetection(chatCtx);\n }\n }\n break;\n case SpeechEventType.PREFLIGHT_TRANSCRIPT:\n this.hooks.onInterimTranscript(ev);\n const preflightTranscript = ev.alternatives?.[0]?.text ?? '';\n const preflightConfidence = ev.alternatives?.[0]?.confidence ?? 0;\n const preflightLanguage = ev.alternatives?.[0]?.language;\n\n const MIN_LANGUAGE_DETECTION_LENGTH = 5;\n if (\n !this.lastLanguage ||\n (preflightLanguage && preflightTranscript.length > MIN_LANGUAGE_DETECTION_LENGTH)\n ) {\n this.lastLanguage = preflightLanguage;\n }\n\n if (!preflightTranscript) {\n return;\n }\n\n this.logger.debug(\n {\n user_transcript: preflightTranscript,\n language: this.lastLanguage,\n },\n 'received user preflight transcript',\n );\n\n // still need to increment it as it's used for turn detection,\n this.lastFinalTranscriptTime = Date.now();\n // preflight transcript includes all pre-committed transcripts (including final transcript from the previous STT run)\n this.audioPreflightTranscript =\n `${this.audioTranscript} ${preflightTranscript}`.trimStart();\n this.audioInterimTranscript = preflightTranscript;\n\n if (!this.vad || this.lastSpeakingTime === undefined) {\n // vad disabled, use stt timestamp\n this.lastSpeakingTime = Date.now();\n }\n\n if (this.turnDetectionMode !== 'manual' || this.userTurnCommitted) {\n const confidenceVals = [...this.finalTranscriptConfidence, preflightConfidence];\n this.logger.debug(\n {\n transcript:\n this.audioPreflightTranscript.length > 100\n ? this.audioPreflightTranscript.slice(0, 100) + '...'\n : this.audioPreflightTranscript,\n },\n 'triggering preemptive generation (PREFLIGHT_TRANSCRIPT)',\n );\n this.hooks.onPreemptiveGeneration({\n newTranscript: this.audioPreflightTranscript,\n transcriptConfidence:\n confidenceVals.length > 0\n ? confidenceVals.reduce((a, b) => a + b, 0) / confidenceVals.length\n : 0,\n });\n }\n break;\n case SpeechEventType.INTERIM_TRANSCRIPT:\n this.logger.debug({ transcript: ev.alternatives?.[0]?.text }, 'interim transcript');\n this.hooks.onInterimTranscript(ev);\n this.audioInterimTranscript = ev.alternatives?.[0]?.text ?? '';\n break;\n case SpeechEventType.START_OF_SPEECH:\n if (this.turnDetectionMode !== 'stt') break;\n {\n const span = this.ensureUserTurnSpan(Date.now());\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => {\n this.hooks.onStartOfSpeech({\n type: VADEventType.START_OF_SPEECH,\n samplesIndex: 0,\n timestamp: Date.now(),\n speechDuration: 0,\n silenceDuration: 0,\n frames: [],\n probability: 0,\n inferenceDuration: 0,\n speaking: true,\n rawAccumulatedSilence: 0,\n rawAccumulatedSpeech: 0,\n });\n });\n }\n this.speaking = true;\n this.lastSpeakingTime = Date.now();\n\n this.bounceEOUTask?.cancel();\n break;\n case SpeechEventType.END_OF_SPEECH:\n if (this.turnDetectionMode !== 'stt') break;\n {\n const span = this.ensureUserTurnSpan();\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => {\n this.hooks.onEndOfSpeech({\n type: VADEventType.END_OF_SPEECH,\n samplesIndex: 0,\n timestamp: Date.now(),\n speechDuration: 0,\n silenceDuration: 0,\n frames: [],\n probability: 0,\n inferenceDuration: 0,\n speaking: false,\n rawAccumulatedSilence: 0,\n rawAccumulatedSpeech: 0,\n });\n });\n }\n this.speaking = false;\n this.userTurnCommitted = true;\n this.lastSpeakingTime = Date.now();\n\n if (!this.speaking) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on stt END_OF_SPEECH');\n this.runEOUDetection(chatCtx);\n }\n }\n }\n\n private runEOUDetection(chatCtx: ChatContext) {\n this.logger.debug(\n {\n stt: this.stt,\n audioTranscript: this.audioTranscript,\n turnDetectionMode: this.turnDetectionMode,\n },\n 'running EOU detection',\n );\n\n if (this.stt && !this.audioTranscript && this.turnDetectionMode !== 'manual') {\n // stt enabled but no transcript yet\n this.logger.debug('skipping EOU detection');\n return;\n }\n\n chatCtx = chatCtx.copy();\n chatCtx.addMessage({ role: 'user', content: this.audioTranscript });\n\n const turnDetector =\n // disable EOU model if manual turn detection enabled\n this.audioTranscript && this.turnDetectionMode !== 'manual' ? this.turnDetector : undefined;\n\n const bounceEOUTask =\n (\n lastSpeakingTime: number | undefined,\n lastFinalTranscriptTime: number,\n speechStartTime: number | undefined,\n ) =>\n async (controller: AbortController) => {\n let endpointingDelay = this.minEndpointingDelay;\n\n const userTurnSpan = this.ensureUserTurnSpan();\n const userTurnCtx = this.userTurnContext(userTurnSpan);\n\n if (turnDetector) {\n await tracer.startActiveSpan(\n async (span) => {\n this.logger.debug('Running turn detector model');\n\n let endOfTurnProbability = 0.0;\n let unlikelyThreshold: number | undefined;\n\n if (!(await turnDetector.supportsLanguage(this.lastLanguage))) {\n this.logger.debug(`Turn detector does not support language ${this.lastLanguage}`);\n } else {\n try {\n endOfTurnProbability = await turnDetector.predictEndOfTurn(chatCtx);\n unlikelyThreshold = await turnDetector.unlikelyThreshold(this.lastLanguage);\n\n this.logger.debug(\n { endOfTurnProbability, unlikelyThreshold, language: this.lastLanguage },\n 'end of turn probability',\n );\n\n if (unlikelyThreshold && endOfTurnProbability < unlikelyThreshold) {\n endpointingDelay = this.maxEndpointingDelay;\n }\n } catch (error) {\n this.logger.error(error, 'Error predicting end of turn');\n }\n }\n\n span.setAttribute(\n traceTypes.ATTR_CHAT_CTX,\n JSON.stringify(chatCtx.toJSON({ excludeTimestamp: false })),\n );\n span.setAttribute(traceTypes.ATTR_EOU_PROBABILITY, endOfTurnProbability);\n span.setAttribute(traceTypes.ATTR_EOU_UNLIKELY_THRESHOLD, unlikelyThreshold ?? 0);\n span.setAttribute(traceTypes.ATTR_EOU_DELAY, endpointingDelay);\n span.setAttribute(traceTypes.ATTR_EOU_LANGUAGE, this.lastLanguage ?? '');\n },\n {\n name: 'eou_detection',\n context: userTurnCtx,\n },\n );\n }\n\n let extraSleep = endpointingDelay;\n if (lastSpeakingTime !== undefined) {\n extraSleep += lastSpeakingTime - Date.now();\n }\n\n if (extraSleep > 0) {\n // add delay to see if there's a potential upcoming EOU task that cancels this one\n await delay(Math.max(extraSleep, 0), { signal: controller.signal });\n }\n\n this.logger.debug({ transcript: this.audioTranscript }, 'end of user turn');\n\n const confidenceAvg =\n this.finalTranscriptConfidence.length > 0\n ? this.finalTranscriptConfidence.reduce((a, b) => a + b, 0) /\n this.finalTranscriptConfidence.length\n : 0;\n\n let startedSpeakingAt: number | undefined;\n let stoppedSpeakingAt: number | undefined;\n let transcriptionDelay: number | undefined;\n let endOfUtteranceDelay: number | undefined;\n\n // sometimes, we can't calculate the metrics because VAD was unreliable.\n // in this case, we just ignore the calculation, it's better than providing likely wrong values\n if (\n lastFinalTranscriptTime !== 0 &&\n lastSpeakingTime !== undefined &&\n speechStartTime !== undefined\n ) {\n startedSpeakingAt = speechStartTime;\n stoppedSpeakingAt = lastSpeakingTime;\n transcriptionDelay = Math.max(lastFinalTranscriptTime - lastSpeakingTime, 0);\n endOfUtteranceDelay = Date.now() - lastSpeakingTime;\n }\n\n const committed = await this.hooks.onEndOfTurn({\n newTranscript: this.audioTranscript,\n transcriptConfidence: confidenceAvg,\n transcriptionDelay: transcriptionDelay ?? 0,\n endOfUtteranceDelay: endOfUtteranceDelay ?? 0,\n startedSpeakingAt,\n stoppedSpeakingAt,\n });\n\n if (committed) {\n this._endUserTurnSpan({\n transcript: this.audioTranscript,\n confidence: confidenceAvg,\n transcriptionDelay: transcriptionDelay ?? 0,\n endOfUtteranceDelay: endOfUtteranceDelay ?? 0,\n });\n\n // clear the transcript if the user turn was committed\n this.audioTranscript = '';\n this.finalTranscriptConfidence = [];\n this.lastSpeakingTime = undefined;\n this.lastFinalTranscriptTime = 0;\n this.speechStartTime = undefined;\n }\n\n this.userTurnCommitted = false;\n };\n\n // cancel any existing EOU task\n this.bounceEOUTask?.cancel();\n // copy the values before awaiting (the values can change)\n this.bounceEOUTask = Task.from(\n bounceEOUTask(this.lastSpeakingTime, this.lastFinalTranscriptTime, this.speechStartTime),\n );\n\n this.bounceEOUTask.result\n .then(() => {\n this.logger.debug('EOU detection task completed');\n })\n .catch((err: unknown) => {\n if (err instanceof Error && err.message.includes('This operation was aborted')) {\n // ignore aborted errors\n return;\n }\n this.logger.error(err, 'Error in EOU detection task:');\n });\n }\n\n private async createSttTask(stt: STTNode | undefined, signal: AbortSignal) {\n if (!stt) return;\n\n this.logger.debug('createSttTask: create stt stream from stt node');\n\n const sttStream = await stt(this.sttInputStream, {});\n\n if (signal.aborted || sttStream === null) return;\n\n if (sttStream instanceof ReadableStream) {\n const reader = sttStream.getReader();\n\n signal.addEventListener('abort', async () => {\n try {\n reader.releaseLock();\n await sttStream?.cancel();\n } catch (e) {\n this.logger.debug('createSttTask: error during abort handler:', e);\n }\n });\n\n try {\n while (true) {\n if (signal.aborted) break;\n\n const { done, value: ev } = await reader.read();\n if (done) break;\n\n if (typeof ev === 'string') {\n throw new Error('STT node must yield SpeechEvent');\n } else {\n await this.onSTTEvent(ev);\n }\n }\n } catch (e) {\n if (isStreamReaderReleaseError(e)) {\n return;\n }\n this.logger.error({ error: e }, 'createSttTask: error reading sttStream');\n } finally {\n reader.releaseLock();\n try {\n await sttStream.cancel();\n } catch (e) {\n this.logger.debug(\n 'createSttTask: error cancelling sttStream (may already be cancelled):',\n e,\n );\n }\n }\n }\n }\n\n private async createVadTask(vad: VAD | undefined, signal: AbortSignal) {\n if (!vad) return;\n\n const vadStream = vad.stream();\n vadStream.updateInputStream(this.vadInputStream);\n\n const abortHandler = () => {\n vadStream.detachInputStream();\n vadStream.close();\n signal.removeEventListener('abort', abortHandler);\n };\n signal.addEventListener('abort', abortHandler);\n\n try {\n for await (const ev of vadStream) {\n if (signal.aborted) break;\n\n switch (ev.type) {\n case VADEventType.START_OF_SPEECH:\n this.logger.debug('VAD task: START_OF_SPEECH');\n {\n const startTime = Date.now() - ev.speechDuration;\n const span = this.ensureUserTurnSpan(startTime);\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => this.hooks.onStartOfSpeech(ev));\n }\n this.speaking = true;\n\n // Capture sample rate from the first VAD event if not already set\n if (ev.frames.length > 0 && ev.frames[0]) {\n this.sampleRate = ev.frames[0].sampleRate;\n }\n\n this.bounceEOUTask?.cancel();\n break;\n case VADEventType.INFERENCE_DONE:\n this.hooks.onVADInferenceDone(ev);\n // for metrics, get the \"earliest\" signal of speech as possible\n if (ev.rawAccumulatedSpeech > 0.0) {\n this.lastSpeakingTime = Date.now();\n\n if (this.speechStartTime === undefined) {\n this.speechStartTime = Date.now();\n }\n }\n break;\n case VADEventType.END_OF_SPEECH:\n this.logger.debug('VAD task: END_OF_SPEECH');\n {\n const span = this.ensureUserTurnSpan();\n const ctx = this.userTurnContext(span);\n otelContext.with(ctx, () => this.hooks.onEndOfSpeech(ev));\n }\n\n // when VAD fires END_OF_SPEECH, it already waited for the silence_duration\n this.speaking = false;\n\n if (\n this.vadBaseTurnDetection ||\n (this.turnDetectionMode === 'stt' && this.userTurnCommitted)\n ) {\n const chatCtx = this.hooks.retrieveChatCtx();\n this.runEOUDetection(chatCtx);\n }\n break;\n }\n }\n } catch (e) {\n this.logger.error(e, 'Error in VAD task');\n } finally {\n this.logger.debug('VAD task closed');\n }\n }\n\n setInputAudioStream(audioStream: ReadableStream<AudioFrame>) {\n this.deferredInputStream.setSource(audioStream);\n }\n\n detachInputAudioStream() {\n this.deferredInputStream.detachSource();\n }\n\n clearUserTurn() {\n this.audioTranscript = '';\n this.audioInterimTranscript = '';\n this.audioPreflightTranscript = '';\n this.finalTranscriptConfidence = [];\n this.userTurnCommitted = false;\n\n this.sttTask?.cancelAndWait().finally(() => {\n this.sttTask = Task.from(({ signal }) => this.createSttTask(this.stt, signal));\n this.sttTask.result.catch((err) => {\n this.logger.error(`Error running STT task: ${err}`);\n });\n });\n }\n\n commitUserTurn(audioDetached: boolean) {\n const commitUserTurnTask =\n (delayDuration: number = 500) =>\n async (controller: AbortController) => {\n if (Date.now() - this.lastFinalTranscriptTime > delayDuration) {\n // flush the stt by pushing silence\n if (audioDetached && this.sampleRate !== undefined) {\n const numSamples = Math.floor(this.sampleRate * 0.5);\n const silence = new Int16Array(numSamples * 2);\n const silenceFrame = new AudioFrame(silence, this.sampleRate, 1, numSamples);\n this.silenceAudioWriter.write(silenceFrame);\n }\n\n // wait for the final transcript to be available\n await delay(delayDuration, { signal: controller.signal });\n }\n\n if (this.audioInterimTranscript) {\n // append interim transcript in case the final transcript is not ready\n this.audioTranscript = `${this.audioTranscript} ${this.audioInterimTranscript}`.trim();\n }\n this.audioInterimTranscript = '';\n\n const chatCtx = this.hooks.retrieveChatCtx();\n this.logger.debug('running EOU detection on commitUserTurn');\n this.runEOUDetection(chatCtx);\n this.userTurnCommitted = true;\n };\n\n // cancel any existing commit user turn task\n this.commitUserTurnTask?.cancel();\n this.commitUserTurnTask = Task.from(commitUserTurnTask());\n\n this.commitUserTurnTask.result\n .then(() => {\n this.logger.debug('User turn committed');\n })\n .catch((err: unknown) => {\n if (err instanceof Error && err.name === 'AbortError') {\n this.logger.debug('User turn commit task cancelled');\n return;\n }\n this.logger.error(err, 'Error in user turn commit task:');\n });\n }\n\n async close() {\n this.detachInputAudioStream();\n this.silenceAudioWriter.releaseLock();\n await this.commitUserTurnTask?.cancelAndWait();\n await this.sttTask?.cancelAndWait();\n await this.vadTask?.cancelAndWait();\n await this.bounceEOUTask?.cancelAndWait();\n }\n\n private _endUserTurnSpan({\n transcript,\n confidence,\n transcriptionDelay,\n endOfUtteranceDelay,\n }: {\n transcript: string;\n confidence: number;\n transcriptionDelay: number;\n endOfUtteranceDelay: number;\n }): void {\n if (this.userTurnSpan) {\n this.userTurnSpan.setAttributes({\n [traceTypes.ATTR_USER_TRANSCRIPT]: transcript,\n [traceTypes.ATTR_TRANSCRIPT_CONFIDENCE]: confidence,\n [traceTypes.ATTR_TRANSCRIPTION_DELAY]: transcriptionDelay,\n [traceTypes.ATTR_END_OF_TURN_DELAY]: endOfUtteranceDelay,\n });\n this.userTurnSpan.end();\n this.userTurnSpan = undefined;\n }\n }\n\n private get vadBaseTurnDetection() {\n return ['vad', undefined].includes(this.turnDetectionMode);\n }\n}\n"],"mappings":"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","names":["import_utils","otelContext"]}
|
|
@@ -3,6 +3,7 @@ import type { ParticipantKind } from '@livekit/rtc-node';
|
|
|
3
3
|
import { AudioFrame } from '@livekit/rtc-node';
|
|
4
4
|
import { type Context } from '@opentelemetry/api';
|
|
5
5
|
import { ReadableStream } from 'node:stream/web';
|
|
6
|
+
import type { LanguageCode } from '../language.js';
|
|
6
7
|
import { type ChatContext } from '../llm/chat_context.js';
|
|
7
8
|
import { type SpeechEvent } from '../stt/stt.js';
|
|
8
9
|
import { type VAD, type VADEvent } from '../vad.js';
|
|
@@ -37,8 +38,8 @@ export interface RecognitionHooks {
|
|
|
37
38
|
retrieveChatCtx: () => ChatContext;
|
|
38
39
|
}
|
|
39
40
|
export interface _TurnDetector {
|
|
40
|
-
unlikelyThreshold: (language?:
|
|
41
|
-
supportsLanguage: (language?:
|
|
41
|
+
unlikelyThreshold: (language?: LanguageCode) => Promise<number | undefined>;
|
|
42
|
+
supportsLanguage: (language?: LanguageCode) => Promise<boolean>;
|
|
42
43
|
predictEndOfTurn(chatCtx: ChatContext): Promise<number>;
|
|
43
44
|
}
|
|
44
45
|
export interface AudioRecognitionOptions {
|
|
@@ -3,6 +3,7 @@ import type { ParticipantKind } from '@livekit/rtc-node';
|
|
|
3
3
|
import { AudioFrame } from '@livekit/rtc-node';
|
|
4
4
|
import { type Context } from '@opentelemetry/api';
|
|
5
5
|
import { ReadableStream } from 'node:stream/web';
|
|
6
|
+
import type { LanguageCode } from '../language.js';
|
|
6
7
|
import { type ChatContext } from '../llm/chat_context.js';
|
|
7
8
|
import { type SpeechEvent } from '../stt/stt.js';
|
|
8
9
|
import { type VAD, type VADEvent } from '../vad.js';
|
|
@@ -37,8 +38,8 @@ export interface RecognitionHooks {
|
|
|
37
38
|
retrieveChatCtx: () => ChatContext;
|
|
38
39
|
}
|
|
39
40
|
export interface _TurnDetector {
|
|
40
|
-
unlikelyThreshold: (language?:
|
|
41
|
-
supportsLanguage: (language?:
|
|
41
|
+
unlikelyThreshold: (language?: LanguageCode) => Promise<number | undefined>;
|
|
42
|
+
supportsLanguage: (language?: LanguageCode) => Promise<boolean>;
|
|
42
43
|
predictEndOfTurn(chatCtx: ChatContext): Promise<number>;
|
|
43
44
|
}
|
|
44
45
|
export interface AudioRecognitionOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio_recognition.d.ts","sourceRoot":"","sources":["../../src/voice/audio_recognition.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,KAAK,OAAO,EAKb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAK1D,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAGlE,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAgB,MAAM,WAAW,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qEAAqE;IACrE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uEAAuE;IACvE,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,uEAAuE;IACvE,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,IAAI,CAAC;IACxC,kBAAkB,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC3C,aAAa,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,IAAI,CAAC;IACtC,mBAAmB,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAC;IAC/C,iBAAiB,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAC;IAC7C,WAAW,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,sBAAsB,EAAE,CAAC,IAAI,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAEjE,eAAe,EAAE,MAAM,WAAW,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,iBAAiB,EAAE,CAAC,QAAQ,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"audio_recognition.d.ts","sourceRoot":"","sources":["../../src/voice/audio_recognition.ts"],"names":[],"mappings":"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"}
|