@livekit/agents 1.0.36-dev.0 → 1.0.37

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 (176) hide show
  1. package/dist/index.cjs +1 -3
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +0 -1
  4. package/dist/index.d.ts +0 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +0 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/inference/utils.cjs +2 -15
  9. package/dist/inference/utils.cjs.map +1 -1
  10. package/dist/inference/utils.d.cts +0 -1
  11. package/dist/inference/utils.d.ts +0 -1
  12. package/dist/inference/utils.d.ts.map +1 -1
  13. package/dist/inference/utils.js +1 -13
  14. package/dist/inference/utils.js.map +1 -1
  15. package/dist/stream/stream_channel.cjs +0 -3
  16. package/dist/stream/stream_channel.cjs.map +1 -1
  17. package/dist/stream/stream_channel.d.cts +2 -3
  18. package/dist/stream/stream_channel.d.ts +2 -3
  19. package/dist/stream/stream_channel.d.ts.map +1 -1
  20. package/dist/stream/stream_channel.js +0 -3
  21. package/dist/stream/stream_channel.js.map +1 -1
  22. package/dist/telemetry/trace_types.cjs +0 -15
  23. package/dist/telemetry/trace_types.cjs.map +1 -1
  24. package/dist/telemetry/trace_types.d.cts +0 -5
  25. package/dist/telemetry/trace_types.d.ts +0 -5
  26. package/dist/telemetry/trace_types.d.ts.map +1 -1
  27. package/dist/telemetry/trace_types.js +0 -10
  28. package/dist/telemetry/trace_types.js.map +1 -1
  29. package/dist/voice/agent_activity.cjs +19 -68
  30. package/dist/voice/agent_activity.cjs.map +1 -1
  31. package/dist/voice/agent_activity.d.cts +0 -14
  32. package/dist/voice/agent_activity.d.ts +0 -14
  33. package/dist/voice/agent_activity.d.ts.map +1 -1
  34. package/dist/voice/agent_activity.js +19 -68
  35. package/dist/voice/agent_activity.js.map +1 -1
  36. package/dist/voice/agent_session.cjs +65 -37
  37. package/dist/voice/agent_session.cjs.map +1 -1
  38. package/dist/voice/agent_session.d.cts +25 -4
  39. package/dist/voice/agent_session.d.ts +25 -4
  40. package/dist/voice/agent_session.d.ts.map +1 -1
  41. package/dist/voice/agent_session.js +65 -37
  42. package/dist/voice/agent_session.js.map +1 -1
  43. package/dist/voice/audio_recognition.cjs +2 -124
  44. package/dist/voice/audio_recognition.cjs.map +1 -1
  45. package/dist/voice/audio_recognition.d.cts +1 -32
  46. package/dist/voice/audio_recognition.d.ts +1 -32
  47. package/dist/voice/audio_recognition.d.ts.map +1 -1
  48. package/dist/voice/audio_recognition.js +2 -127
  49. package/dist/voice/audio_recognition.js.map +1 -1
  50. package/dist/voice/index.cjs +14 -1
  51. package/dist/voice/index.cjs.map +1 -1
  52. package/dist/voice/index.d.cts +1 -0
  53. package/dist/voice/index.d.ts +1 -0
  54. package/dist/voice/index.d.ts.map +1 -1
  55. package/dist/voice/index.js +3 -1
  56. package/dist/voice/index.js.map +1 -1
  57. package/dist/voice/room_io/room_io.cjs +1 -0
  58. package/dist/voice/room_io/room_io.cjs.map +1 -1
  59. package/dist/voice/room_io/room_io.d.ts.map +1 -1
  60. package/dist/voice/room_io/room_io.js +1 -0
  61. package/dist/voice/room_io/room_io.js.map +1 -1
  62. package/dist/voice/speech_handle.cjs +12 -3
  63. package/dist/voice/speech_handle.cjs.map +1 -1
  64. package/dist/voice/speech_handle.d.cts +12 -2
  65. package/dist/voice/speech_handle.d.ts +12 -2
  66. package/dist/voice/speech_handle.d.ts.map +1 -1
  67. package/dist/voice/speech_handle.js +10 -2
  68. package/dist/voice/speech_handle.js.map +1 -1
  69. package/dist/voice/testing/index.cjs +54 -0
  70. package/dist/voice/testing/index.cjs.map +1 -0
  71. package/dist/voice/testing/index.d.cts +20 -0
  72. package/dist/voice/testing/index.d.ts +20 -0
  73. package/dist/voice/testing/index.d.ts.map +1 -0
  74. package/dist/voice/testing/index.js +33 -0
  75. package/dist/voice/testing/index.js.map +1 -0
  76. package/dist/voice/testing/run_result.cjs +766 -0
  77. package/dist/voice/testing/run_result.cjs.map +1 -0
  78. package/dist/voice/testing/run_result.d.cts +374 -0
  79. package/dist/voice/testing/run_result.d.ts +374 -0
  80. package/dist/voice/testing/run_result.d.ts.map +1 -0
  81. package/dist/voice/testing/run_result.js +739 -0
  82. package/dist/voice/testing/run_result.js.map +1 -0
  83. package/dist/{inference/interruption/index.cjs → voice/testing/types.cjs} +24 -12
  84. package/dist/voice/testing/types.cjs.map +1 -0
  85. package/dist/voice/testing/types.d.cts +83 -0
  86. package/dist/voice/testing/types.d.ts +83 -0
  87. package/dist/voice/testing/types.d.ts.map +1 -0
  88. package/dist/voice/testing/types.js +19 -0
  89. package/dist/voice/testing/types.js.map +1 -0
  90. package/package.json +3 -4
  91. package/src/index.ts +0 -2
  92. package/src/inference/utils.ts +0 -15
  93. package/src/stream/stream_channel.ts +2 -6
  94. package/src/telemetry/trace_types.ts +0 -7
  95. package/src/voice/agent_activity.ts +24 -83
  96. package/src/voice/agent_session.ts +74 -49
  97. package/src/voice/audio_recognition.ts +1 -161
  98. package/src/voice/index.ts +1 -0
  99. package/src/voice/room_io/room_io.ts +1 -0
  100. package/src/voice/speech_handle.ts +24 -4
  101. package/src/voice/testing/index.ts +50 -0
  102. package/src/voice/testing/run_result.ts +937 -0
  103. package/src/voice/testing/types.ts +118 -0
  104. package/dist/inference/interruption/AdaptiveInterruptionDetector.cjs +0 -152
  105. package/dist/inference/interruption/AdaptiveInterruptionDetector.cjs.map +0 -1
  106. package/dist/inference/interruption/AdaptiveInterruptionDetector.d.cts +0 -50
  107. package/dist/inference/interruption/AdaptiveInterruptionDetector.d.ts +0 -50
  108. package/dist/inference/interruption/AdaptiveInterruptionDetector.d.ts.map +0 -1
  109. package/dist/inference/interruption/AdaptiveInterruptionDetector.js +0 -125
  110. package/dist/inference/interruption/AdaptiveInterruptionDetector.js.map +0 -1
  111. package/dist/inference/interruption/InterruptionStream.cjs +0 -310
  112. package/dist/inference/interruption/InterruptionStream.cjs.map +0 -1
  113. package/dist/inference/interruption/InterruptionStream.d.cts +0 -57
  114. package/dist/inference/interruption/InterruptionStream.d.ts +0 -57
  115. package/dist/inference/interruption/InterruptionStream.d.ts.map +0 -1
  116. package/dist/inference/interruption/InterruptionStream.js +0 -288
  117. package/dist/inference/interruption/InterruptionStream.js.map +0 -1
  118. package/dist/inference/interruption/defaults.cjs +0 -76
  119. package/dist/inference/interruption/defaults.cjs.map +0 -1
  120. package/dist/inference/interruption/defaults.d.cts +0 -14
  121. package/dist/inference/interruption/defaults.d.ts +0 -14
  122. package/dist/inference/interruption/defaults.d.ts.map +0 -1
  123. package/dist/inference/interruption/defaults.js +0 -42
  124. package/dist/inference/interruption/defaults.js.map +0 -1
  125. package/dist/inference/interruption/errors.cjs +0 -2
  126. package/dist/inference/interruption/errors.cjs.map +0 -1
  127. package/dist/inference/interruption/errors.d.cts +0 -2
  128. package/dist/inference/interruption/errors.d.ts +0 -2
  129. package/dist/inference/interruption/errors.d.ts.map +0 -1
  130. package/dist/inference/interruption/errors.js +0 -1
  131. package/dist/inference/interruption/errors.js.map +0 -1
  132. package/dist/inference/interruption/http_transport.cjs +0 -57
  133. package/dist/inference/interruption/http_transport.cjs.map +0 -1
  134. package/dist/inference/interruption/http_transport.d.cts +0 -23
  135. package/dist/inference/interruption/http_transport.d.ts +0 -23
  136. package/dist/inference/interruption/http_transport.d.ts.map +0 -1
  137. package/dist/inference/interruption/http_transport.js +0 -33
  138. package/dist/inference/interruption/http_transport.js.map +0 -1
  139. package/dist/inference/interruption/index.cjs.map +0 -1
  140. package/dist/inference/interruption/index.d.cts +0 -5
  141. package/dist/inference/interruption/index.d.ts +0 -5
  142. package/dist/inference/interruption/index.d.ts.map +0 -1
  143. package/dist/inference/interruption/index.js +0 -7
  144. package/dist/inference/interruption/index.js.map +0 -1
  145. package/dist/inference/interruption/interruption.cjs +0 -85
  146. package/dist/inference/interruption/interruption.cjs.map +0 -1
  147. package/dist/inference/interruption/interruption.d.cts +0 -48
  148. package/dist/inference/interruption/interruption.d.ts +0 -48
  149. package/dist/inference/interruption/interruption.d.ts.map +0 -1
  150. package/dist/inference/interruption/interruption.js +0 -59
  151. package/dist/inference/interruption/interruption.js.map +0 -1
  152. package/dist/inference/utils.test.cjs +0 -20
  153. package/dist/inference/utils.test.cjs.map +0 -1
  154. package/dist/inference/utils.test.js +0 -19
  155. package/dist/inference/utils.test.js.map +0 -1
  156. package/dist/utils/ws_transport.cjs +0 -51
  157. package/dist/utils/ws_transport.cjs.map +0 -1
  158. package/dist/utils/ws_transport.d.cts +0 -9
  159. package/dist/utils/ws_transport.d.ts +0 -9
  160. package/dist/utils/ws_transport.d.ts.map +0 -1
  161. package/dist/utils/ws_transport.js +0 -17
  162. package/dist/utils/ws_transport.js.map +0 -1
  163. package/dist/utils/ws_transport.test.cjs +0 -212
  164. package/dist/utils/ws_transport.test.cjs.map +0 -1
  165. package/dist/utils/ws_transport.test.js +0 -211
  166. package/dist/utils/ws_transport.test.js.map +0 -1
  167. package/src/inference/interruption/AdaptiveInterruptionDetector.ts +0 -166
  168. package/src/inference/interruption/InterruptionStream.ts +0 -397
  169. package/src/inference/interruption/defaults.ts +0 -33
  170. package/src/inference/interruption/errors.ts +0 -0
  171. package/src/inference/interruption/http_transport.ts +0 -61
  172. package/src/inference/interruption/index.ts +0 -4
  173. package/src/inference/interruption/interruption.ts +0 -88
  174. package/src/inference/utils.test.ts +0 -31
  175. package/src/utils/ws_transport.test.ts +0 -282
  176. package/src/utils/ws_transport.ts +0 -22
