@jaypie/llm 1.2.10 → 1.2.12

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.
@@ -1128,17 +1128,24 @@ class AnthropicAdapter extends BaseProviderAdapter {
1128
1128
  //
1129
1129
  // API Execution
1130
1130
  //
1131
- async executeRequest(client, request) {
1131
+ async executeRequest(client, request, signal) {
1132
1132
  const anthropic = client;
1133
- return (await anthropic.messages.create(request));
1133
+ try {
1134
+ return (await anthropic.messages.create(request, signal ? { signal } : undefined));
1135
+ }
1136
+ catch (error) {
1137
+ if (signal?.aborted)
1138
+ return undefined;
1139
+ throw error;
1140
+ }
1134
1141
  }
1135
- async *executeStreamRequest(client, request) {
1142
+ async *executeStreamRequest(client, request, signal) {
1136
1143
  const anthropic = client;
1137
1144
  const streamRequest = {
1138
1145
  ...request,
1139
1146
  stream: true,
1140
1147
  };
1141
- const stream = await anthropic.messages.create(streamRequest);
1148
+ const stream = await anthropic.messages.create(streamRequest, signal ? { signal } : undefined);
1142
1149
  // Track current tool call being built
1143
1150
  let currentToolCall = null;
1144
1151
  // Track usage for final chunk
@@ -1568,19 +1575,26 @@ class GeminiAdapter extends BaseProviderAdapter {
1568
1575
  //
1569
1576
  // API Execution
1570
1577
  //
1571
- async executeRequest(client, request) {
1578
+ async executeRequest(client, request, signal) {
1572
1579
  const genAI = client;
1573
1580
  const geminiRequest = request;
1574
- // Cast config to any to bypass strict type checking between our internal types
1575
- // and the SDK's types. The SDK will validate at runtime.
1576
- const response = await genAI.models.generateContent({
1577
- model: geminiRequest.model,
1578
- contents: geminiRequest.contents,
1579
- config: geminiRequest.config,
1580
- });
1581
- return response;
1581
+ try {
1582
+ // Cast config to any to bypass strict type checking between our internal types
1583
+ // and the SDK's types. The SDK will validate at runtime.
1584
+ const response = await genAI.models.generateContent({
1585
+ model: geminiRequest.model,
1586
+ contents: geminiRequest.contents,
1587
+ config: geminiRequest.config,
1588
+ });
1589
+ return response;
1590
+ }
1591
+ catch (error) {
1592
+ if (signal?.aborted)
1593
+ return undefined;
1594
+ throw error;
1595
+ }
1582
1596
  }
1583
- async *executeStreamRequest(client, request) {
1597
+ async *executeStreamRequest(client, request, signal) {
1584
1598
  const genAI = client;
1585
1599
  const geminiRequest = request;
1586
1600
  // Use generateContentStream for streaming
@@ -2230,19 +2244,26 @@ class OpenAiAdapter extends BaseProviderAdapter {
2230
2244
  //
2231
2245
  // API Execution
2232
2246
  //
2233
- async executeRequest(client, request) {
2247
+ async executeRequest(client, request, signal) {
2234
2248
  const openai = client;
2235
- // @ts-expect-error OpenAI SDK types don't match our request format exactly
2236
- return await openai.responses.create(request);
2249
+ try {
2250
+ // @ts-expect-error OpenAI SDK types don't match our request format exactly
2251
+ return await openai.responses.create(request, signal ? { signal } : undefined);
2252
+ }
2253
+ catch (error) {
2254
+ if (signal?.aborted)
2255
+ return undefined;
2256
+ throw error;
2257
+ }
2237
2258
  }
2238
- async *executeStreamRequest(client, request) {
2259
+ async *executeStreamRequest(client, request, signal) {
2239
2260
  const openai = client;
2240
2261
  const baseRequest = request;
2241
2262
  const streamRequest = {
2242
2263
  ...baseRequest,
2243
2264
  stream: true,
2244
2265
  };
2245
- const stream = await openai.responses.create(streamRequest);
2266
+ const stream = await openai.responses.create(streamRequest, signal ? { signal } : undefined);
2246
2267
  // Track current function call being built
2247
2268
  let currentFunctionCall = null;
2248
2269
  // Track usage for final chunk
@@ -2673,19 +2694,26 @@ class OpenRouterAdapter extends BaseProviderAdapter {
2673
2694
  //
2674
2695
  // API Execution
2675
2696
  //
2676
- async executeRequest(client, request) {
2697
+ async executeRequest(client, request, signal) {
2677
2698
  const openRouter = client;
2678
2699
  const openRouterRequest = request;
2679
- const response = await openRouter.chat.send({
2680
- model: openRouterRequest.model,
2681
- messages: openRouterRequest.messages,
2682
- tools: openRouterRequest.tools,
2683
- toolChoice: openRouterRequest.tool_choice,
2684
- user: openRouterRequest.user,
2685
- });
2686
- return response;
2700
+ try {
2701
+ const response = await openRouter.chat.send({
2702
+ model: openRouterRequest.model,
2703
+ messages: openRouterRequest.messages,
2704
+ tools: openRouterRequest.tools,
2705
+ toolChoice: openRouterRequest.tool_choice,
2706
+ user: openRouterRequest.user,
2707
+ }, signal ? { signal } : undefined);
2708
+ return response;
2709
+ }
2710
+ catch (error) {
2711
+ if (signal?.aborted)
2712
+ return undefined;
2713
+ throw error;
2714
+ }
2687
2715
  }
2688
- async *executeStreamRequest(client, request) {
2716
+ async *executeStreamRequest(client, request, signal) {
2689
2717
  const openRouter = client;
2690
2718
  const openRouterRequest = request;
2691
2719
  // Use chat.send with stream: true for streaming responses
@@ -2696,7 +2724,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
2696
2724
  toolChoice: openRouterRequest.tool_choice,
2697
2725
  user: openRouterRequest.user,
2698
2726
  stream: true,
2699
- });
2727
+ }, signal ? { signal } : undefined);
2700
2728
  // Track current tool call being built
2701
2729
  let currentToolCall = null;
2702
2730
  // Track usage for final chunk
@@ -3907,9 +3935,12 @@ class RetryExecutor {
3907
3935
  this.errorClassifier = config.errorClassifier;
3908
3936
  }
3909
3937
  /**
3910
- * Execute an operation with retry logic
3938
+ * Execute an operation with retry logic.
3939
+ * Each attempt receives an AbortSignal. On failure, the signal is aborted
3940
+ * before sleeping — this kills lingering socket callbacks from the previous
3941
+ * request and prevents stale async errors from escaping the retry loop.
3911
3942
  *
3912
- * @param operation - The async operation to execute
3943
+ * @param operation - The async operation to execute (receives AbortSignal)
3913
3944
  * @param options - Execution options including context and hooks
3914
3945
  * @returns The result of the operation
3915
3946
  * @throws BadGatewayError if all retries are exhausted or error is not retryable
@@ -3917,14 +3948,17 @@ class RetryExecutor {
3917
3948
  async execute(operation, options) {
3918
3949
  let attempt = 0;
3919
3950
  while (true) {
3951
+ const controller = new AbortController();
3920
3952
  try {
3921
- const result = await operation();
3953
+ const result = await operation(controller.signal);
3922
3954
  if (attempt > 0) {
3923
3955
  log$1.debug(`API call succeeded after ${attempt} retries`);
3924
3956
  }
3925
3957
  return result;
3926
3958
  }
3927
3959
  catch (error) {
3960
+ // Abort the previous request to kill lingering socket callbacks
3961
+ controller.abort("retry");
3928
3962
  // Check if we've exhausted retries
3929
3963
  if (!this.policy.shouldRetry(attempt)) {
3930
3964
  log$1.error(`API call failed after ${this.policy.maxRetries} retries`);
@@ -3964,7 +3998,19 @@ class RetryExecutor {
3964
3998
  providerRequest: options.context.providerRequest,
3965
3999
  error,
3966
4000
  });
3967
- await kit.sleep(delay);
4001
+ // Guard against stale socket errors that fire during sleep
4002
+ const staleHandler = (reason) => {
4003
+ if (isTransientNetworkError(reason)) {
4004
+ log$1.trace("Suppressed stale socket error during retry sleep");
4005
+ }
4006
+ };
4007
+ process.on("unhandledRejection", staleHandler);
4008
+ try {
4009
+ await kit.sleep(delay);
4010
+ }
4011
+ finally {
4012
+ process.removeListener("unhandledRejection", staleHandler);
4013
+ }
3968
4014
  attempt++;
3969
4015
  }
3970
4016
  }
@@ -4149,7 +4195,7 @@ class OperateLoop {
4149
4195
  providerRequest,
4150
4196
  });
4151
4197
  // Execute with retry (RetryExecutor handles error hooks and throws appropriate errors)
4152
- const response = await retryExecutor.execute(() => this.adapter.executeRequest(this.client, providerRequest), {
4198
+ const response = await retryExecutor.execute((signal) => this.adapter.executeRequest(this.client, providerRequest, signal), {
4153
4199
  context: {
4154
4200
  input: state.currentInput,
4155
4201
  options,
@@ -4542,9 +4588,10 @@ class StreamLoop {
4542
4588
  let attempt = 0;
4543
4589
  let chunksYielded = false;
4544
4590
  while (true) {
4591
+ const controller = new AbortController();
4545
4592
  try {
4546
4593
  // Execute streaming request
4547
- const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest);
4594
+ const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest, controller.signal);
4548
4595
  for await (const chunk of streamGenerator) {
4549
4596
  // Pass through text chunks
4550
4597
  if (chunk.type === exports.LlmStreamChunkType.Text) {
@@ -4579,6 +4626,8 @@ class StreamLoop {
4579
4626
  break;
4580
4627
  }
4581
4628
  catch (error) {
4629
+ // Abort the previous request to kill lingering socket callbacks
4630
+ controller.abort("retry");
4582
4631
  // If chunks were already yielded, we can't transparently retry
4583
4632
  if (chunksYielded) {
4584
4633
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -4605,7 +4654,19 @@ class StreamLoop {
4605
4654
  const delay = this.retryPolicy.getDelayForAttempt(attempt);
4606
4655
  log$1.warn(`Stream request failed. Retrying in ${delay}ms...`);
4607
4656
  log$1.var({ error });
4608
- await kit.sleep(delay);
4657
+ // Guard against stale socket errors that fire during sleep
4658
+ const staleHandler = (reason) => {
4659
+ if (isTransientNetworkError(reason)) {
4660
+ log$1.trace("Suppressed stale socket error during retry sleep");
4661
+ }
4662
+ };
4663
+ process.on("unhandledRejection", staleHandler);
4664
+ try {
4665
+ await kit.sleep(delay);
4666
+ }
4667
+ finally {
4668
+ process.removeListener("unhandledRejection", staleHandler);
4669
+ }
4609
4670
  attempt++;
4610
4671
  }
4611
4672
  }