@livekit/agents 0.6.3 → 0.7.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.
Files changed (185) hide show
  1. package/dist/index.cjs +6 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/inference_runner.cjs +38 -0
  8. package/dist/inference_runner.cjs.map +1 -0
  9. package/dist/inference_runner.d.ts +11 -0
  10. package/dist/inference_runner.d.ts.map +1 -0
  11. package/dist/inference_runner.js +14 -0
  12. package/dist/inference_runner.js.map +1 -0
  13. package/dist/ipc/index.cjs +23 -0
  14. package/dist/ipc/index.cjs.map +1 -0
  15. package/dist/ipc/index.d.ts +2 -0
  16. package/dist/ipc/index.d.ts.map +1 -0
  17. package/dist/ipc/index.js +2 -0
  18. package/dist/ipc/index.js.map +1 -0
  19. package/dist/ipc/inference_executor.cjs +17 -0
  20. package/dist/ipc/inference_executor.cjs.map +1 -0
  21. package/dist/ipc/inference_executor.d.ts +4 -0
  22. package/dist/ipc/inference_executor.d.ts.map +1 -0
  23. package/dist/ipc/inference_executor.js +1 -0
  24. package/dist/ipc/inference_executor.js.map +1 -0
  25. package/dist/ipc/inference_proc_executor.cjs +97 -0
  26. package/dist/ipc/inference_proc_executor.cjs.map +1 -0
  27. package/dist/ipc/inference_proc_executor.d.ts +23 -0
  28. package/dist/ipc/inference_proc_executor.d.ts.map +1 -0
  29. package/dist/ipc/inference_proc_executor.js +72 -0
  30. package/dist/ipc/inference_proc_executor.js.map +1 -0
  31. package/dist/ipc/inference_proc_lazy_main.cjs +90 -0
  32. package/dist/ipc/inference_proc_lazy_main.cjs.map +1 -0
  33. package/dist/ipc/inference_proc_lazy_main.d.ts +2 -0
  34. package/dist/ipc/inference_proc_lazy_main.d.ts.map +1 -0
  35. package/dist/ipc/inference_proc_lazy_main.js +67 -0
  36. package/dist/ipc/inference_proc_lazy_main.js.map +1 -0
  37. package/dist/ipc/job_executor.cjs +8 -7
  38. package/dist/ipc/job_executor.cjs.map +1 -1
  39. package/dist/ipc/job_executor.d.ts +14 -15
  40. package/dist/ipc/job_executor.d.ts.map +1 -1
  41. package/dist/ipc/job_executor.js +7 -6
  42. package/dist/ipc/job_executor.js.map +1 -1
  43. package/dist/ipc/job_proc_executor.cjs +108 -0
  44. package/dist/ipc/job_proc_executor.cjs.map +1 -0
  45. package/dist/ipc/job_proc_executor.d.ts +19 -0
  46. package/dist/ipc/job_proc_executor.d.ts.map +1 -0
  47. package/dist/ipc/job_proc_executor.js +83 -0
  48. package/dist/ipc/job_proc_executor.js.map +1 -0
  49. package/dist/ipc/{job_main.cjs → job_proc_lazy_main.cjs} +41 -36
  50. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -0
  51. package/dist/ipc/job_proc_lazy_main.d.ts +2 -0
  52. package/dist/ipc/job_proc_lazy_main.d.ts.map +1 -0
  53. package/dist/ipc/{job_main.js → job_proc_lazy_main.js} +41 -11
  54. package/dist/ipc/job_proc_lazy_main.js.map +1 -0
  55. package/dist/ipc/message.cjs.map +1 -1
  56. package/dist/ipc/message.d.ts +17 -0
  57. package/dist/ipc/message.d.ts.map +1 -1
  58. package/dist/ipc/proc_pool.cjs +30 -4
  59. package/dist/ipc/proc_pool.cjs.map +1 -1
  60. package/dist/ipc/proc_pool.d.ts +5 -1
  61. package/dist/ipc/proc_pool.d.ts.map +1 -1
  62. package/dist/ipc/proc_pool.js +30 -4
  63. package/dist/ipc/proc_pool.js.map +1 -1
  64. package/dist/ipc/{proc_job_executor.cjs → supervised_proc.cjs} +58 -46
  65. package/dist/ipc/supervised_proc.cjs.map +1 -0
  66. package/dist/ipc/supervised_proc.d.ts +30 -0
  67. package/dist/ipc/supervised_proc.d.ts.map +1 -0
  68. package/dist/ipc/{proc_job_executor.js → supervised_proc.js} +54 -32
  69. package/dist/ipc/supervised_proc.js.map +1 -0
  70. package/dist/job.cjs +18 -1
  71. package/dist/job.cjs.map +1 -1
  72. package/dist/job.d.ts +9 -1
  73. package/dist/job.d.ts.map +1 -1
  74. package/dist/job.js +17 -1
  75. package/dist/job.js.map +1 -1
  76. package/dist/metrics/base.cjs +2 -2
  77. package/dist/metrics/base.cjs.map +1 -1
  78. package/dist/metrics/base.d.ts +1 -1
  79. package/dist/metrics/base.d.ts.map +1 -1
  80. package/dist/metrics/base.js +2 -2
  81. package/dist/metrics/base.js.map +1 -1
  82. package/dist/multimodal/agent_playout.cjs +13 -14
  83. package/dist/multimodal/agent_playout.cjs.map +1 -1
  84. package/dist/multimodal/agent_playout.d.ts +4 -4
  85. package/dist/multimodal/agent_playout.d.ts.map +1 -1
  86. package/dist/multimodal/agent_playout.js +13 -14
  87. package/dist/multimodal/agent_playout.js.map +1 -1
  88. package/dist/multimodal/multimodal_agent.cjs +12 -8
  89. package/dist/multimodal/multimodal_agent.cjs.map +1 -1
  90. package/dist/multimodal/multimodal_agent.d.ts.map +1 -1
  91. package/dist/multimodal/multimodal_agent.js +13 -9
  92. package/dist/multimodal/multimodal_agent.js.map +1 -1
  93. package/dist/pipeline/agent_output.cjs +20 -4
  94. package/dist/pipeline/agent_output.cjs.map +1 -1
  95. package/dist/pipeline/agent_output.d.ts +4 -2
  96. package/dist/pipeline/agent_output.d.ts.map +1 -1
  97. package/dist/pipeline/agent_output.js +20 -4
  98. package/dist/pipeline/agent_output.js.map +1 -1
  99. package/dist/pipeline/agent_playout.cjs +9 -3
  100. package/dist/pipeline/agent_playout.cjs.map +1 -1
  101. package/dist/pipeline/agent_playout.d.ts +4 -2
  102. package/dist/pipeline/agent_playout.d.ts.map +1 -1
  103. package/dist/pipeline/agent_playout.js +9 -3
  104. package/dist/pipeline/agent_playout.js.map +1 -1
  105. package/dist/pipeline/human_input.cjs +6 -0
  106. package/dist/pipeline/human_input.cjs.map +1 -1
  107. package/dist/pipeline/human_input.d.ts +3 -1
  108. package/dist/pipeline/human_input.d.ts.map +1 -1
  109. package/dist/pipeline/human_input.js +6 -0
  110. package/dist/pipeline/human_input.js.map +1 -1
  111. package/dist/pipeline/pipeline_agent.cjs +79 -12
  112. package/dist/pipeline/pipeline_agent.cjs.map +1 -1
  113. package/dist/pipeline/pipeline_agent.d.ts +8 -0
  114. package/dist/pipeline/pipeline_agent.d.ts.map +1 -1
  115. package/dist/pipeline/pipeline_agent.js +79 -12
  116. package/dist/pipeline/pipeline_agent.js.map +1 -1
  117. package/dist/stt/stream_adapter.cjs +16 -4
  118. package/dist/stt/stream_adapter.cjs.map +1 -1
  119. package/dist/stt/stream_adapter.d.ts.map +1 -1
  120. package/dist/stt/stream_adapter.js +16 -4
  121. package/dist/stt/stream_adapter.js.map +1 -1
  122. package/dist/tokenize/basic/basic.cjs +2 -0
  123. package/dist/tokenize/basic/basic.cjs.map +1 -1
  124. package/dist/tokenize/basic/basic.d.ts +2 -0
  125. package/dist/tokenize/basic/basic.d.ts.map +1 -1
  126. package/dist/tokenize/basic/basic.js +1 -0
  127. package/dist/tokenize/basic/basic.js.map +1 -1
  128. package/dist/tokenize/basic/index.cjs +2 -0
  129. package/dist/tokenize/basic/index.cjs.map +1 -1
  130. package/dist/tokenize/basic/index.d.ts +1 -1
  131. package/dist/tokenize/basic/index.d.ts.map +1 -1
  132. package/dist/tokenize/basic/index.js +8 -1
  133. package/dist/tokenize/basic/index.js.map +1 -1
  134. package/dist/tokenize/token_stream.cjs +5 -3
  135. package/dist/tokenize/token_stream.cjs.map +1 -1
  136. package/dist/tokenize/token_stream.d.ts.map +1 -1
  137. package/dist/tokenize/token_stream.js +5 -3
  138. package/dist/tokenize/token_stream.js.map +1 -1
  139. package/dist/transcription.cjs +203 -86
  140. package/dist/transcription.cjs.map +1 -1
  141. package/dist/transcription.d.ts +24 -17
  142. package/dist/transcription.d.ts.map +1 -1
  143. package/dist/transcription.js +201 -85
  144. package/dist/transcription.js.map +1 -1
  145. package/dist/worker.cjs +42 -9
  146. package/dist/worker.cjs.map +1 -1
  147. package/dist/worker.d.ts +5 -1
  148. package/dist/worker.d.ts.map +1 -1
  149. package/dist/worker.js +42 -9
  150. package/dist/worker.js.map +1 -1
  151. package/package.json +3 -3
  152. package/src/index.ts +3 -1
  153. package/src/inference_runner.ts +19 -0
  154. package/src/ipc/index.ts +5 -0
  155. package/src/ipc/inference_executor.ts +7 -0
  156. package/src/ipc/inference_proc_executor.ts +93 -0
  157. package/src/ipc/inference_proc_lazy_main.ts +86 -0
  158. package/src/ipc/job_executor.ts +15 -17
  159. package/src/ipc/job_proc_executor.ts +112 -0
  160. package/src/ipc/{job_main.ts → job_proc_lazy_main.ts} +44 -14
  161. package/src/ipc/message.ts +14 -1
  162. package/src/ipc/proc_pool.ts +33 -3
  163. package/src/ipc/{proc_job_executor.ts → supervised_proc.ts} +80 -30
  164. package/src/job.ts +21 -0
  165. package/src/metrics/base.ts +7 -10
  166. package/src/multimodal/agent_playout.ts +14 -16
  167. package/src/multimodal/multimodal_agent.ts +13 -9
  168. package/src/pipeline/agent_output.ts +34 -5
  169. package/src/pipeline/agent_playout.ts +10 -1
  170. package/src/pipeline/human_input.ts +8 -0
  171. package/src/pipeline/pipeline_agent.ts +96 -11
  172. package/src/stt/stream_adapter.ts +17 -5
  173. package/src/tokenize/basic/basic.ts +2 -0
  174. package/src/tokenize/basic/index.ts +7 -1
  175. package/src/tokenize/token_stream.ts +6 -3
  176. package/src/transcription.ts +270 -96
  177. package/src/worker.ts +42 -5
  178. package/dist/ipc/job_main.cjs.map +0 -1
  179. package/dist/ipc/job_main.d.ts +0 -8
  180. package/dist/ipc/job_main.d.ts.map +0 -1
  181. package/dist/ipc/job_main.js.map +0 -1
  182. package/dist/ipc/proc_job_executor.cjs.map +0 -1
  183. package/dist/ipc/proc_job_executor.d.ts +0 -15
  184. package/dist/ipc/proc_job_executor.d.ts.map +0 -1
  185. package/dist/ipc/proc_job_executor.js.map +0 -1
