@livekit/agents 1.0.47 → 1.0.49

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 (151) hide show
  1. package/dist/beta/index.cjs +29 -0
  2. package/dist/beta/index.cjs.map +1 -0
  3. package/dist/beta/index.d.cts +2 -0
  4. package/dist/beta/index.d.ts +2 -0
  5. package/dist/beta/index.d.ts.map +1 -0
  6. package/dist/beta/index.js +7 -0
  7. package/dist/beta/index.js.map +1 -0
  8. package/dist/beta/workflows/index.cjs +29 -0
  9. package/dist/beta/workflows/index.cjs.map +1 -0
  10. package/dist/beta/workflows/index.d.cts +2 -0
  11. package/dist/beta/workflows/index.d.ts +2 -0
  12. package/dist/beta/workflows/index.d.ts.map +1 -0
  13. package/dist/beta/workflows/index.js +7 -0
  14. package/dist/beta/workflows/index.js.map +1 -0
  15. package/dist/beta/workflows/task_group.cjs +162 -0
  16. package/dist/beta/workflows/task_group.cjs.map +1 -0
  17. package/dist/beta/workflows/task_group.d.cts +32 -0
  18. package/dist/beta/workflows/task_group.d.ts +32 -0
  19. package/dist/beta/workflows/task_group.d.ts.map +1 -0
  20. package/dist/beta/workflows/task_group.js +138 -0
  21. package/dist/beta/workflows/task_group.js.map +1 -0
  22. package/dist/cpu.cjs +189 -0
  23. package/dist/cpu.cjs.map +1 -0
  24. package/dist/cpu.d.cts +24 -0
  25. package/dist/cpu.d.ts +24 -0
  26. package/dist/cpu.d.ts.map +1 -0
  27. package/dist/cpu.js +152 -0
  28. package/dist/cpu.js.map +1 -0
  29. package/dist/cpu.test.cjs +227 -0
  30. package/dist/cpu.test.cjs.map +1 -0
  31. package/dist/cpu.test.js +204 -0
  32. package/dist/cpu.test.js.map +1 -0
  33. package/dist/index.cjs +3 -0
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +2 -1
  36. package/dist/index.d.ts +2 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/inference/api_protos.d.cts +59 -59
  41. package/dist/inference/api_protos.d.ts +59 -59
  42. package/dist/inference/llm.cjs.map +1 -1
  43. package/dist/inference/llm.d.cts +1 -1
  44. package/dist/inference/llm.d.ts +1 -1
  45. package/dist/inference/llm.d.ts.map +1 -1
  46. package/dist/inference/llm.js.map +1 -1
  47. package/dist/inference/tts.cjs.map +1 -1
  48. package/dist/inference/tts.d.cts +6 -0
  49. package/dist/inference/tts.d.ts +6 -0
  50. package/dist/inference/tts.d.ts.map +1 -1
  51. package/dist/inference/tts.js.map +1 -1
  52. package/dist/llm/chat_context.cjs +89 -1
  53. package/dist/llm/chat_context.cjs.map +1 -1
  54. package/dist/llm/chat_context.d.cts +10 -1
  55. package/dist/llm/chat_context.d.ts +10 -1
  56. package/dist/llm/chat_context.d.ts.map +1 -1
  57. package/dist/llm/chat_context.js +89 -1
  58. package/dist/llm/chat_context.js.map +1 -1
  59. package/dist/llm/chat_context.test.cjs +43 -0
  60. package/dist/llm/chat_context.test.cjs.map +1 -1
  61. package/dist/llm/chat_context.test.js +43 -0
  62. package/dist/llm/chat_context.test.js.map +1 -1
  63. package/dist/llm/index.cjs +2 -0
  64. package/dist/llm/index.cjs.map +1 -1
  65. package/dist/llm/index.d.cts +1 -1
  66. package/dist/llm/index.d.ts +1 -1
  67. package/dist/llm/index.d.ts.map +1 -1
  68. package/dist/llm/index.js +3 -1
  69. package/dist/llm/index.js.map +1 -1
  70. package/dist/llm/provider_format/index.d.cts +1 -1
  71. package/dist/llm/provider_format/index.d.ts +1 -1
  72. package/dist/llm/tool_context.cjs +7 -0
  73. package/dist/llm/tool_context.cjs.map +1 -1
  74. package/dist/llm/tool_context.d.cts +10 -2
  75. package/dist/llm/tool_context.d.ts +10 -2
  76. package/dist/llm/tool_context.d.ts.map +1 -1
  77. package/dist/llm/tool_context.js +6 -0
  78. package/dist/llm/tool_context.js.map +1 -1
  79. package/dist/utils.cjs +1 -0
  80. package/dist/utils.cjs.map +1 -1
  81. package/dist/utils.d.ts.map +1 -1
  82. package/dist/utils.js +1 -0
  83. package/dist/utils.js.map +1 -1
  84. package/dist/version.cjs +1 -1
  85. package/dist/version.js +1 -1
  86. package/dist/voice/agent.cjs +9 -0
  87. package/dist/voice/agent.cjs.map +1 -1
  88. package/dist/voice/agent.d.cts +1 -0
  89. package/dist/voice/agent.d.ts +1 -0
  90. package/dist/voice/agent.d.ts.map +1 -1
  91. package/dist/voice/agent.js +9 -0
  92. package/dist/voice/agent.js.map +1 -1
  93. package/dist/voice/agent_activity.cjs +67 -16
  94. package/dist/voice/agent_activity.cjs.map +1 -1
  95. package/dist/voice/agent_activity.d.cts +7 -0
  96. package/dist/voice/agent_activity.d.ts +7 -0
  97. package/dist/voice/agent_activity.d.ts.map +1 -1
  98. package/dist/voice/agent_activity.js +68 -17
  99. package/dist/voice/agent_activity.js.map +1 -1
  100. package/dist/voice/agent_session.cjs +27 -1
  101. package/dist/voice/agent_session.cjs.map +1 -1
  102. package/dist/voice/agent_session.d.cts +6 -0
  103. package/dist/voice/agent_session.d.ts +6 -0
  104. package/dist/voice/agent_session.d.ts.map +1 -1
  105. package/dist/voice/agent_session.js +27 -1
  106. package/dist/voice/agent_session.js.map +1 -1
  107. package/dist/voice/room_io/room_io.cjs +11 -2
  108. package/dist/voice/room_io/room_io.cjs.map +1 -1
  109. package/dist/voice/room_io/room_io.d.ts.map +1 -1
  110. package/dist/voice/room_io/room_io.js +12 -3
  111. package/dist/voice/room_io/room_io.js.map +1 -1
  112. package/dist/voice/testing/fake_llm.cjs +127 -0
  113. package/dist/voice/testing/fake_llm.cjs.map +1 -0
  114. package/dist/voice/testing/fake_llm.d.cts +30 -0
  115. package/dist/voice/testing/fake_llm.d.ts +30 -0
  116. package/dist/voice/testing/fake_llm.d.ts.map +1 -0
  117. package/dist/voice/testing/fake_llm.js +103 -0
  118. package/dist/voice/testing/fake_llm.js.map +1 -0
  119. package/dist/voice/testing/index.cjs +3 -0
  120. package/dist/voice/testing/index.cjs.map +1 -1
  121. package/dist/voice/testing/index.d.cts +1 -0
  122. package/dist/voice/testing/index.d.ts +1 -0
  123. package/dist/voice/testing/index.d.ts.map +1 -1
  124. package/dist/voice/testing/index.js +2 -0
  125. package/dist/voice/testing/index.js.map +1 -1
  126. package/dist/worker.cjs +6 -29
  127. package/dist/worker.cjs.map +1 -1
  128. package/dist/worker.d.ts.map +1 -1
  129. package/dist/worker.js +6 -19
  130. package/dist/worker.js.map +1 -1
  131. package/package.json +1 -1
  132. package/src/beta/index.ts +9 -0
  133. package/src/beta/workflows/index.ts +9 -0
  134. package/src/beta/workflows/task_group.ts +194 -0
  135. package/src/cpu.test.ts +239 -0
  136. package/src/cpu.ts +173 -0
  137. package/src/index.ts +2 -1
  138. package/src/inference/llm.ts +2 -0
  139. package/src/inference/tts.ts +8 -1
  140. package/src/llm/chat_context.test.ts +48 -0
  141. package/src/llm/chat_context.ts +123 -0
  142. package/src/llm/index.ts +1 -0
  143. package/src/llm/tool_context.ts +14 -0
  144. package/src/utils.ts +5 -0
  145. package/src/voice/agent.ts +11 -0
  146. package/src/voice/agent_activity.ts +102 -16
  147. package/src/voice/agent_session.ts +33 -2
  148. package/src/voice/room_io/room_io.ts +14 -3
  149. package/src/voice/testing/fake_llm.ts +138 -0
  150. package/src/voice/testing/index.ts +2 -0
  151. package/src/worker.ts +34 -50