@@ -0,0 +1,739 @@
1
+ import { z } from "zod";
2
+ import { ChatContext } from "../../llm/chat_context.js";
3
+ import { tool } from "../../llm/tool_context.js";
4
+ import { Future } from "../../utils.js";
5
+ import { isSpeechHandle } from "../speech_handle.js";
6
+ import {
7
+ isAgentHandoffEvent,
8
+ isChatMessageEvent,
9
+ isFunctionCallEvent,
10
+ isFunctionCallOutputEvent
11
+ } from "./types.js";
12
+ const evalsVerbose = parseInt(process.env.LIVEKIT_EVALS_VERBOSE || "0", 10);
13
+ class RunResult {
14
+ _events = [];
15
+ doneFut = new Future();
16
+ userInput;
17
+ handles = /* @__PURE__ */ new Set();
18
+ lastSpeechHandle;
19
+ runAssert;
20
+ // TODO(brian): Add typed output support for parity with Python
21
+ // - Add outputType?: new (...args: unknown[]) => T
22
+ // - Add finalOutput?: T
23
+ // - Implement markDone() to extract final_output from SpeechHandle.maybeRunFinalOutput
24
+ // - See Python: run_result.py lines 182-201
25
+ constructor(options) {
26
+ this.userInput = options == null ? void 0 : options.userInput;
27
+ }
28
+ /**
29
+ * List of all recorded events generated during the run.
30
+ */
31
+ get events() {
32
+ return this._events;
33
+ }
34
+ /**
35
+ * Provides an assertion helper for verifying the run events.
36
+ */
37
+ get expect() {
38
+ if (evalsVerbose) {
39
+ const eventsStr = formatEvents(this._events).map((line) => ` ${line}`).join("\n");
40
+ console.log(
41
+ `
42
+ + RunResult {
43
+ userInput: "${this.userInput}"
44
+ events: [
45
+ ${eventsStr}
46
+ ]
47
+ }`
48
+ );
49
+ }
50
+ if (!this.runAssert) {
51
+ this.runAssert = new RunAssert(this);
52
+ }
53
+ return this.runAssert;
54
+ }
55
+ /**
56
+ * Returns the final output of the run after completion.
57
+ *
58
+ * @throws Error - Not implemented yet.
59
+ */
60
+ get finalOutput() {
61
+ throw new Error("finalOutput is not yet implemented in JS.");
62
+ }
63
+ /**
64
+ * Indicates whether the run has finished processing all events.
65
+ */
66
+ done() {
67
+ return this.doneFut.done;
68
+ }
69
+ /**
70
+ * Wait for the RunResult to complete. Returns `this` for method chaining.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const result = session.run({ userInput: 'Hi!' });
75
+ * await result.wait(); // waits for completion
76
+ * result.expect.nextEvent().isMessage({ role: 'assistant' });
77
+ * ```
78
+ */
79
+ async wait() {
80
+ await this.doneFut.await;
81
+ return this;
82
+ }
83
+ /**
84
+ * @internal
85
+ * Records an agent handoff event.
86
+ */
87
+ _agentHandoff(params) {
88
+ const event = {
89
+ type: "agent_handoff",
90
+ item: params.item,
91
+ oldAgent: params.oldAgent,
92
+ newAgent: params.newAgent
93
+ };
94
+ const index = this._findInsertionIndex(event.item.createdAt);
95
+ this._events.splice(index, 0, event);
96
+ }
97
+ /**
98
+ * @internal
99
+ * Called when a chat item is added during the run.
100
+ */
101
+ _itemAdded(item) {
102
+ if (this.doneFut.done) {
103
+ return;
104
+ }
105
+ let event;
106
+ if (item.type === "message") {
107
+ event = { type: "message", item };
108
+ } else if (item.type === "function_call") {
109
+ event = { type: "function_call", item };
110
+ } else if (item.type === "function_call_output") {
111
+ event = { type: "function_call_output", item };
112
+ }
113
+ if (event) {
114
+ const index = this._findInsertionIndex(item.createdAt);
115
+ this._events.splice(index, 0, event);
116
+ }
117
+ }
118
+ /**
119
+ * @internal
120
+ * Watch a speech handle or task for completion.
121
+ */
122
+ _watchHandle(handle) {
123
+ this.handles.add(handle);
124
+ if (isSpeechHandle(handle)) {
125
+ handle._addItemAddedCallback(this._itemAdded.bind(this));
126
+ }
127
+ handle.addDoneCallback(() => {
128
+ this._markDoneIfNeeded(handle);
129
+ });
130
+ }
131
+ /**
132
+ * @internal
133
+ * Unwatch a handle.
134
+ */
135
+ _unwatchHandle(handle) {
136
+ this.handles.delete(handle);
137
+ if (isSpeechHandle(handle)) {
138
+ handle._removeItemAddedCallback(this._itemAdded.bind(this));
139
+ }
140
+ }
141
+ _markDoneIfNeeded(handle) {
142
+ if (isSpeechHandle(handle)) {
143
+ this.lastSpeechHandle = handle;
144
+ }
145
+ if ([...this.handles].every((h) => isSpeechHandle(h) ? h.done() : h.done)) {
146
+ this._markDone();
147
+ }
148
+ }
149
+ _markDone() {
150
+ if (!this.doneFut.done) {
151
+ this.doneFut.resolve();
152
+ }
153
+ }
154
+ /**
155
+ * Find the correct insertion index to maintain chronological order.
156
+ */
157
+ _findInsertionIndex(createdAt) {
158
+ for (let i = this._events.length - 1; i >= 0; i--) {
159
+ if (this._events[i].item.createdAt <= createdAt) {
160
+ return i + 1;
161
+ }
162
+ }
163
+ return 0;
164
+ }
165
+ }
166
+ class RunAssert {
167
+ _events;
168
+ _currentIndex = 0;
169
+ constructor(runResult) {
170
+ this._events = runResult.events;
171
+ }
172
+ /**
173
+ * Access a specific event by index for assertions.
174
+ * Supports negative indices (e.g., -1 for last event).
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * result.expect.at(0).isMessage({ role: 'user' });
179
+ * result.expect.at(-1).isMessage({ role: 'assistant' });
180
+ * ```
181
+ */
182
+ at(index) {
183
+ let normalizedIndex = index;
184
+ if (index < 0) {
185
+ normalizedIndex = this._events.length + index;
186
+ }
187
+ if (normalizedIndex < 0 || normalizedIndex >= this._events.length) {
188
+ this._raiseWithDebugInfo(
189
+ `at(${index}) out of range (total events: ${this._events.length})`,
190
+ normalizedIndex
191
+ );
192
+ }
193
+ return new EventAssert(this._events[normalizedIndex], this, normalizedIndex);
194
+ }
195
+ /**
196
+ * Advance to the next event, optionally filtering by type.
197
+ *
198
+ * @example
199
+ * ```typescript
200
+ * result.expect.nextEvent().isMessage({ role: 'assistant' });
201
+ * result.expect.nextEvent({ type: 'function_call' }).isFunctionCall({ name: 'foo' });
202
+ * ```
203
+ */
204
+ nextEvent(options) {
205
+ while (true) {
206
+ const evAssert = this._currentEvent();
207
+ this._currentIndex++;
208
+ if (!(options == null ? void 0 : options.type) || evAssert.event().type === options.type) {
209
+ return evAssert;
210
+ }
211
+ }
212
+ }
213
+ /**
214
+ * Skip a specified number of upcoming events without assertions.
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * result.expect.skipNext(2);
219
+ * ```
220
+ */
221
+ skipNext(count = 1) {
222
+ for (let i = 0; i < count; i++) {
223
+ if (this._currentIndex >= this._events.length) {
224
+ this._raiseWithDebugInfo(`Tried to skip ${count} event(s), but only ${i} were available.`);
225
+ }
226
+ this._currentIndex++;
227
+ }
228
+ return this;
229
+ }
230
+ /**
231
+ * Conditionally skip the next event if it matches the specified criteria.
232
+ * Returns the event assertion if matched and skipped, or undefined if not matched.
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * // Skip optional assistant message before function call
237
+ * result.expect.skipNextEventIf({ type: 'message', role: 'assistant' });
238
+ * result.expect.nextEvent().isFunctionCall({ name: 'foo' });
239
+ * ```
240
+ */
241
+ skipNextEventIf(options) {
242
+ if (this._currentIndex >= this._events.length) {
243
+ return void 0;
244
+ }
245
+ try {
246
+ const evAssert = this._currentEvent();
247
+ if (options.type === "message") {
248
+ const { role } = options;
249
+ const result = evAssert.isMessage({ role });
250
+ this._currentIndex++;
251
+ return result;
252
+ } else if (options.type === "function_call") {
253
+ const { name, args } = options;
254
+ const result = evAssert.isFunctionCall({
255
+ name,
256
+ args
257
+ });
258
+ this._currentIndex++;
259
+ return result;
260
+ } else if (options.type === "function_call_output") {
261
+ const { output, isError } = options;
262
+ const result = evAssert.isFunctionCallOutput({
263
+ output,
264
+ isError
265
+ });
266
+ this._currentIndex++;
267
+ return result;
268
+ } else if (options.type === "agent_handoff") {
269
+ const { newAgentType } = options;
270
+ const result = evAssert.isAgentHandoff({ newAgentType });
271
+ this._currentIndex++;
272
+ return result;
273
+ }
274
+ } catch {
275
+ return void 0;
276
+ }
277
+ return void 0;
278
+ }
279
+ /**
280
+ * Get an EventRangeAssert for a range of events.
281
+ * Similar to Python's slice access: expect[0:3] or expect[:]
282
+ *
283
+ * @param start - Start index (inclusive), defaults to 0
284
+ * @param end - End index (exclusive), defaults to events.length
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * // Search all events
289
+ * result.expect.range().containsFunctionCall({ name: 'foo' });
290
+ * // Search first 3 events
291
+ * result.expect.range(0, 3).containsMessage({ role: 'assistant' });
292
+ * ```
293
+ */
294
+ range(start, end) {
295
+ const startIdx = start ?? 0;
296
+ const endIdx = end ?? this._events.length;
297
+ const events = this._events.slice(startIdx, endIdx);
298
+ return new EventRangeAssert(events, this, { start: startIdx, end: endIdx });
299
+ }
300
+ /**
301
+ * Assert that a function call matching criteria exists anywhere in the events.
302
+ *
303
+ * @example
304
+ * ```typescript
305
+ * result.expect.containsFunctionCall({ name: 'order_item' });
306
+ * ```
307
+ */
308
+ containsFunctionCall(options) {
309
+ return this.range().containsFunctionCall(options);
310
+ }
311
+ /**
312
+ * Assert that a message matching criteria exists anywhere in the events.
313
+ *
314
+ * @example
315
+ * ```typescript
316
+ * result.expect.containsMessage({ role: 'assistant' });
317
+ * ```
318
+ */
319
+ containsMessage(options) {
320
+ return this.range().containsMessage(options);
321
+ }
322
+ /**
323
+ * Assert that a function call output matching criteria exists anywhere in the events.
324
+ *
325
+ * @example
326
+ * ```typescript
327
+ * result.expect.containsFunctionCallOutput({ isError: false });
328
+ * ```
329
+ */
330
+ containsFunctionCallOutput(options) {
331
+ return this.range().containsFunctionCallOutput(options);
332
+ }
333
+ /**
334
+ * Assert that an agent handoff matching criteria exists anywhere in the events.
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * result.expect.containsAgentHandoff({ newAgentType: MyAgent });
339
+ * ```
340
+ */
341
+ containsAgentHandoff(options) {
342
+ return this.range().containsAgentHandoff(options);
343
+ }
344
+ /**
345
+ * Assert that there are no further events.
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * result.expect.noMoreEvents();
350
+ * ```
351
+ */
352
+ noMoreEvents() {
353
+ if (this._currentIndex < this._events.length) {
354
+ const event = this._events[this._currentIndex];
355
+ this._raiseWithDebugInfo(`Expected no more events, but found: ${event.type}`);
356
+ }
357
+ }
358
+ _currentEvent() {
359
+ if (this._currentIndex >= this._events.length) {
360
+ this._raiseWithDebugInfo("Expected another event, but none left.");
361
+ }
362
+ return this.at(this._currentIndex);
363
+ }
364
+ /** @internal */
365
+ _raiseWithDebugInfo(message, index) {
366
+ const markerIndex = index ?? this._currentIndex;
367
+ const eventsStr = formatEvents(this._events, markerIndex).join("\n");
368
+ throw new AssertionError(`${message}
369
+ Context around failure:
370
+ ${eventsStr}`);
371
+ }
372
+ }
373
+ class EventAssert {
374
+ _event;
375
+ _parent;
376
+ _index;
377
+ constructor(event, parent, index) {
378
+ this._event = event;
379
+ this._parent = parent;
380
+ this._index = index;
381
+ }
382
+ /**
383
+ * Get the underlying event.
384
+ */
385
+ event() {
386
+ return this._event;
387
+ }
388
+ _raise(message) {
389
+ this._parent._raiseWithDebugInfo(message, this._index);
390
+ }
391
+ /**
392
+ * Verify this event is a message with optional role matching.
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * result.expect.nextEvent().isMessage({ role: 'assistant' });
397
+ * ```
398
+ */
399
+ isMessage(options) {
400
+ if (!isChatMessageEvent(this._event)) {
401
+ this._raise(`Expected ChatMessageEvent, got ${this._event.type}`);
402
+ }
403
+ if ((options == null ? void 0 : options.role) && this._event.item.role !== options.role) {
404
+ this._raise(`Expected role '${options.role}', got '${this._event.item.role}'`);
405
+ }
406
+ return new MessageAssert(this._event, this._parent, this._index);
407
+ }
408
+ /**
409
+ * Verify this event is a function call with optional name/args matching.
410
+ *
411
+ * @example
412
+ * ```typescript
413
+ * result.expect.nextEvent().isFunctionCall({ name: 'order_item', args: { id: 'big_mac' } });
414
+ * ```
415
+ */
416
+ isFunctionCall(options) {
417
+ if (!isFunctionCallEvent(this._event)) {
418
+ this._raise(`Expected FunctionCallEvent, got ${this._event.type}`);
419
+ }
420
+ if ((options == null ? void 0 : options.name) && this._event.item.name !== options.name) {
421
+ this._raise(`Expected call name '${options.name}', got '${this._event.item.name}'`);
422
+ }
423
+ if (options == null ? void 0 : options.args) {
424
+ let actual;
425
+ try {
426
+ actual = JSON.parse(this._event.item.args);
427
+ } catch {
428
+ this._raise(`Failed to parse function call arguments: ${this._event.item.args}`);
429
+ }
430
+ for (const [key, value] of Object.entries(options.args)) {
431
+ if (!(key in actual) || actual[key] !== value) {
432
+ this._raise(
433
+ `For key '${key}', expected ${JSON.stringify(value)}, got ${JSON.stringify(actual[key])}`
434
+ );
435
+ }
436
+ }
437
+ }
438
+ return new FunctionCallAssert(this._event, this._parent, this._index);
439
+ }
440
+ /**
441
+ * Verify this event is a function call output with optional matching.
442
+ *
443
+ * @example
444
+ * ```typescript
445
+ * result.expect.nextEvent().isFunctionCallOutput({ isError: false });
446
+ * ```
447
+ */
448
+ isFunctionCallOutput(options) {
449
+ if (!isFunctionCallOutputEvent(this._event)) {
450
+ this._raise(`Expected FunctionCallOutputEvent, got ${this._event.type}`);
451
+ }
452
+ if ((options == null ? void 0 : options.output) !== void 0 && this._event.item.output !== options.output) {
453
+ this._raise(`Expected output '${options.output}', got '${this._event.item.output}'`);
454
+ }
455
+ if ((options == null ? void 0 : options.isError) !== void 0 && this._event.item.isError !== options.isError) {
456
+ this._raise(`Expected isError=${options.isError}, got ${this._event.item.isError}`);
457
+ }
458
+ return new FunctionCallOutputAssert(this._event, this._parent, this._index);
459
+ }
460
+ /**
461
+ * Verify this event is an agent handoff with optional type matching.
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * result.expect.nextEvent().isAgentHandoff({ newAgentType: MyAgent });
466
+ * ```
467
+ */
468
+ isAgentHandoff(options) {
469
+ if (!isAgentHandoffEvent(this._event)) {
470
+ this._raise(`Expected AgentHandoffEvent, got ${this._event.type}`);
471
+ }
472
+ const event = this._event;
473
+ if (options == null ? void 0 : options.newAgentType) {
474
+ const actualType = event.newAgent.constructor.name;
475
+ if (!(event.newAgent instanceof options.newAgentType)) {
476
+ this._raise(`Expected new_agent '${options.newAgentType.name}', got '${actualType}'`);
477
+ }
478
+ }
479
+ return new AgentHandoffAssert(event, this._parent, this._index);
480
+ }
481
+ }
482
+ class EventRangeAssert {
483
+ _events;
484
+ _parent;
485
+ _range;
486
+ constructor(events, parent, range) {
487
+ this._events = events;
488
+ this._parent = parent;
489
+ this._range = range;
490
+ }
491
+ /**
492
+ * Assert that a function call matching criteria exists in this event range.
493
+ *
494
+ * @example
495
+ * ```typescript
496
+ * result.expect.range(0, 3).containsFunctionCall({ name: 'foo' });
497
+ * ```
498
+ */
499
+ containsFunctionCall(options) {
500
+ for (let idx = 0; idx < this._events.length; idx++) {
501
+ const ev = this._events[idx];
502
+ const candidate = new EventAssert(ev, this._parent, this._range.start + idx);
503
+ try {
504
+ return candidate.isFunctionCall(options);
505
+ } catch {
506
+ }
507
+ }
508
+ this._parent._raiseWithDebugInfo(
509
+ `No FunctionCallEvent satisfying criteria found in range [${this._range.start}:${this._range.end}]`
510
+ );
511
+ }
512
+ /**
513
+ * Assert that a message matching criteria exists in this event range.
514
+ *
515
+ * @example
516
+ * ```typescript
517
+ * result.expect.range(0, 2).containsMessage({ role: 'assistant' });
518
+ * ```
519
+ */
520
+ containsMessage(options) {
521
+ for (let idx = 0; idx < this._events.length; idx++) {
522
+ const ev = this._events[idx];
523
+ const candidate = new EventAssert(ev, this._parent, this._range.start + idx);
524
+ try {
525
+ return candidate.isMessage(options);
526
+ } catch {
527
+ }
528
+ }
529
+ this._parent._raiseWithDebugInfo(
530
+ `No ChatMessageEvent matching criteria found in range [${this._range.start}:${this._range.end}]`
531
+ );
532
+ }
533
+ /**
534
+ * Assert that a function call output matching criteria exists in this event range.
535
+ *
536
+ * @example
537
+ * ```typescript
538
+ * result.expect.range(1, 4).containsFunctionCallOutput({ isError: true });
539
+ * ```
540
+ */
541
+ containsFunctionCallOutput(options) {
542
+ for (let idx = 0; idx < this._events.length; idx++) {
543
+ const ev = this._events[idx];
544
+ const candidate = new EventAssert(ev, this._parent, this._range.start + idx);
545
+ try {
546
+ return candidate.isFunctionCallOutput(options);
547
+ } catch {
548
+ }
549
+ }
550
+ this._parent._raiseWithDebugInfo(
551
+ `No FunctionCallOutputEvent matching criteria found in range [${this._range.start}:${this._range.end}]`
552
+ );
553
+ }
554
+ /**
555
+ * Assert that an agent handoff matching criteria exists in this event range.
556
+ *
557
+ * @example
558
+ * ```typescript
559
+ * result.expect.range(0, 3).containsAgentHandoff({ newAgentType: MyAgent });
560
+ * ```
561
+ */
562
+ containsAgentHandoff(options) {
563
+ for (let idx = 0; idx < this._events.length; idx++) {
564
+ const ev = this._events[idx];
565
+ const candidate = new EventAssert(ev, this._parent, this._range.start + idx);
566
+ try {
567
+ return candidate.isAgentHandoff(options);
568
+ } catch {
569
+ }
570
+ }
571
+ this._parent._raiseWithDebugInfo(
572
+ `No AgentHandoffEvent matching criteria found in range [${this._range.start}:${this._range.end}]`
573
+ );
574
+ }
575
+ }
576
+ class MessageAssert extends EventAssert {
577
+ constructor(event, parent, index) {
578
+ super(event, parent, index);
579
+ }
580
+ event() {
581
+ return this._event;
582
+ }
583
+ /**
584
+ * Evaluate whether the message fulfills the given intent using an LLM.
585
+ *
586
+ * @param llm - LLM instance for judgment
587
+ * @param options - Options containing the intent description
588
+ * @returns Self for chaining further assertions
589
+ *
590
+ * @example
591
+ * ```typescript
592
+ * await result.expect
593
+ * .nextEvent()
594
+ * .isMessage({ role: 'assistant' })
595
+ * .judge(llm, { intent: 'should ask for the drink size' });
596
+ * ```
597
+ */
598
+ async judge(llm, options) {
599
+ const { intent } = options;
600
+ const content = this._event.item.content;
601
+ const msgContent = typeof content === "string" ? content : Array.isArray(content) ? content.filter((c) => typeof c === "string").join(" ") : "";
602
+ if (!msgContent) {
603
+ this._raise("The chat message is empty.");
604
+ }
605
+ if (!intent) {
606
+ this._raise("Intent is required to judge the message.");
607
+ }
608
+ const checkIntentTool = tool({
609
+ description: "Determines whether the message correctly fulfills the given intent. Returns success=true if the message satisfies the intent, false otherwise. Provide a concise reason justifying the result.",
610
+ parameters: z.object({
611
+ success: z.boolean().describe("Whether the message satisfies the intent"),
612
+ reason: z.string().describe("A concise explanation justifying the result")
613
+ }),
614
+ execute: async ({ success: success2, reason: reason2 }) => {
615
+ return { success: success2, reason: reason2 };
616
+ }
617
+ });
618
+ const chatCtx = ChatContext.empty();
619
+ chatCtx.addMessage({
620
+ role: "system",
621
+ content: "You are a test evaluator for conversational agents.\nYou will be shown a message and a target intent. Determine whether the message accomplishes the intent.\nOnly respond by calling the `check_intent(success: bool, reason: str)` function with your final judgment.\nBe strict: if the message does not clearly fulfill the intent, return `success = false` and explain why."
622
+ });
623
+ chatCtx.addMessage({
624
+ role: "user",
625
+ content: `Check if the following message fulfills the given intent.
626
+
627
+ Intent:
628
+ ${intent}
629
+
630
+ Message:
631
+ ${msgContent}`
632
+ });
633
+ let toolArgs;
634
+ const stream = llm.chat({
635
+ chatCtx,
636
+ toolCtx: { check_intent: checkIntentTool },
637
+ toolChoice: { type: "function", function: { name: "check_intent" } },
638
+ extraKwargs: { temperature: 0 }
639
+ });
640
+ for await (const chunk of stream) {
641
+ if (!chunk.delta) continue;
642
+ if (chunk.delta.toolCalls && chunk.delta.toolCalls.length > 0) {
643
+ const toolCall = chunk.delta.toolCalls[0];
644
+ if (toolCall.args) {
645
+ try {
646
+ toolArgs = JSON.parse(toolCall.args);
647
+ } catch {
648
+ }
649
+ }
650
+ }
651
+ }
652
+ if (!toolArgs) {
653
+ this._raise("LLM did not return any arguments for evaluation.");
654
+ }
655
+ const { success, reason } = toolArgs;
656
+ if (!success) {
657
+ this._raise(`Judgment failed: ${reason}`);
658
+ } else if (evalsVerbose) {
659
+ const printMsg = msgContent.length > 30 ? msgContent.slice(0, 30).replace(/\n/g, "\\n") + "..." : msgContent;
660
+ console.log(`- Judgment succeeded for \`${printMsg}\`: \`${reason}\``);
661
+ }
662
+ return this;
663
+ }
664
+ }
665
+ class FunctionCallAssert extends EventAssert {
666
+ constructor(event, parent, index) {
667
+ super(event, parent, index);
668
+ }
669
+ event() {
670
+ return this._event;
671
+ }
672
+ }
673
+ class FunctionCallOutputAssert extends EventAssert {
674
+ constructor(event, parent, index) {
675
+ super(event, parent, index);
676
+ }
677
+ event() {
678
+ return this._event;
679
+ }
680
+ }
681
+ class AgentHandoffAssert extends EventAssert {
682
+ constructor(event, parent, index) {
683
+ super(event, parent, index);
684
+ }
685
+ event() {
686
+ return this._event;
687
+ }
688
+ }
689
+ class AssertionError extends Error {
690
+ constructor(message) {
691
+ var _a;
692
+ super(message);
693
+ this.name = "AssertionError";
694
+ (_a = Error.captureStackTrace) == null ? void 0 : _a.call(Error, this, AssertionError);
695
+ }
696
+ }
697
+ function formatEvents(events, selectedIndex) {
698
+ var _a;
699
+ const lines = [];
700
+ for (let i = 0; i < events.length; i++) {
701
+ const event = events[i];
702
+ let prefix = "";
703
+ if (selectedIndex !== void 0) {
704
+ prefix = i === selectedIndex ? ">>>" : " ";
705
+ }
706
+ let line;
707
+ if (isChatMessageEvent(event)) {
708
+ const { role, content, interrupted } = event.item;
709
+ const textContent = typeof content === "string" ? content : Array.isArray(content) ? content.filter((c) => typeof c === "string").join(" ") : "";
710
+ const truncated = textContent.length > 50 ? textContent.slice(0, 50) + "..." : textContent;
711
+ line = `${prefix}[${i}] { type: "message", role: "${role}", content: "${truncated}", interrupted: ${interrupted} }`;
712
+ } else if (isFunctionCallEvent(event)) {
713
+ const { name, args } = event.item;
714
+ line = `${prefix}[${i}] { type: "function_call", name: "${name}", args: ${args} }`;
715
+ } else if (isFunctionCallOutputEvent(event)) {
716
+ const { output, isError } = event.item;
717
+ const truncated = output.length > 50 ? output.slice(0, 50) + "..." : output;
718
+ line = `${prefix}[${i}] { type: "function_call_output", output: "${truncated}", isError: ${isError} }`;
719
+ } else if (isAgentHandoffEvent(event)) {
720
+ line = `${prefix}[${i}] { type: "agent_handoff", oldAgent: "${(_a = event.oldAgent) == null ? void 0 : _a.constructor.name}", newAgent: "${event.newAgent.constructor.name}" }`;
721
+ } else {
722
+ line = `${prefix}[${i}] ${event}`;
723
+ }
724
+ lines.push(line);
725
+ }
726
+ return lines;
727
+ }
728
+ export {
729
+ AgentHandoffAssert,
730
+ AssertionError,
731
+ EventAssert,
732
+ EventRangeAssert,
733
+ FunctionCallAssert,
734
+ FunctionCallOutputAssert,
735
+ MessageAssert,
736
+ RunAssert,
737
+ RunResult
738
+ };
739
+ //# sourceMappingURL=run_result.js.map