llmist 1.7.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-JGORHSHC.js → chunk-LSCCBXS7.js} +10 -3
- package/dist/chunk-LSCCBXS7.js.map +1 -0
- package/dist/{chunk-E52IO2NO.js → chunk-PDYVT3FI.js} +421 -53
- package/dist/chunk-PDYVT3FI.js.map +1 -0
- package/dist/cli.cjs +591 -154
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +188 -110
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +415 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -13
- package/dist/index.d.ts +72 -13
- package/dist/index.js +4 -2
- package/dist/{mock-stream-BMuFlQI1.d.cts → mock-stream-HF7MBNhi.d.cts} +650 -319
- package/dist/{mock-stream-BMuFlQI1.d.ts → mock-stream-HF7MBNhi.d.ts} +650 -319
- package/dist/testing/index.cjs +416 -49
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +4 -2
- package/dist/testing/index.d.ts +4 -2
- package/dist/testing/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-E52IO2NO.js.map +0 -1
- package/dist/chunk-JGORHSHC.js.map +0 -1
package/dist/testing/index.cjs
CHANGED
|
@@ -722,6 +722,44 @@ ${this.endPrefix}`
|
|
|
722
722
|
}
|
|
723
723
|
});
|
|
724
724
|
|
|
725
|
+
// src/gadgets/exceptions.ts
|
|
726
|
+
var BreakLoopException, HumanInputException, TimeoutException, AbortError;
|
|
727
|
+
var init_exceptions = __esm({
|
|
728
|
+
"src/gadgets/exceptions.ts"() {
|
|
729
|
+
"use strict";
|
|
730
|
+
BreakLoopException = class extends Error {
|
|
731
|
+
constructor(message) {
|
|
732
|
+
super(message ?? "Agent loop terminated by gadget");
|
|
733
|
+
this.name = "BreakLoopException";
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
HumanInputException = class extends Error {
|
|
737
|
+
question;
|
|
738
|
+
constructor(question) {
|
|
739
|
+
super(`Human input required: ${question}`);
|
|
740
|
+
this.name = "HumanInputException";
|
|
741
|
+
this.question = question;
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
TimeoutException = class extends Error {
|
|
745
|
+
timeoutMs;
|
|
746
|
+
gadgetName;
|
|
747
|
+
constructor(gadgetName, timeoutMs) {
|
|
748
|
+
super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
|
|
749
|
+
this.name = "TimeoutException";
|
|
750
|
+
this.gadgetName = gadgetName;
|
|
751
|
+
this.timeoutMs = timeoutMs;
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
AbortError = class extends Error {
|
|
755
|
+
constructor(message) {
|
|
756
|
+
super(message || "Gadget execution was aborted");
|
|
757
|
+
this.name = "AbortError";
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
|
|
725
763
|
// src/gadgets/schema-to-json.ts
|
|
726
764
|
function schemaToJSONSchema(schema, options) {
|
|
727
765
|
const jsonSchema = z2.toJSONSchema(schema, options ?? { target: "draft-7" });
|
|
@@ -915,6 +953,7 @@ var init_gadget = __esm({
|
|
|
915
953
|
"src/gadgets/gadget.ts"() {
|
|
916
954
|
"use strict";
|
|
917
955
|
init_constants();
|
|
956
|
+
init_exceptions();
|
|
918
957
|
init_schema_to_json();
|
|
919
958
|
init_schema_validator();
|
|
920
959
|
BaseGadget = class {
|
|
@@ -944,6 +983,136 @@ var init_gadget = __esm({
|
|
|
944
983
|
* while maintaining runtime compatibility.
|
|
945
984
|
*/
|
|
946
985
|
examples;
|
|
986
|
+
/**
|
|
987
|
+
* Throws an AbortError if the execution has been aborted.
|
|
988
|
+
*
|
|
989
|
+
* Call this at key checkpoints in long-running gadgets to allow early exit
|
|
990
|
+
* when the gadget has been cancelled (e.g., due to timeout). This enables
|
|
991
|
+
* resource cleanup and prevents unnecessary work after cancellation.
|
|
992
|
+
*
|
|
993
|
+
* @param ctx - The execution context containing the abort signal
|
|
994
|
+
* @throws AbortError if ctx.signal.aborted is true
|
|
995
|
+
*
|
|
996
|
+
* @example
|
|
997
|
+
* ```typescript
|
|
998
|
+
* class DataProcessor extends Gadget({
|
|
999
|
+
* description: 'Processes data in multiple steps',
|
|
1000
|
+
* schema: z.object({ items: z.array(z.string()) }),
|
|
1001
|
+
* }) {
|
|
1002
|
+
* async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
|
|
1003
|
+
* const results: string[] = [];
|
|
1004
|
+
*
|
|
1005
|
+
* for (const item of params.items) {
|
|
1006
|
+
* // Check before each expensive operation
|
|
1007
|
+
* this.throwIfAborted(ctx);
|
|
1008
|
+
*
|
|
1009
|
+
* results.push(await this.processItem(item));
|
|
1010
|
+
* }
|
|
1011
|
+
*
|
|
1012
|
+
* return results.join(', ');
|
|
1013
|
+
* }
|
|
1014
|
+
* }
|
|
1015
|
+
* ```
|
|
1016
|
+
*/
|
|
1017
|
+
throwIfAborted(ctx) {
|
|
1018
|
+
if (ctx?.signal?.aborted) {
|
|
1019
|
+
throw new AbortError();
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Register a cleanup function to run when execution is aborted (timeout or cancellation).
|
|
1024
|
+
* The cleanup function is called immediately if the signal is already aborted.
|
|
1025
|
+
* Errors thrown by the cleanup function are silently ignored.
|
|
1026
|
+
*
|
|
1027
|
+
* Use this to clean up resources like browser instances, database connections,
|
|
1028
|
+
* or child processes when the gadget is cancelled due to timeout.
|
|
1029
|
+
*
|
|
1030
|
+
* @param ctx - The execution context containing the abort signal
|
|
1031
|
+
* @param cleanup - Function to run on abort (can be sync or async)
|
|
1032
|
+
*
|
|
1033
|
+
* @example
|
|
1034
|
+
* ```typescript
|
|
1035
|
+
* class BrowserGadget extends Gadget({
|
|
1036
|
+
* description: 'Fetches web page content',
|
|
1037
|
+
* schema: z.object({ url: z.string() }),
|
|
1038
|
+
* }) {
|
|
1039
|
+
* async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
|
|
1040
|
+
* const browser = await chromium.launch();
|
|
1041
|
+
* this.onAbort(ctx, () => browser.close());
|
|
1042
|
+
*
|
|
1043
|
+
* const page = await browser.newPage();
|
|
1044
|
+
* this.onAbort(ctx, () => page.close());
|
|
1045
|
+
*
|
|
1046
|
+
* await page.goto(params.url);
|
|
1047
|
+
* const content = await page.content();
|
|
1048
|
+
*
|
|
1049
|
+
* await browser.close();
|
|
1050
|
+
* return content;
|
|
1051
|
+
* }
|
|
1052
|
+
* }
|
|
1053
|
+
* ```
|
|
1054
|
+
*/
|
|
1055
|
+
onAbort(ctx, cleanup) {
|
|
1056
|
+
if (!ctx?.signal) return;
|
|
1057
|
+
const safeCleanup = () => {
|
|
1058
|
+
try {
|
|
1059
|
+
const result = cleanup();
|
|
1060
|
+
if (result && typeof result === "object" && "catch" in result) {
|
|
1061
|
+
result.catch(() => {
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
} catch {
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
if (ctx.signal.aborted) {
|
|
1068
|
+
safeCleanup();
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
ctx.signal.addEventListener("abort", safeCleanup, { once: true });
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Create an AbortController linked to the execution context's signal.
|
|
1075
|
+
* When the parent signal aborts, the returned controller also aborts with the same reason.
|
|
1076
|
+
*
|
|
1077
|
+
* Useful for passing abort signals to child operations like fetch() while still
|
|
1078
|
+
* being able to abort them independently if needed.
|
|
1079
|
+
*
|
|
1080
|
+
* @param ctx - The execution context containing the parent abort signal
|
|
1081
|
+
* @returns A new AbortController linked to the parent signal
|
|
1082
|
+
*
|
|
1083
|
+
* @example
|
|
1084
|
+
* ```typescript
|
|
1085
|
+
* class FetchGadget extends Gadget({
|
|
1086
|
+
* description: 'Fetches data from URL',
|
|
1087
|
+
* schema: z.object({ url: z.string() }),
|
|
1088
|
+
* }) {
|
|
1089
|
+
* async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
|
|
1090
|
+
* const controller = this.createLinkedAbortController(ctx);
|
|
1091
|
+
*
|
|
1092
|
+
* // fetch() will automatically abort when parent times out
|
|
1093
|
+
* const response = await fetch(params.url, { signal: controller.signal });
|
|
1094
|
+
* return response.text();
|
|
1095
|
+
* }
|
|
1096
|
+
* }
|
|
1097
|
+
* ```
|
|
1098
|
+
*/
|
|
1099
|
+
createLinkedAbortController(ctx) {
|
|
1100
|
+
const controller = new AbortController();
|
|
1101
|
+
if (ctx?.signal) {
|
|
1102
|
+
if (ctx.signal.aborted) {
|
|
1103
|
+
controller.abort(ctx.signal.reason);
|
|
1104
|
+
} else {
|
|
1105
|
+
ctx.signal.addEventListener(
|
|
1106
|
+
"abort",
|
|
1107
|
+
() => {
|
|
1108
|
+
controller.abort(ctx.signal.reason);
|
|
1109
|
+
},
|
|
1110
|
+
{ once: true }
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return controller;
|
|
1115
|
+
}
|
|
947
1116
|
/**
|
|
948
1117
|
* Auto-generated instruction text for the LLM.
|
|
949
1118
|
* Combines name, description, and parameter schema into a formatted instruction.
|
|
@@ -1011,8 +1180,8 @@ function createGadget(config) {
|
|
|
1011
1180
|
parameterSchema = config.schema;
|
|
1012
1181
|
timeoutMs = config.timeoutMs;
|
|
1013
1182
|
examples = config.examples;
|
|
1014
|
-
execute(params) {
|
|
1015
|
-
return config.execute(params);
|
|
1183
|
+
execute(params, ctx) {
|
|
1184
|
+
return config.execute(params, ctx);
|
|
1016
1185
|
}
|
|
1017
1186
|
}
|
|
1018
1187
|
return new DynamicGadget();
|
|
@@ -2318,6 +2487,162 @@ var init_block_params = __esm({
|
|
|
2318
2487
|
}
|
|
2319
2488
|
});
|
|
2320
2489
|
|
|
2490
|
+
// src/gadgets/cost-reporting-client.ts
|
|
2491
|
+
var CostReportingLLMistWrapper;
|
|
2492
|
+
var init_cost_reporting_client = __esm({
|
|
2493
|
+
"src/gadgets/cost-reporting-client.ts"() {
|
|
2494
|
+
"use strict";
|
|
2495
|
+
init_model_shortcuts();
|
|
2496
|
+
CostReportingLLMistWrapper = class {
|
|
2497
|
+
constructor(client, reportCost) {
|
|
2498
|
+
this.client = client;
|
|
2499
|
+
this.reportCost = reportCost;
|
|
2500
|
+
}
|
|
2501
|
+
/**
|
|
2502
|
+
* Access to model registry for cost estimation.
|
|
2503
|
+
*/
|
|
2504
|
+
get modelRegistry() {
|
|
2505
|
+
return this.client.modelRegistry;
|
|
2506
|
+
}
|
|
2507
|
+
/**
|
|
2508
|
+
* Quick completion with automatic cost reporting.
|
|
2509
|
+
*
|
|
2510
|
+
* Streams internally to track token usage, then reports the calculated cost.
|
|
2511
|
+
*
|
|
2512
|
+
* @param prompt - User prompt
|
|
2513
|
+
* @param options - Optional configuration (model, temperature, etc.)
|
|
2514
|
+
* @returns Complete text response
|
|
2515
|
+
*/
|
|
2516
|
+
async complete(prompt, options) {
|
|
2517
|
+
const model = resolveModel(options?.model ?? "haiku");
|
|
2518
|
+
let result = "";
|
|
2519
|
+
let inputTokens = 0;
|
|
2520
|
+
let outputTokens = 0;
|
|
2521
|
+
let cachedInputTokens = 0;
|
|
2522
|
+
let cacheCreationInputTokens = 0;
|
|
2523
|
+
const messages = [
|
|
2524
|
+
...options?.systemPrompt ? [{ role: "system", content: options.systemPrompt }] : [],
|
|
2525
|
+
{ role: "user", content: prompt }
|
|
2526
|
+
];
|
|
2527
|
+
for await (const chunk of this.client.stream({
|
|
2528
|
+
model,
|
|
2529
|
+
messages,
|
|
2530
|
+
temperature: options?.temperature,
|
|
2531
|
+
maxTokens: options?.maxTokens
|
|
2532
|
+
})) {
|
|
2533
|
+
result += chunk.text ?? "";
|
|
2534
|
+
if (chunk.usage) {
|
|
2535
|
+
inputTokens = chunk.usage.inputTokens;
|
|
2536
|
+
outputTokens = chunk.usage.outputTokens;
|
|
2537
|
+
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
2538
|
+
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
this.reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
|
|
2542
|
+
return result;
|
|
2543
|
+
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Quick streaming with automatic cost reporting when stream completes.
|
|
2546
|
+
*
|
|
2547
|
+
* Yields text chunks as they arrive, then reports cost in finally block.
|
|
2548
|
+
*
|
|
2549
|
+
* @param prompt - User prompt
|
|
2550
|
+
* @param options - Optional configuration (model, temperature, etc.)
|
|
2551
|
+
* @returns Async generator yielding text chunks
|
|
2552
|
+
*/
|
|
2553
|
+
async *streamText(prompt, options) {
|
|
2554
|
+
const model = resolveModel(options?.model ?? "haiku");
|
|
2555
|
+
let inputTokens = 0;
|
|
2556
|
+
let outputTokens = 0;
|
|
2557
|
+
let cachedInputTokens = 0;
|
|
2558
|
+
let cacheCreationInputTokens = 0;
|
|
2559
|
+
const messages = [
|
|
2560
|
+
...options?.systemPrompt ? [{ role: "system", content: options.systemPrompt }] : [],
|
|
2561
|
+
{ role: "user", content: prompt }
|
|
2562
|
+
];
|
|
2563
|
+
try {
|
|
2564
|
+
for await (const chunk of this.client.stream({
|
|
2565
|
+
model,
|
|
2566
|
+
messages,
|
|
2567
|
+
temperature: options?.temperature,
|
|
2568
|
+
maxTokens: options?.maxTokens
|
|
2569
|
+
})) {
|
|
2570
|
+
if (chunk.text) {
|
|
2571
|
+
yield chunk.text;
|
|
2572
|
+
}
|
|
2573
|
+
if (chunk.usage) {
|
|
2574
|
+
inputTokens = chunk.usage.inputTokens;
|
|
2575
|
+
outputTokens = chunk.usage.outputTokens;
|
|
2576
|
+
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
2577
|
+
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
} finally {
|
|
2581
|
+
this.reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
/**
|
|
2585
|
+
* Low-level stream access with automatic cost reporting.
|
|
2586
|
+
*
|
|
2587
|
+
* Returns a wrapped stream that reports costs when iteration completes.
|
|
2588
|
+
*
|
|
2589
|
+
* @param options - Full LLM generation options
|
|
2590
|
+
* @returns Wrapped LLM stream that auto-reports costs
|
|
2591
|
+
*/
|
|
2592
|
+
stream(options) {
|
|
2593
|
+
return this.createCostReportingStream(options);
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
* Creates a wrapped stream that tracks usage and reports costs on completion.
|
|
2597
|
+
*/
|
|
2598
|
+
createCostReportingStream(options) {
|
|
2599
|
+
const innerStream = this.client.stream(options);
|
|
2600
|
+
const reportCostFromUsage = this.reportCostFromUsage.bind(this);
|
|
2601
|
+
const model = options.model;
|
|
2602
|
+
async function* costReportingWrapper() {
|
|
2603
|
+
let inputTokens = 0;
|
|
2604
|
+
let outputTokens = 0;
|
|
2605
|
+
let cachedInputTokens = 0;
|
|
2606
|
+
let cacheCreationInputTokens = 0;
|
|
2607
|
+
try {
|
|
2608
|
+
for await (const chunk of innerStream) {
|
|
2609
|
+
if (chunk.usage) {
|
|
2610
|
+
inputTokens = chunk.usage.inputTokens;
|
|
2611
|
+
outputTokens = chunk.usage.outputTokens;
|
|
2612
|
+
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
2613
|
+
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
2614
|
+
}
|
|
2615
|
+
yield chunk;
|
|
2616
|
+
}
|
|
2617
|
+
} finally {
|
|
2618
|
+
if (inputTokens > 0 || outputTokens > 0) {
|
|
2619
|
+
reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
return costReportingWrapper();
|
|
2624
|
+
}
|
|
2625
|
+
/**
|
|
2626
|
+
* Calculates and reports cost from token usage.
|
|
2627
|
+
*/
|
|
2628
|
+
reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens = 0, cacheCreationInputTokens = 0) {
|
|
2629
|
+
if (inputTokens === 0 && outputTokens === 0) return;
|
|
2630
|
+
const modelName = model.includes(":") ? model.split(":")[1] : model;
|
|
2631
|
+
const estimate = this.client.modelRegistry.estimateCost(
|
|
2632
|
+
modelName,
|
|
2633
|
+
inputTokens,
|
|
2634
|
+
outputTokens,
|
|
2635
|
+
cachedInputTokens,
|
|
2636
|
+
cacheCreationInputTokens
|
|
2637
|
+
);
|
|
2638
|
+
if (estimate && estimate.totalCost > 0) {
|
|
2639
|
+
this.reportCost(estimate.totalCost);
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
});
|
|
2645
|
+
|
|
2321
2646
|
// src/gadgets/error-formatter.ts
|
|
2322
2647
|
var GadgetErrorFormatter;
|
|
2323
2648
|
var init_error_formatter = __esm({
|
|
@@ -2401,38 +2726,6 @@ var init_error_formatter = __esm({
|
|
|
2401
2726
|
}
|
|
2402
2727
|
});
|
|
2403
2728
|
|
|
2404
|
-
// src/gadgets/exceptions.ts
|
|
2405
|
-
var BreakLoopException, HumanInputException, TimeoutException;
|
|
2406
|
-
var init_exceptions = __esm({
|
|
2407
|
-
"src/gadgets/exceptions.ts"() {
|
|
2408
|
-
"use strict";
|
|
2409
|
-
BreakLoopException = class extends Error {
|
|
2410
|
-
constructor(message) {
|
|
2411
|
-
super(message ?? "Agent loop terminated by gadget");
|
|
2412
|
-
this.name = "BreakLoopException";
|
|
2413
|
-
}
|
|
2414
|
-
};
|
|
2415
|
-
HumanInputException = class extends Error {
|
|
2416
|
-
question;
|
|
2417
|
-
constructor(question) {
|
|
2418
|
-
super(`Human input required: ${question}`);
|
|
2419
|
-
this.name = "HumanInputException";
|
|
2420
|
-
this.question = question;
|
|
2421
|
-
}
|
|
2422
|
-
};
|
|
2423
|
-
TimeoutException = class extends Error {
|
|
2424
|
-
timeoutMs;
|
|
2425
|
-
gadgetName;
|
|
2426
|
-
constructor(gadgetName, timeoutMs) {
|
|
2427
|
-
super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
|
|
2428
|
-
this.name = "TimeoutException";
|
|
2429
|
-
this.gadgetName = gadgetName;
|
|
2430
|
-
this.timeoutMs = timeoutMs;
|
|
2431
|
-
}
|
|
2432
|
-
};
|
|
2433
|
-
}
|
|
2434
|
-
});
|
|
2435
|
-
|
|
2436
2729
|
// src/gadgets/parser.ts
|
|
2437
2730
|
function stripMarkdownFences(content) {
|
|
2438
2731
|
let cleaned = content.trim();
|
|
@@ -2618,14 +2911,16 @@ var init_executor = __esm({
|
|
|
2618
2911
|
init_constants();
|
|
2619
2912
|
init_logger();
|
|
2620
2913
|
init_block_params();
|
|
2914
|
+
init_cost_reporting_client();
|
|
2621
2915
|
init_error_formatter();
|
|
2622
2916
|
init_exceptions();
|
|
2623
2917
|
init_parser();
|
|
2624
2918
|
GadgetExecutor = class {
|
|
2625
|
-
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
|
|
2919
|
+
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client) {
|
|
2626
2920
|
this.registry = registry;
|
|
2627
2921
|
this.onHumanInputRequired = onHumanInputRequired;
|
|
2628
2922
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
2923
|
+
this.client = client;
|
|
2629
2924
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
2630
2925
|
this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
|
|
2631
2926
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -2635,14 +2930,27 @@ var init_executor = __esm({
|
|
|
2635
2930
|
argPrefix;
|
|
2636
2931
|
/**
|
|
2637
2932
|
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
2933
|
+
* Aborts the provided AbortController before rejecting, allowing gadgets to clean up.
|
|
2638
2934
|
*/
|
|
2639
|
-
createTimeoutPromise(gadgetName, timeoutMs) {
|
|
2935
|
+
createTimeoutPromise(gadgetName, timeoutMs, abortController) {
|
|
2640
2936
|
return new Promise((_, reject) => {
|
|
2641
2937
|
setTimeout(() => {
|
|
2642
|
-
|
|
2938
|
+
const timeoutError = new TimeoutException(gadgetName, timeoutMs);
|
|
2939
|
+
abortController.abort(timeoutError.message);
|
|
2940
|
+
reject(timeoutError);
|
|
2643
2941
|
}, timeoutMs);
|
|
2644
2942
|
});
|
|
2645
2943
|
}
|
|
2944
|
+
/**
|
|
2945
|
+
* Normalizes gadget execute result to consistent format.
|
|
2946
|
+
* Handles both string returns (backwards compat) and object returns with cost.
|
|
2947
|
+
*/
|
|
2948
|
+
normalizeExecuteResult(raw) {
|
|
2949
|
+
if (typeof raw === "string") {
|
|
2950
|
+
return { result: raw, cost: 0 };
|
|
2951
|
+
}
|
|
2952
|
+
return { result: raw.result, cost: raw.cost ?? 0 };
|
|
2953
|
+
}
|
|
2646
2954
|
// Execute a gadget call asynchronously
|
|
2647
2955
|
async execute(call) {
|
|
2648
2956
|
const startTime = Date.now();
|
|
@@ -2737,30 +3045,53 @@ var init_executor = __esm({
|
|
|
2737
3045
|
validatedParameters = schemaAwareParameters;
|
|
2738
3046
|
}
|
|
2739
3047
|
const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
|
|
2740
|
-
|
|
3048
|
+
const abortController = new AbortController();
|
|
3049
|
+
let callbackCost = 0;
|
|
3050
|
+
const reportCost = (amount) => {
|
|
3051
|
+
if (amount > 0) {
|
|
3052
|
+
callbackCost += amount;
|
|
3053
|
+
this.logger.debug("Gadget reported cost via callback", {
|
|
3054
|
+
gadgetName: call.gadgetName,
|
|
3055
|
+
amount,
|
|
3056
|
+
totalCallbackCost: callbackCost
|
|
3057
|
+
});
|
|
3058
|
+
}
|
|
3059
|
+
};
|
|
3060
|
+
const ctx = {
|
|
3061
|
+
reportCost,
|
|
3062
|
+
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
3063
|
+
signal: abortController.signal
|
|
3064
|
+
};
|
|
3065
|
+
let rawResult;
|
|
2741
3066
|
if (timeoutMs && timeoutMs > 0) {
|
|
2742
3067
|
this.logger.debug("Executing gadget with timeout", {
|
|
2743
3068
|
gadgetName: call.gadgetName,
|
|
2744
3069
|
timeoutMs
|
|
2745
3070
|
});
|
|
2746
|
-
|
|
2747
|
-
Promise.resolve(gadget.execute(validatedParameters)),
|
|
2748
|
-
this.createTimeoutPromise(call.gadgetName, timeoutMs)
|
|
3071
|
+
rawResult = await Promise.race([
|
|
3072
|
+
Promise.resolve(gadget.execute(validatedParameters, ctx)),
|
|
3073
|
+
this.createTimeoutPromise(call.gadgetName, timeoutMs, abortController)
|
|
2749
3074
|
]);
|
|
2750
3075
|
} else {
|
|
2751
|
-
|
|
3076
|
+
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
2752
3077
|
}
|
|
3078
|
+
const { result, cost: returnCost } = this.normalizeExecuteResult(rawResult);
|
|
3079
|
+
const totalCost = callbackCost + returnCost;
|
|
2753
3080
|
const executionTimeMs = Date.now() - startTime;
|
|
2754
3081
|
this.logger.info("Gadget executed successfully", {
|
|
2755
3082
|
gadgetName: call.gadgetName,
|
|
2756
3083
|
invocationId: call.invocationId,
|
|
2757
|
-
executionTimeMs
|
|
3084
|
+
executionTimeMs,
|
|
3085
|
+
cost: totalCost > 0 ? totalCost : void 0,
|
|
3086
|
+
callbackCost: callbackCost > 0 ? callbackCost : void 0,
|
|
3087
|
+
returnCost: returnCost > 0 ? returnCost : void 0
|
|
2758
3088
|
});
|
|
2759
3089
|
this.logger.debug("Gadget result", {
|
|
2760
3090
|
gadgetName: call.gadgetName,
|
|
2761
3091
|
invocationId: call.invocationId,
|
|
2762
3092
|
parameters: validatedParameters,
|
|
2763
3093
|
result,
|
|
3094
|
+
cost: totalCost,
|
|
2764
3095
|
executionTimeMs
|
|
2765
3096
|
});
|
|
2766
3097
|
return {
|
|
@@ -2768,7 +3099,8 @@ var init_executor = __esm({
|
|
|
2768
3099
|
invocationId: call.invocationId,
|
|
2769
3100
|
parameters: validatedParameters,
|
|
2770
3101
|
result,
|
|
2771
|
-
executionTimeMs
|
|
3102
|
+
executionTimeMs,
|
|
3103
|
+
cost: totalCost
|
|
2772
3104
|
};
|
|
2773
3105
|
} catch (error) {
|
|
2774
3106
|
if (error instanceof BreakLoopException) {
|
|
@@ -2799,6 +3131,19 @@ var init_executor = __esm({
|
|
|
2799
3131
|
executionTimeMs: Date.now() - startTime
|
|
2800
3132
|
};
|
|
2801
3133
|
}
|
|
3134
|
+
if (error instanceof AbortError) {
|
|
3135
|
+
this.logger.info("Gadget execution was aborted", {
|
|
3136
|
+
gadgetName: call.gadgetName,
|
|
3137
|
+
executionTimeMs: Date.now() - startTime
|
|
3138
|
+
});
|
|
3139
|
+
return {
|
|
3140
|
+
gadgetName: call.gadgetName,
|
|
3141
|
+
invocationId: call.invocationId,
|
|
3142
|
+
parameters: validatedParameters,
|
|
3143
|
+
error: error.message,
|
|
3144
|
+
executionTimeMs: Date.now() - startTime
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
2802
3147
|
if (error instanceof HumanInputException) {
|
|
2803
3148
|
this.logger.info("Gadget requested human input", {
|
|
2804
3149
|
gadgetName: call.gadgetName,
|
|
@@ -2925,7 +3270,8 @@ var init_stream_processor = __esm({
|
|
|
2925
3270
|
options.onHumanInputRequired,
|
|
2926
3271
|
this.logger.getSubLogger({ name: "executor" }),
|
|
2927
3272
|
options.defaultGadgetTimeoutMs,
|
|
2928
|
-
{ argPrefix: options.gadgetArgPrefix }
|
|
3273
|
+
{ argPrefix: options.gadgetArgPrefix },
|
|
3274
|
+
options.client
|
|
2929
3275
|
);
|
|
2930
3276
|
}
|
|
2931
3277
|
/**
|
|
@@ -3192,6 +3538,7 @@ var init_stream_processor = __esm({
|
|
|
3192
3538
|
error: result.error,
|
|
3193
3539
|
executionTimeMs: result.executionTimeMs,
|
|
3194
3540
|
breaksLoop: result.breaksLoop,
|
|
3541
|
+
cost: result.cost,
|
|
3195
3542
|
logger: this.logger
|
|
3196
3543
|
};
|
|
3197
3544
|
await this.hooks.observers.onGadgetExecutionComplete(context);
|
|
@@ -3548,6 +3895,17 @@ var init_agent = __esm({
|
|
|
3548
3895
|
llmOptions = { ...llmOptions, ...action.modifiedOptions };
|
|
3549
3896
|
}
|
|
3550
3897
|
}
|
|
3898
|
+
await this.safeObserve(async () => {
|
|
3899
|
+
if (this.hooks.observers?.onLLMCallReady) {
|
|
3900
|
+
const context = {
|
|
3901
|
+
iteration: currentIteration,
|
|
3902
|
+
maxIterations: this.maxIterations,
|
|
3903
|
+
options: llmOptions,
|
|
3904
|
+
logger: this.logger
|
|
3905
|
+
};
|
|
3906
|
+
await this.hooks.observers.onLLMCallReady(context);
|
|
3907
|
+
}
|
|
3908
|
+
});
|
|
3551
3909
|
this.logger.info("Calling LLM", { model: this.model });
|
|
3552
3910
|
this.logger.silly("LLM request details", {
|
|
3553
3911
|
model: llmOptions.model,
|
|
@@ -3568,7 +3926,8 @@ var init_agent = __esm({
|
|
|
3568
3926
|
onHumanInputRequired: this.onHumanInputRequired,
|
|
3569
3927
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
3570
3928
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
3571
|
-
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
|
|
3929
|
+
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
3930
|
+
client: this.client
|
|
3572
3931
|
});
|
|
3573
3932
|
const result = await processor.process(stream2);
|
|
3574
3933
|
for (const output of result.outputs) {
|
|
@@ -6700,10 +7059,18 @@ async function testGadget(gadget, params, options) {
|
|
|
6700
7059
|
validatedParams = validationResult.data;
|
|
6701
7060
|
}
|
|
6702
7061
|
try {
|
|
6703
|
-
const
|
|
7062
|
+
const rawResult = await Promise.resolve(gadget.execute(validatedParams));
|
|
7063
|
+
if (typeof rawResult === "string") {
|
|
7064
|
+
return {
|
|
7065
|
+
result: rawResult,
|
|
7066
|
+
validatedParams,
|
|
7067
|
+
cost: 0
|
|
7068
|
+
};
|
|
7069
|
+
}
|
|
6704
7070
|
return {
|
|
6705
|
-
result,
|
|
6706
|
-
validatedParams
|
|
7071
|
+
result: rawResult.result,
|
|
7072
|
+
validatedParams,
|
|
7073
|
+
cost: rawResult.cost ?? 0
|
|
6707
7074
|
};
|
|
6708
7075
|
} catch (error) {
|
|
6709
7076
|
return {
|