llmist 15.14.2 → 15.15.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/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,9 @@ 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;
13452
13489
  constructor(options) {
13453
13490
  this.iteration = options.iteration;
13454
13491
  this.registry = options.registry;
@@ -13462,6 +13499,7 @@ var init_stream_processor = __esm({
13462
13499
  this.priorFailedInvocations = options.priorFailedInvocations ?? /* @__PURE__ */ new Set();
13463
13500
  this.subagentConfig = options.subagentConfig;
13464
13501
  this.parentObservers = options.parentObservers;
13502
+ this.maxGadgetsPerResponse = options.maxGadgetsPerResponse ?? 0;
13465
13503
  this.parser = new GadgetCallParser({
13466
13504
  startPrefix: options.gadgetStartPrefix,
13467
13505
  endPrefix: options.gadgetEndPrefix,
@@ -13757,6 +13795,15 @@ var init_stream_processor = __esm({
13757
13795
  this.gadgetsAwaitingDependencies.set(call.invocationId, call);
13758
13796
  return;
13759
13797
  }
13798
+ const limitCheckGen2 = this.checkGadgetLimitExceeded(call);
13799
+ let limitResult2 = await limitCheckGen2.next();
13800
+ while (!limitResult2.done) {
13801
+ yield limitResult2.value;
13802
+ limitResult2 = await limitCheckGen2.next();
13803
+ }
13804
+ if (limitResult2.value === true) {
13805
+ return;
13806
+ }
13760
13807
  for await (const evt of this.executeGadgetGenerator(call)) {
13761
13808
  yield evt;
13762
13809
  }
@@ -13765,6 +13812,15 @@ var init_stream_processor = __esm({
13765
13812
  }
13766
13813
  return;
13767
13814
  }
13815
+ const limitCheckGen = this.checkGadgetLimitExceeded(call);
13816
+ let limitResult = await limitCheckGen.next();
13817
+ while (!limitResult.done) {
13818
+ yield limitResult.value;
13819
+ limitResult = await limitCheckGen.next();
13820
+ }
13821
+ if (limitResult.value === true) {
13822
+ return;
13823
+ }
13768
13824
  const limit = this.getConcurrencyLimit(call.gadgetName);
13769
13825
  const activeCount = this.activeCountByGadget.get(call.gadgetName) ?? 0;
13770
13826
  if (limit > 0 && activeCount >= limit) {
@@ -14200,6 +14256,80 @@ var init_stream_processor = __esm({
14200
14256
  }
14201
14257
  return events;
14202
14258
  }
14259
+ /**
14260
+ * Check if gadget execution should be skipped due to maxGadgetsPerResponse limit.
14261
+ * If limit is exceeded, yields skip events and returns true.
14262
+ * If execution can proceed, increments counter and returns false.
14263
+ *
14264
+ * @returns true if gadget should be skipped, false if execution can proceed
14265
+ */
14266
+ async *checkGadgetLimitExceeded(call) {
14267
+ if (this.maxGadgetsPerResponse <= 0) {
14268
+ this.gadgetStartedCount++;
14269
+ return false;
14270
+ }
14271
+ if (this.gadgetStartedCount >= this.maxGadgetsPerResponse) {
14272
+ const errorMessage = `Gadget limit (${this.maxGadgetsPerResponse}) exceeded. Consider calling fewer gadgets per response.`;
14273
+ this.logger.info("Gadget skipped due to maxGadgetsPerResponse limit", {
14274
+ gadgetName: call.gadgetName,
14275
+ invocationId: call.invocationId,
14276
+ limit: this.maxGadgetsPerResponse,
14277
+ currentCount: this.gadgetStartedCount
14278
+ });
14279
+ this.failedInvocations.add(call.invocationId);
14280
+ if (this.tree) {
14281
+ const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
14282
+ if (gadgetNode) {
14283
+ this.tree.skipGadget(
14284
+ gadgetNode.id,
14285
+ "maxGadgetsPerResponse",
14286
+ errorMessage,
14287
+ "limit_exceeded"
14288
+ );
14289
+ }
14290
+ }
14291
+ const skipEvent = {
14292
+ type: "gadget_skipped",
14293
+ gadgetName: call.gadgetName,
14294
+ invocationId: call.invocationId,
14295
+ parameters: call.parameters ?? {},
14296
+ failedDependency: "maxGadgetsPerResponse",
14297
+ failedDependencyError: errorMessage
14298
+ };
14299
+ yield skipEvent;
14300
+ const limitSkipNode = this.tree?.getNodeByInvocationId(call.invocationId);
14301
+ const limitSkipSubagentContext = this.tree && limitSkipNode ? getSubagentContextForNode(this.tree, limitSkipNode.id) : void 0;
14302
+ if (this.hooks.observers?.onGadgetSkipped) {
14303
+ const context = {
14304
+ iteration: this.iteration,
14305
+ gadgetName: call.gadgetName,
14306
+ invocationId: call.invocationId,
14307
+ parameters: call.parameters ?? {},
14308
+ failedDependency: "maxGadgetsPerResponse",
14309
+ failedDependencyError: errorMessage,
14310
+ logger: this.logger,
14311
+ subagentContext: limitSkipSubagentContext
14312
+ };
14313
+ await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
14314
+ }
14315
+ if (this.parentObservers?.onGadgetSkipped) {
14316
+ const context = {
14317
+ iteration: this.iteration,
14318
+ gadgetName: call.gadgetName,
14319
+ invocationId: call.invocationId,
14320
+ parameters: call.parameters ?? {},
14321
+ failedDependency: "maxGadgetsPerResponse",
14322
+ failedDependencyError: errorMessage,
14323
+ logger: this.logger,
14324
+ subagentContext: limitSkipSubagentContext
14325
+ };
14326
+ await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
14327
+ }
14328
+ return true;
14329
+ }
14330
+ this.gadgetStartedCount++;
14331
+ return false;
14332
+ }
14203
14333
  /**
14204
14334
  * Process pending gadgets whose dependencies are now satisfied.
14205
14335
  * Yields events in real-time as gadgets complete.
@@ -14246,6 +14376,15 @@ var init_stream_processor = __esm({
14246
14376
  invocationIds: readyToExecute.map((c) => c.invocationId)
14247
14377
  });
14248
14378
  for (const call of readyToExecute) {
14379
+ const limitCheckGen = this.checkGadgetLimitExceeded(call);
14380
+ let limitResult = await limitCheckGen.next();
14381
+ while (!limitResult.done) {
14382
+ yield limitResult.value;
14383
+ limitResult = await limitCheckGen.next();
14384
+ }
14385
+ if (limitResult.value === true) {
14386
+ continue;
14387
+ }
14249
14388
  for await (const evt of this.executeGadgetGenerator(call)) {
14250
14389
  yield evt;
14251
14390
  }
@@ -14258,6 +14397,15 @@ var init_stream_processor = __esm({
14258
14397
  const eventSets = await Promise.all(
14259
14398
  readyToExecute.map(async (call) => {
14260
14399
  const events = [];
14400
+ const limitCheckGen = this.checkGadgetLimitExceeded(call);
14401
+ let limitResult = await limitCheckGen.next();
14402
+ while (!limitResult.done) {
14403
+ events.push(limitResult.value);
14404
+ limitResult = await limitCheckGen.next();
14405
+ }
14406
+ if (limitResult.value === true) {
14407
+ return events;
14408
+ }
14261
14409
  for await (const evt of this.executeGadgetGenerator(call)) {
14262
14410
  events.push(evt);
14263
14411
  }
@@ -14443,6 +14591,8 @@ var init_agent = __esm({
14443
14591
  // Subagent configuration
14444
14592
  agentContextConfig;
14445
14593
  subagentConfig;
14594
+ // Gadget limiting
14595
+ maxGadgetsPerResponse;
14446
14596
  // Counter for generating synthetic invocation IDs for wrapped text content
14447
14597
  syntheticInvocationCounter = 0;
14448
14598
  // Cross-iteration dependency tracking - allows gadgets to depend on results from prior iterations
@@ -14543,6 +14693,7 @@ var init_agent = __esm({
14543
14693
  temperature: this.temperature
14544
14694
  };
14545
14695
  this.subagentConfig = options.subagentConfig;
14696
+ this.maxGadgetsPerResponse = options.maxGadgetsPerResponse ?? 0;
14546
14697
  this.tree = options.parentTree ?? new ExecutionTree();
14547
14698
  this.parentNodeId = options.parentNodeId ?? null;
14548
14699
  this.baseDepth = options.baseDepth ?? 0;
@@ -14830,7 +14981,9 @@ var init_agent = __esm({
14830
14981
  parentObservers: this.parentObservers,
14831
14982
  // Shared rate limit tracker and retry config for subagents
14832
14983
  rateLimitTracker: this.rateLimitTracker,
14833
- retryConfig: this.retryConfig
14984
+ retryConfig: this.retryConfig,
14985
+ // Gadget limiting
14986
+ maxGadgetsPerResponse: this.maxGadgetsPerResponse
14834
14987
  });
14835
14988
  for await (const event of processor.process(stream2)) {
14836
14989
  if (event.type === "stream_complete") {