@jaypie/llm 1.2.14 → 1.2.15
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 +161 -129
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +161 -129
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -3957,73 +3957,91 @@ class RetryExecutor {
|
|
|
3957
3957
|
*/
|
|
3958
3958
|
async execute(operation, options) {
|
|
3959
3959
|
let attempt = 0;
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3960
|
+
// Persistent guard against stale socket errors (TypeError: terminated).
|
|
3961
|
+
// Installed after the first abort and kept alive through subsequent attempts
|
|
3962
|
+
// so that asynchronous undici socket teardown errors that fire between
|
|
3963
|
+
// sleep completion and the next operation's await are caught.
|
|
3964
|
+
let staleGuard;
|
|
3965
|
+
const installGuard = () => {
|
|
3966
|
+
if (staleGuard)
|
|
3967
|
+
return;
|
|
3968
|
+
staleGuard = (reason) => {
|
|
3969
|
+
if (isTransientNetworkError(reason)) {
|
|
3970
|
+
log$1.trace("Suppressed stale socket error during retry");
|
|
3966
3971
|
}
|
|
3967
|
-
|
|
3972
|
+
};
|
|
3973
|
+
process.on("unhandledRejection", staleGuard);
|
|
3974
|
+
};
|
|
3975
|
+
const removeGuard = () => {
|
|
3976
|
+
if (staleGuard) {
|
|
3977
|
+
process.removeListener("unhandledRejection", staleGuard);
|
|
3978
|
+
staleGuard = undefined;
|
|
3968
3979
|
}
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
providerRequest: options.context.providerRequest,
|
|
3980
|
-
error,
|
|
3981
|
-
});
|
|
3982
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3983
|
-
throw new errors.BadGatewayError(errorMessage);
|
|
3980
|
+
};
|
|
3981
|
+
try {
|
|
3982
|
+
while (true) {
|
|
3983
|
+
const controller = new AbortController();
|
|
3984
|
+
try {
|
|
3985
|
+
const result = await operation(controller.signal);
|
|
3986
|
+
if (attempt > 0) {
|
|
3987
|
+
log$1.debug(`API call succeeded after ${attempt} retries`);
|
|
3988
|
+
}
|
|
3989
|
+
return result;
|
|
3984
3990
|
}
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3991
|
+
catch (error) {
|
|
3992
|
+
// Abort the previous request to kill lingering socket callbacks
|
|
3993
|
+
controller.abort("retry");
|
|
3994
|
+
// Install the guard immediately after abort — stale socket errors
|
|
3995
|
+
// can fire on any subsequent microtask boundary (during hook calls,
|
|
3996
|
+
// sleep, or the next operation attempt)
|
|
3997
|
+
installGuard();
|
|
3998
|
+
// Check if we've exhausted retries
|
|
3999
|
+
if (!this.policy.shouldRetry(attempt)) {
|
|
4000
|
+
log$1.error(`API call failed after ${this.policy.maxRetries} retries`);
|
|
4001
|
+
log$1.var({ error });
|
|
4002
|
+
await this.hookRunner.runOnUnrecoverableError(options.hooks, {
|
|
4003
|
+
input: options.context.input,
|
|
4004
|
+
options: options.context.options,
|
|
4005
|
+
providerRequest: options.context.providerRequest,
|
|
4006
|
+
error,
|
|
4007
|
+
});
|
|
4008
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4009
|
+
throw new errors.BadGatewayError(errorMessage);
|
|
4010
|
+
}
|
|
4011
|
+
// Check if error is not retryable
|
|
4012
|
+
if (!this.errorClassifier.isRetryable(error)) {
|
|
4013
|
+
log$1.error("API call failed with non-retryable error");
|
|
4014
|
+
log$1.var({ error });
|
|
4015
|
+
await this.hookRunner.runOnUnrecoverableError(options.hooks, {
|
|
4016
|
+
input: options.context.input,
|
|
4017
|
+
options: options.context.options,
|
|
4018
|
+
providerRequest: options.context.providerRequest,
|
|
4019
|
+
error,
|
|
4020
|
+
});
|
|
4021
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4022
|
+
throw new errors.BadGatewayError(errorMessage);
|
|
4023
|
+
}
|
|
4024
|
+
// Warn if this is an unknown error type
|
|
4025
|
+
if (!this.errorClassifier.isKnownError(error)) {
|
|
4026
|
+
log$1.warn("API returned unknown error type, will retry");
|
|
4027
|
+
log$1.var({ error });
|
|
4028
|
+
}
|
|
4029
|
+
const delay = this.policy.getDelayForAttempt(attempt);
|
|
4030
|
+
log$1.warn(`API call failed. Retrying in ${delay}ms...`);
|
|
4031
|
+
await this.hookRunner.runOnRetryableError(options.hooks, {
|
|
3990
4032
|
input: options.context.input,
|
|
3991
4033
|
options: options.context.options,
|
|
3992
4034
|
providerRequest: options.context.providerRequest,
|
|
3993
4035
|
error,
|
|
3994
4036
|
});
|
|
3995
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3996
|
-
throw new errors.BadGatewayError(errorMessage);
|
|
3997
|
-
}
|
|
3998
|
-
// Warn if this is an unknown error type
|
|
3999
|
-
if (!this.errorClassifier.isKnownError(error)) {
|
|
4000
|
-
log$1.warn("API returned unknown error type, will retry");
|
|
4001
|
-
log$1.var({ error });
|
|
4002
|
-
}
|
|
4003
|
-
const delay = this.policy.getDelayForAttempt(attempt);
|
|
4004
|
-
log$1.warn(`API call failed. Retrying in ${delay}ms...`);
|
|
4005
|
-
await this.hookRunner.runOnRetryableError(options.hooks, {
|
|
4006
|
-
input: options.context.input,
|
|
4007
|
-
options: options.context.options,
|
|
4008
|
-
providerRequest: options.context.providerRequest,
|
|
4009
|
-
error,
|
|
4010
|
-
});
|
|
4011
|
-
// Guard against stale socket errors that fire during sleep
|
|
4012
|
-
const staleHandler = (reason) => {
|
|
4013
|
-
if (isTransientNetworkError(reason)) {
|
|
4014
|
-
log$1.trace("Suppressed stale socket error during retry sleep");
|
|
4015
|
-
}
|
|
4016
|
-
};
|
|
4017
|
-
process.on("unhandledRejection", staleHandler);
|
|
4018
|
-
try {
|
|
4019
4037
|
await kit.sleep(delay);
|
|
4038
|
+
attempt++;
|
|
4020
4039
|
}
|
|
4021
|
-
finally {
|
|
4022
|
-
process.removeListener("unhandledRejection", staleHandler);
|
|
4023
|
-
}
|
|
4024
|
-
attempt++;
|
|
4025
4040
|
}
|
|
4026
4041
|
}
|
|
4042
|
+
finally {
|
|
4043
|
+
removeGuard();
|
|
4044
|
+
}
|
|
4027
4045
|
}
|
|
4028
4046
|
}
|
|
4029
4047
|
|
|
@@ -4597,89 +4615,103 @@ class StreamLoop {
|
|
|
4597
4615
|
// Retry loop for connection-level failures
|
|
4598
4616
|
let attempt = 0;
|
|
4599
4617
|
let chunksYielded = false;
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4618
|
+
// Persistent guard against stale socket errors (TypeError: terminated).
|
|
4619
|
+
// Installed after the first abort and kept alive through subsequent attempts.
|
|
4620
|
+
let staleGuard;
|
|
4621
|
+
const installGuard = () => {
|
|
4622
|
+
if (staleGuard)
|
|
4623
|
+
return;
|
|
4624
|
+
staleGuard = (reason) => {
|
|
4625
|
+
if (isTransientNetworkError(reason)) {
|
|
4626
|
+
log$1.trace("Suppressed stale socket error during retry");
|
|
4627
|
+
}
|
|
4628
|
+
};
|
|
4629
|
+
process.on("unhandledRejection", staleGuard);
|
|
4630
|
+
};
|
|
4631
|
+
const removeGuard = () => {
|
|
4632
|
+
if (staleGuard) {
|
|
4633
|
+
process.removeListener("unhandledRejection", staleGuard);
|
|
4634
|
+
staleGuard = undefined;
|
|
4635
|
+
}
|
|
4636
|
+
};
|
|
4637
|
+
try {
|
|
4638
|
+
while (true) {
|
|
4639
|
+
const controller = new AbortController();
|
|
4640
|
+
try {
|
|
4641
|
+
// Execute streaming request
|
|
4642
|
+
const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest, controller.signal);
|
|
4643
|
+
for await (const chunk of streamGenerator) {
|
|
4644
|
+
// Pass through text chunks
|
|
4645
|
+
if (chunk.type === exports.LlmStreamChunkType.Text) {
|
|
4646
|
+
chunksYielded = true;
|
|
4647
|
+
yield chunk;
|
|
4648
|
+
}
|
|
4649
|
+
// Collect tool calls
|
|
4650
|
+
if (chunk.type === exports.LlmStreamChunkType.ToolCall) {
|
|
4651
|
+
chunksYielded = true;
|
|
4652
|
+
collectedToolCalls.push({
|
|
4653
|
+
callId: chunk.toolCall.id,
|
|
4654
|
+
name: chunk.toolCall.name,
|
|
4655
|
+
arguments: chunk.toolCall.arguments,
|
|
4656
|
+
raw: chunk.toolCall,
|
|
4657
|
+
});
|
|
4658
|
+
yield chunk;
|
|
4659
|
+
}
|
|
4660
|
+
// Track usage from done chunk (but don't yield it yet - we'll emit our own)
|
|
4661
|
+
if (chunk.type === exports.LlmStreamChunkType.Done && chunk.usage) {
|
|
4662
|
+
state.usageItems.push(...chunk.usage);
|
|
4663
|
+
}
|
|
4664
|
+
// Pass through error chunks
|
|
4665
|
+
if (chunk.type === exports.LlmStreamChunkType.Error) {
|
|
4666
|
+
chunksYielded = true;
|
|
4667
|
+
yield chunk;
|
|
4668
|
+
}
|
|
4610
4669
|
}
|
|
4611
|
-
//
|
|
4612
|
-
if (
|
|
4613
|
-
|
|
4614
|
-
collectedToolCalls.push({
|
|
4615
|
-
callId: chunk.toolCall.id,
|
|
4616
|
-
name: chunk.toolCall.name,
|
|
4617
|
-
arguments: chunk.toolCall.arguments,
|
|
4618
|
-
raw: chunk.toolCall,
|
|
4619
|
-
});
|
|
4620
|
-
yield chunk;
|
|
4670
|
+
// Stream completed successfully
|
|
4671
|
+
if (attempt > 0) {
|
|
4672
|
+
log$1.debug(`Stream request succeeded after ${attempt} retries`);
|
|
4621
4673
|
}
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4674
|
+
break;
|
|
4675
|
+
}
|
|
4676
|
+
catch (error) {
|
|
4677
|
+
// Abort the previous request to kill lingering socket callbacks
|
|
4678
|
+
controller.abort("retry");
|
|
4679
|
+
// Install the guard immediately after abort
|
|
4680
|
+
installGuard();
|
|
4681
|
+
// If chunks were already yielded, we can't transparently retry
|
|
4682
|
+
if (chunksYielded) {
|
|
4683
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4684
|
+
log$1.error("Stream failed after partial data was delivered");
|
|
4685
|
+
log$1.var({ error });
|
|
4686
|
+
yield {
|
|
4687
|
+
type: exports.LlmStreamChunkType.Error,
|
|
4688
|
+
error: {
|
|
4689
|
+
detail: errorMessage,
|
|
4690
|
+
status: 502,
|
|
4691
|
+
title: "Stream Error",
|
|
4692
|
+
},
|
|
4693
|
+
};
|
|
4694
|
+
return { shouldContinue: false };
|
|
4625
4695
|
}
|
|
4626
|
-
//
|
|
4627
|
-
if (
|
|
4628
|
-
|
|
4629
|
-
|
|
4696
|
+
// Check if we've exhausted retries or error is not retryable
|
|
4697
|
+
if (!this.retryPolicy.shouldRetry(attempt) ||
|
|
4698
|
+
!this.adapter.isRetryableError(error)) {
|
|
4699
|
+
log$1.error(`Stream request failed after ${this.retryPolicy.maxRetries} retries`);
|
|
4700
|
+
log$1.var({ error });
|
|
4701
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4702
|
+
throw new errors.BadGatewayError(errorMessage);
|
|
4630
4703
|
}
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
if (attempt > 0) {
|
|
4634
|
-
log$1.debug(`Stream request succeeded after ${attempt} retries`);
|
|
4635
|
-
}
|
|
4636
|
-
break;
|
|
4637
|
-
}
|
|
4638
|
-
catch (error) {
|
|
4639
|
-
// Abort the previous request to kill lingering socket callbacks
|
|
4640
|
-
controller.abort("retry");
|
|
4641
|
-
// If chunks were already yielded, we can't transparently retry
|
|
4642
|
-
if (chunksYielded) {
|
|
4643
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4644
|
-
log$1.error("Stream failed after partial data was delivered");
|
|
4645
|
-
log$1.var({ error });
|
|
4646
|
-
yield {
|
|
4647
|
-
type: exports.LlmStreamChunkType.Error,
|
|
4648
|
-
error: {
|
|
4649
|
-
detail: errorMessage,
|
|
4650
|
-
status: 502,
|
|
4651
|
-
title: "Stream Error",
|
|
4652
|
-
},
|
|
4653
|
-
};
|
|
4654
|
-
return { shouldContinue: false };
|
|
4655
|
-
}
|
|
4656
|
-
// Check if we've exhausted retries or error is not retryable
|
|
4657
|
-
if (!this.retryPolicy.shouldRetry(attempt) ||
|
|
4658
|
-
!this.adapter.isRetryableError(error)) {
|
|
4659
|
-
log$1.error(`Stream request failed after ${this.retryPolicy.maxRetries} retries`);
|
|
4704
|
+
const delay = this.retryPolicy.getDelayForAttempt(attempt);
|
|
4705
|
+
log$1.warn(`Stream request failed. Retrying in ${delay}ms...`);
|
|
4660
4706
|
log$1.var({ error });
|
|
4661
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4662
|
-
throw new errors.BadGatewayError(errorMessage);
|
|
4663
|
-
}
|
|
4664
|
-
const delay = this.retryPolicy.getDelayForAttempt(attempt);
|
|
4665
|
-
log$1.warn(`Stream request failed. Retrying in ${delay}ms...`);
|
|
4666
|
-
log$1.var({ error });
|
|
4667
|
-
// Guard against stale socket errors that fire during sleep
|
|
4668
|
-
const staleHandler = (reason) => {
|
|
4669
|
-
if (isTransientNetworkError(reason)) {
|
|
4670
|
-
log$1.trace("Suppressed stale socket error during retry sleep");
|
|
4671
|
-
}
|
|
4672
|
-
};
|
|
4673
|
-
process.on("unhandledRejection", staleHandler);
|
|
4674
|
-
try {
|
|
4675
4707
|
await kit.sleep(delay);
|
|
4708
|
+
attempt++;
|
|
4676
4709
|
}
|
|
4677
|
-
finally {
|
|
4678
|
-
process.removeListener("unhandledRejection", staleHandler);
|
|
4679
|
-
}
|
|
4680
|
-
attempt++;
|
|
4681
4710
|
}
|
|
4682
4711
|
}
|
|
4712
|
+
finally {
|
|
4713
|
+
removeGuard();
|
|
4714
|
+
}
|
|
4683
4715
|
// Execute afterEachModelResponse hook
|
|
4684
4716
|
await this.hookRunnerInstance.runAfterModelResponse(context.hooks, {
|
|
4685
4717
|
content: "",
|