llmist 1.6.2 → 2.0.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-T3DIKQWU.js → chunk-LBHWVCZ2.js} +374 -55
- package/dist/chunk-LBHWVCZ2.js.map +1 -0
- package/dist/{chunk-TDRPJP2Q.js → chunk-LFSIEPAE.js} +10 -3
- package/dist/chunk-LFSIEPAE.js.map +1 -0
- package/dist/cli.cjs +384 -61
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +28 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +368 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -13
- package/dist/index.d.ts +70 -13
- package/dist/index.js +4 -2
- package/dist/{mock-stream-Cc47j12U.d.cts → mock-stream-BQHut0lQ.d.cts} +595 -303
- package/dist/{mock-stream-Cc47j12U.d.ts → mock-stream-BQHut0lQ.d.ts} +595 -303
- package/dist/testing/index.cjs +369 -51
- 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-T3DIKQWU.js.map +0 -1
- package/dist/chunk-TDRPJP2Q.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -644,6 +644,44 @@ ${this.endPrefix}`
|
|
|
644
644
|
}
|
|
645
645
|
});
|
|
646
646
|
|
|
647
|
+
// src/gadgets/exceptions.ts
|
|
648
|
+
var BreakLoopException, HumanInputException, TimeoutException, AbortError;
|
|
649
|
+
var init_exceptions = __esm({
|
|
650
|
+
"src/gadgets/exceptions.ts"() {
|
|
651
|
+
"use strict";
|
|
652
|
+
BreakLoopException = class extends Error {
|
|
653
|
+
constructor(message) {
|
|
654
|
+
super(message ?? "Agent loop terminated by gadget");
|
|
655
|
+
this.name = "BreakLoopException";
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
HumanInputException = class extends Error {
|
|
659
|
+
question;
|
|
660
|
+
constructor(question) {
|
|
661
|
+
super(`Human input required: ${question}`);
|
|
662
|
+
this.name = "HumanInputException";
|
|
663
|
+
this.question = question;
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
TimeoutException = class extends Error {
|
|
667
|
+
timeoutMs;
|
|
668
|
+
gadgetName;
|
|
669
|
+
constructor(gadgetName, timeoutMs) {
|
|
670
|
+
super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
|
|
671
|
+
this.name = "TimeoutException";
|
|
672
|
+
this.gadgetName = gadgetName;
|
|
673
|
+
this.timeoutMs = timeoutMs;
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
AbortError = class extends Error {
|
|
677
|
+
constructor(message) {
|
|
678
|
+
super(message || "Gadget execution was aborted");
|
|
679
|
+
this.name = "AbortError";
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
|
|
647
685
|
// src/logging/logger.ts
|
|
648
686
|
function parseLogLevel(value) {
|
|
649
687
|
if (!value) {
|
|
@@ -916,6 +954,7 @@ var init_gadget = __esm({
|
|
|
916
954
|
"src/gadgets/gadget.ts"() {
|
|
917
955
|
"use strict";
|
|
918
956
|
init_constants();
|
|
957
|
+
init_exceptions();
|
|
919
958
|
init_schema_to_json();
|
|
920
959
|
init_schema_validator();
|
|
921
960
|
BaseGadget = class {
|
|
@@ -945,6 +984,42 @@ var init_gadget = __esm({
|
|
|
945
984
|
* while maintaining runtime compatibility.
|
|
946
985
|
*/
|
|
947
986
|
examples;
|
|
987
|
+
/**
|
|
988
|
+
* Throws an AbortError if the execution has been aborted.
|
|
989
|
+
*
|
|
990
|
+
* Call this at key checkpoints in long-running gadgets to allow early exit
|
|
991
|
+
* when the gadget has been cancelled (e.g., due to timeout). This enables
|
|
992
|
+
* resource cleanup and prevents unnecessary work after cancellation.
|
|
993
|
+
*
|
|
994
|
+
* @param ctx - The execution context containing the abort signal
|
|
995
|
+
* @throws AbortError if ctx.signal.aborted is true
|
|
996
|
+
*
|
|
997
|
+
* @example
|
|
998
|
+
* ```typescript
|
|
999
|
+
* class DataProcessor extends Gadget({
|
|
1000
|
+
* description: 'Processes data in multiple steps',
|
|
1001
|
+
* schema: z.object({ items: z.array(z.string()) }),
|
|
1002
|
+
* }) {
|
|
1003
|
+
* async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
|
|
1004
|
+
* const results: string[] = [];
|
|
1005
|
+
*
|
|
1006
|
+
* for (const item of params.items) {
|
|
1007
|
+
* // Check before each expensive operation
|
|
1008
|
+
* this.throwIfAborted(ctx);
|
|
1009
|
+
*
|
|
1010
|
+
* results.push(await this.processItem(item));
|
|
1011
|
+
* }
|
|
1012
|
+
*
|
|
1013
|
+
* return results.join(', ');
|
|
1014
|
+
* }
|
|
1015
|
+
* }
|
|
1016
|
+
* ```
|
|
1017
|
+
*/
|
|
1018
|
+
throwIfAborted(ctx) {
|
|
1019
|
+
if (ctx?.signal?.aborted) {
|
|
1020
|
+
throw new AbortError();
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
948
1023
|
/**
|
|
949
1024
|
* Auto-generated instruction text for the LLM.
|
|
950
1025
|
* Combines name, description, and parameter schema into a formatted instruction.
|
|
@@ -1012,8 +1087,8 @@ function createGadget(config) {
|
|
|
1012
1087
|
parameterSchema = config.schema;
|
|
1013
1088
|
timeoutMs = config.timeoutMs;
|
|
1014
1089
|
examples = config.examples;
|
|
1015
|
-
execute(params) {
|
|
1016
|
-
return config.execute(params);
|
|
1090
|
+
execute(params, ctx) {
|
|
1091
|
+
return config.execute(params, ctx);
|
|
1017
1092
|
}
|
|
1018
1093
|
}
|
|
1019
1094
|
return new DynamicGadget();
|
|
@@ -2319,6 +2394,162 @@ var init_block_params = __esm({
|
|
|
2319
2394
|
}
|
|
2320
2395
|
});
|
|
2321
2396
|
|
|
2397
|
+
// src/gadgets/cost-reporting-client.ts
|
|
2398
|
+
var CostReportingLLMistWrapper;
|
|
2399
|
+
var init_cost_reporting_client = __esm({
|
|
2400
|
+
"src/gadgets/cost-reporting-client.ts"() {
|
|
2401
|
+
"use strict";
|
|
2402
|
+
init_model_shortcuts();
|
|
2403
|
+
CostReportingLLMistWrapper = class {
|
|
2404
|
+
constructor(client, reportCost) {
|
|
2405
|
+
this.client = client;
|
|
2406
|
+
this.reportCost = reportCost;
|
|
2407
|
+
}
|
|
2408
|
+
/**
|
|
2409
|
+
* Access to model registry for cost estimation.
|
|
2410
|
+
*/
|
|
2411
|
+
get modelRegistry() {
|
|
2412
|
+
return this.client.modelRegistry;
|
|
2413
|
+
}
|
|
2414
|
+
/**
|
|
2415
|
+
* Quick completion with automatic cost reporting.
|
|
2416
|
+
*
|
|
2417
|
+
* Streams internally to track token usage, then reports the calculated cost.
|
|
2418
|
+
*
|
|
2419
|
+
* @param prompt - User prompt
|
|
2420
|
+
* @param options - Optional configuration (model, temperature, etc.)
|
|
2421
|
+
* @returns Complete text response
|
|
2422
|
+
*/
|
|
2423
|
+
async complete(prompt, options) {
|
|
2424
|
+
const model = resolveModel(options?.model ?? "haiku");
|
|
2425
|
+
let result = "";
|
|
2426
|
+
let inputTokens = 0;
|
|
2427
|
+
let outputTokens = 0;
|
|
2428
|
+
let cachedInputTokens = 0;
|
|
2429
|
+
let cacheCreationInputTokens = 0;
|
|
2430
|
+
const messages = [
|
|
2431
|
+
...options?.systemPrompt ? [{ role: "system", content: options.systemPrompt }] : [],
|
|
2432
|
+
{ role: "user", content: prompt }
|
|
2433
|
+
];
|
|
2434
|
+
for await (const chunk of this.client.stream({
|
|
2435
|
+
model,
|
|
2436
|
+
messages,
|
|
2437
|
+
temperature: options?.temperature,
|
|
2438
|
+
maxTokens: options?.maxTokens
|
|
2439
|
+
})) {
|
|
2440
|
+
result += chunk.text ?? "";
|
|
2441
|
+
if (chunk.usage) {
|
|
2442
|
+
inputTokens = chunk.usage.inputTokens;
|
|
2443
|
+
outputTokens = chunk.usage.outputTokens;
|
|
2444
|
+
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
2445
|
+
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
this.reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
|
|
2449
|
+
return result;
|
|
2450
|
+
}
|
|
2451
|
+
/**
|
|
2452
|
+
* Quick streaming with automatic cost reporting when stream completes.
|
|
2453
|
+
*
|
|
2454
|
+
* Yields text chunks as they arrive, then reports cost in finally block.
|
|
2455
|
+
*
|
|
2456
|
+
* @param prompt - User prompt
|
|
2457
|
+
* @param options - Optional configuration (model, temperature, etc.)
|
|
2458
|
+
* @returns Async generator yielding text chunks
|
|
2459
|
+
*/
|
|
2460
|
+
async *streamText(prompt, options) {
|
|
2461
|
+
const model = resolveModel(options?.model ?? "haiku");
|
|
2462
|
+
let inputTokens = 0;
|
|
2463
|
+
let outputTokens = 0;
|
|
2464
|
+
let cachedInputTokens = 0;
|
|
2465
|
+
let cacheCreationInputTokens = 0;
|
|
2466
|
+
const messages = [
|
|
2467
|
+
...options?.systemPrompt ? [{ role: "system", content: options.systemPrompt }] : [],
|
|
2468
|
+
{ role: "user", content: prompt }
|
|
2469
|
+
];
|
|
2470
|
+
try {
|
|
2471
|
+
for await (const chunk of this.client.stream({
|
|
2472
|
+
model,
|
|
2473
|
+
messages,
|
|
2474
|
+
temperature: options?.temperature,
|
|
2475
|
+
maxTokens: options?.maxTokens
|
|
2476
|
+
})) {
|
|
2477
|
+
if (chunk.text) {
|
|
2478
|
+
yield chunk.text;
|
|
2479
|
+
}
|
|
2480
|
+
if (chunk.usage) {
|
|
2481
|
+
inputTokens = chunk.usage.inputTokens;
|
|
2482
|
+
outputTokens = chunk.usage.outputTokens;
|
|
2483
|
+
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
2484
|
+
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
} finally {
|
|
2488
|
+
this.reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* Low-level stream access with automatic cost reporting.
|
|
2493
|
+
*
|
|
2494
|
+
* Returns a wrapped stream that reports costs when iteration completes.
|
|
2495
|
+
*
|
|
2496
|
+
* @param options - Full LLM generation options
|
|
2497
|
+
* @returns Wrapped LLM stream that auto-reports costs
|
|
2498
|
+
*/
|
|
2499
|
+
stream(options) {
|
|
2500
|
+
return this.createCostReportingStream(options);
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Creates a wrapped stream that tracks usage and reports costs on completion.
|
|
2504
|
+
*/
|
|
2505
|
+
createCostReportingStream(options) {
|
|
2506
|
+
const innerStream = this.client.stream(options);
|
|
2507
|
+
const reportCostFromUsage = this.reportCostFromUsage.bind(this);
|
|
2508
|
+
const model = options.model;
|
|
2509
|
+
async function* costReportingWrapper() {
|
|
2510
|
+
let inputTokens = 0;
|
|
2511
|
+
let outputTokens = 0;
|
|
2512
|
+
let cachedInputTokens = 0;
|
|
2513
|
+
let cacheCreationInputTokens = 0;
|
|
2514
|
+
try {
|
|
2515
|
+
for await (const chunk of innerStream) {
|
|
2516
|
+
if (chunk.usage) {
|
|
2517
|
+
inputTokens = chunk.usage.inputTokens;
|
|
2518
|
+
outputTokens = chunk.usage.outputTokens;
|
|
2519
|
+
cachedInputTokens = chunk.usage.cachedInputTokens ?? 0;
|
|
2520
|
+
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
2521
|
+
}
|
|
2522
|
+
yield chunk;
|
|
2523
|
+
}
|
|
2524
|
+
} finally {
|
|
2525
|
+
if (inputTokens > 0 || outputTokens > 0) {
|
|
2526
|
+
reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
return costReportingWrapper();
|
|
2531
|
+
}
|
|
2532
|
+
/**
|
|
2533
|
+
* Calculates and reports cost from token usage.
|
|
2534
|
+
*/
|
|
2535
|
+
reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens = 0, cacheCreationInputTokens = 0) {
|
|
2536
|
+
if (inputTokens === 0 && outputTokens === 0) return;
|
|
2537
|
+
const modelName = model.includes(":") ? model.split(":")[1] : model;
|
|
2538
|
+
const estimate = this.client.modelRegistry.estimateCost(
|
|
2539
|
+
modelName,
|
|
2540
|
+
inputTokens,
|
|
2541
|
+
outputTokens,
|
|
2542
|
+
cachedInputTokens,
|
|
2543
|
+
cacheCreationInputTokens
|
|
2544
|
+
);
|
|
2545
|
+
if (estimate && estimate.totalCost > 0) {
|
|
2546
|
+
this.reportCost(estimate.totalCost);
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
});
|
|
2552
|
+
|
|
2322
2553
|
// src/gadgets/error-formatter.ts
|
|
2323
2554
|
var GadgetErrorFormatter;
|
|
2324
2555
|
var init_error_formatter = __esm({
|
|
@@ -2402,38 +2633,6 @@ var init_error_formatter = __esm({
|
|
|
2402
2633
|
}
|
|
2403
2634
|
});
|
|
2404
2635
|
|
|
2405
|
-
// src/gadgets/exceptions.ts
|
|
2406
|
-
var BreakLoopException, HumanInputException, TimeoutException;
|
|
2407
|
-
var init_exceptions = __esm({
|
|
2408
|
-
"src/gadgets/exceptions.ts"() {
|
|
2409
|
-
"use strict";
|
|
2410
|
-
BreakLoopException = class extends Error {
|
|
2411
|
-
constructor(message) {
|
|
2412
|
-
super(message ?? "Agent loop terminated by gadget");
|
|
2413
|
-
this.name = "BreakLoopException";
|
|
2414
|
-
}
|
|
2415
|
-
};
|
|
2416
|
-
HumanInputException = class extends Error {
|
|
2417
|
-
question;
|
|
2418
|
-
constructor(question) {
|
|
2419
|
-
super(`Human input required: ${question}`);
|
|
2420
|
-
this.name = "HumanInputException";
|
|
2421
|
-
this.question = question;
|
|
2422
|
-
}
|
|
2423
|
-
};
|
|
2424
|
-
TimeoutException = class extends Error {
|
|
2425
|
-
timeoutMs;
|
|
2426
|
-
gadgetName;
|
|
2427
|
-
constructor(gadgetName, timeoutMs) {
|
|
2428
|
-
super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
|
|
2429
|
-
this.name = "TimeoutException";
|
|
2430
|
-
this.gadgetName = gadgetName;
|
|
2431
|
-
this.timeoutMs = timeoutMs;
|
|
2432
|
-
}
|
|
2433
|
-
};
|
|
2434
|
-
}
|
|
2435
|
-
});
|
|
2436
|
-
|
|
2437
2636
|
// src/gadgets/parser.ts
|
|
2438
2637
|
function stripMarkdownFences(content) {
|
|
2439
2638
|
let cleaned = content.trim();
|
|
@@ -2619,14 +2818,16 @@ var init_executor = __esm({
|
|
|
2619
2818
|
init_constants();
|
|
2620
2819
|
init_logger();
|
|
2621
2820
|
init_block_params();
|
|
2821
|
+
init_cost_reporting_client();
|
|
2622
2822
|
init_error_formatter();
|
|
2623
2823
|
init_exceptions();
|
|
2624
2824
|
init_parser();
|
|
2625
2825
|
GadgetExecutor = class {
|
|
2626
|
-
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
|
|
2826
|
+
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client) {
|
|
2627
2827
|
this.registry = registry;
|
|
2628
2828
|
this.onHumanInputRequired = onHumanInputRequired;
|
|
2629
2829
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
2830
|
+
this.client = client;
|
|
2630
2831
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
2631
2832
|
this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
|
|
2632
2833
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -2636,14 +2837,27 @@ var init_executor = __esm({
|
|
|
2636
2837
|
argPrefix;
|
|
2637
2838
|
/**
|
|
2638
2839
|
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
2840
|
+
* Aborts the provided AbortController before rejecting, allowing gadgets to clean up.
|
|
2639
2841
|
*/
|
|
2640
|
-
createTimeoutPromise(gadgetName, timeoutMs) {
|
|
2842
|
+
createTimeoutPromise(gadgetName, timeoutMs, abortController) {
|
|
2641
2843
|
return new Promise((_, reject) => {
|
|
2642
2844
|
setTimeout(() => {
|
|
2643
|
-
|
|
2845
|
+
const timeoutError = new TimeoutException(gadgetName, timeoutMs);
|
|
2846
|
+
abortController.abort(timeoutError.message);
|
|
2847
|
+
reject(timeoutError);
|
|
2644
2848
|
}, timeoutMs);
|
|
2645
2849
|
});
|
|
2646
2850
|
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Normalizes gadget execute result to consistent format.
|
|
2853
|
+
* Handles both string returns (backwards compat) and object returns with cost.
|
|
2854
|
+
*/
|
|
2855
|
+
normalizeExecuteResult(raw) {
|
|
2856
|
+
if (typeof raw === "string") {
|
|
2857
|
+
return { result: raw, cost: 0 };
|
|
2858
|
+
}
|
|
2859
|
+
return { result: raw.result, cost: raw.cost ?? 0 };
|
|
2860
|
+
}
|
|
2647
2861
|
// Execute a gadget call asynchronously
|
|
2648
2862
|
async execute(call) {
|
|
2649
2863
|
const startTime = Date.now();
|
|
@@ -2738,30 +2952,53 @@ var init_executor = __esm({
|
|
|
2738
2952
|
validatedParameters = schemaAwareParameters;
|
|
2739
2953
|
}
|
|
2740
2954
|
const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
|
|
2741
|
-
|
|
2955
|
+
const abortController = new AbortController();
|
|
2956
|
+
let callbackCost = 0;
|
|
2957
|
+
const reportCost = (amount) => {
|
|
2958
|
+
if (amount > 0) {
|
|
2959
|
+
callbackCost += amount;
|
|
2960
|
+
this.logger.debug("Gadget reported cost via callback", {
|
|
2961
|
+
gadgetName: call.gadgetName,
|
|
2962
|
+
amount,
|
|
2963
|
+
totalCallbackCost: callbackCost
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
};
|
|
2967
|
+
const ctx = {
|
|
2968
|
+
reportCost,
|
|
2969
|
+
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
2970
|
+
signal: abortController.signal
|
|
2971
|
+
};
|
|
2972
|
+
let rawResult;
|
|
2742
2973
|
if (timeoutMs && timeoutMs > 0) {
|
|
2743
2974
|
this.logger.debug("Executing gadget with timeout", {
|
|
2744
2975
|
gadgetName: call.gadgetName,
|
|
2745
2976
|
timeoutMs
|
|
2746
2977
|
});
|
|
2747
|
-
|
|
2748
|
-
Promise.resolve(gadget.execute(validatedParameters)),
|
|
2749
|
-
this.createTimeoutPromise(call.gadgetName, timeoutMs)
|
|
2978
|
+
rawResult = await Promise.race([
|
|
2979
|
+
Promise.resolve(gadget.execute(validatedParameters, ctx)),
|
|
2980
|
+
this.createTimeoutPromise(call.gadgetName, timeoutMs, abortController)
|
|
2750
2981
|
]);
|
|
2751
2982
|
} else {
|
|
2752
|
-
|
|
2983
|
+
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
2753
2984
|
}
|
|
2985
|
+
const { result, cost: returnCost } = this.normalizeExecuteResult(rawResult);
|
|
2986
|
+
const totalCost = callbackCost + returnCost;
|
|
2754
2987
|
const executionTimeMs = Date.now() - startTime;
|
|
2755
2988
|
this.logger.info("Gadget executed successfully", {
|
|
2756
2989
|
gadgetName: call.gadgetName,
|
|
2757
2990
|
invocationId: call.invocationId,
|
|
2758
|
-
executionTimeMs
|
|
2991
|
+
executionTimeMs,
|
|
2992
|
+
cost: totalCost > 0 ? totalCost : void 0,
|
|
2993
|
+
callbackCost: callbackCost > 0 ? callbackCost : void 0,
|
|
2994
|
+
returnCost: returnCost > 0 ? returnCost : void 0
|
|
2759
2995
|
});
|
|
2760
2996
|
this.logger.debug("Gadget result", {
|
|
2761
2997
|
gadgetName: call.gadgetName,
|
|
2762
2998
|
invocationId: call.invocationId,
|
|
2763
2999
|
parameters: validatedParameters,
|
|
2764
3000
|
result,
|
|
3001
|
+
cost: totalCost,
|
|
2765
3002
|
executionTimeMs
|
|
2766
3003
|
});
|
|
2767
3004
|
return {
|
|
@@ -2769,7 +3006,8 @@ var init_executor = __esm({
|
|
|
2769
3006
|
invocationId: call.invocationId,
|
|
2770
3007
|
parameters: validatedParameters,
|
|
2771
3008
|
result,
|
|
2772
|
-
executionTimeMs
|
|
3009
|
+
executionTimeMs,
|
|
3010
|
+
cost: totalCost
|
|
2773
3011
|
};
|
|
2774
3012
|
} catch (error) {
|
|
2775
3013
|
if (error instanceof BreakLoopException) {
|
|
@@ -2800,6 +3038,19 @@ var init_executor = __esm({
|
|
|
2800
3038
|
executionTimeMs: Date.now() - startTime
|
|
2801
3039
|
};
|
|
2802
3040
|
}
|
|
3041
|
+
if (error instanceof AbortError) {
|
|
3042
|
+
this.logger.info("Gadget execution was aborted", {
|
|
3043
|
+
gadgetName: call.gadgetName,
|
|
3044
|
+
executionTimeMs: Date.now() - startTime
|
|
3045
|
+
});
|
|
3046
|
+
return {
|
|
3047
|
+
gadgetName: call.gadgetName,
|
|
3048
|
+
invocationId: call.invocationId,
|
|
3049
|
+
parameters: validatedParameters,
|
|
3050
|
+
error: error.message,
|
|
3051
|
+
executionTimeMs: Date.now() - startTime
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
2803
3054
|
if (error instanceof HumanInputException) {
|
|
2804
3055
|
this.logger.info("Gadget requested human input", {
|
|
2805
3056
|
gadgetName: call.gadgetName,
|
|
@@ -2926,7 +3177,8 @@ var init_stream_processor = __esm({
|
|
|
2926
3177
|
options.onHumanInputRequired,
|
|
2927
3178
|
this.logger.getSubLogger({ name: "executor" }),
|
|
2928
3179
|
options.defaultGadgetTimeoutMs,
|
|
2929
|
-
{ argPrefix: options.gadgetArgPrefix }
|
|
3180
|
+
{ argPrefix: options.gadgetArgPrefix },
|
|
3181
|
+
options.client
|
|
2930
3182
|
);
|
|
2931
3183
|
}
|
|
2932
3184
|
/**
|
|
@@ -3193,6 +3445,7 @@ var init_stream_processor = __esm({
|
|
|
3193
3445
|
error: result.error,
|
|
3194
3446
|
executionTimeMs: result.executionTimeMs,
|
|
3195
3447
|
breaksLoop: result.breaksLoop,
|
|
3448
|
+
cost: result.cost,
|
|
3196
3449
|
logger: this.logger
|
|
3197
3450
|
};
|
|
3198
3451
|
await this.hooks.observers.onGadgetExecutionComplete(context);
|
|
@@ -3569,7 +3822,8 @@ var init_agent = __esm({
|
|
|
3569
3822
|
onHumanInputRequired: this.onHumanInputRequired,
|
|
3570
3823
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
3571
3824
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
3572
|
-
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
|
|
3825
|
+
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
3826
|
+
client: this.client
|
|
3573
3827
|
});
|
|
3574
3828
|
const result = await processor.process(stream2);
|
|
3575
3829
|
for (const output of result.outputs) {
|
|
@@ -5852,6 +6106,7 @@ var init_builder = __esm({
|
|
|
5852
6106
|
gadgetOutputLimitPercent;
|
|
5853
6107
|
compactionConfig;
|
|
5854
6108
|
signal;
|
|
6109
|
+
trailingMessage;
|
|
5855
6110
|
constructor(client) {
|
|
5856
6111
|
this.client = client;
|
|
5857
6112
|
}
|
|
@@ -6327,6 +6582,31 @@ var init_builder = __esm({
|
|
|
6327
6582
|
this.signal = signal;
|
|
6328
6583
|
return this;
|
|
6329
6584
|
}
|
|
6585
|
+
/**
|
|
6586
|
+
* Add an ephemeral trailing message that appears at the end of each LLM request.
|
|
6587
|
+
*
|
|
6588
|
+
* The message is NOT persisted to conversation history - it only appears in the
|
|
6589
|
+
* current LLM call. This is useful for injecting context-specific instructions
|
|
6590
|
+
* or reminders without polluting the conversation history.
|
|
6591
|
+
*
|
|
6592
|
+
* @param message - Static string or function that generates the message
|
|
6593
|
+
* @returns This builder for chaining
|
|
6594
|
+
*
|
|
6595
|
+
* @example
|
|
6596
|
+
* ```typescript
|
|
6597
|
+
* // Static message
|
|
6598
|
+
* .withTrailingMessage("Always respond in JSON format.")
|
|
6599
|
+
*
|
|
6600
|
+
* // Dynamic message based on iteration
|
|
6601
|
+
* .withTrailingMessage((ctx) =>
|
|
6602
|
+
* `[Iteration ${ctx.iteration}/${ctx.maxIterations}] Stay focused on the task.`
|
|
6603
|
+
* )
|
|
6604
|
+
* ```
|
|
6605
|
+
*/
|
|
6606
|
+
withTrailingMessage(message) {
|
|
6607
|
+
this.trailingMessage = message;
|
|
6608
|
+
return this;
|
|
6609
|
+
}
|
|
6330
6610
|
/**
|
|
6331
6611
|
* Add a synthetic gadget call to the conversation history.
|
|
6332
6612
|
*
|
|
@@ -6368,6 +6648,36 @@ ${endPrefix}`
|
|
|
6368
6648
|
});
|
|
6369
6649
|
return this;
|
|
6370
6650
|
}
|
|
6651
|
+
/**
|
|
6652
|
+
* Compose the final hooks, including trailing message if configured.
|
|
6653
|
+
*/
|
|
6654
|
+
composeHooks() {
|
|
6655
|
+
if (!this.trailingMessage) {
|
|
6656
|
+
return this.hooks;
|
|
6657
|
+
}
|
|
6658
|
+
const trailingMsg = this.trailingMessage;
|
|
6659
|
+
const existingBeforeLLMCall = this.hooks?.controllers?.beforeLLMCall;
|
|
6660
|
+
const trailingMessageController = async (ctx) => {
|
|
6661
|
+
const result = existingBeforeLLMCall ? await existingBeforeLLMCall(ctx) : { action: "proceed" };
|
|
6662
|
+
if (result.action === "skip") {
|
|
6663
|
+
return result;
|
|
6664
|
+
}
|
|
6665
|
+
const messages = [...result.modifiedOptions?.messages || ctx.options.messages];
|
|
6666
|
+
const content = typeof trailingMsg === "function" ? trailingMsg({ iteration: ctx.iteration, maxIterations: ctx.maxIterations }) : trailingMsg;
|
|
6667
|
+
messages.push({ role: "user", content });
|
|
6668
|
+
return {
|
|
6669
|
+
action: "proceed",
|
|
6670
|
+
modifiedOptions: { ...result.modifiedOptions, messages }
|
|
6671
|
+
};
|
|
6672
|
+
};
|
|
6673
|
+
return {
|
|
6674
|
+
...this.hooks,
|
|
6675
|
+
controllers: {
|
|
6676
|
+
...this.hooks?.controllers,
|
|
6677
|
+
beforeLLMCall: trailingMessageController
|
|
6678
|
+
}
|
|
6679
|
+
};
|
|
6680
|
+
}
|
|
6371
6681
|
/**
|
|
6372
6682
|
* Format parameters as block format with JSON Pointer paths.
|
|
6373
6683
|
*/
|
|
@@ -6429,7 +6739,7 @@ ${endPrefix}`
|
|
|
6429
6739
|
maxIterations: this.maxIterations,
|
|
6430
6740
|
temperature: this.temperature,
|
|
6431
6741
|
logger: this.logger,
|
|
6432
|
-
hooks: this.
|
|
6742
|
+
hooks: this.composeHooks(),
|
|
6433
6743
|
promptConfig: this.promptConfig,
|
|
6434
6744
|
initialMessages: this.initialMessages,
|
|
6435
6745
|
onHumanInputRequired: this.onHumanInputRequired,
|
|
@@ -6533,7 +6843,7 @@ ${endPrefix}`
|
|
|
6533
6843
|
maxIterations: this.maxIterations,
|
|
6534
6844
|
temperature: this.temperature,
|
|
6535
6845
|
logger: this.logger,
|
|
6536
|
-
hooks: this.
|
|
6846
|
+
hooks: this.composeHooks(),
|
|
6537
6847
|
promptConfig: this.promptConfig,
|
|
6538
6848
|
initialMessages: this.initialMessages,
|
|
6539
6849
|
onHumanInputRequired: this.onHumanInputRequired,
|
|
@@ -6615,7 +6925,7 @@ var import_commander2 = require("commander");
|
|
|
6615
6925
|
// package.json
|
|
6616
6926
|
var package_default = {
|
|
6617
6927
|
name: "llmist",
|
|
6618
|
-
version: "1.
|
|
6928
|
+
version: "1.7.0",
|
|
6619
6929
|
description: "Universal TypeScript LLM client with streaming-first agent framework. Works with any model - no structured outputs or native tool calling required. Implements its own flexible grammar for function calling.",
|
|
6620
6930
|
type: "module",
|
|
6621
6931
|
main: "dist/index.cjs",
|
|
@@ -7478,38 +7788,46 @@ error: ${message}`;
|
|
|
7478
7788
|
var import_zod8 = require("zod");
|
|
7479
7789
|
var runCommand = createGadget({
|
|
7480
7790
|
name: "RunCommand",
|
|
7481
|
-
description: "Execute a
|
|
7791
|
+
description: "Execute a command with arguments and return its output. Uses argv array to bypass shell - arguments are passed directly without interpretation. Returns stdout/stderr combined with exit status.",
|
|
7482
7792
|
schema: import_zod8.z.object({
|
|
7483
|
-
|
|
7793
|
+
argv: import_zod8.z.array(import_zod8.z.string()).describe("Command and arguments as array (e.g., ['git', 'commit', '-m', 'message'])"),
|
|
7484
7794
|
cwd: import_zod8.z.string().optional().describe("Working directory for the command (default: current directory)"),
|
|
7485
7795
|
timeout: import_zod8.z.number().default(3e4).describe("Timeout in milliseconds (default: 30000)")
|
|
7486
7796
|
}),
|
|
7487
7797
|
examples: [
|
|
7488
7798
|
{
|
|
7489
|
-
params: {
|
|
7799
|
+
params: { argv: ["ls", "-la"], timeout: 3e4 },
|
|
7490
7800
|
output: "status=0\n\ntotal 24\ndrwxr-xr-x 5 user staff 160 Nov 27 10:00 .\ndrwxr-xr-x 3 user staff 96 Nov 27 09:00 ..\n-rw-r--r-- 1 user staff 1024 Nov 27 10:00 package.json",
|
|
7491
7801
|
comment: "List directory contents with details"
|
|
7492
7802
|
},
|
|
7493
7803
|
{
|
|
7494
|
-
params: {
|
|
7804
|
+
params: { argv: ["echo", "Hello World"], timeout: 3e4 },
|
|
7495
7805
|
output: "status=0\n\nHello World",
|
|
7496
|
-
comment: "
|
|
7806
|
+
comment: "Echo without shell - argument passed directly"
|
|
7497
7807
|
},
|
|
7498
7808
|
{
|
|
7499
|
-
params: {
|
|
7809
|
+
params: { argv: ["cat", "nonexistent.txt"], timeout: 3e4 },
|
|
7500
7810
|
output: "status=1\n\ncat: nonexistent.txt: No such file or directory",
|
|
7501
7811
|
comment: "Command that fails returns non-zero status"
|
|
7502
7812
|
},
|
|
7503
7813
|
{
|
|
7504
|
-
params: {
|
|
7814
|
+
params: { argv: ["pwd"], cwd: "/tmp", timeout: 3e4 },
|
|
7505
7815
|
output: "status=0\n\n/tmp",
|
|
7506
7816
|
comment: "Execute command in a specific directory"
|
|
7817
|
+
},
|
|
7818
|
+
{
|
|
7819
|
+
params: { argv: ["gh", "pr", "review", "123", "--comment", "--body", "Review with `backticks` and 'quotes'"], timeout: 3e4 },
|
|
7820
|
+
output: "status=0\n\n(no output)",
|
|
7821
|
+
comment: "Complex arguments with special characters - no escaping needed"
|
|
7507
7822
|
}
|
|
7508
7823
|
],
|
|
7509
|
-
execute: async ({
|
|
7824
|
+
execute: async ({ argv, cwd, timeout }) => {
|
|
7510
7825
|
const workingDir = cwd ?? process.cwd();
|
|
7826
|
+
if (argv.length === 0) {
|
|
7827
|
+
return "status=1\n\nerror: argv array cannot be empty";
|
|
7828
|
+
}
|
|
7511
7829
|
try {
|
|
7512
|
-
const proc = Bun.spawn(
|
|
7830
|
+
const proc = Bun.spawn(argv, {
|
|
7513
7831
|
cwd: workingDir,
|
|
7514
7832
|
stdout: "pipe",
|
|
7515
7833
|
stderr: "pipe"
|
|
@@ -10417,9 +10735,11 @@ ${issues}`);
|
|
|
10417
10735
|
env.stderr.write(import_chalk7.default.dim("\nExecuting...\n"));
|
|
10418
10736
|
const startTime = Date.now();
|
|
10419
10737
|
let result;
|
|
10738
|
+
let cost;
|
|
10420
10739
|
try {
|
|
10740
|
+
let rawResult;
|
|
10421
10741
|
if (gadget.timeoutMs && gadget.timeoutMs > 0) {
|
|
10422
|
-
|
|
10742
|
+
rawResult = await Promise.race([
|
|
10423
10743
|
Promise.resolve(gadget.execute(params)),
|
|
10424
10744
|
new Promise(
|
|
10425
10745
|
(_, reject) => setTimeout(
|
|
@@ -10429,15 +10749,18 @@ ${issues}`);
|
|
|
10429
10749
|
)
|
|
10430
10750
|
]);
|
|
10431
10751
|
} else {
|
|
10432
|
-
|
|
10752
|
+
rawResult = await Promise.resolve(gadget.execute(params));
|
|
10433
10753
|
}
|
|
10754
|
+
result = typeof rawResult === "string" ? rawResult : rawResult.result;
|
|
10755
|
+
cost = typeof rawResult === "object" ? rawResult.cost : void 0;
|
|
10434
10756
|
} catch (error) {
|
|
10435
10757
|
const message = error instanceof Error ? error.message : String(error);
|
|
10436
10758
|
throw new Error(`Execution failed: ${message}`);
|
|
10437
10759
|
}
|
|
10438
10760
|
const elapsed = Date.now() - startTime;
|
|
10761
|
+
const costInfo = cost !== void 0 && cost > 0 ? ` (Cost: $${cost.toFixed(6)})` : "";
|
|
10439
10762
|
env.stderr.write(import_chalk7.default.green(`
|
|
10440
|
-
\u2713 Completed in ${elapsed}ms
|
|
10763
|
+
\u2713 Completed in ${elapsed}ms${costInfo}
|
|
10441
10764
|
|
|
10442
10765
|
`));
|
|
10443
10766
|
formatOutput(result, options, env.stdout);
|