@google/genai 1.39.0 → 1.41.0

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.
@@ -1,3 +1,5 @@
1
+ import pRetry, { AbortError } from 'p-retry';
2
+
1
3
  /**
2
4
  * @license
3
5
  * Copyright 2025 Google LLC
@@ -11608,10 +11610,22 @@ const CONTENT_TYPE_HEADER = 'Content-Type';
11608
11610
  const SERVER_TIMEOUT_HEADER = 'X-Server-Timeout';
11609
11611
  const USER_AGENT_HEADER = 'User-Agent';
11610
11612
  const GOOGLE_API_CLIENT_HEADER = 'x-goog-api-client';
11611
- const SDK_VERSION = '1.39.0'; // x-release-please-version
11613
+ const SDK_VERSION = '1.41.0'; // x-release-please-version
11612
11614
  const LIBRARY_LABEL = `google-genai-sdk/${SDK_VERSION}`;
11613
11615
  const VERTEX_AI_API_DEFAULT_VERSION = 'v1beta1';
11614
11616
  const GOOGLE_AI_API_DEFAULT_VERSION = 'v1beta';
11617
+ // Default retry options.
11618
+ // The config is based on https://cloud.google.com/storage/docs/retry-strategy.
11619
+ const DEFAULT_RETRY_ATTEMPTS = 5; // Including the initial call
11620
+ // LINT.IfChange
11621
+ const DEFAULT_RETRY_HTTP_STATUS_CODES = [
11622
+ 408, // Request timeout
11623
+ 429, // Too many requests
11624
+ 500, // Internal server error
11625
+ 502, // Bad gateway
11626
+ 503, // Service unavailable
11627
+ 504, // Gateway timeout
11628
+ ];
11615
11629
  /**
11616
11630
  * The ApiClient class is used to send requests to the Gemini API or Vertex AI
11617
11631
  * endpoints.
@@ -11996,8 +12010,25 @@ class ApiClient {
11996
12010
  });
11997
12011
  }
11998
12012
  async apiCall(url, requestInit) {
11999
- return fetch(url, requestInit).catch((e) => {
12000
- throw new Error(`exception ${e} sending request`);
12013
+ var _a;
12014
+ if (!this.clientOptions.httpOptions ||
12015
+ !this.clientOptions.httpOptions.retryOptions) {
12016
+ return fetch(url, requestInit);
12017
+ }
12018
+ const retryOptions = this.clientOptions.httpOptions.retryOptions;
12019
+ const runFetch = async () => {
12020
+ const response = await fetch(url, requestInit);
12021
+ if (response.ok) {
12022
+ return response;
12023
+ }
12024
+ if (DEFAULT_RETRY_HTTP_STATUS_CODES.includes(response.status)) {
12025
+ throw new Error(`Retryable HTTP Error: ${response.statusText}`);
12026
+ }
12027
+ throw new AbortError(`Non-retryable exception ${response.statusText} sending request`);
12028
+ };
12029
+ return pRetry(runFetch, {
12030
+ // Retry attempts is one less than the number of total attempts.
12031
+ retries: ((_a = retryOptions.attempts) !== null && _a !== void 0 ? _a : DEFAULT_RETRY_ATTEMPTS) - 1,
12001
12032
  });
12002
12033
  }
12003
12034
  getDefaultHeaders() {
@@ -17381,6 +17412,11 @@ async function defaultParseResponse(client, props) {
17381
17412
  const mediaType = (_a = contentType === null || contentType === void 0 ? void 0 : contentType.split(';')[0]) === null || _a === void 0 ? void 0 : _a.trim();
17382
17413
  const isJSON = (mediaType === null || mediaType === void 0 ? void 0 : mediaType.includes('application/json')) || (mediaType === null || mediaType === void 0 ? void 0 : mediaType.endsWith('+json'));
17383
17414
  if (isJSON) {
17415
+ const contentLength = response.headers.get('content-length');
17416
+ if (contentLength === '0') {
17417
+ // if there is no content we can't do anything
17418
+ return undefined;
17419
+ }
17384
17420
  const json = await response.json();
17385
17421
  return json;
17386
17422
  }
@@ -17840,9 +17876,10 @@ class BaseGeminiNextGenAPIClient {
17840
17876
  }
17841
17877
  async fetchWithTimeout(url, init, ms, controller) {
17842
17878
  const _b = init || {}, { signal, method } = _b, options = __rest(_b, ["signal", "method"]);
17879
+ const abort = this._makeAbort(controller);
17843
17880
  if (signal)
17844
- signal.addEventListener('abort', () => controller.abort());
17845
- const timeout = setTimeout(() => controller.abort(), ms);
17881
+ signal.addEventListener('abort', abort, { once: true });
17882
+ const timeout = setTimeout(abort, ms);
17846
17883
  const isReadableBody = (globalThis.ReadableStream && options.body instanceof globalThis.ReadableStream) ||
17847
17884
  (typeof options.body === 'object' && options.body !== null && Symbol.asyncIterator in options.body);
17848
17885
  const fetchOptions = Object.assign(Object.assign(Object.assign({ signal: controller.signal }, (isReadableBody ? { duplex: 'half' } : {})), { method: 'GET' }), options);
@@ -17955,6 +17992,11 @@ class BaseGeminiNextGenAPIClient {
17955
17992
  this.validateHeaders(headers);
17956
17993
  return headers.values;
17957
17994
  }
17995
+ _makeAbort(controller) {
17996
+ // note: we can't just inline this method inside `fetchWithTimeout()` because then the closure
17997
+ // would capture all request options, and cause a memory leak.
17998
+ return () => controller.abort();
17999
+ }
17958
18000
  buildBody({ options: { body, headers: rawHeaders } }) {
17959
18001
  if (!body) {
17960
18002
  return { bodyHeaders: undefined, body: undefined };
@@ -18125,6 +18167,9 @@ function createTuningJobConfigToMldev(fromObject, parentObject, _rootObject) {
18125
18167
  if (getValueByPath(fromObject, ['outputUri']) !== undefined) {
18126
18168
  throw new Error('outputUri parameter is not supported in Gemini API.');
18127
18169
  }
18170
+ if (getValueByPath(fromObject, ['encryptionSpec']) !== undefined) {
18171
+ throw new Error('encryptionSpec parameter is not supported in Gemini API.');
18172
+ }
18128
18173
  return toObject;
18129
18174
  }
18130
18175
  function createTuningJobConfigToVertex(fromObject, parentObject, rootObject) {
@@ -18360,6 +18405,12 @@ function createTuningJobConfigToVertex(fromObject, parentObject, rootObject) {
18360
18405
  if (parentObject !== undefined && fromOutputUri != null) {
18361
18406
  setValueByPath(parentObject, ['outputUri'], fromOutputUri);
18362
18407
  }
18408
+ const fromEncryptionSpec = getValueByPath(fromObject, [
18409
+ 'encryptionSpec',
18410
+ ]);
18411
+ if (parentObject !== undefined && fromEncryptionSpec != null) {
18412
+ setValueByPath(parentObject, ['encryptionSpec'], fromEncryptionSpec);
18413
+ }
18363
18414
  return toObject;
18364
18415
  }
18365
18416
  function createTuningJobParametersPrivateToMldev(fromObject, rootObject) {
@@ -19448,6 +19499,7 @@ const LANGUAGE_LABEL_PREFIX = 'gl-node/';
19448
19499
  */
19449
19500
  class GoogleGenAI {
19450
19501
  get interactions() {
19502
+ var _a;
19451
19503
  if (this._interactions !== undefined) {
19452
19504
  return this._interactions;
19453
19505
  }
@@ -19464,6 +19516,7 @@ class GoogleGenAI {
19464
19516
  clientAdapter: this.apiClient,
19465
19517
  defaultHeaders: this.apiClient.getDefaultHeaders(),
19466
19518
  timeout: httpOpts === null || httpOpts === void 0 ? void 0 : httpOpts.timeout,
19519
+ maxRetries: (_a = httpOpts === null || httpOpts === void 0 ? void 0 : httpOpts.retryOptions) === null || _a === void 0 ? void 0 : _a.attempts,
19467
19520
  });
19468
19521
  this._interactions = nextGenClient.interactions;
19469
19522
  return this._interactions;