@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
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ai = require('ai');
|
|
4
|
+
var zod = require('zod');
|
|
5
|
+
var Handlebars = require('handlebars');
|
|
6
|
+
var fs = require('fs/promises');
|
|
7
|
+
var path3 = require('path');
|
|
8
|
+
var yaml = require('yaml');
|
|
9
|
+
var crypto = require('crypto');
|
|
10
|
+
var google = require('@ai-sdk/google');
|
|
11
|
+
var genai = require('@google/genai');
|
|
12
|
+
var merge = require('lodash/merge');
|
|
13
|
+
var openai = require('@ai-sdk/openai');
|
|
14
|
+
|
|
15
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
|
|
17
|
+
function _interopNamespace(e) {
|
|
18
|
+
if (e && e.__esModule) return e;
|
|
19
|
+
var n = Object.create(null);
|
|
20
|
+
if (e) {
|
|
21
|
+
Object.keys(e).forEach(function (k) {
|
|
22
|
+
if (k !== 'default') {
|
|
23
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
24
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () { return e[k]; }
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
n.default = e;
|
|
32
|
+
return Object.freeze(n);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
|
|
36
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
37
|
+
var path3__namespace = /*#__PURE__*/_interopNamespace(path3);
|
|
38
|
+
var yaml__namespace = /*#__PURE__*/_interopNamespace(yaml);
|
|
39
|
+
var merge__default = /*#__PURE__*/_interopDefault(merge);
|
|
40
|
+
|
|
41
|
+
// src/errors/utils.ts
|
|
42
|
+
function wrapAsError(error, ErrorClass, options) {
|
|
43
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
44
|
+
return new ErrorClass(cause.message, { ...options, cause });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/errors/types.ts
|
|
48
|
+
var ExecutionErrorCode = /* @__PURE__ */ ((ExecutionErrorCode2) => {
|
|
49
|
+
ExecutionErrorCode2["EXECUTION_ERROR"] = "EXECUTION_ERROR";
|
|
50
|
+
ExecutionErrorCode2["STREAM_ERROR"] = "STREAM_ERROR";
|
|
51
|
+
ExecutionErrorCode2["RESULT_EXTRACTION_ERROR"] = "RESULT_EXTRACTION_ERROR";
|
|
52
|
+
ExecutionErrorCode2["CANCELLED"] = "CANCELLED";
|
|
53
|
+
ExecutionErrorCode2["VALIDATION_ERROR"] = "VALIDATION_ERROR";
|
|
54
|
+
return ExecutionErrorCode2;
|
|
55
|
+
})(ExecutionErrorCode || {});
|
|
56
|
+
var ConfigurationErrorCode = /* @__PURE__ */ ((ConfigurationErrorCode2) => {
|
|
57
|
+
ConfigurationErrorCode2["CONFIG_ERROR"] = "CONFIG_ERROR";
|
|
58
|
+
ConfigurationErrorCode2["MISSING_API_KEY"] = "MISSING_API_KEY";
|
|
59
|
+
ConfigurationErrorCode2["INVALID_CONFIG"] = "INVALID_CONFIG";
|
|
60
|
+
ConfigurationErrorCode2["MISSING_REQUIRED"] = "MISSING_REQUIRED";
|
|
61
|
+
return ConfigurationErrorCode2;
|
|
62
|
+
})(ConfigurationErrorCode || {});
|
|
63
|
+
var FileErrorCode = /* @__PURE__ */ ((FileErrorCode2) => {
|
|
64
|
+
FileErrorCode2["FILE_ERROR"] = "FILE_ERROR";
|
|
65
|
+
FileErrorCode2["UPLOAD_ERROR"] = "UPLOAD_ERROR";
|
|
66
|
+
FileErrorCode2["DELETE_ERROR"] = "DELETE_ERROR";
|
|
67
|
+
FileErrorCode2["NOT_FOUND"] = "NOT_FOUND";
|
|
68
|
+
FileErrorCode2["TOO_LARGE"] = "TOO_LARGE";
|
|
69
|
+
FileErrorCode2["UNSUPPORTED_TYPE"] = "UNSUPPORTED_TYPE";
|
|
70
|
+
return FileErrorCode2;
|
|
71
|
+
})(FileErrorCode || {});
|
|
72
|
+
var AgtlantisError = class extends Error {
|
|
73
|
+
code;
|
|
74
|
+
cause;
|
|
75
|
+
context;
|
|
76
|
+
constructor(message, options) {
|
|
77
|
+
super(message);
|
|
78
|
+
this.name = "AgtlantisError";
|
|
79
|
+
this.code = options.code;
|
|
80
|
+
this.cause = options.cause;
|
|
81
|
+
this.context = options.context;
|
|
82
|
+
const ErrorWithCapture = Error;
|
|
83
|
+
ErrorWithCapture.captureStackTrace?.(this, this.constructor);
|
|
84
|
+
}
|
|
85
|
+
get isRetryable() {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
toJSON() {
|
|
89
|
+
return {
|
|
90
|
+
name: this.name,
|
|
91
|
+
message: this.message,
|
|
92
|
+
code: this.code,
|
|
93
|
+
isRetryable: this.isRetryable,
|
|
94
|
+
context: this.context,
|
|
95
|
+
cause: this.cause?.message,
|
|
96
|
+
stack: this.stack
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var ExecutionError = class _ExecutionError extends AgtlantisError {
|
|
101
|
+
constructor(message, options = {}) {
|
|
102
|
+
super(message, {
|
|
103
|
+
code: options.code ?? "EXECUTION_ERROR" /* EXECUTION_ERROR */,
|
|
104
|
+
cause: options.cause,
|
|
105
|
+
context: options.context
|
|
106
|
+
});
|
|
107
|
+
this.name = "ExecutionError";
|
|
108
|
+
}
|
|
109
|
+
static from(error, code = "EXECUTION_ERROR" /* EXECUTION_ERROR */, context) {
|
|
110
|
+
if (error instanceof _ExecutionError) {
|
|
111
|
+
return error;
|
|
112
|
+
}
|
|
113
|
+
return wrapAsError(error, _ExecutionError, { code, context });
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var ConfigurationError = class _ConfigurationError extends AgtlantisError {
|
|
117
|
+
constructor(message, options = {}) {
|
|
118
|
+
super(message, {
|
|
119
|
+
code: options.code ?? "CONFIG_ERROR" /* CONFIG_ERROR */,
|
|
120
|
+
cause: options.cause,
|
|
121
|
+
context: options.context
|
|
122
|
+
});
|
|
123
|
+
this.name = "ConfigurationError";
|
|
124
|
+
}
|
|
125
|
+
static from(error, code = "CONFIG_ERROR" /* CONFIG_ERROR */, context) {
|
|
126
|
+
if (error instanceof _ConfigurationError) {
|
|
127
|
+
return error;
|
|
128
|
+
}
|
|
129
|
+
return wrapAsError(error, _ConfigurationError, { code, context });
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var FileError = class _FileError extends AgtlantisError {
|
|
133
|
+
constructor(message, options = {}) {
|
|
134
|
+
super(message, {
|
|
135
|
+
code: options.code ?? "FILE_ERROR" /* FILE_ERROR */,
|
|
136
|
+
cause: options.cause,
|
|
137
|
+
context: options.context
|
|
138
|
+
});
|
|
139
|
+
this.name = "FileError";
|
|
140
|
+
}
|
|
141
|
+
static from(error, code = "FILE_ERROR" /* FILE_ERROR */, context) {
|
|
142
|
+
if (error instanceof _FileError) {
|
|
143
|
+
return error;
|
|
144
|
+
}
|
|
145
|
+
return wrapAsError(error, _FileError, { code, context });
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// src/execution/constants.ts
|
|
150
|
+
var ERRORS = {
|
|
151
|
+
ALREADY_CONSUMED: "Execution already consumed",
|
|
152
|
+
METADATA_NOT_AVAILABLE: "Metadata not available yet. Consume the execution first.",
|
|
153
|
+
NO_RESULT: "No result available",
|
|
154
|
+
UNKNOWN_ERROR: "Execution failed with unknown error"
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// src/execution/utils.ts
|
|
158
|
+
function getDuration(startTime) {
|
|
159
|
+
return Date.now() - startTime;
|
|
160
|
+
}
|
|
161
|
+
function combineSignals(...signals) {
|
|
162
|
+
const controller = new AbortController();
|
|
163
|
+
for (const signal of signals) {
|
|
164
|
+
if (signal.aborted) {
|
|
165
|
+
controller.abort(signal.reason);
|
|
166
|
+
return controller.signal;
|
|
167
|
+
}
|
|
168
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), {
|
|
169
|
+
once: true
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return controller.signal;
|
|
173
|
+
}
|
|
174
|
+
var Deferred = class {
|
|
175
|
+
promise;
|
|
176
|
+
resolve;
|
|
177
|
+
reject;
|
|
178
|
+
constructor() {
|
|
179
|
+
let res;
|
|
180
|
+
let rej;
|
|
181
|
+
this.promise = new Promise((resolve2, reject) => {
|
|
182
|
+
res = resolve2;
|
|
183
|
+
rej = reject;
|
|
184
|
+
});
|
|
185
|
+
this.resolve = res;
|
|
186
|
+
this.reject = rej;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/execution/shared.ts
|
|
191
|
+
function isAbortError(error, signal) {
|
|
192
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
return signal.aborted;
|
|
196
|
+
}
|
|
197
|
+
function normalizeError(error) {
|
|
198
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
199
|
+
}
|
|
200
|
+
function determineResultStatus(cancelRequested, aborted, hasError) {
|
|
201
|
+
if (cancelRequested || aborted) {
|
|
202
|
+
return "canceled";
|
|
203
|
+
}
|
|
204
|
+
if (hasError) {
|
|
205
|
+
return "failed";
|
|
206
|
+
}
|
|
207
|
+
return "succeeded";
|
|
208
|
+
}
|
|
209
|
+
function createHookRunner(runHooks) {
|
|
210
|
+
let ran = false;
|
|
211
|
+
return {
|
|
212
|
+
ensureRun: async () => {
|
|
213
|
+
if (!ran) {
|
|
214
|
+
ran = true;
|
|
215
|
+
await runHooks();
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
hasRun: () => ran
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/execution/streaming-host.ts
|
|
223
|
+
var StreamingExecutionHost = class {
|
|
224
|
+
constructor(createSession, generator, userSignal) {
|
|
225
|
+
this.createSession = createSession;
|
|
226
|
+
this.generator = generator;
|
|
227
|
+
this.effectiveSignal = userSignal ? combineSignals(userSignal, this.abortController.signal) : this.abortController.signal;
|
|
228
|
+
this.consumerPromise = this.startConsuming();
|
|
229
|
+
}
|
|
230
|
+
abortController = new AbortController();
|
|
231
|
+
effectiveSignal;
|
|
232
|
+
consumerPromise;
|
|
233
|
+
eventBuffer = [];
|
|
234
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
235
|
+
completed = false;
|
|
236
|
+
cleaned = false;
|
|
237
|
+
hookRunner = null;
|
|
238
|
+
cancelRequested = false;
|
|
239
|
+
extractedOutcome = null;
|
|
240
|
+
extractedSummary = null;
|
|
241
|
+
hasDataField(event) {
|
|
242
|
+
return "data" in event && event.data !== void 0;
|
|
243
|
+
}
|
|
244
|
+
hasSummaryField(event) {
|
|
245
|
+
return "summary" in event && event.summary !== void 0;
|
|
246
|
+
}
|
|
247
|
+
hasErrorField(event) {
|
|
248
|
+
return "error" in event && event.error instanceof Error;
|
|
249
|
+
}
|
|
250
|
+
extractResultAndMetadata(event) {
|
|
251
|
+
const isCompleteOrError = event.type === "complete" || event.type === "error";
|
|
252
|
+
if (!isCompleteOrError) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (this.hasErrorField(event)) {
|
|
256
|
+
this.extractedOutcome = { type: "error", error: event.error };
|
|
257
|
+
} else if (this.hasDataField(event)) {
|
|
258
|
+
this.extractedOutcome = { type: "result", value: event.data };
|
|
259
|
+
}
|
|
260
|
+
if (this.hasSummaryField(event)) {
|
|
261
|
+
this.extractedSummary = event.summary;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
notifySubscribers(event) {
|
|
265
|
+
this.subscribers.forEach((fn) => fn(event));
|
|
266
|
+
}
|
|
267
|
+
async startConsuming() {
|
|
268
|
+
const session = this.createSession(this.effectiveSignal);
|
|
269
|
+
this.hookRunner = createHookRunner(() => session.runOnDoneHooks());
|
|
270
|
+
const gen = this.generator(session);
|
|
271
|
+
try {
|
|
272
|
+
let next = await gen.next();
|
|
273
|
+
while (!next.done) {
|
|
274
|
+
this.eventBuffer.push(next.value);
|
|
275
|
+
this.notifySubscribers(next.value);
|
|
276
|
+
const isTerminal = next.value.type === "complete" || next.value.type === "error";
|
|
277
|
+
if (isTerminal) {
|
|
278
|
+
this.extractResultAndMetadata(next.value);
|
|
279
|
+
this.abortController.abort();
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
if (this.abortController.signal.aborted) {
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
next = await gen.next();
|
|
286
|
+
}
|
|
287
|
+
if (next.done && next.value !== void 0) {
|
|
288
|
+
const finalEvent = await Promise.resolve(next.value);
|
|
289
|
+
this.eventBuffer.push(finalEvent);
|
|
290
|
+
this.notifySubscribers(finalEvent);
|
|
291
|
+
this.extractResultAndMetadata(finalEvent);
|
|
292
|
+
const isTerminal = finalEvent.type === "complete" || finalEvent.type === "error";
|
|
293
|
+
if (isTerminal) {
|
|
294
|
+
this.abortController.abort();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return this.buildResult(session);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
const errorObj = normalizeError(error);
|
|
300
|
+
if (isAbortError(error, this.abortController.signal)) {
|
|
301
|
+
return {
|
|
302
|
+
success: false,
|
|
303
|
+
aborted: true,
|
|
304
|
+
error: errorObj,
|
|
305
|
+
summary: await session.getSummary()
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const errorEvent = await session.fail(errorObj);
|
|
309
|
+
this.eventBuffer.push(errorEvent);
|
|
310
|
+
this.notifySubscribers(errorEvent);
|
|
311
|
+
this.extractResultAndMetadata(errorEvent);
|
|
312
|
+
this.abortController.abort();
|
|
313
|
+
return this.buildResult(session);
|
|
314
|
+
} finally {
|
|
315
|
+
this.completed = true;
|
|
316
|
+
await this.hookRunner?.ensureRun();
|
|
317
|
+
await gen.return(void 0);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async buildResult(session) {
|
|
321
|
+
const summary = this.extractedSummary ?? await session.getSummary();
|
|
322
|
+
if (this.extractedOutcome?.type === "error") {
|
|
323
|
+
return {
|
|
324
|
+
success: false,
|
|
325
|
+
aborted: false,
|
|
326
|
+
error: this.extractedOutcome.error,
|
|
327
|
+
summary
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
if (this.extractedOutcome?.type === "result") {
|
|
331
|
+
return {
|
|
332
|
+
success: true,
|
|
333
|
+
result: this.extractedOutcome.value,
|
|
334
|
+
summary
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
success: false,
|
|
339
|
+
aborted: true,
|
|
340
|
+
error: new Error(ERRORS.NO_RESULT),
|
|
341
|
+
summary
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get the event stream.
|
|
346
|
+
* Returns buffered events first, then real-time events.
|
|
347
|
+
* Can be called multiple times - replays buffer each time.
|
|
348
|
+
*/
|
|
349
|
+
async *stream() {
|
|
350
|
+
let index = 0;
|
|
351
|
+
while (index < this.eventBuffer.length) {
|
|
352
|
+
yield this.eventBuffer[index++];
|
|
353
|
+
}
|
|
354
|
+
if (this.completed) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const queue = [];
|
|
358
|
+
let pending = new Deferred();
|
|
359
|
+
const subscriber = (event) => {
|
|
360
|
+
queue.push(event);
|
|
361
|
+
pending.resolve();
|
|
362
|
+
};
|
|
363
|
+
this.subscribers.add(subscriber);
|
|
364
|
+
try {
|
|
365
|
+
while (!this.completed || queue.length > 0) {
|
|
366
|
+
if (queue.length > 0) {
|
|
367
|
+
yield queue.shift();
|
|
368
|
+
} else if (!this.completed) {
|
|
369
|
+
await pending.promise;
|
|
370
|
+
pending = new Deferred();
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} finally {
|
|
374
|
+
this.subscribers.delete(subscriber);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
cancel() {
|
|
378
|
+
this.cancelRequested = true;
|
|
379
|
+
this.abortController.abort();
|
|
380
|
+
}
|
|
381
|
+
async cleanup() {
|
|
382
|
+
if (this.cleaned) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
this.cleaned = true;
|
|
386
|
+
if (!this.completed) {
|
|
387
|
+
this.cancel();
|
|
388
|
+
await this.consumerPromise.catch(() => {
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
this.subscribers.clear();
|
|
392
|
+
await this.hookRunner?.ensureRun();
|
|
393
|
+
}
|
|
394
|
+
async [Symbol.asyncDispose]() {
|
|
395
|
+
await this.cleanup();
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Get the execution result with status, summary, and all events.
|
|
399
|
+
* Never throws - returns a discriminated union with status.
|
|
400
|
+
*/
|
|
401
|
+
async result() {
|
|
402
|
+
const internal = await this.consumerPromise;
|
|
403
|
+
const events = Object.freeze([...this.eventBuffer]);
|
|
404
|
+
if (internal.success) {
|
|
405
|
+
return {
|
|
406
|
+
status: "succeeded",
|
|
407
|
+
value: internal.result,
|
|
408
|
+
summary: internal.summary,
|
|
409
|
+
events
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (this.cancelRequested || internal.aborted) {
|
|
413
|
+
return {
|
|
414
|
+
status: "canceled",
|
|
415
|
+
summary: internal.summary,
|
|
416
|
+
events
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
status: "failed",
|
|
421
|
+
error: internal.error,
|
|
422
|
+
summary: internal.summary,
|
|
423
|
+
events
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
// src/execution/simple-host.ts
|
|
429
|
+
var SimpleExecutionHost = class {
|
|
430
|
+
abortController = new AbortController();
|
|
431
|
+
effectiveSignal;
|
|
432
|
+
consumerPromise;
|
|
433
|
+
cachedSession;
|
|
434
|
+
startTime = Date.now();
|
|
435
|
+
cancelRequested = false;
|
|
436
|
+
constructor(createSession, fn, userSignal) {
|
|
437
|
+
this.effectiveSignal = userSignal ? combineSignals(userSignal, this.abortController.signal) : this.abortController.signal;
|
|
438
|
+
this.consumerPromise = this.execute(createSession, fn);
|
|
439
|
+
}
|
|
440
|
+
async execute(createSession, fn) {
|
|
441
|
+
const session = createSession(this.effectiveSignal);
|
|
442
|
+
this.cachedSession = session;
|
|
443
|
+
const hookRunner = createHookRunner(() => session.runOnDoneHooks());
|
|
444
|
+
session.notifyExecutionStart();
|
|
445
|
+
try {
|
|
446
|
+
const result = await fn(session);
|
|
447
|
+
await session.notifyExecutionDone(result, this.startTime);
|
|
448
|
+
return {
|
|
449
|
+
success: true,
|
|
450
|
+
result,
|
|
451
|
+
summary: await session.getSummary()
|
|
452
|
+
};
|
|
453
|
+
} catch (error) {
|
|
454
|
+
const errorObj = normalizeError(error);
|
|
455
|
+
const isCancellation = isAbortError(error, this.abortController.signal);
|
|
456
|
+
if (!isCancellation) {
|
|
457
|
+
await session.notifyExecutionError(errorObj, this.startTime);
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
success: false,
|
|
461
|
+
error: errorObj,
|
|
462
|
+
aborted: isCancellation,
|
|
463
|
+
summary: await session.getSummary()
|
|
464
|
+
};
|
|
465
|
+
} finally {
|
|
466
|
+
await hookRunner.ensureRun();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Request cancellation of the execution.
|
|
471
|
+
* Aborts the current LLM call if in progress.
|
|
472
|
+
* No-op if execution already completed.
|
|
473
|
+
*/
|
|
474
|
+
cancel() {
|
|
475
|
+
this.cancelRequested = true;
|
|
476
|
+
this.abortController.abort();
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Get the execution result with status and summary.
|
|
480
|
+
* Never throws - returns a discriminated union with status.
|
|
481
|
+
*/
|
|
482
|
+
async result() {
|
|
483
|
+
const internal = await this.consumerPromise;
|
|
484
|
+
if (internal.success) {
|
|
485
|
+
return {
|
|
486
|
+
status: "succeeded",
|
|
487
|
+
value: internal.result,
|
|
488
|
+
summary: internal.summary
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
if (this.cancelRequested || internal.aborted) {
|
|
492
|
+
return {
|
|
493
|
+
status: "canceled",
|
|
494
|
+
summary: internal.summary
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
status: "failed",
|
|
499
|
+
error: internal.error,
|
|
500
|
+
summary: internal.summary
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Cleanup resources.
|
|
505
|
+
* For SimpleExecution, hooks are already run during execution,
|
|
506
|
+
* so this is intentionally a no-op.
|
|
507
|
+
*/
|
|
508
|
+
async cleanup() {
|
|
509
|
+
}
|
|
510
|
+
async [Symbol.asyncDispose]() {
|
|
511
|
+
await this.cleanup();
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// src/execution/mapping.ts
|
|
516
|
+
function mapExecution(execution, fn) {
|
|
517
|
+
if ("stream" in execution) {
|
|
518
|
+
return mapStreamingExecution(execution, fn);
|
|
519
|
+
}
|
|
520
|
+
return mapSimpleExecution(execution, fn);
|
|
521
|
+
}
|
|
522
|
+
function mapExecutionResult(execution, fn) {
|
|
523
|
+
if ("stream" in execution) {
|
|
524
|
+
return mapStreamingExecutionResult(execution, fn);
|
|
525
|
+
}
|
|
526
|
+
return mapSimpleExecution(execution, fn);
|
|
527
|
+
}
|
|
528
|
+
function mapStreamingExecution(execution, fn) {
|
|
529
|
+
return {
|
|
530
|
+
stream() {
|
|
531
|
+
const original = execution.stream();
|
|
532
|
+
return {
|
|
533
|
+
[Symbol.asyncIterator]() {
|
|
534
|
+
const iter = original[Symbol.asyncIterator]();
|
|
535
|
+
return {
|
|
536
|
+
async next() {
|
|
537
|
+
const { value, done } = await iter.next();
|
|
538
|
+
if (done) return { value: void 0, done: true };
|
|
539
|
+
const event = value;
|
|
540
|
+
if (event.type === "error") {
|
|
541
|
+
return { value: event, done: false };
|
|
542
|
+
}
|
|
543
|
+
const { metrics, ...pureEvent } = event;
|
|
544
|
+
try {
|
|
545
|
+
const mapped = await fn(pureEvent);
|
|
546
|
+
return {
|
|
547
|
+
value: { ...mapped, metrics },
|
|
548
|
+
done: false
|
|
549
|
+
};
|
|
550
|
+
} catch (err) {
|
|
551
|
+
const errorEvent = {
|
|
552
|
+
type: "error",
|
|
553
|
+
error: normalizeError(err),
|
|
554
|
+
metrics
|
|
555
|
+
};
|
|
556
|
+
return { value: errorEvent, done: false };
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
},
|
|
563
|
+
async result() {
|
|
564
|
+
const original = await execution.result();
|
|
565
|
+
if (original.status !== "succeeded") {
|
|
566
|
+
return original;
|
|
567
|
+
}
|
|
568
|
+
try {
|
|
569
|
+
const mappedEvents = [];
|
|
570
|
+
for (const event of original.events) {
|
|
571
|
+
if (event.type === "error") {
|
|
572
|
+
mappedEvents.push(event);
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
const { metrics, ...pureEvent } = event;
|
|
576
|
+
const mapped = await fn(pureEvent);
|
|
577
|
+
mappedEvents.push({ ...mapped, metrics });
|
|
578
|
+
}
|
|
579
|
+
const completionEvent = mappedEvents.find((e) => e.type === "complete");
|
|
580
|
+
return {
|
|
581
|
+
status: "succeeded",
|
|
582
|
+
value: completionEvent.data,
|
|
583
|
+
events: mappedEvents,
|
|
584
|
+
summary: original.summary
|
|
585
|
+
};
|
|
586
|
+
} catch (err) {
|
|
587
|
+
return {
|
|
588
|
+
status: "failed",
|
|
589
|
+
error: normalizeError(err),
|
|
590
|
+
events: original.events,
|
|
591
|
+
summary: original.summary
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
cancel: () => execution.cancel(),
|
|
596
|
+
cleanup: () => execution.cleanup(),
|
|
597
|
+
[Symbol.asyncDispose]: () => execution[Symbol.asyncDispose]()
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
function mapStreamingExecutionResult(execution, fn) {
|
|
601
|
+
return {
|
|
602
|
+
stream() {
|
|
603
|
+
const original = execution.stream();
|
|
604
|
+
return {
|
|
605
|
+
[Symbol.asyncIterator]() {
|
|
606
|
+
const iter = original[Symbol.asyncIterator]();
|
|
607
|
+
return {
|
|
608
|
+
async next() {
|
|
609
|
+
const { value, done } = await iter.next();
|
|
610
|
+
if (done) return { value: void 0, done: true };
|
|
611
|
+
const event = value;
|
|
612
|
+
if (event.type === "complete") {
|
|
613
|
+
const { metrics, ...rest } = event;
|
|
614
|
+
try {
|
|
615
|
+
const mapped = await fn(rest.data);
|
|
616
|
+
return {
|
|
617
|
+
value: { type: "complete", data: mapped, summary: rest.summary, metrics },
|
|
618
|
+
done: false
|
|
619
|
+
};
|
|
620
|
+
} catch (err) {
|
|
621
|
+
const errorEvent = {
|
|
622
|
+
type: "error",
|
|
623
|
+
error: normalizeError(err),
|
|
624
|
+
metrics
|
|
625
|
+
};
|
|
626
|
+
return { value: errorEvent, done: false };
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return { value: event, done: false };
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
},
|
|
635
|
+
async result() {
|
|
636
|
+
const original = await execution.result();
|
|
637
|
+
if (original.status !== "succeeded") {
|
|
638
|
+
return original;
|
|
639
|
+
}
|
|
640
|
+
try {
|
|
641
|
+
const mapped = await fn(original.value);
|
|
642
|
+
const mappedEvents = original.events.map((event) => {
|
|
643
|
+
if (event.type === "complete") {
|
|
644
|
+
return { ...event, data: mapped };
|
|
645
|
+
}
|
|
646
|
+
return event;
|
|
647
|
+
});
|
|
648
|
+
return {
|
|
649
|
+
status: "succeeded",
|
|
650
|
+
value: mapped,
|
|
651
|
+
events: mappedEvents,
|
|
652
|
+
summary: original.summary
|
|
653
|
+
};
|
|
654
|
+
} catch (err) {
|
|
655
|
+
return {
|
|
656
|
+
status: "failed",
|
|
657
|
+
error: normalizeError(err),
|
|
658
|
+
events: original.events,
|
|
659
|
+
summary: original.summary
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
cancel: () => execution.cancel(),
|
|
664
|
+
cleanup: () => execution.cleanup(),
|
|
665
|
+
[Symbol.asyncDispose]: () => execution[Symbol.asyncDispose]()
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function mapSimpleExecution(execution, fn) {
|
|
669
|
+
return {
|
|
670
|
+
async result() {
|
|
671
|
+
const original = await execution.result();
|
|
672
|
+
if (original.status !== "succeeded") {
|
|
673
|
+
return original;
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
const mapped = await fn(original.value);
|
|
677
|
+
return {
|
|
678
|
+
status: "succeeded",
|
|
679
|
+
value: mapped,
|
|
680
|
+
summary: original.summary
|
|
681
|
+
};
|
|
682
|
+
} catch (err) {
|
|
683
|
+
return {
|
|
684
|
+
status: "failed",
|
|
685
|
+
error: normalizeError(err),
|
|
686
|
+
summary: original.summary
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
cancel: () => execution.cancel(),
|
|
691
|
+
cleanup: () => execution.cleanup(),
|
|
692
|
+
[Symbol.asyncDispose]: () => execution[Symbol.asyncDispose]()
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// src/observability/logger.ts
|
|
697
|
+
var noopLogger = {};
|
|
698
|
+
function createLogger(handlers) {
|
|
699
|
+
return handlers;
|
|
700
|
+
}
|
|
701
|
+
var TOOL_DESCRIPTIONS = {
|
|
702
|
+
reportProgress: "[OPTIONAL] Report progress during task execution. Use this to show intermediate work. You may call this multiple times, then you MUST call tools::submitResult.",
|
|
703
|
+
submitResult: "[REQUIRED] Submit the final result. You MUST call this exactly once to complete the task. Without this call, the task FAILS."
|
|
704
|
+
};
|
|
705
|
+
var TOOL_CALLING_PROTOCOL = `## CRITICAL INSTRUCTION - READ CAREFULLY
|
|
706
|
+
|
|
707
|
+
You have 2 tools available:
|
|
708
|
+
1. reportProgress - [OPTIONAL] Show intermediate work (multiple times)
|
|
709
|
+
2. submitResult - [REQUIRED] Submit your final answer
|
|
710
|
+
|
|
711
|
+
\u26A0\uFE0F IMPORTANT RULES:
|
|
712
|
+
- You may call reportProgress 0-3 times to show progress
|
|
713
|
+
- You MUST call submitResult exactly once to complete the task
|
|
714
|
+
- After calling reportProgress, you MUST call submitResult in your NEXT response
|
|
715
|
+
- If you call reportProgress more than 3 times without submitResult, the task FAILS
|
|
716
|
+
- The task is NOT complete until submitResult is called
|
|
717
|
+
|
|
718
|
+
CORRECT SEQUENCE:
|
|
719
|
+
1. [Optional] reportProgress
|
|
720
|
+
2. [Required] submitResult (exactly once)
|
|
721
|
+
|
|
722
|
+
\u274C WRONG: reportProgress \u2192 reportProgress \u2192 reportProgress \u2192 reportProgress (no submitResult = FAIL)
|
|
723
|
+
\u2705 CORRECT: reportProgress \u2192 submitResult (SUCCESS)`;
|
|
724
|
+
function defineProgressivePattern(config) {
|
|
725
|
+
return new ProgressivePattern(config.progressSchema, config.resultSchema);
|
|
726
|
+
}
|
|
727
|
+
var ProgressivePattern = class {
|
|
728
|
+
constructor(progressSchema, resultSchema) {
|
|
729
|
+
this.progressSchema = progressSchema;
|
|
730
|
+
this.resultSchema = resultSchema;
|
|
731
|
+
}
|
|
732
|
+
lastParseError = null;
|
|
733
|
+
/**
|
|
734
|
+
* Runs the pattern within an existing session. Use this for composing
|
|
735
|
+
* multiple patterns or when you need fine-grained control over the session.
|
|
736
|
+
*
|
|
737
|
+
* @param session - The streaming session to run within
|
|
738
|
+
* @param options - Stream options including:
|
|
739
|
+
* - `stopWhen` - Additional stop conditions (combined with default `hasToolCall('submitResult')`)
|
|
740
|
+
* - `protocol` - Custom protocol instructions (replaces default `TOOL_CALLING_PROTOCOL`)
|
|
741
|
+
* - All other `streamText` options except `tools`, `toolChoice`, `stopWhen`
|
|
742
|
+
*
|
|
743
|
+
* @example Basic usage
|
|
744
|
+
* ```typescript
|
|
745
|
+
* provider.streamingExecution(async function*(session) {
|
|
746
|
+
* yield* pattern.runInSession(session, {
|
|
747
|
+
* system: 'You are an analyzer.',
|
|
748
|
+
* messages: [{ role: 'user', content: 'Analyze...' }],
|
|
749
|
+
* });
|
|
750
|
+
* });
|
|
751
|
+
* ```
|
|
752
|
+
*
|
|
753
|
+
* @example With custom stopWhen
|
|
754
|
+
* ```typescript
|
|
755
|
+
* yield* pattern.runInSession(session, {
|
|
756
|
+
* prompt: 'Analyze...',
|
|
757
|
+
* stopWhen: stepCountIs(5), // Combined: submitResult OR 5 steps
|
|
758
|
+
* });
|
|
759
|
+
* ```
|
|
760
|
+
*/
|
|
761
|
+
async *runInSession(session, options) {
|
|
762
|
+
const {
|
|
763
|
+
tools: userTools,
|
|
764
|
+
system,
|
|
765
|
+
stopWhen: userStopWhen,
|
|
766
|
+
protocol,
|
|
767
|
+
...restOptions
|
|
768
|
+
} = options;
|
|
769
|
+
const internalTools = this.createTools();
|
|
770
|
+
const allTools = { ...userTools, ...internalTools };
|
|
771
|
+
const systemString = typeof system === "string" ? system : void 0;
|
|
772
|
+
const fullSystem = this.renderSystemPrompt(systemString, protocol);
|
|
773
|
+
const defaultStopCondition = ai.hasToolCall("submitResult");
|
|
774
|
+
const stopConditions = this.combineStopConditions(defaultStopCondition, userStopWhen);
|
|
775
|
+
const stream = session.streamText({
|
|
776
|
+
...restOptions,
|
|
777
|
+
system: fullSystem,
|
|
778
|
+
tools: allTools,
|
|
779
|
+
toolChoice: "required",
|
|
780
|
+
stopWhen: stopConditions
|
|
781
|
+
});
|
|
782
|
+
let result = null;
|
|
783
|
+
for await (const part of stream.fullStream) {
|
|
784
|
+
if (part.type === "tool-call") {
|
|
785
|
+
if (part.toolName === "reportProgress") {
|
|
786
|
+
const input = "input" in part ? part.input : void 0;
|
|
787
|
+
const progressData = this.parseProgressInput(input);
|
|
788
|
+
if (progressData !== null) {
|
|
789
|
+
yield session.emit({
|
|
790
|
+
type: "progress",
|
|
791
|
+
data: progressData
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
} else if (part.toolName === "submitResult") {
|
|
795
|
+
const input = "input" in part ? part.input : void 0;
|
|
796
|
+
result = this.parseResultInput(input);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (result === null) {
|
|
801
|
+
const baseMsg = "ProgressivePattern: No result received.";
|
|
802
|
+
const detail = this.lastParseError ? ` Last parse error: ${this.lastParseError.message}` : " The LLM did not call submitResult tool.";
|
|
803
|
+
throw new Error(baseMsg + detail);
|
|
804
|
+
}
|
|
805
|
+
const completeEvent = await session.done(result);
|
|
806
|
+
yield completeEvent;
|
|
807
|
+
return completeEvent;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Standalone execution that creates a new session internally.
|
|
811
|
+
* Use this for simple, single-pattern executions.
|
|
812
|
+
*
|
|
813
|
+
* @param provider - The AI provider to use
|
|
814
|
+
* @param options - Stream options including:
|
|
815
|
+
* - `stopWhen` - Additional stop conditions (combined with default `hasToolCall('submitResult')`)
|
|
816
|
+
* - `protocol` - Custom protocol instructions (replaces default `TOOL_CALLING_PROTOCOL`)
|
|
817
|
+
*
|
|
818
|
+
* @example Basic usage
|
|
819
|
+
* ```typescript
|
|
820
|
+
* for await (const event of pattern.run(provider, {
|
|
821
|
+
* system: 'You are an analyzer.',
|
|
822
|
+
* prompt: 'Analyze this document...',
|
|
823
|
+
* })) {
|
|
824
|
+
* if (event.type === 'progress') {
|
|
825
|
+
* console.log('Progress:', event.data);
|
|
826
|
+
* } else if (event.type === 'complete') {
|
|
827
|
+
* console.log('Result:', event.data);
|
|
828
|
+
* }
|
|
829
|
+
* }
|
|
830
|
+
* ```
|
|
831
|
+
*
|
|
832
|
+
* @example With step limit
|
|
833
|
+
* ```typescript
|
|
834
|
+
* import { stepCountIs } from 'ai';
|
|
835
|
+
*
|
|
836
|
+
* for await (const event of pattern.run(provider, {
|
|
837
|
+
* prompt: 'Analyze...',
|
|
838
|
+
* stopWhen: stepCountIs(10),
|
|
839
|
+
* })) { ... }
|
|
840
|
+
* ```
|
|
841
|
+
*/
|
|
842
|
+
run(provider, options) {
|
|
843
|
+
const self = this;
|
|
844
|
+
const execution = provider.streamingExecution(async function* (session) {
|
|
845
|
+
return yield* self.runInSession(session, options);
|
|
846
|
+
});
|
|
847
|
+
return execution.stream();
|
|
848
|
+
}
|
|
849
|
+
createTools() {
|
|
850
|
+
return {
|
|
851
|
+
reportProgress: ai.tool({
|
|
852
|
+
description: TOOL_DESCRIPTIONS.reportProgress,
|
|
853
|
+
inputSchema: zod.z.object({
|
|
854
|
+
data: this.progressSchema
|
|
855
|
+
}),
|
|
856
|
+
execute: async () => ({
|
|
857
|
+
status: "progress_recorded",
|
|
858
|
+
instruction: "After all progress reports, you MUST call tools::submitResult to complete the task."
|
|
859
|
+
})
|
|
860
|
+
}),
|
|
861
|
+
submitResult: ai.tool({
|
|
862
|
+
description: TOOL_DESCRIPTIONS.submitResult,
|
|
863
|
+
inputSchema: zod.z.object({
|
|
864
|
+
data: this.resultSchema
|
|
865
|
+
}),
|
|
866
|
+
execute: async () => ({
|
|
867
|
+
status: "result_submitted",
|
|
868
|
+
message: "Task completed successfully."
|
|
869
|
+
})
|
|
870
|
+
})
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
parseJsonWrapper(input, schema) {
|
|
874
|
+
try {
|
|
875
|
+
if (!input || typeof input !== "object") return null;
|
|
876
|
+
const wrapper = input;
|
|
877
|
+
if (wrapper.data === void 0) return null;
|
|
878
|
+
const parsed = typeof wrapper.data === "string" ? JSON.parse(wrapper.data) : wrapper.data;
|
|
879
|
+
return schema.parse(parsed);
|
|
880
|
+
} catch (error) {
|
|
881
|
+
this.lastParseError = error instanceof Error ? error : new Error(String(error));
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
parseProgressInput(input) {
|
|
886
|
+
return this.parseJsonWrapper(input, this.progressSchema);
|
|
887
|
+
}
|
|
888
|
+
parseResultInput(input) {
|
|
889
|
+
return this.parseJsonWrapper(input, this.resultSchema);
|
|
890
|
+
}
|
|
891
|
+
combineStopConditions(defaultCondition, userConditions) {
|
|
892
|
+
if (!userConditions) {
|
|
893
|
+
return [defaultCondition];
|
|
894
|
+
}
|
|
895
|
+
const userArray = Array.isArray(userConditions) ? userConditions : [userConditions];
|
|
896
|
+
return [defaultCondition, ...userArray];
|
|
897
|
+
}
|
|
898
|
+
renderSystemPrompt(userSystem, protocol) {
|
|
899
|
+
const protocolText = protocol ?? TOOL_CALLING_PROTOCOL;
|
|
900
|
+
return userSystem ? `${userSystem}
|
|
901
|
+
|
|
902
|
+
${protocolText}` : protocolText;
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
// src/pricing/validator.ts
|
|
907
|
+
function validatePriceValue(value, fieldName, context) {
|
|
908
|
+
if (!Number.isFinite(value)) {
|
|
909
|
+
throw new Error(`${context}: ${fieldName} must be a finite number`);
|
|
910
|
+
}
|
|
911
|
+
if (value < 0) {
|
|
912
|
+
throw new Error(`${context}: ${fieldName} cannot be negative`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function validateModelPricing(pricing, context) {
|
|
916
|
+
validatePriceValue(
|
|
917
|
+
pricing.inputPricePerMillion,
|
|
918
|
+
"inputPricePerMillion",
|
|
919
|
+
context
|
|
920
|
+
);
|
|
921
|
+
validatePriceValue(
|
|
922
|
+
pricing.outputPricePerMillion,
|
|
923
|
+
"outputPricePerMillion",
|
|
924
|
+
context
|
|
925
|
+
);
|
|
926
|
+
if (pricing.cachedInputPricePerMillion !== void 0) {
|
|
927
|
+
validatePriceValue(
|
|
928
|
+
pricing.cachedInputPricePerMillion,
|
|
929
|
+
"cachedInputPricePerMillion",
|
|
930
|
+
context
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
function validateProviderPricing(pricing, providerContext) {
|
|
935
|
+
for (const [model, modelPricing] of Object.entries(pricing)) {
|
|
936
|
+
const context = providerContext ? `${providerContext}/${model}` : model;
|
|
937
|
+
validateModelPricing(modelPricing, context);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
function validatePricingConfig(config) {
|
|
941
|
+
if (config.providers) {
|
|
942
|
+
for (const [providerKey, pricing] of Object.entries(config.providers)) {
|
|
943
|
+
if (pricing) {
|
|
944
|
+
validateProviderPricing(pricing, providerKey);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (config.fallback) {
|
|
949
|
+
validateModelPricing(config.fallback, "fallback");
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// src/pricing/defaults.ts
|
|
954
|
+
var OPENAI_PRICING = {
|
|
955
|
+
"gpt-4o": { inputPricePerMillion: 2.5, outputPricePerMillion: 10 },
|
|
956
|
+
"gpt-4o-mini": { inputPricePerMillion: 0.15, outputPricePerMillion: 0.6 },
|
|
957
|
+
"gpt-4-turbo": { inputPricePerMillion: 10, outputPricePerMillion: 30 },
|
|
958
|
+
"gpt-4-turbo-preview": {
|
|
959
|
+
inputPricePerMillion: 10,
|
|
960
|
+
outputPricePerMillion: 30
|
|
961
|
+
},
|
|
962
|
+
"gpt-4": { inputPricePerMillion: 30, outputPricePerMillion: 60 },
|
|
963
|
+
"gpt-4-32k": { inputPricePerMillion: 60, outputPricePerMillion: 120 },
|
|
964
|
+
"gpt-3.5-turbo": { inputPricePerMillion: 0.5, outputPricePerMillion: 1.5 },
|
|
965
|
+
"gpt-3.5-turbo-16k": {
|
|
966
|
+
inputPricePerMillion: 3,
|
|
967
|
+
outputPricePerMillion: 4
|
|
968
|
+
},
|
|
969
|
+
"o1": { inputPricePerMillion: 15, outputPricePerMillion: 60 },
|
|
970
|
+
"o1-mini": { inputPricePerMillion: 3, outputPricePerMillion: 12 },
|
|
971
|
+
"o1-preview": { inputPricePerMillion: 15, outputPricePerMillion: 60 },
|
|
972
|
+
"o3": { inputPricePerMillion: 20, outputPricePerMillion: 80 },
|
|
973
|
+
"o3-mini": { inputPricePerMillion: 4, outputPricePerMillion: 16 }
|
|
974
|
+
};
|
|
975
|
+
var GOOGLE_PRICING = {
|
|
976
|
+
"gemini-2.5-flash": {
|
|
977
|
+
inputPricePerMillion: 0.15,
|
|
978
|
+
outputPricePerMillion: 0.6,
|
|
979
|
+
cachedInputPricePerMillion: 0.0375
|
|
980
|
+
},
|
|
981
|
+
"gemini-2.5-flash-lite": {
|
|
982
|
+
inputPricePerMillion: 0.075,
|
|
983
|
+
outputPricePerMillion: 0.3,
|
|
984
|
+
cachedInputPricePerMillion: 0.01875
|
|
985
|
+
},
|
|
986
|
+
"gemini-2.5-pro": {
|
|
987
|
+
inputPricePerMillion: 1.25,
|
|
988
|
+
outputPricePerMillion: 10,
|
|
989
|
+
cachedInputPricePerMillion: 0.3125
|
|
990
|
+
},
|
|
991
|
+
"gemini-2.0-flash": {
|
|
992
|
+
inputPricePerMillion: 0.1,
|
|
993
|
+
outputPricePerMillion: 0.4,
|
|
994
|
+
cachedInputPricePerMillion: 0.025
|
|
995
|
+
},
|
|
996
|
+
"gemini-2.0-flash-lite": {
|
|
997
|
+
inputPricePerMillion: 0.075,
|
|
998
|
+
outputPricePerMillion: 0.3,
|
|
999
|
+
cachedInputPricePerMillion: 0.01875
|
|
1000
|
+
},
|
|
1001
|
+
"gemini-1.5-pro": {
|
|
1002
|
+
inputPricePerMillion: 1.25,
|
|
1003
|
+
outputPricePerMillion: 5,
|
|
1004
|
+
cachedInputPricePerMillion: 0.3125
|
|
1005
|
+
},
|
|
1006
|
+
"gemini-1.5-flash": {
|
|
1007
|
+
inputPricePerMillion: 0.075,
|
|
1008
|
+
outputPricePerMillion: 0.3,
|
|
1009
|
+
cachedInputPricePerMillion: 0.01875
|
|
1010
|
+
},
|
|
1011
|
+
"gemini-1.5-flash-8b": {
|
|
1012
|
+
inputPricePerMillion: 0.0375,
|
|
1013
|
+
outputPricePerMillion: 0.15,
|
|
1014
|
+
cachedInputPricePerMillion: 0.01
|
|
1015
|
+
},
|
|
1016
|
+
"gemini-pro": { inputPricePerMillion: 0.5, outputPricePerMillion: 1.5 }
|
|
1017
|
+
};
|
|
1018
|
+
var ANTHROPIC_PRICING = {
|
|
1019
|
+
"claude-opus-4-5-20250514": {
|
|
1020
|
+
inputPricePerMillion: 15,
|
|
1021
|
+
outputPricePerMillion: 75,
|
|
1022
|
+
cachedInputPricePerMillion: 1.875
|
|
1023
|
+
},
|
|
1024
|
+
"claude-sonnet-4-20250514": {
|
|
1025
|
+
inputPricePerMillion: 3,
|
|
1026
|
+
outputPricePerMillion: 15,
|
|
1027
|
+
cachedInputPricePerMillion: 0.375
|
|
1028
|
+
},
|
|
1029
|
+
"claude-3-5-sonnet-20241022": {
|
|
1030
|
+
inputPricePerMillion: 3,
|
|
1031
|
+
outputPricePerMillion: 15,
|
|
1032
|
+
cachedInputPricePerMillion: 0.375
|
|
1033
|
+
},
|
|
1034
|
+
"claude-3-5-haiku-20241022": {
|
|
1035
|
+
inputPricePerMillion: 0.8,
|
|
1036
|
+
outputPricePerMillion: 4,
|
|
1037
|
+
cachedInputPricePerMillion: 0.1
|
|
1038
|
+
},
|
|
1039
|
+
"claude-3-opus-20240229": {
|
|
1040
|
+
inputPricePerMillion: 15,
|
|
1041
|
+
outputPricePerMillion: 75,
|
|
1042
|
+
cachedInputPricePerMillion: 1.875
|
|
1043
|
+
},
|
|
1044
|
+
"claude-3-sonnet-20240229": {
|
|
1045
|
+
inputPricePerMillion: 3,
|
|
1046
|
+
outputPricePerMillion: 15,
|
|
1047
|
+
cachedInputPricePerMillion: 0.375
|
|
1048
|
+
},
|
|
1049
|
+
"claude-3-haiku-20240307": {
|
|
1050
|
+
inputPricePerMillion: 0.25,
|
|
1051
|
+
outputPricePerMillion: 1.25,
|
|
1052
|
+
cachedInputPricePerMillion: 0.03
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
var DEFAULT_PRICING_CONFIG = {
|
|
1056
|
+
providers: {
|
|
1057
|
+
openai: OPENAI_PRICING,
|
|
1058
|
+
google: GOOGLE_PRICING,
|
|
1059
|
+
anthropic: ANTHROPIC_PRICING
|
|
1060
|
+
},
|
|
1061
|
+
fallback: {
|
|
1062
|
+
inputPricePerMillion: 1,
|
|
1063
|
+
outputPricePerMillion: 5
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
var DEFAULT_FALLBACK_PRICING = {
|
|
1067
|
+
inputPricePerMillion: 1,
|
|
1068
|
+
outputPricePerMillion: 5
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
// src/pricing/config.ts
|
|
1072
|
+
var globalConfig;
|
|
1073
|
+
function configurePricing(config) {
|
|
1074
|
+
if (config) {
|
|
1075
|
+
validatePricingConfig(config);
|
|
1076
|
+
}
|
|
1077
|
+
globalConfig = config;
|
|
1078
|
+
}
|
|
1079
|
+
function getPricingConfig() {
|
|
1080
|
+
return globalConfig;
|
|
1081
|
+
}
|
|
1082
|
+
function resetPricingConfig() {
|
|
1083
|
+
globalConfig = void 0;
|
|
1084
|
+
}
|
|
1085
|
+
function getEffectivePricing(model, provider) {
|
|
1086
|
+
if (globalConfig?.providers?.[provider]?.[model]) {
|
|
1087
|
+
return {
|
|
1088
|
+
pricing: globalConfig.providers[provider][model],
|
|
1089
|
+
source: "global"
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
const defaultPricing = DEFAULT_PRICING_CONFIG.providers[provider];
|
|
1093
|
+
if (defaultPricing?.[model]) {
|
|
1094
|
+
return {
|
|
1095
|
+
pricing: defaultPricing[model],
|
|
1096
|
+
source: "default"
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
return {
|
|
1100
|
+
pricing: globalConfig?.fallback ?? DEFAULT_PRICING_CONFIG.fallback ?? DEFAULT_FALLBACK_PRICING,
|
|
1101
|
+
source: "fallback"
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// src/pricing/calculator.ts
|
|
1106
|
+
var TOKENS_PER_MILLION = 1e6;
|
|
1107
|
+
function getModelPricing(model, provider, providerPricing) {
|
|
1108
|
+
if (providerPricing?.[model]) {
|
|
1109
|
+
return providerPricing[model];
|
|
1110
|
+
}
|
|
1111
|
+
const globalConfig2 = getPricingConfig();
|
|
1112
|
+
if (globalConfig2?.providers?.[provider]?.[model]) {
|
|
1113
|
+
return globalConfig2.providers[provider][model];
|
|
1114
|
+
}
|
|
1115
|
+
const defaultProviderPricing = DEFAULT_PRICING_CONFIG.providers[provider];
|
|
1116
|
+
if (defaultProviderPricing?.[model]) {
|
|
1117
|
+
return defaultProviderPricing[model];
|
|
1118
|
+
}
|
|
1119
|
+
return globalConfig2?.fallback ?? DEFAULT_PRICING_CONFIG.fallback ?? DEFAULT_FALLBACK_PRICING;
|
|
1120
|
+
}
|
|
1121
|
+
function validateCostParams(params) {
|
|
1122
|
+
const { inputTokens, outputTokens, cachedInputTokens = 0 } = params;
|
|
1123
|
+
if (inputTokens < 0 || outputTokens < 0 || cachedInputTokens < 0) {
|
|
1124
|
+
throw new Error("Token counts must be non-negative");
|
|
1125
|
+
}
|
|
1126
|
+
if (cachedInputTokens > inputTokens) {
|
|
1127
|
+
throw new Error("cachedInputTokens cannot exceed inputTokens");
|
|
1128
|
+
}
|
|
1129
|
+
if (!Number.isFinite(inputTokens) || !Number.isFinite(outputTokens) || !Number.isFinite(cachedInputTokens)) {
|
|
1130
|
+
throw new Error("Token counts must be finite numbers");
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
function calculateCost(params, providerPricing) {
|
|
1134
|
+
validateCostParams(params);
|
|
1135
|
+
const {
|
|
1136
|
+
inputTokens,
|
|
1137
|
+
outputTokens,
|
|
1138
|
+
cachedInputTokens = 0,
|
|
1139
|
+
model,
|
|
1140
|
+
provider
|
|
1141
|
+
} = params;
|
|
1142
|
+
const pricing = getModelPricing(model, provider, providerPricing);
|
|
1143
|
+
const nonCachedInputTokens = inputTokens - cachedInputTokens;
|
|
1144
|
+
const inputCost = nonCachedInputTokens / TOKENS_PER_MILLION * pricing.inputPricePerMillion;
|
|
1145
|
+
const outputCost = outputTokens / TOKENS_PER_MILLION * pricing.outputPricePerMillion;
|
|
1146
|
+
const cachedInputPricePerMillion = pricing.cachedInputPricePerMillion ?? pricing.inputPricePerMillion;
|
|
1147
|
+
const cachedInputCost = cachedInputTokens / TOKENS_PER_MILLION * cachedInputPricePerMillion;
|
|
1148
|
+
return {
|
|
1149
|
+
total: inputCost + outputCost + cachedInputCost,
|
|
1150
|
+
inputCost,
|
|
1151
|
+
outputCost,
|
|
1152
|
+
cachedInputCost
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
function calculateCostFromUsage(usage, model, provider, providerPricing) {
|
|
1156
|
+
return calculateCost(
|
|
1157
|
+
{
|
|
1158
|
+
inputTokens: usage.inputTokens ?? 0,
|
|
1159
|
+
outputTokens: usage.outputTokens ?? 0,
|
|
1160
|
+
cachedInputTokens: usage.inputTokenDetails?.cacheReadTokens ?? 0,
|
|
1161
|
+
model,
|
|
1162
|
+
provider
|
|
1163
|
+
},
|
|
1164
|
+
providerPricing
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
function calculateTotalCost(calls, providerPricing) {
|
|
1168
|
+
const costByModel = {};
|
|
1169
|
+
let totalCost = 0;
|
|
1170
|
+
for (const call of calls) {
|
|
1171
|
+
const cost = calculateCostFromUsage(
|
|
1172
|
+
call.usage,
|
|
1173
|
+
call.model,
|
|
1174
|
+
call.provider,
|
|
1175
|
+
providerPricing
|
|
1176
|
+
);
|
|
1177
|
+
totalCost += cost.total;
|
|
1178
|
+
const key = `${call.provider}/${call.model}`;
|
|
1179
|
+
costByModel[key] = (costByModel[key] ?? 0) + cost.total;
|
|
1180
|
+
}
|
|
1181
|
+
return { totalCost, costByModel };
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// src/prompt/errors.ts
|
|
1185
|
+
var PromptErrorCode = /* @__PURE__ */ ((PromptErrorCode2) => {
|
|
1186
|
+
PromptErrorCode2["PROMPT_ERROR"] = "PROMPT_ERROR";
|
|
1187
|
+
PromptErrorCode2["NOT_FOUND"] = "PROMPT_NOT_FOUND";
|
|
1188
|
+
PromptErrorCode2["INVALID_FORMAT"] = "PROMPT_INVALID_FORMAT";
|
|
1189
|
+
PromptErrorCode2["TEMPLATE_ERROR"] = "PROMPT_TEMPLATE_ERROR";
|
|
1190
|
+
PromptErrorCode2["IO_ERROR"] = "PROMPT_IO_ERROR";
|
|
1191
|
+
return PromptErrorCode2;
|
|
1192
|
+
})(PromptErrorCode || {});
|
|
1193
|
+
var PromptError = class _PromptError extends AgtlantisError {
|
|
1194
|
+
constructor(message, options = {}) {
|
|
1195
|
+
super(message, {
|
|
1196
|
+
code: options.code ?? "PROMPT_ERROR" /* PROMPT_ERROR */,
|
|
1197
|
+
cause: options.cause,
|
|
1198
|
+
context: options.context
|
|
1199
|
+
});
|
|
1200
|
+
this.name = "PromptError";
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Creates a PromptError from an unknown error.
|
|
1204
|
+
*/
|
|
1205
|
+
static from(error, code = "PROMPT_ERROR" /* PROMPT_ERROR */, context) {
|
|
1206
|
+
if (error instanceof _PromptError) {
|
|
1207
|
+
return error;
|
|
1208
|
+
}
|
|
1209
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
1210
|
+
return new _PromptError(cause.message, { code, cause, context });
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
var PromptNotFoundError = class extends PromptError {
|
|
1214
|
+
promptId;
|
|
1215
|
+
version;
|
|
1216
|
+
constructor(promptId, version, options = {}) {
|
|
1217
|
+
const message = version ? `Prompt '${promptId}' version '${version}' not found` : `Prompt '${promptId}' not found`;
|
|
1218
|
+
super(message, {
|
|
1219
|
+
code: "PROMPT_NOT_FOUND" /* NOT_FOUND */,
|
|
1220
|
+
cause: options.cause,
|
|
1221
|
+
context: { promptId, version, ...options.context }
|
|
1222
|
+
});
|
|
1223
|
+
this.name = "PromptNotFoundError";
|
|
1224
|
+
this.promptId = promptId;
|
|
1225
|
+
this.version = version;
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
var PromptInvalidFormatError = class extends PromptError {
|
|
1229
|
+
promptId;
|
|
1230
|
+
details;
|
|
1231
|
+
constructor(promptId, details, options = {}) {
|
|
1232
|
+
super(`Invalid format for prompt '${promptId}': ${details}`, {
|
|
1233
|
+
code: "PROMPT_INVALID_FORMAT" /* INVALID_FORMAT */,
|
|
1234
|
+
cause: options.cause,
|
|
1235
|
+
context: { promptId, details, ...options.context }
|
|
1236
|
+
});
|
|
1237
|
+
this.name = "PromptInvalidFormatError";
|
|
1238
|
+
this.promptId = promptId;
|
|
1239
|
+
this.details = details;
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
var PromptTemplateError = class extends PromptError {
|
|
1243
|
+
promptId;
|
|
1244
|
+
details;
|
|
1245
|
+
constructor(promptId, details, options = {}) {
|
|
1246
|
+
super(`Template compilation failed for prompt '${promptId}': ${details}`, {
|
|
1247
|
+
code: "PROMPT_TEMPLATE_ERROR" /* TEMPLATE_ERROR */,
|
|
1248
|
+
cause: options.cause,
|
|
1249
|
+
context: { promptId, details, ...options.context }
|
|
1250
|
+
});
|
|
1251
|
+
this.name = "PromptTemplateError";
|
|
1252
|
+
this.promptId = promptId;
|
|
1253
|
+
this.details = details;
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
var PromptIOError = class extends PromptError {
|
|
1257
|
+
operation;
|
|
1258
|
+
path;
|
|
1259
|
+
constructor(operation, path5, options = {}) {
|
|
1260
|
+
const opText = operation === "list" ? "list prompts in" : `${operation} prompt file`;
|
|
1261
|
+
super(`Failed to ${opText}: ${path5}`, {
|
|
1262
|
+
code: "PROMPT_IO_ERROR" /* IO_ERROR */,
|
|
1263
|
+
cause: options.cause,
|
|
1264
|
+
context: { operation, path: path5, ...options.context }
|
|
1265
|
+
});
|
|
1266
|
+
this.name = "PromptIOError";
|
|
1267
|
+
this.operation = operation;
|
|
1268
|
+
this.path = path5;
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
// src/prompt/template.ts
|
|
1273
|
+
var handlebars = Handlebars__default.default.create();
|
|
1274
|
+
handlebars.registerHelper("add", (a, b) => {
|
|
1275
|
+
const numA = Number(a);
|
|
1276
|
+
const numB = Number(b);
|
|
1277
|
+
if (Number.isNaN(numA) || Number.isNaN(numB)) {
|
|
1278
|
+
return NaN;
|
|
1279
|
+
}
|
|
1280
|
+
return numA + numB;
|
|
1281
|
+
});
|
|
1282
|
+
function wrapTemplateError(promptId, error) {
|
|
1283
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1284
|
+
const cause = error instanceof Error ? error : void 0;
|
|
1285
|
+
return new PromptTemplateError(promptId, message, { cause });
|
|
1286
|
+
}
|
|
1287
|
+
function compileTemplate(template, promptId) {
|
|
1288
|
+
try {
|
|
1289
|
+
const compiled = handlebars.compile(template, {
|
|
1290
|
+
strict: true,
|
|
1291
|
+
noEscape: true
|
|
1292
|
+
});
|
|
1293
|
+
return (input) => {
|
|
1294
|
+
try {
|
|
1295
|
+
return compiled(input);
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
throw wrapTemplateError(promptId, error);
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
throw wrapTemplateError(promptId, error);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// src/prompt/prompt-template.ts
|
|
1306
|
+
var PromptTemplate = class _PromptTemplate {
|
|
1307
|
+
constructor(id, version, system, userTemplate) {
|
|
1308
|
+
this.id = id;
|
|
1309
|
+
this.version = version;
|
|
1310
|
+
this.system = system;
|
|
1311
|
+
this.userTemplate = userTemplate;
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Creates a PromptTemplate instance from raw data.
|
|
1315
|
+
*
|
|
1316
|
+
* @param data - Raw prompt template data
|
|
1317
|
+
* @returns PromptTemplate instance
|
|
1318
|
+
*/
|
|
1319
|
+
static from(data) {
|
|
1320
|
+
return new _PromptTemplate(data.id, data.version, data.system, data.userTemplate);
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Compiles templates and returns a PromptRenderer.
|
|
1324
|
+
*
|
|
1325
|
+
* @typeParam TSystemInput - Type of input for system prompt template
|
|
1326
|
+
* @typeParam TUserInput - Type of input for user prompt template (defaults to TSystemInput)
|
|
1327
|
+
* @returns Compiled prompt renderer with renderSystemPrompt and renderUserPrompt functions
|
|
1328
|
+
* @throws {PromptTemplateError} If template compilation fails
|
|
1329
|
+
*
|
|
1330
|
+
* @example
|
|
1331
|
+
* ```typescript
|
|
1332
|
+
* // Different input types for system and user prompts
|
|
1333
|
+
* const renderer = template.compile<SessionCtx, TurnCtx>();
|
|
1334
|
+
*
|
|
1335
|
+
* // Same input type for both
|
|
1336
|
+
* const simpleRenderer = template.compile<CommonCtx>();
|
|
1337
|
+
* ```
|
|
1338
|
+
*/
|
|
1339
|
+
compile() {
|
|
1340
|
+
return {
|
|
1341
|
+
id: this.id,
|
|
1342
|
+
version: this.version,
|
|
1343
|
+
renderSystemPrompt: compileTemplate(this.system, this.id),
|
|
1344
|
+
renderUserPrompt: compileTemplate(this.userTemplate, this.id)
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Returns raw data representation.
|
|
1349
|
+
* Useful for serialization or passing to repository.write().
|
|
1350
|
+
*/
|
|
1351
|
+
toData() {
|
|
1352
|
+
return {
|
|
1353
|
+
id: this.id,
|
|
1354
|
+
version: this.version,
|
|
1355
|
+
system: this.system,
|
|
1356
|
+
userTemplate: this.userTemplate
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
function toErrorCause(error) {
|
|
1361
|
+
return error instanceof Error ? error : void 0;
|
|
1362
|
+
}
|
|
1363
|
+
function toErrorMessage(error) {
|
|
1364
|
+
return error instanceof Error ? error.message : String(error);
|
|
1365
|
+
}
|
|
1366
|
+
var defaultFileSystem = {
|
|
1367
|
+
readFile: (filePath) => fs__namespace.readFile(filePath, "utf-8"),
|
|
1368
|
+
writeFile: (filePath, content) => fs__namespace.writeFile(filePath, content, "utf-8"),
|
|
1369
|
+
readdir: (dirPath) => fs__namespace.readdir(dirPath)
|
|
1370
|
+
};
|
|
1371
|
+
function parseVersion(version) {
|
|
1372
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
1373
|
+
if (!match) return null;
|
|
1374
|
+
return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10)];
|
|
1375
|
+
}
|
|
1376
|
+
function compareVersions(a, b) {
|
|
1377
|
+
const parsedA = parseVersion(a);
|
|
1378
|
+
const parsedB = parseVersion(b);
|
|
1379
|
+
if (!parsedA && !parsedB) return 0;
|
|
1380
|
+
if (!parsedA) return 1;
|
|
1381
|
+
if (!parsedB) return -1;
|
|
1382
|
+
for (let i = 0; i < 3; i++) {
|
|
1383
|
+
if (parsedA[i] !== parsedB[i]) {
|
|
1384
|
+
return parsedA[i] - parsedB[i];
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
return 0;
|
|
1388
|
+
}
|
|
1389
|
+
var FILE_EXTENSION = ".yaml";
|
|
1390
|
+
function getFileName(id, version) {
|
|
1391
|
+
return `${id}-${version}${FILE_EXTENSION}`;
|
|
1392
|
+
}
|
|
1393
|
+
function parseFileName(fileName) {
|
|
1394
|
+
if (!fileName.endsWith(FILE_EXTENSION)) return null;
|
|
1395
|
+
const baseName = fileName.slice(0, -FILE_EXTENSION.length);
|
|
1396
|
+
const lastDash = baseName.lastIndexOf("-");
|
|
1397
|
+
if (lastDash === -1) return null;
|
|
1398
|
+
const id = baseName.slice(0, lastDash);
|
|
1399
|
+
const version = baseName.slice(lastDash + 1);
|
|
1400
|
+
if (!id || !parseVersion(version)) return null;
|
|
1401
|
+
return { id, version };
|
|
1402
|
+
}
|
|
1403
|
+
function parsePromptYaml(content, promptId) {
|
|
1404
|
+
let parsed;
|
|
1405
|
+
try {
|
|
1406
|
+
parsed = yaml__namespace.parse(content);
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
throw new PromptInvalidFormatError(
|
|
1409
|
+
promptId,
|
|
1410
|
+
`Invalid YAML: ${toErrorMessage(error)}`,
|
|
1411
|
+
{ cause: toErrorCause(error) }
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
if (!parsed || typeof parsed !== "object") {
|
|
1415
|
+
throw new PromptInvalidFormatError(promptId, "Expected YAML object");
|
|
1416
|
+
}
|
|
1417
|
+
const obj = parsed;
|
|
1418
|
+
const requiredFields = ["id", "version", "system", "userTemplate"];
|
|
1419
|
+
for (const field of requiredFields) {
|
|
1420
|
+
if (typeof obj[field] !== "string") {
|
|
1421
|
+
throw new PromptInvalidFormatError(
|
|
1422
|
+
promptId,
|
|
1423
|
+
`Missing or invalid required field: ${field} (expected string)`
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
return {
|
|
1428
|
+
id: obj.id,
|
|
1429
|
+
version: obj.version,
|
|
1430
|
+
system: obj.system,
|
|
1431
|
+
userTemplate: obj.userTemplate
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
function serializePromptYaml(content) {
|
|
1435
|
+
return yaml__namespace.stringify(content);
|
|
1436
|
+
}
|
|
1437
|
+
var FilePromptRepository = class {
|
|
1438
|
+
directory;
|
|
1439
|
+
fileSystem;
|
|
1440
|
+
cacheEnabled;
|
|
1441
|
+
contentCache = /* @__PURE__ */ new Map();
|
|
1442
|
+
constructor(options) {
|
|
1443
|
+
this.directory = options.directory;
|
|
1444
|
+
this.fileSystem = options.fs ?? defaultFileSystem;
|
|
1445
|
+
this.cacheEnabled = options.cache ?? true;
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Generates file name from prompt id and version.
|
|
1449
|
+
* Override this along with `parseFileName` to change file naming convention.
|
|
1450
|
+
*/
|
|
1451
|
+
getFileName(id, version) {
|
|
1452
|
+
return getFileName(id, version);
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Parses file name into id and version.
|
|
1456
|
+
* Override this along with `getFileName` to change file naming convention.
|
|
1457
|
+
*/
|
|
1458
|
+
parseFileName(fileName) {
|
|
1459
|
+
return parseFileName(fileName);
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Parses raw file content into PromptTemplateData.
|
|
1463
|
+
* Override this to support different file formats (e.g., JSON, TOML).
|
|
1464
|
+
*/
|
|
1465
|
+
parseContent(content, promptId) {
|
|
1466
|
+
return parsePromptYaml(content, promptId);
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Serializes PromptTemplateData to file content string.
|
|
1470
|
+
* Override this to support different file formats (e.g., JSON, TOML).
|
|
1471
|
+
*/
|
|
1472
|
+
serializeContent(content) {
|
|
1473
|
+
return serializePromptYaml(content);
|
|
1474
|
+
}
|
|
1475
|
+
getCacheKey(id, version) {
|
|
1476
|
+
return `${id}:${version}`;
|
|
1477
|
+
}
|
|
1478
|
+
async read(id, version) {
|
|
1479
|
+
if (version) {
|
|
1480
|
+
const cacheKey = this.getCacheKey(id, version);
|
|
1481
|
+
if (this.cacheEnabled) {
|
|
1482
|
+
const cached = this.contentCache.get(cacheKey);
|
|
1483
|
+
if (cached) {
|
|
1484
|
+
return cached;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
const fileName = this.getFileName(id, version);
|
|
1488
|
+
const filePath = path3__namespace.join(this.directory, fileName);
|
|
1489
|
+
let content;
|
|
1490
|
+
try {
|
|
1491
|
+
content = await this.fileSystem.readFile(filePath);
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
1494
|
+
throw new PromptNotFoundError(id, version);
|
|
1495
|
+
}
|
|
1496
|
+
throw new PromptIOError("read", filePath, { cause: toErrorCause(error) });
|
|
1497
|
+
}
|
|
1498
|
+
const promptContent = this.parseContent(content, id);
|
|
1499
|
+
if (promptContent.id !== id || promptContent.version !== version) {
|
|
1500
|
+
throw new PromptInvalidFormatError(
|
|
1501
|
+
id,
|
|
1502
|
+
`File content mismatch: expected id='${id}' version='${version}', got id='${promptContent.id}' version='${promptContent.version}'`
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
if (this.cacheEnabled) {
|
|
1506
|
+
this.contentCache.set(cacheKey, promptContent);
|
|
1507
|
+
}
|
|
1508
|
+
return promptContent;
|
|
1509
|
+
}
|
|
1510
|
+
let files;
|
|
1511
|
+
try {
|
|
1512
|
+
files = await this.fileSystem.readdir(this.directory);
|
|
1513
|
+
} catch (error) {
|
|
1514
|
+
throw new PromptIOError("list", this.directory, { cause: toErrorCause(error) });
|
|
1515
|
+
}
|
|
1516
|
+
const versions = [];
|
|
1517
|
+
for (const file of files) {
|
|
1518
|
+
const parsed = this.parseFileName(file);
|
|
1519
|
+
if (parsed && parsed.id === id) {
|
|
1520
|
+
versions.push(parsed.version);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
if (versions.length === 0) {
|
|
1524
|
+
throw new PromptNotFoundError(id);
|
|
1525
|
+
}
|
|
1526
|
+
versions.sort((a, b) => compareVersions(b, a));
|
|
1527
|
+
const latestVersion = versions[0];
|
|
1528
|
+
return this.read(id, latestVersion);
|
|
1529
|
+
}
|
|
1530
|
+
async write(content) {
|
|
1531
|
+
if (this.cacheEnabled) {
|
|
1532
|
+
this.contentCache.delete(this.getCacheKey(content.id, content.version));
|
|
1533
|
+
}
|
|
1534
|
+
const fileName = this.getFileName(content.id, content.version);
|
|
1535
|
+
const filePath = path3__namespace.join(this.directory, fileName);
|
|
1536
|
+
if (!parseVersion(content.version)) {
|
|
1537
|
+
throw new PromptInvalidFormatError(
|
|
1538
|
+
content.id,
|
|
1539
|
+
`Invalid version format: '${content.version}' (expected semver like '1.0.0')`
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
compileTemplate(content.system, content.id);
|
|
1543
|
+
compileTemplate(content.userTemplate, content.id);
|
|
1544
|
+
const yamlContent = this.serializeContent(content);
|
|
1545
|
+
try {
|
|
1546
|
+
await this.fileSystem.writeFile(filePath, yamlContent);
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
throw new PromptIOError("write", filePath, { cause: toErrorCause(error) });
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
};
|
|
1552
|
+
function createFilePromptRepository(options) {
|
|
1553
|
+
return new FilePromptRepository(options);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// src/provider/types.ts
|
|
1557
|
+
var FILE_SOURCE_TYPES = /* @__PURE__ */ new Set(["path", "data", "base64", "url"]);
|
|
1558
|
+
function isFileSource(v) {
|
|
1559
|
+
return typeof v === "object" && v !== null && FILE_SOURCE_TYPES.has(v.source);
|
|
1560
|
+
}
|
|
1561
|
+
function isFileSourcePath(v) {
|
|
1562
|
+
return v.source === "path";
|
|
1563
|
+
}
|
|
1564
|
+
function isFileSourceData(v) {
|
|
1565
|
+
return v.source === "data";
|
|
1566
|
+
}
|
|
1567
|
+
function isFileSourceBase64(v) {
|
|
1568
|
+
return v.source === "base64";
|
|
1569
|
+
}
|
|
1570
|
+
function isFileSourceUrl(v) {
|
|
1571
|
+
return v.source === "url";
|
|
1572
|
+
}
|
|
1573
|
+
async function computeFileSourceHash(source) {
|
|
1574
|
+
if (source.hash) {
|
|
1575
|
+
return source.hash;
|
|
1576
|
+
}
|
|
1577
|
+
const hash = crypto.createHash("sha256");
|
|
1578
|
+
switch (source.source) {
|
|
1579
|
+
case "path": {
|
|
1580
|
+
const fullPath = path3__namespace.default.isAbsolute(source.path) ? source.path : path3__namespace.default.resolve(process.cwd(), source.path);
|
|
1581
|
+
const content = await fs.readFile(fullPath);
|
|
1582
|
+
hash.update(content);
|
|
1583
|
+
break;
|
|
1584
|
+
}
|
|
1585
|
+
case "data": {
|
|
1586
|
+
hash.update(source.data);
|
|
1587
|
+
break;
|
|
1588
|
+
}
|
|
1589
|
+
case "base64": {
|
|
1590
|
+
const buffer = Buffer.from(source.data, "base64");
|
|
1591
|
+
hash.update(buffer);
|
|
1592
|
+
break;
|
|
1593
|
+
}
|
|
1594
|
+
case "url": {
|
|
1595
|
+
hash.update(source.url);
|
|
1596
|
+
break;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
return hash.digest("hex");
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// src/provider/file-cache.ts
|
|
1603
|
+
var InMemoryFileCache = class {
|
|
1604
|
+
cache = /* @__PURE__ */ new Map();
|
|
1605
|
+
defaultTTL;
|
|
1606
|
+
constructor(options) {
|
|
1607
|
+
this.defaultTTL = options?.defaultTTL;
|
|
1608
|
+
}
|
|
1609
|
+
get(hash) {
|
|
1610
|
+
const entry = this.cache.get(hash);
|
|
1611
|
+
if (!entry) return null;
|
|
1612
|
+
if (entry.expiresAt !== void 0 && Date.now() > entry.expiresAt) {
|
|
1613
|
+
this.cache.delete(hash);
|
|
1614
|
+
return null;
|
|
1615
|
+
}
|
|
1616
|
+
return entry.file;
|
|
1617
|
+
}
|
|
1618
|
+
set(hash, file, ttl) {
|
|
1619
|
+
const effectiveTTL = ttl ?? this.defaultTTL;
|
|
1620
|
+
const expiresAt = effectiveTTL !== void 0 ? Date.now() + effectiveTTL : void 0;
|
|
1621
|
+
this.cache.set(hash, { file, expiresAt });
|
|
1622
|
+
}
|
|
1623
|
+
delete(hash) {
|
|
1624
|
+
this.cache.delete(hash);
|
|
1625
|
+
}
|
|
1626
|
+
clear() {
|
|
1627
|
+
this.cache.clear();
|
|
1628
|
+
}
|
|
1629
|
+
};
|
|
1630
|
+
|
|
1631
|
+
// src/provider/base-provider.ts
|
|
1632
|
+
var BaseProvider = class {
|
|
1633
|
+
streamingExecution(generator, options) {
|
|
1634
|
+
return new StreamingExecutionHost(
|
|
1635
|
+
(signal) => this.createStreamingSession(signal),
|
|
1636
|
+
generator,
|
|
1637
|
+
options?.signal
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Execute a non-streaming function with cancellation support.
|
|
1642
|
+
* Returns immediately - execution starts in the background.
|
|
1643
|
+
*/
|
|
1644
|
+
simpleExecution(fn, options) {
|
|
1645
|
+
return new SimpleExecutionHost(
|
|
1646
|
+
(signal) => this.createSimpleSession(signal),
|
|
1647
|
+
fn,
|
|
1648
|
+
options?.signal
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
// src/provider/noop-file-manager.ts
|
|
1654
|
+
var NoOpFileManager = class {
|
|
1655
|
+
upload(_files) {
|
|
1656
|
+
throw new FileError("File upload not supported by this provider", {
|
|
1657
|
+
code: "UNSUPPORTED_TYPE" /* UNSUPPORTED_TYPE */,
|
|
1658
|
+
context: {
|
|
1659
|
+
provider: "noop",
|
|
1660
|
+
suggestion: "Use a provider with file support (e.g., Google) or pass files inline"
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
delete(_fileId) {
|
|
1665
|
+
throw new FileError("File delete not supported by this provider", {
|
|
1666
|
+
code: "UNSUPPORTED_TYPE" /* UNSUPPORTED_TYPE */,
|
|
1667
|
+
context: {
|
|
1668
|
+
provider: "noop"
|
|
1669
|
+
}
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
clear() {
|
|
1673
|
+
return Promise.resolve();
|
|
1674
|
+
}
|
|
1675
|
+
getUploadedFiles() {
|
|
1676
|
+
return [];
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
var EXTENSION_TO_MIME = {
|
|
1680
|
+
".pdf": "application/pdf",
|
|
1681
|
+
".html": "text/html",
|
|
1682
|
+
".htm": "text/html",
|
|
1683
|
+
".txt": "text/plain",
|
|
1684
|
+
".json": "application/json",
|
|
1685
|
+
".xml": "application/xml",
|
|
1686
|
+
".csv": "text/csv",
|
|
1687
|
+
".png": "image/png",
|
|
1688
|
+
".jpg": "image/jpeg",
|
|
1689
|
+
".jpeg": "image/jpeg",
|
|
1690
|
+
".gif": "image/gif",
|
|
1691
|
+
".webp": "image/webp",
|
|
1692
|
+
".svg": "image/svg+xml",
|
|
1693
|
+
".bmp": "image/bmp",
|
|
1694
|
+
".zip": "application/zip"
|
|
1695
|
+
};
|
|
1696
|
+
function inferMediaType(filePath) {
|
|
1697
|
+
const ext = path3__namespace.extname(filePath).toLowerCase();
|
|
1698
|
+
return EXTENSION_TO_MIME[ext];
|
|
1699
|
+
}
|
|
1700
|
+
function scanForFileSources(input, currentPath = []) {
|
|
1701
|
+
if (isFileSource(input)) {
|
|
1702
|
+
return [{ part: input, path: currentPath }];
|
|
1703
|
+
}
|
|
1704
|
+
if (input === null || typeof input !== "object") {
|
|
1705
|
+
return [];
|
|
1706
|
+
}
|
|
1707
|
+
if (Buffer.isBuffer(input) || input instanceof Uint8Array || input instanceof URL) {
|
|
1708
|
+
return [];
|
|
1709
|
+
}
|
|
1710
|
+
const results = [];
|
|
1711
|
+
if (Array.isArray(input)) {
|
|
1712
|
+
for (let i = 0; i < input.length; i++) {
|
|
1713
|
+
results.push(...scanForFileSources(input[i], [...currentPath, i]));
|
|
1714
|
+
}
|
|
1715
|
+
} else {
|
|
1716
|
+
for (const key of Object.keys(input)) {
|
|
1717
|
+
results.push(
|
|
1718
|
+
...scanForFileSources(input[key], [
|
|
1719
|
+
...currentPath,
|
|
1720
|
+
key
|
|
1721
|
+
])
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
return results;
|
|
1726
|
+
}
|
|
1727
|
+
var DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
|
|
1728
|
+
async function resolveFileSource(part, options = {}) {
|
|
1729
|
+
const { basePath = process.cwd(), maxSize = DEFAULT_MAX_SIZE } = options;
|
|
1730
|
+
if (!isFileSourcePath(part)) {
|
|
1731
|
+
return part;
|
|
1732
|
+
}
|
|
1733
|
+
const fullPath = path3__namespace.isAbsolute(part.path) ? part.path : path3__namespace.resolve(basePath, part.path);
|
|
1734
|
+
const stats = await fs.stat(fullPath).catch((err) => {
|
|
1735
|
+
throw new FileError(`File not found: ${part.path}`, {
|
|
1736
|
+
code: "NOT_FOUND" /* NOT_FOUND */,
|
|
1737
|
+
context: { path: part.path, fullPath },
|
|
1738
|
+
cause: err
|
|
1739
|
+
});
|
|
1740
|
+
});
|
|
1741
|
+
if (stats.size > maxSize) {
|
|
1742
|
+
throw new FileError(`File too large: ${stats.size} bytes > ${maxSize} bytes`, {
|
|
1743
|
+
code: "TOO_LARGE" /* TOO_LARGE */,
|
|
1744
|
+
context: { path: part.path, size: stats.size, maxSize }
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
const buffer = await fs.readFile(fullPath);
|
|
1748
|
+
const mediaType = part.mediaType ?? inferMediaType(part.path) ?? "application/octet-stream";
|
|
1749
|
+
const filename = part.filename ?? path3__namespace.basename(part.path);
|
|
1750
|
+
const result = {
|
|
1751
|
+
source: "data",
|
|
1752
|
+
data: buffer,
|
|
1753
|
+
mediaType,
|
|
1754
|
+
filename
|
|
1755
|
+
};
|
|
1756
|
+
return result;
|
|
1757
|
+
}
|
|
1758
|
+
function deepClone(value) {
|
|
1759
|
+
if (value === null || typeof value !== "object") return value;
|
|
1760
|
+
if (Buffer.isBuffer(value)) return Buffer.from(value);
|
|
1761
|
+
if (value instanceof URL) return new URL(value.href);
|
|
1762
|
+
if (value instanceof Uint8Array) return new Uint8Array(value);
|
|
1763
|
+
if (Array.isArray(value)) return value.map(deepClone);
|
|
1764
|
+
const result = {};
|
|
1765
|
+
for (const key of Object.keys(value)) {
|
|
1766
|
+
result[key] = deepClone(value[key]);
|
|
1767
|
+
}
|
|
1768
|
+
return result;
|
|
1769
|
+
}
|
|
1770
|
+
function setAtPath(obj, targetPath, value) {
|
|
1771
|
+
if (targetPath.length === 0) {
|
|
1772
|
+
return value;
|
|
1773
|
+
}
|
|
1774
|
+
let current = obj;
|
|
1775
|
+
for (let i = 0; i < targetPath.length - 1; i++) {
|
|
1776
|
+
current = current[targetPath[i]];
|
|
1777
|
+
}
|
|
1778
|
+
current[targetPath[targetPath.length - 1]] = value;
|
|
1779
|
+
return obj;
|
|
1780
|
+
}
|
|
1781
|
+
async function resolveFileSourcesInInput(input, options = {}) {
|
|
1782
|
+
const found = scanForFileSources(input);
|
|
1783
|
+
if (found.length === 0) {
|
|
1784
|
+
return input;
|
|
1785
|
+
}
|
|
1786
|
+
const resolved = await Promise.all(found.map(({ part }) => resolveFileSource(part, options)));
|
|
1787
|
+
if (found.length === 1 && found[0].path.length === 0) {
|
|
1788
|
+
return resolved[0];
|
|
1789
|
+
}
|
|
1790
|
+
const result = deepClone(input);
|
|
1791
|
+
for (let i = 0; i < found.length; i++) {
|
|
1792
|
+
setAtPath(result, found[i].path, resolved[i]);
|
|
1793
|
+
}
|
|
1794
|
+
return result;
|
|
1795
|
+
}
|
|
1796
|
+
function getFileSourceDisplayInfo(part) {
|
|
1797
|
+
switch (part.source) {
|
|
1798
|
+
case "path":
|
|
1799
|
+
return {
|
|
1800
|
+
source: "path",
|
|
1801
|
+
description: part.path,
|
|
1802
|
+
mediaType: part.mediaType ?? inferMediaType(part.path) ?? "unknown",
|
|
1803
|
+
filename: part.filename ?? path3__namespace.basename(part.path)
|
|
1804
|
+
};
|
|
1805
|
+
case "url":
|
|
1806
|
+
return {
|
|
1807
|
+
source: "url",
|
|
1808
|
+
description: part.url,
|
|
1809
|
+
mediaType: part.mediaType ?? "unknown",
|
|
1810
|
+
filename: part.filename
|
|
1811
|
+
};
|
|
1812
|
+
case "base64": {
|
|
1813
|
+
const sizeKB = (part.data.length * 3 / 4 / 1024).toFixed(1);
|
|
1814
|
+
return {
|
|
1815
|
+
source: "base64",
|
|
1816
|
+
description: `[base64 data, ~${sizeKB}KB]`,
|
|
1817
|
+
mediaType: part.mediaType,
|
|
1818
|
+
filename: part.filename
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
case "data": {
|
|
1822
|
+
const size = Buffer.isBuffer(part.data) ? part.data.length : part.data.length;
|
|
1823
|
+
const sizeKB = (size / 1024).toFixed(1);
|
|
1824
|
+
return {
|
|
1825
|
+
source: "data",
|
|
1826
|
+
description: `[Buffer, ${sizeKB}KB]`,
|
|
1827
|
+
mediaType: part.mediaType,
|
|
1828
|
+
filename: part.filename
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function getFileSourcesDisplayInfo(input) {
|
|
1834
|
+
const found = scanForFileSources(input);
|
|
1835
|
+
return found.map(({ part }) => getFileSourceDisplayInfo(part));
|
|
1836
|
+
}
|
|
1837
|
+
function assertValidUploadResponse(response, context) {
|
|
1838
|
+
if (!response.name || !response.uri || !response.mimeType) {
|
|
1839
|
+
throw new FileError("Invalid upload response from Google API: missing required fields", {
|
|
1840
|
+
code: "UPLOAD_ERROR" /* UPLOAD_ERROR */,
|
|
1841
|
+
context: {
|
|
1842
|
+
...context,
|
|
1843
|
+
hasName: !!response.name,
|
|
1844
|
+
hasUri: !!response.uri,
|
|
1845
|
+
hasMediaType: !!response.mimeType
|
|
1846
|
+
}
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
function isImageMediaType(mediaType) {
|
|
1851
|
+
return mediaType.startsWith("image/");
|
|
1852
|
+
}
|
|
1853
|
+
function createPart(uri, mediaType) {
|
|
1854
|
+
const url = new URL(uri);
|
|
1855
|
+
if (isImageMediaType(mediaType)) {
|
|
1856
|
+
return { type: "image", image: url, mediaType };
|
|
1857
|
+
}
|
|
1858
|
+
return { type: "file", data: url, mediaType };
|
|
1859
|
+
}
|
|
1860
|
+
var GoogleFileManager = class {
|
|
1861
|
+
uploadedFiles = [];
|
|
1862
|
+
client;
|
|
1863
|
+
cache;
|
|
1864
|
+
constructor(apiKey, options) {
|
|
1865
|
+
this.client = new genai.GoogleGenAI({ apiKey });
|
|
1866
|
+
this.cache = options?.cache ?? null;
|
|
1867
|
+
}
|
|
1868
|
+
async uploadOne(fileSource, index, hash) {
|
|
1869
|
+
if (this.cache) {
|
|
1870
|
+
const cached = this.cache.get(hash);
|
|
1871
|
+
if (cached) return cached;
|
|
1872
|
+
}
|
|
1873
|
+
if (fileSource.source === "url") {
|
|
1874
|
+
const mediaType = fileSource.mediaType ?? "application/octet-stream";
|
|
1875
|
+
const result = {
|
|
1876
|
+
id: null,
|
|
1877
|
+
part: createPart(fileSource.url, mediaType)
|
|
1878
|
+
};
|
|
1879
|
+
if (this.cache) {
|
|
1880
|
+
this.cache.set(hash, result);
|
|
1881
|
+
}
|
|
1882
|
+
return result;
|
|
1883
|
+
}
|
|
1884
|
+
if (fileSource.source === "path") {
|
|
1885
|
+
const fullPath = path3__namespace.default.isAbsolute(fileSource.path) ? fileSource.path : path3__namespace.default.resolve(process.cwd(), fileSource.path);
|
|
1886
|
+
try {
|
|
1887
|
+
const uploaded = await this.client.files.upload({
|
|
1888
|
+
file: fullPath,
|
|
1889
|
+
config: {
|
|
1890
|
+
mimeType: fileSource.mediaType,
|
|
1891
|
+
displayName: fileSource.filename ?? path3__namespace.default.basename(fileSource.path)
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
assertValidUploadResponse(uploaded, {
|
|
1895
|
+
source: "path",
|
|
1896
|
+
path: fileSource.path
|
|
1897
|
+
});
|
|
1898
|
+
const result = {
|
|
1899
|
+
id: uploaded.name,
|
|
1900
|
+
part: createPart(uploaded.uri, uploaded.mimeType)
|
|
1901
|
+
};
|
|
1902
|
+
if (this.cache) {
|
|
1903
|
+
this.cache.set(hash, result);
|
|
1904
|
+
}
|
|
1905
|
+
return result;
|
|
1906
|
+
} catch (error) {
|
|
1907
|
+
if (error instanceof FileError) throw error;
|
|
1908
|
+
throw FileError.from(error, "UPLOAD_ERROR" /* UPLOAD_ERROR */, {
|
|
1909
|
+
source: "path",
|
|
1910
|
+
path: fileSource.path,
|
|
1911
|
+
mediaType: fileSource.mediaType
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
const buffer = fileSource.source === "base64" ? Buffer.from(fileSource.data, "base64") : fileSource.data;
|
|
1916
|
+
const arrayBuffer = buffer.buffer.slice(
|
|
1917
|
+
buffer.byteOffset,
|
|
1918
|
+
buffer.byteOffset + buffer.byteLength
|
|
1919
|
+
);
|
|
1920
|
+
const blob = new Blob([arrayBuffer], { type: fileSource.mediaType });
|
|
1921
|
+
try {
|
|
1922
|
+
const uploaded = await this.client.files.upload({
|
|
1923
|
+
file: blob,
|
|
1924
|
+
config: {
|
|
1925
|
+
mimeType: fileSource.mediaType,
|
|
1926
|
+
displayName: fileSource.filename ?? `upload-${Date.now()}-${index}`
|
|
1927
|
+
}
|
|
1928
|
+
});
|
|
1929
|
+
assertValidUploadResponse(uploaded, {
|
|
1930
|
+
source: fileSource.source,
|
|
1931
|
+
mediaType: fileSource.mediaType
|
|
1932
|
+
});
|
|
1933
|
+
const result = {
|
|
1934
|
+
id: uploaded.name,
|
|
1935
|
+
part: createPart(uploaded.uri, uploaded.mimeType)
|
|
1936
|
+
};
|
|
1937
|
+
if (this.cache) {
|
|
1938
|
+
this.cache.set(hash, result);
|
|
1939
|
+
}
|
|
1940
|
+
return result;
|
|
1941
|
+
} catch (error) {
|
|
1942
|
+
if (error instanceof FileError) throw error;
|
|
1943
|
+
throw FileError.from(error, "UPLOAD_ERROR" /* UPLOAD_ERROR */, {
|
|
1944
|
+
source: fileSource.source,
|
|
1945
|
+
mediaType: fileSource.mediaType,
|
|
1946
|
+
filename: fileSource.filename
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
async upload(files) {
|
|
1951
|
+
const hashes = await Promise.all(files.map((file) => computeFileSourceHash(file)));
|
|
1952
|
+
const results = await Promise.allSettled(
|
|
1953
|
+
files.map((file, i) => this.uploadOne(file, i, hashes[i]))
|
|
1954
|
+
);
|
|
1955
|
+
const successful = [];
|
|
1956
|
+
const failed = [];
|
|
1957
|
+
for (let i = 0; i < results.length; i++) {
|
|
1958
|
+
const result = results[i];
|
|
1959
|
+
if (result.status === "fulfilled") {
|
|
1960
|
+
successful.push({ file: result.value, hash: hashes[i] });
|
|
1961
|
+
} else {
|
|
1962
|
+
failed.push(result);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
if (failed.length > 0) {
|
|
1966
|
+
await Promise.all(
|
|
1967
|
+
successful.filter(({ file }) => file.id !== null).map(async ({ file, hash }) => {
|
|
1968
|
+
await this.client.files.delete({ name: file.id }).catch(() => {
|
|
1969
|
+
});
|
|
1970
|
+
if (this.cache) {
|
|
1971
|
+
this.cache.delete(hash);
|
|
1972
|
+
}
|
|
1973
|
+
})
|
|
1974
|
+
);
|
|
1975
|
+
const firstError = failed[0].reason;
|
|
1976
|
+
throw new FileError(
|
|
1977
|
+
`Failed to upload ${failed.length} file(s): ${firstError instanceof Error ? firstError.message : String(firstError)}`,
|
|
1978
|
+
{
|
|
1979
|
+
code: "UPLOAD_ERROR" /* UPLOAD_ERROR */,
|
|
1980
|
+
cause: firstError instanceof Error ? firstError : void 0,
|
|
1981
|
+
context: {
|
|
1982
|
+
totalFiles: files.length,
|
|
1983
|
+
failedCount: failed.length,
|
|
1984
|
+
successCount: successful.length
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
);
|
|
1988
|
+
}
|
|
1989
|
+
this.uploadedFiles.push(...successful.filter(({ file }) => file.id !== null).map(({ file }) => file));
|
|
1990
|
+
return successful.map(({ file }) => file);
|
|
1991
|
+
}
|
|
1992
|
+
async delete(fileId) {
|
|
1993
|
+
try {
|
|
1994
|
+
await this.client.files.delete({ name: fileId });
|
|
1995
|
+
this.uploadedFiles = this.uploadedFiles.filter((f) => f.id !== fileId);
|
|
1996
|
+
} catch (error) {
|
|
1997
|
+
throw FileError.from(error, "DELETE_ERROR" /* DELETE_ERROR */, {
|
|
1998
|
+
fileId
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
async clear() {
|
|
2003
|
+
await Promise.all(
|
|
2004
|
+
this.uploadedFiles.filter((f) => f.id !== null).map((f) => this.delete(f.id).catch(() => {
|
|
2005
|
+
}))
|
|
2006
|
+
);
|
|
2007
|
+
this.uploadedFiles = [];
|
|
2008
|
+
}
|
|
2009
|
+
getUploadedFiles() {
|
|
2010
|
+
return [...this.uploadedFiles];
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
|
|
2014
|
+
// src/session/usage-extractors.ts
|
|
2015
|
+
function mergeUsages(usages) {
|
|
2016
|
+
if (usages.length === 0) {
|
|
2017
|
+
return createZeroUsage();
|
|
2018
|
+
}
|
|
2019
|
+
let inputTokens = 0;
|
|
2020
|
+
let outputTokens = 0;
|
|
2021
|
+
let totalTokens = 0;
|
|
2022
|
+
let noCacheTokens = 0;
|
|
2023
|
+
let cacheReadTokens = 0;
|
|
2024
|
+
let cacheWriteTokens = 0;
|
|
2025
|
+
let textTokens = 0;
|
|
2026
|
+
let reasoningTokens = 0;
|
|
2027
|
+
for (const usage of usages) {
|
|
2028
|
+
inputTokens += usage.inputTokens ?? 0;
|
|
2029
|
+
outputTokens += usage.outputTokens ?? 0;
|
|
2030
|
+
totalTokens += usage.totalTokens ?? 0;
|
|
2031
|
+
noCacheTokens += usage.inputTokenDetails?.noCacheTokens ?? 0;
|
|
2032
|
+
cacheReadTokens += usage.inputTokenDetails?.cacheReadTokens ?? 0;
|
|
2033
|
+
cacheWriteTokens += usage.inputTokenDetails?.cacheWriteTokens ?? 0;
|
|
2034
|
+
textTokens += usage.outputTokenDetails?.textTokens ?? 0;
|
|
2035
|
+
reasoningTokens += usage.outputTokenDetails?.reasoningTokens ?? 0;
|
|
2036
|
+
}
|
|
2037
|
+
return {
|
|
2038
|
+
inputTokens,
|
|
2039
|
+
outputTokens,
|
|
2040
|
+
totalTokens,
|
|
2041
|
+
inputTokenDetails: {
|
|
2042
|
+
noCacheTokens,
|
|
2043
|
+
cacheReadTokens,
|
|
2044
|
+
cacheWriteTokens
|
|
2045
|
+
},
|
|
2046
|
+
outputTokenDetails: {
|
|
2047
|
+
textTokens,
|
|
2048
|
+
reasoningTokens
|
|
2049
|
+
}
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
function createZeroUsage() {
|
|
2053
|
+
return {
|
|
2054
|
+
inputTokens: 0,
|
|
2055
|
+
outputTokens: 0,
|
|
2056
|
+
totalTokens: 0,
|
|
2057
|
+
inputTokenDetails: {
|
|
2058
|
+
noCacheTokens: 0,
|
|
2059
|
+
cacheReadTokens: 0,
|
|
2060
|
+
cacheWriteTokens: 0
|
|
2061
|
+
},
|
|
2062
|
+
outputTokenDetails: {
|
|
2063
|
+
textTokens: 0,
|
|
2064
|
+
reasoningTokens: 0
|
|
2065
|
+
}
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
function detectProviderType(modelId) {
|
|
2069
|
+
const lowerModel = modelId.toLowerCase();
|
|
2070
|
+
if (lowerModel.startsWith("gpt-") || lowerModel === "o1" || lowerModel.startsWith("o1-") || lowerModel === "o3" || lowerModel.startsWith("o3-")) {
|
|
2071
|
+
return "openai";
|
|
2072
|
+
}
|
|
2073
|
+
if (lowerModel.startsWith("gemini")) {
|
|
2074
|
+
return "google";
|
|
2075
|
+
}
|
|
2076
|
+
if (lowerModel.startsWith("claude")) {
|
|
2077
|
+
return "anthropic";
|
|
2078
|
+
}
|
|
2079
|
+
return void 0;
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
// src/session/types.ts
|
|
2083
|
+
var SessionSummary = class _SessionSummary {
|
|
2084
|
+
totalLLMUsage;
|
|
2085
|
+
llmCallCount;
|
|
2086
|
+
llmCalls;
|
|
2087
|
+
toolCalls;
|
|
2088
|
+
customRecords;
|
|
2089
|
+
llmCost;
|
|
2090
|
+
additionalCosts;
|
|
2091
|
+
metadata;
|
|
2092
|
+
/** Cost breakdown by model. Key format: `${provider}/${model}` */
|
|
2093
|
+
costByModel;
|
|
2094
|
+
startTime;
|
|
2095
|
+
constructor(data, startTime) {
|
|
2096
|
+
this.startTime = startTime;
|
|
2097
|
+
this.totalLLMUsage = data.totalLLMUsage;
|
|
2098
|
+
this.llmCallCount = data.llmCalls.length;
|
|
2099
|
+
this.llmCalls = Object.freeze([...data.llmCalls]);
|
|
2100
|
+
this.toolCalls = Object.freeze([...data.toolCalls]);
|
|
2101
|
+
this.customRecords = Object.freeze([...data.customRecords]);
|
|
2102
|
+
this.llmCost = data.llmCost;
|
|
2103
|
+
this.additionalCosts = Object.freeze([...data.additionalCosts]);
|
|
2104
|
+
this.metadata = Object.freeze({ ...data.metadata });
|
|
2105
|
+
this.costByModel = Object.freeze({ ...data.costByModel });
|
|
2106
|
+
}
|
|
2107
|
+
/**
|
|
2108
|
+
* Total duration from session start to now (computed dynamically).
|
|
2109
|
+
*/
|
|
2110
|
+
get totalDuration() {
|
|
2111
|
+
return Date.now() - this.startTime;
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Creates an empty SessionSummary.
|
|
2115
|
+
*/
|
|
2116
|
+
static empty(startTime) {
|
|
2117
|
+
return new _SessionSummary(
|
|
2118
|
+
{
|
|
2119
|
+
totalLLMUsage: createZeroUsage(),
|
|
2120
|
+
llmCalls: [],
|
|
2121
|
+
toolCalls: [],
|
|
2122
|
+
customRecords: [],
|
|
2123
|
+
llmCost: 0,
|
|
2124
|
+
additionalCosts: [],
|
|
2125
|
+
metadata: {},
|
|
2126
|
+
costByModel: {}
|
|
2127
|
+
},
|
|
2128
|
+
startTime
|
|
2129
|
+
);
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Creates a SessionSummary with custom data for testing purposes.
|
|
2133
|
+
* @internal For testing only - do not use in production code.
|
|
2134
|
+
*/
|
|
2135
|
+
static forTest(data) {
|
|
2136
|
+
const startTime = data.startTime ?? Date.now() - 1e3;
|
|
2137
|
+
return new _SessionSummary(
|
|
2138
|
+
{
|
|
2139
|
+
totalLLMUsage: data.totalLLMUsage ?? createZeroUsage(),
|
|
2140
|
+
llmCalls: data.llmCalls ?? [],
|
|
2141
|
+
toolCalls: data.toolCalls ?? [],
|
|
2142
|
+
customRecords: data.customRecords ?? [],
|
|
2143
|
+
llmCost: data.llmCost ?? 0,
|
|
2144
|
+
additionalCosts: data.additionalCosts ?? [],
|
|
2145
|
+
metadata: data.metadata ?? {},
|
|
2146
|
+
costByModel: data.costByModel ?? {}
|
|
2147
|
+
},
|
|
2148
|
+
startTime
|
|
2149
|
+
);
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Total cost of all additional (non-LLM) operations.
|
|
2153
|
+
*/
|
|
2154
|
+
get totalAdditionalCost() {
|
|
2155
|
+
return this.additionalCosts.reduce((sum, c) => sum + c.cost, 0);
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Total cost including LLM and additional costs.
|
|
2159
|
+
*/
|
|
2160
|
+
get totalCost() {
|
|
2161
|
+
return this.llmCost + this.totalAdditionalCost;
|
|
2162
|
+
}
|
|
2163
|
+
/**
|
|
2164
|
+
* Returns a new SessionSummary with an LLM call added.
|
|
2165
|
+
*/
|
|
2166
|
+
withLLMCall(call, newLlmCost, newCostByModel, newTotalUsage) {
|
|
2167
|
+
return new _SessionSummary(
|
|
2168
|
+
{
|
|
2169
|
+
totalLLMUsage: newTotalUsage,
|
|
2170
|
+
llmCalls: [...this.llmCalls, call],
|
|
2171
|
+
toolCalls: [...this.toolCalls],
|
|
2172
|
+
customRecords: [...this.customRecords],
|
|
2173
|
+
llmCost: newLlmCost,
|
|
2174
|
+
additionalCosts: [...this.additionalCosts],
|
|
2175
|
+
metadata: { ...this.metadata },
|
|
2176
|
+
costByModel: newCostByModel
|
|
2177
|
+
},
|
|
2178
|
+
this.startTime
|
|
2179
|
+
);
|
|
2180
|
+
}
|
|
2181
|
+
/**
|
|
2182
|
+
* Returns a new SessionSummary with an additional cost recorded.
|
|
2183
|
+
*/
|
|
2184
|
+
withAdditionalCost(cost) {
|
|
2185
|
+
return new _SessionSummary(
|
|
2186
|
+
{
|
|
2187
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
2188
|
+
llmCalls: [...this.llmCalls],
|
|
2189
|
+
toolCalls: [...this.toolCalls],
|
|
2190
|
+
customRecords: [...this.customRecords],
|
|
2191
|
+
llmCost: this.llmCost,
|
|
2192
|
+
additionalCosts: [...this.additionalCosts, cost],
|
|
2193
|
+
metadata: { ...this.metadata },
|
|
2194
|
+
costByModel: { ...this.costByModel }
|
|
2195
|
+
},
|
|
2196
|
+
this.startTime
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Returns a new SessionSummary with metadata updated.
|
|
2201
|
+
*/
|
|
2202
|
+
withMetadata(key, value) {
|
|
2203
|
+
return new _SessionSummary(
|
|
2204
|
+
{
|
|
2205
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
2206
|
+
llmCalls: [...this.llmCalls],
|
|
2207
|
+
toolCalls: [...this.toolCalls],
|
|
2208
|
+
customRecords: [...this.customRecords],
|
|
2209
|
+
llmCost: this.llmCost,
|
|
2210
|
+
additionalCosts: [...this.additionalCosts],
|
|
2211
|
+
metadata: { ...this.metadata, [key]: value },
|
|
2212
|
+
costByModel: { ...this.costByModel }
|
|
2213
|
+
},
|
|
2214
|
+
this.startTime
|
|
2215
|
+
);
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Returns a new SessionSummary with a tool call added.
|
|
2219
|
+
*/
|
|
2220
|
+
withToolCall(call) {
|
|
2221
|
+
return new _SessionSummary(
|
|
2222
|
+
{
|
|
2223
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
2224
|
+
llmCalls: [...this.llmCalls],
|
|
2225
|
+
toolCalls: [...this.toolCalls, call],
|
|
2226
|
+
customRecords: [...this.customRecords],
|
|
2227
|
+
llmCost: this.llmCost,
|
|
2228
|
+
additionalCosts: [...this.additionalCosts],
|
|
2229
|
+
metadata: { ...this.metadata },
|
|
2230
|
+
costByModel: { ...this.costByModel }
|
|
2231
|
+
},
|
|
2232
|
+
this.startTime
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
/**
|
|
2236
|
+
* Returns a new SessionSummary with a custom record added.
|
|
2237
|
+
*/
|
|
2238
|
+
withCustomRecord(record) {
|
|
2239
|
+
return new _SessionSummary(
|
|
2240
|
+
{
|
|
2241
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
2242
|
+
llmCalls: [...this.llmCalls],
|
|
2243
|
+
toolCalls: [...this.toolCalls],
|
|
2244
|
+
customRecords: [...this.customRecords, record],
|
|
2245
|
+
llmCost: this.llmCost,
|
|
2246
|
+
additionalCosts: [...this.additionalCosts],
|
|
2247
|
+
metadata: { ...this.metadata },
|
|
2248
|
+
costByModel: { ...this.costByModel }
|
|
2249
|
+
},
|
|
2250
|
+
this.startTime
|
|
2251
|
+
);
|
|
2252
|
+
}
|
|
2253
|
+
/**
|
|
2254
|
+
* Serializes to plain JSON object for database storage.
|
|
2255
|
+
*/
|
|
2256
|
+
toJSON() {
|
|
2257
|
+
return {
|
|
2258
|
+
totalDuration: this.totalDuration,
|
|
2259
|
+
totalLLMUsage: this.totalLLMUsage,
|
|
2260
|
+
llmCallCount: this.llmCallCount,
|
|
2261
|
+
llmCalls: [...this.llmCalls],
|
|
2262
|
+
toolCalls: [...this.toolCalls],
|
|
2263
|
+
customRecords: [...this.customRecords],
|
|
2264
|
+
llmCost: this.llmCost,
|
|
2265
|
+
additionalCosts: [...this.additionalCosts],
|
|
2266
|
+
metadata: { ...this.metadata },
|
|
2267
|
+
costByModel: { ...this.costByModel },
|
|
2268
|
+
totalCost: this.totalCost,
|
|
2269
|
+
totalAdditionalCost: this.totalAdditionalCost
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
|
|
2274
|
+
// src/session/simple-session.ts
|
|
2275
|
+
var SimpleSession = class {
|
|
2276
|
+
defaultLanguageModel;
|
|
2277
|
+
modelFactory;
|
|
2278
|
+
providerType;
|
|
2279
|
+
providerPricing;
|
|
2280
|
+
defaultProviderOptions;
|
|
2281
|
+
defaultTools;
|
|
2282
|
+
_fileManager;
|
|
2283
|
+
logger;
|
|
2284
|
+
sessionStartTime;
|
|
2285
|
+
signal;
|
|
2286
|
+
summary;
|
|
2287
|
+
pendingUsagePromises = [];
|
|
2288
|
+
onDoneFns = [];
|
|
2289
|
+
constructor(options) {
|
|
2290
|
+
this.defaultLanguageModel = options.defaultLanguageModel ?? null;
|
|
2291
|
+
this.modelFactory = options.modelFactory ?? null;
|
|
2292
|
+
this.providerType = options.providerType;
|
|
2293
|
+
this.providerPricing = options.providerPricing;
|
|
2294
|
+
this.defaultProviderOptions = options.defaultProviderOptions;
|
|
2295
|
+
this.defaultTools = options.defaultTools;
|
|
2296
|
+
this._fileManager = options.fileManager;
|
|
2297
|
+
this.logger = options.logger ?? noopLogger;
|
|
2298
|
+
this.sessionStartTime = options.startTime ?? Date.now();
|
|
2299
|
+
this.signal = options.signal;
|
|
2300
|
+
this.summary = SessionSummary.empty(this.sessionStartTime);
|
|
2301
|
+
}
|
|
2302
|
+
getModel(requestedModelId) {
|
|
2303
|
+
if (requestedModelId) {
|
|
2304
|
+
if (!this.modelFactory) {
|
|
2305
|
+
throw new Error(
|
|
2306
|
+
`Model '${requestedModelId}' requested but no modelFactory provided. Either use the default model or configure the provider with modelFactory.`
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
return this.modelFactory(requestedModelId);
|
|
2310
|
+
}
|
|
2311
|
+
if (!this.defaultLanguageModel) {
|
|
2312
|
+
throw new Error(
|
|
2313
|
+
"No model specified and no default model set. Either specify a model in the call or configure the provider with withDefaultModel()."
|
|
2314
|
+
);
|
|
2315
|
+
}
|
|
2316
|
+
return this.defaultLanguageModel;
|
|
2317
|
+
}
|
|
2318
|
+
extractModelId(model) {
|
|
2319
|
+
const modelWithId = model;
|
|
2320
|
+
if (!modelWithId.modelId) {
|
|
2321
|
+
console.warn(
|
|
2322
|
+
'[SimpleSession] Model does not have modelId property, using "unknown". This may affect cost tracking accuracy.'
|
|
2323
|
+
);
|
|
2324
|
+
}
|
|
2325
|
+
return modelWithId.modelId ?? "unknown";
|
|
2326
|
+
}
|
|
2327
|
+
async generateText(params) {
|
|
2328
|
+
const callStartTime = Date.now();
|
|
2329
|
+
const { model: requestedModel, providerOptions, tools, ...restParams } = params;
|
|
2330
|
+
const languageModel = this.getModel(requestedModel);
|
|
2331
|
+
const modelId = this.extractModelId(languageModel);
|
|
2332
|
+
const mergedProviderOptions = this.defaultProviderOptions || providerOptions ? merge__default.default({}, this.defaultProviderOptions ?? {}, providerOptions ?? {}) : void 0;
|
|
2333
|
+
const mergedTools = this.defaultTools || tools ? { ...this.defaultTools, ...tools } : void 0;
|
|
2334
|
+
this.logger.onLLMCallStart?.({
|
|
2335
|
+
type: "llm_call_start",
|
|
2336
|
+
callType: "generateText",
|
|
2337
|
+
modelId,
|
|
2338
|
+
timestamp: callStartTime,
|
|
2339
|
+
request: { params: restParams }
|
|
2340
|
+
});
|
|
2341
|
+
try {
|
|
2342
|
+
const result = await ai.generateText({
|
|
2343
|
+
...restParams,
|
|
2344
|
+
tools: mergedTools,
|
|
2345
|
+
providerOptions: mergedProviderOptions,
|
|
2346
|
+
model: languageModel,
|
|
2347
|
+
abortSignal: this.signal
|
|
2348
|
+
});
|
|
2349
|
+
const callEndTime = Date.now();
|
|
2350
|
+
const call = {
|
|
2351
|
+
startTime: callStartTime,
|
|
2352
|
+
endTime: callEndTime,
|
|
2353
|
+
duration: callEndTime - callStartTime,
|
|
2354
|
+
usage: result.usage ?? createZeroUsage(),
|
|
2355
|
+
type: "generateText",
|
|
2356
|
+
model: modelId,
|
|
2357
|
+
provider: this.providerType
|
|
2358
|
+
};
|
|
2359
|
+
this.updateSummaryWithLLMCall(call);
|
|
2360
|
+
this.logger.onLLMCallEnd?.({
|
|
2361
|
+
type: "llm_call_end",
|
|
2362
|
+
callType: "generateText",
|
|
2363
|
+
modelId,
|
|
2364
|
+
timestamp: callEndTime,
|
|
2365
|
+
response: {
|
|
2366
|
+
duration: callEndTime - callStartTime,
|
|
2367
|
+
usage: result.usage,
|
|
2368
|
+
raw: result
|
|
2369
|
+
}
|
|
2370
|
+
});
|
|
2371
|
+
return result;
|
|
2372
|
+
} catch (error) {
|
|
2373
|
+
const callEndTime = Date.now();
|
|
2374
|
+
this.logger.onLLMCallEnd?.({
|
|
2375
|
+
type: "llm_call_end",
|
|
2376
|
+
callType: "generateText",
|
|
2377
|
+
modelId,
|
|
2378
|
+
timestamp: callEndTime,
|
|
2379
|
+
response: {
|
|
2380
|
+
duration: callEndTime - callStartTime,
|
|
2381
|
+
raw: null,
|
|
2382
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
2383
|
+
}
|
|
2384
|
+
});
|
|
2385
|
+
throw error;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
streamText(params) {
|
|
2389
|
+
const callStartTime = Date.now();
|
|
2390
|
+
const { model: requestedModel, providerOptions, tools, ...restParams } = params;
|
|
2391
|
+
const languageModel = this.getModel(requestedModel);
|
|
2392
|
+
const modelId = this.extractModelId(languageModel);
|
|
2393
|
+
const mergedProviderOptions = this.defaultProviderOptions || providerOptions ? merge__default.default({}, this.defaultProviderOptions ?? {}, providerOptions ?? {}) : void 0;
|
|
2394
|
+
const mergedTools = this.defaultTools || tools ? { ...this.defaultTools, ...tools } : void 0;
|
|
2395
|
+
this.logger.onLLMCallStart?.({
|
|
2396
|
+
type: "llm_call_start",
|
|
2397
|
+
callType: "streamText",
|
|
2398
|
+
modelId,
|
|
2399
|
+
timestamp: callStartTime,
|
|
2400
|
+
request: { params: restParams }
|
|
2401
|
+
});
|
|
2402
|
+
const result = ai.streamText({
|
|
2403
|
+
...restParams,
|
|
2404
|
+
tools: mergedTools,
|
|
2405
|
+
providerOptions: mergedProviderOptions,
|
|
2406
|
+
model: languageModel,
|
|
2407
|
+
abortSignal: this.signal
|
|
2408
|
+
});
|
|
2409
|
+
const usagePromise = Promise.resolve(result.usage).then((usage) => {
|
|
2410
|
+
const callEndTime = Date.now();
|
|
2411
|
+
const call = {
|
|
2412
|
+
startTime: callStartTime,
|
|
2413
|
+
endTime: callEndTime,
|
|
2414
|
+
duration: callEndTime - callStartTime,
|
|
2415
|
+
usage: usage ?? createZeroUsage(),
|
|
2416
|
+
type: "streamText",
|
|
2417
|
+
model: modelId,
|
|
2418
|
+
provider: this.providerType
|
|
2419
|
+
};
|
|
2420
|
+
this.updateSummaryWithLLMCall(call);
|
|
2421
|
+
this.logger.onLLMCallEnd?.({
|
|
2422
|
+
type: "llm_call_end",
|
|
2423
|
+
callType: "streamText",
|
|
2424
|
+
modelId,
|
|
2425
|
+
timestamp: callEndTime,
|
|
2426
|
+
response: {
|
|
2427
|
+
duration: callEndTime - callStartTime,
|
|
2428
|
+
usage,
|
|
2429
|
+
raw: result
|
|
2430
|
+
}
|
|
2431
|
+
});
|
|
2432
|
+
return usage;
|
|
2433
|
+
});
|
|
2434
|
+
this.pendingUsagePromises.push(usagePromise);
|
|
2435
|
+
return result;
|
|
2436
|
+
}
|
|
2437
|
+
get fileManager() {
|
|
2438
|
+
return this._fileManager;
|
|
2439
|
+
}
|
|
2440
|
+
record(data) {
|
|
2441
|
+
this.summary = this.summary.withCustomRecord(data);
|
|
2442
|
+
}
|
|
2443
|
+
recordToolCall(toolCallSummary) {
|
|
2444
|
+
this.summary = this.summary.withToolCall(toolCallSummary);
|
|
2445
|
+
}
|
|
2446
|
+
recordLLMCall(record) {
|
|
2447
|
+
const call = {
|
|
2448
|
+
...record,
|
|
2449
|
+
type: record.type ?? "manual"
|
|
2450
|
+
};
|
|
2451
|
+
this.updateSummaryWithLLMCall(call);
|
|
2452
|
+
}
|
|
2453
|
+
recordAdditionalCost(cost) {
|
|
2454
|
+
this.summary = this.summary.withAdditionalCost({
|
|
2455
|
+
...cost,
|
|
2456
|
+
timestamp: Date.now()
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
setMetadata(keyOrData, value) {
|
|
2460
|
+
if (typeof keyOrData === "string") {
|
|
2461
|
+
this.summary = this.summary.withMetadata(keyOrData, value);
|
|
2462
|
+
} else {
|
|
2463
|
+
for (const [k, v] of Object.entries(keyOrData)) {
|
|
2464
|
+
this.summary = this.summary.withMetadata(k, v);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
updateSummaryWithLLMCall(call) {
|
|
2469
|
+
const newCalls = [...this.summary.llmCalls, call];
|
|
2470
|
+
const { totalCost: llmCost, costByModel } = calculateTotalCost(
|
|
2471
|
+
newCalls.map((c) => ({ usage: c.usage, model: c.model, provider: c.provider })),
|
|
2472
|
+
this.providerPricing
|
|
2473
|
+
);
|
|
2474
|
+
const newTotalUsage = mergeUsages(newCalls.map((c) => c.usage));
|
|
2475
|
+
this.summary = this.summary.withLLMCall(call, llmCost, costByModel, newTotalUsage);
|
|
2476
|
+
}
|
|
2477
|
+
onDone(fn) {
|
|
2478
|
+
this.onDoneFns.push(fn);
|
|
2479
|
+
}
|
|
2480
|
+
async runOnDoneHooks() {
|
|
2481
|
+
const reversedHooks = [...this.onDoneFns].reverse();
|
|
2482
|
+
for (const fn of reversedHooks) {
|
|
2483
|
+
try {
|
|
2484
|
+
await fn();
|
|
2485
|
+
} catch (error) {
|
|
2486
|
+
console.error("[SimpleSession] onDone hook error:", error);
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
async getSummary() {
|
|
2491
|
+
await Promise.all(this.pendingUsagePromises);
|
|
2492
|
+
return this.summary;
|
|
2493
|
+
}
|
|
2494
|
+
/**
|
|
2495
|
+
* Notifies Logger of execution start.
|
|
2496
|
+
* @internal Called by SimpleExecutionHost - not intended for direct use.
|
|
2497
|
+
*/
|
|
2498
|
+
notifyExecutionStart() {
|
|
2499
|
+
this._logger.onExecutionStart?.({
|
|
2500
|
+
type: "execution_start",
|
|
2501
|
+
timestamp: Date.now()
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
/**
|
|
2505
|
+
* Notifies Logger of execution completion with result data and summary.
|
|
2506
|
+
* @param data - The execution result data
|
|
2507
|
+
* @param startTime - Execution start timestamp for duration calculation
|
|
2508
|
+
* @internal Called by SimpleExecutionHost - not intended for direct use.
|
|
2509
|
+
*/
|
|
2510
|
+
async notifyExecutionDone(data, startTime) {
|
|
2511
|
+
const summary = await this.getSummary();
|
|
2512
|
+
this._logger.onExecutionDone?.({
|
|
2513
|
+
type: "execution_done",
|
|
2514
|
+
timestamp: Date.now(),
|
|
2515
|
+
duration: Date.now() - startTime,
|
|
2516
|
+
data,
|
|
2517
|
+
summary
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
/**
|
|
2521
|
+
* Notifies Logger of execution error with error details and summary (if available).
|
|
2522
|
+
* Gracefully handles getSummary() failures - summary will be undefined if it fails.
|
|
2523
|
+
* @param error - The error that occurred
|
|
2524
|
+
* @param startTime - Execution start timestamp for duration calculation
|
|
2525
|
+
* @internal Called by SimpleExecutionHost - not intended for direct use.
|
|
2526
|
+
*/
|
|
2527
|
+
async notifyExecutionError(error, startTime) {
|
|
2528
|
+
let summary;
|
|
2529
|
+
try {
|
|
2530
|
+
summary = await this.getSummary();
|
|
2531
|
+
} catch {
|
|
2532
|
+
}
|
|
2533
|
+
this._logger.onExecutionError?.({
|
|
2534
|
+
type: "execution_error",
|
|
2535
|
+
timestamp: Date.now(),
|
|
2536
|
+
duration: Date.now() - startTime,
|
|
2537
|
+
error,
|
|
2538
|
+
summary
|
|
2539
|
+
});
|
|
2540
|
+
}
|
|
2541
|
+
get _logger() {
|
|
2542
|
+
return this.logger;
|
|
2543
|
+
}
|
|
2544
|
+
get _startTime() {
|
|
2545
|
+
return this.sessionStartTime;
|
|
2546
|
+
}
|
|
2547
|
+
get _modelId() {
|
|
2548
|
+
if (!this.defaultLanguageModel) {
|
|
2549
|
+
return "unknown";
|
|
2550
|
+
}
|
|
2551
|
+
return this.extractModelId(this.defaultLanguageModel);
|
|
2552
|
+
}
|
|
2553
|
+
};
|
|
2554
|
+
|
|
2555
|
+
// src/session/streaming-session.ts
|
|
2556
|
+
var StreamingSession = class extends SimpleSession {
|
|
2557
|
+
lastEventTime;
|
|
2558
|
+
_terminated = false;
|
|
2559
|
+
constructor(options) {
|
|
2560
|
+
super({
|
|
2561
|
+
defaultLanguageModel: options.defaultLanguageModel,
|
|
2562
|
+
modelFactory: options.modelFactory,
|
|
2563
|
+
providerType: options.providerType,
|
|
2564
|
+
providerPricing: options.providerPricing,
|
|
2565
|
+
fileManager: options.fileManager,
|
|
2566
|
+
logger: options.logger,
|
|
2567
|
+
startTime: options.startTime,
|
|
2568
|
+
signal: options.signal,
|
|
2569
|
+
defaultProviderOptions: options.defaultProviderOptions,
|
|
2570
|
+
defaultTools: options.defaultTools
|
|
2571
|
+
});
|
|
2572
|
+
this.lastEventTime = this._startTime;
|
|
2573
|
+
this._logger.onExecutionStart?.({
|
|
2574
|
+
type: "execution_start",
|
|
2575
|
+
timestamp: Date.now()
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
/**
|
|
2579
|
+
* Emits a streaming event with automatically attached metrics.
|
|
2580
|
+
*
|
|
2581
|
+
* Reserved types ('complete', 'error') throw at runtime - use session.done()
|
|
2582
|
+
* or session.fail() instead.
|
|
2583
|
+
*
|
|
2584
|
+
* @param event - The event to emit (metrics will be added automatically)
|
|
2585
|
+
* @returns The complete event with metrics attached
|
|
2586
|
+
* @throws Error when attempting to emit reserved types ('complete', 'error')
|
|
2587
|
+
*/
|
|
2588
|
+
emit(event) {
|
|
2589
|
+
if (this._terminated) {
|
|
2590
|
+
throw new Error("Session already terminated. Cannot call emit() after a terminal operation.");
|
|
2591
|
+
}
|
|
2592
|
+
const eventType = event.type;
|
|
2593
|
+
if (eventType === "complete" || eventType === "error") {
|
|
2594
|
+
throw new Error(
|
|
2595
|
+
`Cannot emit reserved type "${eventType}". Use session.done() for completion or session.fail() for errors.`
|
|
2596
|
+
);
|
|
2597
|
+
}
|
|
2598
|
+
return this.emitInternal(event);
|
|
2599
|
+
}
|
|
2600
|
+
/**
|
|
2601
|
+
* Internal emit method - bypasses reserved type check.
|
|
2602
|
+
* Used by done() and fail() to emit terminal events.
|
|
2603
|
+
*/
|
|
2604
|
+
emitInternal(event) {
|
|
2605
|
+
const metrics = this.createMetrics();
|
|
2606
|
+
const fullEvent = { ...event, metrics };
|
|
2607
|
+
this._logger.onExecutionEmit?.({
|
|
2608
|
+
type: "execution_emit",
|
|
2609
|
+
event: fullEvent
|
|
2610
|
+
});
|
|
2611
|
+
return fullEvent;
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Signals successful completion of the streaming execution.
|
|
2615
|
+
* Emits a 'complete' event with the result data and session summary.
|
|
2616
|
+
* Also triggers Logger.onExecutionDone for observability.
|
|
2617
|
+
* @param data - The final result data
|
|
2618
|
+
* @returns The complete event with data and summary
|
|
2619
|
+
*/
|
|
2620
|
+
async done(data) {
|
|
2621
|
+
if (this._terminated) {
|
|
2622
|
+
throw new Error("Session already terminated. Cannot call done() after a terminal operation.");
|
|
2623
|
+
}
|
|
2624
|
+
this._terminated = true;
|
|
2625
|
+
const summary = await this.getSummary();
|
|
2626
|
+
this._logger.onExecutionDone?.({
|
|
2627
|
+
type: "execution_done",
|
|
2628
|
+
timestamp: Date.now(),
|
|
2629
|
+
duration: summary.totalDuration,
|
|
2630
|
+
data,
|
|
2631
|
+
summary
|
|
2632
|
+
});
|
|
2633
|
+
return this.emitInternal({
|
|
2634
|
+
type: "complete",
|
|
2635
|
+
data,
|
|
2636
|
+
summary
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
/**
|
|
2640
|
+
* Signals that the streaming execution failed with an error.
|
|
2641
|
+
* Emits an 'error' event and triggers Logger.onExecutionError for observability.
|
|
2642
|
+
* Gracefully handles getSummary() failures - summary will be undefined if it fails.
|
|
2643
|
+
* @param error - The error that caused the failure
|
|
2644
|
+
* @param data - Optional partial result data (if any was produced before failure)
|
|
2645
|
+
* @returns The error event
|
|
2646
|
+
*/
|
|
2647
|
+
async fail(error, data) {
|
|
2648
|
+
if (this._terminated) {
|
|
2649
|
+
throw new Error("Session already terminated. Cannot call fail() after a terminal operation.");
|
|
2650
|
+
}
|
|
2651
|
+
this._terminated = true;
|
|
2652
|
+
let summary;
|
|
2653
|
+
try {
|
|
2654
|
+
summary = await this.getSummary();
|
|
2655
|
+
} catch {
|
|
2656
|
+
}
|
|
2657
|
+
this._logger.onExecutionError?.({
|
|
2658
|
+
type: "execution_error",
|
|
2659
|
+
timestamp: Date.now(),
|
|
2660
|
+
duration: summary?.totalDuration ?? Date.now() - this._startTime,
|
|
2661
|
+
error,
|
|
2662
|
+
data,
|
|
2663
|
+
summary
|
|
2664
|
+
});
|
|
2665
|
+
const errorEvent = {
|
|
2666
|
+
type: "error",
|
|
2667
|
+
error
|
|
2668
|
+
};
|
|
2669
|
+
if (summary) {
|
|
2670
|
+
errorEvent.summary = summary;
|
|
2671
|
+
}
|
|
2672
|
+
if (data !== void 0) {
|
|
2673
|
+
errorEvent.data = data;
|
|
2674
|
+
}
|
|
2675
|
+
return this.emitInternal(errorEvent);
|
|
2676
|
+
}
|
|
2677
|
+
createMetrics() {
|
|
2678
|
+
const now = Date.now();
|
|
2679
|
+
const metrics = {
|
|
2680
|
+
timestamp: now,
|
|
2681
|
+
elapsedMs: now - this._startTime,
|
|
2682
|
+
deltaMs: now - this.lastEventTime
|
|
2683
|
+
};
|
|
2684
|
+
this.lastEventTime = now;
|
|
2685
|
+
return metrics;
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2688
|
+
function createStreamingSession(options) {
|
|
2689
|
+
const session = new StreamingSession({
|
|
2690
|
+
defaultLanguageModel: options.defaultLanguageModel,
|
|
2691
|
+
providerType: options.providerType,
|
|
2692
|
+
fileManager: options.fileManager,
|
|
2693
|
+
logger: options.logger ?? noopLogger,
|
|
2694
|
+
startTime: options.startTime,
|
|
2695
|
+
signal: options.signal
|
|
2696
|
+
});
|
|
2697
|
+
return session;
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
// src/provider/google/factory.ts
|
|
2701
|
+
var GoogleProvider = class _GoogleProvider extends BaseProvider {
|
|
2702
|
+
constructor(apiKey, defaultModelId, logger, safetySettings, pricingConfig, defaultOptions, searchEnabled = false, urlContextEnabled = false, fileCache) {
|
|
2703
|
+
super();
|
|
2704
|
+
this.apiKey = apiKey;
|
|
2705
|
+
this.defaultModelId = defaultModelId;
|
|
2706
|
+
this.logger = logger;
|
|
2707
|
+
this.safetySettings = safetySettings;
|
|
2708
|
+
this.pricingConfig = pricingConfig;
|
|
2709
|
+
this.defaultOptions = defaultOptions;
|
|
2710
|
+
this.searchEnabled = searchEnabled;
|
|
2711
|
+
this.urlContextEnabled = urlContextEnabled;
|
|
2712
|
+
this.fileCache = fileCache;
|
|
2713
|
+
this.google = google.createGoogleGenerativeAI({ apiKey });
|
|
2714
|
+
}
|
|
2715
|
+
google;
|
|
2716
|
+
withDefaultModel(modelId) {
|
|
2717
|
+
return new _GoogleProvider(
|
|
2718
|
+
this.apiKey,
|
|
2719
|
+
modelId,
|
|
2720
|
+
this.logger,
|
|
2721
|
+
this.safetySettings,
|
|
2722
|
+
this.pricingConfig,
|
|
2723
|
+
this.defaultOptions,
|
|
2724
|
+
this.searchEnabled,
|
|
2725
|
+
this.urlContextEnabled,
|
|
2726
|
+
this.fileCache
|
|
2727
|
+
);
|
|
2728
|
+
}
|
|
2729
|
+
withLogger(newLogger) {
|
|
2730
|
+
return new _GoogleProvider(
|
|
2731
|
+
this.apiKey,
|
|
2732
|
+
this.defaultModelId,
|
|
2733
|
+
newLogger,
|
|
2734
|
+
this.safetySettings,
|
|
2735
|
+
this.pricingConfig,
|
|
2736
|
+
this.defaultOptions,
|
|
2737
|
+
this.searchEnabled,
|
|
2738
|
+
this.urlContextEnabled,
|
|
2739
|
+
this.fileCache
|
|
2740
|
+
);
|
|
2741
|
+
}
|
|
2742
|
+
withPricing(pricing) {
|
|
2743
|
+
validateProviderPricing(pricing, "google");
|
|
2744
|
+
return new _GoogleProvider(
|
|
2745
|
+
this.apiKey,
|
|
2746
|
+
this.defaultModelId,
|
|
2747
|
+
this.logger,
|
|
2748
|
+
this.safetySettings,
|
|
2749
|
+
pricing,
|
|
2750
|
+
this.defaultOptions,
|
|
2751
|
+
this.searchEnabled,
|
|
2752
|
+
this.urlContextEnabled,
|
|
2753
|
+
this.fileCache
|
|
2754
|
+
);
|
|
2755
|
+
}
|
|
2756
|
+
/**
|
|
2757
|
+
* Set default provider-specific options for all LLM calls.
|
|
2758
|
+
* These options will be deep-merged with per-call providerOptions.
|
|
2759
|
+
*
|
|
2760
|
+
* @example
|
|
2761
|
+
* ```typescript
|
|
2762
|
+
* createGoogleProvider({ apiKey: 'xxx' })
|
|
2763
|
+
* .withDefaultModel('gemini-2.0-flash-thinking-exp')
|
|
2764
|
+
* .withDefaultOptions({
|
|
2765
|
+
* thinkingConfig: { includeThoughts: true, thinkingLevel: 'low' }
|
|
2766
|
+
* })
|
|
2767
|
+
* ```
|
|
2768
|
+
*/
|
|
2769
|
+
withDefaultOptions(options) {
|
|
2770
|
+
return new _GoogleProvider(
|
|
2771
|
+
this.apiKey,
|
|
2772
|
+
this.defaultModelId,
|
|
2773
|
+
this.logger,
|
|
2774
|
+
this.safetySettings,
|
|
2775
|
+
this.pricingConfig,
|
|
2776
|
+
options,
|
|
2777
|
+
this.searchEnabled,
|
|
2778
|
+
this.urlContextEnabled,
|
|
2779
|
+
this.fileCache
|
|
2780
|
+
);
|
|
2781
|
+
}
|
|
2782
|
+
/**
|
|
2783
|
+
* Enable Google Search grounding for all LLM calls.
|
|
2784
|
+
* Allows the model to access real-time web information.
|
|
2785
|
+
*
|
|
2786
|
+
* @example
|
|
2787
|
+
* ```typescript
|
|
2788
|
+
* createGoogleProvider({ apiKey: 'xxx' })
|
|
2789
|
+
* .withDefaultModel('gemini-2.5-flash')
|
|
2790
|
+
* .withSearchEnabled()
|
|
2791
|
+
* ```
|
|
2792
|
+
*/
|
|
2793
|
+
withSearchEnabled() {
|
|
2794
|
+
return new _GoogleProvider(
|
|
2795
|
+
this.apiKey,
|
|
2796
|
+
this.defaultModelId,
|
|
2797
|
+
this.logger,
|
|
2798
|
+
this.safetySettings,
|
|
2799
|
+
this.pricingConfig,
|
|
2800
|
+
this.defaultOptions,
|
|
2801
|
+
true,
|
|
2802
|
+
this.urlContextEnabled,
|
|
2803
|
+
this.fileCache
|
|
2804
|
+
);
|
|
2805
|
+
}
|
|
2806
|
+
/**
|
|
2807
|
+
* Enable URL Context grounding for all LLM calls.
|
|
2808
|
+
* Allows the model to retrieve and use content from URLs in the prompt.
|
|
2809
|
+
*
|
|
2810
|
+
* @example
|
|
2811
|
+
* ```typescript
|
|
2812
|
+
* createGoogleProvider({ apiKey: 'xxx' })
|
|
2813
|
+
* .withDefaultModel('gemini-2.5-flash')
|
|
2814
|
+
* .withUrlContextEnabled()
|
|
2815
|
+
* ```
|
|
2816
|
+
*/
|
|
2817
|
+
withUrlContextEnabled() {
|
|
2818
|
+
return new _GoogleProvider(
|
|
2819
|
+
this.apiKey,
|
|
2820
|
+
this.defaultModelId,
|
|
2821
|
+
this.logger,
|
|
2822
|
+
this.safetySettings,
|
|
2823
|
+
this.pricingConfig,
|
|
2824
|
+
this.defaultOptions,
|
|
2825
|
+
this.searchEnabled,
|
|
2826
|
+
true,
|
|
2827
|
+
this.fileCache
|
|
2828
|
+
);
|
|
2829
|
+
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Set a file cache for reusing uploaded files across sessions.
|
|
2832
|
+
* If no cache is provided, creates a new InMemoryFileCache.
|
|
2833
|
+
*
|
|
2834
|
+
* @example
|
|
2835
|
+
* ```typescript
|
|
2836
|
+
* // Use default InMemoryFileCache
|
|
2837
|
+
* createGoogleProvider({ apiKey: 'xxx' })
|
|
2838
|
+
* .withFileCache()
|
|
2839
|
+
*
|
|
2840
|
+
* // Use custom cache with TTL
|
|
2841
|
+
* const cache = new InMemoryFileCache({ defaultTTL: 3600000 });
|
|
2842
|
+
* createGoogleProvider({ apiKey: 'xxx' })
|
|
2843
|
+
* .withFileCache(cache)
|
|
2844
|
+
* ```
|
|
2845
|
+
*/
|
|
2846
|
+
withFileCache(cache) {
|
|
2847
|
+
return new _GoogleProvider(
|
|
2848
|
+
this.apiKey,
|
|
2849
|
+
this.defaultModelId,
|
|
2850
|
+
this.logger,
|
|
2851
|
+
this.safetySettings,
|
|
2852
|
+
this.pricingConfig,
|
|
2853
|
+
this.defaultOptions,
|
|
2854
|
+
this.searchEnabled,
|
|
2855
|
+
this.urlContextEnabled,
|
|
2856
|
+
cache ?? new InMemoryFileCache()
|
|
2857
|
+
);
|
|
2858
|
+
}
|
|
2859
|
+
getSessionConfig() {
|
|
2860
|
+
const defaultTools = {
|
|
2861
|
+
...this.searchEnabled && { google_search: this.google.tools.googleSearch({}) },
|
|
2862
|
+
...this.urlContextEnabled && { url_context: this.google.tools.urlContext({}) }
|
|
2863
|
+
};
|
|
2864
|
+
const hasDefaultTools = this.searchEnabled || this.urlContextEnabled;
|
|
2865
|
+
return {
|
|
2866
|
+
defaultLanguageModel: this.defaultModelId ? this.createModel(this.defaultModelId) : null,
|
|
2867
|
+
modelFactory: (modelId) => this.createModel(modelId),
|
|
2868
|
+
providerType: "google",
|
|
2869
|
+
providerPricing: this.pricingConfig,
|
|
2870
|
+
fileManager: new GoogleFileManager(this.apiKey, { cache: this.fileCache }),
|
|
2871
|
+
logger: this.logger,
|
|
2872
|
+
defaultProviderOptions: this.defaultOptions ? { google: this.defaultOptions } : void 0,
|
|
2873
|
+
defaultTools: hasDefaultTools ? defaultTools : void 0
|
|
2874
|
+
};
|
|
2875
|
+
}
|
|
2876
|
+
createStreamingSession(signal) {
|
|
2877
|
+
return new StreamingSession({ ...this.getSessionConfig(), signal });
|
|
2878
|
+
}
|
|
2879
|
+
createSimpleSession(signal) {
|
|
2880
|
+
return new SimpleSession({ ...this.getSessionConfig(), signal });
|
|
2881
|
+
}
|
|
2882
|
+
/**
|
|
2883
|
+
* Type assertion needed because @ai-sdk/google's type signature doesn't
|
|
2884
|
+
* include the second parameter, but it's supported at runtime.
|
|
2885
|
+
*/
|
|
2886
|
+
createModel(modelId) {
|
|
2887
|
+
if (this.safetySettings) {
|
|
2888
|
+
return this.google(modelId, { safetySettings: this.safetySettings });
|
|
2889
|
+
}
|
|
2890
|
+
return this.google(modelId);
|
|
2891
|
+
}
|
|
2892
|
+
};
|
|
2893
|
+
function createGoogleProvider(config) {
|
|
2894
|
+
return new GoogleProvider(
|
|
2895
|
+
config.apiKey,
|
|
2896
|
+
null,
|
|
2897
|
+
// No default model - must be set with withDefaultModel()
|
|
2898
|
+
noopLogger,
|
|
2899
|
+
config.safetySettings
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
|
|
2903
|
+
constructor(apiKey, defaultModelId, logger, baseURL, organization, pricingConfig, defaultOptions, fileCache) {
|
|
2904
|
+
super();
|
|
2905
|
+
this.apiKey = apiKey;
|
|
2906
|
+
this.defaultModelId = defaultModelId;
|
|
2907
|
+
this.logger = logger;
|
|
2908
|
+
this.baseURL = baseURL;
|
|
2909
|
+
this.organization = organization;
|
|
2910
|
+
this.pricingConfig = pricingConfig;
|
|
2911
|
+
this.defaultOptions = defaultOptions;
|
|
2912
|
+
this.fileCache = fileCache;
|
|
2913
|
+
this.openai = openai.createOpenAI({
|
|
2914
|
+
apiKey,
|
|
2915
|
+
baseURL,
|
|
2916
|
+
organization
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
openai;
|
|
2920
|
+
withDefaultModel(modelId) {
|
|
2921
|
+
return new _OpenAIProvider(
|
|
2922
|
+
this.apiKey,
|
|
2923
|
+
modelId,
|
|
2924
|
+
this.logger,
|
|
2925
|
+
this.baseURL,
|
|
2926
|
+
this.organization,
|
|
2927
|
+
this.pricingConfig,
|
|
2928
|
+
this.defaultOptions,
|
|
2929
|
+
this.fileCache
|
|
2930
|
+
);
|
|
2931
|
+
}
|
|
2932
|
+
withLogger(newLogger) {
|
|
2933
|
+
return new _OpenAIProvider(
|
|
2934
|
+
this.apiKey,
|
|
2935
|
+
this.defaultModelId,
|
|
2936
|
+
newLogger,
|
|
2937
|
+
this.baseURL,
|
|
2938
|
+
this.organization,
|
|
2939
|
+
this.pricingConfig,
|
|
2940
|
+
this.defaultOptions,
|
|
2941
|
+
this.fileCache
|
|
2942
|
+
);
|
|
2943
|
+
}
|
|
2944
|
+
withPricing(pricing) {
|
|
2945
|
+
validateProviderPricing(pricing, "openai");
|
|
2946
|
+
return new _OpenAIProvider(
|
|
2947
|
+
this.apiKey,
|
|
2948
|
+
this.defaultModelId,
|
|
2949
|
+
this.logger,
|
|
2950
|
+
this.baseURL,
|
|
2951
|
+
this.organization,
|
|
2952
|
+
pricing,
|
|
2953
|
+
this.defaultOptions,
|
|
2954
|
+
this.fileCache
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2957
|
+
/**
|
|
2958
|
+
* Set default provider-specific options for all LLM calls.
|
|
2959
|
+
* These options will be deep-merged with per-call providerOptions.
|
|
2960
|
+
*
|
|
2961
|
+
* @example
|
|
2962
|
+
* ```typescript
|
|
2963
|
+
* createOpenAIProvider({ apiKey: 'xxx' })
|
|
2964
|
+
* .withDefaultModel('gpt-4o')
|
|
2965
|
+
* .withDefaultOptions({
|
|
2966
|
+
* reasoningEffort: 'high',
|
|
2967
|
+
* parallelToolCalls: true,
|
|
2968
|
+
* })
|
|
2969
|
+
* ```
|
|
2970
|
+
*/
|
|
2971
|
+
withDefaultOptions(options) {
|
|
2972
|
+
return new _OpenAIProvider(
|
|
2973
|
+
this.apiKey,
|
|
2974
|
+
this.defaultModelId,
|
|
2975
|
+
this.logger,
|
|
2976
|
+
this.baseURL,
|
|
2977
|
+
this.organization,
|
|
2978
|
+
this.pricingConfig,
|
|
2979
|
+
options,
|
|
2980
|
+
this.fileCache
|
|
2981
|
+
);
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Set a file cache for API consistency with GoogleProvider.
|
|
2985
|
+
* Note: OpenAI does not support file caching, so this is a no-op.
|
|
2986
|
+
*
|
|
2987
|
+
* @example
|
|
2988
|
+
* ```typescript
|
|
2989
|
+
* createOpenAIProvider({ apiKey: 'xxx' })
|
|
2990
|
+
* .withFileCache()
|
|
2991
|
+
* ```
|
|
2992
|
+
*/
|
|
2993
|
+
withFileCache(cache) {
|
|
2994
|
+
return new _OpenAIProvider(
|
|
2995
|
+
this.apiKey,
|
|
2996
|
+
this.defaultModelId,
|
|
2997
|
+
this.logger,
|
|
2998
|
+
this.baseURL,
|
|
2999
|
+
this.organization,
|
|
3000
|
+
this.pricingConfig,
|
|
3001
|
+
this.defaultOptions,
|
|
3002
|
+
cache
|
|
3003
|
+
);
|
|
3004
|
+
}
|
|
3005
|
+
getSessionConfig() {
|
|
3006
|
+
return {
|
|
3007
|
+
defaultLanguageModel: this.defaultModelId ? this.openai(this.defaultModelId) : null,
|
|
3008
|
+
modelFactory: (modelId) => this.openai(modelId),
|
|
3009
|
+
providerType: "openai",
|
|
3010
|
+
providerPricing: this.pricingConfig,
|
|
3011
|
+
fileManager: new NoOpFileManager(),
|
|
3012
|
+
logger: this.logger,
|
|
3013
|
+
defaultProviderOptions: this.defaultOptions ? { openai: this.defaultOptions } : void 0
|
|
3014
|
+
};
|
|
3015
|
+
}
|
|
3016
|
+
createStreamingSession(signal) {
|
|
3017
|
+
return new StreamingSession({ ...this.getSessionConfig(), signal });
|
|
3018
|
+
}
|
|
3019
|
+
createSimpleSession(signal) {
|
|
3020
|
+
return new SimpleSession({ ...this.getSessionConfig(), signal });
|
|
3021
|
+
}
|
|
3022
|
+
};
|
|
3023
|
+
function createOpenAIProvider(config) {
|
|
3024
|
+
return new OpenAIProvider(
|
|
3025
|
+
config.apiKey,
|
|
3026
|
+
null,
|
|
3027
|
+
// No default model - must be set with withDefaultModel()
|
|
3028
|
+
noopLogger,
|
|
3029
|
+
config.baseURL,
|
|
3030
|
+
config.organization
|
|
3031
|
+
);
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
// src/validation/validation-history.ts
|
|
3035
|
+
var ValidationHistory = class {
|
|
3036
|
+
attempts = [];
|
|
3037
|
+
get nextAttempt() {
|
|
3038
|
+
return this.attempts.length + 1;
|
|
3039
|
+
}
|
|
3040
|
+
get last() {
|
|
3041
|
+
return this.attempts.at(-1);
|
|
3042
|
+
}
|
|
3043
|
+
get all() {
|
|
3044
|
+
return this.attempts;
|
|
3045
|
+
}
|
|
3046
|
+
get failureReasons() {
|
|
3047
|
+
return this.attempts.filter((a) => !a.valid && a.reason).map((a) => a.reason);
|
|
3048
|
+
}
|
|
3049
|
+
get isRetry() {
|
|
3050
|
+
return this.attempts.length > 0;
|
|
3051
|
+
}
|
|
3052
|
+
/** Internal use only - not exposed via ReadonlyValidationHistory. */
|
|
3053
|
+
add(result, validation) {
|
|
3054
|
+
this.attempts.push({
|
|
3055
|
+
result,
|
|
3056
|
+
...validation,
|
|
3057
|
+
attempt: this.attempts.length + 1
|
|
3058
|
+
});
|
|
3059
|
+
}
|
|
3060
|
+
};
|
|
3061
|
+
|
|
3062
|
+
// src/validation/errors.ts
|
|
3063
|
+
var ValidationErrorCode = /* @__PURE__ */ ((ValidationErrorCode2) => {
|
|
3064
|
+
ValidationErrorCode2["VALIDATION_EXHAUSTED"] = "VALIDATION_EXHAUSTED";
|
|
3065
|
+
return ValidationErrorCode2;
|
|
3066
|
+
})(ValidationErrorCode || {});
|
|
3067
|
+
var ValidationExhaustedError = class extends AgtlantisError {
|
|
3068
|
+
constructor(message, history) {
|
|
3069
|
+
super(message, {
|
|
3070
|
+
code: "VALIDATION_EXHAUSTED" /* VALIDATION_EXHAUSTED */,
|
|
3071
|
+
context: {
|
|
3072
|
+
attempts: history.all.length,
|
|
3073
|
+
failureReasons: history.failureReasons
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
3076
|
+
this.history = history;
|
|
3077
|
+
this.name = "ValidationExhaustedError";
|
|
3078
|
+
}
|
|
3079
|
+
};
|
|
3080
|
+
|
|
3081
|
+
// src/validation/with-validation.ts
|
|
3082
|
+
async function withValidation(execute, options) {
|
|
3083
|
+
const { validate, maxAttempts: rawMax, signal, retryDelay, onAttempt } = options;
|
|
3084
|
+
const maxAttempts = Math.max(1, rawMax ?? 3);
|
|
3085
|
+
const history = new ValidationHistory();
|
|
3086
|
+
while (history.nextAttempt <= maxAttempts) {
|
|
3087
|
+
signal?.throwIfAborted();
|
|
3088
|
+
const result = await execute(history);
|
|
3089
|
+
const validation = await validate(result, history);
|
|
3090
|
+
history.add(result, validation);
|
|
3091
|
+
onAttempt?.(history.last);
|
|
3092
|
+
if (validation.valid) {
|
|
3093
|
+
return result;
|
|
3094
|
+
}
|
|
3095
|
+
const hasMoreAttempts = history.nextAttempt <= maxAttempts;
|
|
3096
|
+
if (retryDelay && hasMoreAttempts) {
|
|
3097
|
+
await new Promise((resolve2) => setTimeout(resolve2, retryDelay));
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
throw new ValidationExhaustedError(
|
|
3101
|
+
`Validation failed after ${history.all.length} attempts`,
|
|
3102
|
+
history
|
|
3103
|
+
);
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
exports.ANTHROPIC_PRICING = ANTHROPIC_PRICING;
|
|
3107
|
+
exports.AgtlantisError = AgtlantisError;
|
|
3108
|
+
exports.BaseProvider = BaseProvider;
|
|
3109
|
+
exports.ConfigurationError = ConfigurationError;
|
|
3110
|
+
exports.ConfigurationErrorCode = ConfigurationErrorCode;
|
|
3111
|
+
exports.DEFAULT_FALLBACK_PRICING = DEFAULT_FALLBACK_PRICING;
|
|
3112
|
+
exports.DEFAULT_MAX_SIZE = DEFAULT_MAX_SIZE;
|
|
3113
|
+
exports.DEFAULT_PRICING_CONFIG = DEFAULT_PRICING_CONFIG;
|
|
3114
|
+
exports.ERRORS = ERRORS;
|
|
3115
|
+
exports.EXTENSION_TO_MIME = EXTENSION_TO_MIME;
|
|
3116
|
+
exports.ExecutionError = ExecutionError;
|
|
3117
|
+
exports.ExecutionErrorCode = ExecutionErrorCode;
|
|
3118
|
+
exports.FileError = FileError;
|
|
3119
|
+
exports.FileErrorCode = FileErrorCode;
|
|
3120
|
+
exports.FilePromptRepository = FilePromptRepository;
|
|
3121
|
+
exports.GOOGLE_PRICING = GOOGLE_PRICING;
|
|
3122
|
+
exports.GoogleFileManager = GoogleFileManager;
|
|
3123
|
+
exports.GoogleProvider = GoogleProvider;
|
|
3124
|
+
exports.InMemoryFileCache = InMemoryFileCache;
|
|
3125
|
+
exports.NoOpFileManager = NoOpFileManager;
|
|
3126
|
+
exports.OPENAI_PRICING = OPENAI_PRICING;
|
|
3127
|
+
exports.ProgressivePattern = ProgressivePattern;
|
|
3128
|
+
exports.PromptError = PromptError;
|
|
3129
|
+
exports.PromptErrorCode = PromptErrorCode;
|
|
3130
|
+
exports.PromptIOError = PromptIOError;
|
|
3131
|
+
exports.PromptInvalidFormatError = PromptInvalidFormatError;
|
|
3132
|
+
exports.PromptNotFoundError = PromptNotFoundError;
|
|
3133
|
+
exports.PromptTemplate = PromptTemplate;
|
|
3134
|
+
exports.PromptTemplateError = PromptTemplateError;
|
|
3135
|
+
exports.SessionSummary = SessionSummary;
|
|
3136
|
+
exports.SimpleExecutionHost = SimpleExecutionHost;
|
|
3137
|
+
exports.SimpleSession = SimpleSession;
|
|
3138
|
+
exports.StreamingExecutionHost = StreamingExecutionHost;
|
|
3139
|
+
exports.StreamingSession = StreamingSession;
|
|
3140
|
+
exports.TOOL_CALLING_PROTOCOL = TOOL_CALLING_PROTOCOL;
|
|
3141
|
+
exports.ValidationErrorCode = ValidationErrorCode;
|
|
3142
|
+
exports.ValidationExhaustedError = ValidationExhaustedError;
|
|
3143
|
+
exports.ValidationHistory = ValidationHistory;
|
|
3144
|
+
exports.calculateCost = calculateCost;
|
|
3145
|
+
exports.calculateCostFromUsage = calculateCostFromUsage;
|
|
3146
|
+
exports.calculateTotalCost = calculateTotalCost;
|
|
3147
|
+
exports.combineSignals = combineSignals;
|
|
3148
|
+
exports.compileTemplate = compileTemplate;
|
|
3149
|
+
exports.computeFileSourceHash = computeFileSourceHash;
|
|
3150
|
+
exports.configurePricing = configurePricing;
|
|
3151
|
+
exports.createFilePromptRepository = createFilePromptRepository;
|
|
3152
|
+
exports.createGoogleProvider = createGoogleProvider;
|
|
3153
|
+
exports.createHookRunner = createHookRunner;
|
|
3154
|
+
exports.createLogger = createLogger;
|
|
3155
|
+
exports.createOpenAIProvider = createOpenAIProvider;
|
|
3156
|
+
exports.createStreamingSession = createStreamingSession;
|
|
3157
|
+
exports.createZeroUsage = createZeroUsage;
|
|
3158
|
+
exports.defineProgressivePattern = defineProgressivePattern;
|
|
3159
|
+
exports.detectProviderType = detectProviderType;
|
|
3160
|
+
exports.determineResultStatus = determineResultStatus;
|
|
3161
|
+
exports.getDuration = getDuration;
|
|
3162
|
+
exports.getEffectivePricing = getEffectivePricing;
|
|
3163
|
+
exports.getFileSourceDisplayInfo = getFileSourceDisplayInfo;
|
|
3164
|
+
exports.getFileSourcesDisplayInfo = getFileSourcesDisplayInfo;
|
|
3165
|
+
exports.getModelPricing = getModelPricing;
|
|
3166
|
+
exports.getPricingConfig = getPricingConfig;
|
|
3167
|
+
exports.inferMediaType = inferMediaType;
|
|
3168
|
+
exports.isAbortError = isAbortError;
|
|
3169
|
+
exports.isFileSource = isFileSource;
|
|
3170
|
+
exports.isFileSourceBase64 = isFileSourceBase64;
|
|
3171
|
+
exports.isFileSourceData = isFileSourceData;
|
|
3172
|
+
exports.isFileSourcePath = isFileSourcePath;
|
|
3173
|
+
exports.isFileSourceUrl = isFileSourceUrl;
|
|
3174
|
+
exports.mapExecution = mapExecution;
|
|
3175
|
+
exports.mapExecutionResult = mapExecutionResult;
|
|
3176
|
+
exports.mergeUsages = mergeUsages;
|
|
3177
|
+
exports.noopLogger = noopLogger;
|
|
3178
|
+
exports.normalizeError = normalizeError;
|
|
3179
|
+
exports.resetPricingConfig = resetPricingConfig;
|
|
3180
|
+
exports.resolveFileSource = resolveFileSource;
|
|
3181
|
+
exports.resolveFileSourcesInInput = resolveFileSourcesInInput;
|
|
3182
|
+
exports.scanForFileSources = scanForFileSources;
|
|
3183
|
+
exports.validateModelPricing = validateModelPricing;
|
|
3184
|
+
exports.validatePricingConfig = validatePricingConfig;
|
|
3185
|
+
exports.validateProviderPricing = validateProviderPricing;
|
|
3186
|
+
exports.withValidation = withValidation;
|
|
3187
|
+
//# sourceMappingURL=index.cjs.map
|
|
3188
|
+
//# sourceMappingURL=index.cjs.map
|