llmist 15.4.1 → 15.6.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
@@ -186,7 +186,31 @@ var init_execution_tree = __esm({
186
186
  });
187
187
  }
188
188
  /**
189
- * Complete an LLM call node.
189
+ * Mark an LLM call's response as ended (tokens stopped).
190
+ *
191
+ * Called when the LLM stream ends, before gadget execution completes.
192
+ * Use this event to track "LLM thinking time" separately from gadget execution.
193
+ *
194
+ * @param nodeId - The LLM call node ID
195
+ * @param params - Response end parameters (finishReason, usage)
196
+ */
197
+ endLLMResponse(nodeId, params) {
198
+ const node = this.nodes.get(nodeId);
199
+ if (!node || node.type !== "llm_call") {
200
+ return;
201
+ }
202
+ const llmNode = node;
203
+ this.emit({
204
+ type: "llm_response_end",
205
+ ...this.createBaseEventProps(node),
206
+ iteration: llmNode.iteration,
207
+ model: llmNode.model,
208
+ finishReason: params.finishReason,
209
+ usage: params.usage
210
+ });
211
+ }
212
+ /**
213
+ * Complete an LLM call node (after all gadgets finish).
190
214
  */
191
215
  completeLLMCall(nodeId, params) {
192
216
  const node = this.nodes.get(nodeId);
@@ -2631,6 +2655,32 @@ var init_gadget = __esm({
2631
2655
  * while maintaining runtime compatibility.
2632
2656
  */
2633
2657
  examples;
2658
+ /**
2659
+ * Maximum number of concurrent executions allowed for this gadget.
2660
+ * Use this to prevent race conditions in gadgets that modify shared state.
2661
+ *
2662
+ * - `1` = Sequential execution (only one instance runs at a time)
2663
+ * - `0` or `undefined` = Unlimited concurrency (default)
2664
+ * - `N > 1` = At most N concurrent executions
2665
+ *
2666
+ * This property sets a safety floor: external configuration (SubagentConfig)
2667
+ * can only make concurrency MORE restrictive, never less. For example, if
2668
+ * a gadget declares `maxConcurrent: 1`, external config cannot override it
2669
+ * to allow parallel execution.
2670
+ *
2671
+ * @example
2672
+ * ```typescript
2673
+ * // File writer that must run sequentially to avoid race conditions
2674
+ * class WriteFile extends Gadget({
2675
+ * description: 'Writes content to a file',
2676
+ * schema: z.object({ path: z.string(), content: z.string() }),
2677
+ * maxConcurrent: 1, // Sequential - prevents race conditions
2678
+ * }) {
2679
+ * execute(params: this['params']) { ... }
2680
+ * }
2681
+ * ```
2682
+ */
2683
+ maxConcurrent;
2634
2684
  /**
2635
2685
  * Throws an AbortException if the execution has been aborted.
2636
2686
  *
@@ -2834,6 +2884,7 @@ function createGadget(config) {
2834
2884
  parameterSchema = config.schema;
2835
2885
  timeoutMs = config.timeoutMs;
2836
2886
  examples = config.examples;
2887
+ maxConcurrent = config.maxConcurrent;
2837
2888
  execute(params, ctx) {
2838
2889
  return config.execute(params, ctx);
2839
2890
  }
@@ -10967,6 +11018,7 @@ function Gadget(config) {
10967
11018
  name = config.name;
10968
11019
  timeoutMs = config.timeoutMs;
10969
11020
  examples = config.examples;
11021
+ maxConcurrent = config.maxConcurrent;
10970
11022
  /**
10971
11023
  * Type helper property for accessing inferred parameter type.
10972
11024
  * This is used in the execute method signature: `execute(params: this['params'])`
@@ -11265,7 +11317,8 @@ var init_executor = __esm({
11265
11317
  storedMedia
11266
11318
  };
11267
11319
  } catch (error) {
11268
- if (error instanceof TaskCompletionSignal) {
11320
+ const isTaskCompletionSignal = error instanceof Error && error.name === "TaskCompletionSignal";
11321
+ if (isTaskCompletionSignal) {
11269
11322
  this.logger.info("Gadget requested loop termination", {
11270
11323
  gadgetName: call.gadgetName,
11271
11324
  message: error.message
@@ -11818,6 +11871,7 @@ var init_stream_processor = __esm({
11818
11871
  }
11819
11872
  }
11820
11873
  }
11874
+ yield { type: "llm_response_end", finishReason, usage };
11821
11875
  for (const event of this.parser.finalize()) {
11822
11876
  for await (const processedEvent of this.processEventGenerator(event)) {
11823
11877
  yield processedEvent;
@@ -12022,12 +12076,23 @@ var init_stream_processor = __esm({
12022
12076
  this.startGadgetWithConcurrencyTracking(call);
12023
12077
  }
12024
12078
  /**
12025
- * Get the concurrency limit for a gadget from subagent config.
12026
- * Returns 0 if no limit is set (unlimited).
12079
+ * Get the effective concurrency limit for a gadget.
12080
+ * Uses "most restrictive wins" strategy: the lowest non-zero value from
12081
+ * external config (SubagentConfig) and gadget's intrinsic maxConcurrent.
12082
+ *
12083
+ * This ensures gadget authors can set safety floors (e.g., maxConcurrent: 1
12084
+ * for file writers) that cannot be weakened by external configuration.
12085
+ *
12086
+ * @returns 0 if unlimited, otherwise the effective limit
12027
12087
  */
12028
12088
  getConcurrencyLimit(gadgetName) {
12029
- const config = this.subagentConfig?.[gadgetName];
12030
- return config?.maxConcurrent ?? 0;
12089
+ const configLimit = this.subagentConfig?.[gadgetName]?.maxConcurrent;
12090
+ const gadget = this.registry.get(gadgetName);
12091
+ const gadgetLimit = gadget?.maxConcurrent;
12092
+ const config = configLimit || Number.POSITIVE_INFINITY;
12093
+ const intrinsic = gadgetLimit || Number.POSITIVE_INFINITY;
12094
+ const effective = Math.min(config, intrinsic);
12095
+ return effective === Number.POSITIVE_INFINITY ? 0 : effective;
12031
12096
  }
12032
12097
  /**
12033
12098
  * Start a gadget execution with concurrency tracking.
@@ -13039,6 +13104,11 @@ var init_agent = __esm({
13039
13104
  } else if (event.type === "gadget_result") {
13040
13105
  gadgetCallCount++;
13041
13106
  gadgetResults.push(event);
13107
+ } else if (event.type === "llm_response_end") {
13108
+ this.tree.endLLMResponse(currentLLMNodeId, {
13109
+ finishReason: event.finishReason,
13110
+ usage: event.usage
13111
+ });
13042
13112
  }
13043
13113
  yield event;
13044
13114
  }