@jaypie/llm 1.2.10 → 1.2.11

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.
@@ -88,8 +88,8 @@ export declare class OpenRouterAdapter extends BaseProviderAdapter {
88
88
  buildRequest(request: OperateRequest): OpenRouterRequest;
89
89
  formatTools(toolkit: Toolkit, outputSchema?: JsonObject): ProviderToolDefinition[];
90
90
  formatOutputSchema(schema: JsonObject | NaturalSchema | z.ZodType): JsonObject;
91
- executeRequest(client: unknown, request: unknown): Promise<OpenRouterResponse>;
92
- executeStreamRequest(client: unknown, request: unknown): AsyncIterable<LlmStreamChunk>;
91
+ executeRequest(client: unknown, request: unknown, signal?: AbortSignal): Promise<OpenRouterResponse>;
92
+ executeStreamRequest(client: unknown, request: unknown, signal?: AbortSignal): AsyncIterable<LlmStreamChunk>;
93
93
  parseResponse(response: unknown, _options?: LlmOperateOptions): ParsedResponse;
94
94
  extractToolCalls(response: unknown): StandardToolCall[];
95
95
  extractUsage(response: unknown, model: string): LlmUsageItem;
@@ -47,17 +47,19 @@ export interface ProviderAdapter {
47
47
  *
48
48
  * @param client - The provider's SDK client instance
49
49
  * @param request - Provider-specific request object (from buildRequest)
50
+ * @param signal - Optional AbortSignal to cancel the request on retry
50
51
  * @returns Raw provider response
51
52
  */
52
- executeRequest(client: unknown, request: unknown): Promise<unknown>;
53
+ executeRequest(client: unknown, request: unknown, signal?: AbortSignal): Promise<unknown>;
53
54
  /**
54
55
  * Execute a streaming API request to the provider
55
56
  *
56
57
  * @param client - The provider's SDK client instance
57
58
  * @param request - Provider-specific request object (from buildRequest)
59
+ * @param signal - Optional AbortSignal to cancel the request on retry
58
60
  * @returns AsyncIterable of stream chunks
59
61
  */
60
- executeStreamRequest?(client: unknown, request: unknown): AsyncIterable<LlmStreamChunk>;
62
+ executeStreamRequest?(client: unknown, request: unknown, signal?: AbortSignal): AsyncIterable<LlmStreamChunk>;
61
63
  /**
62
64
  * Parse a provider response into standardized format
63
65
  *
@@ -158,7 +160,7 @@ export declare abstract class BaseProviderAdapter implements ProviderAdapter {
158
160
  abstract buildRequest(request: OperateRequest): unknown;
159
161
  abstract formatTools(toolkit: Toolkit, outputSchema?: JsonObject): ProviderToolDefinition[];
160
162
  abstract formatOutputSchema(schema: JsonObject | NaturalSchema | z.ZodType): JsonObject;
161
- abstract executeRequest(client: unknown, request: unknown): Promise<unknown>;
163
+ abstract executeRequest(client: unknown, request: unknown, signal?: AbortSignal): Promise<unknown>;
162
164
  abstract parseResponse(response: unknown, options?: LlmOperateOptions): ParsedResponse;
163
165
  abstract extractToolCalls(response: unknown): StandardToolCall[];
164
166
  abstract extractUsage(response: unknown, model: string): LlmUsageItem;
@@ -28,12 +28,15 @@ export declare class RetryExecutor {
28
28
  private readonly errorClassifier;
29
29
  constructor(config: RetryExecutorConfig);
30
30
  /**
31
- * Execute an operation with retry logic
31
+ * Execute an operation with retry logic.
32
+ * Each attempt receives an AbortSignal. On failure, the signal is aborted
33
+ * before sleeping — this kills lingering socket callbacks from the previous
34
+ * request and prevents stale async errors from escaping the retry loop.
32
35
  *
33
- * @param operation - The async operation to execute
36
+ * @param operation - The async operation to execute (receives AbortSignal)
34
37
  * @param options - Execution options including context and hooks
35
38
  * @returns The result of the operation
36
39
  * @throws BadGatewayError if all retries are exhausted or error is not retryable
37
40
  */
38
- execute<T>(operation: () => Promise<T>, options: ExecuteOptions): Promise<T>;
41
+ execute<T>(operation: ((signal: AbortSignal) => Promise<T>) | (() => Promise<T>), options: ExecuteOptions): Promise<T>;
39
42
  }
package/dist/esm/index.js CHANGED
@@ -2671,7 +2671,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
2671
2671
  //
2672
2672
  // API Execution
2673
2673
  //
2674
- async executeRequest(client, request) {
2674
+ async executeRequest(client, request, signal) {
2675
2675
  const openRouter = client;
2676
2676
  const openRouterRequest = request;
2677
2677
  const response = await openRouter.chat.send({
@@ -2680,10 +2680,10 @@ class OpenRouterAdapter extends BaseProviderAdapter {
2680
2680
  tools: openRouterRequest.tools,
2681
2681
  toolChoice: openRouterRequest.tool_choice,
2682
2682
  user: openRouterRequest.user,
2683
- });
2683
+ }, signal ? { signal } : undefined);
2684
2684
  return response;
2685
2685
  }
2686
- async *executeStreamRequest(client, request) {
2686
+ async *executeStreamRequest(client, request, signal) {
2687
2687
  const openRouter = client;
2688
2688
  const openRouterRequest = request;
2689
2689
  // Use chat.send with stream: true for streaming responses
@@ -2694,7 +2694,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
2694
2694
  toolChoice: openRouterRequest.tool_choice,
2695
2695
  user: openRouterRequest.user,
2696
2696
  stream: true,
2697
- });
2697
+ }, signal ? { signal } : undefined);
2698
2698
  // Track current tool call being built
