@jaypie/llm 1.2.13 → 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.
@@ -32,7 +32,7 @@ export declare const PROVIDER: {
32
32
  readonly TINY: "gemini-3-flash-preview";
33
33
  };
34
34
  readonly MODEL_MATCH_WORDS: readonly ["gemini", "google"];
35
- readonly NAME: "gemini";
35
+ readonly NAME: "google";
36
36
  readonly ROLE: {
37
37
  readonly MODEL: "model";
38
38
  readonly USER: "user";
@@ -81,7 +81,7 @@ const PROVIDER = {
81
81
  TINY: FIRST_CLASS_PROVIDER.GEMINI.TINY,
82
82
  },
83
83
  MODEL_MATCH_WORDS: ["gemini", "google"],
84
- NAME: "gemini",
84
+ NAME: "google",
85
85
  ROLE: {
86
86
  MODEL: "model",
87
87
  USER: "user",
@@ -196,7 +196,7 @@ function determineModelProvider(input) {
196
196
  provider: PROVIDER.ANTHROPIC.NAME,
197
197
  };
198
198
  }
199
- if (input === PROVIDER.GEMINI.NAME) {
199
+ if (input === PROVIDER.GEMINI.NAME || input === "gemini") {
200
200
  return {
201
201
  model: PROVIDER.GEMINI.MODEL.DEFAULT,
202
202
  provider: PROVIDER.GEMINI.NAME,
@@ -3957,73 +3957,91 @@ class RetryExecutor {
3957
3957
  */
3958
3958
  async execute(operation, options) {
3959
3959
  let attempt = 0;
3960
- while (true) {
3961
- const controller = new AbortController();
3962
- try {
3963
- const result = await operation(controller.signal);
3964
- if (attempt > 0) {
3965
- log$1.debug(`API call succeeded after ${attempt} retries`);
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
- return result;
3972
+ };
3973
+ process.on("unhandledRejection", staleGuard);
3974
+ };
3975
+ const removeGuard = () => {
3976
+ if (staleGuard) {
3977
+ process.removeListener("unhandledRejection", staleGuard);
3978
+ staleGuard = undefined;
3968
3979
  }
3969
- catch (error) {
3970
- // Abort the previous request to kill lingering socket callbacks
3971
- controller.abort("retry");
3972
- // Check if we've exhausted retries
3973
- if (!this.policy.shouldRetry(attempt)) {
3974
- log$1.error(`API call failed after ${this.policy.maxRetries} retries`);
3975
- log$1.var({ error });
3976
- await this.hookRunner.runOnUnrecoverableError(options.hooks, {
3977
- input: options.context.input,
3978
- options: options.context.options,
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
- // Check if error is not retryable
3986
- if (!this.errorClassifier.isRetryable(error)) {
3987
- log$1.error("API call failed with non-retryable error");
3988
- log$1.var({ error });
3989
- await this.hookRunner.runOnUnrecoverableError(options.hooks, {
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
- while (true) {
4601
- const controller = new AbortController();
4602
- try {
4603
- // Execute streaming request
4604
- const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest, controller.signal);
4605
- for await (const chunk of streamGenerator) {
4606
- // Pass through text chunks
4607
- if (chunk.type === exports.LlmStreamChunkType.Text) {
4608
- chunksYielded = true;
4609
- yield chunk;
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
- // Collect tool calls
4612
- if (chunk.type === exports.LlmStreamChunkType.ToolCall) {
4613
- chunksYielded = true;
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
- // Track usage from done chunk (but don't yield it yet - we'll emit our own)
4623
- if (chunk.type === exports.LlmStreamChunkType.Done && chunk.usage) {
4624
- state.usageItems.push(...chunk.usage);
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
- // Pass through error chunks
4627
- if (chunk.type === exports.LlmStreamChunkType.Error) {
4628
- chunksYielded = true;
4629
- yield chunk;
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
- // Stream completed successfully
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: "",
@@ -5539,6 +5571,10 @@ class Llm {
5539
5571
  const { fallback, model } = options;
5540
5572
  let finalProvider = providerName;
5541
5573
  let finalModel = model;
5574
+ // Legacy: accept "gemini" but warn
5575
+ if (providerName === "gemini") {
5576
+ log$2.warn(`Provider "gemini" is deprecated, use "${PROVIDER.GEMINI.NAME}" instead`);
5577
+ }
5542
5578
  if (model) {
5543
5579
  const modelDetermined = determineModelProvider(model);
5544
5580
  finalModel = modelDetermined.model;
@@ -5553,6 +5589,10 @@ class Llm {
5553
5589
  throw new errors.ConfigurationError(`Unable to determine provider from: ${providerName}`);
5554
5590
  }
5555
5591
  finalProvider = providerDetermined.provider;
5592
+ // When providerName is actually a model name, extract the model (#213)
5593
+ if (!finalModel && providerName !== providerDetermined.provider) {
5594
+ finalModel = providerDetermined.model;
5595
+ }
5556
5596
  }
5557
5597
  // Handle conflicts: if both providerName and model specify different providers
5558
5598
  if (model && providerName !== DEFAULT.PROVIDER.NAME) {