@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.
@@ -12,7 +12,7 @@ import { GeminiPart, GeminiRawResponse, GeminiRequest } from "../../providers/ge
12
12
  * specific to Gemini's generateContent API.
13
13
  */
14
14
  export declare class GeminiAdapter extends BaseProviderAdapter {
15
- readonly name: "gemini";
15
+ readonly name: "google";
16
16
  readonly defaultModel: "gemini-3-pro-preview";
17
17
  buildRequest(request: OperateRequest): GeminiRequest;
18
18
  formatTools(toolkit: Toolkit, outputSchema?: JsonObject): ProviderToolDefinition[];
@@ -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";
package/dist/esm/index.js CHANGED
@@ -79,7 +79,7 @@ const PROVIDER = {
79
79
  TINY: FIRST_CLASS_PROVIDER.GEMINI.TINY,
80
80
  },
81
81
  MODEL_MATCH_WORDS: ["gemini", "google"],
82
- NAME: "gemini",
82
+ NAME: "google",
83
83
  ROLE: {
84
84
  MODEL: "model",
85
85
  USER: "user",
@@ -194,7 +194,7 @@ function determineModelProvider(input) {
194
194
  provider: PROVIDER.ANTHROPIC.NAME,
195
195
  };
196
196
  }
197
- if (input === PROVIDER.GEMINI.NAME) {
197
+ if (input === PROVIDER.GEMINI.NAME || input === "gemini") {
198
198
  return {
199
199
  model: PROVIDER.GEMINI.MODEL.DEFAULT,
200
200
  provider: PROVIDER.GEMINI.NAME,
@@ -3955,73 +3955,91 @@ class RetryExecutor {
3955
3955
  */
3956
3956
  async execute(operation, options) {
3957
3957
  let attempt = 0;
3958
- while (true) {
3959
- const controller = new AbortController();
3960
- try {
3961
- const result = await operation(controller.signal);
3962
- if (attempt > 0) {
3963
- log$1.debug(`API call succeeded after ${attempt} retries`);
3958
+ // Persistent guard against stale socket errors (TypeError: terminated).
3959
+ // Installed after the first abort and kept alive through subsequent attempts
3960
+ // so that asynchronous undici socket teardown errors that fire between
3961
+ // sleep completion and the next operation's await are caught.
3962
+ let staleGuard;
3963
+ const installGuard = () => {
3964
+ if (staleGuard)
3965
+ return;
3966
+ staleGuard = (reason) => {
3967
+ if (isTransientNetworkError(reason)) {
3968
+ log$1.trace("Suppressed stale socket error during retry");
3964
3969
  }
3965
- return result;
3970
+ };
3971
+ process.on("unhandledRejection", staleGuard);
3972
+ };
3973
+ const removeGuard = () => {
3974
+ if (staleGuard) {
3975
+ process.removeListener("unhandledRejection", staleGuard);
3976
+ staleGuard = undefined;
3966
3977
  }
3967
- catch (error) {
3968
- // Abort the previous request to kill lingering socket callbacks
3969
- controller.abort("retry");
3970
- // Check if we've exhausted retries
3971
- if (!this.policy.shouldRetry(attempt)) {
3972
- log$1.error(`API call failed after ${this.policy.maxRetries} retries`);
3973
- log$1.var({ error });
3974
- await this.hookRunner.runOnUnrecoverableError(options.hooks, {
3975
- input: options.context.input,
3976
- options: options.context.options,
3977
- providerRequest: options.context.providerRequest,
3978
- error,
3979
- });
3980
- const errorMessage = error instanceof Error ? error.message : String(error);
3981
- throw new BadGatewayError(errorMessage);
3978
+ };
3979
+ try {
3980
+ while (true) {
3981
+ const controller = new AbortController();
3982
+ try {
3983
+ const result = await operation(controller.signal);
3984
+ if (attempt > 0) {
3985
+ log$1.debug(`API call succeeded after ${attempt} retries`);
3986
+ }
3987
+ return result;
3982
3988
  }
3983
- // Check if error is not retryable
3984
- if (!this.errorClassifier.isRetryable(error)) {
3985
- log$1.error("API call failed with non-retryable error");
3986
- log$1.var({ error });
3987
- await this.hookRunner.runOnUnrecoverableError(options.hooks, {
3989
+ catch (error) {
3990
+ // Abort the previous request to kill lingering socket callbacks
3991
+ controller.abort("retry");
3992
+ // Install the guard immediately after abort — stale socket errors
3993
+ // can fire on any subsequent microtask boundary (during hook calls,
3994
+ // sleep, or the next operation attempt)
3995
+ installGuard();
3996
+ // Check if we've exhausted retries
3997
+ if (!this.policy.shouldRetry(attempt)) {
3998
+ log$1.error(`API call failed after ${this.policy.maxRetries} retries`);
3999
+ log$1.var({ error });
4000
+ await this.hookRunner.runOnUnrecoverableError(options.hooks, {
4001
+ input: options.context.input,
4002
+ options: options.context.options,
4003
+ providerRequest: options.context.providerRequest,
4004
+ error,
4005
+ });
4006
+ const errorMessage = error instanceof Error ? error.message : String(error);
4007
+ throw new BadGatewayError(errorMessage);
4008
+ }
4009
+ // Check if error is not retryable
4010
+ if (!this.errorClassifier.isRetryable(error)) {
4011
+ log$1.error("API call failed with non-retryable error");
4012
+ log$1.var({ error });
4013
+ await this.hookRunner.runOnUnrecoverableError(options.hooks, {
4014
+ input: options.context.input,
4015
+ options: options.context.options,
4016
+ providerRequest: options.context.providerRequest,
4017
+ error,
4018
+ });
4019
+ const errorMessage = error instanceof Error ? error.message : String(error);
4020
+ throw new BadGatewayError(errorMessage);
4021
+ }
4022
+ // Warn if this is an unknown error type
4023
+ if (!this.errorClassifier.isKnownError(error)) {
4024
+ log$1.warn("API returned unknown error type, will retry");
4025
+ log$1.var({ error });
4026
+ }
4027
+ const delay = this.policy.getDelayForAttempt(attempt);
4028
+ log$1.warn(`API call failed. Retrying in ${delay}ms...`);
4029
+ await this.hookRunner.runOnRetryableError(options.hooks, {
3988
4030
  input: options.context.input,
3989
4031
  options: options.context.options,
3990
4032
  providerRequest: options.context.providerRequest,
3991
4033
  error,
3992
4034
  });
3993
- const errorMessage = error instanceof Error ? error.message : String(error);
3994
- throw new BadGatewayError(errorMessage);
3995
- }
3996
- // Warn if this is an unknown error type
3997
- if (!this.errorClassifier.isKnownError(error)) {
3998
- log$1.warn("API returned unknown error type, will retry");
3999
- log$1.var({ error });
4000
- }
4001
- const delay = this.policy.getDelayForAttempt(attempt);
4002
- log$1.warn(`API call failed. Retrying in ${delay}ms...`);
4003
- await this.hookRunner.runOnRetryableError(options.hooks, {
4004
- input: options.context.input,
4005
- options: options.context.options,
4006
- providerRequest: options.context.providerRequest,
4007
- error,
4008
- });
4009
- // Guard against stale socket errors that fire during sleep
4010
- const staleHandler = (reason) => {
4011
- if (isTransientNetworkError(reason)) {
4012
- log$1.trace("Suppressed stale socket error during retry sleep");
4013
- }
4014
- };
4015
- process.on("unhandledRejection", staleHandler);
4016
- try {
4017
4035
  await sleep(delay);
4036
+ attempt++;
4018
4037
  }
4019
- finally {
4020
- process.removeListener("unhandledRejection", staleHandler);
4021
- }
4022
- attempt++;
4023
4038
  }
4024
4039
  }
4040
+ finally {
4041
+ removeGuard();
4042
+ }
4025
4043
  }
4026
4044
  }
4027
4045
 
@@ -4595,89 +4613,103 @@ class StreamLoop {
4595
4613
  // Retry loop for connection-level failures
4596
4614
  let attempt = 0;
4597
4615
  let chunksYielded = false;
4598
- while (true) {
4599
- const controller = new AbortController();
4600
- try {
4601
- // Execute streaming request
4602
- const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest, controller.signal);
4603
- for await (const chunk of streamGenerator) {
4604
- // Pass through text chunks
4605
- if (chunk.type === LlmStreamChunkType.Text) {
4606
- chunksYielded = true;
4607
- yield chunk;
4616
+ // Persistent guard against stale socket errors (TypeError: terminated).
4617
+ // Installed after the first abort and kept alive through subsequent attempts.
4618
+ let staleGuard;
4619
+ const installGuard = () => {
4620
+ if (staleGuard)
4621
+ return;
4622
+ staleGuard = (reason) => {
4623
+ if (isTransientNetworkError(reason)) {
4624
+ log$1.trace("Suppressed stale socket error during retry");
4625
+ }
4626
+ };
4627
+ process.on("unhandledRejection", staleGuard);
4628
+ };
4629
+ const removeGuard = () => {
4630
+ if (staleGuard) {
4631
+ process.removeListener("unhandledRejection", staleGuard);
4632
+ staleGuard = undefined;
4633
+ }
4634
+ };
4635
+ try {
4636
+ while (true) {
4637
+ const controller = new AbortController();
4638
+ try {
4639
+ // Execute streaming request
4640
+ const streamGenerator = this.adapter.executeStreamRequest(this.client, providerRequest, controller.signal);
4641
+ for await (const chunk of streamGenerator) {
4642
+ // Pass through text chunks
4643
+ if (chunk.type === LlmStreamChunkType.Text) {
4644
+ chunksYielded = true;
4645
+ yield chunk;
4646
+ }
4647
+ // Collect tool calls
4648
+ if (chunk.type === LlmStreamChunkType.ToolCall) {
4649
+ chunksYielded = true;
4650
+ collectedToolCalls.push({
4651
+ callId: chunk.toolCall.id,
4652
+ name: chunk.toolCall.name,
4653
+ arguments: chunk.toolCall.arguments,
4654
+ raw: chunk.toolCall,
4655
+ });
4656
+ yield chunk;
4657
+ }
4658
+ // Track usage from done chunk (but don't yield it yet - we'll emit our own)
4659
+ if (chunk.type === LlmStreamChunkType.Done && chunk.usage) {
4660
+ state.usageItems.push(...chunk.usage);
4661
+ }
4662
+ // Pass through error chunks
4663
+ if (chunk.type === LlmStreamChunkType.Error) {
4664
+ chunksYielded = true;
4665
+ yield chunk;
4666
+ }
4608
4667
  }
4609
- // Collect tool calls
4610
- if (chunk.type === LlmStreamChunkType.ToolCall) {
4611
- chunksYielded = true;
4612
- collectedToolCalls.push({
4613
- callId: chunk.toolCall.id,
4614
- name: chunk.toolCall.name,
4615
- arguments: chunk.toolCall.arguments,
4616
- raw: chunk.toolCall,
4617
- });
4618
- yield chunk;
4668
+ // Stream completed successfully
4669
+ if (attempt > 0) {
4670
+ log$1.debug(`Stream request succeeded after ${attempt} retries`);
4619
4671
  }
4620
- // Track usage from done chunk (but don't yield it yet - we'll emit our own)
4621
- if (chunk.type === LlmStreamChunkType.Done && chunk.usage) {
4622
- state.usageItems.push(...chunk.usage);
4672
+ break;
4673
+ }
4674
+ catch (error) {
4675
+ // Abort the previous request to kill lingering socket callbacks
4676
+ controller.abort("retry");
4677
+ // Install the guard immediately after abort
4678
+ installGuard();
4679
+ // If chunks were already yielded, we can't transparently retry
4680
+ if (chunksYielded) {
4681
+ const errorMessage = error instanceof Error ? error.message : String(error);
4682
+ log$1.error("Stream failed after partial data was delivered");
4683
+ log$1.var({ error });
4684
+ yield {
4685
+ type: LlmStreamChunkType.Error,
4686
+ error: {
4687
+ detail: errorMessage,
4688
+ status: 502,
4689
+ title: "Stream Error",
4690
+ },
4691
+ };
4692
+ return { shouldContinue: false };
4623
4693
  }
4624
- // Pass through error chunks
4625
- if (chunk.type === LlmStreamChunkType.Error) {
4626
- chunksYielded = true;
4627
- yield chunk;
4694
+ // Check if we've exhausted retries or error is not retryable
4695
+ if (!this.retryPolicy.shouldRetry(attempt) ||
4696
+ !this.adapter.isRetryableError(error)) {
4697
+ log$1.error(`Stream request failed after ${this.retryPolicy.maxRetries} retries`);
4698
+ log$1.var({ error });
4699
+ const errorMessage = error instanceof Error ? error.message : String(error);
4700
+ throw new BadGatewayError(errorMessage);
4628
4701
  }
4629
- }
4630
- // Stream completed successfully
4631
- if (attempt > 0) {
4632
- log$1.debug(`Stream request succeeded after ${attempt} retries`);
4633
- }
4634
- break;
4635
- }
4636
- catch (error) {
4637
- // Abort the previous request to kill lingering socket callbacks
4638
- controller.abort("retry");
4639
- // If chunks were already yielded, we can't transparently retry
4640
- if (chunksYielded) {
4641
- const errorMessage = error instanceof Error ? error.message : String(error);
4642
- log$1.error("Stream failed after partial data was delivered");
4643
- log$1.var({ error });
4644
- yield {
4645
- type: LlmStreamChunkType.Error,
4646
- error: {
4647
- detail: errorMessage,
4648
- status: 502,
4649
- title: "Stream Error",
4650
- },
4651
- };
4652
- return { shouldContinue: false };
4653
- }
4654
- // Check if we've exhausted retries or error is not retryable
4655
- if (!this.retryPolicy.shouldRetry(attempt) ||
4656
- !this.adapter.isRetryableError(error)) {
4657
- log$1.error(`Stream request failed after ${this.retryPolicy.maxRetries} retries`);
4702
+ const delay = this.retryPolicy.getDelayForAttempt(attempt);
4703
+ log$1.warn(`Stream request failed. Retrying in ${delay}ms...`);
4658
4704
  log$1.var({ error });
4659
- const errorMessage = error instanceof Error ? error.message : String(error);
4660
- throw new BadGatewayError(errorMessage);
4661
- }
4662
- const delay = this.retryPolicy.getDelayForAttempt(attempt);
4663
- log$1.warn(`Stream request failed. Retrying in ${delay}ms...`);
4664
- log$1.var({ error });
4665
- // Guard against stale socket errors that fire during sleep
4666
- const staleHandler = (reason) => {
4667
- if (isTransientNetworkError(reason)) {
4668
- log$1.trace("Suppressed stale socket error during retry sleep");
4669
- }
4670
- };
4671
- process.on("unhandledRejection", staleHandler);
4672
- try {
4673
4705
  await sleep(delay);
4706
+ attempt++;
4674
4707
  }
4675
- finally {
4676
- process.removeListener("unhandledRejection", staleHandler);
4677
- }
4678
- attempt++;
4679
4708
  }
4680
4709
  }
4710
+ finally {
4711
+ removeGuard();
4712
+ }
4681
4713
  // Execute afterEachModelResponse hook
4682
4714
  await this.hookRunnerInstance.runAfterModelResponse(context.hooks, {
4683
4715
  content: "",
@@ -5537,6 +5569,10 @@ class Llm {
5537
5569
  const { fallback, model } = options;
5538
5570
  let finalProvider = providerName;
5539
5571
  let finalModel = model;
5572
+ // Legacy: accept "gemini" but warn
5573
+ if (providerName === "gemini") {
5574
+ log$3.warn(`Provider "gemini" is deprecated, use "${PROVIDER.GEMINI.NAME}" instead`);
5575
+ }
5540
5576
  if (model) {
5541
5577
  const modelDetermined = determineModelProvider(model);
5542
5578
  finalModel = modelDetermined.model;
@@ -5551,6 +5587,10 @@ class Llm {
5551
5587
  throw new ConfigurationError(`Unable to determine provider from: ${providerName}`);
5552
5588
  }
5553
5589
  finalProvider = providerDetermined.provider;
5590
+ // When providerName is actually a model name, extract the model (#213)
5591
+ if (!finalModel && providerName !== providerDetermined.provider) {
5592
+ finalModel = providerDetermined.model;
5593
+ }
5554
5594
  }
5555
5595
  // Handle conflicts: if both providerName and model specify different providers
5556
5596
  if (model && providerName !== DEFAULT.PROVIDER.NAME) {