2699
2699
  let currentToolCall = null;
2700
2700
  // Track usage for final chunk
@@ -3905,9 +3905,12 @@ class RetryExecutor {
3905
3905
  this.errorClassifier = config.errorClassifier;
3906
3906
  }
3907
3907
  /**
3908
- * Execute an operation with retry logic
3908
+ * Execute an operation with retry logic.
3909
+ * Each attempt receives an AbortSignal. On failure, the signal is aborted
3910
+ * before sleeping — this kills lingering socket callbacks from the previous
3911
+ * request and prevents stale async errors from escaping the retry loop.
3909
3912
  *
3910
- * @param operation - The async operation to execute
3913
+ * @param operation - The async operation to execute (receives AbortSignal)
3911
3914
  * @param options - Execution options including context and hooks
3912
3915
  * @returns The result of the operation
3913
3916
  * @throws BadGatewayError if all retries are exhausted or error is not retryable
@@ -3915,14 +3918,17 @@ class RetryExecutor {
3915
3918
  async execute(operation, options) {
3916
3919
  let attempt = 0;
3917
3920
  while (true) {
3921
+ const controller = new AbortController();
3918
3922
  try {
3919
- const result = await operation();
3923
+ const result = await operation(controller.signal);
3920
3924
  if (attempt > 0) {
3921
3925
  log$1.debug(`API call succeeded after ${attempt} retries`);
3922
3926
  }
3923
3927
  return result;
3924
3928
  }
3925
3929
  catch (error) {
3930
+ // Abort the previous request to kill lingering socket callbacks
3931
+ controller.abort("retry");
3926
3932
  // Check if we've exhausted retries
3927
3933
  if (!this.policy.shouldRetry(attempt)) {
3928
3934
  log$1.error(`API call failed after ${this.policy.maxRetries} retries`);
@@ -4147,7 +4153,7 @@ class OperateLoop {
4147
4153
  providerRequest,
4148
4154
  });
4149
4155
  // Execute with retry (RetryExecutor handles error hooks and throws appropriate errors)
4150
- const response = await retryExecutor.execute(() => this.adapter.executeRequest(this.client, providerRequest), {
4156
+ const response = await retryExecutor.execute((signal) => this.adapter.executeRequest(this.client, providerRequest, signal), {
4151
4157
  context: {
4152
4158
  input: state.currentInput,
4153
4159
  options,
@@ -4540,9 +4546,10 @@ class StreamLoop {
4540
4546
  let attempt = 0;
4541
4547
  let chunksYielded = false;
4542
4548
  while (true) {
4549
+ const controller = new AbortController();
4543
4550
  try {
4544
4551
  // Execute streaming request
4545
- const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest);
4552
+ const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest, controller.signal);
4546
4553
  for await (const chunk of streamGenerator) {
4547
4554
  // Pass through text chunks
4548
4555
  if (chunk.type === LlmStreamChunkType.Text) {
@@ -4577,6 +4584,8 @@ class StreamLoop {
4577
4584
  break;
4578
4585
  }
4579
4586
  catch (error) {
4587
+ // Abort the previous request to kill lingering socket callbacks
4588
+ controller.abort("retry");
4580
4589
  // If chunks were already yielded, we can't transparently retry
4581
4590
  if (chunksYielded) {
4582
4591
  const errorMessage = error instanceof Error ? error.message : String(error);