@@ -0,0 +1,93 @@
1
+ // SPDX-FileCopyrightText: 2025 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import type { ChildProcess } from 'node:child_process';
5
+ import { fork } from 'node:child_process';
6
+ import { randomUUID } from 'node:crypto';
7
+ import { log } from '../log.js';
8
+ import type { InferenceExecutor } from './inference_executor.js';
9
+ import type { IPCMessage } from './message.js';
10
+ import { SupervisedProc } from './supervised_proc.js';
11
+
12
+ class PendingInference {
13
+ promise = new Promise<{ requestId: string; data: unknown; error?: Error }>((resolve) => {
14
+ this.resolve = resolve;
15
+ });
16
+ resolve(arg: { requestId: string; data: unknown; error?: Error }) {
17
+ arg;
18
+ }
19
+ }
20
+
21
+ export class InferenceProcExecutor extends SupervisedProc implements InferenceExecutor {
22
+ #runners: { [id: string]: string };
23
+ #activeRequests: { [id: string]: PendingInference } = {};
24
+ #logger = log();
25
+
26
+ constructor({
27
+ runners,
28
+ initializeTimeout,
29
+ closeTimeout,
30
+ memoryWarnMB,
31
+ memoryLimitMB,
32
+ pingInterval,
33
+ pingTimeout,
34
+ highPingThreshold,
35
+ }: {
36
+ runners: { [id: string]: string };
37
+ initializeTimeout: number;
38
+ closeTimeout: number;
39
+ memoryWarnMB: number;
40
+ memoryLimitMB: number;
41
+ pingInterval: number;
42
+ pingTimeout: number;
43
+ highPingThreshold: number;
44
+ }) {
45
+ super(
46
+ initializeTimeout,
47
+ closeTimeout,
48
+ memoryWarnMB,
49
+ memoryLimitMB,
50
+ pingInterval,
51
+ pingTimeout,
52
+ highPingThreshold,
53
+ );
54
+ this.#runners = runners;
55
+ }
56
+
57
+ createProcess(): ChildProcess {
58
+ return fork(new URL(import.meta.resolve('./inference_proc_lazy_main.js')), [
59
+ JSON.stringify(this.#runners),
60
+ ]);
61
+ }
62
+
63
+ async mainTask(proc: ChildProcess) {
64
+ proc.on('message', (msg: IPCMessage) => {
65
+ switch (msg.case) {
66
+ case 'inferenceResponse':
67
+ const res = this.#activeRequests[msg.value.requestId];
68
+ delete this.#activeRequests[msg.value.requestId];
69
+ if (!res) {
70
+ this.#logger
71
+ .child({ requestId: msg.value.requestId })
72
+ .warn('received unexpected inference response');
73
+ return;
74
+ }
75
+
76
+ res.resolve(msg.value);
77
+ }
78
+ });
79
+ }
80
+
81
+ async doInference(method: string, data: unknown): Promise<unknown> {
82
+ const requestId = 'inference_req_' + randomUUID();
83
+ const fut = new PendingInference();
84
+ this.proc!.send({ case: 'inferenceRequest', value: { requestId, method, data } });
85
+ this.#activeRequests[requestId] = fut;
86
+
87
+ const res = await fut.promise;
88
+ if (res.error) {
89
+ throw new Error(`inference of ${method} failed: ${res.error}`);
90
+ }
91
+ return res.data;
92
+ }
93
+ }
@@ -0,0 +1,86 @@
1
+ // SPDX-FileCopyrightText: 2025 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import EventEmitter, { once } from 'node:events';
5
+ import type { InferenceRunner } from '../inference_runner.js';
6
+ import { initializeLogger, log } from '../log.js';
7
+ import type { IPCMessage } from './message.js';
8
+
9
+ const ORPHANED_TIMEOUT = 15 * 1000;
10
+
11
+ (async () => {
12
+ if (process.send) {
13
+ // don't do anything on C-c
14
+ // this is handled in cli, triggering a termination of all child processes at once.
15
+ process.on('SIGINT', () => {});
16
+
17
+ await once(process, 'message').then(([msg]: IPCMessage[]) => {
18
+ msg = msg!;
19
+ if (msg.case !== 'initializeRequest') {
20
+ throw new Error('first message must be InitializeRequest');
21
+ }
22
+ initializeLogger(msg.value.loggerOptions);
23
+ });
24
+ const logger = log().child({ pid: process.pid });
25
+
26
+ const runners: { [id: string]: InferenceRunner } = await Promise.all(
27
+ Object.entries(JSON.parse(process.argv[2]!)).map(async ([k, v]) => {
28
+ return [k, await import(v as string).then((m) => new m.default())];
29
+ }),
30
+ ).then(Object.fromEntries);
31
+
32
+ await Promise.all(
33
+ Object.entries(runners).map(async ([runner, v]) => {
34
+ logger.child({ runner }).debug('initializing inference runner');
35
+ await v.initialize();
36
+ }),
37
+ );
38
+ logger.debug('all inference runners initialized');
39
+ process.send({ case: 'initializeResponse' });
40
+
41
+ const closeEvent = new EventEmitter();
42
+
43
+ const orphanedTimeout = setTimeout(() => {
44
+ logger.warn('process orphaned, shutting down');
45
+ process.exit();
46
+ }, ORPHANED_TIMEOUT);
47
+
48
+ const handleInferenceRequest = async ({
49
+ method,
50
+ requestId,
51
+ data,
52
+ }: {
53
+ method: string;
54
+ requestId: string;
55
+ data: unknown;
56
+ }) => {
57
+ if (!runners[method]) {
58
+ logger.child({ method }).warn('unknown inference method');
59
+ }
60
+
61
+ try {
62
+ const resp = await runners[method]!.run(data);
63
+ process.send!({ case: 'inferenceResponse', value: { requestId, data: resp } });
64
+ } catch (error) {
65
+ process.send!({ case: 'inferenceResponse', value: { requestId, error } });
66
+ }
67
+ };
68
+
69
+ process.on('message', (msg: IPCMessage) => {
70
+ switch (msg.case) {
71
+ case 'pingRequest':
72
+ orphanedTimeout.refresh();
73
+ process.send!({
74
+ case: 'pongResponse',
75
+ value: { lastTimestamp: msg.value.timestamp, timestamp: Date.now() },
76
+ });
77
+ break;
78
+ case 'shutdownRequest':
79
+ closeEvent.emit('close');
80
+ break;
81
+ case 'inferenceRequest':
82
+ handleInferenceRequest(msg.value);
83
+ }
84
+ });
85
+ }
86
+ })();
@@ -3,23 +3,21 @@
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import type { RunningJobInfo } from '../job.js';
5
5
 
6
- export interface ProcOpts {
7
- agent: string;
8
- initializeTimeout: number;
9
- closeTimeout: number;
10
- }
11
-
12
- export abstract class JobExecutor {
13
- PING_INTERVAL = 2.5 * 1000;
14
- PING_TIMEOUT = 90 * 1000;
15
- HIGH_PING_THRESHOLD = 0.5 * 1000;
6
+ export interface JobExecutor {
7
+ started: boolean;
8
+ userArguments: any;
9
+ runningJob: RunningJobInfo | undefined;
10
+ status: JobStatus;
16
11
 
17
- abstract get started(): boolean;
18
- abstract get runningJob(): RunningJobInfo | undefined;
12
+ start(): Promise<void>;
13
+ join(): Promise<void>;
14
+ initialize(): Promise<void>;
15
+ close(): Promise<void>;
16
+ launchJob(info: RunningJobInfo): Promise<void>;
17
+ }
19
18
 
20
- abstract start(): Promise<void>;
21
- abstract join(): Promise<void>;
22
- abstract initialize(): Promise<void>;
23
- abstract close(): Promise<void>;
24
- abstract launchJob(info: RunningJobInfo): Promise<void>;
19
+ export enum JobStatus {
20
+ RUNNING,
21
+ FAILED,
22
+ SUCCESS,
25
23
  }
@@ -0,0 +1,112 @@
1
+ // SPDX-FileCopyrightText: 2025 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import type { ChildProcess } from 'node:child_process';
5
+ import { fork } from 'node:child_process';
6
+ import type { RunningJobInfo } from '../job.js';
7
+ import { log } from '../log.js';
8
+ import type { InferenceExecutor } from './inference_executor.js';
9
+ import type { JobExecutor } from './job_executor.js';
10
+ import { JobStatus } from './job_executor.js';
11
+ import type { IPCMessage } from './message.js';
12
+ import { SupervisedProc } from './supervised_proc.js';
13
+
14
+ export class JobProcExecutor extends SupervisedProc implements JobExecutor {
15
+ #userArgs?: any;
16
+ #jobStatus?: JobStatus;
17
+ #runningJob?: RunningJobInfo;
18
+ #agent: string;
19
+ #inferenceExecutor?: InferenceExecutor;
20
+ #inferenceTasks: Promise<void>[] = [];
21
+ #logger = log();
22
+
23
+ constructor(
24
+ agent: string,
25
+ inferenceExecutor: InferenceExecutor | undefined,
26
+ initializeTimeout: number,
27
+ closeTimeout: number,
28
+ memoryWarnMB: number,
29
+ memoryLimitMB: number,
30
+ pingInterval: number,
31
+ pingTimeout: number,
32
+ highPingThreshold: number,
33
+ ) {
34
+ super(
35
+ initializeTimeout,
36
+ closeTimeout,
37
+ memoryWarnMB,
38
+ memoryLimitMB,
39
+ pingInterval,
40
+ pingTimeout,
41
+ highPingThreshold,
42
+ );
43
+ this.#agent = agent;
44
+ this.#inferenceExecutor = inferenceExecutor;
45
+ }
46
+
47
+ get status(): JobStatus {
48
+ if (this.#jobStatus) {
49
+ return this.#jobStatus;
50
+ }
51
+ throw new Error('job status not available');
52
+ }
53
+
54
+ get userArguments(): any {
55
+ return this.#userArgs;
56
+ }
57
+
58
+ set userArguments(args: any) {
59
+ this.#userArgs = args;
60
+ }
61
+
62
+ get runningJob(): RunningJobInfo | undefined {
63
+ return this.#runningJob;
64
+ }
65
+
66
+ createProcess(): ChildProcess {
67
+ return fork(new URL(import.meta.resolve('./job_proc_lazy_main.js')), [this.#agent]);
68
+ }
69
+
70
+ async mainTask(proc: ChildProcess) {
71
+ proc.on('message', (msg: IPCMessage) => {
72
+ switch (msg.case) {
73
+ case 'inferenceRequest':
74
+ this.#inferenceTasks.push(this.#doInferenceTask(proc, msg.value));
75
+ }
76
+ });
77
+ }
78
+
79
+ async #doInferenceTask(
80
+ proc: ChildProcess,
81
+ req: { method: string; requestId: string; data: unknown },
82
+ ) {
83
+ if (!this.#inferenceExecutor) {
84
+ this.#logger.warn('inference request received but no inference executor');
85
+ proc.send({
86
+ case: 'inferenceResponse',
87
+ value: { requestId: req.requestId, error: new Error('no inference executor') },
88
+ });
89
+ return;
90
+ }
91
+
92
+ try {
93
+ const data = await this.#inferenceExecutor.doInference(req.method, req.data);
94
+ proc.send({ case: 'inferenceResponse', value: { requestId: req.requestId, data } });
95
+ } catch (error) {
96
+ proc.send({ case: 'inferenceResponse', value: { requestId: req.requestId, error } });
97
+ }
98
+ }
99
+
100
+ async launchJob(info: RunningJobInfo) {
101
+ if (this.#runningJob) {
102
+ throw Error('process already has a running job');
103
+ }
104
+ if (!this.init.done) {
105
+ throw Error('process not initialized');
106
+ }
107
+ this.#jobStatus = JobStatus.RUNNING;
108
+ this.#runningJob = info;
109
+
110
+ this.proc!.send({ case: 'startJobRequest', value: { runningJob: info } });
111
+ }
112
+ }
@@ -2,34 +2,63 @@
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
4
  import { Room, RoomEvent } from '@livekit/rtc-node';
5
- import type { ChildProcess } from 'node:child_process';
6
- import { fork } from 'node:child_process';
5
+ import { randomUUID } from 'node:crypto';
7
6
  import { EventEmitter, once } from 'node:events';
8
7
  import { pathToFileURL } from 'node:url';
9
8
  import type { Logger } from 'pino';
10
9
  import { type Agent, isAgent } from '../generator.js';
11
- import type { RunningJobInfo } from '../job.js';
12
- import { JobContext } from '../job.js';
13
- import { JobProcess } from '../job.js';
10
+ import { CurrentJobContext, JobContext, JobProcess, type RunningJobInfo } from '../job.js';
14
11
  import { initializeLogger, log } from '../log.js';
15
12
  import { defaultInitializeProcessFunc } from '../worker.js';
13
+ import type { InferenceExecutor } from './inference_executor.js';
16
14
  import type { IPCMessage } from './message.js';
17
15
 
18
16
  const ORPHANED_TIMEOUT = 15 * 1000;
19
17
 
20
- type StartArgs = {
21
- agentFile: string;
22
- // userArguments: unknown;
23
- };
24
-
25
18
  type JobTask = {
26
19
  ctx: JobContext;
27
20
  task: Promise<void>;
28
21
  };
29
22
 
30
- export const runProcess = (args: StartArgs): ChildProcess => {
31
- return fork(new URL(import.meta.url), [args.agentFile]);
32
- };
23
+ class PendingInference {
24
+ promise = new Promise<{ requestId: string; data: unknown; error?: Error }>((resolve) => {
25
+ this.resolve = resolve; // this is how JavaScript lets you resolve promises externally
26
+ });
27
+ resolve(arg: { requestId: string; data: unknown; error?: Error }) {
28
+ arg; // useless call to counteract TypeScript E6133
29
+ }
30
+ }
31
+
32
+ class InfClient implements InferenceExecutor {
33
+ #requests: { [id: string]: PendingInference } = {};
34
+
35
+ constructor() {
36
+ process.on('message', (msg: IPCMessage) => {
37
+ switch (msg.case) {
38
+ case 'inferenceResponse':
39
+ const fut = this.#requests[msg.value.requestId];
40
+ delete this.#requests[msg.value.requestId];
41
+ if (!fut) {
42
+ log().child({ resp: msg.value }).warn('received unexpected inference response');
43
+ return;
44
+ }
45
+ fut.resolve(msg.value);
46
+ break;
47
+ }
48
+ });
49
+ }
50
+
51
+ async doInference(method: string, data: unknown): Promise<unknown> {
52
+ const requestId = 'inference_job_' + randomUUID;
53
+ process.send!({ case: 'inferenceRequest', value: { requestId, method, data } });
54
+ this.#requests[requestId] = new PendingInference();
55
+ const resp = await this.#requests[requestId]!.promise;
56
+ if (resp.error) {
57
+ throw new Error(`inference of ${method} failed: ${resp.error.message}`);
58
+ }
59
+ return resp.data;
60
+ }
61
+ }
33
62
 
34
63
  const startJob = (
35
64
  proc: JobProcess,
@@ -54,7 +83,8 @@ const startJob = (
54
83
  closeEvent.emit('close', true, reason);
55
84
  };
56
85
 
57
- const ctx = new JobContext(proc, info, room, onConnect, onShutdown);
86
+ const ctx = new JobContext(proc, info, room, onConnect, onShutdown, new InfClient());
87
+ new CurrentJobContext(ctx);
58
88
 
59
89
  const task = new Promise<void>(async () => {
60
90
  const unconnectedTimeout = setTimeout(() => {
@@ -7,7 +7,12 @@ import type { LoggerOptions } from '../log.js';
7
7
  export type IPCMessage =
8
8
  | {
9
9
  case: 'initializeRequest';
10
- value: { loggerOptions: LoggerOptions };
10
+ value: {
11
+ loggerOptions: LoggerOptions;
12
+ pingInterval?: number;
13
+ pingTimeout?: number;
14
+ highPingThreshold?: number;
15
+ };
11
16
  }
12
17
  | {
13
18
  case: 'initializeResponse';
@@ -29,6 +34,14 @@ export type IPCMessage =
29
34
  case: 'shutdownRequest';
30
35
  value: { reason?: string };
31
36
  }
37
+ | {
38
+ case: 'inferenceRequest';
39
+ value: { method: string; requestId: string; data: unknown };
40
+ }
41
+ | {
42
+ case: 'inferenceResponse';
43
+ value: { requestId: string; data: unknown; error?: Error };
44
+ }
32
45
  | {
33
46
  case: 'exiting';
34
47
  value: { reason?: string };
@@ -4,8 +4,9 @@
4
4
  import { MultiMutex, Mutex } from '@livekit/mutex';
5
5
  import type { RunningJobInfo } from '../job.js';
6
6
  import { Queue } from '../utils.js';
7
+ import type { InferenceExecutor } from './inference_executor.js';
7
8
  import type { JobExecutor } from './job_executor.js';
8
- import { ProcJobExecutor } from './proc_job_executor.js';
9
+ import { JobProcExecutor } from './job_proc_executor.js';
9
10
 
10
11
  export class ProcPool {
11
12
  agent: string;
@@ -20,12 +21,18 @@ export class ProcPool {
20
21
  procMutex?: MultiMutex;
21
22
  procUnlock?: () => void;
22
23
  warmedProcQueue = new Queue<JobExecutor>();
24
+ inferenceExecutor?: InferenceExecutor;
25
+ memoryWarnMB: number;
26
+ memoryLimitMB: number;
23
27
 
24
28
  constructor(
25
29
  agent: string,
26
30
  numIdleProcesses: number,
27
31
  initializeTimeout: number,
28
32
  closeTimeout: number,
33
+ inferenceExecutor: InferenceExecutor | undefined,
34
+ memoryWarnMB: number,
35
+ memoryLimitMB: number,
29
36
  ) {
30
37
  this.agent = agent;
31
38
  if (numIdleProcesses > 0) {
@@ -33,6 +40,9 @@ export class ProcPool {
33
40
  }
34
41
  this.initializeTimeout = initializeTimeout;
35
42
  this.closeTimeout = closeTimeout;
43
+ this.inferenceExecutor = inferenceExecutor;
44
+ this.memoryWarnMB = memoryWarnMB;
45
+ this.memoryLimitMB = memoryLimitMB;
36
46
  }
37
47
 
38
48
  get processes(): JobExecutor[] {
@@ -52,7 +62,17 @@ export class ProcPool {
52
62
  this.procUnlock = undefined;
53
63
  }
54
64
  } else {
55
- proc = new ProcJobExecutor(this.agent, this.initializeTimeout, this.closeTimeout);
65
+ proc = new JobProcExecutor(
66
+ this.agent,
67
+ this.inferenceExecutor,
68
+ this.initializeTimeout,
69
+ this.closeTimeout,
70
+ this.memoryWarnMB,
71
+ this.memoryLimitMB,
72
+ 2500,
73
+ 60000,
74
+ 500,
75
+ );
56
76
  this.executors.push(proc);
57
77
  await proc.start();
58
78
  await proc.initialize();
@@ -61,7 +81,17 @@ export class ProcPool {
61
81
  }
62
82
 
63
83
  async procWatchTask() {
64
- const proc = new ProcJobExecutor(this.agent, this.initializeTimeout, this.closeTimeout);
84
+ const proc = new JobProcExecutor(
85
+ this.agent,
86
+ this.inferenceExecutor,
87
+ this.initializeTimeout,
88
+ this.closeTimeout,
89
+ this.memoryWarnMB,
90
+ this.memoryLimitMB,
91
+ 2500,
92
+ 60000,
93
+ 500,
94
+ );
65
95
 
66
96
  try {
67
97
  this.executors.push(proc);