@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.
- package/dist/index.cjs +6 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/inference_runner.cjs +38 -0
- package/dist/inference_runner.cjs.map +1 -0
- package/dist/inference_runner.d.ts +11 -0
- package/dist/inference_runner.d.ts.map +1 -0
- package/dist/inference_runner.js +14 -0
- package/dist/inference_runner.js.map +1 -0
- package/dist/ipc/index.cjs +23 -0
- package/dist/ipc/index.cjs.map +1 -0
- package/dist/ipc/index.d.ts +2 -0
- package/dist/ipc/index.d.ts.map +1 -0
- package/dist/ipc/index.js +2 -0
- package/dist/ipc/index.js.map +1 -0
- package/dist/ipc/inference_executor.cjs +17 -0
- package/dist/ipc/inference_executor.cjs.map +1 -0
- package/dist/ipc/inference_executor.d.ts +4 -0
- package/dist/ipc/inference_executor.d.ts.map +1 -0
- package/dist/ipc/inference_executor.js +1 -0
- package/dist/ipc/inference_executor.js.map +1 -0
- package/dist/ipc/inference_proc_executor.cjs +97 -0
- package/dist/ipc/inference_proc_executor.cjs.map +1 -0
- package/dist/ipc/inference_proc_executor.d.ts +23 -0
- package/dist/ipc/inference_proc_executor.d.ts.map +1 -0
- package/dist/ipc/inference_proc_executor.js +72 -0
- package/dist/ipc/inference_proc_executor.js.map +1 -0
- package/dist/ipc/inference_proc_lazy_main.cjs +90 -0
- package/dist/ipc/inference_proc_lazy_main.cjs.map +1 -0
- package/dist/ipc/inference_proc_lazy_main.d.ts +2 -0
- package/dist/ipc/inference_proc_lazy_main.d.ts.map +1 -0
- package/dist/ipc/inference_proc_lazy_main.js +67 -0
- package/dist/ipc/inference_proc_lazy_main.js.map +1 -0
- package/dist/ipc/job_executor.cjs +8 -7
- package/dist/ipc/job_executor.cjs.map +1 -1
- package/dist/ipc/job_executor.d.ts +14 -15
- package/dist/ipc/job_executor.d.ts.map +1 -1
- package/dist/ipc/job_executor.js +7 -6
- package/dist/ipc/job_executor.js.map +1 -1
- package/dist/ipc/job_proc_executor.cjs +108 -0
- package/dist/ipc/job_proc_executor.cjs.map +1 -0
- package/dist/ipc/job_proc_executor.d.ts +19 -0
- package/dist/ipc/job_proc_executor.d.ts.map +1 -0
- package/dist/ipc/job_proc_executor.js +83 -0
- package/dist/ipc/job_proc_executor.js.map +1 -0
- package/dist/ipc/{job_main.cjs → job_proc_lazy_main.cjs} +41 -36
- package/dist/ipc/job_proc_lazy_main.cjs.map +1 -0
- package/dist/ipc/job_proc_lazy_main.d.ts +2 -0
- package/dist/ipc/job_proc_lazy_main.d.ts.map +1 -0
- package/dist/ipc/{job_main.js → job_proc_lazy_main.js} +41 -11
- package/dist/ipc/job_proc_lazy_main.js.map +1 -0
- package/dist/ipc/message.cjs.map +1 -1
- package/dist/ipc/message.d.ts +17 -0
- package/dist/ipc/message.d.ts.map +1 -1
- package/dist/ipc/proc_pool.cjs +30 -4
- package/dist/ipc/proc_pool.cjs.map +1 -1
- package/dist/ipc/proc_pool.d.ts +5 -1
- package/dist/ipc/proc_pool.d.ts.map +1 -1
- package/dist/ipc/proc_pool.js +30 -4
- package/dist/ipc/proc_pool.js.map +1 -1
- package/dist/ipc/{proc_job_executor.cjs → supervised_proc.cjs} +58 -46
- package/dist/ipc/supervised_proc.cjs.map +1 -0
- package/dist/ipc/supervised_proc.d.ts +30 -0
- package/dist/ipc/supervised_proc.d.ts.map +1 -0
- package/dist/ipc/{proc_job_executor.js → supervised_proc.js} +54 -32
- package/dist/ipc/supervised_proc.js.map +1 -0
- package/dist/job.cjs +18 -1
- package/dist/job.cjs.map +1 -1
- package/dist/job.d.ts +9 -1
- package/dist/job.d.ts.map +1 -1
- package/dist/job.js +17 -1
- package/dist/job.js.map +1 -1
- package/dist/metrics/base.cjs +2 -2
- package/dist/metrics/base.cjs.map +1 -1
- package/dist/metrics/base.d.ts +1 -1
- package/dist/metrics/base.d.ts.map +1 -1
- package/dist/metrics/base.js +2 -2
- package/dist/metrics/base.js.map +1 -1
- package/dist/multimodal/agent_playout.cjs +13 -14
- package/dist/multimodal/agent_playout.cjs.map +1 -1
- package/dist/multimodal/agent_playout.d.ts +4 -4
- package/dist/multimodal/agent_playout.d.ts.map +1 -1
- package/dist/multimodal/agent_playout.js +13 -14
- package/dist/multimodal/agent_playout.js.map +1 -1
- package/dist/multimodal/multimodal_agent.cjs +12 -8
- package/dist/multimodal/multimodal_agent.cjs.map +1 -1
- package/dist/multimodal/multimodal_agent.d.ts.map +1 -1
- package/dist/multimodal/multimodal_agent.js +13 -9
- package/dist/multimodal/multimodal_agent.js.map +1 -1
- package/dist/pipeline/agent_output.cjs +20 -4
- package/dist/pipeline/agent_output.cjs.map +1 -1
- package/dist/pipeline/agent_output.d.ts +4 -2
- package/dist/pipeline/agent_output.d.ts.map +1 -1
- package/dist/pipeline/agent_output.js +20 -4
- package/dist/pipeline/agent_output.js.map +1 -1
- package/dist/pipeline/agent_playout.cjs +9 -3
- package/dist/pipeline/agent_playout.cjs.map +1 -1
- package/dist/pipeline/agent_playout.d.ts +4 -2
- package/dist/pipeline/agent_playout.d.ts.map +1 -1
- package/dist/pipeline/agent_playout.js +9 -3
- package/dist/pipeline/agent_playout.js.map +1 -1
- package/dist/pipeline/human_input.cjs +6 -0
- package/dist/pipeline/human_input.cjs.map +1 -1
- package/dist/pipeline/human_input.d.ts +3 -1
- package/dist/pipeline/human_input.d.ts.map +1 -1
- package/dist/pipeline/human_input.js +6 -0
- package/dist/pipeline/human_input.js.map +1 -1
- package/dist/pipeline/pipeline_agent.cjs +79 -12
- package/dist/pipeline/pipeline_agent.cjs.map +1 -1
- package/dist/pipeline/pipeline_agent.d.ts +8 -0
- package/dist/pipeline/pipeline_agent.d.ts.map +1 -1
- package/dist/pipeline/pipeline_agent.js +79 -12
- package/dist/pipeline/pipeline_agent.js.map +1 -1
- package/dist/stt/stream_adapter.cjs +16 -4
- package/dist/stt/stream_adapter.cjs.map +1 -1
- package/dist/stt/stream_adapter.d.ts.map +1 -1
- package/dist/stt/stream_adapter.js +16 -4
- package/dist/stt/stream_adapter.js.map +1 -1
- package/dist/tokenize/basic/basic.cjs +2 -0
- package/dist/tokenize/basic/basic.cjs.map +1 -1
- package/dist/tokenize/basic/basic.d.ts +2 -0
- package/dist/tokenize/basic/basic.d.ts.map +1 -1
- package/dist/tokenize/basic/basic.js +1 -0
- package/dist/tokenize/basic/basic.js.map +1 -1
- package/dist/tokenize/basic/index.cjs +2 -0
- package/dist/tokenize/basic/index.cjs.map +1 -1
- package/dist/tokenize/basic/index.d.ts +1 -1
- package/dist/tokenize/basic/index.d.ts.map +1 -1
- package/dist/tokenize/basic/index.js +8 -1
- package/dist/tokenize/basic/index.js.map +1 -1
- package/dist/tokenize/token_stream.cjs +5 -3
- package/dist/tokenize/token_stream.cjs.map +1 -1
- package/dist/tokenize/token_stream.d.ts.map +1 -1
- package/dist/tokenize/token_stream.js +5 -3
- package/dist/tokenize/token_stream.js.map +1 -1
- package/dist/transcription.cjs +203 -86
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.ts +24 -17
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +201 -85
- package/dist/transcription.js.map +1 -1
- package/dist/worker.cjs +42 -9
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.ts +5 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +42 -9
- package/dist/worker.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +3 -1
- package/src/inference_runner.ts +19 -0
- package/src/ipc/index.ts +5 -0
- package/src/ipc/inference_executor.ts +7 -0
- package/src/ipc/inference_proc_executor.ts +93 -0
- package/src/ipc/inference_proc_lazy_main.ts +86 -0
- package/src/ipc/job_executor.ts +15 -17
- package/src/ipc/job_proc_executor.ts +112 -0
- package/src/ipc/{job_main.ts → job_proc_lazy_main.ts} +44 -14
- package/src/ipc/message.ts +14 -1
- package/src/ipc/proc_pool.ts +33 -3
- package/src/ipc/{proc_job_executor.ts → supervised_proc.ts} +80 -30
- package/src/job.ts +21 -0
- package/src/metrics/base.ts +7 -10
- package/src/multimodal/agent_playout.ts +14 -16
- package/src/multimodal/multimodal_agent.ts +13 -9
- package/src/pipeline/agent_output.ts +34 -5
- package/src/pipeline/agent_playout.ts +10 -1
- package/src/pipeline/human_input.ts +8 -0
- package/src/pipeline/pipeline_agent.ts +96 -11
- package/src/stt/stream_adapter.ts +17 -5
- package/src/tokenize/basic/basic.ts +2 -0
- package/src/tokenize/basic/index.ts +7 -1
- package/src/tokenize/token_stream.ts +6 -3
- package/src/transcription.ts +270 -96
- package/src/worker.ts +42 -5
- package/dist/ipc/job_main.cjs.map +0 -1
- package/dist/ipc/job_main.d.ts +0 -8
- package/dist/ipc/job_main.d.ts.map +0 -1
- package/dist/ipc/job_main.js.map +0 -1
- package/dist/ipc/proc_job_executor.cjs.map +0 -1
- package/dist/ipc/proc_job_executor.d.ts +0 -15
- package/dist/ipc/proc_job_executor.d.ts.map +0 -1
- 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
|
+
})();
|
package/src/ipc/job_executor.ts
CHANGED
|
@@ -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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
31
|
-
|
|
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(() => {
|
package/src/ipc/message.ts
CHANGED
|
@@ -7,7 +7,12 @@ import type { LoggerOptions } from '../log.js';
|
|
|
7
7
|
export type IPCMessage =
|
|
8
8
|
| {
|
|
9
9
|
case: 'initializeRequest';
|
|
10
|
-
value: {
|
|
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 };
|
package/src/ipc/proc_pool.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
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);
|