@livekit/agents 1.1.0-dev.0 → 1.2.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.
- package/dist/cli.cjs +2 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/constants.cjs +3 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/cpu.cjs +189 -0
- package/dist/cpu.cjs.map +1 -0
- package/dist/cpu.d.cts +24 -0
- package/dist/cpu.d.ts +24 -0
- package/dist/cpu.d.ts.map +1 -0
- package/dist/cpu.js +152 -0
- package/dist/cpu.js.map +1 -0
- package/dist/cpu.test.cjs +227 -0
- package/dist/cpu.test.cjs.map +1 -0
- package/dist/cpu.test.js +204 -0
- package/dist/cpu.test.js.map +1 -0
- 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/interruption/defaults.cjs +1 -1
- package/dist/inference/interruption/defaults.cjs.map +1 -1
- package/dist/inference/interruption/defaults.d.cts +1 -1
- package/dist/inference/interruption/defaults.d.ts +1 -1
- package/dist/inference/interruption/defaults.d.ts.map +1 -1
- package/dist/inference/interruption/defaults.js +1 -1
- package/dist/inference/interruption/defaults.js.map +1 -1
- package/dist/inference/interruption/http_transport.cjs +44 -28
- package/dist/inference/interruption/http_transport.cjs.map +1 -1
- package/dist/inference/interruption/http_transport.d.ts.map +1 -1
- package/dist/inference/interruption/http_transport.js +45 -29
- package/dist/inference/interruption/http_transport.js.map +1 -1
- package/dist/inference/interruption/interruption_detector.cjs +22 -5
- package/dist/inference/interruption/interruption_detector.cjs.map +1 -1
- package/dist/inference/interruption/interruption_detector.d.cts +2 -2
- package/dist/inference/interruption/interruption_detector.d.ts +2 -2
- package/dist/inference/interruption/interruption_detector.d.ts.map +1 -1
- package/dist/inference/interruption/interruption_detector.js +22 -5
- package/dist/inference/interruption/interruption_detector.js.map +1 -1
- package/dist/inference/interruption/interruption_stream.cjs +4 -4
- package/dist/inference/interruption/interruption_stream.cjs.map +1 -1
- package/dist/inference/interruption/interruption_stream.js +4 -4
- package/dist/inference/interruption/interruption_stream.js.map +1 -1
- package/dist/inference/interruption/types.cjs.map +1 -1
- package/dist/inference/interruption/types.d.cts +2 -2
- package/dist/inference/interruption/types.d.ts +2 -2
- package/dist/inference/interruption/types.d.ts.map +1 -1
- package/dist/inference/interruption/ws_transport.cjs +60 -47
- package/dist/inference/interruption/ws_transport.cjs.map +1 -1
- package/dist/inference/interruption/ws_transport.d.ts.map +1 -1
- package/dist/inference/interruption/ws_transport.js +60 -47
- package/dist/inference/interruption/ws_transport.js.map +1 -1
- 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.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 +8 -1
- package/dist/inference/tts.d.ts +8 -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/ipc/job_proc_lazy_main.cjs +41 -23
- package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
- package/dist/ipc/job_proc_lazy_main.js +41 -23
- package/dist/ipc/job_proc_lazy_main.js.map +1 -1
- package/dist/job.cjs +1 -1
- package/dist/job.cjs.map +1 -1
- package/dist/job.js +1 -1
- package/dist/job.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/llm/index.cjs +2 -0
- package/dist/llm/index.cjs.map +1 -1
- package/dist/llm/index.d.cts +1 -1
- package/dist/llm/index.d.ts +1 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +2 -0
- package/dist/llm/index.js.map +1 -1
- 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/utils.cjs +15 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +8 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +13 -0
- package/dist/utils.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.js +1 -1
- package/dist/voice/agent.cjs +14 -17
- package/dist/voice/agent.cjs.map +1 -1
- package/dist/voice/agent.d.cts +10 -11
- package/dist/voice/agent.d.ts +10 -11
- package/dist/voice/agent.d.ts.map +1 -1
- package/dist/voice/agent.js +15 -18
- package/dist/voice/agent.js.map +1 -1
- package/dist/voice/agent.test.cjs +194 -0
- package/dist/voice/agent.test.cjs.map +1 -1
- package/dist/voice/agent.test.js +195 -1
- package/dist/voice/agent.test.js.map +1 -1
- package/dist/voice/agent_activity.cjs +116 -39
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.cts +2 -0
- package/dist/voice/agent_activity.d.ts +2 -0
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +117 -40
- 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/agent_session.cjs +38 -38
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +65 -56
- package/dist/voice/agent_session.d.ts +65 -56
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +37 -37
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs +106 -52
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +4 -2
- package/dist/voice/audio_recognition.d.ts +4 -2
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js +106 -52
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/audio_recognition_span.test.cjs +84 -22
- package/dist/voice/audio_recognition_span.test.cjs.map +1 -1
- package/dist/voice/audio_recognition_span.test.js +90 -23
- package/dist/voice/audio_recognition_span.test.js.map +1 -1
- package/dist/voice/events.cjs +1 -1
- package/dist/voice/events.cjs.map +1 -1
- package/dist/voice/events.d.cts +4 -3
- package/dist/voice/events.d.ts +4 -3
- package/dist/voice/events.d.ts.map +1 -1
- package/dist/voice/events.js +1 -1
- package/dist/voice/events.js.map +1 -1
- package/dist/voice/index.cjs +9 -1
- package/dist/voice/index.cjs.map +1 -1
- package/dist/voice/index.d.cts +1 -1
- package/dist/voice/index.d.ts +1 -1
- package/dist/voice/index.d.ts.map +1 -1
- package/dist/voice/index.js +10 -1
- package/dist/voice/index.js.map +1 -1
- package/dist/voice/remote_session.cjs +922 -0
- package/dist/voice/remote_session.cjs.map +1 -0
- package/dist/voice/remote_session.d.cts +108 -0
- package/dist/voice/remote_session.d.ts +108 -0
- package/dist/voice/remote_session.d.ts.map +1 -0
- package/dist/voice/remote_session.js +887 -0
- package/dist/voice/remote_session.js.map +1 -0
- package/dist/voice/report.cjs +11 -10
- package/dist/voice/report.cjs.map +1 -1
- package/dist/voice/report.d.cts +5 -3
- package/dist/voice/report.d.ts +5 -3
- package/dist/voice/report.d.ts.map +1 -1
- package/dist/voice/report.js +11 -10
- package/dist/voice/report.js.map +1 -1
- package/dist/voice/report.test.cjs +15 -0
- package/dist/voice/report.test.cjs.map +1 -1
- package/dist/voice/report.test.js +15 -0
- package/dist/voice/report.test.js.map +1 -1
- package/dist/voice/room_io/room_io.cjs +39 -0
- package/dist/voice/room_io/room_io.cjs.map +1 -1
- package/dist/voice/room_io/room_io.d.cts +3 -1
- package/dist/voice/room_io/room_io.d.ts +3 -1
- package/dist/voice/room_io/room_io.d.ts.map +1 -1
- package/dist/voice/room_io/room_io.js +40 -1
- package/dist/voice/room_io/room_io.js.map +1 -1
- package/dist/voice/turn_config/interruption.cjs.map +1 -1
- package/dist/voice/turn_config/interruption.d.cts +1 -1
- package/dist/voice/turn_config/interruption.d.ts +1 -1
- package/dist/voice/turn_config/interruption.d.ts.map +1 -1
- package/dist/voice/turn_config/interruption.js.map +1 -1
- package/dist/voice/turn_config/utils.cjs +95 -35
- package/dist/voice/turn_config/utils.cjs.map +1 -1
- package/dist/voice/turn_config/utils.d.cts +17 -5
- package/dist/voice/turn_config/utils.d.ts +17 -5
- package/dist/voice/turn_config/utils.d.ts.map +1 -1
- package/dist/voice/turn_config/utils.js +93 -35
- package/dist/voice/turn_config/utils.js.map +1 -1
- package/dist/voice/turn_config/utils.test.cjs +83 -41
- package/dist/voice/turn_config/utils.test.cjs.map +1 -1
- package/dist/voice/turn_config/utils.test.js +84 -42
- package/dist/voice/turn_config/utils.test.js.map +1 -1
- package/dist/worker.cjs +6 -29
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +6 -19
- package/dist/worker.js.map +1 -1
- package/package.json +3 -2
- package/src/cli.ts +2 -0
- package/src/constants.ts +1 -0
- package/src/cpu.test.ts +239 -0
- package/src/cpu.ts +173 -0
- package/src/index.ts +13 -15
- package/src/inference/interruption/defaults.ts +1 -1
- package/src/inference/interruption/http_transport.ts +49 -30
- package/src/inference/interruption/interruption_detector.ts +22 -6
- package/src/inference/interruption/interruption_stream.ts +4 -4
- package/src/inference/interruption/types.ts +2 -2
- package/src/inference/interruption/ws_transport.ts +63 -59
- package/src/inference/llm.ts +3 -1
- 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 +22 -6
- package/src/ipc/job_proc_lazy_main.ts +44 -24
- package/src/job.ts +1 -1
- package/src/language.test.ts +62 -0
- package/src/language.ts +380 -0
- package/src/llm/index.ts +2 -0
- package/src/stream/deferred_stream.ts +5 -1
- package/src/stt/stt.ts +2 -1
- package/src/utils.ts +20 -0
- package/src/voice/agent.test.ts +208 -1
- package/src/voice/agent.ts +21 -22
- package/src/voice/agent_activity.test.ts +194 -0
- package/src/voice/agent_activity.ts +161 -43
- package/src/voice/agent_session.ts +103 -92
- package/src/voice/audio_recognition.ts +124 -61
- package/src/voice/audio_recognition_span.test.ts +115 -35
- package/src/voice/events.ts +4 -3
- package/src/voice/index.ts +10 -1
- package/src/voice/remote_session.ts +1083 -0
- package/src/voice/report.test.ts +22 -3
- package/src/voice/report.ts +31 -14
- package/src/voice/room_io/room_io.ts +52 -2
- package/src/voice/turn_config/interruption.ts +1 -1
- package/src/voice/turn_config/utils.test.ts +91 -43
- package/src/voice/turn_config/utils.ts +120 -56
- package/src/worker.ts +34 -50
- package/dist/voice/client_events.cjs +0 -554
- package/dist/voice/client_events.cjs.map +0 -1
- package/dist/voice/client_events.d.cts +0 -195
- package/dist/voice/client_events.d.ts +0 -195
- package/dist/voice/client_events.d.ts.map +0 -1
- package/dist/voice/client_events.js +0 -548
- package/dist/voice/client_events.js.map +0 -1
- package/dist/voice/wire_format.cjs +0 -798
- package/dist/voice/wire_format.cjs.map +0 -1
- package/dist/voice/wire_format.d.cts +0 -5503
- package/dist/voice/wire_format.d.ts +0 -5503
- package/dist/voice/wire_format.d.ts.map +0 -1
- package/dist/voice/wire_format.js +0 -728
- package/dist/voice/wire_format.js.map +0 -1
- package/src/voice/client_events.ts +0 -838
- package/src/voice/wire_format.ts +0 -827
|
@@ -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":"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","names":[]}
|
|
@@ -19,7 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var agent_session_exports = {};
|
|
20
20
|
__export(agent_session_exports, {
|
|
21
21
|
AgentSession: () => AgentSession,
|
|
22
|
-
|
|
22
|
+
defaultAgentSessionOptions: () => defaultAgentSessionOptions
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(agent_session_exports);
|
|
25
25
|
var import_mutex = require("@livekit/mutex");
|
|
@@ -34,17 +34,17 @@ var import_telemetry = require("../telemetry/index.cjs");
|
|
|
34
34
|
var import_types = require("../types.cjs");
|
|
35
35
|
var import_utils = require("../utils.cjs");
|
|
36
36
|
var import_agent_activity = require("./agent_activity.cjs");
|
|
37
|
-
var import_client_events = require("./client_events.cjs");
|
|
38
37
|
var import_events = require("./events.cjs");
|
|
39
38
|
var import_io = require("./io.cjs");
|
|
40
39
|
var import_recorder_io = require("./recorder_io/index.cjs");
|
|
40
|
+
var import_remote_session = require("./remote_session.cjs");
|
|
41
41
|
var import_room_io = require("./room_io/index.cjs");
|
|
42
42
|
var import_run_result = require("./testing/run_result.cjs");
|
|
43
43
|
var import_utils2 = require("./turn_config/utils.cjs");
|
|
44
44
|
var import_utils3 = require("./utils.cjs");
|
|
45
|
-
const
|
|
45
|
+
const defaultAgentSessionOptions = {
|
|
46
46
|
maxToolSteps: 3,
|
|
47
|
-
preemptiveGeneration:
|
|
47
|
+
preemptiveGeneration: true,
|
|
48
48
|
userAwayTimeout: 15,
|
|
49
49
|
aecWarmupDuration: 3e3,
|
|
50
50
|
turnHandling: {},
|
|
@@ -56,14 +56,16 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
56
56
|
llm;
|
|
57
57
|
tts;
|
|
58
58
|
turnDetection;
|
|
59
|
+
/** @deprecated use {@link sessionOptions } instead */
|
|
59
60
|
options;
|
|
61
|
+
sessionOptions;
|
|
60
62
|
activityLock = new import_mutex.Mutex();
|
|
61
63
|
agent;
|
|
62
64
|
activity;
|
|
63
65
|
nextActivity;
|
|
64
66
|
updateActivityTask;
|
|
65
67
|
started = false;
|
|
66
|
-
|
|
68
|
+
sessionHost;
|
|
67
69
|
_chatCtx;
|
|
68
70
|
_userData;
|
|
69
71
|
_userState = "listening";
|
|
@@ -78,10 +80,10 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
78
80
|
// Unrecoverable error counts, reset after agent speaking
|
|
79
81
|
llmErrorCounts = 0;
|
|
80
82
|
ttsErrorCounts = 0;
|
|
81
|
-
interruptionDetectionErrorCounts = 0;
|
|
82
83
|
sessionSpan;
|
|
83
84
|
agentSpeakingSpan;
|
|
84
85
|
_interruptionDetection;
|
|
86
|
+
/** @internal */
|
|
85
87
|
_usageCollector = new import_model_usage.ModelUsageCollector();
|
|
86
88
|
/** @internal */
|
|
87
89
|
_roomIO;
|
|
@@ -103,10 +105,10 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
103
105
|
_userSpeakingSpan;
|
|
104
106
|
logger = (0, import_log.log)();
|
|
105
107
|
constructor(options) {
|
|
106
|
-
var _a
|
|
108
|
+
var _a;
|
|
107
109
|
super();
|
|
108
|
-
const opts = (0, import_utils2.migrateLegacyOptions)(options);
|
|
109
|
-
const { vad, stt, llm, tts, userData, connOptions,
|
|
110
|
+
const { agentSessionOptions: opts, legacyVoiceOptions } = (0, import_utils2.migrateLegacyOptions)(options);
|
|
111
|
+
const { vad, stt, llm, tts, userData, connOptions, ...resolvedSessionOptions } = opts;
|
|
110
112
|
this._connOptions = {
|
|
111
113
|
sttConnOptions: { ...import_types.DEFAULT_API_CONNECT_OPTIONS, ...connOptions == null ? void 0 : connOptions.sttConnOptions },
|
|
112
114
|
llmConnOptions: { ...import_types.DEFAULT_API_CONNECT_OPTIONS, ...connOptions == null ? void 0 : connOptions.llmConnOptions },
|
|
@@ -129,23 +131,21 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
129
131
|
} else {
|
|
130
132
|
this.tts = tts;
|
|
131
133
|
}
|
|
132
|
-
this.turnDetection =
|
|
133
|
-
this._interruptionDetection = (
|
|
134
|
+
this.turnDetection = resolvedSessionOptions.turnHandling.turnDetection;
|
|
135
|
+
this._interruptionDetection = (_a = resolvedSessionOptions.turnHandling.interruption) == null ? void 0 : _a.mode;
|
|
134
136
|
this._userData = userData;
|
|
135
137
|
this._input = new import_io.AgentInput(this.onAudioInputChanged);
|
|
136
138
|
this._output = new import_io.AgentOutput(this.onAudioOutputChanged, this.onTextOutputChanged);
|
|
137
139
|
this._chatCtx = import_chat_context.ChatContext.empty();
|
|
138
|
-
this.
|
|
139
|
-
this.
|
|
140
|
+
this.sessionOptions = resolvedSessionOptions;
|
|
141
|
+
this.options = legacyVoiceOptions;
|
|
142
|
+
this._aecWarmupRemaining = this.sessionOptions.aecWarmupDuration ?? 0;
|
|
140
143
|
this._onUserInputTranscribed = this._onUserInputTranscribed.bind(this);
|
|
141
144
|
this.on(import_events.AgentSessionEventTypes.UserInputTranscribed, this._onUserInputTranscribed);
|
|
142
145
|
}
|
|
143
146
|
emit(event, ...args) {
|
|
144
147
|
const eventData = args[0];
|
|
145
148
|
this._recordedEvents.push(eventData);
|
|
146
|
-
if (event === import_events.AgentSessionEventTypes.MetricsCollected) {
|
|
147
|
-
this._usageCollector.collect(eventData.metrics);
|
|
148
|
-
}
|
|
149
149
|
return super.emit(event, ...args);
|
|
150
150
|
}
|
|
151
151
|
get input() {
|
|
@@ -177,7 +177,7 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
177
177
|
return { modelUsage: this._usageCollector.flatten().map(import_model_usage.filterZeroValues) };
|
|
178
178
|
}
|
|
179
179
|
get useTtsAlignedTranscript() {
|
|
180
|
-
return this.
|
|
180
|
+
return this.sessionOptions.useTtsAlignedTranscript;
|
|
181
181
|
}
|
|
182
182
|
set userData(value) {
|
|
183
183
|
this._userData = value;
|
|
@@ -216,9 +216,11 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
216
216
|
outputOptions
|
|
217
217
|
});
|
|
218
218
|
this._roomIO.start();
|
|
219
|
-
|
|
219
|
+
const transport = new import_remote_session.RoomSessionTransport(room, this._roomIO);
|
|
220
|
+
this.sessionHost = new import_remote_session.SessionHost(transport);
|
|
221
|
+
this.sessionHost.registerSession(this);
|
|
220
222
|
if ((inputOptions == null ? void 0 : inputOptions.textEnabled) !== false) {
|
|
221
|
-
this.
|
|
223
|
+
this.sessionHost.registerTextInput(
|
|
222
224
|
(inputOptions == null ? void 0 : inputOptions.textInputCallback) ?? import_room_io.DEFAULT_TEXT_INPUT_CALLBACK
|
|
223
225
|
);
|
|
224
226
|
}
|
|
@@ -252,8 +254,8 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
252
254
|
}
|
|
253
255
|
tasks.push(this._updateActivity(this.agent, { waitOnEnter: false }));
|
|
254
256
|
await Promise.allSettled(tasks);
|
|
255
|
-
if (this.
|
|
256
|
-
await this.
|
|
257
|
+
if (this.sessionHost) {
|
|
258
|
+
await this.sessionHost.start();
|
|
257
259
|
}
|
|
258
260
|
this.logger.debug(
|
|
259
261
|
`using audio io: ${this.input.audio ? "`" + this.input.audio.constructor.name + "`" : "(none)"} -> \`AgentSession\` -> ${this.output.audio ? "`" + this.output.audio.constructor.name + "`" : "(none)"}`
|
|
@@ -554,7 +556,9 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
554
556
|
if (this.closingTask) {
|
|
555
557
|
return;
|
|
556
558
|
}
|
|
557
|
-
this.closeImpl(reason, error, drain)
|
|
559
|
+
this.closingTask = this.closeImpl(reason, error, drain).finally(() => {
|
|
560
|
+
this.closingTask = null;
|
|
561
|
+
});
|
|
558
562
|
}
|
|
559
563
|
/** @internal */
|
|
560
564
|
_onError(error) {
|
|
@@ -572,12 +576,10 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
572
576
|
return;
|
|
573
577
|
}
|
|
574
578
|
} else if (error.type === "interruption_detection_error") {
|
|
575
|
-
this.
|
|
576
|
-
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
+
this.logger.error(error.toString());
|
|
580
|
+
return;
|
|
579
581
|
}
|
|
580
|
-
this.logger.error(error, "AgentSession is closing due to unrecoverable error");
|
|
582
|
+
this.logger.error(error, "AgentSession is closing due to an unrecoverable error");
|
|
581
583
|
this.closingTask = (async () => {
|
|
582
584
|
await this.closeImpl(import_events.CloseReason.ERROR, error);
|
|
583
585
|
})().then(() => {
|
|
@@ -602,7 +604,6 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
602
604
|
if (state === "speaking") {
|
|
603
605
|
this.llmErrorCounts = 0;
|
|
604
606
|
this.ttsErrorCounts = 0;
|
|
605
|
-
this.interruptionDetectionErrorCounts = 0;
|
|
606
607
|
if (this.agentSpeakingSpan === void 0) {
|
|
607
608
|
this.agentSpeakingSpan = import_telemetry.tracer.startSpan({
|
|
608
609
|
name: "agent_speaking",
|
|
@@ -638,7 +639,7 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
638
639
|
);
|
|
639
640
|
}
|
|
640
641
|
/** @internal */
|
|
641
|
-
_updateUserState(state,
|
|
642
|
+
_updateUserState(state, options) {
|
|
642
643
|
var _a;
|
|
643
644
|
if (this._userState === state) {
|
|
644
645
|
return;
|
|
@@ -646,15 +647,15 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
646
647
|
if (state === "speaking" && this._userSpeakingSpan === void 0) {
|
|
647
648
|
this._userSpeakingSpan = import_telemetry.tracer.startSpan({
|
|
648
649
|
name: "user_speaking",
|
|
649
|
-
context: this.rootSpanContext,
|
|
650
|
-
startTime: lastSpeakingTime
|
|
650
|
+
context: (options == null ? void 0 : options.otelContext) ?? this.rootSpanContext,
|
|
651
|
+
startTime: options == null ? void 0 : options.lastSpeakingTime
|
|
651
652
|
});
|
|
652
653
|
const linked = (_a = this._roomIO) == null ? void 0 : _a.linkedParticipant;
|
|
653
654
|
if (linked) {
|
|
654
655
|
(0, import_utils3.setParticipantSpanAttributes)(this._userSpeakingSpan, linked);
|
|
655
656
|
}
|
|
656
657
|
} else if (this._userSpeakingSpan !== void 0) {
|
|
657
|
-
this._userSpeakingSpan.end(lastSpeakingTime);
|
|
658
|
+
this._userSpeakingSpan.end(options == null ? void 0 : options.lastSpeakingTime);
|
|
658
659
|
this._userSpeakingSpan = void 0;
|
|
659
660
|
}
|
|
660
661
|
const oldState = this._userState;
|
|
@@ -684,7 +685,7 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
684
685
|
}
|
|
685
686
|
_setUserAwayTimer() {
|
|
686
687
|
this._cancelUserAwayTimer();
|
|
687
|
-
if (this.
|
|
688
|
+
if (this.sessionOptions.userAwayTimeout === null || this.sessionOptions.userAwayTimeout === void 0) {
|
|
688
689
|
return;
|
|
689
690
|
}
|
|
690
691
|
if (this._roomIO && !this._roomIO.isParticipantAvailable) {
|
|
@@ -693,7 +694,7 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
693
694
|
this.userAwayTimer = setTimeout(() => {
|
|
694
695
|
this.logger.debug("User away timeout triggered");
|
|
695
696
|
this._updateUserState("away");
|
|
696
|
-
}, this.
|
|
697
|
+
}, this.sessionOptions.userAwayTimeout * 1e3);
|
|
697
698
|
}
|
|
698
699
|
_cancelUserAwayTimer() {
|
|
699
700
|
if (this.userAwayTimer !== null) {
|
|
@@ -758,8 +759,8 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
758
759
|
this.input.audio = null;
|
|
759
760
|
this.output.audio = null;
|
|
760
761
|
this.output.transcription = null;
|
|
761
|
-
await ((_b = this.
|
|
762
|
-
this.
|
|
762
|
+
await ((_b = this.sessionHost) == null ? void 0 : _b.close());
|
|
763
|
+
this.sessionHost = void 0;
|
|
763
764
|
await ((_c = this._roomIO) == null ? void 0 : _c.close());
|
|
764
765
|
this._roomIO = void 0;
|
|
765
766
|
await ((_d = this.activity) == null ? void 0 : _d.close());
|
|
@@ -783,13 +784,12 @@ class AgentSession extends import_node_events.EventEmitter {
|
|
|
783
784
|
this.rootSpanContext = void 0;
|
|
784
785
|
this.llmErrorCounts = 0;
|
|
785
786
|
this.ttsErrorCounts = 0;
|
|
786
|
-
this.interruptionDetectionErrorCounts = 0;
|
|
787
787
|
this.logger.info({ reason, error }, "AgentSession closed");
|
|
788
788
|
}
|
|
789
789
|
}
|
|
790
790
|
// Annotate the CommonJS export names for ESM import in node:
|
|
791
791
|
0 && (module.exports = {
|
|
792
792
|
AgentSession,
|
|
793
|
-
|
|
793
|
+
defaultAgentSessionOptions
|
|
794
794
|
});
|
|
795
795
|
//# sourceMappingURL=agent_session.cjs.map
|