From 988746546b9e843cdbe7bffda16b57f9e649a73d Mon Sep 17 00:00:00 2001 From: Alex Ostapenko Date: Tue, 12 Mar 2024 18:05:31 +0100 Subject: [PATCH] Function calling (#32) * initial function calling * working * added sample * changeset * fixed text * removed InputContent interface * fixed validation * reference for docs * addressed comments * copyright and docs * fix --- .changeset/stupid-knives-compete.md | 5 + docs/reference/generative-ai.content.md | 4 +- ....role.md => generative-ai.content.role.md} | 6 +- ...cedgeneratecontentresponse.functioncall.md | 11 + ...tive-ai.enhancedgeneratecontentresponse.md | 1 + .../generative-ai.functioncall.args.md | 11 + docs/reference/generative-ai.functioncall.md | 21 + .../generative-ai.functioncall.name.md | 11 + ...rative-ai.functioncallpart.functioncall.md | 11 + ...ve-ai.functioncallpart.functionresponse.md | 11 + ...nerative-ai.functioncallpart.inlinedata.md | 11 + .../generative-ai.functioncallpart.md | 23 + .../generative-ai.functioncallpart.text.md | 11 + ...tive-ai.functiondeclaration.description.md | 13 + .../generative-ai.functiondeclaration.md | 22 + .../generative-ai.functiondeclaration.name.md | 13 + ...ative-ai.functiondeclaration.parameters.md | 30 ++ ...i.functiondeclarationschema.description.md | 13 + ...generative-ai.functiondeclarationschema.md | 23 + ...ai.functiondeclarationschema.properties.md | 15 + ...e-ai.functiondeclarationschema.required.md | 13 + ...ative-ai.functiondeclarationschema.type.md | 13 + ...ondeclarationschemaproperty.description.md | 13 + ....functiondeclarationschemaproperty.enum.md | 13 + ...nctiondeclarationschemaproperty.example.md | 13 + ...unctiondeclarationschemaproperty.format.md | 13 + ...functiondeclarationschemaproperty.items.md | 13 + ...ve-ai.functiondeclarationschemaproperty.md | 28 ++ ...ctiondeclarationschemaproperty.nullable.md | 13 + ...iondeclarationschemaproperty.properties.md | 15 + ...ctiondeclarationschemaproperty.required.md | 13 + ....functiondeclarationschemaproperty.type.md | 13 + ...rative-ai.functiondeclarationschematype.md | 25 ++ ...ondeclarationstool.functiondeclarations.md | 13 + .../generative-ai.functiondeclarationstool.md | 20 + .../generative-ai.functionresponse.md | 21 + .../generative-ai.functionresponse.name.md | 11 + ...generative-ai.functionresponse.response.md | 11 + ...ve-ai.functionresponsepart.functioncall.md | 11 + ...i.functionresponsepart.functionresponse.md | 11 + ...tive-ai.functionresponsepart.inlinedata.md | 11 + .../generative-ai.functionresponsepart.md | 23 + ...generative-ai.functionresponsepart.text.md | 11 + .../generative-ai.generatecontentrequest.md | 1 + ...erative-ai.generatecontentrequest.tools.md | 11 + .../generative-ai.generativemodel.md | 1 + .../generative-ai.generativemodel.tools.md | 11 + ...nerative-ai.inlinedatapart.functioncall.md | 11 + ...tive-ai.inlinedatapart.functionresponse.md | 11 + .../reference/generative-ai.inlinedatapart.md | 2 + docs/reference/generative-ai.inputcontent.md | 21 - .../generative-ai.inputcontent.parts.md | 11 - docs/reference/generative-ai.md | 18 +- docs/reference/generative-ai.modelparams.md | 1 + .../generative-ai.modelparams.tools.md | 11 + docs/reference/generative-ai.part.md | 4 +- .../reference/generative-ai.possible_roles.md | 13 + docs/reference/generative-ai.role.md | 15 + .../generative-ai.startchatparams.history.md | 2 +- .../generative-ai.startchatparams.md | 3 +- .../generative-ai.startchatparams.tools.md | 11 + .../generative-ai.textpart.functioncall.md | 11 + ...generative-ai.textpart.functionresponse.md | 11 + docs/reference/generative-ai.textpart.md | 2 + docs/reference/generative-ai.tool.md | 15 + packages/main/.eslintrc.js | 2 +- packages/main/.mocharc.js | 2 +- packages/main/rollup.config.mjs | 2 +- packages/main/src/errors.ts | 2 +- packages/main/src/gen-ai.test.ts | 2 +- packages/main/src/gen-ai.ts | 2 +- packages/main/src/index.ts | 2 +- .../src/methods/chat-session-helpers.test.ts | 153 +++++++ .../main/src/methods/chat-session-helpers.ts | 98 +++++ .../main/src/methods/chat-session.test.ts | 2 +- packages/main/src/methods/chat-session.ts | 19 +- packages/main/src/methods/count-tokens.ts | 2 +- packages/main/src/methods/embed-content.ts | 2 +- .../main/src/methods/generate-content.test.ts | 14 +- packages/main/src/methods/generate-content.ts | 2 +- .../main/src/models/generative-model.test.ts | 2 +- packages/main/src/models/generative-model.ts | 12 +- packages/main/src/requests/request-helpers.ts | 57 ++- packages/main/src/requests/request.test.ts | 2 +- packages/main/src/requests/request.ts | 2 +- .../src/requests/response-helpers.test.ts | 41 +- .../main/src/requests/response-helpers.ts | 40 +- .../main/src/requests/stream-reader.test.ts | 27 +- packages/main/src/requests/stream-reader.ts | 18 +- .../node/count-tokens.test.ts | 49 +++ .../node/embed-content.test.ts | 43 ++ .../node/generate-content-multimodal.test.ts | 68 +++ .../node/generate-content-tools.test.ts | 265 ++++++++++++ .../node/generate-content.test.ts | 149 +++++++ .../main/test-integration/node/index.test.ts | 394 ------------------ .../node/start-chat-tools.test.ts | 99 +++++ .../test-integration/node/start-chat.test.ts | 173 ++++++++ .../main/test-integration/web/index.test.ts | 2 +- packages/main/test-utils/base64cat.ts | 2 +- packages/main/test-utils/mock-response.ts | 2 +- .../streaming-success-function-call-short.txt | 2 + packages/main/types/content.ts | 73 +++- packages/main/types/enums.ts | 14 +- packages/main/types/index.ts | 2 +- packages/main/types/requests.ts | 142 ++++++- packages/main/types/responses.ts | 5 +- samples/node/advanced-chat.js | 4 +- samples/node/advanced-function-calling.js | 111 +++++ 108 files changed, 2393 insertions(+), 517 deletions(-) create mode 100644 .changeset/stupid-knives-compete.md rename docs/reference/{generative-ai.inputcontent.role.md => generative-ai.content.role.md} (52%) create mode 100644 docs/reference/generative-ai.enhancedgeneratecontentresponse.functioncall.md create mode 100644 docs/reference/generative-ai.functioncall.args.md create mode 100644 docs/reference/generative-ai.functioncall.md create mode 100644 docs/reference/generative-ai.functioncall.name.md create mode 100644 docs/reference/generative-ai.functioncallpart.functioncall.md create mode 100644 docs/reference/generative-ai.functioncallpart.functionresponse.md create mode 100644 docs/reference/generative-ai.functioncallpart.inlinedata.md create mode 100644 docs/reference/generative-ai.functioncallpart.md create mode 100644 docs/reference/generative-ai.functioncallpart.text.md create mode 100644 docs/reference/generative-ai.functiondeclaration.description.md create mode 100644 docs/reference/generative-ai.functiondeclaration.md create mode 100644 docs/reference/generative-ai.functiondeclaration.name.md create mode 100644 docs/reference/generative-ai.functiondeclaration.parameters.md create mode 100644 docs/reference/generative-ai.functiondeclarationschema.description.md create mode 100644 docs/reference/generative-ai.functiondeclarationschema.md create mode 100644 docs/reference/generative-ai.functiondeclarationschema.properties.md create mode 100644 docs/reference/generative-ai.functiondeclarationschema.required.md create mode 100644 docs/reference/generative-ai.functiondeclarationschema.type.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.description.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.enum.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.example.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.format.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.items.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.nullable.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.properties.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.required.md create mode 100644 docs/reference/generative-ai.functiondeclarationschemaproperty.type.md create mode 100644 docs/reference/generative-ai.functiondeclarationschematype.md create mode 100644 docs/reference/generative-ai.functiondeclarationstool.functiondeclarations.md create mode 100644 docs/reference/generative-ai.functiondeclarationstool.md create mode 100644 docs/reference/generative-ai.functionresponse.md create mode 100644 docs/reference/generative-ai.functionresponse.name.md create mode 100644 docs/reference/generative-ai.functionresponse.response.md create mode 100644 docs/reference/generative-ai.functionresponsepart.functioncall.md create mode 100644 docs/reference/generative-ai.functionresponsepart.functionresponse.md create mode 100644 docs/reference/generative-ai.functionresponsepart.inlinedata.md create mode 100644 docs/reference/generative-ai.functionresponsepart.md create mode 100644 docs/reference/generative-ai.functionresponsepart.text.md create mode 100644 docs/reference/generative-ai.generatecontentrequest.tools.md create mode 100644 docs/reference/generative-ai.generativemodel.tools.md create mode 100644 docs/reference/generative-ai.inlinedatapart.functioncall.md create mode 100644 docs/reference/generative-ai.inlinedatapart.functionresponse.md delete mode 100644 docs/reference/generative-ai.inputcontent.md delete mode 100644 docs/reference/generative-ai.inputcontent.parts.md create mode 100644 docs/reference/generative-ai.modelparams.tools.md create mode 100644 docs/reference/generative-ai.possible_roles.md create mode 100644 docs/reference/generative-ai.role.md create mode 100644 docs/reference/generative-ai.startchatparams.tools.md create mode 100644 docs/reference/generative-ai.textpart.functioncall.md create mode 100644 docs/reference/generative-ai.textpart.functionresponse.md create mode 100644 docs/reference/generative-ai.tool.md create mode 100644 packages/main/src/methods/chat-session-helpers.test.ts create mode 100644 packages/main/src/methods/chat-session-helpers.ts create mode 100644 packages/main/test-integration/node/count-tokens.test.ts create mode 100644 packages/main/test-integration/node/embed-content.test.ts create mode 100644 packages/main/test-integration/node/generate-content-multimodal.test.ts create mode 100644 packages/main/test-integration/node/generate-content-tools.test.ts create mode 100644 packages/main/test-integration/node/generate-content.test.ts delete mode 100644 packages/main/test-integration/node/index.test.ts create mode 100644 packages/main/test-integration/node/start-chat-tools.test.ts create mode 100644 packages/main/test-integration/node/start-chat.test.ts create mode 100644 packages/main/test-utils/mock-responses/streaming-success-function-call-short.txt create mode 100644 samples/node/advanced-function-calling.js diff --git a/.changeset/stupid-knives-compete.md b/.changeset/stupid-knives-compete.md new file mode 100644 index 00000000..a0037a4f --- /dev/null +++ b/.changeset/stupid-knives-compete.md @@ -0,0 +1,5 @@ +--- +"@google/generative-ai": minor +--- + +Added support for function calling diff --git a/docs/reference/generative-ai.content.md b/docs/reference/generative-ai.content.md index 9576b197..83be490d 100644 --- a/docs/reference/generative-ai.content.md +++ b/docs/reference/generative-ai.content.md @@ -9,13 +9,13 @@ Content type for both prompts and response candidates. **Signature:** ```typescript -export interface Content extends InputContent +export interface Content ``` -**Extends:** [InputContent](./generative-ai.inputcontent.md) ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [parts](./generative-ai.content.parts.md) | | [Part](./generative-ai.part.md)\[\] | | +| [role](./generative-ai.content.role.md) | | [Role](./generative-ai.role.md) | | diff --git a/docs/reference/generative-ai.inputcontent.role.md b/docs/reference/generative-ai.content.role.md similarity index 52% rename from docs/reference/generative-ai.inputcontent.role.md rename to docs/reference/generative-ai.content.role.md index baaf88a0..3a151e46 100644 --- a/docs/reference/generative-ai.inputcontent.role.md +++ b/docs/reference/generative-ai.content.role.md @@ -1,11 +1,11 @@ -[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [InputContent](./generative-ai.inputcontent.md) > [role](./generative-ai.inputcontent.role.md) +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [Content](./generative-ai.content.md) > [role](./generative-ai.content.role.md) -## InputContent.role property +## Content.role property **Signature:** ```typescript -role: string; +role: Role; ``` diff --git a/docs/reference/generative-ai.enhancedgeneratecontentresponse.functioncall.md b/docs/reference/generative-ai.enhancedgeneratecontentresponse.functioncall.md new file mode 100644 index 00000000..c8f636b9 --- /dev/null +++ b/docs/reference/generative-ai.enhancedgeneratecontentresponse.functioncall.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [EnhancedGenerateContentResponse](./generative-ai.enhancedgeneratecontentresponse.md) > [functionCall](./generative-ai.enhancedgeneratecontentresponse.functioncall.md) + +## EnhancedGenerateContentResponse.functionCall property + +**Signature:** + +```typescript +functionCall: () => FunctionCall | undefined; +``` diff --git a/docs/reference/generative-ai.enhancedgeneratecontentresponse.md b/docs/reference/generative-ai.enhancedgeneratecontentresponse.md index c3622d45..05b1d204 100644 --- a/docs/reference/generative-ai.enhancedgeneratecontentresponse.md +++ b/docs/reference/generative-ai.enhancedgeneratecontentresponse.md @@ -17,5 +17,6 @@ export interface EnhancedGenerateContentResponse extends GenerateContentResponse | Property | Modifiers | Type | Description | | --- | --- | --- | --- | +| [functionCall](./generative-ai.enhancedgeneratecontentresponse.functioncall.md) | | () => [FunctionCall](./generative-ai.functioncall.md) \| undefined | | | [text](./generative-ai.enhancedgeneratecontentresponse.text.md) | | () => string | Returns the text string from the response, if available. Throws if the prompt or candidate was blocked. | diff --git a/docs/reference/generative-ai.functioncall.args.md b/docs/reference/generative-ai.functioncall.args.md new file mode 100644 index 00000000..f05c46b4 --- /dev/null +++ b/docs/reference/generative-ai.functioncall.args.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionCall](./generative-ai.functioncall.md) > [args](./generative-ai.functioncall.args.md) + +## FunctionCall.args property + +**Signature:** + +```typescript +args: object; +``` diff --git a/docs/reference/generative-ai.functioncall.md b/docs/reference/generative-ai.functioncall.md new file mode 100644 index 00000000..b6f88eed --- /dev/null +++ b/docs/reference/generative-ai.functioncall.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionCall](./generative-ai.functioncall.md) + +## FunctionCall interface + +A predicted \[FunctionCall\] returned from the model that contains a string representing the \[FunctionDeclaration.name\] and a structured JSON object containing the parameters and their values. + +**Signature:** + +```typescript +export interface FunctionCall +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [args](./generative-ai.functioncall.args.md) | | object | | +| [name](./generative-ai.functioncall.name.md) | | string | | + diff --git a/docs/reference/generative-ai.functioncall.name.md b/docs/reference/generative-ai.functioncall.name.md new file mode 100644 index 00000000..9375e8e8 --- /dev/null +++ b/docs/reference/generative-ai.functioncall.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionCall](./generative-ai.functioncall.md) > [name](./generative-ai.functioncall.name.md) + +## FunctionCall.name property + +**Signature:** + +```typescript +name: string; +``` diff --git a/docs/reference/generative-ai.functioncallpart.functioncall.md b/docs/reference/generative-ai.functioncallpart.functioncall.md new file mode 100644 index 00000000..578587aa --- /dev/null +++ b/docs/reference/generative-ai.functioncallpart.functioncall.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionCallPart](./generative-ai.functioncallpart.md) > [functionCall](./generative-ai.functioncallpart.functioncall.md) + +## FunctionCallPart.functionCall property + +**Signature:** + +```typescript +functionCall: FunctionCall; +``` diff --git a/docs/reference/generative-ai.functioncallpart.functionresponse.md b/docs/reference/generative-ai.functioncallpart.functionresponse.md new file mode 100644 index 00000000..3a37c84c --- /dev/null +++ b/docs/reference/generative-ai.functioncallpart.functionresponse.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionCallPart](./generative-ai.functioncallpart.md) > [functionResponse](./generative-ai.functioncallpart.functionresponse.md) + +## FunctionCallPart.functionResponse property + +**Signature:** + +```typescript +functionResponse?: never; +``` diff --git a/docs/reference/generative-ai.functioncallpart.inlinedata.md b/docs/reference/generative-ai.functioncallpart.inlinedata.md new file mode 100644 index 00000000..7dd9624b --- /dev/null +++ b/docs/reference/generative-ai.functioncallpart.inlinedata.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionCallPart](./generative-ai.functioncallpart.md) > [inlineData](./generative-ai.functioncallpart.inlinedata.md) + +## FunctionCallPart.inlineData property + +**Signature:** + +```typescript +inlineData?: never; +``` diff --git a/docs/reference/generative-ai.functioncallpart.md b/docs/reference/generative-ai.functioncallpart.md new file mode 100644 index 00000000..45ed1cdb --- /dev/null +++ b/docs/reference/generative-ai.functioncallpart.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionCallPart](./generative-ai.functioncallpart.md) + +## FunctionCallPart interface + +Content part interface if the part represents FunctionResponse. + +**Signature:** + +```typescript +export interface FunctionCallPart +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [functionCall](./generative-ai.functioncallpart.functioncall.md) | | [FunctionCall](./generative-ai.functioncall.md) | | +| [functionResponse?](./generative-ai.functioncallpart.functionresponse.md) | | never | _(Optional)_ | +| [inlineData?](./generative-ai.functioncallpart.inlinedata.md) | | never | _(Optional)_ | +| [text?](./generative-ai.functioncallpart.text.md) | | never | _(Optional)_ | + diff --git a/docs/reference/generative-ai.functioncallpart.text.md b/docs/reference/generative-ai.functioncallpart.text.md new file mode 100644 index 00000000..03daf162 --- /dev/null +++ b/docs/reference/generative-ai.functioncallpart.text.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionCallPart](./generative-ai.functioncallpart.md) > [text](./generative-ai.functioncallpart.text.md) + +## FunctionCallPart.text property + +**Signature:** + +```typescript +text?: never; +``` diff --git a/docs/reference/generative-ai.functiondeclaration.description.md b/docs/reference/generative-ai.functiondeclaration.description.md new file mode 100644 index 00000000..aea9a3f7 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclaration.description.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclaration](./generative-ai.functiondeclaration.md) > [description](./generative-ai.functiondeclaration.description.md) + +## FunctionDeclaration.description property + +Optional. Description and purpose of the function. Model uses it to decide how and whether to call the function. + +**Signature:** + +```typescript +description?: string; +``` diff --git a/docs/reference/generative-ai.functiondeclaration.md b/docs/reference/generative-ai.functiondeclaration.md new file mode 100644 index 00000000..1657ab4b --- /dev/null +++ b/docs/reference/generative-ai.functiondeclaration.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclaration](./generative-ai.functiondeclaration.md) + +## FunctionDeclaration interface + +Structured representation of a function declaration as defined by the \[OpenAPI 3.0 specification\](https://spec.openapis.org/oas/v3.0.3). Included in this declaration are the function name and parameters. This FunctionDeclaration is a representation of a block of code that can be used as a Tool by the model and executed by the client. + +**Signature:** + +```typescript +export declare interface FunctionDeclaration +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [description?](./generative-ai.functiondeclaration.description.md) | | string | _(Optional)_ Optional. Description and purpose of the function. Model uses it to decide how and whether to call the function. | +| [name](./generative-ai.functiondeclaration.name.md) | | string | The name of the function to call. Must start with a letter or an underscore. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a max length of 64. | +| [parameters?](./generative-ai.functiondeclaration.parameters.md) | | [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) | _(Optional)_ Optional. Describes the parameters to this function in JSON Schema Object format. Reflects the Open API 3.03 Parameter Object. string Key: the name of the parameter. Parameter names are case sensitive. Schema Value: the Schema defining the type used for the parameter. For function with no parameters, this can be left unset. | + diff --git a/docs/reference/generative-ai.functiondeclaration.name.md b/docs/reference/generative-ai.functiondeclaration.name.md new file mode 100644 index 00000000..8df84c9d --- /dev/null +++ b/docs/reference/generative-ai.functiondeclaration.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclaration](./generative-ai.functiondeclaration.md) > [name](./generative-ai.functiondeclaration.name.md) + +## FunctionDeclaration.name property + +The name of the function to call. Must start with a letter or an underscore. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a max length of 64. + +**Signature:** + +```typescript +name: string; +``` diff --git a/docs/reference/generative-ai.functiondeclaration.parameters.md b/docs/reference/generative-ai.functiondeclaration.parameters.md new file mode 100644 index 00000000..f6005c5b --- /dev/null +++ b/docs/reference/generative-ai.functiondeclaration.parameters.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclaration](./generative-ai.functiondeclaration.md) > [parameters](./generative-ai.functiondeclaration.parameters.md) + +## FunctionDeclaration.parameters property + +Optional. Describes the parameters to this function in JSON Schema Object format. Reflects the Open API 3.03 Parameter Object. string Key: the name of the parameter. Parameter names are case sensitive. Schema Value: the Schema defining the type used for the parameter. For function with no parameters, this can be left unset. + +**Signature:** + +```typescript +parameters?: FunctionDeclarationSchema; +``` + +## Example + +with 1 required and 1 optional parameter: type: OBJECT properties: + +``` +param1: + + type: STRING +param2: + + type: INTEGER +required: + + - param1 +``` + diff --git a/docs/reference/generative-ai.functiondeclarationschema.description.md b/docs/reference/generative-ai.functiondeclarationschema.description.md new file mode 100644 index 00000000..a8d37cec --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschema.description.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) > [description](./generative-ai.functiondeclarationschema.description.md) + +## FunctionDeclarationSchema.description property + +Optional. Description of the parameter. + +**Signature:** + +```typescript +description?: string; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschema.md b/docs/reference/generative-ai.functiondeclarationschema.md new file mode 100644 index 00000000..0af8377b --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschema.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) + +## FunctionDeclarationSchema interface + +Schema for parameters passed to [FunctionDeclaration.parameters](./generative-ai.functiondeclaration.parameters.md). + +**Signature:** + +```typescript +export interface FunctionDeclarationSchema +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [description?](./generative-ai.functiondeclarationschema.description.md) | | string | _(Optional)_ Optional. Description of the parameter. | +| [properties](./generative-ai.functiondeclarationschema.properties.md) | | { \[k: string\]: [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md); } | The format of the parameter. | +| [required?](./generative-ai.functiondeclarationschema.required.md) | | string\[\] | _(Optional)_ Optional. Array of required parameters. | +| [type](./generative-ai.functiondeclarationschema.type.md) | | [FunctionDeclarationSchemaType](./generative-ai.functiondeclarationschematype.md) | The type of the parameter. | + diff --git a/docs/reference/generative-ai.functiondeclarationschema.properties.md b/docs/reference/generative-ai.functiondeclarationschema.properties.md new file mode 100644 index 00000000..b1642c49 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschema.properties.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) > [properties](./generative-ai.functiondeclarationschema.properties.md) + +## FunctionDeclarationSchema.properties property + +The format of the parameter. + +**Signature:** + +```typescript +properties: { + [k: string]: FunctionDeclarationSchemaProperty; + }; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschema.required.md b/docs/reference/generative-ai.functiondeclarationschema.required.md new file mode 100644 index 00000000..fdddab0b --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschema.required.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) > [required](./generative-ai.functiondeclarationschema.required.md) + +## FunctionDeclarationSchema.required property + +Optional. Array of required parameters. + +**Signature:** + +```typescript +required?: string[]; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschema.type.md b/docs/reference/generative-ai.functiondeclarationschema.type.md new file mode 100644 index 00000000..7d336a82 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschema.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) > [type](./generative-ai.functiondeclarationschema.type.md) + +## FunctionDeclarationSchema.type property + +The type of the parameter. + +**Signature:** + +```typescript +type: FunctionDeclarationSchemaType; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.description.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.description.md new file mode 100644 index 00000000..24f053b4 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.description.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [description](./generative-ai.functiondeclarationschemaproperty.description.md) + +## FunctionDeclarationSchemaProperty.description property + +Optional. The description of the property. + +**Signature:** + +```typescript +description?: string; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.enum.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.enum.md new file mode 100644 index 00000000..f97b19da --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.enum.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [enum](./generative-ai.functiondeclarationschemaproperty.enum.md) + +## FunctionDeclarationSchemaProperty.enum property + +Optional. The enum of the property. + +**Signature:** + +```typescript +enum?: string[]; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.example.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.example.md new file mode 100644 index 00000000..d429ade7 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.example.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [example](./generative-ai.functiondeclarationschemaproperty.example.md) + +## FunctionDeclarationSchemaProperty.example property + +Optional. The example of the property. + +**Signature:** + +```typescript +example?: unknown; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.format.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.format.md new file mode 100644 index 00000000..2519703b --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.format.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [format](./generative-ai.functiondeclarationschemaproperty.format.md) + +## FunctionDeclarationSchemaProperty.format property + +Optional. The format of the property. + +**Signature:** + +```typescript +format?: string; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.items.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.items.md new file mode 100644 index 00000000..16bac40f --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.items.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [items](./generative-ai.functiondeclarationschemaproperty.items.md) + +## FunctionDeclarationSchemaProperty.items property + +Optional. The items of the property. [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) + +**Signature:** + +```typescript +items?: FunctionDeclarationSchema; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.md new file mode 100644 index 00000000..2c11aeb4 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.md @@ -0,0 +1,28 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) + +## FunctionDeclarationSchemaProperty interface + +Schema is used to define the format of input/output data. Represents a select subset of an OpenAPI 3.0 schema object. More fields may be added in the future as needed. + +**Signature:** + +```typescript +export interface FunctionDeclarationSchemaProperty +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [description?](./generative-ai.functiondeclarationschemaproperty.description.md) | | string | _(Optional)_ Optional. The description of the property. | +| [enum?](./generative-ai.functiondeclarationschemaproperty.enum.md) | | string\[\] | _(Optional)_ Optional. The enum of the property. | +| [example?](./generative-ai.functiondeclarationschemaproperty.example.md) | | unknown | _(Optional)_ Optional. The example of the property. | +| [format?](./generative-ai.functiondeclarationschemaproperty.format.md) | | string | _(Optional)_ Optional. The format of the property. | +| [items?](./generative-ai.functiondeclarationschemaproperty.items.md) | | [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) | _(Optional)_ Optional. The items of the property. [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) | +| [nullable?](./generative-ai.functiondeclarationschemaproperty.nullable.md) | | boolean | _(Optional)_ Optional. Whether the property is nullable. | +| [properties?](./generative-ai.functiondeclarationschemaproperty.properties.md) | | { \[k: string\]: [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md); } | _(Optional)_ Optional. Map of [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md). | +| [required?](./generative-ai.functiondeclarationschemaproperty.required.md) | | string\[\] | _(Optional)_ Optional. Array of required property. | +| [type?](./generative-ai.functiondeclarationschemaproperty.type.md) | | [FunctionDeclarationSchemaType](./generative-ai.functiondeclarationschematype.md) | _(Optional)_ Optional. The type of the property. [FunctionDeclarationSchemaType](./generative-ai.functiondeclarationschematype.md). | + diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.nullable.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.nullable.md new file mode 100644 index 00000000..dee0e1d9 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.nullable.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [nullable](./generative-ai.functiondeclarationschemaproperty.nullable.md) + +## FunctionDeclarationSchemaProperty.nullable property + +Optional. Whether the property is nullable. + +**Signature:** + +```typescript +nullable?: boolean; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.properties.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.properties.md new file mode 100644 index 00000000..74715a77 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.properties.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [properties](./generative-ai.functiondeclarationschemaproperty.properties.md) + +## FunctionDeclarationSchemaProperty.properties property + +Optional. Map of [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md). + +**Signature:** + +```typescript +properties?: { + [k: string]: FunctionDeclarationSchema; + }; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.required.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.required.md new file mode 100644 index 00000000..e6b4321c --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.required.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [required](./generative-ai.functiondeclarationschemaproperty.required.md) + +## FunctionDeclarationSchemaProperty.required property + +Optional. Array of required property. + +**Signature:** + +```typescript +required?: string[]; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschemaproperty.type.md b/docs/reference/generative-ai.functiondeclarationschemaproperty.type.md new file mode 100644 index 00000000..9c049436 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschemaproperty.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) > [type](./generative-ai.functiondeclarationschemaproperty.type.md) + +## FunctionDeclarationSchemaProperty.type property + +Optional. The type of the property. [FunctionDeclarationSchemaType](./generative-ai.functiondeclarationschematype.md). + +**Signature:** + +```typescript +type?: FunctionDeclarationSchemaType; +``` diff --git a/docs/reference/generative-ai.functiondeclarationschematype.md b/docs/reference/generative-ai.functiondeclarationschematype.md new file mode 100644 index 00000000..6d873939 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationschematype.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationSchemaType](./generative-ai.functiondeclarationschematype.md) + +## FunctionDeclarationSchemaType enum + +Contains the list of OpenAPI data types as defined by https://swagger.io/docs/specification/data-models/data-types/ + +**Signature:** + +```typescript +export declare enum FunctionDeclarationSchemaType +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| ARRAY | "ARRAY" | Array type. | +| BOOLEAN | "BOOLEAN" | Boolean type. | +| INTEGER | "INTEGER" | Integer type. | +| NUMBER | "NUMBER" | Number type. | +| OBJECT | "OBJECT" | Object type. | +| STRING | "STRING" | String type. | + diff --git a/docs/reference/generative-ai.functiondeclarationstool.functiondeclarations.md b/docs/reference/generative-ai.functiondeclarationstool.functiondeclarations.md new file mode 100644 index 00000000..4d45fc5e --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationstool.functiondeclarations.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationsTool](./generative-ai.functiondeclarationstool.md) > [functionDeclarations](./generative-ai.functiondeclarationstool.functiondeclarations.md) + +## FunctionDeclarationsTool.functionDeclarations property + +Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating \[FunctionCall\]\[content.part.functionCall\] in the response. User should provide a \[FunctionResponse\]\[content.part.functionResponse\] for each function call in the next turn. Based on the function responses, Model will generate the final response back to the user. Maximum 64 function declarations can be provided. + +**Signature:** + +```typescript +functionDeclarations?: FunctionDeclaration[]; +``` diff --git a/docs/reference/generative-ai.functiondeclarationstool.md b/docs/reference/generative-ai.functiondeclarationstool.md new file mode 100644 index 00000000..94dba378 --- /dev/null +++ b/docs/reference/generative-ai.functiondeclarationstool.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionDeclarationsTool](./generative-ai.functiondeclarationstool.md) + +## FunctionDeclarationsTool interface + +A FunctionDeclarationsTool is a piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. + +**Signature:** + +```typescript +export declare interface FunctionDeclarationsTool +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [functionDeclarations?](./generative-ai.functiondeclarationstool.functiondeclarations.md) | | [FunctionDeclaration](./generative-ai.functiondeclaration.md)\[\] | _(Optional)_ Optional. One or more function declarations to be passed to the model along with the current user query. Model may decide to call a subset of these functions by populating \[FunctionCall\]\[content.part.functionCall\] in the response. User should provide a \[FunctionResponse\]\[content.part.functionResponse\] for each function call in the next turn. Based on the function responses, Model will generate the final response back to the user. Maximum 64 function declarations can be provided. | + diff --git a/docs/reference/generative-ai.functionresponse.md b/docs/reference/generative-ai.functionresponse.md new file mode 100644 index 00000000..ccf31158 --- /dev/null +++ b/docs/reference/generative-ai.functionresponse.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionResponse](./generative-ai.functionresponse.md) + +## FunctionResponse interface + +The result output from a \[FunctionCall\] that contains a string representing the \[FunctionDeclaration.name\] and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a \[FunctionCall\] made based on model prediction. + +**Signature:** + +```typescript +export interface FunctionResponse +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [name](./generative-ai.functionresponse.name.md) | | string | | +| [response](./generative-ai.functionresponse.response.md) | | object | | + diff --git a/docs/reference/generative-ai.functionresponse.name.md b/docs/reference/generative-ai.functionresponse.name.md new file mode 100644 index 00000000..1660034a --- /dev/null +++ b/docs/reference/generative-ai.functionresponse.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionResponse](./generative-ai.functionresponse.md) > [name](./generative-ai.functionresponse.name.md) + +## FunctionResponse.name property + +**Signature:** + +```typescript +name: string; +``` diff --git a/docs/reference/generative-ai.functionresponse.response.md b/docs/reference/generative-ai.functionresponse.response.md new file mode 100644 index 00000000..b2248dee --- /dev/null +++ b/docs/reference/generative-ai.functionresponse.response.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionResponse](./generative-ai.functionresponse.md) > [response](./generative-ai.functionresponse.response.md) + +## FunctionResponse.response property + +**Signature:** + +```typescript +response: object; +``` diff --git a/docs/reference/generative-ai.functionresponsepart.functioncall.md b/docs/reference/generative-ai.functionresponsepart.functioncall.md new file mode 100644 index 00000000..bf4c526b --- /dev/null +++ b/docs/reference/generative-ai.functionresponsepart.functioncall.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionResponsePart](./generative-ai.functionresponsepart.md) > [functionCall](./generative-ai.functionresponsepart.functioncall.md) + +## FunctionResponsePart.functionCall property + +**Signature:** + +```typescript +functionCall?: never; +``` diff --git a/docs/reference/generative-ai.functionresponsepart.functionresponse.md b/docs/reference/generative-ai.functionresponsepart.functionresponse.md new file mode 100644 index 00000000..715c8327 --- /dev/null +++ b/docs/reference/generative-ai.functionresponsepart.functionresponse.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionResponsePart](./generative-ai.functionresponsepart.md) > [functionResponse](./generative-ai.functionresponsepart.functionresponse.md) + +## FunctionResponsePart.functionResponse property + +**Signature:** + +```typescript +functionResponse: FunctionResponse; +``` diff --git a/docs/reference/generative-ai.functionresponsepart.inlinedata.md b/docs/reference/generative-ai.functionresponsepart.inlinedata.md new file mode 100644 index 00000000..f5186809 --- /dev/null +++ b/docs/reference/generative-ai.functionresponsepart.inlinedata.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionResponsePart](./generative-ai.functionresponsepart.md) > [inlineData](./generative-ai.functionresponsepart.inlinedata.md) + +## FunctionResponsePart.inlineData property + +**Signature:** + +```typescript +inlineData?: never; +``` diff --git a/docs/reference/generative-ai.functionresponsepart.md b/docs/reference/generative-ai.functionresponsepart.md new file mode 100644 index 00000000..f5947c8f --- /dev/null +++ b/docs/reference/generative-ai.functionresponsepart.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionResponsePart](./generative-ai.functionresponsepart.md) + +## FunctionResponsePart interface + +Content part interface if the part represents FunctionResponse. + +**Signature:** + +```typescript +export interface FunctionResponsePart +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [functionCall?](./generative-ai.functionresponsepart.functioncall.md) | | never | _(Optional)_ | +| [functionResponse](./generative-ai.functionresponsepart.functionresponse.md) | | [FunctionResponse](./generative-ai.functionresponse.md) | | +| [inlineData?](./generative-ai.functionresponsepart.inlinedata.md) | | never | _(Optional)_ | +| [text?](./generative-ai.functionresponsepart.text.md) | | never | _(Optional)_ | + diff --git a/docs/reference/generative-ai.functionresponsepart.text.md b/docs/reference/generative-ai.functionresponsepart.text.md new file mode 100644 index 00000000..7aca2d41 --- /dev/null +++ b/docs/reference/generative-ai.functionresponsepart.text.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [FunctionResponsePart](./generative-ai.functionresponsepart.md) > [text](./generative-ai.functionresponsepart.text.md) + +## FunctionResponsePart.text property + +**Signature:** + +```typescript +text?: never; +``` diff --git a/docs/reference/generative-ai.generatecontentrequest.md b/docs/reference/generative-ai.generatecontentrequest.md index c7b39ac2..017f0642 100644 --- a/docs/reference/generative-ai.generatecontentrequest.md +++ b/docs/reference/generative-ai.generatecontentrequest.md @@ -18,4 +18,5 @@ export interface GenerateContentRequest extends BaseParams | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [contents](./generative-ai.generatecontentrequest.contents.md) | | [Content](./generative-ai.content.md)\[\] | | +| [tools?](./generative-ai.generatecontentrequest.tools.md) | | [Tool](./generative-ai.tool.md)\[\] | _(Optional)_ | diff --git a/docs/reference/generative-ai.generatecontentrequest.tools.md b/docs/reference/generative-ai.generatecontentrequest.tools.md new file mode 100644 index 00000000..d8639029 --- /dev/null +++ b/docs/reference/generative-ai.generatecontentrequest.tools.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GenerateContentRequest](./generative-ai.generatecontentrequest.md) > [tools](./generative-ai.generatecontentrequest.tools.md) + +## GenerateContentRequest.tools property + +**Signature:** + +```typescript +tools?: Tool[]; +``` diff --git a/docs/reference/generative-ai.generativemodel.md b/docs/reference/generative-ai.generativemodel.md index 5c3fc223..3bbd2007 100644 --- a/docs/reference/generative-ai.generativemodel.md +++ b/docs/reference/generative-ai.generativemodel.md @@ -27,6 +27,7 @@ export declare class GenerativeModel | [model](./generative-ai.generativemodel.model.md) | | string | | | [requestOptions](./generative-ai.generativemodel.requestoptions.md) | | [RequestOptions](./generative-ai.requestoptions.md) | | | [safetySettings](./generative-ai.generativemodel.safetysettings.md) | | [SafetySetting](./generative-ai.safetysetting.md)\[\] | | +| [tools?](./generative-ai.generativemodel.tools.md) | | [Tool](./generative-ai.tool.md)\[\] | _(Optional)_ | ## Methods diff --git a/docs/reference/generative-ai.generativemodel.tools.md b/docs/reference/generative-ai.generativemodel.tools.md new file mode 100644 index 00000000..28319de8 --- /dev/null +++ b/docs/reference/generative-ai.generativemodel.tools.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [GenerativeModel](./generative-ai.generativemodel.md) > [tools](./generative-ai.generativemodel.tools.md) + +## GenerativeModel.tools property + +**Signature:** + +```typescript +tools?: Tool[]; +``` diff --git a/docs/reference/generative-ai.inlinedatapart.functioncall.md b/docs/reference/generative-ai.inlinedatapart.functioncall.md new file mode 100644 index 00000000..b1070992 --- /dev/null +++ b/docs/reference/generative-ai.inlinedatapart.functioncall.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [InlineDataPart](./generative-ai.inlinedatapart.md) > [functionCall](./generative-ai.inlinedatapart.functioncall.md) + +## InlineDataPart.functionCall property + +**Signature:** + +```typescript +functionCall?: never; +``` diff --git a/docs/reference/generative-ai.inlinedatapart.functionresponse.md b/docs/reference/generative-ai.inlinedatapart.functionresponse.md new file mode 100644 index 00000000..8b0bb928 --- /dev/null +++ b/docs/reference/generative-ai.inlinedatapart.functionresponse.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [InlineDataPart](./generative-ai.inlinedatapart.md) > [functionResponse](./generative-ai.inlinedatapart.functionresponse.md) + +## InlineDataPart.functionResponse property + +**Signature:** + +```typescript +functionResponse?: never; +``` diff --git a/docs/reference/generative-ai.inlinedatapart.md b/docs/reference/generative-ai.inlinedatapart.md index 20e9f915..57925b30 100644 --- a/docs/reference/generative-ai.inlinedatapart.md +++ b/docs/reference/generative-ai.inlinedatapart.md @@ -16,6 +16,8 @@ export interface InlineDataPart | Property | Modifiers | Type | Description | | --- | --- | --- | --- | +| [functionCall?](./generative-ai.inlinedatapart.functioncall.md) | | never | _(Optional)_ | +| [functionResponse?](./generative-ai.inlinedatapart.functionresponse.md) | | never | _(Optional)_ | | [inlineData](./generative-ai.inlinedatapart.inlinedata.md) | | [GenerativeContentBlob](./generative-ai.generativecontentblob.md) | | | [text?](./generative-ai.inlinedatapart.text.md) | | never | _(Optional)_ | diff --git a/docs/reference/generative-ai.inputcontent.md b/docs/reference/generative-ai.inputcontent.md deleted file mode 100644 index 52eeaedc..00000000 --- a/docs/reference/generative-ai.inputcontent.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [InputContent](./generative-ai.inputcontent.md) - -## InputContent interface - -Content that can be provided as history input to startChat(). - -**Signature:** - -```typescript -export interface InputContent -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [parts](./generative-ai.inputcontent.parts.md) | | string \| Array<string \| [Part](./generative-ai.part.md)> | | -| [role](./generative-ai.inputcontent.role.md) | | string | | - diff --git a/docs/reference/generative-ai.inputcontent.parts.md b/docs/reference/generative-ai.inputcontent.parts.md deleted file mode 100644 index aa8259fc..00000000 --- a/docs/reference/generative-ai.inputcontent.parts.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [InputContent](./generative-ai.inputcontent.md) > [parts](./generative-ai.inputcontent.parts.md) - -## InputContent.parts property - -**Signature:** - -```typescript -parts: string | Array; -``` diff --git a/docs/reference/generative-ai.md b/docs/reference/generative-ai.md index 23605fc6..59926e91 100644 --- a/docs/reference/generative-ai.md +++ b/docs/reference/generative-ai.md @@ -18,6 +18,7 @@ | --- | --- | | [BlockReason](./generative-ai.blockreason.md) | Reason that a prompt was blocked. | | [FinishReason](./generative-ai.finishreason.md) | Reason that a candidate finished. | +| [FunctionDeclarationSchemaType](./generative-ai.functiondeclarationschematype.md) | Contains the list of OpenAPI data types as defined by https://swagger.io/docs/specification/data-models/data-types/ | | [HarmBlockThreshold](./generative-ai.harmblockthreshold.md) | Threshold above which a prompt or candidate will be blocked. | | [HarmCategory](./generative-ai.harmcategory.md) | Harm categories that would cause prompts or candidates to be blocked. | | [HarmProbability](./generative-ai.harmprobability.md) | Probability that a prompt or candidate matches a harm category. | @@ -39,6 +40,14 @@ | [EmbedContentRequest](./generative-ai.embedcontentrequest.md) | Params for calling [GenerativeModel.embedContent()](./generative-ai.generativemodel.embedcontent.md) | | [EmbedContentResponse](./generative-ai.embedcontentresponse.md) | Response from calling [GenerativeModel.embedContent()](./generative-ai.generativemodel.embedcontent.md). | | [EnhancedGenerateContentResponse](./generative-ai.enhancedgeneratecontentresponse.md) | Response object wrapped with helper methods. | +| [FunctionCall](./generative-ai.functioncall.md) | A predicted \[FunctionCall\] returned from the model that contains a string representing the \[FunctionDeclaration.name\] and a structured JSON object containing the parameters and their values. | +| [FunctionCallPart](./generative-ai.functioncallpart.md) | Content part interface if the part represents FunctionResponse. | +| [FunctionDeclaration](./generative-ai.functiondeclaration.md) | Structured representation of a function declaration as defined by the \[OpenAPI 3.0 specification\](https://spec.openapis.org/oas/v3.0.3). Included in this declaration are the function name and parameters. This FunctionDeclaration is a representation of a block of code that can be used as a Tool by the model and executed by the client. | +| [FunctionDeclarationSchema](./generative-ai.functiondeclarationschema.md) | Schema for parameters passed to [FunctionDeclaration.parameters](./generative-ai.functiondeclaration.parameters.md). | +| [FunctionDeclarationSchemaProperty](./generative-ai.functiondeclarationschemaproperty.md) | Schema is used to define the format of input/output data. Represents a select subset of an OpenAPI 3.0 schema object. More fields may be added in the future as needed. | +| [FunctionDeclarationsTool](./generative-ai.functiondeclarationstool.md) | A FunctionDeclarationsTool is a piece of code that enables the system to interact with external systems to perform an action, or set of actions, outside of knowledge and scope of the model. | +| [FunctionResponse](./generative-ai.functionresponse.md) | The result output from a \[FunctionCall\] that contains a string representing the \[FunctionDeclaration.name\] and a structured JSON object containing any output from the function is used as context to the model. This should contain the result of a \[FunctionCall\] made based on model prediction. | +| [FunctionResponsePart](./generative-ai.functionresponsepart.md) | Content part interface if the part represents FunctionResponse. | | [GenerateContentCandidate](./generative-ai.generatecontentcandidate.md) | A candidate returned as part of a [GenerateContentResponse](./generative-ai.generatecontentresponse.md). | | [GenerateContentRequest](./generative-ai.generatecontentrequest.md) | Request sent to generateContent endpoint. | | [GenerateContentResponse](./generative-ai.generatecontentresponse.md) | Individual response from [GenerativeModel.generateContent()](./generative-ai.generativemodel.generatecontent.md) and [GenerativeModel.generateContentStream()](./generative-ai.generativemodel.generatecontentstream.md). generateContentStream() will return one in each chunk until the stream is done. | @@ -47,7 +56,6 @@ | [GenerationConfig](./generative-ai.generationconfig.md) | Config options for content-related requests | | [GenerativeContentBlob](./generative-ai.generativecontentblob.md) | Interface for sending an image. | | [InlineDataPart](./generative-ai.inlinedatapart.md) | Content part interface if the part represents an image. | -| [InputContent](./generative-ai.inputcontent.md) | Content that can be provided as history input to startChat(). | | [ModelParams](./generative-ai.modelparams.md) | Params passed to [GoogleGenerativeAI.getGenerativeModel()](./generative-ai.googlegenerativeai.getgenerativemodel.md). | | [PromptFeedback](./generative-ai.promptfeedback.md) | If the prompt was blocked, this will be populated with blockReason and the relevant safetyRatings. | | [RequestOptions](./generative-ai.requestoptions.md) | Params passed to [GoogleGenerativeAI.getGenerativeModel()](./generative-ai.googlegenerativeai.getgenerativemodel.md). | @@ -56,9 +64,17 @@ | [StartChatParams](./generative-ai.startchatparams.md) | Params for [GenerativeModel.startChat()](./generative-ai.generativemodel.startchat.md). | | [TextPart](./generative-ai.textpart.md) | Content part interface if the part represents a text string. | +## Variables + +| Variable | Description | +| --- | --- | +| [POSSIBLE\_ROLES](./generative-ai.possible_roles.md) | Possible roles. | + ## Type Aliases | Type Alias | Description | | --- | --- | | [Part](./generative-ai.part.md) | Content part - includes text or image part types. | +| [Role](./generative-ai.role.md) | Role is the producer of the content. | +| [Tool](./generative-ai.tool.md) | Defines a tool that model can call to access external knowledge. | diff --git a/docs/reference/generative-ai.modelparams.md b/docs/reference/generative-ai.modelparams.md index eba4d68c..e6f1467d 100644 --- a/docs/reference/generative-ai.modelparams.md +++ b/docs/reference/generative-ai.modelparams.md @@ -18,4 +18,5 @@ export interface ModelParams extends BaseParams | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [model](./generative-ai.modelparams.model.md) | | string | | +| [tools?](./generative-ai.modelparams.tools.md) | | [Tool](./generative-ai.tool.md)\[\] | _(Optional)_ | diff --git a/docs/reference/generative-ai.modelparams.tools.md b/docs/reference/generative-ai.modelparams.tools.md new file mode 100644 index 00000000..63a1dffe --- /dev/null +++ b/docs/reference/generative-ai.modelparams.tools.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [ModelParams](./generative-ai.modelparams.md) > [tools](./generative-ai.modelparams.tools.md) + +## ModelParams.tools property + +**Signature:** + +```typescript +tools?: Tool[]; +``` diff --git a/docs/reference/generative-ai.part.md b/docs/reference/generative-ai.part.md index 420d1128..fc4146f0 100644 --- a/docs/reference/generative-ai.part.md +++ b/docs/reference/generative-ai.part.md @@ -9,7 +9,7 @@ Content part - includes text or image part types. **Signature:** ```typescript -export type Part = TextPart | InlineDataPart; +export type Part = TextPart | InlineDataPart | FunctionCallPart | FunctionResponsePart; ``` -**References:** [TextPart](./generative-ai.textpart.md), [InlineDataPart](./generative-ai.inlinedatapart.md) +**References:** [TextPart](./generative-ai.textpart.md), [InlineDataPart](./generative-ai.inlinedatapart.md), [FunctionCallPart](./generative-ai.functioncallpart.md), [FunctionResponsePart](./generative-ai.functionresponsepart.md) diff --git a/docs/reference/generative-ai.possible_roles.md b/docs/reference/generative-ai.possible_roles.md new file mode 100644 index 00000000..570d5bc8 --- /dev/null +++ b/docs/reference/generative-ai.possible_roles.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [POSSIBLE\_ROLES](./generative-ai.possible_roles.md) + +## POSSIBLE\_ROLES variable + +Possible roles. + +**Signature:** + +```typescript +POSSIBLE_ROLES: readonly ["user", "model", "function"] +``` diff --git a/docs/reference/generative-ai.role.md b/docs/reference/generative-ai.role.md new file mode 100644 index 00000000..79f8e9a1 --- /dev/null +++ b/docs/reference/generative-ai.role.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [Role](./generative-ai.role.md) + +## Role type + +Role is the producer of the content. + +**Signature:** + +```typescript +export type Role = (typeof POSSIBLE_ROLES)[number]; +``` +**References:** [POSSIBLE\_ROLES](./generative-ai.possible_roles.md) + diff --git a/docs/reference/generative-ai.startchatparams.history.md b/docs/reference/generative-ai.startchatparams.history.md index 65dee956..4b31c07e 100644 --- a/docs/reference/generative-ai.startchatparams.history.md +++ b/docs/reference/generative-ai.startchatparams.history.md @@ -7,5 +7,5 @@ **Signature:** ```typescript -history?: InputContent[]; +history?: Content[]; ``` diff --git a/docs/reference/generative-ai.startchatparams.md b/docs/reference/generative-ai.startchatparams.md index 2b69eeef..f901c5ed 100644 --- a/docs/reference/generative-ai.startchatparams.md +++ b/docs/reference/generative-ai.startchatparams.md @@ -17,5 +17,6 @@ export interface StartChatParams extends BaseParams | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [history?](./generative-ai.startchatparams.history.md) | | [InputContent](./generative-ai.inputcontent.md)\[\] | _(Optional)_ | +| [history?](./generative-ai.startchatparams.history.md) | | [Content](./generative-ai.content.md)\[\] | _(Optional)_ | +| [tools?](./generative-ai.startchatparams.tools.md) | | [Tool](./generative-ai.tool.md)\[\] | _(Optional)_ | diff --git a/docs/reference/generative-ai.startchatparams.tools.md b/docs/reference/generative-ai.startchatparams.tools.md new file mode 100644 index 00000000..1bf3a986 --- /dev/null +++ b/docs/reference/generative-ai.startchatparams.tools.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [StartChatParams](./generative-ai.startchatparams.md) > [tools](./generative-ai.startchatparams.tools.md) + +## StartChatParams.tools property + +**Signature:** + +```typescript +tools?: Tool[]; +``` diff --git a/docs/reference/generative-ai.textpart.functioncall.md b/docs/reference/generative-ai.textpart.functioncall.md new file mode 100644 index 00000000..acef3d14 --- /dev/null +++ b/docs/reference/generative-ai.textpart.functioncall.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [TextPart](./generative-ai.textpart.md) > [functionCall](./generative-ai.textpart.functioncall.md) + +## TextPart.functionCall property + +**Signature:** + +```typescript +functionCall?: never; +``` diff --git a/docs/reference/generative-ai.textpart.functionresponse.md b/docs/reference/generative-ai.textpart.functionresponse.md new file mode 100644 index 00000000..cfcffdda --- /dev/null +++ b/docs/reference/generative-ai.textpart.functionresponse.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [TextPart](./generative-ai.textpart.md) > [functionResponse](./generative-ai.textpart.functionresponse.md) + +## TextPart.functionResponse property + +**Signature:** + +```typescript +functionResponse?: never; +``` diff --git a/docs/reference/generative-ai.textpart.md b/docs/reference/generative-ai.textpart.md index 8fed1291..6e13dfea 100644 --- a/docs/reference/generative-ai.textpart.md +++ b/docs/reference/generative-ai.textpart.md @@ -16,6 +16,8 @@ export interface TextPart | Property | Modifiers | Type | Description | | --- | --- | --- | --- | +| [functionCall?](./generative-ai.textpart.functioncall.md) | | never | _(Optional)_ | +| [functionResponse?](./generative-ai.textpart.functionresponse.md) | | never | _(Optional)_ | | [inlineData?](./generative-ai.textpart.inlinedata.md) | | never | _(Optional)_ | | [text](./generative-ai.textpart.text.md) | | string | | diff --git a/docs/reference/generative-ai.tool.md b/docs/reference/generative-ai.tool.md new file mode 100644 index 00000000..0f146f0c --- /dev/null +++ b/docs/reference/generative-ai.tool.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@google/generative-ai](./generative-ai.md) > [Tool](./generative-ai.tool.md) + +## Tool type + +Defines a tool that model can call to access external knowledge. + +**Signature:** + +```typescript +export declare type Tool = FunctionDeclarationsTool; +``` +**References:** [FunctionDeclarationsTool](./generative-ai.functiondeclarationstool.md) + diff --git a/packages/main/.eslintrc.js b/packages/main/.eslintrc.js index 29947ae8..59cf5b8e 100644 --- a/packages/main/.eslintrc.js +++ b/packages/main/.eslintrc.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/.mocharc.js b/packages/main/.mocharc.js index 74497a85..1484b270 100644 --- a/packages/main/.mocharc.js +++ b/packages/main/.mocharc.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/rollup.config.mjs b/packages/main/rollup.config.mjs index a2c9e208..170f68d7 100644 --- a/packages/main/rollup.config.mjs +++ b/packages/main/rollup.config.mjs @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/errors.ts b/packages/main/src/errors.ts index 86d17b51..795049de 100644 --- a/packages/main/src/errors.ts +++ b/packages/main/src/errors.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/gen-ai.test.ts b/packages/main/src/gen-ai.test.ts index aa12ec0f..9e8972e9 100644 --- a/packages/main/src/gen-ai.test.ts +++ b/packages/main/src/gen-ai.test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/gen-ai.ts b/packages/main/src/gen-ai.ts index cb89c3ad..f6c9f865 100644 --- a/packages/main/src/gen-ai.ts +++ b/packages/main/src/gen-ai.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index d8de2486..903ccb2b 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/methods/chat-session-helpers.test.ts b/packages/main/src/methods/chat-session-helpers.test.ts new file mode 100644 index 00000000..7914309c --- /dev/null +++ b/packages/main/src/methods/chat-session-helpers.test.ts @@ -0,0 +1,153 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { validateChatHistory } from "./chat-session-helpers"; +import { expect } from "chai"; +import { Content } from "../../types"; +import { GoogleGenerativeAIError } from "../errors"; + +describe("chat-session-helpers", () => { + describe("validateChatHistory", () => { + const TCS: Array<{ history: Content[]; isValid: boolean }> = [ + { + history: [{ role: "user", parts: [{ text: "hi" }] }], + isValid: true, + }, + { + history: [ + { + role: "user", + parts: [ + { text: "hi" }, + { inlineData: { mimeType: "image/jpeg", data: "base64==" } }, + ], + }, + ], + isValid: true, + }, + { + history: [ + { role: "user", parts: [{ text: "hi" }] }, + { role: "model", parts: [{ text: "hi" }, { text: "hi" }] }, + ], + isValid: true, + }, + { + history: [ + { role: "user", parts: [{ text: "hi" }] }, + { + role: "model", + parts: [ + { functionCall: { name: "greet", args: { name: "user" } } }, + ], + }, + ], + isValid: true, + }, + { + history: [ + { role: "user", parts: [{ text: "hi" }] }, + { + role: "model", + parts: [ + { functionCall: { name: "greet", args: { name: "user" } } }, + ], + }, + { + role: "function", + parts: [ + { + functionResponse: { name: "greet", response: { name: "user" } }, + }, + ], + }, + ], + isValid: true, + }, + { + history: [ + { role: "user", parts: [{ text: "hi" }] }, + { + role: "model", + parts: [ + { functionCall: { name: "greet", args: { name: "user" } } }, + ], + }, + { + role: "function", + parts: [ + { + functionResponse: { name: "greet", response: { name: "user" } }, + }, + ], + }, + { + role: "model", + parts: [{ text: "hi name" }], + }, + ], + isValid: true, + }, + { + history: [{ role: "user", parts: [] }], + isValid: false, + }, + { + history: [{ role: "model", parts: [{ text: "hi" }] }], + isValid: false, + }, + { + history: [ + { + role: "function", + parts: [ + { + functionResponse: { name: "greet", response: { name: "user" } }, + }, + ], + }, + ], + isValid: false, + }, + { + history: [ + { role: "user", parts: [{ text: "hi" }] }, + { role: "user", parts: [{ text: "hi" }] }, + ], + isValid: false, + }, + { + history: [ + { role: "user", parts: [{ text: "hi" }] }, + { role: "model", parts: [{ text: "hi" }] }, + { role: "model", parts: [{ text: "hi" }] }, + ], + isValid: false, + }, + ]; + TCS.forEach((tc, index) => { + it(`case ${index}`, () => { + const fn = (): void => validateChatHistory(tc.history); + if (tc.isValid) { + expect(fn).to.not.throw(); + } else { + expect(fn).to.throw(GoogleGenerativeAIError); + } + }); + }); + }); +}); diff --git a/packages/main/src/methods/chat-session-helpers.ts b/packages/main/src/methods/chat-session-helpers.ts new file mode 100644 index 00000000..95931a44 --- /dev/null +++ b/packages/main/src/methods/chat-session-helpers.ts @@ -0,0 +1,98 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Content, POSSIBLE_ROLES, Part, Role } from "../../types"; +import { GoogleGenerativeAIError } from "../errors"; + +// https://ai.google.dev/api/rest/v1beta/Content#part + +const VALID_PART_FIELDS: Array = [ + "text", + "inlineData", + "functionCall", + "functionResponse", +]; + +const VALID_PARTS_PER_ROLE: { [key in Role]: Array } = { + user: ["text", "inlineData"], + function: ["functionResponse"], + model: ["text", "functionCall"], +}; + +const VALID_PREVIOUS_CONTENT_ROLES: { [key in Role]: Role[] } = { + user: ["model"], + function: ["model"], + model: ["user", "function"], +}; + +export function validateChatHistory(history: Content[]): void { + let prevContent: Content; + for (const currContent of history) { + const { role, parts } = currContent; + if (!prevContent && role !== "user") { + throw new GoogleGenerativeAIError( + `First content should be with role 'user', got ${role}`, + ); + } + if (!POSSIBLE_ROLES.includes(role)) { + throw new GoogleGenerativeAIError( + `Each item should include role field. Got ${role} but valid roles are: ${JSON.stringify( + POSSIBLE_ROLES, + )}`, + ); + } + + if (parts.length === 0) { + throw new GoogleGenerativeAIError( + "Each Content should have at least one part", + ); + } + + const countFields: Record = { + text: 0, + inlineData: 0, + functionCall: 0, + functionResponse: 0, + }; + + for (const part of parts) { + for (const key of VALID_PART_FIELDS) { + if (key in part) { + countFields[key] += 1; + } + } + } + const validParts = VALID_PARTS_PER_ROLE[role]; + for (const key of VALID_PART_FIELDS) { + if (!validParts.includes(key) && countFields[key] > 0) { + throw new GoogleGenerativeAIError( + `Content with role '${role}' can't contain '${key}' part`, + ); + } + } + + if (prevContent) { + const validPreviousContentRoles = VALID_PREVIOUS_CONTENT_ROLES[role]; + if (!validPreviousContentRoles.includes(prevContent.role)) { + throw new GoogleGenerativeAIError( + `Content with role '${role}' can't follow '${prevContent.role}'`, + ); + } + } + prevContent = currContent; + } +} diff --git a/packages/main/src/methods/chat-session.test.ts b/packages/main/src/methods/chat-session.test.ts index 76fc4eda..52354e54 100644 --- a/packages/main/src/methods/chat-session.test.ts +++ b/packages/main/src/methods/chat-session.test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/methods/chat-session.ts b/packages/main/src/methods/chat-session.ts index 78125703..08da2e18 100644 --- a/packages/main/src/methods/chat-session.ts +++ b/packages/main/src/methods/chat-session.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import { } from "../../types"; import { formatNewContent } from "../requests/request-helpers"; import { formatBlockErrorMessage } from "../requests/response-helpers"; +import { validateChatHistory } from "./chat-session-helpers"; import { generateContent, generateContentStream } from "./generate-content"; /** @@ -52,14 +53,8 @@ export class ChatSession { ) { this._apiKey = apiKey; if (params?.history) { - this._history = params.history.map((content) => { - if (!content.role) { - throw new Error( - "Missing role for history item: " + JSON.stringify(content), - ); - } - return formatNewContent(content.parts, content.role); - }); + validateChatHistory(params.history); + this._history = params.history; } } @@ -81,10 +76,11 @@ export class ChatSession { request: string | Array, ): Promise { await this._sendPromise; - const newContent = formatNewContent(request, "user"); + const newContent = formatNewContent(request); const generateContentRequest: GenerateContentRequest = { safetySettings: this.params?.safetySettings, generationConfig: this.params?.generationConfig, + tools: this.params?.tools, contents: [...this._history, newContent], }; let finalResult; @@ -134,10 +130,11 @@ export class ChatSession { request: string | Array, ): Promise { await this._sendPromise; - const newContent = formatNewContent(request, "user"); + const newContent = formatNewContent(request); const generateContentRequest: GenerateContentRequest = { safetySettings: this.params?.safetySettings, generationConfig: this.params?.generationConfig, + tools: this.params?.tools, contents: [...this._history, newContent], }; const streamPromise = generateContentStream( diff --git a/packages/main/src/methods/count-tokens.ts b/packages/main/src/methods/count-tokens.ts index 1b065169..b91c9437 100644 --- a/packages/main/src/methods/count-tokens.ts +++ b/packages/main/src/methods/count-tokens.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/methods/embed-content.ts b/packages/main/src/methods/embed-content.ts index cd4cc26a..b2c9936b 100644 --- a/packages/main/src/methods/embed-content.ts +++ b/packages/main/src/methods/embed-content.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/methods/generate-content.test.ts b/packages/main/src/methods/generate-content.test.ts index c4200a1d..1b6c2dbe 100644 --- a/packages/main/src/methods/generate-content.test.ts +++ b/packages/main/src/methods/generate-content.test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,14 +22,18 @@ import * as chaiAsPromised from "chai-as-promised"; import { getMockResponse } from "../../test-utils/mock-response"; import * as request from "../requests/request"; import { generateContent } from "./generate-content"; -import { HarmBlockThreshold, HarmCategory } from "../../types"; +import { + GenerateContentRequest, + HarmBlockThreshold, + HarmCategory, +} from "../../types"; use(sinonChai); use(chaiAsPromised); -const fakeRequestParams = { +const fakeRequestParams: GenerateContentRequest = { contents: [{ parts: [{ text: "hello" }], role: "user" }], - generateConfig: { + generationConfig: { topK: 16, }, safetySettings: [ @@ -55,7 +59,7 @@ describe("generateContent()", () => { expect(result.response.text()).to.include("Helena"); expect(makeRequestStub).to.be.calledWith( match.instanceOf(request.RequestUrl), - match((value) => { + match((value: string) => { return value.includes("contents"); }), ); diff --git a/packages/main/src/methods/generate-content.ts b/packages/main/src/methods/generate-content.ts index 2af684f6..60c39243 100644 --- a/packages/main/src/methods/generate-content.ts +++ b/packages/main/src/methods/generate-content.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/models/generative-model.test.ts b/packages/main/src/models/generative-model.test.ts index ab9f2a50..b8435051 100644 --- a/packages/main/src/models/generative-model.test.ts +++ b/packages/main/src/models/generative-model.test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/models/generative-model.ts b/packages/main/src/models/generative-model.ts index 19dc8e80..3ca5a3ef 100644 --- a/packages/main/src/models/generative-model.ts +++ b/packages/main/src/models/generative-model.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import { RequestOptions, SafetySetting, StartChatParams, + Tool, } from "../../types"; import { ChatSession } from "../methods/chat-session"; import { countTokens } from "../methods/count-tokens"; @@ -53,6 +54,7 @@ export class GenerativeModel { generationConfig: GenerationConfig; safetySettings: SafetySetting[]; requestOptions: RequestOptions; + tools?: Tool[]; constructor( public apiKey: string, @@ -68,6 +70,7 @@ export class GenerativeModel { } this.generationConfig = modelParams.generationConfig || {}; this.safetySettings = modelParams.safetySettings || []; + this.tools = modelParams.tools; this.requestOptions = requestOptions || {}; } @@ -85,6 +88,7 @@ export class GenerativeModel { { generationConfig: this.generationConfig, safetySettings: this.safetySettings, + tools: this.tools, ...formattedParams, }, this.requestOptions, @@ -107,6 +111,7 @@ export class GenerativeModel { { generationConfig: this.generationConfig, safetySettings: this.safetySettings, + tools: this.tools, ...formattedParams, }, this.requestOptions, @@ -121,7 +126,10 @@ export class GenerativeModel { return new ChatSession( this.apiKey, this.model, - startChatParams, + { + tools: this.tools, + ...startChatParams, + }, this.requestOptions, ); } diff --git a/packages/main/src/requests/request-helpers.ts b/packages/main/src/requests/request-helpers.ts index efd8fe78..1af1d671 100644 --- a/packages/main/src/requests/request-helpers.ts +++ b/packages/main/src/requests/request-helpers.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,10 @@ import { GenerateContentRequest, Part, } from "../../types"; +import { GoogleGenerativeAIError } from "../errors"; export function formatNewContent( request: string | Array, - role: string, ): Content { let newParts: Part[] = []; if (typeof request === "string") { @@ -38,7 +38,51 @@ export function formatNewContent( } } } - return { role, parts: newParts }; + return assignRoleToPartsAndValidateSendMessageRequest(newParts); +} + +/** + * When multiple Part types (i.e. FunctionResponsePart and TextPart) are + * passed in a single Part array, we may need to assign different roles to each + * part. Currently only FunctionResponsePart requires a role other than 'user'. + * @private + * @param parts Array of parts to pass to the model + * @returns Array of content items + */ +function assignRoleToPartsAndValidateSendMessageRequest( + parts: Part[], +): Content { + const userContent: Content = { role: "user", parts: [] }; + const functionContent: Content = { role: "function", parts: [] }; + let hasUserContent = false; + let hasFunctionContent = false; + for (const part of parts) { + if ("functionResponse" in part) { + functionContent.parts.push(part); + hasFunctionContent = true; + } else { + userContent.parts.push(part); + hasUserContent = true; + } + } + + if (hasUserContent && hasFunctionContent) { + throw new GoogleGenerativeAIError( + "Within a single message, FunctionResponse cannot be mixed with other type of part in the request for sending chat message.", + ); + } + + if (!hasUserContent && !hasFunctionContent) { + throw new GoogleGenerativeAIError( + "No content is provided for sending chat message.", + ); + } + + if (hasUserContent) { + return userContent; + } + + return functionContent; } export function formatGenerateContentInput( @@ -47,10 +91,7 @@ export function formatGenerateContentInput( if ((params as GenerateContentRequest).contents) { return params as GenerateContentRequest; } else { - const content = formatNewContent( - params as string | Array, - "user", - ); + const content = formatNewContent(params as string | Array); return { contents: [content] }; } } @@ -59,7 +100,7 @@ export function formatEmbedContentInput( params: EmbedContentRequest | string | Array, ): EmbedContentRequest { if (typeof params === "string" || Array.isArray(params)) { - const content = formatNewContent(params, "user"); + const content = formatNewContent(params); return { content }; } return params; diff --git a/packages/main/src/requests/request.test.ts b/packages/main/src/requests/request.test.ts index 8ef93970..e205919b 100644 --- a/packages/main/src/requests/request.test.ts +++ b/packages/main/src/requests/request.test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/requests/request.ts b/packages/main/src/requests/request.ts index b816b258..f5769c91 100644 --- a/packages/main/src/requests/request.ts +++ b/packages/main/src/requests/request.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/src/requests/response-helpers.test.ts b/packages/main/src/requests/response-helpers.test.ts index 39abef7c..cc3889a2 100644 --- a/packages/main/src/requests/response-helpers.test.ts +++ b/packages/main/src/requests/response-helpers.test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,13 +28,34 @@ import { use(sinonChai); -const fakeResponse: GenerateContentResponse = { +const fakeResponseText: GenerateContentResponse = { candidates: [ { index: 0, content: { role: "model", - parts: [{ text: "Some text" }], + parts: [{ text: "Some text" }, { text: " and some more text" }], + }, + }, + ], +}; +const fakeResponseFunctionCall: GenerateContentResponse = { + candidates: [ + { + index: 0, + content: { + role: "model", + parts: [ + { + functionCall: { + name: "find_theaters", + args: { + location: "Mountain View, CA", + movie: "Barbie", + }, + }, + }, + ], }, }, ], @@ -52,11 +73,17 @@ describe("response-helpers methods", () => { restore(); }); describe("addHelpers", () => { - it("good response", async () => { - const enhancedResponse = addHelpers(fakeResponse); - expect(enhancedResponse.text()).to.equal("Some text"); + it("good response text", async () => { + const enhancedResponse = addHelpers(fakeResponseText); + expect(enhancedResponse.text()).to.equal("Some text and some more text"); + }); + it("good response functionCall", async () => { + const enhancedResponse = addHelpers(fakeResponseFunctionCall); + expect(enhancedResponse.functionCall()).to.deep.equal( + fakeResponseFunctionCall.candidates[0].content.parts[0].functionCall, + ); }); - it("bad response", async () => { + it("bad response safety", async () => { const enhancedResponse = addHelpers(badFakeResponse); expect(enhancedResponse.text).to.throw("SAFETY"); }); diff --git a/packages/main/src/requests/response-helpers.ts b/packages/main/src/requests/response-helpers.ts index 854f6c80..8f3f8228 100644 --- a/packages/main/src/requests/response-helpers.ts +++ b/packages/main/src/requests/response-helpers.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import { EnhancedGenerateContentResponse, FinishReason, + FunctionCall, GenerateContentCandidate, GenerateContentResponse, } from "../../types"; @@ -54,6 +55,30 @@ export function addHelpers( } return ""; }; + (response as EnhancedGenerateContentResponse).functionCall = () => { + if (response.candidates && response.candidates.length > 0) { + if (response.candidates.length > 1) { + console.warn( + `This response had ${response.candidates.length} ` + + `candidates. Returning function call from the first candidate only. ` + + `Access response.candidates directly to use the other candidates.`, + ); + } + if (hadBadFinishReason(response.candidates[0])) { + throw new GoogleGenerativeAIResponseError( + `${formatBlockErrorMessage(response)}`, + response, + ); + } + return getFunctionCall(response); + } else if (response.promptFeedback) { + throw new GoogleGenerativeAIResponseError( + `Function call not available. ${formatBlockErrorMessage(response)}`, + response, + ); + } + return undefined; + }; return response as EnhancedGenerateContentResponse; } @@ -62,12 +87,23 @@ export function addHelpers( */ export function getText(response: GenerateContentResponse): string { if (response.candidates?.[0].content?.parts?.[0]?.text) { - return response.candidates[0].content.parts[0].text; + return response.candidates[0].content.parts + .map(({ text }) => text) + .join(""); } else { return ""; } } +/** + * Returns functionCall of first candidate. + */ +export function getFunctionCall( + response: GenerateContentResponse, +): FunctionCall { + return response.candidates?.[0].content?.parts?.[0]?.functionCall; +} + const badFinishReasons = [FinishReason.RECITATION, FinishReason.SAFETY]; function hadBadFinishReason(candidate: GenerateContentCandidate): boolean { diff --git a/packages/main/src/requests/stream-reader.test.ts b/packages/main/src/requests/stream-reader.test.ts index 40df025f..d2fdc4c7 100644 --- a/packages/main/src/requests/stream-reader.test.ts +++ b/packages/main/src/requests/stream-reader.test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -113,6 +113,25 @@ describe("processStream", () => { expect(aggregatedResponse.text()).to.include("秋风瑟瑟,叶落纷纷"); expect(aggregatedResponse.text()).to.include("家人围坐在一起"); }); + it("streaming response - functioncall", async () => { + const fakeResponse = getMockResponseStreaming( + "streaming-success-function-call-short.txt", + ); + const result = processStream(fakeResponse as Response); + for await (const response of result.stream) { + expect(response.text()).to.be.empty; + expect(response.functionCall()).to.be.deep.equal({ + name: "getTemperature", + args: { city: "San Jose" }, + }); + } + const aggregatedResponse = await result.response; + expect(aggregatedResponse.text()).to.be.empty; + expect(aggregatedResponse.functionCall()).to.be.deep.equal({ + name: "getTemperature", + args: { city: "San Jose" }, + }); + }); it("candidate had finishReason", async () => { const fakeResponse = getMockResponseStreaming( "streaming-failure-finish-reason-safety.txt", @@ -335,9 +354,9 @@ describe("aggregateResponses", () => { it("aggregates text across responses", () => { expect(response.candidates.length).to.equal(1); - expect(response.candidates[0].content.parts[0].text).to.equal( - "hello.angry stuff...more stuff", - ); + expect( + response.candidates[0].content.parts.map(({ text }) => text), + ).to.deep.equal(["hello.", "angry stuff", "...more stuff"]); }); it("takes the last response's promptFeedback", () => { diff --git a/packages/main/src/requests/stream-reader.ts b/packages/main/src/requests/stream-reader.ts index 653bb3c3..ebd98534 100644 --- a/packages/main/src/requests/stream-reader.ts +++ b/packages/main/src/requests/stream-reader.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import { GenerateContentCandidate, GenerateContentResponse, GenerateContentStreamResult, + Part, } from "../../types"; import { GoogleGenerativeAIError } from "../errors"; import { addHelpers } from "./response-helpers"; @@ -166,14 +167,23 @@ export function aggregateResponses( if (!aggregatedResponse.candidates[i].content) { aggregatedResponse.candidates[i].content = { role: candidate.content.role || "user", - parts: [{ text: "" }], + parts: [], }; } + const newPart: Partial = {}; for (const part of candidate.content.parts) { if (part.text) { - aggregatedResponse.candidates[i].content.parts[0].text += - part.text; + newPart.text = part.text; } + if (part.functionCall) { + newPart.functionCall = part.functionCall; + } + if (Object.keys(newPart).length === 0) { + newPart.text = ""; + } + aggregatedResponse.candidates[i].content.parts.push( + newPart as Part, + ); } } } diff --git a/packages/main/test-integration/node/count-tokens.test.ts b/packages/main/test-integration/node/count-tokens.test.ts new file mode 100644 index 00000000..cdf46889 --- /dev/null +++ b/packages/main/test-integration/node/count-tokens.test.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from "chai"; +import * as chaiAsPromised from "chai-as-promised"; +import { GoogleGenerativeAI, HarmBlockThreshold, HarmCategory } from "../.."; + +use(chaiAsPromised); + +/** + * Integration tests against live backend. + */ + +describe("countTokens", function () { + this.timeout(60e3); + this.slow(10e3); + it("counts tokens right", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + const response1 = await model.countTokens("count me"); + const response2 = await model.countTokens({ + contents: [{ role: "user", parts: [{ text: "count me" }] }], + }); + expect(response1.totalTokens).to.equal(3); + expect(response2.totalTokens).to.equal(3); + }); +}); diff --git a/packages/main/test-integration/node/embed-content.test.ts b/packages/main/test-integration/node/embed-content.test.ts new file mode 100644 index 00000000..0bd4a5dc --- /dev/null +++ b/packages/main/test-integration/node/embed-content.test.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from "chai"; +import * as chaiAsPromised from "chai-as-promised"; +import { GoogleGenerativeAI } from "../.."; + +use(chaiAsPromised); + +/** + * Integration tests against live backend. + */ + +describe("embedContent", function () { + this.timeout(60e3); + this.slow(10e3); + it("embeds a single Content object", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "embedding-001", + }); + const response1 = await model.embedContent("embed me"); + const response2 = await model.embedContent({ + content: { role: "user", parts: [{ text: "embed me" }] }, + }); + expect(response1.embedding).to.not.be.empty; + expect(response1).to.eql(response2); + }); +}); diff --git a/packages/main/test-integration/node/generate-content-multimodal.test.ts b/packages/main/test-integration/node/generate-content-multimodal.test.ts new file mode 100644 index 00000000..85c95616 --- /dev/null +++ b/packages/main/test-integration/node/generate-content-multimodal.test.ts @@ -0,0 +1,68 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from "fs"; +import { join } from "path"; + +import { expect, use } from "chai"; +import * as chaiAsPromised from "chai-as-promised"; +import { GoogleGenerativeAI, HarmBlockThreshold, HarmCategory } from "../.."; + +use(chaiAsPromised); + +/** + * Integration tests against live backend. + */ + +describe("generateContent - multimodal", function () { + this.timeout(60e3); + this.slow(10e3); + it("non-streaming, image buffer provided", async () => { + const imageBuffer = fs.readFileSync( + join(__dirname, "../../test-utils/cat.png"), + ); + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const base64Image = imageBuffer.toString("base64"); + const model = genAI.getGenerativeModel({ + model: "gemini-pro-vision", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + const result = await model.generateContent({ + contents: [ + { + role: "user", + parts: [ + { text: "Is it a cat?" }, + { + inlineData: { + mimeType: "image/png", + data: base64Image, + }, + }, + ], + }, + ], + }); + const response = result.response; + expect(response.text()).to.not.be.empty; + }); +}); diff --git a/packages/main/test-integration/node/generate-content-tools.test.ts b/packages/main/test-integration/node/generate-content-tools.test.ts new file mode 100644 index 00000000..62e5e420 --- /dev/null +++ b/packages/main/test-integration/node/generate-content-tools.test.ts @@ -0,0 +1,265 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from "chai"; +import * as chaiAsPromised from "chai-as-promised"; +import { FunctionDeclarationSchemaType, GoogleGenerativeAI } from "../.."; +import { Content } from "../../types"; + +use(chaiAsPromised); + +/** + * Integration tests against live backend. + */ + +describe("generateContent - tools", function () { + this.timeout(60e3); + this.slow(10e3); + // This test can be flaky + // eslint-disable-next-line no-restricted-properties + it("non-streaming, tools usage", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel( + { + model: "gemini-pro", + tools: [ + { + functionDeclarations: [ + { + name: "find_movies", + description: + "find movie titles currently playing in theaters based on any description, genre, title words, etc.", + parameters: { + type: FunctionDeclarationSchemaType.OBJECT, + properties: { + location: { + type: FunctionDeclarationSchemaType.STRING, + description: + "The city and state, e.g. San Francisco, CA or a zip code e.g. 95616", + }, + description: { + type: FunctionDeclarationSchemaType.STRING, + description: + "Any kind of description including category or genre, title words, attributes, etc.", + }, + }, + required: ["description"], + }, + }, + { + name: "find_theaters", + description: + "find theaters based on location and optionally movie title which are is currently playing in theaters", + parameters: { + type: FunctionDeclarationSchemaType.OBJECT, + properties: { + location: { + type: FunctionDeclarationSchemaType.STRING, + description: + "The city and state, e.g. San Francisco, CA or a zip code e.g. 95616", + }, + movie: { + type: FunctionDeclarationSchemaType.STRING, + description: "Any movie title", + }, + }, + required: ["location"], + }, + }, + { + name: "get_showtimes", + description: + "Find the start times for movies playing in a specific theater", + parameters: { + type: FunctionDeclarationSchemaType.OBJECT, + properties: { + location: { + type: FunctionDeclarationSchemaType.STRING, + description: + "The city and state, e.g. San Francisco, CA or a zip code e.g. 95616", + }, + movie: { + type: FunctionDeclarationSchemaType.STRING, + description: "Any movie title", + }, + theater: { + type: FunctionDeclarationSchemaType.STRING, + description: "Name of the theater", + }, + date: { + type: FunctionDeclarationSchemaType.STRING, + description: "Date for requested showtime", + }, + }, + required: ["location", "movie", "theater", "date"], + }, + }, + ], + }, + ], + }, + { apiVersion: "v1beta" }, + ); + + const src1 = { + role: "user", + parts: [ + { + text: "Which theaters in Mountain View show Barbie movie?", + }, + ], + }; + const exp1 = { + role: "model", + parts: [ + { + functionCall: { + name: "find_theaters", + args: { + location: "Mountain View, CA", + movie: "Barbie", + }, + }, + }, + ], + }; + + const src2 = { + role: "function", + parts: [ + { + functionResponse: { + name: "find_theaters", + response: { + name: "find_theaters", + content: { + movie: "Barbie", + theaters: [ + { + name: "AMC Mountain View 16", + address: "2000 W El Camino Real, Mountain View, CA 94040", + }, + { + name: "Regal Edwards 14", + address: "245 Castro St, Mountain View, CA 94040", + }, + ], + }, + }, + }, + }, + ], + }; + + const result1 = await model.generateContentStream({ + contents: [src1], + }); + const response1 = await result1.response; + expect(response1.candidates.length).to.equal(1); + expect(response1.candidates[0].content.role).to.equal("model"); + expect(response1.candidates[0].content.parts.length).to.equal(1); + expect(response1.candidates[0].content).to.deep.equal(exp1); + + const result3 = await model.generateContent({ + contents: [src1, exp1, src2], + }); + const response3 = result3.response; + expect(response3.text()).include("AMC Mountain View 16"); + expect(response3.text()).include("Regal Edwards 14"); + }); + it("streaming, tools usage", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel( + { + model: "gemini-pro", + tools: [ + { + functionDeclarations: [ + { + name: "getTemperature", + description: + "Get current temperature in degrees Celsius in a given city", + parameters: { + type: FunctionDeclarationSchemaType.OBJECT, + properties: { + city: { type: FunctionDeclarationSchemaType.STRING }, + }, + required: ["city"], + }, + }, + ], + }, + ], + }, + { apiVersion: "v1beta" }, + ); + + const src1: Content = { + role: "user", + parts: [ + { + text: "Is the temperature the same in New York and San Jose right now?", + }, + ], + }; + const src2: Content = { + role: "model", + parts: [ + { + functionCall: { + name: "getTemperature", + args: { city: "New York" }, + }, + }, + ], + }; + const src3: Content = { + role: "model", + parts: [ + { + functionCall: { + name: "getTemperature", + args: { city: "San Jose" }, + }, + }, + ], + }; + const fn1 = { + role: "function", + parts: [ + { + functionResponse: { + name: "getTemperature", + response: { + name: "getTemperature", + content: { + temperature: "30", + }, + }, + }, + }, + ], + }; + + const result = await model.generateContentStream({ + contents: [src1, src2, fn1, src3, fn1], + }); + const response = await result.response; + console.log(response.text()); + expect(response.text()).to.match(/(\bsame\b|\byes\b)/i); + }); +}); diff --git a/packages/main/test-integration/node/generate-content.test.ts b/packages/main/test-integration/node/generate-content.test.ts new file mode 100644 index 00000000..e956827f --- /dev/null +++ b/packages/main/test-integration/node/generate-content.test.ts @@ -0,0 +1,149 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from "chai"; +import * as chaiAsPromised from "chai-as-promised"; +import { + GoogleGenerativeAI, + HarmBlockThreshold, + HarmCategory, + Part, +} from "../.."; + +use(chaiAsPromised); + +/** + * Integration tests against live backend. + */ + +describe("generateContent", function () { + this.timeout(60e3); + this.slow(10e3); + // This test can be flaky + // eslint-disable-next-line no-restricted-properties + it.skip("streaming - count numbers", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + generationConfig: { + temperature: 0, + candidateCount: 1, + }, + }); + const result = await model.generateContentStream({ + contents: [ + { + role: "user", + parts: [ + { + text: "Count from 1 to 10, put each number into square brackets and on a separate line", + }, + ], + }, + ], + }); + const finalResponse = await result.response; + expect(finalResponse.candidates.length).to.be.equal(1); + const text = finalResponse.text(); + expect(text).to.include("[1]"); + expect(text).to.include("[10]"); + }); + it("stream true, blocked", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + const result = await model.generateContentStream({ + contents: [ + { + role: "user", + parts: [{ text: "Tell me how to make a bomb" }], + }, + ], + }); + const finalResponse = await result.response; + expect(finalResponse.candidates).to.be.undefined; + expect(finalResponse.promptFeedback?.blockReason).to.equal("SAFETY"); + for await (const response of result.stream) { + expect(response.text).to.throw( + "[GoogleGenerativeAI Error]: Text not available. " + + "Response was blocked due to SAFETY", + ); + } + }); + it("stream true, invalid argument", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + await expect( + model.generateContentStream({ + contents: [ + { + role: "user", + parts: [{ inlineData: "This is not an image" } as unknown as Part], + }, + ], + }), + ).to.be.rejectedWith("Invalid value"); + }); + it("non-streaming, simple interface", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + const result = await model.generateContent("What do cats eat?"); + const response = result.response; + expect(response.text()).to.not.be.empty; + }); + it("non-streaming, simple interface, custom API version", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel( + { + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }, + { apiVersion: "v1beta" }, + ); + const result = await model.generateContent("What do cats eat?"); + const response = result.response; + expect(response.text()).to.not.be.empty; + }); +}); diff --git a/packages/main/test-integration/node/index.test.ts b/packages/main/test-integration/node/index.test.ts deleted file mode 100644 index 2a747d04..00000000 --- a/packages/main/test-integration/node/index.test.ts +++ /dev/null @@ -1,394 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as fs from "fs"; -import { join } from "path"; - -import { expect, use } from "chai"; -import * as chaiAsPromised from "chai-as-promised"; -import { - GoogleGenerativeAI, - HarmBlockThreshold, - HarmCategory, - Part, -} from "../.."; - -use(chaiAsPromised); - -/** - * Integration tests against live backend. - */ - -describe("generateContent", function () { - this.timeout(60e3); - this.slow(10e3); - // This test can be flaky - // eslint-disable-next-line no-restricted-properties - it.skip("streaming - count numbers", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - generationConfig: { - temperature: 0, - candidateCount: 1, - }, - }); - const result = await model.generateContentStream({ - contents: [ - { - role: "user", - parts: [ - { - text: "Count from 1 to 10, put each number into square brackets and on a separate line", - }, - ], - }, - ], - }); - const finalResponse = await result.response; - expect(finalResponse.candidates.length).to.be.equal(1); - const text = finalResponse.text(); - expect(text).to.include("[1]"); - expect(text).to.include("[10]"); - }); - it("stream true, blocked", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - const result = await model.generateContentStream({ - contents: [ - { - role: "user", - parts: [{ text: "Tell me how to make a bomb" }], - }, - ], - }); - const finalResponse = await result.response; - expect(finalResponse.candidates).to.be.undefined; - expect(finalResponse.promptFeedback?.blockReason).to.equal("SAFETY"); - for await (const response of result.stream) { - expect(response.text).to.throw( - "[GoogleGenerativeAI Error]: Text not available. " + - "Response was blocked due to SAFETY", - ); - } - }); - it("stream true, invalid argument", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - await expect( - model.generateContentStream({ - contents: [ - { - role: "user", - parts: [{ inlineData: "This is not an image" } as unknown as Part], - }, - ], - }), - ).to.be.rejectedWith("Invalid value"); - }); - it("non-streaming, simple interface", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - const result = await model.generateContent("What do cats eat?"); - const response = result.response; - expect(response.text()).to.not.be.empty; - }); - it("non-streaming, simple interface, custom API version", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel( - { - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }, - { apiVersion: "v1beta" }, - ); - const result = await model.generateContent("What do cats eat?"); - const response = result.response; - expect(response.text()).to.not.be.empty; - }); - it("non-streaming, image buffer provided", async () => { - const imageBuffer = fs.readFileSync( - join(__dirname, "../../test-utils/cat.png"), - ); - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const base64Image = imageBuffer.toString("base64"); - const model = genAI.getGenerativeModel({ - model: "gemini-pro-vision", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - const result = await model.generateContent({ - contents: [ - { - role: "user", - parts: [ - { text: "Is it a cat?" }, - { - inlineData: { - mimeType: "image/png", - data: base64Image, - }, - }, - ], - }, - ], - }); - const response = result.response; - expect(response.text()).to.not.be.empty; - }); -}); - -describe("startChat", function () { - this.timeout(60e3); - this.slow(10e3); - it("stream false", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - const question1 = "What is the capital of Oregon?"; - const question2 = "How many people live there?"; - const chat = model.startChat(); - const result1 = await chat.sendMessage(question1); - expect(result1.response.text()).to.not.be.empty; - const result2 = await chat.sendMessage(question2); - expect(result2.response.text()).to.not.be.empty; - const history = await chat.getHistory(); - expect(history[0].parts[0].text).to.equal(question1); - expect(history[2].parts[0].text).to.equal(question2); - expect(history.length).to.equal(4); - }); - it("stream true, blocked", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - // Blockable question. - const question1 = "Should I push this guy out the window?"; - // Non-blockable question, ensure chat is still usable after block. - const question2 = "Tell me an appropriate joke"; - const chat = model.startChat({ - generationConfig: { - maxOutputTokens: 100, - }, - }); - const result = await chat.sendMessageStream(question1); - const finalResponse = await result.response; - expect(finalResponse.candidates).to.be.undefined; - expect(finalResponse.promptFeedback?.blockReason).to.equal("SAFETY"); - expect(finalResponse.text).to.throw( - "[GoogleGenerativeAI Error]: Text not available. " + - "Response was blocked due to SAFETY", - ); - for await (const response of result.stream) { - expect(response.text).to.throw( - "[GoogleGenerativeAI Error]: Text not available. " + - "Response was blocked due to SAFETY", - ); - } - expect((await chat.getHistory()).length).to.equal(0); - const result2 = await chat.sendMessageStream(question2); - const response2 = await result2.response; - expect(response2.text).to.not.throw; - expect((await chat.getHistory()).length).to.equal(2); - }); - it("stream true", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - const question1 = "What is the capital of Oregon?"; - const question2 = "How many people live there?"; - const question3 = "What is the closest river?"; - const chat = model.startChat(); - const result1 = await chat.sendMessageStream(question1); - const response1 = await result1.response; - expect(response1.text()).to.not.be.empty; - const result2 = await chat.sendMessageStream(question2); - for await (const response of result2.stream) { - expect(response.text()).to.not.be.empty; - } - const response2 = await result2.response; - expect(response2.text()).to.not.be.empty; - const result3 = await chat.sendMessageStream(question3); - for await (const response of result3.stream) { - expect(response.text()).to.not.be.empty; - } - const response3 = await result3.response; - expect(response3.text()).to.not.be.empty; - const history = await chat.getHistory(); - expect(history[0].parts[0].text).to.equal(question1); - expect(history[2].parts[0].text).to.equal(question2); - expect(history[4].parts[0].text).to.equal(question3); - expect(history.length).to.equal(6); - }); - it("stream true, try to send message before previous stream is done", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - const question1 = "What are the most interesting cities in Oregon?"; - const question2 = "How many people live there?"; - const question3 = "What is the closest river?"; - const chat = model.startChat(); - const promise1 = chat.sendMessageStream(question1).then(async (result1) => { - for await (const response of result1.stream) { - expect(response.text()).to.not.be.empty; - } - const response1 = await result1.response; - expect(response1.text()).to.not.be.empty; - }); - const promise2 = chat.sendMessageStream(question2).then(async (result2) => { - for await (const response of result2.stream) { - expect(response.text()).to.not.be.empty; - } - const response2 = await result2.response; - expect(response2.text()).to.not.be.empty; - }); - const promise3 = chat - .sendMessage(question3) - .then(async (result3) => { - const response3 = result3.response; - expect(response3.text()).to.not.be.empty; - }) - .catch((e) => console.error(e)); - await Promise.all([promise1, promise2, promise3]); - const history = await chat.getHistory(); - expect(history[0].parts[0].text).to.equal(question1); - expect(history[2].parts[0].text).to.equal(question2); - expect(history[4].parts[0].text).to.equal(question3); - expect(history.length).to.equal(6); - }); -}); - -describe("countTokens", function () { - this.timeout(60e3); - this.slow(10e3); - it("counts tokens right", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "gemini-pro", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, - }, - ], - }); - const response1 = await model.countTokens("count me"); - const response2 = await model.countTokens({ - contents: [{ role: "user", parts: [{ text: "count me" }] }], - }); - expect(response1.totalTokens).to.equal(3); - expect(response2.totalTokens).to.equal(3); - }); -}); - -describe("embedContent", function () { - this.timeout(60e3); - this.slow(10e3); - it("embeds a single Content object", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "embedding-001", - }); - const response1 = await model.embedContent("embed me"); - const response2 = await model.embedContent({ - content: { role: "user", parts: [{ text: "embed me" }] }, - }); - expect(response1.embedding).to.not.be.empty; - expect(response1).to.eql(response2); - }); -}); - -describe("batchEmbedContents", function () { - this.timeout(60e3); - this.slow(10e3); - it("embeds multiple requests", async () => { - const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); - const model = genAI.getGenerativeModel({ - model: "embedding-001", - }); - const content1 = { - content: { role: "user", parts: [{ text: "embed me" }] }, - }; - const content2 = { - content: { role: "user", parts: [{ text: "embed me" }] }, - }; - const response = await model.batchEmbedContents({ - requests: [content1, content2], - }); - expect(response.embeddings.length).to.equal(2); - }); -}); diff --git a/packages/main/test-integration/node/start-chat-tools.test.ts b/packages/main/test-integration/node/start-chat-tools.test.ts new file mode 100644 index 00000000..f238775b --- /dev/null +++ b/packages/main/test-integration/node/start-chat-tools.test.ts @@ -0,0 +1,99 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from "chai"; +import * as chaiAsPromised from "chai-as-promised"; +import { + FunctionDeclarationSchemaType, + GoogleGenerativeAI, + HarmBlockThreshold, + HarmCategory, + Tool, +} from "../.."; +import { Part } from "../../types"; + +use(chaiAsPromised); + +/** + * Integration tests against live backend. + */ + +describe("startChat - tools", function () { + const tools: Tool[] = [ + { + functionDeclarations: [ + { + name: "getTemperature", + description: + "Get current temperature in degrees Celsius in a given city", + parameters: { + type: FunctionDeclarationSchemaType.OBJECT, + properties: { + city: { type: FunctionDeclarationSchemaType.STRING }, + }, + required: ["city"], + }, + }, + ], + }, + ]; + + const part1: Part = { + text: "What is the temperature in New York?", + }; + const part2: Part = { + functionResponse: { + name: "getTemperature", + response: { + name: "getTemperature", + content: { + temperature: "30", + }, + }, + }, + }; + + this.timeout(60e3); + this.slow(10e3); + it("stream false", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel( + { + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + tools, + }, + { apiVersion: "v1beta" }, + ); + const chat = model.startChat(); + const result1 = await chat.sendMessage([part1]); + expect(result1.response.text()).to.be.empty; + expect(result1.response.functionCall()).not.to.be.empty; + const result2 = await chat.sendMessage([part2]); + expect(result2.response.text()).to.not.be.empty; + const history = await chat.getHistory(); + expect(history[0].parts[0].text).to.equal(part1.text); + expect(history[2].parts[0].functionCall).to.deep.equal(part2.functionCall); + expect(history[3].parts[0].text).to.include("30"); + expect(history.length).to.equal(4); + }); +}); diff --git a/packages/main/test-integration/node/start-chat.test.ts b/packages/main/test-integration/node/start-chat.test.ts new file mode 100644 index 00000000..c7217d26 --- /dev/null +++ b/packages/main/test-integration/node/start-chat.test.ts @@ -0,0 +1,173 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from "chai"; +import * as chaiAsPromised from "chai-as-promised"; +import { GoogleGenerativeAI, HarmBlockThreshold, HarmCategory } from "../.."; + +use(chaiAsPromised); + +/** + * Integration tests against live backend. + */ + +describe("startChat", function () { + this.timeout(60e3); + this.slow(10e3); + it("stream false", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + const question1 = "What is the capital of Oregon?"; + const question2 = "How many people live there?"; + const chat = model.startChat(); + const result1 = await chat.sendMessage(question1); + expect(result1.response.text()).to.not.be.empty; + const result2 = await chat.sendMessage(question2); + expect(result2.response.text()).to.not.be.empty; + const history = await chat.getHistory(); + expect(history[0].parts[0].text).to.equal(question1); + expect(history[2].parts[0].text).to.equal(question2); + expect(history.length).to.equal(4); + }); + it("stream true, blocked", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + // Blockable question. + const question1 = "Should I push this guy out the window?"; + // Non-blockable question, ensure chat is still usable after block. + const question2 = "Tell me an appropriate joke"; + const chat = model.startChat({ + generationConfig: { + maxOutputTokens: 100, + }, + }); + const result = await chat.sendMessageStream(question1); + const finalResponse = await result.response; + expect(finalResponse.candidates).to.be.undefined; + expect(finalResponse.promptFeedback?.blockReason).to.equal("SAFETY"); + expect(finalResponse.text).to.throw( + "[GoogleGenerativeAI Error]: Text not available. " + + "Response was blocked due to SAFETY", + ); + for await (const response of result.stream) { + expect(response.text).to.throw( + "[GoogleGenerativeAI Error]: Text not available. " + + "Response was blocked due to SAFETY", + ); + } + expect((await chat.getHistory()).length).to.equal(0); + const result2 = await chat.sendMessageStream(question2); + const response2 = await result2.response; + expect(response2.text).to.not.throw; + expect((await chat.getHistory()).length).to.equal(2); + }); + it("stream true", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + const question1 = "What is the capital of Oregon?"; + const question2 = "How many people live there?"; + const question3 = "What is the closest river?"; + const chat = model.startChat(); + const result1 = await chat.sendMessageStream(question1); + const response1 = await result1.response; + expect(response1.text()).to.not.be.empty; + const result2 = await chat.sendMessageStream(question2); + for await (const response of result2.stream) { + expect(response.text()).to.not.be.empty; + } + const response2 = await result2.response; + expect(response2.text()).to.not.be.empty; + const result3 = await chat.sendMessageStream(question3); + for await (const response of result3.stream) { + expect(response.text()).to.not.be.empty; + } + const response3 = await result3.response; + expect(response3.text()).to.not.be.empty; + const history = await chat.getHistory(); + expect(history[0].parts[0].text).to.equal(question1); + expect(history[2].parts[0].text).to.equal(question2); + expect(history[4].parts[0].text).to.equal(question3); + expect(history.length).to.equal(6); + }); + it("stream true, try to send message before previous stream is done", async () => { + const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); + const model = genAI.getGenerativeModel({ + model: "gemini-pro", + safetySettings: [ + { + category: HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH, + }, + ], + }); + const question1 = "What are the most interesting cities in Oregon?"; + const question2 = "How many people live there?"; + const question3 = "What is the closest river?"; + const chat = model.startChat(); + const promise1 = chat.sendMessageStream(question1).then(async (result1) => { + for await (const response of result1.stream) { + expect(response.text()).to.not.be.empty; + } + const response1 = await result1.response; + expect(response1.text()).to.not.be.empty; + }); + const promise2 = chat.sendMessageStream(question2).then(async (result2) => { + for await (const response of result2.stream) { + expect(response.text()).to.not.be.empty; + } + const response2 = await result2.response; + expect(response2.text()).to.not.be.empty; + }); + const promise3 = chat + .sendMessage(question3) + .then(async (result3) => { + const response3 = result3.response; + expect(response3.text()).to.not.be.empty; + }) + .catch((e) => console.error(e)); + await Promise.all([promise1, promise2, promise3]); + const history = await chat.getHistory(); + expect(history[0].parts[0].text).to.equal(question1); + expect(history[2].parts[0].text).to.equal(question2); + expect(history[4].parts[0].text).to.equal(question3); + expect(history.length).to.equal(6); + }); +}); diff --git a/packages/main/test-integration/web/index.test.ts b/packages/main/test-integration/web/index.test.ts index 49631cc4..3dcfbfcd 100644 --- a/packages/main/test-integration/web/index.test.ts +++ b/packages/main/test-integration/web/index.test.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/test-utils/base64cat.ts b/packages/main/test-utils/base64cat.ts index f9a34950..85ef4722 100644 --- a/packages/main/test-utils/base64cat.ts +++ b/packages/main/test-utils/base64cat.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/test-utils/mock-response.ts b/packages/main/test-utils/mock-response.ts index 38549872..8e37dc22 100644 --- a/packages/main/test-utils/mock-response.ts +++ b/packages/main/test-utils/mock-response.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/test-utils/mock-responses/streaming-success-function-call-short.txt b/packages/main/test-utils/mock-responses/streaming-success-function-call-short.txt new file mode 100644 index 00000000..ad6cb050 --- /dev/null +++ b/packages/main/test-utils/mock-responses/streaming-success-function-call-short.txt @@ -0,0 +1,2 @@ +data: {"candidates": [{"content": {"parts": [{ "functionCall": { "name": "getTemperature", "args": { "city": "San Jose" } } }]}, "finishReason": "STOP","index": 0,"safetyRatings": [{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_HATE_SPEECH","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_HARASSMENT","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_DANGEROUS_CONTENT","probability": "NEGLIGIBLE"}]}],"promptFeedback": {"safetyRatings": [{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_HATE_SPEECH","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_HARASSMENT","probability": "NEGLIGIBLE"},{"category": "HARM_CATEGORY_DANGEROUS_CONTENT","probability": "NEGLIGIBLE"}]}} + diff --git a/packages/main/types/content.ts b/packages/main/types/content.ts index ded4d174..8fb373a4 100644 --- a/packages/main/types/content.ts +++ b/packages/main/types/content.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,28 +15,26 @@ * limitations under the License. */ +import { Role } from "./enums"; + /** * Content type for both prompts and response candidates. * @public */ -export interface Content extends InputContent { +export interface Content { + role: Role; parts: Part[]; } -/** - * Content that can be provided as history input to startChat(). - * @public - */ -export interface InputContent { - parts: string | Array; - role: string; -} - /** * Content part - includes text or image part types. * @public */ -export type Part = TextPart | InlineDataPart; +export type Part = + | TextPart + | InlineDataPart + | FunctionCallPart + | FunctionResponsePart; /** * Content part interface if the part represents a text string. @@ -45,6 +43,8 @@ export type Part = TextPart | InlineDataPart; export interface TextPart { text: string; inlineData?: never; + functionCall?: never; + functionResponse?: never; } /** @@ -54,6 +54,55 @@ export interface TextPart { export interface InlineDataPart { text?: never; inlineData: GenerativeContentBlob; + functionCall?: never; + functionResponse?: never; +} + +/** + * Content part interface if the part represents FunctionResponse. + * @public + */ +export interface FunctionCallPart { + text?: never; + inlineData?: never; + functionCall: FunctionCall; + functionResponse?: never; +} + +/** + * Content part interface if the part represents FunctionResponse. + * @public + */ +export interface FunctionResponsePart { + text?: never; + inlineData?: never; + functionCall?: never; + functionResponse: FunctionResponse; +} + +/** + * A predicted [FunctionCall] returned from the model + * that contains a string representing the [FunctionDeclaration.name] + * and a structured JSON object containing the parameters and their values. + * @public + */ +export interface FunctionCall { + name: string; + args: object; +} + +/** + * The result output from a [FunctionCall] that contains a string + * representing the [FunctionDeclaration.name] + * and a structured JSON object containing any output + * from the function is used as context to the model. + * This should contain the result of a [FunctionCall] + * made based on model prediction. + * @public + */ +export interface FunctionResponse { + name: string; + response: object; } /** diff --git a/packages/main/types/enums.ts b/packages/main/types/enums.ts index 081b9c10..d6d67410 100644 --- a/packages/main/types/enums.ts +++ b/packages/main/types/enums.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,18 @@ * limitations under the License. */ +/** + * Role is the producer of the content. + * @public + */ +export type Role = (typeof POSSIBLE_ROLES)[number]; + +/** + * Possible roles. + * @public + */ +export const POSSIBLE_ROLES = ["user", "model", "function"] as const; + /** * Harm categories that would cause prompts or candidates to be blocked. * @public diff --git a/packages/main/types/index.ts b/packages/main/types/index.ts index 63d3ff02..55b341f6 100644 --- a/packages/main/types/index.ts +++ b/packages/main/types/index.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/main/types/requests.ts b/packages/main/types/requests.ts index 887cf9c6..17bc99f3 100644 --- a/packages/main/types/requests.ts +++ b/packages/main/types/requests.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Content, InputContent } from "./content"; +import { Content } from "./content"; import { HarmBlockThreshold, HarmCategory, TaskType } from "./enums"; /** @@ -33,6 +33,7 @@ export interface BaseParams { */ export interface ModelParams extends BaseParams { model: string; + tools?: Tool[]; } /** @@ -41,6 +42,7 @@ export interface ModelParams extends BaseParams { */ export interface GenerateContentRequest extends BaseParams { contents: Content[]; + tools?: Tool[]; } /** @@ -70,7 +72,8 @@ export interface GenerationConfig { * @public */ export interface StartChatParams extends BaseParams { - history?: InputContent[]; + history?: Content[]; + tools?: Tool[]; } /** @@ -114,3 +117,136 @@ export interface RequestOptions { */ apiVersion?: string; } +/** + * Defines a tool that model can call to access external knowledge. + * @public + */ +export declare type Tool = FunctionDeclarationsTool; + +/** + * Structured representation of a function declaration as defined by the + * [OpenAPI 3.0 specification](https://spec.openapis.org/oas/v3.0.3). Included + * in this declaration are the function name and parameters. This + * FunctionDeclaration is a representation of a block of code that can be used + * as a Tool by the model and executed by the client. + * @public + */ +export declare interface FunctionDeclaration { + /** + * The name of the function to call. Must start with a letter or an + * underscore. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with + * a max length of 64. + */ + name: string; + /** + * Optional. Description and purpose of the function. Model uses it to decide + * how and whether to call the function. + */ + description?: string; + /** + * Optional. Describes the parameters to this function in JSON Schema Object + * format. Reflects the Open API 3.03 Parameter Object. string Key: the name + * of the parameter. Parameter names are case sensitive. Schema Value: the + * Schema defining the type used for the parameter. For function with no + * parameters, this can be left unset. + * + * @example with 1 required and 1 optional parameter: type: OBJECT properties: + * ``` + * param1: + * + * type: STRING + * param2: + * + * type: INTEGER + * required: + * + * - param1 + * ``` + */ + parameters?: FunctionDeclarationSchema; +} + +/** + * A FunctionDeclarationsTool is a piece of code that enables the system to + * interact with external systems to perform an action, or set of actions, + * outside of knowledge and scope of the model. + * @public + */ +export declare interface FunctionDeclarationsTool { + /** + * Optional. One or more function declarations + * to be passed to the model along with the current user query. Model may + * decide to call a subset of these functions by populating + * [FunctionCall][content.part.functionCall] in the response. User should + * provide a [FunctionResponse][content.part.functionResponse] for each + * function call in the next turn. Based on the function responses, Model will + * generate the final response back to the user. Maximum 64 function + * declarations can be provided. + */ + functionDeclarations?: FunctionDeclaration[]; +} + +/** + * Contains the list of OpenAPI data types + * as defined by https://swagger.io/docs/specification/data-models/data-types/ + * @public + */ +export enum FunctionDeclarationSchemaType { + /** String type. */ + STRING = "STRING", + /** Number type. */ + NUMBER = "NUMBER", + /** Integer type. */ + INTEGER = "INTEGER", + /** Boolean type. */ + BOOLEAN = "BOOLEAN", + /** Array type. */ + ARRAY = "ARRAY", + /** Object type. */ + OBJECT = "OBJECT", +} + +/** + * Schema for parameters passed to {@link FunctionDeclaration.parameters}. + * @public + */ +export interface FunctionDeclarationSchema { + /** The type of the parameter. */ + type: FunctionDeclarationSchemaType; + /** The format of the parameter. */ + properties: { [k: string]: FunctionDeclarationSchemaProperty }; + /** Optional. Description of the parameter. */ + description?: string; + /** Optional. Array of required parameters. */ + required?: string[]; +} + +/** + * Schema is used to define the format of input/output data. + * Represents a select subset of an OpenAPI 3.0 schema object. + * More fields may be added in the future as needed. + * @public + */ +export interface FunctionDeclarationSchemaProperty { + /** + * Optional. The type of the property. {@link + * FunctionDeclarationSchemaType}. + */ + type?: FunctionDeclarationSchemaType; + /** Optional. The format of the property. */ + format?: string; + /** Optional. The description of the property. */ + description?: string; + /** Optional. Whether the property is nullable. */ + nullable?: boolean; + /** Optional. The items of the property. {@link FunctionDeclarationSchema} */ + items?: FunctionDeclarationSchema; + /** Optional. The enum of the property. */ + enum?: string[]; + /** Optional. Map of {@link FunctionDeclarationSchema}. */ + properties?: { [k: string]: FunctionDeclarationSchema }; + /** Optional. Array of required property. */ + required?: string[]; + /** Optional. The example of the property. */ + example?: unknown; +} diff --git a/packages/main/types/responses.ts b/packages/main/types/responses.ts index 57e47544..1788efc3 100644 --- a/packages/main/types/responses.ts +++ b/packages/main/types/responses.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Content } from "./content"; +import { Content, FunctionCall } from "./content"; import { BlockReason, FinishReason, @@ -57,6 +57,7 @@ export interface EnhancedGenerateContentResponse * Throws if the prompt or candidate was blocked. */ text: () => string; + functionCall: () => FunctionCall | undefined; } /** diff --git a/samples/node/advanced-chat.js b/samples/node/advanced-chat.js index 23d18bc7..52b7b304 100644 --- a/samples/node/advanced-chat.js +++ b/samples/node/advanced-chat.js @@ -29,11 +29,11 @@ async function run() { history: [ { role: "user", - parts: "Hello, I have 2 dogs in my house.", + parts: [{text: "Hello, I have 2 dogs in my house."}], }, { role: "model", - parts: "Great to meet you. What would you like to know?", + parts: [{text: "Great to meet you. What would you like to know?"}], }, ], generationConfig: { diff --git a/samples/node/advanced-function-calling.js b/samples/node/advanced-function-calling.js new file mode 100644 index 00000000..f2a373cc --- /dev/null +++ b/samples/node/advanced-function-calling.js @@ -0,0 +1,111 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FunctionDeclarationSchemaType } from "@google/generative-ai"; +import { genAI } from "./utils/common.js"; + +async function run() { + const functions = { + convertCtoF: ({ value }) => { + const num = typeof value === "string" ? parseFloat(value) : value; + if (!Number.isFinite(num)) { + throw new Error("Value should finite number"); + } + return (num * 9) / 5 + 32; + }, + }; + const tools = [ + { + functionDeclarations: [ + { + name: "convertCtoF", + description: "Convert temperature from Celsius to Fahrenheit", + parameters: { + type: FunctionDeclarationSchemaType.OBJECT, + properties: { + value: { type: FunctionDeclarationSchemaType.NUMBER }, + }, + required: ["value"], + }, + }, + ], + }, + ]; + + // For text-only inputs, use the gemini-pro model + const model = genAI.getGenerativeModel( + { model: "gemini-pro", tools }, + { apiVersion: "v1beta" }, + ); + + const prompt = { + role: "user", + parts: [ + { + text: "Convert 15 Celsius to Fahrenheit", + }, + ], + }; + + const result = await model.generateContent({ + contents: [prompt], + }); + const response = result.response; + console.dir(response, { depth: null }); + + if (response.candidates.length === 0) { + throw new Error("No candidates"); + } + + const content = result.response.candidates[0].content; + if (content.parts.length === 0) { + throw new Error("No parts"); + } + const fc = content.parts[0].functionCall; + const text = content.parts.map(({ text }) => text).join(""); + if (fc) { + const { name, args } = fc; + const fn = functions[name]; + if (!fn) { + throw new Error(`Unknown function "${name}"`); + } + const fr = { + role: "function", + parts: [ + { + functionResponse: { + name, + response: { + name, + content: functions[name](args), + }, + }, + }, + ], + }; + const request2 = { + contents: [prompt, content, fr], + }; + const response2 = await model.generateContent(request2); + const result2 = response2.response; + console.log(result2.text()); + } else if (text) { + console.log(text); + } +} + +run();