llmist 15.14.2 → 15.15.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/dist/index.cjs +166 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -2
- package/dist/index.d.ts +41 -2
- package/dist/index.js +166 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -10625,6 +10625,7 @@ var init_builder = __esm({
|
|
|
10625
10625
|
textWithGadgetsHandler;
|
|
10626
10626
|
defaultGadgetTimeoutMs;
|
|
10627
10627
|
gadgetExecutionMode;
|
|
10628
|
+
maxGadgetsPerResponse;
|
|
10628
10629
|
gadgetOutputLimit;
|
|
10629
10630
|
gadgetOutputLimitPercent;
|
|
10630
10631
|
compactionConfig;
|
|
@@ -11025,6 +11026,37 @@ var init_builder = __esm({
|
|
|
11025
11026
|
this.gadgetExecutionMode = mode;
|
|
11026
11027
|
return this;
|
|
11027
11028
|
}
|
|
11029
|
+
/**
|
|
11030
|
+
* Set the maximum number of gadgets to execute per LLM response.
|
|
11031
|
+
*
|
|
11032
|
+
* When the limit is reached, remaining gadgets are skipped with an informative
|
|
11033
|
+
* message visible to the LLM, allowing it to adjust on the next iteration.
|
|
11034
|
+
* Gadgets already in-flight (executing in parallel) are allowed to complete.
|
|
11035
|
+
*
|
|
11036
|
+
* @param max - Maximum gadgets per response (0 = unlimited, default)
|
|
11037
|
+
* @returns This builder for chaining
|
|
11038
|
+
* @throws {Error} If max is negative or non-integer
|
|
11039
|
+
*
|
|
11040
|
+
* @example
|
|
11041
|
+
* ```typescript
|
|
11042
|
+
* // Limit to 5 gadgets per response
|
|
11043
|
+
* LLMist.createAgent()
|
|
11044
|
+
* .withModel("sonnet")
|
|
11045
|
+
* .withGadgets(ReadFile, WriteFile, Search)
|
|
11046
|
+
* .withMaxGadgetsPerResponse(5)
|
|
11047
|
+
* .ask("Process these files...");
|
|
11048
|
+
* ```
|
|
11049
|
+
*/
|
|
11050
|
+
withMaxGadgetsPerResponse(max) {
|
|
11051
|
+
if (max < 0) {
|
|
11052
|
+
throw new Error("maxGadgetsPerResponse must be a non-negative number");
|
|
11053
|
+
}
|
|
11054
|
+
if (!Number.isInteger(max)) {
|
|
11055
|
+
throw new Error("maxGadgetsPerResponse must be an integer");
|
|
11056
|
+
}
|
|
11057
|
+
this.maxGadgetsPerResponse = max;
|
|
11058
|
+
return this;
|
|
11059
|
+
}
|
|
11028
11060
|
/**
|
|
11029
11061
|
* Enable or disable gadget output limiting.
|
|
11030
11062
|
*
|
|
@@ -11623,6 +11655,7 @@ ${endPrefix}`
|
|
|
11623
11655
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
11624
11656
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
11625
11657
|
gadgetExecutionMode: this.gadgetExecutionMode,
|
|
11658
|
+
maxGadgetsPerResponse: this.maxGadgetsPerResponse,
|
|
11626
11659
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
11627
11660
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
11628
11661
|
compactionConfig: this.compactionConfig,
|
|
@@ -11812,6 +11845,7 @@ ${endPrefix}`
|
|
|
11812
11845
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
11813
11846
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
11814
11847
|
gadgetExecutionMode: this.gadgetExecutionMode,
|
|
11848
|
+
maxGadgetsPerResponse: this.maxGadgetsPerResponse,
|
|
11815
11849
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
11816
11850
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
11817
11851
|
compactionConfig: this.compactionConfig,
|
|
@@ -13449,6 +13483,10 @@ var init_stream_processor = __esm({
|
|
|
13449
13483
|
priorFailedInvocations;
|
|
13450
13484
|
// Parent observer hooks for subagent visibility
|
|
13451
13485
|
parentObservers;
|
|
13486
|
+
// Gadget limiting per response
|
|
13487
|
+
maxGadgetsPerResponse;
|
|
13488
|
+
gadgetStartedCount = 0;
|
|
13489
|
+
limitExceeded = false;
|
|
13452
13490
|
constructor(options) {
|
|
13453
13491
|
this.iteration = options.iteration;
|
|
13454
13492
|
this.registry = options.registry;
|
|
@@ -13462,6 +13500,7 @@ var init_stream_processor = __esm({
|
|
|
13462
13500
|
this.priorFailedInvocations = options.priorFailedInvocations ?? /* @__PURE__ */ new Set();
|
|
13463
13501
|
this.subagentConfig = options.subagentConfig;
|
|
13464
13502
|
this.parentObservers = options.parentObservers;
|
|
13503
|
+
this.maxGadgetsPerResponse = options.maxGadgetsPerResponse ?? 0;
|
|
13465
13504
|
this.parser = new GadgetCallParser({
|
|
13466
13505
|
startPrefix: options.gadgetStartPrefix,
|
|
13467
13506
|
endPrefix: options.gadgetEndPrefix,
|
|
@@ -13575,6 +13614,10 @@ var init_stream_processor = __esm({
|
|
|
13575
13614
|
}
|
|
13576
13615
|
}
|
|
13577
13616
|
}
|
|
13617
|
+
if (this.limitExceeded) {
|
|
13618
|
+
this.logger.info("Breaking stream loop due to gadget limit");
|
|
13619
|
+
break;
|
|
13620
|
+
}
|
|
13578
13621
|
}
|
|
13579
13622
|
yield { type: "llm_response_end", finishReason, usage };
|
|
13580
13623
|
for (const event of this.parser.finalize()) {
|
|
@@ -13678,6 +13721,9 @@ var init_stream_processor = __esm({
|
|
|
13678
13721
|
* enabling real-time UI feedback.
|
|
13679
13722
|
*/
|
|
13680
13723
|
async *processGadgetCallGenerator(call) {
|
|
13724
|
+
if (this.limitExceeded) {
|
|
13725
|
+
return;
|
|
13726
|
+
}
|
|
13681
13727
|
yield { type: "gadget_call", call };
|
|
13682
13728
|
if (this.tree) {
|
|
13683
13729
|
this.tree.addGadget({
|
|
@@ -13757,6 +13803,15 @@ var init_stream_processor = __esm({
|
|
|
13757
13803
|
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
13758
13804
|
return;
|
|
13759
13805
|
}
|
|
13806
|
+
const limitCheckGen2 = this.checkGadgetLimitExceeded(call);
|
|
13807
|
+
let limitResult2 = await limitCheckGen2.next();
|
|
13808
|
+
while (!limitResult2.done) {
|
|
13809
|
+
yield limitResult2.value;
|
|
13810
|
+
limitResult2 = await limitCheckGen2.next();
|
|
13811
|
+
}
|
|
13812
|
+
if (limitResult2.value === true) {
|
|
13813
|
+
return;
|
|
13814
|
+
}
|
|
13760
13815
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
13761
13816
|
yield evt;
|
|
13762
13817
|
}
|
|
@@ -13765,6 +13820,15 @@ var init_stream_processor = __esm({
|
|
|
13765
13820
|
}
|
|
13766
13821
|
return;
|
|
13767
13822
|
}
|
|
13823
|
+
const limitCheckGen = this.checkGadgetLimitExceeded(call);
|
|
13824
|
+
let limitResult = await limitCheckGen.next();
|
|
13825
|
+
while (!limitResult.done) {
|
|
13826
|
+
yield limitResult.value;
|
|
13827
|
+
limitResult = await limitCheckGen.next();
|
|
13828
|
+
}
|
|
13829
|
+
if (limitResult.value === true) {
|
|
13830
|
+
return;
|
|
13831
|
+
}
|
|
13768
13832
|
const limit = this.getConcurrencyLimit(call.gadgetName);
|
|
13769
13833
|
const activeCount = this.activeCountByGadget.get(call.gadgetName) ?? 0;
|
|
13770
13834
|
if (limit > 0 && activeCount >= limit) {
|
|
@@ -14200,6 +14264,81 @@ var init_stream_processor = __esm({
|
|
|
14200
14264
|
}
|
|
14201
14265
|
return events;
|
|
14202
14266
|
}
|
|
14267
|
+
/**
|
|
14268
|
+
* Check if gadget execution should be skipped due to maxGadgetsPerResponse limit.
|
|
14269
|
+
* If limit is exceeded, yields skip events and returns true.
|
|
14270
|
+
* If execution can proceed, increments counter and returns false.
|
|
14271
|
+
*
|
|
14272
|
+
* @returns true if gadget should be skipped, false if execution can proceed
|
|
14273
|
+
*/
|
|
14274
|
+
async *checkGadgetLimitExceeded(call) {
|
|
14275
|
+
if (this.maxGadgetsPerResponse <= 0) {
|
|
14276
|
+
this.gadgetStartedCount++;
|
|
14277
|
+
return false;
|
|
14278
|
+
}
|
|
14279
|
+
if (this.gadgetStartedCount >= this.maxGadgetsPerResponse) {
|
|
14280
|
+
const errorMessage = `Gadget limit (${this.maxGadgetsPerResponse}) exceeded. Consider calling fewer gadgets per response.`;
|
|
14281
|
+
this.limitExceeded = true;
|
|
14282
|
+
this.logger.info("Gadget limit exceeded, stopping stream processing", {
|
|
14283
|
+
gadgetName: call.gadgetName,
|
|
14284
|
+
invocationId: call.invocationId,
|
|
14285
|
+
limit: this.maxGadgetsPerResponse,
|
|
14286
|
+
currentCount: this.gadgetStartedCount
|
|
14287
|
+
});
|
|
14288
|
+
this.failedInvocations.add(call.invocationId);
|
|
14289
|
+
if (this.tree) {
|
|
14290
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
14291
|
+
if (gadgetNode) {
|
|
14292
|
+
this.tree.skipGadget(
|
|
14293
|
+
gadgetNode.id,
|
|
14294
|
+
"maxGadgetsPerResponse",
|
|
14295
|
+
errorMessage,
|
|
14296
|
+
"limit_exceeded"
|
|
14297
|
+
);
|
|
14298
|
+
}
|
|
14299
|
+
}
|
|
14300
|
+
const skipEvent = {
|
|
14301
|
+
type: "gadget_skipped",
|
|
14302
|
+
gadgetName: call.gadgetName,
|
|
14303
|
+
invocationId: call.invocationId,
|
|
14304
|
+
parameters: call.parameters ?? {},
|
|
14305
|
+
failedDependency: "maxGadgetsPerResponse",
|
|
14306
|
+
failedDependencyError: errorMessage
|
|
14307
|
+
};
|
|
14308
|
+
yield skipEvent;
|
|
14309
|
+
const limitSkipNode = this.tree?.getNodeByInvocationId(call.invocationId);
|
|
14310
|
+
const limitSkipSubagentContext = this.tree && limitSkipNode ? getSubagentContextForNode(this.tree, limitSkipNode.id) : void 0;
|
|
14311
|
+
if (this.hooks.observers?.onGadgetSkipped) {
|
|
14312
|
+
const context = {
|
|
14313
|
+
iteration: this.iteration,
|
|
14314
|
+
gadgetName: call.gadgetName,
|
|
14315
|
+
invocationId: call.invocationId,
|
|
14316
|
+
parameters: call.parameters ?? {},
|
|
14317
|
+
failedDependency: "maxGadgetsPerResponse",
|
|
14318
|
+
failedDependencyError: errorMessage,
|
|
14319
|
+
logger: this.logger,
|
|
14320
|
+
subagentContext: limitSkipSubagentContext
|
|
14321
|
+
};
|
|
14322
|
+
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14323
|
+
}
|
|
14324
|
+
if (this.parentObservers?.onGadgetSkipped) {
|
|
14325
|
+
const context = {
|
|
14326
|
+
iteration: this.iteration,
|
|
14327
|
+
gadgetName: call.gadgetName,
|
|
14328
|
+
invocationId: call.invocationId,
|
|
14329
|
+
parameters: call.parameters ?? {},
|
|
14330
|
+
failedDependency: "maxGadgetsPerResponse",
|
|
14331
|
+
failedDependencyError: errorMessage,
|
|
14332
|
+
logger: this.logger,
|
|
14333
|
+
subagentContext: limitSkipSubagentContext
|
|
14334
|
+
};
|
|
14335
|
+
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14336
|
+
}
|
|
14337
|
+
return true;
|
|
14338
|
+
}
|
|
14339
|
+
this.gadgetStartedCount++;
|
|
14340
|
+
return false;
|
|
14341
|
+
}
|
|
14203
14342
|
/**
|
|
14204
14343
|
* Process pending gadgets whose dependencies are now satisfied.
|
|
14205
14344
|
* Yields events in real-time as gadgets complete.
|
|
@@ -14208,6 +14347,9 @@ var init_stream_processor = __esm({
|
|
|
14208
14347
|
* but results are yielded as they become available.
|
|
14209
14348
|
*/
|
|
14210
14349
|
async *processPendingGadgetsGenerator() {
|
|
14350
|
+
if (this.limitExceeded) {
|
|
14351
|
+
return;
|
|
14352
|
+
}
|
|
14211
14353
|
let progress = true;
|
|
14212
14354
|
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
14213
14355
|
progress = false;
|
|
@@ -14246,6 +14388,15 @@ var init_stream_processor = __esm({
|
|
|
14246
14388
|
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
14247
14389
|
});
|
|
14248
14390
|
for (const call of readyToExecute) {
|
|
14391
|
+
const limitCheckGen = this.checkGadgetLimitExceeded(call);
|
|
14392
|
+
let limitResult = await limitCheckGen.next();
|
|
14393
|
+
while (!limitResult.done) {
|
|
14394
|
+
yield limitResult.value;
|
|
14395
|
+
limitResult = await limitCheckGen.next();
|
|
14396
|
+
}
|
|
14397
|
+
if (limitResult.value === true) {
|
|
14398
|
+
continue;
|
|
14399
|
+
}
|
|
14249
14400
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
14250
14401
|
yield evt;
|
|
14251
14402
|
}
|
|
@@ -14258,6 +14409,15 @@ var init_stream_processor = __esm({
|
|
|
14258
14409
|
const eventSets = await Promise.all(
|
|
14259
14410
|
readyToExecute.map(async (call) => {
|
|
14260
14411
|
const events = [];
|
|
14412
|
+
const limitCheckGen = this.checkGadgetLimitExceeded(call);
|
|
14413
|
+
let limitResult = await limitCheckGen.next();
|
|
14414
|
+
while (!limitResult.done) {
|
|
14415
|
+
events.push(limitResult.value);
|
|
14416
|
+
limitResult = await limitCheckGen.next();
|
|
14417
|
+
}
|
|
14418
|
+
if (limitResult.value === true) {
|
|
14419
|
+
return events;
|
|
14420
|
+
}
|
|
14261
14421
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
14262
14422
|
events.push(evt);
|
|
14263
14423
|
}
|
|
@@ -14443,6 +14603,8 @@ var init_agent = __esm({
|
|
|
14443
14603
|
// Subagent configuration
|
|
14444
14604
|
agentContextConfig;
|
|
14445
14605
|
subagentConfig;
|
|
14606
|
+
// Gadget limiting
|
|
14607
|
+
maxGadgetsPerResponse;
|
|
14446
14608
|
// Counter for generating synthetic invocation IDs for wrapped text content
|
|
14447
14609
|
syntheticInvocationCounter = 0;
|
|
14448
14610
|
// Cross-iteration dependency tracking - allows gadgets to depend on results from prior iterations
|
|
@@ -14543,6 +14705,7 @@ var init_agent = __esm({
|
|
|
14543
14705
|
temperature: this.temperature
|
|
14544
14706
|
};
|
|
14545
14707
|
this.subagentConfig = options.subagentConfig;
|
|
14708
|
+
this.maxGadgetsPerResponse = options.maxGadgetsPerResponse ?? 0;
|
|
14546
14709
|
this.tree = options.parentTree ?? new ExecutionTree();
|
|
14547
14710
|
this.parentNodeId = options.parentNodeId ?? null;
|
|
14548
14711
|
this.baseDepth = options.baseDepth ?? 0;
|
|
@@ -14830,7 +14993,9 @@ var init_agent = __esm({
|
|
|
14830
14993
|
parentObservers: this.parentObservers,
|
|
14831
14994
|
// Shared rate limit tracker and retry config for subagents
|
|
14832
14995
|
rateLimitTracker: this.rateLimitTracker,
|
|
14833
|
-
retryConfig: this.retryConfig
|
|
14996
|
+
retryConfig: this.retryConfig,
|
|
14997
|
+
// Gadget limiting
|
|
14998
|
+
maxGadgetsPerResponse: this.maxGadgetsPerResponse
|
|
14834
14999
|
});
|
|
14835
15000
|
for await (const event of processor.process(stream2)) {
|
|
14836
15001
|
if (event.type === "stream_complete") {
|