@agtlantis/core 0.4.1
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/README.md +267 -0
- package/dist/base-provider-C3mFLNiN.d.cts +1236 -0
- package/dist/base-provider-C3mFLNiN.d.ts +1236 -0
- package/dist/index.cjs +3188 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1502 -0
- package/dist/index.d.ts +1502 -0
- package/dist/index.js +3082 -0
- package/dist/index.js.map +1 -0
- package/dist/testing/index.cjs +2144 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +516 -0
- package/dist/testing/index.d.ts +516 -0
- package/dist/testing/index.js +2098 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,2144 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var test = require('ai/test');
|
|
4
|
+
var ai = require('ai');
|
|
5
|
+
var merge = require('lodash/merge');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var merge__default = /*#__PURE__*/_interopDefault(merge);
|
|
10
|
+
|
|
11
|
+
// src/session/usage-extractors.ts
|
|
12
|
+
function mergeUsages(usages) {
|
|
13
|
+
if (usages.length === 0) {
|
|
14
|
+
return createZeroUsage();
|
|
15
|
+
}
|
|
16
|
+
let inputTokens = 0;
|
|
17
|
+
let outputTokens = 0;
|
|
18
|
+
let totalTokens = 0;
|
|
19
|
+
let noCacheTokens = 0;
|
|
20
|
+
let cacheReadTokens = 0;
|
|
21
|
+
let cacheWriteTokens = 0;
|
|
22
|
+
let textTokens = 0;
|
|
23
|
+
let reasoningTokens = 0;
|
|
24
|
+
for (const usage of usages) {
|
|
25
|
+
inputTokens += usage.inputTokens ?? 0;
|
|
26
|
+
outputTokens += usage.outputTokens ?? 0;
|
|
27
|
+
totalTokens += usage.totalTokens ?? 0;
|
|
28
|
+
noCacheTokens += usage.inputTokenDetails?.noCacheTokens ?? 0;
|
|
29
|
+
cacheReadTokens += usage.inputTokenDetails?.cacheReadTokens ?? 0;
|
|
30
|
+
cacheWriteTokens += usage.inputTokenDetails?.cacheWriteTokens ?? 0;
|
|
31
|
+
textTokens += usage.outputTokenDetails?.textTokens ?? 0;
|
|
32
|
+
reasoningTokens += usage.outputTokenDetails?.reasoningTokens ?? 0;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
inputTokens,
|
|
36
|
+
outputTokens,
|
|
37
|
+
totalTokens,
|
|
38
|
+
inputTokenDetails: {
|
|
39
|
+
noCacheTokens,
|
|
40
|
+
cacheReadTokens,
|
|
41
|
+
cacheWriteTokens
|
|
42
|
+
},
|
|
43
|
+
outputTokenDetails: {
|
|
44
|
+
textTokens,
|
|
45
|
+
reasoningTokens
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function createZeroUsage() {
|
|
50
|
+
return {
|
|
51
|
+
inputTokens: 0,
|
|
52
|
+
outputTokens: 0,
|
|
53
|
+
totalTokens: 0,
|
|
54
|
+
inputTokenDetails: {
|
|
55
|
+
noCacheTokens: 0,
|
|
56
|
+
cacheReadTokens: 0,
|
|
57
|
+
cacheWriteTokens: 0
|
|
58
|
+
},
|
|
59
|
+
outputTokenDetails: {
|
|
60
|
+
textTokens: 0,
|
|
61
|
+
reasoningTokens: 0
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/session/types.ts
|
|
67
|
+
var SessionSummary = class _SessionSummary {
|
|
68
|
+
totalLLMUsage;
|
|
69
|
+
llmCallCount;
|
|
70
|
+
llmCalls;
|
|
71
|
+
toolCalls;
|
|
72
|
+
customRecords;
|
|
73
|
+
llmCost;
|
|
74
|
+
additionalCosts;
|
|
75
|
+
metadata;
|
|
76
|
+
/** Cost breakdown by model. Key format: `${provider}/${model}` */
|
|
77
|
+
costByModel;
|
|
78
|
+
startTime;
|
|
79
|
+
constructor(data, startTime) {
|
|
80
|
+
this.startTime = startTime;
|
|
81
|
+
this.totalLLMUsage = data.totalLLMUsage;
|
|
82
|
+
this.llmCallCount = data.llmCalls.length;
|
|
83
|
+
this.llmCalls = Object.freeze([...data.llmCalls]);
|
|
84
|
+
this.toolCalls = Object.freeze([...data.toolCalls]);
|
|
85
|
+
this.customRecords = Object.freeze([...data.customRecords]);
|
|
86
|
+
this.llmCost = data.llmCost;
|
|
87
|
+
this.additionalCosts = Object.freeze([...data.additionalCosts]);
|
|
88
|
+
this.metadata = Object.freeze({ ...data.metadata });
|
|
89
|
+
this.costByModel = Object.freeze({ ...data.costByModel });
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Total duration from session start to now (computed dynamically).
|
|
93
|
+
*/
|
|
94
|
+
get totalDuration() {
|
|
95
|
+
return Date.now() - this.startTime;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Creates an empty SessionSummary.
|
|
99
|
+
*/
|
|
100
|
+
static empty(startTime) {
|
|
101
|
+
return new _SessionSummary(
|
|
102
|
+
{
|
|
103
|
+
totalLLMUsage: createZeroUsage(),
|
|
104
|
+
llmCalls: [],
|
|
105
|
+
toolCalls: [],
|
|
106
|
+
customRecords: [],
|
|
107
|
+
llmCost: 0,
|
|
108
|
+
additionalCosts: [],
|
|
109
|
+
metadata: {},
|
|
110
|
+
costByModel: {}
|
|
111
|
+
},
|
|
112
|
+
startTime
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Creates a SessionSummary with custom data for testing purposes.
|
|
117
|
+
* @internal For testing only - do not use in production code.
|
|
118
|
+
*/
|
|
119
|
+
static forTest(data) {
|
|
120
|
+
const startTime = data.startTime ?? Date.now() - 1e3;
|
|
121
|
+
return new _SessionSummary(
|
|
122
|
+
{
|
|
123
|
+
totalLLMUsage: data.totalLLMUsage ?? createZeroUsage(),
|
|
124
|
+
llmCalls: data.llmCalls ?? [],
|
|
125
|
+
toolCalls: data.toolCalls ?? [],
|
|
126
|
+
customRecords: data.customRecords ?? [],
|
|
127
|
+
llmCost: data.llmCost ?? 0,
|
|
128
|
+
additionalCosts: data.additionalCosts ?? [],
|
|
129
|
+
metadata: data.metadata ?? {},
|
|
130
|
+
costByModel: data.costByModel ?? {}
|
|
131
|
+
},
|
|
132
|
+
startTime
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Total cost of all additional (non-LLM) operations.
|
|
137
|
+
*/
|
|
138
|
+
get totalAdditionalCost() {
|
|
139
|
+
return this.additionalCosts.reduce((sum, c) => sum + c.cost, 0);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Total cost including LLM and additional costs.
|
|
143
|
+
*/
|
|
144
|
+
get totalCost() {
|
|
145
|
+
return this.llmCost + this.totalAdditionalCost;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Returns a new SessionSummary with an LLM call added.
|
|
149
|
+
*/
|
|
150
|
+
withLLMCall(call, newLlmCost, newCostByModel, newTotalUsage) {
|
|
151
|
+
return new _SessionSummary(
|
|
152
|
+
{
|
|
153
|
+
totalLLMUsage: newTotalUsage,
|
|
154
|
+
llmCalls: [...this.llmCalls, call],
|
|
155
|
+
toolCalls: [...this.toolCalls],
|
|
156
|
+
customRecords: [...this.customRecords],
|
|
157
|
+
llmCost: newLlmCost,
|
|
158
|
+
additionalCosts: [...this.additionalCosts],
|
|
159
|
+
metadata: { ...this.metadata },
|
|
160
|
+
costByModel: newCostByModel
|
|
161
|
+
},
|
|
162
|
+
this.startTime
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Returns a new SessionSummary with an additional cost recorded.
|
|
167
|
+
*/
|
|
168
|
+
withAdditionalCost(cost) {
|
|
169
|
+
return new _SessionSummary(
|
|
170
|
+
{
|
|
171
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
172
|
+
llmCalls: [...this.llmCalls],
|
|
173
|
+
toolCalls: [...this.toolCalls],
|
|
174
|
+
customRecords: [...this.customRecords],
|
|
175
|
+
llmCost: this.llmCost,
|
|
176
|
+
additionalCosts: [...this.additionalCosts, cost],
|
|
177
|
+
metadata: { ...this.metadata },
|
|
178
|
+
costByModel: { ...this.costByModel }
|
|
179
|
+
},
|
|
180
|
+
this.startTime
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Returns a new SessionSummary with metadata updated.
|
|
185
|
+
*/
|
|
186
|
+
withMetadata(key, value) {
|
|
187
|
+
return new _SessionSummary(
|
|
188
|
+
{
|
|
189
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
190
|
+
llmCalls: [...this.llmCalls],
|
|
191
|
+
toolCalls: [...this.toolCalls],
|
|
192
|
+
customRecords: [...this.customRecords],
|
|
193
|
+
llmCost: this.llmCost,
|
|
194
|
+
additionalCosts: [...this.additionalCosts],
|
|
195
|
+
metadata: { ...this.metadata, [key]: value },
|
|
196
|
+
costByModel: { ...this.costByModel }
|
|
197
|
+
},
|
|
198
|
+
this.startTime
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Returns a new SessionSummary with a tool call added.
|
|
203
|
+
*/
|
|
204
|
+
withToolCall(call) {
|
|
205
|
+
return new _SessionSummary(
|
|
206
|
+
{
|
|
207
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
208
|
+
llmCalls: [...this.llmCalls],
|
|
209
|
+
toolCalls: [...this.toolCalls, call],
|
|
210
|
+
customRecords: [...this.customRecords],
|
|
211
|
+
llmCost: this.llmCost,
|
|
212
|
+
additionalCosts: [...this.additionalCosts],
|
|
213
|
+
metadata: { ...this.metadata },
|
|
214
|
+
costByModel: { ...this.costByModel }
|
|
215
|
+
},
|
|
216
|
+
this.startTime
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Returns a new SessionSummary with a custom record added.
|
|
221
|
+
*/
|
|
222
|
+
withCustomRecord(record) {
|
|
223
|
+
return new _SessionSummary(
|
|
224
|
+
{
|
|
225
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
226
|
+
llmCalls: [...this.llmCalls],
|
|
227
|
+
toolCalls: [...this.toolCalls],
|
|
228
|
+
customRecords: [...this.customRecords, record],
|
|
229
|
+
llmCost: this.llmCost,
|
|
230
|
+
additionalCosts: [...this.additionalCosts],
|
|
231
|
+
metadata: { ...this.metadata },
|
|
232
|
+
costByModel: { ...this.costByModel }
|
|
233
|
+
},
|
|
234
|
+
this.startTime
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Serializes to plain JSON object for database storage.
|
|
239
|
+
*/
|
|
240
|
+
toJSON() {
|
|
241
|
+
return {
|
|
242
|
+
totalDuration: this.totalDuration,
|
|
243
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
244
|
+
llmCallCount: this.llmCallCount,
|
|
245
|
+
llmCalls: [...this.llmCalls],
|
|
246
|
+
toolCalls: [...this.toolCalls],
|
|
247
|
+
customRecords: [...this.customRecords],
|
|
248
|
+
llmCost: this.llmCost,
|
|
249
|
+
additionalCosts: [...this.additionalCosts],
|
|
250
|
+
metadata: { ...this.metadata },
|
|
251
|
+
costByModel: { ...this.costByModel },
|
|
252
|
+
totalCost: this.totalCost,
|
|
253
|
+
totalAdditionalCost: this.totalAdditionalCost
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/testing/fixtures.ts
|
|
259
|
+
var TEST_API_KEY = "test-api-key";
|
|
260
|
+
function createMockUsage(overrides) {
|
|
261
|
+
const inputTokens = overrides?.inputTokens ?? 10;
|
|
262
|
+
const outputTokens = overrides?.outputTokens ?? 5;
|
|
263
|
+
return {
|
|
264
|
+
inputTokens,
|
|
265
|
+
outputTokens,
|
|
266
|
+
totalTokens: overrides?.totalTokens ?? inputTokens + outputTokens,
|
|
267
|
+
inputTokenDetails: {
|
|
268
|
+
cacheReadTokens: void 0,
|
|
269
|
+
cacheWriteTokens: void 0,
|
|
270
|
+
noCacheTokens: void 0,
|
|
271
|
+
...overrides?.inputTokenDetails
|
|
272
|
+
},
|
|
273
|
+
outputTokenDetails: {
|
|
274
|
+
textTokens: void 0,
|
|
275
|
+
reasoningTokens: void 0,
|
|
276
|
+
...overrides?.outputTokenDetails
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function createTestEvent(type, overrides) {
|
|
281
|
+
return {
|
|
282
|
+
type,
|
|
283
|
+
...overrides
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function createDummyLLMCall() {
|
|
287
|
+
return {
|
|
288
|
+
startTime: Date.now() - 100,
|
|
289
|
+
endTime: Date.now(),
|
|
290
|
+
duration: 100,
|
|
291
|
+
usage: createMockUsage(),
|
|
292
|
+
type: "generateText",
|
|
293
|
+
model: "test-model",
|
|
294
|
+
provider: "openai"
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function createMockSessionSummary(overrides) {
|
|
298
|
+
let startTime = overrides?.startTime;
|
|
299
|
+
if (startTime === void 0 && overrides?.totalDuration !== void 0) {
|
|
300
|
+
startTime = Date.now() - overrides.totalDuration;
|
|
301
|
+
}
|
|
302
|
+
const llmCost = overrides?.llmCost ?? overrides?.totalCost ?? 0;
|
|
303
|
+
let llmCalls = overrides?.llmCalls;
|
|
304
|
+
if (!llmCalls && overrides?.llmCallCount !== void 0) {
|
|
305
|
+
llmCalls = Array.from({ length: overrides.llmCallCount }, () => createDummyLLMCall());
|
|
306
|
+
}
|
|
307
|
+
return SessionSummary.forTest({
|
|
308
|
+
startTime,
|
|
309
|
+
totalLLMUsage: overrides?.totalLLMUsage ?? createMockUsage(),
|
|
310
|
+
llmCalls: llmCalls ?? [],
|
|
311
|
+
toolCalls: overrides?.toolCalls ?? [],
|
|
312
|
+
customRecords: overrides?.customRecords ?? [],
|
|
313
|
+
llmCost,
|
|
314
|
+
additionalCosts: overrides?.additionalCosts ?? [],
|
|
315
|
+
metadata: overrides?.metadata ?? {},
|
|
316
|
+
costByModel: overrides?.costByModel ?? {}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/testing/helpers.ts
|
|
321
|
+
async function collectEvents(execution) {
|
|
322
|
+
const events = [];
|
|
323
|
+
const iterable = "stream" in execution && typeof execution.stream === "function" ? execution.stream() : execution;
|
|
324
|
+
for await (const event of iterable) {
|
|
325
|
+
events.push(event);
|
|
326
|
+
}
|
|
327
|
+
return events;
|
|
328
|
+
}
|
|
329
|
+
async function consumeExecution(execution) {
|
|
330
|
+
const iterable = "stream" in execution && typeof execution.stream === "function" ? execution.stream() : execution;
|
|
331
|
+
for await (const _event of iterable) {
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function expectFileManagerInterface(obj) {
|
|
335
|
+
const fm = obj;
|
|
336
|
+
const requiredMethods = ["upload", "delete", "clear", "getUploadedFiles"];
|
|
337
|
+
for (const method of requiredMethods) {
|
|
338
|
+
if (typeof fm[method] !== "function") {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`Expected FileManager.${method} to be a function, got ${typeof fm[method]}`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/observability/logger.ts
|
|
347
|
+
var noopLogger = {};
|
|
348
|
+
|
|
349
|
+
// src/execution/constants.ts
|
|
350
|
+
var ERRORS = {
|
|
351
|
+
NO_RESULT: "No result available"};
|
|
352
|
+
|
|
353
|
+
// src/execution/utils.ts
|
|
354
|
+
function combineSignals(...signals) {
|
|
355
|
+
const controller = new AbortController();
|
|
356
|
+
for (const signal of signals) {
|
|
357
|
+
if (signal.aborted) {
|
|
358
|
+
controller.abort(signal.reason);
|
|
359
|
+
return controller.signal;
|
|
360
|
+
}
|
|
361
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), {
|
|
362
|
+
once: true
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
return controller.signal;
|
|
366
|
+
}
|
|
367
|
+
var Deferred = class {
|
|
368
|
+
promise;
|
|
369
|
+
resolve;
|
|
370
|
+
reject;
|
|
371
|
+
constructor() {
|
|
372
|
+
let res;
|
|
373
|
+
let rej;
|
|
374
|
+
this.promise = new Promise((resolve, reject) => {
|
|
375
|
+
res = resolve;
|
|
376
|
+
rej = reject;
|
|
377
|
+
});
|
|
378
|
+
this.resolve = res;
|
|
379
|
+
this.reject = rej;
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// src/execution/shared.ts
|
|
384
|
+
function isAbortError(error, signal) {
|
|
385
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
return signal.aborted;
|
|
389
|
+
}
|
|
390
|
+
function normalizeError(error) {
|
|
391
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
392
|
+
}
|
|
393
|
+
function createHookRunner(runHooks) {
|
|
394
|
+
let ran = false;
|
|
395
|
+
return {
|
|
396
|
+
ensureRun: async () => {
|
|
397
|
+
if (!ran) {
|
|
398
|
+
ran = true;
|
|
399
|
+
await runHooks();
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
hasRun: () => ran
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/execution/streaming-host.ts
|
|
407
|
+
var StreamingExecutionHost = class {
|
|
408
|
+
constructor(createSession, generator, userSignal) {
|
|
409
|
+
this.createSession = createSession;
|
|
410
|
+
this.generator = generator;
|
|
411
|
+
this.effectiveSignal = userSignal ? combineSignals(userSignal, this.abortController.signal) : this.abortController.signal;
|
|
412
|
+
this.consumerPromise = this.startConsuming();
|
|
413
|
+
}
|
|
414
|
+
abortController = new AbortController();
|
|
415
|
+
effectiveSignal;
|
|
416
|
+
consumerPromise;
|
|
417
|
+
eventBuffer = [];
|
|
418
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
419
|
+
completed = false;
|
|
420
|
+
cleaned = false;
|
|
421
|
+
hookRunner = null;
|
|
422
|
+
cancelRequested = false;
|
|
423
|
+
extractedOutcome = null;
|
|
424
|
+
extractedSummary = null;
|
|
425
|
+
hasDataField(event) {
|
|
426
|
+
return "data" in event && event.data !== void 0;
|
|
427
|
+
}
|
|
428
|
+
hasSummaryField(event) {
|
|
429
|
+
return "summary" in event && event.summary !== void 0;
|
|
430
|
+
}
|
|
431
|
+
hasErrorField(event) {
|
|
432
|
+
return "error" in event && event.error instanceof Error;
|
|
433
|
+
}
|
|
434
|
+
extractResultAndMetadata(event) {
|
|
435
|
+
const isCompleteOrError = event.type === "complete" || event.type === "error";
|
|
436
|
+
if (!isCompleteOrError) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (this.hasErrorField(event)) {
|
|
440
|
+
this.extractedOutcome = { type: "error", error: event.error };
|
|
441
|
+
} else if (this.hasDataField(event)) {
|
|
442
|
+
this.extractedOutcome = { type: "result", value: event.data };
|
|
443
|
+
}
|
|
444
|
+
if (this.hasSummaryField(event)) {
|
|
445
|
+
this.extractedSummary = event.summary;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
notifySubscribers(event) {
|
|
449
|
+
this.subscribers.forEach((fn) => fn(event));
|
|
450
|
+
}
|
|
451
|
+
async startConsuming() {
|
|
452
|
+
const session = this.createSession(this.effectiveSignal);
|
|
453
|
+
this.hookRunner = createHookRunner(() => session.runOnDoneHooks());
|
|
454
|
+
const gen = this.generator(session);
|
|
455
|
+
try {
|
|
456
|
+
let next = await gen.next();
|
|
457
|
+
while (!next.done) {
|
|
458
|
+
this.eventBuffer.push(next.value);
|
|
459
|
+
this.notifySubscribers(next.value);
|
|
460
|
+
const isTerminal = next.value.type === "complete" || next.value.type === "error";
|
|
461
|
+
if (isTerminal) {
|
|
462
|
+
this.extractResultAndMetadata(next.value);
|
|
463
|
+
this.abortController.abort();
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
if (this.abortController.signal.aborted) {
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
next = await gen.next();
|
|
470
|
+
}
|
|
471
|
+
if (next.done && next.value !== void 0) {
|
|
472
|
+
const finalEvent = await Promise.resolve(next.value);
|
|
473
|
+
this.eventBuffer.push(finalEvent);
|
|
474
|
+
this.notifySubscribers(finalEvent);
|
|
475
|
+
this.extractResultAndMetadata(finalEvent);
|
|
476
|
+
const isTerminal = finalEvent.type === "complete" || finalEvent.type === "error";
|
|
477
|
+
if (isTerminal) {
|
|
478
|
+
this.abortController.abort();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return this.buildResult(session);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
const errorObj = normalizeError(error);
|
|
484
|
+
if (isAbortError(error, this.abortController.signal)) {
|
|
485
|
+
return {
|
|
486
|
+
success: false,
|
|
487
|
+
aborted: true,
|
|
488
|
+
error: errorObj,
|
|
489
|
+
summary: await session.getSummary()
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
const errorEvent = await session.fail(errorObj);
|
|
493
|
+
this.eventBuffer.push(errorEvent);
|
|
494
|
+
this.notifySubscribers(errorEvent);
|
|
495
|
+
this.extractResultAndMetadata(errorEvent);
|
|
496
|
+
this.abortController.abort();
|
|
497
|
+
return this.buildResult(session);
|
|
498
|
+
} finally {
|
|
499
|
+
this.completed = true;
|
|
500
|
+
await this.hookRunner?.ensureRun();
|
|
501
|
+
await gen.return(void 0);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
async buildResult(session) {
|
|
505
|
+
const summary = this.extractedSummary ?? await session.getSummary();
|
|
506
|
+
if (this.extractedOutcome?.type === "error") {
|
|
507
|
+
return {
|
|
508
|
+
success: false,
|
|
509
|
+
aborted: false,
|
|
510
|
+
error: this.extractedOutcome.error,
|
|
511
|
+
summary
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
if (this.extractedOutcome?.type === "result") {
|
|
515
|
+
return {
|
|
516
|
+
success: true,
|
|
517
|
+
result: this.extractedOutcome.value,
|
|
518
|
+
summary
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
return {
|
|
522
|
+
success: false,
|
|
523
|
+
aborted: true,
|
|
524
|
+
error: new Error(ERRORS.NO_RESULT),
|
|
525
|
+
summary
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Get the event stream.
|
|
530
|
+
* Returns buffered events first, then real-time events.
|
|
531
|
+
* Can be called multiple times - replays buffer each time.
|
|
532
|
+
*/
|
|
533
|
+
async *stream() {
|
|
534
|
+
let index = 0;
|
|
535
|
+
while (index < this.eventBuffer.length) {
|
|
536
|
+
yield this.eventBuffer[index++];
|
|
537
|
+
}
|
|
538
|
+
if (this.completed) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
const queue = [];
|
|
542
|
+
let pending = new Deferred();
|
|
543
|
+
const subscriber = (event) => {
|
|
544
|
+
queue.push(event);
|
|
545
|
+
pending.resolve();
|
|
546
|
+
};
|
|
547
|
+
this.subscribers.add(subscriber);
|
|
548
|
+
try {
|
|
549
|
+
while (!this.completed || queue.length > 0) {
|
|
550
|
+
if (queue.length > 0) {
|
|
551
|
+
yield queue.shift();
|
|
552
|
+
} else if (!this.completed) {
|
|
553
|
+
await pending.promise;
|
|
554
|
+
pending = new Deferred();
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} finally {
|
|
558
|
+
this.subscribers.delete(subscriber);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
cancel() {
|
|
562
|
+
this.cancelRequested = true;
|
|
563
|
+
this.abortController.abort();
|
|
564
|
+
}
|
|
565
|
+
async cleanup() {
|
|
566
|
+
if (this.cleaned) {
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
this.cleaned = true;
|
|
570
|
+
if (!this.completed) {
|
|
571
|
+
this.cancel();
|
|
572
|
+
await this.consumerPromise.catch(() => {
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
this.subscribers.clear();
|
|
576
|
+
await this.hookRunner?.ensureRun();
|
|
577
|
+
}
|
|
578
|
+
async [Symbol.asyncDispose]() {
|
|
579
|
+
await this.cleanup();
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Get the execution result with status, summary, and all events.
|
|
583
|
+
* Never throws - returns a discriminated union with status.
|
|
584
|
+
*/
|
|
585
|
+
async result() {
|
|
586
|
+
const internal = await this.consumerPromise;
|
|
587
|
+
const events = Object.freeze([...this.eventBuffer]);
|
|
588
|
+
if (internal.success) {
|
|
589
|
+
return {
|
|
590
|
+
status: "succeeded",
|
|
591
|
+
value: internal.result,
|
|
592
|
+
summary: internal.summary,
|
|
593
|
+
events
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
if (this.cancelRequested || internal.aborted) {
|
|
597
|
+
return {
|
|
598
|
+
status: "canceled",
|
|
599
|
+
summary: internal.summary,
|
|
600
|
+
events
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
status: "failed",
|
|
605
|
+
error: internal.error,
|
|
606
|
+
summary: internal.summary,
|
|
607
|
+
events
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// src/execution/simple-host.ts
|
|
613
|
+
var SimpleExecutionHost = class {
|
|
614
|
+
abortController = new AbortController();
|
|
615
|
+
effectiveSignal;
|
|
616
|
+
consumerPromise;
|
|
617
|
+
cachedSession;
|
|
618
|
+
startTime = Date.now();
|
|
619
|
+
cancelRequested = false;
|
|
620
|
+
constructor(createSession, fn, userSignal) {
|
|
621
|
+
this.effectiveSignal = userSignal ? combineSignals(userSignal, this.abortController.signal) : this.abortController.signal;
|
|
622
|
+
this.consumerPromise = this.execute(createSession, fn);
|
|
623
|
+
}
|
|
624
|
+
async execute(createSession, fn) {
|
|
625
|
+
const session = createSession(this.effectiveSignal);
|
|
626
|
+
this.cachedSession = session;
|
|
627
|
+
const hookRunner = createHookRunner(() => session.runOnDoneHooks());
|
|
628
|
+
session.notifyExecutionStart();
|
|
629
|
+
try {
|
|
630
|
+
const result = await fn(session);
|
|
631
|
+
await session.notifyExecutionDone(result, this.startTime);
|
|
632
|
+
return {
|
|
633
|
+
success: true,
|
|
634
|
+
result,
|
|
635
|
+
summary: await session.getSummary()
|
|
636
|
+
};
|
|
637
|
+
} catch (error) {
|
|
638
|
+
const errorObj = normalizeError(error);
|
|
639
|
+
const isCancellation = isAbortError(error, this.abortController.signal);
|
|
640
|
+
if (!isCancellation) {
|
|
641
|
+
await session.notifyExecutionError(errorObj, this.startTime);
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
success: false,
|
|
645
|
+
error: errorObj,
|
|
646
|
+
aborted: isCancellation,
|
|
647
|
+
summary: await session.getSummary()
|
|
648
|
+
};
|
|
649
|
+
} finally {
|
|
650
|
+
await hookRunner.ensureRun();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Request cancellation of the execution.
|
|
655
|
+
* Aborts the current LLM call if in progress.
|
|
656
|
+
* No-op if execution already completed.
|
|
657
|
+
*/
|
|
658
|
+
cancel() {
|
|
659
|
+
this.cancelRequested = true;
|
|
660
|
+
this.abortController.abort();
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Get the execution result with status and summary.
|
|
664
|
+
* Never throws - returns a discriminated union with status.
|
|
665
|
+
*/
|
|
666
|
+
async result() {
|
|
667
|
+
const internal = await this.consumerPromise;
|
|
668
|
+
if (internal.success) {
|
|
669
|
+
return {
|
|
670
|
+
status: "succeeded",
|
|
671
|
+
value: internal.result,
|
|
672
|
+
summary: internal.summary
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
if (this.cancelRequested || internal.aborted) {
|
|
676
|
+
return {
|
|
677
|
+
status: "canceled",
|
|
678
|
+
summary: internal.summary
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
status: "failed",
|
|
683
|
+
error: internal.error,
|
|
684
|
+
summary: internal.summary
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Cleanup resources.
|
|
689
|
+
* For SimpleExecution, hooks are already run during execution,
|
|
690
|
+
* so this is intentionally a no-op.
|
|
691
|
+
*/
|
|
692
|
+
async cleanup() {
|
|
693
|
+
}
|
|
694
|
+
async [Symbol.asyncDispose]() {
|
|
695
|
+
await this.cleanup();
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// src/provider/base-provider.ts
|
|
700
|
+
var BaseProvider = class {
|
|
701
|
+
streamingExecution(generator, options) {
|
|
702
|
+
return new StreamingExecutionHost(
|
|
703
|
+
(signal) => this.createStreamingSession(signal),
|
|
704
|
+
generator,
|
|
705
|
+
options?.signal
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Execute a non-streaming function with cancellation support.
|
|
710
|
+
* Returns immediately - execution starts in the background.
|
|
711
|
+
*/
|
|
712
|
+
simpleExecution(fn, options) {
|
|
713
|
+
return new SimpleExecutionHost(
|
|
714
|
+
(signal) => this.createSimpleSession(signal),
|
|
715
|
+
fn,
|
|
716
|
+
options?.signal
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// src/pricing/defaults.ts
|
|
722
|
+
var OPENAI_PRICING = {
|
|
723
|
+
"gpt-4o": { inputPricePerMillion: 2.5, outputPricePerMillion: 10 },
|
|
724
|
+
"gpt-4o-mini": { inputPricePerMillion: 0.15, outputPricePerMillion: 0.6 },
|
|
725
|
+
"gpt-4-turbo": { inputPricePerMillion: 10, outputPricePerMillion: 30 },
|
|
726
|
+
"gpt-4-turbo-preview": {
|
|
727
|
+
inputPricePerMillion: 10,
|
|
728
|
+
outputPricePerMillion: 30
|
|
729
|
+
},
|
|
730
|
+
"gpt-4": { inputPricePerMillion: 30, outputPricePerMillion: 60 },
|
|
731
|
+
"gpt-4-32k": { inputPricePerMillion: 60, outputPricePerMillion: 120 },
|
|
732
|
+
"gpt-3.5-turbo": { inputPricePerMillion: 0.5, outputPricePerMillion: 1.5 },
|
|
733
|
+
"gpt-3.5-turbo-16k": {
|
|
734
|
+
inputPricePerMillion: 3,
|
|
735
|
+
outputPricePerMillion: 4
|
|
736
|
+
},
|
|
737
|
+
"o1": { inputPricePerMillion: 15, outputPricePerMillion: 60 },
|
|
738
|
+
"o1-mini": { inputPricePerMillion: 3, outputPricePerMillion: 12 },
|
|
739
|
+
"o1-preview": { inputPricePerMillion: 15, outputPricePerMillion: 60 },
|
|
740
|
+
"o3": { inputPricePerMillion: 20, outputPricePerMillion: 80 },
|
|
741
|
+
"o3-mini": { inputPricePerMillion: 4, outputPricePerMillion: 16 }
|
|
742
|
+
};
|
|
743
|
+
var GOOGLE_PRICING = {
|
|
744
|
+
"gemini-2.5-flash": {
|
|
745
|
+
inputPricePerMillion: 0.15,
|
|
746
|
+
outputPricePerMillion: 0.6,
|
|
747
|
+
cachedInputPricePerMillion: 0.0375
|
|
748
|
+
},
|
|
749
|
+
"gemini-2.5-flash-lite": {
|
|
750
|
+
inputPricePerMillion: 0.075,
|
|
751
|
+
outputPricePerMillion: 0.3,
|
|
752
|
+
cachedInputPricePerMillion: 0.01875
|
|
753
|
+
},
|
|
754
|
+
"gemini-2.5-pro": {
|
|
755
|
+
inputPricePerMillion: 1.25,
|
|
756
|
+
outputPricePerMillion: 10,
|
|
757
|
+
cachedInputPricePerMillion: 0.3125
|
|
758
|
+
},
|
|
759
|
+
"gemini-2.0-flash": {
|
|
760
|
+
inputPricePerMillion: 0.1,
|
|
761
|
+
outputPricePerMillion: 0.4,
|
|
762
|
+
cachedInputPricePerMillion: 0.025
|
|
763
|
+
},
|
|
764
|
+
"gemini-2.0-flash-lite": {
|
|
765
|
+
inputPricePerMillion: 0.075,
|
|
766
|
+
outputPricePerMillion: 0.3,
|
|
767
|
+
cachedInputPricePerMillion: 0.01875
|
|
768
|
+
},
|
|
769
|
+
"gemini-1.5-pro": {
|
|
770
|
+
inputPricePerMillion: 1.25,
|
|
771
|
+
outputPricePerMillion: 5,
|
|
772
|
+
cachedInputPricePerMillion: 0.3125
|
|
773
|
+
},
|
|
774
|
+
"gemini-1.5-flash": {
|
|
775
|
+
inputPricePerMillion: 0.075,
|
|
776
|
+
outputPricePerMillion: 0.3,
|
|
777
|
+
cachedInputPricePerMillion: 0.01875
|
|
778
|
+
},
|
|
779
|
+
"gemini-1.5-flash-8b": {
|
|
780
|
+
inputPricePerMillion: 0.0375,
|
|
781
|
+
outputPricePerMillion: 0.15,
|
|
782
|
+
cachedInputPricePerMillion: 0.01
|
|
783
|
+
},
|
|
784
|
+
"gemini-pro": { inputPricePerMillion: 0.5, outputPricePerMillion: 1.5 }
|
|
785
|
+
};
|
|
786
|
+
var ANTHROPIC_PRICING = {
|
|
787
|
+
"claude-opus-4-5-20250514": {
|
|
788
|
+
inputPricePerMillion: 15,
|
|
789
|
+
outputPricePerMillion: 75,
|
|
790
|
+
cachedInputPricePerMillion: 1.875
|
|
791
|
+
},
|
|
792
|
+
"claude-sonnet-4-20250514": {
|
|
793
|
+
inputPricePerMillion: 3,
|
|
794
|
+
outputPricePerMillion: 15,
|
|
795
|
+
cachedInputPricePerMillion: 0.375
|
|
796
|
+
},
|
|
797
|
+
"claude-3-5-sonnet-20241022": {
|
|
798
|
+
inputPricePerMillion: 3,
|
|
799
|
+
outputPricePerMillion: 15,
|
|
800
|
+
cachedInputPricePerMillion: 0.375
|
|
801
|
+
},
|
|
802
|
+
"claude-3-5-haiku-20241022": {
|
|
803
|
+
inputPricePerMillion: 0.8,
|
|
804
|
+
outputPricePerMillion: 4,
|
|
805
|
+
cachedInputPricePerMillion: 0.1
|
|
806
|
+
},
|
|
807
|
+
"claude-3-opus-20240229": {
|
|
808
|
+
inputPricePerMillion: 15,
|
|
809
|
+
outputPricePerMillion: 75,
|
|
810
|
+
cachedInputPricePerMillion: 1.875
|
|
811
|
+
},
|
|
812
|
+
"claude-3-sonnet-20240229": {
|
|
813
|
+
inputPricePerMillion: 3,
|
|
814
|
+
outputPricePerMillion: 15,
|
|
815
|
+
cachedInputPricePerMillion: 0.375
|
|
816
|
+
},
|
|
817
|
+
"claude-3-haiku-20240307": {
|
|
818
|
+
inputPricePerMillion: 0.25,
|
|
819
|
+
outputPricePerMillion: 1.25,
|
|
820
|
+
cachedInputPricePerMillion: 0.03
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
var DEFAULT_PRICING_CONFIG = {
|
|
824
|
+
providers: {
|
|
825
|
+
openai: OPENAI_PRICING,
|
|
826
|
+
google: GOOGLE_PRICING,
|
|
827
|
+
anthropic: ANTHROPIC_PRICING
|
|
828
|
+
},
|
|
829
|
+
fallback: {
|
|
830
|
+
inputPricePerMillion: 1,
|
|
831
|
+
outputPricePerMillion: 5
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
var DEFAULT_FALLBACK_PRICING = {
|
|
835
|
+
inputPricePerMillion: 1,
|
|
836
|
+
outputPricePerMillion: 5
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
// src/pricing/config.ts
|
|
840
|
+
var globalConfig;
|
|
841
|
+
function getPricingConfig() {
|
|
842
|
+
return globalConfig;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// src/pricing/calculator.ts
|
|
846
|
+
var TOKENS_PER_MILLION = 1e6;
|
|
847
|
+
function getModelPricing(model, provider, providerPricing) {
|
|
848
|
+
if (providerPricing?.[model]) {
|
|
849
|
+
return providerPricing[model];
|
|
850
|
+
}
|
|
851
|
+
const globalConfig2 = getPricingConfig();
|
|
852
|
+
const defaultProviderPricing = DEFAULT_PRICING_CONFIG.providers[provider];
|
|
853
|
+
if (defaultProviderPricing?.[model]) {
|
|
854
|
+
return defaultProviderPricing[model];
|
|
855
|
+
}
|
|
856
|
+
return globalConfig2?.fallback ?? DEFAULT_PRICING_CONFIG.fallback ?? DEFAULT_FALLBACK_PRICING;
|
|
857
|
+
}
|
|
858
|
+
function validateCostParams(params) {
|
|
859
|
+
const { inputTokens, outputTokens, cachedInputTokens = 0 } = params;
|
|
860
|
+
if (inputTokens < 0 || outputTokens < 0 || cachedInputTokens < 0) {
|
|
861
|
+
throw new Error("Token counts must be non-negative");
|
|
862
|
+
}
|
|
863
|
+
if (cachedInputTokens > inputTokens) {
|
|
864
|
+
throw new Error("cachedInputTokens cannot exceed inputTokens");
|
|
865
|
+
}
|
|
866
|
+
if (!Number.isFinite(inputTokens) || !Number.isFinite(outputTokens) || !Number.isFinite(cachedInputTokens)) {
|
|
867
|
+
throw new Error("Token counts must be finite numbers");
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function calculateCost(params, providerPricing) {
|
|
871
|
+
validateCostParams(params);
|
|
872
|
+
const {
|
|
873
|
+
inputTokens,
|
|
874
|
+
outputTokens,
|
|
875
|
+
cachedInputTokens = 0,
|
|
876
|
+
model,
|
|
877
|
+
provider
|
|
878
|
+
} = params;
|
|
879
|
+
const pricing = getModelPricing(model, provider, providerPricing);
|
|
880
|
+
const nonCachedInputTokens = inputTokens - cachedInputTokens;
|
|
881
|
+
const inputCost = nonCachedInputTokens / TOKENS_PER_MILLION * pricing.inputPricePerMillion;
|
|
882
|
+
const outputCost = outputTokens / TOKENS_PER_MILLION * pricing.outputPricePerMillion;
|
|
883
|
+
const cachedInputPricePerMillion = pricing.cachedInputPricePerMillion ?? pricing.inputPricePerMillion;
|
|
884
|
+
const cachedInputCost = cachedInputTokens / TOKENS_PER_MILLION * cachedInputPricePerMillion;
|
|
885
|
+
return {
|
|
886
|
+
total: inputCost + outputCost + cachedInputCost,
|
|
887
|
+
inputCost,
|
|
888
|
+
outputCost,
|
|
889
|
+
cachedInputCost
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
function calculateCostFromUsage(usage, model, provider, providerPricing) {
|
|
893
|
+
return calculateCost(
|
|
894
|
+
{
|
|
895
|
+
inputTokens: usage.inputTokens ?? 0,
|
|
896
|
+
outputTokens: usage.outputTokens ?? 0,
|
|
897
|
+
cachedInputTokens: usage.inputTokenDetails?.cacheReadTokens ?? 0,
|
|
898
|
+
model,
|
|
899
|
+
provider
|
|
900
|
+
},
|
|
901
|
+
providerPricing
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
function calculateTotalCost(calls, providerPricing) {
|
|
905
|
+
const costByModel = {};
|
|
906
|
+
let totalCost = 0;
|
|
907
|
+
for (const call of calls) {
|
|
908
|
+
const cost = calculateCostFromUsage(
|
|
909
|
+
call.usage,
|
|
910
|
+
call.model,
|
|
911
|
+
call.provider,
|
|
912
|
+
providerPricing
|
|
913
|
+
);
|
|
914
|
+
totalCost += cost.total;
|
|
915
|
+
const key = `${call.provider}/${call.model}`;
|
|
916
|
+
costByModel[key] = (costByModel[key] ?? 0) + cost.total;
|
|
917
|
+
}
|
|
918
|
+
return { totalCost, costByModel };
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// src/session/simple-session.ts
|
|
922
|
+
var SimpleSession = class {
|
|
923
|
+
defaultLanguageModel;
|
|
924
|
+
modelFactory;
|
|
925
|
+
providerType;
|
|
926
|
+
providerPricing;
|
|
927
|
+
defaultProviderOptions;
|
|
928
|
+
defaultTools;
|
|
929
|
+
_fileManager;
|
|
930
|
+
logger;
|
|
931
|
+
sessionStartTime;
|
|
932
|
+
signal;
|
|
933
|
+
summary;
|
|
934
|
+
pendingUsagePromises = [];
|
|
935
|
+
onDoneFns = [];
|
|
936
|
+
constructor(options) {
|
|
937
|
+
this.defaultLanguageModel = options.defaultLanguageModel ?? null;
|
|
938
|
+
this.modelFactory = options.modelFactory ?? null;
|
|
939
|
+
this.providerType = options.providerType;
|
|
940
|
+
this.providerPricing = options.providerPricing;
|
|
941
|
+
this.defaultProviderOptions = options.defaultProviderOptions;
|
|
942
|
+
this.defaultTools = options.defaultTools;
|
|
943
|
+
this._fileManager = options.fileManager;
|
|
944
|
+
this.logger = options.logger ?? noopLogger;
|
|
945
|
+
this.sessionStartTime = options.startTime ?? Date.now();
|
|
946
|
+
this.signal = options.signal;
|
|
947
|
+
this.summary = SessionSummary.empty(this.sessionStartTime);
|
|
948
|
+
}
|
|
949
|
+
getModel(requestedModelId) {
|
|
950
|
+
if (requestedModelId) {
|
|
951
|
+
if (!this.modelFactory) {
|
|
952
|
+
throw new Error(
|
|
953
|
+
`Model '${requestedModelId}' requested but no modelFactory provided. Either use the default model or configure the provider with modelFactory.`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
return this.modelFactory(requestedModelId);
|
|
957
|
+
}
|
|
958
|
+
if (!this.defaultLanguageModel) {
|
|
959
|
+
throw new Error(
|
|
960
|
+
"No model specified and no default model set. Either specify a model in the call or configure the provider with withDefaultModel()."
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
return this.defaultLanguageModel;
|
|
964
|
+
}
|
|
965
|
+
extractModelId(model) {
|
|
966
|
+
const modelWithId = model;
|
|
967
|
+
if (!modelWithId.modelId) {
|
|
968
|
+
console.warn(
|
|
969
|
+
'[SimpleSession] Model does not have modelId property, using "unknown". This may affect cost tracking accuracy.'
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
return modelWithId.modelId ?? "unknown";
|
|
973
|
+
}
|
|
974
|
+
async generateText(params) {
|
|
975
|
+
const callStartTime = Date.now();
|
|
976
|
+
const { model: requestedModel, providerOptions, tools, ...restParams } = params;
|
|
977
|
+
const languageModel = this.getModel(requestedModel);
|
|
978
|
+
const modelId = this.extractModelId(languageModel);
|
|
979
|
+
const mergedProviderOptions = this.defaultProviderOptions || providerOptions ? merge__default.default({}, this.defaultProviderOptions ?? {}, providerOptions ?? {}) : void 0;
|
|
980
|
+
const mergedTools = this.defaultTools || tools ? { ...this.defaultTools, ...tools } : void 0;
|
|
981
|
+
this.logger.onLLMCallStart?.({
|
|
982
|
+
type: "llm_call_start",
|
|
983
|
+
callType: "generateText",
|
|
984
|
+
modelId,
|
|
985
|
+
timestamp: callStartTime,
|
|
986
|
+
request: { params: restParams }
|
|
987
|
+
});
|
|
988
|
+
try {
|
|
989
|
+
const result = await ai.generateText({
|
|
990
|
+
...restParams,
|
|
991
|
+
tools: mergedTools,
|
|
992
|
+
providerOptions: mergedProviderOptions,
|
|
993
|
+
model: languageModel,
|
|
994
|
+
abortSignal: this.signal
|
|
995
|
+
});
|
|
996
|
+
const callEndTime = Date.now();
|
|
997
|
+
const call = {
|
|
998
|
+
startTime: callStartTime,
|
|
999
|
+
endTime: callEndTime,
|
|
1000
|
+
duration: callEndTime - callStartTime,
|
|
1001
|
+
usage: result.usage ?? createZeroUsage(),
|
|
1002
|
+
type: "generateText",
|
|
1003
|
+
model: modelId,
|
|
1004
|
+
provider: this.providerType
|
|
1005
|
+
};
|
|
1006
|
+
this.updateSummaryWithLLMCall(call);
|
|
1007
|
+
this.logger.onLLMCallEnd?.({
|
|
1008
|
+
type: "llm_call_end",
|
|
1009
|
+
callType: "generateText",
|
|
1010
|
+
modelId,
|
|
1011
|
+
timestamp: callEndTime,
|
|
1012
|
+
response: {
|
|
1013
|
+
duration: callEndTime - callStartTime,
|
|
1014
|
+
usage: result.usage,
|
|
1015
|
+
raw: result
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
return result;
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
const callEndTime = Date.now();
|
|
1021
|
+
this.logger.onLLMCallEnd?.({
|
|
1022
|
+
type: "llm_call_end",
|
|
1023
|
+
callType: "generateText",
|
|
1024
|
+
modelId,
|
|
1025
|
+
timestamp: callEndTime,
|
|
1026
|
+
response: {
|
|
1027
|
+
duration: callEndTime - callStartTime,
|
|
1028
|
+
raw: null,
|
|
1029
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
throw error;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
streamText(params) {
|
|
1036
|
+
const callStartTime = Date.now();
|
|
1037
|
+
const { model: requestedModel, providerOptions, tools, ...restParams } = params;
|
|
1038
|
+
const languageModel = this.getModel(requestedModel);
|
|
1039
|
+
const modelId = this.extractModelId(languageModel);
|
|
1040
|
+
const mergedProviderOptions = this.defaultProviderOptions || providerOptions ? merge__default.default({}, this.defaultProviderOptions ?? {}, providerOptions ?? {}) : void 0;
|
|
1041
|
+
const mergedTools = this.defaultTools || tools ? { ...this.defaultTools, ...tools } : void 0;
|
|
1042
|
+
this.logger.onLLMCallStart?.({
|
|
1043
|
+
type: "llm_call_start",
|
|
1044
|
+
callType: "streamText",
|
|
1045
|
+
modelId,
|
|
1046
|
+
timestamp: callStartTime,
|
|
1047
|
+
request: { params: restParams }
|
|
1048
|
+
});
|
|
1049
|
+
const result = ai.streamText({
|
|
1050
|
+
...restParams,
|
|
1051
|
+
tools: mergedTools,
|
|
1052
|
+
providerOptions: mergedProviderOptions,
|
|
1053
|
+
model: languageModel,
|
|
1054
|
+
abortSignal: this.signal
|
|
1055
|
+
});
|
|
1056
|
+
const usagePromise = Promise.resolve(result.usage).then((usage) => {
|
|
1057
|
+
const callEndTime = Date.now();
|
|
1058
|
+
const call = {
|
|
1059
|
+
startTime: callStartTime,
|
|
1060
|
+
endTime: callEndTime,
|
|
1061
|
+
duration: callEndTime - callStartTime,
|
|
1062
|
+
usage: usage ?? createZeroUsage(),
|
|
1063
|
+
type: "streamText",
|
|
1064
|
+
model: modelId,
|
|
1065
|
+
provider: this.providerType
|
|
1066
|
+
};
|
|
1067
|
+
this.updateSummaryWithLLMCall(call);
|
|
1068
|
+
this.logger.onLLMCallEnd?.({
|
|
1069
|
+
type: "llm_call_end",
|
|
1070
|
+
callType: "streamText",
|
|
1071
|
+
modelId,
|
|
1072
|
+
timestamp: callEndTime,
|
|
1073
|
+
response: {
|
|
1074
|
+
duration: callEndTime - callStartTime,
|
|
1075
|
+
usage,
|
|
1076
|
+
raw: result
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
return usage;
|
|
1080
|
+
});
|
|
1081
|
+
this.pendingUsagePromises.push(usagePromise);
|
|
1082
|
+
return result;
|
|
1083
|
+
}
|
|
1084
|
+
get fileManager() {
|
|
1085
|
+
return this._fileManager;
|
|
1086
|
+
}
|
|
1087
|
+
record(data) {
|
|
1088
|
+
this.summary = this.summary.withCustomRecord(data);
|
|
1089
|
+
}
|
|
1090
|
+
recordToolCall(toolCallSummary) {
|
|
1091
|
+
this.summary = this.summary.withToolCall(toolCallSummary);
|
|
1092
|
+
}
|
|
1093
|
+
recordLLMCall(record) {
|
|
1094
|
+
const call = {
|
|
1095
|
+
...record,
|
|
1096
|
+
type: record.type ?? "manual"
|
|
1097
|
+
};
|
|
1098
|
+
this.updateSummaryWithLLMCall(call);
|
|
1099
|
+
}
|
|
1100
|
+
recordAdditionalCost(cost) {
|
|
1101
|
+
this.summary = this.summary.withAdditionalCost({
|
|
1102
|
+
...cost,
|
|
1103
|
+
timestamp: Date.now()
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
setMetadata(keyOrData, value) {
|
|
1107
|
+
if (typeof keyOrData === "string") {
|
|
1108
|
+
this.summary = this.summary.withMetadata(keyOrData, value);
|
|
1109
|
+
} else {
|
|
1110
|
+
for (const [k, v] of Object.entries(keyOrData)) {
|
|
1111
|
+
this.summary = this.summary.withMetadata(k, v);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
updateSummaryWithLLMCall(call) {
|
|
1116
|
+
const newCalls = [...this.summary.llmCalls, call];
|
|
1117
|
+
const { totalCost: llmCost, costByModel } = calculateTotalCost(
|
|
1118
|
+
newCalls.map((c) => ({ usage: c.usage, model: c.model, provider: c.provider })),
|
|
1119
|
+
this.providerPricing
|
|
1120
|
+
);
|
|
1121
|
+
const newTotalUsage = mergeUsages(newCalls.map((c) => c.usage));
|
|
1122
|
+
this.summary = this.summary.withLLMCall(call, llmCost, costByModel, newTotalUsage);
|
|
1123
|
+
}
|
|
1124
|
+
onDone(fn) {
|
|
1125
|
+
this.onDoneFns.push(fn);
|
|
1126
|
+
}
|
|
1127
|
+
async runOnDoneHooks() {
|
|
1128
|
+
const reversedHooks = [...this.onDoneFns].reverse();
|
|
1129
|
+
for (const fn of reversedHooks) {
|
|
1130
|
+
try {
|
|
1131
|
+
await fn();
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
console.error("[SimpleSession] onDone hook error:", error);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
async getSummary() {
|
|
1138
|
+
await Promise.all(this.pendingUsagePromises);
|
|
1139
|
+
return this.summary;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Notifies Logger of execution start.
|
|
1143
|
+
* @internal Called by SimpleExecutionHost - not intended for direct use.
|
|
1144
|
+
*/
|
|
1145
|
+
notifyExecutionStart() {
|
|
1146
|
+
this._logger.onExecutionStart?.({
|
|
1147
|
+
type: "execution_start",
|
|
1148
|
+
timestamp: Date.now()
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Notifies Logger of execution completion with result data and summary.
|
|
1153
|
+
* @param data - The execution result data
|
|
1154
|
+
* @param startTime - Execution start timestamp for duration calculation
|
|
1155
|
+
* @internal Called by SimpleExecutionHost - not intended for direct use.
|
|
1156
|
+
*/
|
|
1157
|
+
async notifyExecutionDone(data, startTime) {
|
|
1158
|
+
const summary = await this.getSummary();
|
|
1159
|
+
this._logger.onExecutionDone?.({
|
|
1160
|
+
type: "execution_done",
|
|
1161
|
+
timestamp: Date.now(),
|
|
1162
|
+
duration: Date.now() - startTime,
|
|
1163
|
+
data,
|
|
1164
|
+
summary
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Notifies Logger of execution error with error details and summary (if available).
|
|
1169
|
+
* Gracefully handles getSummary() failures - summary will be undefined if it fails.
|
|
1170
|
+
* @param error - The error that occurred
|
|
1171
|
+
* @param startTime - Execution start timestamp for duration calculation
|
|
1172
|
+
* @internal Called by SimpleExecutionHost - not intended for direct use.
|
|
1173
|
+
*/
|
|
1174
|
+
async notifyExecutionError(error, startTime) {
|
|
1175
|
+
let summary;
|
|
1176
|
+
try {
|
|
1177
|
+
summary = await this.getSummary();
|
|
1178
|
+
} catch {
|
|
1179
|
+
}
|
|
1180
|
+
this._logger.onExecutionError?.({
|
|
1181
|
+
type: "execution_error",
|
|
1182
|
+
timestamp: Date.now(),
|
|
1183
|
+
duration: Date.now() - startTime,
|
|
1184
|
+
error,
|
|
1185
|
+
summary
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
get _logger() {
|
|
1189
|
+
return this.logger;
|
|
1190
|
+
}
|
|
1191
|
+
get _startTime() {
|
|
1192
|
+
return this.sessionStartTime;
|
|
1193
|
+
}
|
|
1194
|
+
get _modelId() {
|
|
1195
|
+
if (!this.defaultLanguageModel) {
|
|
1196
|
+
return "unknown";
|
|
1197
|
+
}
|
|
1198
|
+
return this.extractModelId(this.defaultLanguageModel);
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
// src/session/streaming-session.ts
|
|
1203
|
+
var StreamingSession = class extends SimpleSession {
|
|
1204
|
+
lastEventTime;
|
|
1205
|
+
_terminated = false;
|
|
1206
|
+
constructor(options) {
|
|
1207
|
+
super({
|
|
1208
|
+
defaultLanguageModel: options.defaultLanguageModel,
|
|
1209
|
+
modelFactory: options.modelFactory,
|
|
1210
|
+
providerType: options.providerType,
|
|
1211
|
+
providerPricing: options.providerPricing,
|
|
1212
|
+
fileManager: options.fileManager,
|
|
1213
|
+
logger: options.logger,
|
|
1214
|
+
startTime: options.startTime,
|
|
1215
|
+
signal: options.signal,
|
|
1216
|
+
defaultProviderOptions: options.defaultProviderOptions,
|
|
1217
|
+
defaultTools: options.defaultTools
|
|
1218
|
+
});
|
|
1219
|
+
this.lastEventTime = this._startTime;
|
|
1220
|
+
this._logger.onExecutionStart?.({
|
|
1221
|
+
type: "execution_start",
|
|
1222
|
+
timestamp: Date.now()
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Emits a streaming event with automatically attached metrics.
|
|
1227
|
+
*
|
|
1228
|
+
* Reserved types ('complete', 'error') throw at runtime - use session.done()
|
|
1229
|
+
* or session.fail() instead.
|
|
1230
|
+
*
|
|
1231
|
+
* @param event - The event to emit (metrics will be added automatically)
|
|
1232
|
+
* @returns The complete event with metrics attached
|
|
1233
|
+
* @throws Error when attempting to emit reserved types ('complete', 'error')
|
|
1234
|
+
*/
|
|
1235
|
+
emit(event) {
|
|
1236
|
+
if (this._terminated) {
|
|
1237
|
+
throw new Error("Session already terminated. Cannot call emit() after a terminal operation.");
|
|
1238
|
+
}
|
|
1239
|
+
const eventType = event.type;
|
|
1240
|
+
if (eventType === "complete" || eventType === "error") {
|
|
1241
|
+
throw new Error(
|
|
1242
|
+
`Cannot emit reserved type "${eventType}". Use session.done() for completion or session.fail() for errors.`
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
return this.emitInternal(event);
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Internal emit method - bypasses reserved type check.
|
|
1249
|
+
* Used by done() and fail() to emit terminal events.
|
|
1250
|
+
*/
|
|
1251
|
+
emitInternal(event) {
|
|
1252
|
+
const metrics = this.createMetrics();
|
|
1253
|
+
const fullEvent = { ...event, metrics };
|
|
1254
|
+
this._logger.onExecutionEmit?.({
|
|
1255
|
+
type: "execution_emit",
|
|
1256
|
+
event: fullEvent
|
|
1257
|
+
});
|
|
1258
|
+
return fullEvent;
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Signals successful completion of the streaming execution.
|
|
1262
|
+
* Emits a 'complete' event with the result data and session summary.
|
|
1263
|
+
* Also triggers Logger.onExecutionDone for observability.
|
|
1264
|
+
* @param data - The final result data
|
|
1265
|
+
* @returns The complete event with data and summary
|
|
1266
|
+
*/
|
|
1267
|
+
async done(data) {
|
|
1268
|
+
if (this._terminated) {
|
|
1269
|
+
throw new Error("Session already terminated. Cannot call done() after a terminal operation.");
|
|
1270
|
+
}
|
|
1271
|
+
this._terminated = true;
|
|
1272
|
+
const summary = await this.getSummary();
|
|
1273
|
+
this._logger.onExecutionDone?.({
|
|
1274
|
+
type: "execution_done",
|
|
1275
|
+
timestamp: Date.now(),
|
|
1276
|
+
duration: summary.totalDuration,
|
|
1277
|
+
data,
|
|
1278
|
+
summary
|
|
1279
|
+
});
|
|
1280
|
+
return this.emitInternal({
|
|
1281
|
+
type: "complete",
|
|
1282
|
+
data,
|
|
1283
|
+
summary
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Signals that the streaming execution failed with an error.
|
|
1288
|
+
* Emits an 'error' event and triggers Logger.onExecutionError for observability.
|
|
1289
|
+
* Gracefully handles getSummary() failures - summary will be undefined if it fails.
|
|
1290
|
+
* @param error - The error that caused the failure
|
|
1291
|
+
* @param data - Optional partial result data (if any was produced before failure)
|
|
1292
|
+
* @returns The error event
|
|
1293
|
+
*/
|
|
1294
|
+
async fail(error, data) {
|
|
1295
|
+
if (this._terminated) {
|
|
1296
|
+
throw new Error("Session already terminated. Cannot call fail() after a terminal operation.");
|
|
1297
|
+
}
|
|
1298
|
+
this._terminated = true;
|
|
1299
|
+
let summary;
|
|
1300
|
+
try {
|
|
1301
|
+
summary = await this.getSummary();
|
|
1302
|
+
} catch {
|
|
1303
|
+
}
|
|
1304
|
+
this._logger.onExecutionError?.({
|
|
1305
|
+
type: "execution_error",
|
|
1306
|
+
timestamp: Date.now(),
|
|
1307
|
+
duration: summary?.totalDuration ?? Date.now() - this._startTime,
|
|
1308
|
+
error,
|
|
1309
|
+
data,
|
|
1310
|
+
summary
|
|
1311
|
+
});
|
|
1312
|
+
const errorEvent = {
|
|
1313
|
+
type: "error",
|
|
1314
|
+
error
|
|
1315
|
+
};
|
|
1316
|
+
if (summary) {
|
|
1317
|
+
errorEvent.summary = summary;
|
|
1318
|
+
}
|
|
1319
|
+
if (data !== void 0) {
|
|
1320
|
+
errorEvent.data = data;
|
|
1321
|
+
}
|
|
1322
|
+
return this.emitInternal(errorEvent);
|
|
1323
|
+
}
|
|
1324
|
+
createMetrics() {
|
|
1325
|
+
const now = Date.now();
|
|
1326
|
+
const metrics = {
|
|
1327
|
+
timestamp: now,
|
|
1328
|
+
elapsedMs: now - this._startTime,
|
|
1329
|
+
deltaMs: now - this.lastEventTime
|
|
1330
|
+
};
|
|
1331
|
+
this.lastEventTime = now;
|
|
1332
|
+
return metrics;
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1336
|
+
// src/errors/utils.ts
|
|
1337
|
+
function wrapAsError(error, ErrorClass, options) {
|
|
1338
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
1339
|
+
return new ErrorClass(cause.message, { ...options, cause });
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// src/errors/types.ts
|
|
1343
|
+
var AgtlantisError = class extends Error {
|
|
1344
|
+
code;
|
|
1345
|
+
cause;
|
|
1346
|
+
context;
|
|
1347
|
+
constructor(message, options) {
|
|
1348
|
+
super(message);
|
|
1349
|
+
this.name = "AgtlantisError";
|
|
1350
|
+
this.code = options.code;
|
|
1351
|
+
this.cause = options.cause;
|
|
1352
|
+
this.context = options.context;
|
|
1353
|
+
const ErrorWithCapture = Error;
|
|
1354
|
+
ErrorWithCapture.captureStackTrace?.(this, this.constructor);
|
|
1355
|
+
}
|
|
1356
|
+
get isRetryable() {
|
|
1357
|
+
return false;
|
|
1358
|
+
}
|
|
1359
|
+
toJSON() {
|
|
1360
|
+
return {
|
|
1361
|
+
name: this.name,
|
|
1362
|
+
message: this.message,
|
|
1363
|
+
code: this.code,
|
|
1364
|
+
isRetryable: this.isRetryable,
|
|
1365
|
+
context: this.context,
|
|
1366
|
+
cause: this.cause?.message,
|
|
1367
|
+
stack: this.stack
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
var FileError = class _FileError extends AgtlantisError {
|
|
1372
|
+
constructor(message, options = {}) {
|
|
1373
|
+
super(message, {
|
|
1374
|
+
code: options.code ?? "FILE_ERROR" /* FILE_ERROR */,
|
|
1375
|
+
cause: options.cause,
|
|
1376
|
+
context: options.context
|
|
1377
|
+
});
|
|
1378
|
+
this.name = "FileError";
|
|
1379
|
+
}
|
|
1380
|
+
static from(error, code = "FILE_ERROR" /* FILE_ERROR */, context) {
|
|
1381
|
+
if (error instanceof _FileError) {
|
|
1382
|
+
return error;
|
|
1383
|
+
}
|
|
1384
|
+
return wrapAsError(error, _FileError, { code, context });
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
// src/provider/noop-file-manager.ts
|
|
1389
|
+
var NoOpFileManager = class {
|
|
1390
|
+
upload(_files) {
|
|
1391
|
+
throw new FileError("File upload not supported by this provider", {
|
|
1392
|
+
code: "UNSUPPORTED_TYPE" /* UNSUPPORTED_TYPE */,
|
|
1393
|
+
context: {
|
|
1394
|
+
provider: "noop",
|
|
1395
|
+
suggestion: "Use a provider with file support (e.g., Google) or pass files inline"
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
delete(_fileId) {
|
|
1400
|
+
throw new FileError("File delete not supported by this provider", {
|
|
1401
|
+
code: "UNSUPPORTED_TYPE" /* UNSUPPORTED_TYPE */,
|
|
1402
|
+
context: {
|
|
1403
|
+
provider: "noop"
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
clear() {
|
|
1408
|
+
return Promise.resolve();
|
|
1409
|
+
}
|
|
1410
|
+
getUploadedFiles() {
|
|
1411
|
+
return [];
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
|
|
1415
|
+
// src/testing/mock-provider.ts
|
|
1416
|
+
var MockProvider = class _MockProvider extends BaseProvider {
|
|
1417
|
+
calls = [];
|
|
1418
|
+
modelSource;
|
|
1419
|
+
fileManagerInstance;
|
|
1420
|
+
loggerInstance;
|
|
1421
|
+
defaultModelId;
|
|
1422
|
+
pricingConfig;
|
|
1423
|
+
providerTypeId;
|
|
1424
|
+
constructor(config) {
|
|
1425
|
+
super();
|
|
1426
|
+
if (!config.model && !config.modelFactory) {
|
|
1427
|
+
throw new Error("MockProvider requires either model or modelFactory");
|
|
1428
|
+
}
|
|
1429
|
+
this.modelSource = config.modelFactory ?? config.model;
|
|
1430
|
+
this.fileManagerInstance = config.fileManager ?? new NoOpFileManager();
|
|
1431
|
+
this.loggerInstance = config.logger ?? noopLogger;
|
|
1432
|
+
this.defaultModelId = null;
|
|
1433
|
+
this.providerTypeId = config.providerType ?? "mock";
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Creates instance for fluent API without calling constructor.
|
|
1437
|
+
* Shares calls array between fluent instances for tracking.
|
|
1438
|
+
*/
|
|
1439
|
+
static createWithConfig(modelSource, fileManager, logger, defaultModelId, pricingConfig, providerType, existingCalls) {
|
|
1440
|
+
const provider = Object.create(_MockProvider.prototype);
|
|
1441
|
+
Object.assign(provider, {
|
|
1442
|
+
modelSource,
|
|
1443
|
+
fileManagerInstance: fileManager,
|
|
1444
|
+
loggerInstance: logger,
|
|
1445
|
+
defaultModelId,
|
|
1446
|
+
pricingConfig,
|
|
1447
|
+
providerTypeId: providerType,
|
|
1448
|
+
calls: existingCalls
|
|
1449
|
+
});
|
|
1450
|
+
return provider;
|
|
1451
|
+
}
|
|
1452
|
+
getCalls() {
|
|
1453
|
+
return [...this.calls];
|
|
1454
|
+
}
|
|
1455
|
+
clearCalls() {
|
|
1456
|
+
this.calls.length = 0;
|
|
1457
|
+
}
|
|
1458
|
+
withDefaultModel(modelId) {
|
|
1459
|
+
return _MockProvider.createWithConfig(
|
|
1460
|
+
this.modelSource,
|
|
1461
|
+
this.fileManagerInstance,
|
|
1462
|
+
this.loggerInstance,
|
|
1463
|
+
modelId,
|
|
1464
|
+
this.pricingConfig,
|
|
1465
|
+
this.providerTypeId,
|
|
1466
|
+
this.calls
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
withLogger(logger) {
|
|
1470
|
+
return _MockProvider.createWithConfig(
|
|
1471
|
+
this.modelSource,
|
|
1472
|
+
this.fileManagerInstance,
|
|
1473
|
+
logger,
|
|
1474
|
+
this.defaultModelId,
|
|
1475
|
+
this.pricingConfig,
|
|
1476
|
+
this.providerTypeId,
|
|
1477
|
+
this.calls
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
withPricing(pricing) {
|
|
1481
|
+
return _MockProvider.createWithConfig(
|
|
1482
|
+
this.modelSource,
|
|
1483
|
+
this.fileManagerInstance,
|
|
1484
|
+
this.loggerInstance,
|
|
1485
|
+
this.defaultModelId,
|
|
1486
|
+
pricing,
|
|
1487
|
+
this.providerTypeId,
|
|
1488
|
+
this.calls
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Mock implementation - returns same provider since mocks don't use provider options.
|
|
1493
|
+
*/
|
|
1494
|
+
withDefaultOptions(_options) {
|
|
1495
|
+
return this;
|
|
1496
|
+
}
|
|
1497
|
+
createSimpleSession(signal) {
|
|
1498
|
+
return new SimpleSession({ ...this.buildSessionConfig(), signal });
|
|
1499
|
+
}
|
|
1500
|
+
createStreamingSession(signal) {
|
|
1501
|
+
return new StreamingSession({ ...this.buildSessionConfig(), signal });
|
|
1502
|
+
}
|
|
1503
|
+
buildSessionConfig() {
|
|
1504
|
+
const effectiveModelId = this.defaultModelId ?? "default";
|
|
1505
|
+
return {
|
|
1506
|
+
defaultLanguageModel: this.createTrackingModel(effectiveModelId),
|
|
1507
|
+
modelFactory: (modelId) => this.createTrackingModel(modelId),
|
|
1508
|
+
providerType: this.providerTypeId,
|
|
1509
|
+
providerPricing: this.pricingConfig,
|
|
1510
|
+
fileManager: this.fileManagerInstance,
|
|
1511
|
+
logger: this.loggerInstance
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
getBaseModel(modelId) {
|
|
1515
|
+
if (typeof this.modelSource === "function") {
|
|
1516
|
+
return this.modelSource(modelId);
|
|
1517
|
+
}
|
|
1518
|
+
return this.modelSource;
|
|
1519
|
+
}
|
|
1520
|
+
createTrackingModel(modelId) {
|
|
1521
|
+
const baseModel = this.getBaseModel(modelId);
|
|
1522
|
+
const calls = this.calls;
|
|
1523
|
+
return {
|
|
1524
|
+
...baseModel,
|
|
1525
|
+
specificationVersion: baseModel.specificationVersion,
|
|
1526
|
+
provider: baseModel.provider,
|
|
1527
|
+
modelId: baseModel.modelId,
|
|
1528
|
+
supportedUrls: baseModel.supportedUrls,
|
|
1529
|
+
doGenerate: async (params) => {
|
|
1530
|
+
calls.push({
|
|
1531
|
+
modelId,
|
|
1532
|
+
type: "generate",
|
|
1533
|
+
timestamp: Date.now(),
|
|
1534
|
+
params
|
|
1535
|
+
});
|
|
1536
|
+
return baseModel.doGenerate(params);
|
|
1537
|
+
},
|
|
1538
|
+
doStream: async (params) => {
|
|
1539
|
+
calls.push({
|
|
1540
|
+
modelId,
|
|
1541
|
+
type: "stream",
|
|
1542
|
+
timestamp: Date.now(),
|
|
1543
|
+
params
|
|
1544
|
+
});
|
|
1545
|
+
return baseModel.doStream(params);
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
function createMockProvider(configOrModel) {
|
|
1551
|
+
if (typeof configOrModel === "function") {
|
|
1552
|
+
return new MockProvider({ modelFactory: configOrModel });
|
|
1553
|
+
}
|
|
1554
|
+
if (configOrModel instanceof test.MockLanguageModelV3) {
|
|
1555
|
+
return new MockProvider({ model: configOrModel });
|
|
1556
|
+
}
|
|
1557
|
+
return new MockProvider(configOrModel);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// src/testing/mock.ts
|
|
1561
|
+
var DEFAULT_USAGE = {
|
|
1562
|
+
inputTokens: { total: 0, noCache: 0, cacheRead: void 0, cacheWrite: void 0 },
|
|
1563
|
+
outputTokens: { total: 0, text: 0, reasoning: void 0 }
|
|
1564
|
+
};
|
|
1565
|
+
var DEFAULT_FINISH = {
|
|
1566
|
+
unified: "stop",
|
|
1567
|
+
raw: void 0
|
|
1568
|
+
};
|
|
1569
|
+
var mock = {
|
|
1570
|
+
/**
|
|
1571
|
+
* Creates a MockLanguageModelV3 that returns text content.
|
|
1572
|
+
*
|
|
1573
|
+
* @param text - The text to return from generateText
|
|
1574
|
+
* @param options - Optional overrides for usage, finishReason, etc.
|
|
1575
|
+
*
|
|
1576
|
+
* @example
|
|
1577
|
+
* const model = mock.text('Hello, world!');
|
|
1578
|
+
*
|
|
1579
|
+
* @example
|
|
1580
|
+
* // With custom usage for cost calculation tests
|
|
1581
|
+
* const model = mock.text('Hello', {
|
|
1582
|
+
* usage: {
|
|
1583
|
+
* inputTokens: { total: 100 },
|
|
1584
|
+
* outputTokens: { total: 50 },
|
|
1585
|
+
* },
|
|
1586
|
+
* });
|
|
1587
|
+
*/
|
|
1588
|
+
text(text, options) {
|
|
1589
|
+
return new test.MockLanguageModelV3({
|
|
1590
|
+
doGenerate: async () => ({
|
|
1591
|
+
content: [{ type: "text", text }],
|
|
1592
|
+
finishReason: options?.finishReason ?? DEFAULT_FINISH,
|
|
1593
|
+
usage: options?.usage ?? DEFAULT_USAGE,
|
|
1594
|
+
warnings: options?.warnings ?? [],
|
|
1595
|
+
providerMetadata: options?.providerMetadata
|
|
1596
|
+
})
|
|
1597
|
+
});
|
|
1598
|
+
},
|
|
1599
|
+
/**
|
|
1600
|
+
* Creates a MockLanguageModelV3 that returns JSON content.
|
|
1601
|
+
*
|
|
1602
|
+
* Automatically stringifies the data. Use with generateObject or
|
|
1603
|
+
* generateText + Output.object.
|
|
1604
|
+
*
|
|
1605
|
+
* @param data - The object to return as JSON
|
|
1606
|
+
* @param options - Optional overrides for usage, finishReason, etc.
|
|
1607
|
+
*
|
|
1608
|
+
* @example
|
|
1609
|
+
* const model = mock.json({ name: 'Alice', age: 30 });
|
|
1610
|
+
*/
|
|
1611
|
+
json(data, options) {
|
|
1612
|
+
return mock.text(JSON.stringify(data), options);
|
|
1613
|
+
},
|
|
1614
|
+
/**
|
|
1615
|
+
* Creates a MockLanguageModelV3 that streams text chunks.
|
|
1616
|
+
*
|
|
1617
|
+
* @param chunks - Array of text strings to stream
|
|
1618
|
+
* @param options - Optional overrides for finishReason, usage, etc.
|
|
1619
|
+
*
|
|
1620
|
+
* @example
|
|
1621
|
+
* const model = mock.stream(['Hello', ', ', 'world!']);
|
|
1622
|
+
*/
|
|
1623
|
+
stream(chunks, options) {
|
|
1624
|
+
return new test.MockLanguageModelV3({
|
|
1625
|
+
doStream: async () => ({
|
|
1626
|
+
stream: test.simulateReadableStream({
|
|
1627
|
+
chunks: [
|
|
1628
|
+
{ type: "text-start", id: "text-1" },
|
|
1629
|
+
...chunks.map((chunk) => ({
|
|
1630
|
+
type: "text-delta",
|
|
1631
|
+
id: "text-1",
|
|
1632
|
+
delta: chunk
|
|
1633
|
+
})),
|
|
1634
|
+
{ type: "text-end", id: "text-1" },
|
|
1635
|
+
{
|
|
1636
|
+
type: "finish",
|
|
1637
|
+
finishReason: options?.finishReason ?? DEFAULT_FINISH,
|
|
1638
|
+
usage: options?.usage ?? DEFAULT_USAGE,
|
|
1639
|
+
logprobs: void 0
|
|
1640
|
+
}
|
|
1641
|
+
]
|
|
1642
|
+
})
|
|
1643
|
+
})
|
|
1644
|
+
});
|
|
1645
|
+
},
|
|
1646
|
+
/**
|
|
1647
|
+
* Creates a MockLanguageModelV3 that throws an error.
|
|
1648
|
+
*
|
|
1649
|
+
* @param error - The error to throw
|
|
1650
|
+
*
|
|
1651
|
+
* @example
|
|
1652
|
+
* const model = mock.error(new Error('Rate limit exceeded'));
|
|
1653
|
+
*
|
|
1654
|
+
* await expect(generateText({ model, prompt: 'Hi' }))
|
|
1655
|
+
* .rejects.toThrow('Rate limit exceeded');
|
|
1656
|
+
*/
|
|
1657
|
+
error(error) {
|
|
1658
|
+
return new test.MockLanguageModelV3({
|
|
1659
|
+
doGenerate: async () => {
|
|
1660
|
+
throw error;
|
|
1661
|
+
},
|
|
1662
|
+
doStream: async () => {
|
|
1663
|
+
throw error;
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1666
|
+
},
|
|
1667
|
+
/**
|
|
1668
|
+
* Creates a MockProvider with call tracking.
|
|
1669
|
+
*
|
|
1670
|
+
* Supports three input styles:
|
|
1671
|
+
* - Single model: `mock.provider(mock.text('Hello'))`
|
|
1672
|
+
* - Model factory: `mock.provider((modelId) => mock.text(modelId))`
|
|
1673
|
+
* - Full config: `mock.provider({ model, fileManager, logger })`
|
|
1674
|
+
*
|
|
1675
|
+
* @example
|
|
1676
|
+
* ```typescript
|
|
1677
|
+
* // Basic usage
|
|
1678
|
+
* const provider = mock.provider(mock.text('Hello!'));
|
|
1679
|
+
* const execution = provider.simpleExecution(async (session) => {
|
|
1680
|
+
* const { text } = await session.generateText({ prompt: 'Say hi' });
|
|
1681
|
+
* return text;
|
|
1682
|
+
* });
|
|
1683
|
+
* expect(await execution.toResult()).toBe('Hello!');
|
|
1684
|
+
* expect(provider.getCalls()).toHaveLength(1);
|
|
1685
|
+
*
|
|
1686
|
+
* // With model factory for different responses per model
|
|
1687
|
+
* const provider = mock.provider((modelId) => {
|
|
1688
|
+
* if (modelId === 'gpt-4') return mock.text('GPT-4 response');
|
|
1689
|
+
* return mock.text('Default response');
|
|
1690
|
+
* });
|
|
1691
|
+
* ```
|
|
1692
|
+
*/
|
|
1693
|
+
provider(configOrModel) {
|
|
1694
|
+
return createMockProvider(configOrModel);
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1697
|
+
|
|
1698
|
+
// src/execution/testing/helpers.ts
|
|
1699
|
+
function createAbortScenario() {
|
|
1700
|
+
const controller = new AbortController();
|
|
1701
|
+
return {
|
|
1702
|
+
controller,
|
|
1703
|
+
signal: controller.signal,
|
|
1704
|
+
abort: (reason) => controller.abort(reason),
|
|
1705
|
+
isAborted: () => controller.signal.aborted
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
function createAlreadyAbortedSignal(reason = "Already aborted") {
|
|
1709
|
+
const controller = new AbortController();
|
|
1710
|
+
controller.abort(reason);
|
|
1711
|
+
return controller.signal;
|
|
1712
|
+
}
|
|
1713
|
+
function createSimpleGenerator(result, events = []) {
|
|
1714
|
+
return async function* (session) {
|
|
1715
|
+
for (const event of events) {
|
|
1716
|
+
yield session.emit(event);
|
|
1717
|
+
}
|
|
1718
|
+
return session.done(result);
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
function createErrorGenerator(error, eventsBeforeError = []) {
|
|
1722
|
+
return async function* (session) {
|
|
1723
|
+
for (const event of eventsBeforeError) {
|
|
1724
|
+
yield session.emit(event);
|
|
1725
|
+
}
|
|
1726
|
+
throw error;
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
function createCancelableGenerator(abortScenario, onCancel, eventsBeforeWait = []) {
|
|
1730
|
+
return async function* (session) {
|
|
1731
|
+
for (const event of eventsBeforeWait) {
|
|
1732
|
+
yield session.emit(event);
|
|
1733
|
+
}
|
|
1734
|
+
await new Promise((_, reject) => {
|
|
1735
|
+
const signal = abortScenario.signal;
|
|
1736
|
+
if (signal.aborted) {
|
|
1737
|
+
onCancel?.();
|
|
1738
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
signal.addEventListener("abort", () => {
|
|
1742
|
+
onCancel?.();
|
|
1743
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1744
|
+
});
|
|
1745
|
+
});
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
function createCancelableFunction(abortScenario, onCancel) {
|
|
1749
|
+
return async () => {
|
|
1750
|
+
await new Promise((_, reject) => {
|
|
1751
|
+
const signal = abortScenario.signal;
|
|
1752
|
+
if (signal.aborted) {
|
|
1753
|
+
onCancel?.();
|
|
1754
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1757
|
+
signal.addEventListener("abort", () => {
|
|
1758
|
+
onCancel?.();
|
|
1759
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1760
|
+
});
|
|
1761
|
+
});
|
|
1762
|
+
return "should-not-reach";
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
function createDelayedGenerator(delayMs, result, abortScenario) {
|
|
1766
|
+
return async function* (session) {
|
|
1767
|
+
await new Promise((resolve, reject) => {
|
|
1768
|
+
const timeoutId = setTimeout(resolve, delayMs);
|
|
1769
|
+
if (abortScenario) {
|
|
1770
|
+
abortScenario.signal.addEventListener("abort", () => {
|
|
1771
|
+
clearTimeout(timeoutId);
|
|
1772
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
});
|
|
1776
|
+
return session.done(result);
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
function createSlowGenerator(events, delayBetweenEventsMs, abortScenario) {
|
|
1780
|
+
return async function* (session) {
|
|
1781
|
+
for (const event of events) {
|
|
1782
|
+
if (abortScenario?.isAborted()) {
|
|
1783
|
+
throw new DOMException("Aborted", "AbortError");
|
|
1784
|
+
}
|
|
1785
|
+
await new Promise((resolve, reject) => {
|
|
1786
|
+
const timeoutId = setTimeout(resolve, delayBetweenEventsMs);
|
|
1787
|
+
if (abortScenario) {
|
|
1788
|
+
if (abortScenario.signal.aborted) {
|
|
1789
|
+
clearTimeout(timeoutId);
|
|
1790
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
abortScenario.signal.addEventListener(
|
|
1794
|
+
"abort",
|
|
1795
|
+
() => {
|
|
1796
|
+
clearTimeout(timeoutId);
|
|
1797
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1798
|
+
},
|
|
1799
|
+
{ once: true }
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1802
|
+
});
|
|
1803
|
+
yield session.emit(event);
|
|
1804
|
+
}
|
|
1805
|
+
return void 0;
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
async function collectStreamAsync(stream) {
|
|
1809
|
+
const collected = [];
|
|
1810
|
+
for await (const event of stream) {
|
|
1811
|
+
collected.push(event);
|
|
1812
|
+
}
|
|
1813
|
+
return collected;
|
|
1814
|
+
}
|
|
1815
|
+
function createNeverEndingGenerator(eventsBeforeWait = [], abortScenario) {
|
|
1816
|
+
return async function* (session) {
|
|
1817
|
+
for (const event of eventsBeforeWait) {
|
|
1818
|
+
yield session.emit(event);
|
|
1819
|
+
}
|
|
1820
|
+
await new Promise((_, reject) => {
|
|
1821
|
+
if (abortScenario) {
|
|
1822
|
+
if (abortScenario.signal.aborted) {
|
|
1823
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
abortScenario.signal.addEventListener(
|
|
1827
|
+
"abort",
|
|
1828
|
+
() => reject(new DOMException("Aborted", "AbortError")),
|
|
1829
|
+
{ once: true }
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
return void 0;
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
function createOrderTrackingLogger() {
|
|
1837
|
+
const callOrder = [];
|
|
1838
|
+
const logger = {
|
|
1839
|
+
onLLMCallStart: () => {
|
|
1840
|
+
},
|
|
1841
|
+
onLLMCallEnd: () => {
|
|
1842
|
+
},
|
|
1843
|
+
onExecutionStart: () => {
|
|
1844
|
+
callOrder.push("start");
|
|
1845
|
+
},
|
|
1846
|
+
onExecutionEmit: () => {
|
|
1847
|
+
callOrder.push("emit");
|
|
1848
|
+
},
|
|
1849
|
+
onExecutionDone: () => {
|
|
1850
|
+
callOrder.push("done");
|
|
1851
|
+
},
|
|
1852
|
+
onExecutionError: () => {
|
|
1853
|
+
callOrder.push("error");
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
return {
|
|
1857
|
+
logger,
|
|
1858
|
+
getCallOrder: () => [...callOrder]
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
// src/execution/testing/fixtures.ts
|
|
1863
|
+
var TEST_PROVIDER_TYPE = "google";
|
|
1864
|
+
var noop = () => {
|
|
1865
|
+
};
|
|
1866
|
+
var noopFactory = () => noop;
|
|
1867
|
+
function createNoopWithReturn(value) {
|
|
1868
|
+
return () => value;
|
|
1869
|
+
}
|
|
1870
|
+
function createNoopAsync(value) {
|
|
1871
|
+
return () => Promise.resolve(value);
|
|
1872
|
+
}
|
|
1873
|
+
function createMockModel(options = {}) {
|
|
1874
|
+
const { mockFn = noopFactory } = options;
|
|
1875
|
+
return {
|
|
1876
|
+
specificationVersion: "v1",
|
|
1877
|
+
provider: "test-provider",
|
|
1878
|
+
modelId: "test-model",
|
|
1879
|
+
defaultObjectGenerationMode: "json",
|
|
1880
|
+
doGenerate: mockFn(),
|
|
1881
|
+
doStream: mockFn()
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
function createMockFileManager(options = {}) {
|
|
1885
|
+
const { mockFn } = options;
|
|
1886
|
+
if (mockFn) {
|
|
1887
|
+
return {
|
|
1888
|
+
upload: mockFn(),
|
|
1889
|
+
delete: mockFn(),
|
|
1890
|
+
clear: mockFn(),
|
|
1891
|
+
getUploadedFiles: mockFn()
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
return {
|
|
1895
|
+
upload: createNoopAsync([]),
|
|
1896
|
+
delete: createNoopAsync(void 0),
|
|
1897
|
+
clear: createNoopAsync(void 0),
|
|
1898
|
+
getUploadedFiles: createNoopWithReturn([])
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
function createMockLogger(options = {}) {
|
|
1902
|
+
const { mockFn = noopFactory } = options;
|
|
1903
|
+
return {
|
|
1904
|
+
onLLMCallStart: mockFn(),
|
|
1905
|
+
onLLMCallEnd: mockFn(),
|
|
1906
|
+
onExecutionStart: mockFn(),
|
|
1907
|
+
onExecutionEmit: mockFn(),
|
|
1908
|
+
onExecutionDone: mockFn(),
|
|
1909
|
+
onExecutionError: mockFn()
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
function createMockUsage2(overrides = {}) {
|
|
1913
|
+
return {
|
|
1914
|
+
inputTokens: 100,
|
|
1915
|
+
outputTokens: 50,
|
|
1916
|
+
totalTokens: 150,
|
|
1917
|
+
inputTokenDetails: {
|
|
1918
|
+
noCacheTokens: 100,
|
|
1919
|
+
cacheReadTokens: 0,
|
|
1920
|
+
cacheWriteTokens: 0
|
|
1921
|
+
},
|
|
1922
|
+
outputTokenDetails: {
|
|
1923
|
+
textTokens: 50,
|
|
1924
|
+
reasoningTokens: 0
|
|
1925
|
+
},
|
|
1926
|
+
...overrides
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
function createSimpleSessionFactory(options = {}) {
|
|
1930
|
+
const { mockFn, logger } = options;
|
|
1931
|
+
return (signal) => new SimpleSession({
|
|
1932
|
+
defaultLanguageModel: createMockModel({ mockFn }),
|
|
1933
|
+
providerType: TEST_PROVIDER_TYPE,
|
|
1934
|
+
fileManager: createMockFileManager({ mockFn }),
|
|
1935
|
+
signal,
|
|
1936
|
+
logger
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
function createStreamingSessionFactory(options = {}) {
|
|
1940
|
+
const { mockFn, logger } = options;
|
|
1941
|
+
return () => new StreamingSession({
|
|
1942
|
+
defaultLanguageModel: createMockModel({ mockFn }),
|
|
1943
|
+
providerType: TEST_PROVIDER_TYPE,
|
|
1944
|
+
fileManager: createMockFileManager({ mockFn }),
|
|
1945
|
+
logger
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
function createStreamingSessionFactoryWithSignal(options = {}) {
|
|
1949
|
+
const { mockFn, logger, onSignalCapture } = options;
|
|
1950
|
+
return (signal) => {
|
|
1951
|
+
onSignalCapture?.(signal);
|
|
1952
|
+
return new StreamingSession({
|
|
1953
|
+
defaultLanguageModel: createMockModel({ mockFn }),
|
|
1954
|
+
providerType: TEST_PROVIDER_TYPE,
|
|
1955
|
+
fileManager: createMockFileManager({ mockFn }),
|
|
1956
|
+
signal,
|
|
1957
|
+
logger
|
|
1958
|
+
});
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
async function collectEvents2(stream) {
|
|
1962
|
+
const events = [];
|
|
1963
|
+
for await (const event of stream) {
|
|
1964
|
+
events.push(event);
|
|
1965
|
+
}
|
|
1966
|
+
return events;
|
|
1967
|
+
}
|
|
1968
|
+
function createControllablePromise() {
|
|
1969
|
+
let resolve;
|
|
1970
|
+
let reject;
|
|
1971
|
+
const promise = new Promise((res, rej) => {
|
|
1972
|
+
resolve = res;
|
|
1973
|
+
reject = rej;
|
|
1974
|
+
});
|
|
1975
|
+
return { promise, resolve, reject };
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
// src/testing/test-execution.ts
|
|
1979
|
+
function createTestMetrics() {
|
|
1980
|
+
return { timestamp: Date.now(), elapsedMs: 0, deltaMs: 0 };
|
|
1981
|
+
}
|
|
1982
|
+
function createTestExecution(result, events = []) {
|
|
1983
|
+
const summary = SessionSummary.forTest({});
|
|
1984
|
+
const completionEvent = {
|
|
1985
|
+
type: "complete",
|
|
1986
|
+
data: result,
|
|
1987
|
+
summary,
|
|
1988
|
+
metrics: createTestMetrics()
|
|
1989
|
+
};
|
|
1990
|
+
const allEvents = [
|
|
1991
|
+
...events.map(
|
|
1992
|
+
(e) => ({ ...e, metrics: createTestMetrics() })
|
|
1993
|
+
),
|
|
1994
|
+
completionEvent
|
|
1995
|
+
];
|
|
1996
|
+
return {
|
|
1997
|
+
stream() {
|
|
1998
|
+
return {
|
|
1999
|
+
[Symbol.asyncIterator]() {
|
|
2000
|
+
let index = 0;
|
|
2001
|
+
return {
|
|
2002
|
+
async next() {
|
|
2003
|
+
if (index < allEvents.length) {
|
|
2004
|
+
return { value: allEvents[index++], done: false };
|
|
2005
|
+
}
|
|
2006
|
+
return { value: void 0, done: true };
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
},
|
|
2012
|
+
async result() {
|
|
2013
|
+
return { status: "succeeded", value: result, events: allEvents, summary };
|
|
2014
|
+
},
|
|
2015
|
+
cancel() {
|
|
2016
|
+
},
|
|
2017
|
+
async cleanup() {
|
|
2018
|
+
},
|
|
2019
|
+
async [Symbol.asyncDispose]() {
|
|
2020
|
+
}
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
function createTestErrorExecution(error, options = {}) {
|
|
2024
|
+
const summary = SessionSummary.forTest({});
|
|
2025
|
+
const errorEvent = {
|
|
2026
|
+
type: "error",
|
|
2027
|
+
error,
|
|
2028
|
+
summary,
|
|
2029
|
+
metrics: createTestMetrics()
|
|
2030
|
+
};
|
|
2031
|
+
if (options.data !== void 0) {
|
|
2032
|
+
errorEvent.data = options.data;
|
|
2033
|
+
}
|
|
2034
|
+
const typedErrorEvent = errorEvent;
|
|
2035
|
+
const allEvents = [
|
|
2036
|
+
...(options.events ?? []).map(
|
|
2037
|
+
(e) => ({ ...e, metrics: createTestMetrics() })
|
|
2038
|
+
),
|
|
2039
|
+
typedErrorEvent
|
|
2040
|
+
];
|
|
2041
|
+
return {
|
|
2042
|
+
stream() {
|
|
2043
|
+
return {
|
|
2044
|
+
[Symbol.asyncIterator]() {
|
|
2045
|
+
let index = 0;
|
|
2046
|
+
return {
|
|
2047
|
+
async next() {
|
|
2048
|
+
if (index < allEvents.length) {
|
|
2049
|
+
return { value: allEvents[index++], done: false };
|
|
2050
|
+
}
|
|
2051
|
+
return { value: void 0, done: true };
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
};
|
|
2056
|
+
},
|
|
2057
|
+
async result() {
|
|
2058
|
+
return { status: "failed", error, events: allEvents, summary };
|
|
2059
|
+
},
|
|
2060
|
+
cancel() {
|
|
2061
|
+
},
|
|
2062
|
+
async cleanup() {
|
|
2063
|
+
},
|
|
2064
|
+
async [Symbol.asyncDispose]() {
|
|
2065
|
+
}
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
function createTestCanceledExecution(events = []) {
|
|
2069
|
+
const summary = SessionSummary.forTest({});
|
|
2070
|
+
const allEvents = events.map(
|
|
2071
|
+
(e) => ({ ...e, metrics: createTestMetrics() })
|
|
2072
|
+
);
|
|
2073
|
+
return {
|
|
2074
|
+
stream() {
|
|
2075
|
+
return {
|
|
2076
|
+
[Symbol.asyncIterator]() {
|
|
2077
|
+
let index = 0;
|
|
2078
|
+
return {
|
|
2079
|
+
async next() {
|
|
2080
|
+
if (index < allEvents.length) {
|
|
2081
|
+
return { value: allEvents[index++], done: false };
|
|
2082
|
+
}
|
|
2083
|
+
return { value: void 0, done: true };
|
|
2084
|
+
}
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
};
|
|
2088
|
+
},
|
|
2089
|
+
async result() {
|
|
2090
|
+
return { status: "canceled", events: allEvents, summary };
|
|
2091
|
+
},
|
|
2092
|
+
cancel() {
|
|
2093
|
+
},
|
|
2094
|
+
async cleanup() {
|
|
2095
|
+
},
|
|
2096
|
+
async [Symbol.asyncDispose]() {
|
|
2097
|
+
}
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
Object.defineProperty(exports, "MockLanguageModelV3", {
|
|
2102
|
+
enumerable: true,
|
|
2103
|
+
get: function () { return test.MockLanguageModelV3; }
|
|
2104
|
+
});
|
|
2105
|
+
Object.defineProperty(exports, "simulateReadableStream", {
|
|
2106
|
+
enumerable: true,
|
|
2107
|
+
get: function () { return test.simulateReadableStream; }
|
|
2108
|
+
});
|
|
2109
|
+
exports.MockProvider = MockProvider;
|
|
2110
|
+
exports.TEST_API_KEY = TEST_API_KEY;
|
|
2111
|
+
exports.TEST_PROVIDER_TYPE = TEST_PROVIDER_TYPE;
|
|
2112
|
+
exports.collectEvents = collectEvents;
|
|
2113
|
+
exports.collectExecutionEvents = collectEvents2;
|
|
2114
|
+
exports.collectStreamAsync = collectStreamAsync;
|
|
2115
|
+
exports.consumeExecution = consumeExecution;
|
|
2116
|
+
exports.createAbortScenario = createAbortScenario;
|
|
2117
|
+
exports.createAlreadyAbortedSignal = createAlreadyAbortedSignal;
|
|
2118
|
+
exports.createCancelableFunction = createCancelableFunction;
|
|
2119
|
+
exports.createCancelableGenerator = createCancelableGenerator;
|
|
2120
|
+
exports.createControllablePromise = createControllablePromise;
|
|
2121
|
+
exports.createDelayedGenerator = createDelayedGenerator;
|
|
2122
|
+
exports.createErrorGenerator = createErrorGenerator;
|
|
2123
|
+
exports.createMockFileManager = createMockFileManager;
|
|
2124
|
+
exports.createMockLanguageModelUsage = createMockUsage2;
|
|
2125
|
+
exports.createMockLogger = createMockLogger;
|
|
2126
|
+
exports.createMockModel = createMockModel;
|
|
2127
|
+
exports.createMockProvider = createMockProvider;
|
|
2128
|
+
exports.createMockSessionSummary = createMockSessionSummary;
|
|
2129
|
+
exports.createMockUsage = createMockUsage;
|
|
2130
|
+
exports.createNeverEndingGenerator = createNeverEndingGenerator;
|
|
2131
|
+
exports.createOrderTrackingLogger = createOrderTrackingLogger;
|
|
2132
|
+
exports.createSimpleGenerator = createSimpleGenerator;
|
|
2133
|
+
exports.createSimpleSessionFactory = createSimpleSessionFactory;
|
|
2134
|
+
exports.createSlowGenerator = createSlowGenerator;
|
|
2135
|
+
exports.createStreamingSessionFactory = createStreamingSessionFactory;
|
|
2136
|
+
exports.createStreamingSessionFactoryWithSignal = createStreamingSessionFactoryWithSignal;
|
|
2137
|
+
exports.createTestCanceledExecution = createTestCanceledExecution;
|
|
2138
|
+
exports.createTestErrorExecution = createTestErrorExecution;
|
|
2139
|
+
exports.createTestEvent = createTestEvent;
|
|
2140
|
+
exports.createTestExecution = createTestExecution;
|
|
2141
|
+
exports.expectFileManagerInterface = expectFileManagerInterface;
|
|
2142
|
+
exports.mock = mock;
|
|
2143
|
+
//# sourceMappingURL=index.cjs.map
|
|
2144
|
+
//# sourceMappingURL=index.cjs.map
|