diff --git a/.changeset/quick-actors-beam.md b/.changeset/quick-actors-beam.md new file mode 100644 index 00000000..d304e9e4 --- /dev/null +++ b/.changeset/quick-actors-beam.md @@ -0,0 +1,5 @@ +--- +"@google/generative-ai": minor +--- + +Export error classes and add more properties to fetch errors. diff --git a/docs/reference/main/generative-ai.googlegenerativeaierror._constructor_.md b/docs/reference/main/generative-ai.googlegenerativeaierror._constructor_.md new file mode 100644 index 00000000..1addcdd3 --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeaierror._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIError](./generative-ai.googlegenerativeaierror.md) > [(constructor)](./generative-ai.googlegenerativeaierror._constructor_.md) + +## GoogleGenerativeAIError.(constructor) + +Constructs a new instance of the `GoogleGenerativeAIError` class + +**Signature:** + +```typescript +constructor(message: string); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| message | string | | + diff --git a/docs/reference/main/generative-ai.googlegenerativeaierror.md b/docs/reference/main/generative-ai.googlegenerativeaierror.md new file mode 100644 index 00000000..8e1da55c --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeaierror.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIError](./generative-ai.googlegenerativeaierror.md) + +## GoogleGenerativeAIError class + +Basic error type for this SDK. + +**Signature:** + +```typescript +export declare class GoogleGenerativeAIError extends Error +``` +**Extends:** Error + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(message)](./generative-ai.googlegenerativeaierror._constructor_.md) | | Constructs a new instance of the GoogleGenerativeAIError class | + diff --git a/docs/reference/main/generative-ai.googlegenerativeaifetcherror._constructor_.md b/docs/reference/main/generative-ai.googlegenerativeaifetcherror._constructor_.md new file mode 100644 index 00000000..76217150 --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeaifetcherror._constructor_.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIFetchError](./generative-ai.googlegenerativeaifetcherror.md) > [(constructor)](./generative-ai.googlegenerativeaifetcherror._constructor_.md) + +## GoogleGenerativeAIFetchError.(constructor) + +Constructs a new instance of the `GoogleGenerativeAIFetchError` class + +**Signature:** + +```typescript +constructor(message: string, status?: number, statusText?: string, errorDetails?: ErrorDetails[]); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| message | string | | +| status | number | _(Optional)_ | +| statusText | string | _(Optional)_ | +| errorDetails | ErrorDetails\[\] | _(Optional)_ | + diff --git a/docs/reference/main/generative-ai.googlegenerativeaifetcherror.errordetails.md b/docs/reference/main/generative-ai.googlegenerativeaifetcherror.errordetails.md new file mode 100644 index 00000000..6bc20ff1 --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeaifetcherror.errordetails.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIFetchError](./generative-ai.googlegenerativeaifetcherror.md) > [errorDetails](./generative-ai.googlegenerativeaifetcherror.errordetails.md) + +## GoogleGenerativeAIFetchError.errorDetails property + +**Signature:** + +```typescript +errorDetails?: ErrorDetails[]; +``` diff --git a/docs/reference/main/generative-ai.googlegenerativeaifetcherror.md b/docs/reference/main/generative-ai.googlegenerativeaifetcherror.md new file mode 100644 index 00000000..6ff5c80d --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeaifetcherror.md @@ -0,0 +1,29 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIFetchError](./generative-ai.googlegenerativeaifetcherror.md) + +## GoogleGenerativeAIFetchError class + +Error class covering HTTP errors when calling the server. Includes HTTP status, statusText, and optional details, if provided in the server response. + +**Signature:** + +```typescript +export declare class GoogleGenerativeAIFetchError extends GoogleGenerativeAIError +``` +**Extends:** [GoogleGenerativeAIError](./generative-ai.googlegenerativeaierror.md) + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(message, status, statusText, errorDetails)](./generative-ai.googlegenerativeaifetcherror._constructor_.md) | | Constructs a new instance of the GoogleGenerativeAIFetchError class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [errorDetails?](./generative-ai.googlegenerativeaifetcherror.errordetails.md) | | ErrorDetails\[\] | _(Optional)_ | +| [status?](./generative-ai.googlegenerativeaifetcherror.status.md) | | number | _(Optional)_ | +| [statusText?](./generative-ai.googlegenerativeaifetcherror.statustext.md) | | string | _(Optional)_ | + diff --git a/docs/reference/main/generative-ai.googlegenerativeaifetcherror.status.md b/docs/reference/main/generative-ai.googlegenerativeaifetcherror.status.md new file mode 100644 index 00000000..ccad093a --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeaifetcherror.status.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIFetchError](./generative-ai.googlegenerativeaifetcherror.md) > [status](./generative-ai.googlegenerativeaifetcherror.status.md) + +## GoogleGenerativeAIFetchError.status property + +**Signature:** + +```typescript +status?: number; +``` diff --git a/docs/reference/main/generative-ai.googlegenerativeaifetcherror.statustext.md b/docs/reference/main/generative-ai.googlegenerativeaifetcherror.statustext.md new file mode 100644 index 00000000..923a927d --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeaifetcherror.statustext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIFetchError](./generative-ai.googlegenerativeaifetcherror.md) > [statusText](./generative-ai.googlegenerativeaifetcherror.statustext.md) + +## GoogleGenerativeAIFetchError.statusText property + +**Signature:** + +```typescript +statusText?: string; +``` diff --git a/docs/reference/main/generative-ai.googlegenerativeairesponseerror._constructor_.md b/docs/reference/main/generative-ai.googlegenerativeairesponseerror._constructor_.md new file mode 100644 index 00000000..019c6073 --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeairesponseerror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIResponseError](./generative-ai.googlegenerativeairesponseerror.md) > [(constructor)](./generative-ai.googlegenerativeairesponseerror._constructor_.md) + +## GoogleGenerativeAIResponseError.(constructor) + +Constructs a new instance of the `GoogleGenerativeAIResponseError` class + +**Signature:** + +```typescript +constructor(message: string, response?: T); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| message | string | | +| response | T | _(Optional)_ | + diff --git a/docs/reference/main/generative-ai.googlegenerativeairesponseerror.md b/docs/reference/main/generative-ai.googlegenerativeairesponseerror.md new file mode 100644 index 00000000..c6b628fc --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeairesponseerror.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIResponseError](./generative-ai.googlegenerativeairesponseerror.md) + +## GoogleGenerativeAIResponseError class + +Errors in the contents of a response from the model. This includes parsing errors, or responses including a safety block reason. + +**Signature:** + +```typescript +export declare class GoogleGenerativeAIResponseError extends GoogleGenerativeAIError +``` +**Extends:** [GoogleGenerativeAIError](./generative-ai.googlegenerativeaierror.md) + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(message, response)](./generative-ai.googlegenerativeairesponseerror._constructor_.md) | | Constructs a new instance of the GoogleGenerativeAIResponseError class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [response?](./generative-ai.googlegenerativeairesponseerror.response.md) | | T | _(Optional)_ | + diff --git a/docs/reference/main/generative-ai.googlegenerativeairesponseerror.response.md b/docs/reference/main/generative-ai.googlegenerativeairesponseerror.response.md new file mode 100644 index 00000000..335abdcf --- /dev/null +++ b/docs/reference/main/generative-ai.googlegenerativeairesponseerror.response.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GoogleGenerativeAIResponseError](./generative-ai.googlegenerativeairesponseerror.md) > [response](./generative-ai.googlegenerativeairesponseerror.response.md) + +## GoogleGenerativeAIResponseError.response property + +**Signature:** + +```typescript +response?: T; +``` diff --git a/docs/reference/main/generative-ai.md b/docs/reference/main/generative-ai.md index a231bee1..2a31e0f2 100644 --- a/docs/reference/main/generative-ai.md +++ b/docs/reference/main/generative-ai.md @@ -11,6 +11,9 @@ | [ChatSession](./generative-ai.chatsession.md) | ChatSession class that enables sending chat messages and stores history of sent and received messages so far. | | [GenerativeModel](./generative-ai.generativemodel.md) | Class for generative model APIs. | | [GoogleGenerativeAI](./generative-ai.googlegenerativeai.md) | Top-level class for this SDK | +| [GoogleGenerativeAIError](./generative-ai.googlegenerativeaierror.md) | Basic error type for this SDK. | +| [GoogleGenerativeAIFetchError](./generative-ai.googlegenerativeaifetcherror.md) | Error class covering HTTP errors when calling the server. Includes HTTP status, statusText, and optional details, if provided in the server response. | +| [GoogleGenerativeAIResponseError](./generative-ai.googlegenerativeairesponseerror.md) | Errors in the contents of a response from the model. This includes parsing errors, or responses including a safety block reason. | ## Enumerations diff --git a/packages/main/src/errors.ts b/packages/main/src/errors.ts index 795049de..4ab29a15 100644 --- a/packages/main/src/errors.ts +++ b/packages/main/src/errors.ts @@ -15,12 +15,21 @@ * limitations under the License. */ +/** + * Basic error type for this SDK. + * @public + */ export class GoogleGenerativeAIError extends Error { constructor(message: string) { super(`[GoogleGenerativeAI Error]: ${message}`); } } +/** + * Errors in the contents of a response from the model. This includes parsing + * errors, or responses including a safety block reason. + * @public + */ export class GoogleGenerativeAIResponseError< T, > extends GoogleGenerativeAIError { @@ -31,3 +40,30 @@ export class GoogleGenerativeAIResponseError< super(message); } } + +/** + * Error class covering HTTP errors when calling the server. Includes HTTP + * status, statusText, and optional details, if provided in the server response. + */ +export class GoogleGenerativeAIFetchError extends GoogleGenerativeAIError { + constructor( + message: string, + public status?: number, + public statusText?: string, + public errorDetails?: ErrorDetails[], + ) { + super(message); + } +} + +/** + * Details object that may be included in an error response. + * @public + */ +interface ErrorDetails { + "@type"?: string; + reason?: string; + domain?: string; + metadata?: Record; + [key: string]: unknown; +} diff --git a/packages/main/src/files/request.test.ts b/packages/main/src/files/request.test.ts index 225f8676..ad92dda7 100644 --- a/packages/main/src/files/request.test.ts +++ b/packages/main/src/files/request.test.ts @@ -22,6 +22,7 @@ import * as chaiAsPromised from "chai-as-promised"; import { DEFAULT_API_VERSION, DEFAULT_BASE_URL } from "../requests/request"; import { FilesRequestUrl, makeFilesRequest } from "./request"; import { FilesTask } from "./constants"; +import { GoogleGenerativeAIFetchError } from "../errors"; use(sinonChai); use(chaiAsPromised); @@ -97,9 +98,22 @@ describe("Files API - request methods", () => { const url = new FilesRequestUrl(FilesTask.GET, "key", { timeout: 0 }); const headers = new Headers(); - await expect( - makeFilesRequest(url, headers, new Blob(), fetchStub as typeof fetch), - ).to.be.rejectedWith("500 AbortError"); + try { + await makeFilesRequest( + url, + headers, + new Blob(), + fetchStub as typeof fetch, + ); + } catch (e) { + expect((e as GoogleGenerativeAIFetchError).message).to.include( + "500 AbortError", + ); + expect((e as GoogleGenerativeAIFetchError).status).to.equal(500); + expect((e as GoogleGenerativeAIFetchError).statusText).to.equal( + "AbortError", + ); + } expect(fetchStub).to.be.calledOnce; }); it("Network error, no response.json()", async () => { @@ -110,9 +124,22 @@ describe("Files API - request methods", () => { } as Response); const url = new FilesRequestUrl(FilesTask.GET, "key"); const headers = new Headers(); - await expect( - makeFilesRequest(url, headers, new Blob(), fetchStub as typeof fetch), - ).to.be.rejectedWith(/500 Server Error/); + try { + await makeFilesRequest( + url, + headers, + new Blob(), + fetchStub as typeof fetch, + ); + } catch (e) { + expect((e as GoogleGenerativeAIFetchError).message).to.include( + "500 Server Error", + ); + expect((e as GoogleGenerativeAIFetchError).status).to.equal(500); + expect((e as GoogleGenerativeAIFetchError).statusText).to.equal( + "Server Error", + ); + } expect(fetchStub).to.be.calledOnce; }); it("Network error, includes response.json()", async () => { @@ -124,9 +151,22 @@ describe("Files API - request methods", () => { } as Response); const url = new FilesRequestUrl(FilesTask.GET, "key"); const headers = new Headers(); - await expect( - makeFilesRequest(url, headers, new Blob(), fetchStub as typeof fetch), - ).to.be.rejectedWith(/500 Server Error.*extra info/); + try { + await makeFilesRequest( + url, + headers, + new Blob(), + fetchStub as typeof fetch, + ); + } catch (e) { + expect((e as GoogleGenerativeAIFetchError).message).to.match( + /500 Server Error.+extra info/, + ); + expect((e as GoogleGenerativeAIFetchError).status).to.equal(500); + expect((e as GoogleGenerativeAIFetchError).statusText).to.equal( + "Server Error", + ); + } expect(fetchStub).to.be.calledOnce; }); it("Network error, includes response.json() and details", async () => { @@ -150,11 +190,25 @@ describe("Files API - request methods", () => { } as Response); const url = new FilesRequestUrl(FilesTask.GET, "key"); const headers = new Headers(); - await expect( - makeFilesRequest(url, headers, new Blob(), fetchStub as typeof fetch), - ).to.be.rejectedWith( - /500 Server Error.*extra info.*generic::invalid_argument/, - ); + try { + await makeFilesRequest( + url, + headers, + new Blob(), + fetchStub as typeof fetch, + ); + } catch (e) { + expect((e as GoogleGenerativeAIFetchError).message).to.match( + /500 Server Error.*extra info.*generic::invalid_argument/, + ); + expect((e as GoogleGenerativeAIFetchError).status).to.equal(500); + expect((e as GoogleGenerativeAIFetchError).statusText).to.equal( + "Server Error", + ); + expect( + (e as GoogleGenerativeAIFetchError).errorDetails[0].detail, + ).to.include("generic::invalid_argument"); + } expect(fetchStub).to.be.calledOnce; }); }); diff --git a/packages/main/src/files/request.ts b/packages/main/src/files/request.ts index e7b27ea1..489a1022 100644 --- a/packages/main/src/files/request.ts +++ b/packages/main/src/files/request.ts @@ -15,7 +15,10 @@ * limitations under the License. */ -import { GoogleGenerativeAIError } from "../errors"; +import { + GoogleGenerativeAIError, + GoogleGenerativeAIFetchError, +} from "../errors"; import { DEFAULT_API_VERSION, DEFAULT_BASE_URL, @@ -93,26 +96,36 @@ export async function makeFilesRequest( const response = await fetchFn(url.toString(), requestInit); if (!response.ok) { let message = ""; + let errorDetails; try { const json = await response.json(); message = json.error.message; if (json.error.details) { message += ` ${JSON.stringify(json.error.details)}`; + errorDetails = json.error.details; } } catch (e) { // ignored } - throw new Error(`[${response.status} ${response.statusText}] ${message}`); + throw new GoogleGenerativeAIFetchError( + `Error fetching from ${url.toString()}: [${response.status} ${ + response.statusText + }] ${message}`, + response.status, + response.statusText, + errorDetails, + ); } else { return response; } } catch (e) { - const err = new GoogleGenerativeAIError( - `Error on task type: ${url.task} fetching from ${url.toString()}: ${ - e.message - }`, - ); - err.stack = e.stack; + let err = e; + if (!(e instanceof GoogleGenerativeAIFetchError)) { + err = new GoogleGenerativeAIError( + `Error fetching from ${url.toString()}: ${e.message}`, + ); + err.stack = e.stack; + } throw err; } } diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index 903ccb2b..ec9db61a 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts @@ -17,3 +17,4 @@ export * from "../types"; export * from "./gen-ai"; +export * from "./errors"; diff --git a/packages/main/src/requests/request.test.ts b/packages/main/src/requests/request.test.ts index 288f1d9a..6f02f6cb 100644 --- a/packages/main/src/requests/request.test.ts +++ b/packages/main/src/requests/request.test.ts @@ -27,6 +27,7 @@ import { _makeRequestInternal, constructRequest, } from "./request"; +import { GoogleGenerativeAIFetchError } from "../errors"; use(sinonChai); use(chaiAsPromised); @@ -183,8 +184,8 @@ describe("request methods", () => { statusText: "AbortError", } as Response); - await expect( - _makeRequestInternal( + try { + await _makeRequestInternal( "model-name", Task.GENERATE_CONTENT, "key", @@ -194,8 +195,16 @@ describe("request methods", () => { timeout: 0, }, fetchStub as typeof fetch, - ), - ).to.be.rejectedWith("500 AbortError"); + ); + } catch (e) { + expect((e as GoogleGenerativeAIFetchError).status).to.equal(500); + expect((e as GoogleGenerativeAIFetchError).statusText).to.equal( + "AbortError", + ); + expect((e as GoogleGenerativeAIFetchError).message).to.include( + "500 AbortError", + ); + } expect(fetchStub).to.be.calledOnce; }); it("Network error, no response.json()", async () => { @@ -204,8 +213,8 @@ describe("request methods", () => { status: 500, statusText: "Server Error", } as Response); - await expect( - _makeRequestInternal( + try { + await _makeRequestInternal( "model-name", Task.GENERATE_CONTENT, "key", @@ -213,8 +222,16 @@ describe("request methods", () => { "", {}, fetchStub as typeof fetch, - ), - ).to.be.rejectedWith(/500 Server Error/); + ); + } catch (e) { + expect((e as GoogleGenerativeAIFetchError).status).to.equal(500); + expect((e as GoogleGenerativeAIFetchError).statusText).to.equal( + "Server Error", + ); + expect((e as GoogleGenerativeAIFetchError).message).to.include( + "500 Server Error", + ); + } expect(fetchStub).to.be.calledOnce; }); it("Network error, includes response.json()", async () => { @@ -224,8 +241,9 @@ describe("request methods", () => { statusText: "Server Error", json: () => Promise.resolve({ error: { message: "extra info" } }), } as Response); - await expect( - _makeRequestInternal( + + try { + await _makeRequestInternal( "model-name", Task.GENERATE_CONTENT, "key", @@ -233,8 +251,16 @@ describe("request methods", () => { "", {}, fetchStub as typeof fetch, - ), - ).to.be.rejectedWith(/500 Server Error.*extra info/); + ); + } catch (e) { + expect((e as GoogleGenerativeAIFetchError).status).to.equal(500); + expect((e as GoogleGenerativeAIFetchError).statusText).to.equal( + "Server Error", + ); + expect((e as GoogleGenerativeAIFetchError).message).to.match( + /500 Server Error.*extra info/, + ); + } expect(fetchStub).to.be.calledOnce; }); it("Network error, includes response.json() and details", async () => { @@ -256,8 +282,9 @@ describe("request methods", () => { }, }), } as Response); - await expect( - _makeRequestInternal( + + try { + await _makeRequestInternal( "model-name", Task.GENERATE_CONTENT, "key", @@ -265,10 +292,21 @@ describe("request methods", () => { "", {}, fetchStub as typeof fetch, - ), - ).to.be.rejectedWith( - /500 Server Error.*extra info.*generic::invalid_argument/, - ); + ); + } catch (e) { + expect((e as GoogleGenerativeAIFetchError).status).to.equal(500); + expect((e as GoogleGenerativeAIFetchError).statusText).to.equal( + "Server Error", + ); + expect( + (e as GoogleGenerativeAIFetchError).errorDetails[0].detail, + ).to.equal( + "[ORIGINAL ERROR] generic::invalid_argument: invalid status photos.thumbnailer.Status.Code::5: Source image 0 too short", + ); + expect((e as GoogleGenerativeAIFetchError).message).to.match( + /500 Server Error.*extra info.*generic::invalid_argument/, + ); + } expect(fetchStub).to.be.calledOnce; }); }); diff --git a/packages/main/src/requests/request.ts b/packages/main/src/requests/request.ts index dac7ce33..2485ef99 100644 --- a/packages/main/src/requests/request.ts +++ b/packages/main/src/requests/request.ts @@ -16,7 +16,10 @@ */ import { RequestOptions } from "../../types"; -import { GoogleGenerativeAIError } from "../errors"; +import { + GoogleGenerativeAIError, + GoogleGenerativeAIFetchError, +} from "../errors"; export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com"; @@ -143,22 +146,34 @@ export async function _makeRequestInternal( response = await fetchFn(request.url, request.fetchOptions); if (!response.ok) { let message = ""; + let errorDetails; try { const json = await response.json(); message = json.error.message; if (json.error.details) { message += ` ${JSON.stringify(json.error.details)}`; + errorDetails = json.error.details; } } catch (e) { // ignored } - throw new Error(`[${response.status} ${response.statusText}] ${message}`); + throw new GoogleGenerativeAIFetchError( + `Error fetching from ${url.toString()}: [${response.status} ${ + response.statusText + }] ${message}`, + response.status, + response.statusText, + errorDetails, + ); } } catch (e) { - const err = new GoogleGenerativeAIError( - `Error fetching from ${url.toString()}: ${e.message}`, - ); - err.stack = e.stack; + let err = e; + if (!(e instanceof GoogleGenerativeAIFetchError)) { + err = new GoogleGenerativeAIError( + `Error fetching from ${url.toString()}: ${e.message}`, + ); + err.stack = e.stack; + } throw err; } return response;