@@ -21,7 +21,7 @@ import type { WritableStreamDefaultWriter } from 'node:stream/web';
21
21
  import { ATTRIBUTE_PUBLISH_ON_BEHALF, TOPIC_CHAT } from '../../constants.js';
22
22
  import { log } from '../../log.js';
23
23
  import { IdentityTransform } from '../../stream/identity_transform.js';
24
- import { Future, Task } from '../../utils.js';
24
+ import { Future, Task, waitForAbort } from '../../utils.js';
25
25
  import { type AgentSession } from '../agent_session.js';
26
26
  import {
27
27
  AgentSessionEventTypes,
@@ -177,7 +177,10 @@ export class RoomIO {
177
177
  : this.inputOptions.participantIdentity ?? null;
178
178
  }
179
179
  private async init(signal: AbortSignal): Promise<void> {
180
- await this.roomConnectedFuture.await;
180
+ await Promise.race([this.roomConnectedFuture.await, waitForAbort(signal)]);
181
+ if (signal.aborted) {
182
+ return;
183
+ }
181
184
 
182
185
  for (const participant of this.room.remoteParticipants.values()) {
183
186
  this.onParticipantConnected(participant);
@@ -186,7 +189,15 @@ export class RoomIO {
186
189
  return;
187
190
  }
188
191
 
189
- const participant = await this.participantAvailableFuture.await;
192
+ const participant = await Promise.race([
193
+ this.participantAvailableFuture.await,
194
+ waitForAbort(signal),
195
+ ]);
196
+
197
+ if (!participant) {
198
+ return;
199
+ }
200
+
190
201
  this.setParticipant(participant.identity);
191
202
 
192
203
  // init agent outputs
@@ -0,0 +1,138 @@
1
+ // SPDX-FileCopyrightText: 2026 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import type { ChatContext } from '../../llm/chat_context.js';
5
+ import { FunctionCall } from '../../llm/chat_context.js';
6
+ import { LLMStream as BaseLLMStream, LLM, type LLMStream } from '../../llm/llm.js';
7
+ import type { ToolChoice, ToolContext } from '../../llm/tool_context.js';
8
+ import { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../../types.js';
9
+ import { delay } from '../../utils.js';
10
+
11
+ export interface FakeLLMResponse {
12
+ input: string;
13
+ type?: 'llm';
14
+ content?: string;
15
+ ttft?: number;
16
+ duration?: number;
17
+ toolCalls?: Array<{ name: string; args: Record<string, unknown> }>;
18
+ }
19
+
20
+ export class FakeLLM extends LLM {
21
+ private readonly responseMap = new Map<string, FakeLLMResponse>();
22
+
23
+ constructor(responses: FakeLLMResponse[] = []) {
24
+ super();
25
+ for (const response of responses) {
26
+ this.responseMap.set(response.input, {
27
+ type: 'llm',
28
+ ttft: 0,
29
+ duration: 0,
30
+ ...response,
31
+ });
32
+ }
33
+ }
34
+
35
+ label(): string {
36
+ return 'fake-llm';
37
+ }
38
+
39
+ chat({
40
+ chatCtx,
41
+ toolCtx,
42
+ connOptions = DEFAULT_API_CONNECT_OPTIONS,
43
+ }: {
44
+ chatCtx: ChatContext;
45
+ toolCtx?: ToolContext;
46
+ connOptions?: APIConnectOptions;
47
+ parallelToolCalls?: boolean;
48
+ toolChoice?: ToolChoice;
49
+ extraKwargs?: Record<string, unknown>;
50
+ }): LLMStream {
51
+ return new FakeLLMStream(this, {
52
+ chatCtx,
53
+ toolCtx,
54
+ connOptions,
55
+ });
56
+ }
57
+
58
+ lookup(input: string): FakeLLMResponse | undefined {
59
+ return this.responseMap.get(input);
60
+ }
61
+ }
62
+
63
+ class FakeLLMStream extends BaseLLMStream {
64
+ private readonly fake: FakeLLM;
65
+
66
+ constructor(
67
+ fake: FakeLLM,
68
+ params: { chatCtx: ChatContext; toolCtx?: ToolContext; connOptions: APIConnectOptions },
69
+ ) {
70
+ super(fake, params);
71
+ this.fake = fake;
72
+ }
73
+
74
+ protected async run(): Promise<void> {
75
+ const input = this.getInputText();
76
+ const decision = this.fake.lookup(input);
77
+ if (!decision) {
78
+ return;
79
+ }
80
+
81
+ const startedAt = Date.now();
82
+ if ((decision.ttft ?? 0) > 0) {
83
+ await delay(decision.ttft!);
84
+ }
85
+
86
+ const content = decision.content ?? '';
87
+ const chunkSize = 3;
88
+ for (let i = 0; i < content.length; i += chunkSize) {
89
+ this.queue.put({
90
+ id: 'fake',
91
+ delta: { role: 'assistant', content: content.slice(i, i + chunkSize) },
92
+ });
93
+ }
94
+
95
+ if (decision.toolCalls && decision.toolCalls.length > 0) {
96
+ const calls = decision.toolCalls.map((tc, index) =>
97
+ FunctionCall.create({
98
+ callId: `fake_call_${index}`,
99
+ name: tc.name,
100
+ args: JSON.stringify(tc.args),
101
+ }),
102
+ );
103
+ this.queue.put({
104
+ id: 'fake',
105
+ delta: { role: 'assistant', toolCalls: calls },
106
+ });
107
+ }
108
+
109
+ const elapsed = Date.now() - startedAt;
110
+ const waitMs = Math.max(0, (decision.duration ?? 0) - elapsed);
111
+ if (waitMs > 0) {
112
+ await delay(waitMs);
113
+ }
114
+ }
115
+
116
+ private getInputText(): string {
117
+ const items = this.chatCtx.items;
118
+ if (items.length === 0) {
119
+ throw new Error('No input text found');
120
+ }
121
+
122
+ for (const item of items) {
123
+ if (item.type === 'message' && item.role === 'system') {
124
+ const text = item.textContent ?? '';
125
+ const lines = text.split('\n');
126
+ const tail = lines[lines.length - 1] ?? '';
127
+ if (lines.length > 1 && tail.startsWith('instructions:')) {
128
+ return tail;
129
+ }
130
+ }
131
+ }
132
+
133
+ const last = items[items.length - 1]!;
134
+ if (last.type === 'message' && last.role === 'user') return last.textContent ?? '';
135
+ if (last.type === 'function_call_output') return last.output;
136
+ throw new Error('No input text found');
137
+ }
138
+ }
@@ -48,3 +48,5 @@ export {
48
48
  type MessageAssertOptions,
49
49
  type RunEvent,
50
50
  } from './types.js';
51
+
52
+ export { FakeLLM, type FakeLLMResponse } from './fake_llm.js';
package/src/worker.ts CHANGED
@@ -13,8 +13,8 @@ import {
13
13
  import type { ParticipantInfo } from 'livekit-server-sdk';
14
14
  import { AccessToken, RoomServiceClient } from 'livekit-server-sdk';
15
15
  import { EventEmitter } from 'node:events';
16
- import os from 'node:os';
17
16
  import { WebSocket } from 'ws';
17
+ import { getCpuMonitor } from './cpu.js';
18
18
  import { HTTPServer } from './http_server.js';
19
19
  import { InferenceRunner } from './inference_runner.js';
20
20
  import { InferenceProcExecutor } from './ipc/inference_proc_executor.js';
@@ -79,32 +79,11 @@ const defaultRequestFunc = async (ctx: JobRequest) => {
79
79
  await ctx.accept();
80
80
  };
81
81
 
82
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
83
- const defaultCpuLoad = async (worker: AgentServer): Promise<number> => {
84
- return new Promise((resolve) => {
85
- const cpus1 = os.cpus();
86
-
87
- setTimeout(() => {
88
- const cpus2 = os.cpus();
89
-
90
- let idle = 0;
91
- let total = 0;
92
-
93
- for (let i = 0; i < cpus1.length; i++) {
94
- const cpu1 = cpus1[i]!.times;
95
- const cpu2 = cpus2[i]!.times;
96
-
97
- idle += cpu2.idle - cpu1.idle;
98
-
99
- const total1 = Object.values(cpu1).reduce((acc, i) => acc + i, 0);
100
- const total2 = Object.values(cpu2).reduce((acc, i) => acc + i, 0);
82
+ const cpuMonitor = getCpuMonitor();
101
83
 
102
- total += total2 - total1;
103
- }
104
-
105
- resolve(+(1 - idle / total).toFixed(2));
106
- }, UPDATE_LOAD_INTERVAL);
107
- });
84
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
+ const defaultCpuLoad = async (_worker: AgentServer): Promise<number> => {
86
+ return cpuMonitor.cpuPercent(UPDATE_LOAD_INTERVAL);
108
87
  };
109
88
 
110
89
  /** Participant permissions to pass to every agent spun up by this worker. */
@@ -651,33 +630,38 @@ export class AgentServer {
651
630
  if (closingWS) clearInterval(loadMonitor);
652
631
 
653
632
  const oldStatus = currentStatus;
654
- this.#opts.loadFunc(this).then((currentLoad: number) => {
655
- const isFull = currentLoad >= this.#opts.loadThreshold;
656
- const currentlyAvailable = !isFull;
657
- currentStatus = currentlyAvailable ? WorkerStatus.WS_AVAILABLE : WorkerStatus.WS_FULL;
658
-
659
- if (oldStatus != currentStatus) {
660
- const extra = { load: currentLoad, loadThreshold: this.#opts.loadThreshold };
661
- if (isFull) {
662
- this.#logger.child(extra).info('worker is at full capacity, marking as unavailable');
663
- } else {
664
- this.#logger.child(extra).info('worker is below capacity, marking as available');
633
+ this.#opts
634
+ .loadFunc(this)
635
+ .then((currentLoad: number) => {
636
+ const isFull = currentLoad >= this.#opts.loadThreshold;
637
+ const currentlyAvailable = !isFull;
638
+ currentStatus = currentlyAvailable ? WorkerStatus.WS_AVAILABLE : WorkerStatus.WS_FULL;
639
+
640
+ if (oldStatus != currentStatus) {
641
+ const extra = { load: currentLoad, loadThreshold: this.#opts.loadThreshold };
642
+ if (isFull) {
643
+ this.#logger.child(extra).info('worker is at full capacity, marking as unavailable');
644
+ } else {
645
+ this.#logger.child(extra).info('worker is below capacity, marking as available');
646
+ }
665
647
  }
666
- }
667
648
 
668
- this.event.emit(
669
- 'worker_msg',
670
- new WorkerMessage({
671
- message: {
672
- case: 'updateWorker',
673
- value: {
674
- load: currentLoad,
675
- status: currentStatus,
649
+ this.event.emit(
650
+ 'worker_msg',
651
+ new WorkerMessage({
652
+ message: {
653
+ case: 'updateWorker',
654
+ value: {
655
+ load: currentLoad,
656
+ status: currentStatus,
657
+ },
676
658
  },
677
- },
678
- }),
679
- );
680
- });
659
+ }),
660
+ );
661
+ })
662
+ .catch((e) => {
663
+ this.#logger.warn({ error: e }, 'failed to measure CPU load');
664
+ });
681
665
  }, UPDATE_LOAD_INTERVAL);
682
666
 
683
667
  await close;