@jaypie/llm 1.2.33 → 1.2.35
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/cjs/index.cjs +89 -52
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/operate/retry/createStaleRejectionGuard.d.ts +19 -0
- package/dist/esm/index.js +89 -52
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/retry/createStaleRejectionGuard.d.ts +19 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -4817,6 +4817,78 @@ class RetryPolicy {
|
|
|
4817
4817
|
// Export a default policy instance
|
|
4818
4818
|
const defaultRetryPolicy = new RetryPolicy();
|
|
4819
4819
|
|
|
4820
|
+
//
|
|
4821
|
+
//
|
|
4822
|
+
// Helpers
|
|
4823
|
+
//
|
|
4824
|
+
/**
|
|
4825
|
+
* Compare an unhandled rejection reason against errors the retry loop has
|
|
4826
|
+
* already caught. Reference equality first, then fall back to message + name
|
|
4827
|
+
* — providers sometimes surface twin rejections as fresh Error instances
|
|
4828
|
+
* rebuilt from the same upstream failure.
|
|
4829
|
+
*/
|
|
4830
|
+
function matchesCaughtError(reason, caught) {
|
|
4831
|
+
if (caught.has(reason))
|
|
4832
|
+
return true;
|
|
4833
|
+
if (!(reason instanceof Error))
|
|
4834
|
+
return false;
|
|
4835
|
+
for (const handled of caught) {
|
|
4836
|
+
if (handled instanceof Error &&
|
|
4837
|
+
handled.name === reason.name &&
|
|
4838
|
+
handled.message === reason.message) {
|
|
4839
|
+
return true;
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
return false;
|
|
4843
|
+
}
|
|
4844
|
+
//
|
|
4845
|
+
//
|
|
4846
|
+
// Main
|
|
4847
|
+
//
|
|
4848
|
+
/**
|
|
4849
|
+
* Create a guard that suppresses unhandled rejections firing as siblings of
|
|
4850
|
+
* an error the retry loop has already caught. Provider SDKs occasionally
|
|
4851
|
+
* surface a single upstream failure as twin rejections — the retry layer
|
|
4852
|
+
* accepts responsibility for the first; this guard prevents the second from
|
|
4853
|
+
* crashing the host while the retry is in flight.
|
|
4854
|
+
*
|
|
4855
|
+
* The guard also continues to suppress transient socket teardown errors
|
|
4856
|
+
* (e.g. undici `TypeError: terminated`) emitted between attempts.
|
|
4857
|
+
*/
|
|
4858
|
+
function createStaleRejectionGuard() {
|
|
4859
|
+
const log = getLogger$5();
|
|
4860
|
+
const caughtErrors = new Set();
|
|
4861
|
+
let listener;
|
|
4862
|
+
return {
|
|
4863
|
+
install() {
|
|
4864
|
+
if (listener)
|
|
4865
|
+
return;
|
|
4866
|
+
listener = (reason, promise) => {
|
|
4867
|
+
if (isTransientNetworkError(reason)) {
|
|
4868
|
+
promise?.catch?.(() => { });
|
|
4869
|
+
log.trace("Suppressed stale socket error during retry");
|
|
4870
|
+
return;
|
|
4871
|
+
}
|
|
4872
|
+
if (matchesCaughtError(reason, caughtErrors)) {
|
|
4873
|
+
promise?.catch?.(() => { });
|
|
4874
|
+
log.trace("Suppressed sibling rejection of already-handled error");
|
|
4875
|
+
}
|
|
4876
|
+
};
|
|
4877
|
+
process.on("unhandledRejection", listener);
|
|
4878
|
+
},
|
|
4879
|
+
remove() {
|
|
4880
|
+
if (listener) {
|
|
4881
|
+
process.removeListener("unhandledRejection", listener);
|
|
4882
|
+
listener = undefined;
|
|
4883
|
+
}
|
|
4884
|
+
caughtErrors.clear();
|
|
4885
|
+
},
|
|
4886
|
+
recordCaught(error) {
|
|
4887
|
+
caughtErrors.add(error);
|
|
4888
|
+
},
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4891
|
+
|
|
4820
4892
|
//
|
|
4821
4893
|
//
|
|
4822
4894
|
// Main
|
|
@@ -4845,27 +4917,11 @@ class RetryExecutor {
|
|
|
4845
4917
|
async execute(operation, options) {
|
|
4846
4918
|
const log = getLogger$5();
|
|
4847
4919
|
let attempt = 0;
|
|
4848
|
-
//
|
|
4849
|
-
//
|
|
4850
|
-
//
|
|
4851
|
-
//
|
|
4852
|
-
|
|
4853
|
-
const installGuard = () => {
|
|
4854
|
-
if (staleGuard)
|
|
4855
|
-
return;
|
|
4856
|
-
staleGuard = (reason) => {
|
|
4857
|
-
if (isTransientNetworkError(reason)) {
|
|
4858
|
-
log.trace("Suppressed stale socket error during retry");
|
|
4859
|
-
}
|
|
4860
|
-
};
|
|
4861
|
-
process.on("unhandledRejection", staleGuard);
|
|
4862
|
-
};
|
|
4863
|
-
const removeGuard = () => {
|
|
4864
|
-
if (staleGuard) {
|
|
4865
|
-
process.removeListener("unhandledRejection", staleGuard);
|
|
4866
|
-
staleGuard = undefined;
|
|
4867
|
-
}
|
|
4868
|
-
};
|
|
4920
|
+
// Guard against stale rejections firing on a subsequent microtask after
|
|
4921
|
+
// the retry layer has already caught the originating error: undici socket
|
|
4922
|
+
// teardown (TypeError: terminated) and twin upstream-SDK rejections
|
|
4923
|
+
// (e.g. issue #336 — OpenRouter SyntaxError siblings).
|
|
4924
|
+
const guard = createStaleRejectionGuard();
|
|
4869
4925
|
try {
|
|
4870
4926
|
while (true) {
|
|
4871
4927
|
const controller = new AbortController();
|
|
@@ -4877,12 +4933,9 @@ class RetryExecutor {
|
|
|
4877
4933
|
return result;
|
|
4878
4934
|
}
|
|
4879
4935
|
catch (error) {
|
|
4880
|
-
// Abort the previous request to kill lingering socket callbacks
|
|
4881
4936
|
controller.abort("retry");
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
// sleep, or the next operation attempt)
|
|
4885
|
-
installGuard();
|
|
4937
|
+
guard.recordCaught(error);
|
|
4938
|
+
guard.install();
|
|
4886
4939
|
// Check if we've exhausted retries
|
|
4887
4940
|
if (!this.policy.shouldRetry(attempt)) {
|
|
4888
4941
|
log.error(`API call failed after ${this.policy.maxRetries} retries`);
|
|
@@ -4928,7 +4981,7 @@ class RetryExecutor {
|
|
|
4928
4981
|
}
|
|
4929
4982
|
}
|
|
4930
4983
|
finally {
|
|
4931
|
-
|
|
4984
|
+
guard.remove();
|
|
4932
4985
|
}
|
|
4933
4986
|
}
|
|
4934
4987
|
}
|
|
@@ -5231,7 +5284,7 @@ class OperateLoop {
|
|
|
5231
5284
|
};
|
|
5232
5285
|
const toolResultFormatted = this.adapter.formatToolResult(toolCall, errorResult);
|
|
5233
5286
|
state.responseBuilder.appendToHistory(toolResultFormatted);
|
|
5234
|
-
log.
|
|
5287
|
+
log.warn(`Error executing function call ${toolCall.name}`);
|
|
5235
5288
|
log.var({ error });
|
|
5236
5289
|
// Track consecutive errors and stop if threshold reached
|
|
5237
5290
|
state.consecutiveToolErrors++;
|
|
@@ -5538,25 +5591,10 @@ class StreamLoop {
|
|
|
5538
5591
|
// Retry loop for connection-level failures
|
|
5539
5592
|
let attempt = 0;
|
|
5540
5593
|
let chunksYielded = false;
|
|
5541
|
-
//
|
|
5542
|
-
//
|
|
5543
|
-
|
|
5544
|
-
const
|
|
5545
|
-
if (staleGuard)
|
|
5546
|
-
return;
|
|
5547
|
-
staleGuard = (reason) => {
|
|
5548
|
-
if (isTransientNetworkError(reason)) {
|
|
5549
|
-
log.trace("Suppressed stale socket error during retry");
|
|
5550
|
-
}
|
|
5551
|
-
};
|
|
5552
|
-
process.on("unhandledRejection", staleGuard);
|
|
5553
|
-
};
|
|
5554
|
-
const removeGuard = () => {
|
|
5555
|
-
if (staleGuard) {
|
|
5556
|
-
process.removeListener("unhandledRejection", staleGuard);
|
|
5557
|
-
staleGuard = undefined;
|
|
5558
|
-
}
|
|
5559
|
-
};
|
|
5594
|
+
// Guard against stale rejections firing after the stream loop has already
|
|
5595
|
+
// caught the originating error: undici socket teardown and twin
|
|
5596
|
+
// upstream-SDK rejections (issue #336).
|
|
5597
|
+
const guard = createStaleRejectionGuard();
|
|
5560
5598
|
try {
|
|
5561
5599
|
while (true) {
|
|
5562
5600
|
const controller = new AbortController();
|
|
@@ -5597,10 +5635,9 @@ class StreamLoop {
|
|
|
5597
5635
|
break;
|
|
5598
5636
|
}
|
|
5599
5637
|
catch (error) {
|
|
5600
|
-
// Abort the previous request to kill lingering socket callbacks
|
|
5601
5638
|
controller.abort("retry");
|
|
5602
|
-
|
|
5603
|
-
|
|
5639
|
+
guard.recordCaught(error);
|
|
5640
|
+
guard.install();
|
|
5604
5641
|
// If chunks were already yielded, we can't transparently retry
|
|
5605
5642
|
if (chunksYielded) {
|
|
5606
5643
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -5633,7 +5670,7 @@ class StreamLoop {
|
|
|
5633
5670
|
}
|
|
5634
5671
|
}
|
|
5635
5672
|
finally {
|
|
5636
|
-
|
|
5673
|
+
guard.remove();
|
|
5637
5674
|
}
|
|
5638
5675
|
// Execute afterEachModelResponse hook
|
|
5639
5676
|
await this.hookRunnerInstance.runAfterModelResponse(context.hooks, {
|
|
@@ -5742,7 +5779,7 @@ class StreamLoop {
|
|
|
5742
5779
|
call_id: toolCall.callId,
|
|
5743
5780
|
name: toolCall.name,
|
|
5744
5781
|
});
|
|
5745
|
-
log.
|
|
5782
|
+
log.warn(`Error executing function call ${toolCall.name}`);
|
|
5746
5783
|
log.var({ error });
|
|
5747
5784
|
// Track consecutive errors and stop if threshold reached
|
|
5748
5785
|
state.consecutiveToolErrors++;
|