From 552e1c2db5ed73680f59f9bb10146f11fe3e592a Mon Sep 17 00:00:00 2001 From: Benjamin Sayaque <91118734+Benjamin-Sayaque@users.noreply.github.com> Date: Tue, 2 Jun 2026 11:01:07 +0200 Subject: [PATCH 01/15] Prefer Vertex AI advanced service for Gemini --- README.md | 2 +- src/code.gs | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 196 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 20cab1a..464b029 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ The **GenAIApp** library is a Google Apps Script library designed for creating, The setup for **GenAIApp** varies depending on which models you plan to use: 1. If you want to use **OpenAI models**: You'll need an **OpenAI API key** 2. If you want to use **Google Gemini models**: you’ll need a **Google Cloud Platform (GCP) project** with **Vertex AI** enabled for access to Gemini models. -Ensure to link your Google Apps Script project to a GCP project with Vertex AI enabled, and to include the following scopes in your manifest file: +Ensure to link your Google Apps Script project to a GCP project with Vertex AI enabled. GenAIApp prefers the Google Apps Script Vertex AI Advanced Service when it is enabled in the Apps Script project, and automatically falls back to the direct `UrlFetchApp` Vertex AI call if the Advanced Service is unavailable or fails. Include the following scopes in your manifest file: ```js "oauthScopes": [ "https://www.googleapis.com/auth/cloud-platform", diff --git a/src/code.gs b/src/code.gs index 35777cb..0bdcfba 100644 --- a/src/code.gs +++ b/src/code.gs @@ -513,7 +513,12 @@ const GenAIApp = (function () { } } } - responseMessage = _callGenAIApi(endpointUrl, payload); + if (model.includes("gemini") && !geminiKey) { + responseMessage = callVertexAi(endpointUrl, payload); + } + else { + responseMessage = _callGenAIApi(endpointUrl, payload); + } if (responseMessage?.usage) { this._lastUsage = responseMessage.usage; if (this._inputTokenWarningThreshold !== null @@ -1383,6 +1388,195 @@ const GenAIApp = (function () { } } + /** + * Calls Vertex AI for Gemini requests, preferring the Google Apps Script + * Vertex AI Advanced Service and falling back to the existing UrlFetchApp path. + * + * @private + * @param {string} endpoint - The Vertex AI REST endpoint used by the fallback path. + * @param {Object} payload - The Gemini generateContent payload. + * @returns {object} - The normalized response message from the Gemini API. + */ + function callVertexAi(endpoint, payload) { + try { + return callVertexAiWithAdvancedService(endpoint, payload); + } + catch (err) { + _logVertexAiAdvancedServiceFallback(err); + return callVertexAiWithUrlFetchFallback(endpoint, payload); + } + } + + /** + * Calls Vertex AI through the Apps Script Advanced Service. + * + * @private + * @param {string} endpoint - The Vertex AI REST endpoint, used to derive the model resource. + * @param {Object} payload - The Gemini generateContent payload. + * @returns {object} - The normalized response message from the Gemini API. + * @throws {Error} If the Advanced Service is unavailable, unsupported, misconfigured, or returns an API error. + */ + function callVertexAiWithAdvancedService(endpoint, payload) { + const service = _getVertexAiAdvancedService(); + const generateContent = _getVertexAiGenerateContentMethod(service); + const modelResource = _getVertexAiModelResource(endpoint, payload); + const advancedServicePayload = _buildVertexAiAdvancedServicePayload(payload); + const response = generateContent(advancedServicePayload, modelResource); + + return _normalizeVertexAiResponse(response, payload); + } + + /** + * Calls Vertex AI through the existing UrlFetchApp implementation. + * + * @private + * @param {string} endpoint - The Vertex AI REST endpoint. + * @param {Object} payload - The Gemini generateContent payload. + * @returns {object} - The normalized response message from the Gemini API. + */ + function callVertexAiWithUrlFetchFallback(endpoint, payload) { + return _callGenAIApi(endpoint, payload); + } + + /** + * Finds the Apps Script Vertex AI Advanced Service object if it is enabled. + * + * @private + * @returns {Object} - The Vertex AI Advanced Service object. + * @throws {Error} If the service is not available. + */ + function _getVertexAiAdvancedService() { + if (typeof VertexAI !== 'undefined') { + return VertexAI; + } + if (typeof vertexai !== 'undefined') { + return vertexai; + } + throw new Error('Vertex AI Advanced Service is not enabled or not available in this Apps Script project.'); + } + + /** + * Resolves the generated Apps Script method for projects.locations.publishers.models.generateContent. + * Apps Script Advanced Service names can differ by release/casing, so support the known variants. + * + * @private + * @param {Object} service - The Vertex AI Advanced Service object. + * @returns {Function} - A generateContent method bound to its collection object. + * @throws {Error} If the method is not exposed by the enabled Advanced Service. + */ + function _getVertexAiGenerateContentMethod(service) { + const collectionPaths = [ + ['projects', 'locations', 'publishers', 'models'], + ['Projects', 'Locations', 'Publishers', 'Models'] + ]; + + for (let i = 0; i < collectionPaths.length; i++) { + let collection = service; + for (let j = 0; j < collectionPaths[i].length && collection; j++) { + collection = collection[collectionPaths[i][j]]; + } + if (collection && typeof collection.generateContent === 'function') { + return collection.generateContent.bind(collection); + } + } + + throw new Error('Vertex AI Advanced Service does not expose projects.locations.publishers.models.generateContent.'); + } + + /** + * Derives the model resource name expected by the Vertex AI Advanced Service. + * + * @private + * @param {string} endpoint - The Vertex AI REST endpoint. + * @param {Object} payload - The Gemini generateContent payload. + * @returns {string} - The fully qualified Vertex AI model resource name. + * @throws {Error} If the project, location, or model cannot be resolved. + */ + function _getVertexAiModelResource(endpoint, payload) { + const endpointMatch = endpoint.match(/\/v1\/(projects\/[^:]+):generateContent/); + if (endpointMatch && endpointMatch[1]) { + return endpointMatch[1]; + } + + if (!gcpProjectId) { + throw new Error('Missing GCP project ID for Vertex AI Advanced Service call.'); + } + if (!payload?.model) { + throw new Error('Missing Gemini model for Vertex AI Advanced Service call.'); + } + + const location = (!region || payload.model.includes('gemini-3')) ? 'global' : region; + if (!location) { + throw new Error('Missing Vertex AI location for Advanced Service call.'); + } + + return `projects/${gcpProjectId}/locations/${location}/publishers/google/models/${payload.model}`; + } + + /** + * Builds a request body compatible with Vertex AI generateContent. + * + * @private + * @param {Object} payload - The existing Gemini request payload. + * @returns {Object} - A request body for Vertex AI Advanced Service. + */ + function _buildVertexAiAdvancedServicePayload(payload) { + const advancedServicePayload = JSON.parse(JSON.stringify(payload || {})); + delete advancedServicePayload.model; + return advancedServicePayload; + } + + /** + * Normalizes a Vertex AI Advanced Service GenerateContentResponse to match + * the existing UrlFetchApp response format returned by _callGenAIApi(). + * + * @private + * @param {Object} response - The Advanced Service response. + * @param {Object} payload - The original request payload. + * @returns {object} - The first candidate content object. + * @throws {Error} If the response is invalid or contains an API error. + */ + function _normalizeVertexAiResponse(response, payload) { + if (!response) { + throw new Error('Vertex AI Advanced Service returned an empty response.'); + } + if (response.error) { + throw new Error(`Vertex AI Advanced Service returned an API error: ${JSON.stringify(response.error)}`); + } + + const firstCandidate = response.candidates?.[0]; + const responseMessage = firstCandidate?.content || null; + const finish_reason = firstCandidate?.finishReason || null; + + if (!responseMessage) { + throw new Error('Vertex AI Advanced Service returned no candidate content.'); + } + if (finish_reason == "length" || finish_reason == "incomplete" || finish_reason == "MAX_TOKENS") { + console.warn(`[GenAIApp] - ${payload.model} response could not be completed because of an insufficient amount of tokens. To resolve this issue, you can increase the amount of tokens like this : chat.run({max_tokens: XXXX}).`); + } + + if (verbose) { + Logger.log({ + message: `[GenAIApp] - Got response from ${payload.model}`, + responseMessage: responseMessage + }); + } + return responseMessage; + } + + /** + * Logs the internal reason for falling back from the Vertex AI Advanced Service. + * + * @private + * @param {Error} err - The Advanced Service failure. + */ + function _logVertexAiAdvancedServiceFallback(err) { + if (verbose) { + const message = err?.message || err; + console.warn(`[GenAIApp] - Vertex AI Advanced Service call failed; falling back to UrlFetchApp. Reason: ${message}`); + } + } + /** * Makes an API call to the specified GenAI endpoint (either OpenAI or Google) with a payload * and handles authentication, retries on rate limits and server errors, and response parsing. From 24c63e5d7e6af7bb240ae35880c4c002963c09d1 Mon Sep 17 00:00:00 2001 From: Benjamin Sayaque <91118734+Benjamin-Sayaque@users.noreply.github.com> Date: Tue, 2 Jun 2026 11:13:58 +0200 Subject: [PATCH 02/15] Detect custom Vertex AI service symbols --- src/code.gs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/code.gs b/src/code.gs index 0bdcfba..569a78b 100644 --- a/src/code.gs +++ b/src/code.gs @@ -1452,9 +1452,37 @@ const GenAIApp = (function () { if (typeof vertexai !== 'undefined') { return vertexai; } + + for (const globalSymbol in globalThis) { + try { + const candidateService = globalThis[globalSymbol]; + if (_isVertexAiAdvancedService(candidateService)) { + return candidateService; + } + } + catch (err) { + // Ignore globals that cannot be inspected and keep looking for the Advanced Service. + } + } + throw new Error('Vertex AI Advanced Service is not enabled or not available in this Apps Script project.'); } + /** + * Checks whether an object matches the Apps Script Vertex AI Advanced Service + * shape exposed for any enabledAdvancedServices[].userSymbol. + * + * @private + * @param {Object} service - A global object candidate from globalThis. + * @returns {boolean} - True when the object exposes the Endpoints.generateContent method. + */ + function _isVertexAiAdvancedService(service) { + return !!service + && typeof service === 'object' + && !!service.Endpoints + && typeof service.Endpoints.generateContent === 'function'; + } + /** * Resolves the generated Apps Script method for projects.locations.publishers.models.generateContent. * Apps Script Advanced Service names can differ by release/casing, so support the known variants. @@ -1466,6 +1494,8 @@ const GenAIApp = (function () { */ function _getVertexAiGenerateContentMethod(service) { const collectionPaths = [ + ['Endpoints'], + ['endpoints'], ['projects', 'locations', 'publishers', 'models'], ['Projects', 'Locations', 'Publishers', 'Models'] ]; From 97a7711536b5af04ecdfa9e05ae79bcd80858972 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 09:20:40 +0000 Subject: [PATCH 03/15] fix: apply CodeRabbit auto-fixes Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit --- src/code.gs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code.gs b/src/code.gs index 569a78b..a0fcd8b 100644 --- a/src/code.gs +++ b/src/code.gs @@ -1456,7 +1456,7 @@ const GenAIApp = (function () { for (const globalSymbol in globalThis) { try { const candidateService = globalThis[globalSymbol]; - if (_isVertexAiAdvancedService(candidateService)) { + if (_getVertexAiGenerateContentMethod(candidateService)) { return candidateService; } } From a94e3f052ee34289948e3faf33a90009d480cfc6 Mon Sep 17 00:00:00 2001 From: Paul Aubry Date: Tue, 2 Jun 2026 15:44:06 +0200 Subject: [PATCH 04/15] fixed includeServerSide issue --- src/code.gs | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/code.gs b/src/code.gs index a0fcd8b..062738d 100644 --- a/src/code.gs +++ b/src/code.gs @@ -513,7 +513,7 @@ const GenAIApp = (function () { } } } - if (model.includes("gemini") && !geminiKey) { + if (model.includes("gemini") && !geminiKey && payload?.tool_config?.includeServerSideToolInvocations === false) { // VertexAI does not support server-side tool invocation metadata in the response at the moment responseMessage = callVertexAi(endpointUrl, payload); } else { @@ -1418,10 +1418,10 @@ const GenAIApp = (function () { */ function callVertexAiWithAdvancedService(endpoint, payload) { const service = _getVertexAiAdvancedService(); - const generateContent = _getVertexAiGenerateContentMethod(service); + const generateContentInfo = _getVertexAiGenerateContentMethod(service); const modelResource = _getVertexAiModelResource(endpoint, payload); const advancedServicePayload = _buildVertexAiAdvancedServicePayload(payload); - const response = generateContent(advancedServicePayload, modelResource); + const response = generateContentInfo.method(advancedServicePayload, modelResource); return _normalizeVertexAiResponse(response, payload); } @@ -1468,21 +1468,6 @@ const GenAIApp = (function () { throw new Error('Vertex AI Advanced Service is not enabled or not available in this Apps Script project.'); } - /** - * Checks whether an object matches the Apps Script Vertex AI Advanced Service - * shape exposed for any enabledAdvancedServices[].userSymbol. - * - * @private - * @param {Object} service - A global object candidate from globalThis. - * @returns {boolean} - True when the object exposes the Endpoints.generateContent method. - */ - function _isVertexAiAdvancedService(service) { - return !!service - && typeof service === 'object' - && !!service.Endpoints - && typeof service.Endpoints.generateContent === 'function'; - } - /** * Resolves the generated Apps Script method for projects.locations.publishers.models.generateContent. * Apps Script Advanced Service names can differ by release/casing, so support the known variants. @@ -1494,23 +1479,28 @@ const GenAIApp = (function () { */ function _getVertexAiGenerateContentMethod(service) { const collectionPaths = [ - ['Endpoints'], - ['endpoints'], ['projects', 'locations', 'publishers', 'models'], - ['Projects', 'Locations', 'Publishers', 'Models'] + ['Projects', 'Locations', 'Publishers', 'Models'], + ['endpoints'], + ['Endpoints'] ]; for (let i = 0; i < collectionPaths.length; i++) { let collection = service; + for (let j = 0; j < collectionPaths[i].length && collection; j++) { collection = collection[collectionPaths[i][j]]; } + if (collection && typeof collection.generateContent === 'function') { - return collection.generateContent.bind(collection); + return { + method: collection.generateContent.bind(collection), + path: collectionPaths[i].join('.') + }; } } - throw new Error('Vertex AI Advanced Service does not expose projects.locations.publishers.models.generateContent.'); + throw new Error('Vertex AI Advanced Service does not expose a compatible generateContent method.'); } /** @@ -1553,6 +1543,7 @@ const GenAIApp = (function () { function _buildVertexAiAdvancedServicePayload(payload) { const advancedServicePayload = JSON.parse(JSON.stringify(payload || {})); delete advancedServicePayload.model; + delete advancedServicePayload.tool_config.includeServerSideToolInvocations; return advancedServicePayload; } From 6d8f120a2b96eaa45dc5e7f9650baf053b598d13 Mon Sep 17 00:00:00 2001 From: Paul Aubry Date: Tue, 2 Jun 2026 15:56:21 +0200 Subject: [PATCH 05/15] Code rabbit fix --- src/code.gs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/code.gs b/src/code.gs index 062738d..d5938d7 100644 --- a/src/code.gs +++ b/src/code.gs @@ -1474,7 +1474,7 @@ const GenAIApp = (function () { * * @private * @param {Object} service - The Vertex AI Advanced Service object. - * @returns {Function} - A generateContent method bound to its collection object. + * @returns {{method: Function, path: string}} - The generateContent method and its collection path. * @throws {Error} If the method is not exposed by the enabled Advanced Service. */ function _getVertexAiGenerateContentMethod(service) { @@ -1543,7 +1543,9 @@ const GenAIApp = (function () { function _buildVertexAiAdvancedServicePayload(payload) { const advancedServicePayload = JSON.parse(JSON.stringify(payload || {})); delete advancedServicePayload.model; - delete advancedServicePayload.tool_config.includeServerSideToolInvocations; + if (advancedServicePayload.tool_config) { + delete advancedServicePayload.tool_config.includeServerSideToolInvocations; + } return advancedServicePayload; } From 0a41b2a51f9f7332b446a7f1c0699f3f359faaab Mon Sep 17 00:00:00 2001 From: Paul Aubry Date: Wed, 3 Jun 2026 11:17:12 +0200 Subject: [PATCH 06/15] make functions privates --- src/code.gs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/code.gs b/src/code.gs index d5938d7..b4c590e 100644 --- a/src/code.gs +++ b/src/code.gs @@ -514,7 +514,7 @@ const GenAIApp = (function () { } } if (model.includes("gemini") && !geminiKey && payload?.tool_config?.includeServerSideToolInvocations === false) { // VertexAI does not support server-side tool invocation metadata in the response at the moment - responseMessage = callVertexAi(endpointUrl, payload); + responseMessage = _callVertexAi(endpointUrl, payload); } else { responseMessage = _callGenAIApi(endpointUrl, payload); @@ -1397,9 +1397,9 @@ const GenAIApp = (function () { * @param {Object} payload - The Gemini generateContent payload. * @returns {object} - The normalized response message from the Gemini API. */ - function callVertexAi(endpoint, payload) { + function _callVertexAi(endpoint, payload) { try { - return callVertexAiWithAdvancedService(endpoint, payload); + return _callVertexAiWithAdvancedService(endpoint, payload); } catch (err) { _logVertexAiAdvancedServiceFallback(err); @@ -1416,7 +1416,7 @@ const GenAIApp = (function () { * @returns {object} - The normalized response message from the Gemini API. * @throws {Error} If the Advanced Service is unavailable, unsupported, misconfigured, or returns an API error. */ - function callVertexAiWithAdvancedService(endpoint, payload) { + function _callVertexAiWithAdvancedService(endpoint, payload) { const service = _getVertexAiAdvancedService(); const generateContentInfo = _getVertexAiGenerateContentMethod(service); const modelResource = _getVertexAiModelResource(endpoint, payload); @@ -1434,7 +1434,7 @@ const GenAIApp = (function () { * @param {Object} payload - The Gemini generateContent payload. * @returns {object} - The normalized response message from the Gemini API. */ - function callVertexAiWithUrlFetchFallback(endpoint, payload) { + function _callVertexAiWithUrlFetchFallback(endpoint, payload) { return _callGenAIApi(endpoint, payload); } From 06e64dace09225afe5ec94103a145357fb56d728 Mon Sep 17 00:00:00 2001 From: Paul Aubry Date: Wed, 3 Jun 2026 11:29:57 +0200 Subject: [PATCH 07/15] hotfix --- src/code.gs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code.gs b/src/code.gs index b4c590e..16f0d27 100644 --- a/src/code.gs +++ b/src/code.gs @@ -1403,7 +1403,7 @@ const GenAIApp = (function () { } catch (err) { _logVertexAiAdvancedServiceFallback(err); - return callVertexAiWithUrlFetchFallback(endpoint, payload); + return _callVertexAiWithUrlFetchFallback(endpoint, payload); } } From 58ea6e3b4a52c0ad8b4928832138ad790b3e2d95 Mon Sep 17 00:00:00 2001 From: Benjamin Sayaque <91118734+Benjamin-Sayaque@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:55:10 +0200 Subject: [PATCH 08/15] Remove CI auth workflow from test changes --- README.md | 26 ++++++++ src/testFunctions.gs | 139 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) diff --git a/README.md b/README.md index 464b029..c4ed9b7 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ The **GenAIApp** library is a Google Apps Script library designed for creating, - [Example 6: Extend a Chat with an MCP Connector](#example-6--extend-a-chat-with-an-mcp-connector) - [Example 7: Connect to a Custom MCP Server with setServerUrl()](#example-7--connect-to-a-custom-mcp-server-with-setserverurl) - [Example 8: Continue a Conversation with previous_response_id](#example-8--continue-a-conversation-with-previous_response_id) +- [Testing authentication modes](#testing-authentication-modes) - [Contributing](#contributing) - [License](#license) - [Reference](#reference) @@ -461,6 +462,31 @@ const secondAnswer = secondChat.run({ model: "gpt-5.4" }); Logger.log(secondAnswer); ``` +## Testing authentication modes + +The library includes an Apps Script test entrypoint named `testConfiguredAuthenticationModes()` for explicitly validating the two Gemini authentication paths supported by GenAIApp: + +1. **API key authentication** through `GenAIApp.setGeminiAPIKey(...)`. +2. **Vertex AI authentication** through `GenAIApp.setGeminiAuth(projectId, region)`. + +Each mode can be enabled independently with boolean switches so you can run only API-key tests, only Vertex AI tests, both modes, or neither mode while a path is temporarily blocked. + +| Switch | Default | Behavior | +| --- | --- | --- | +| `ENABLE_API_KEY_AUTH_TESTS` | `true` | Runs the Gemini API-key smoke test when `true`; logs a skip when `false`. | +| `ENABLE_VERTEX_AI_AUTH_TESTS` | `false` | Runs the Gemini Vertex AI smoke test when `true`; logs a skip when `false`. | + +### Local Apps Script configuration + +Set these values as Apps Script **Script Properties** or define equivalent constants in your local test project. Do not print credential values in logs. + +| Name | Required when | Description | +| --- | --- | --- | +| `GEMINI_API_KEY` | `ENABLE_API_KEY_AUTH_TESTS=true` | Gemini API key used by the public Generative Language API test. | +| `VERTEX_AI_GCP_PROJECT_ID` | `ENABLE_VERTEX_AI_AUTH_TESTS=true` | GCP project linked to the Apps Script project and enabled for Vertex AI. | +| `VERTEX_AI_GCP_REGION` | Optional for Vertex AI | Vertex AI region such as `us-central1`; leave blank to use the global endpoint. | + +Run `testConfiguredAuthenticationModes()` from the Apps Script editor after setting the switches and credentials. Disabled modes log a clear skip message. Enabled modes fail before making an API call when required credentials/configuration are missing. The test code clears the opposite Gemini authentication setting before each smoke test so API-key coverage cannot accidentally pass through Vertex AI, and Vertex AI coverage cannot accidentally pass through the API-key path. ## Contributing diff --git a/src/testFunctions.gs b/src/testFunctions.gs index 983256c..130ad65 100644 --- a/src/testFunctions.gs +++ b/src/testFunctions.gs @@ -2,8 +2,147 @@ const GPT_MODEL = "gpt-5.4"; const REASONING_MODEL = "o4-mini"; const GEMINI_MODEL = "gemini-2.5-pro"; +const AUTH_TEST_CONFIG_KEYS = { + ENABLE_API_KEY_AUTH_TESTS: "ENABLE_API_KEY_AUTH_TESTS", + ENABLE_VERTEX_AI_AUTH_TESTS: "ENABLE_VERTEX_AI_AUTH_TESTS", + GEMINI_API_KEY: "GEMINI_API_KEY", + VERTEX_AI_GCP_PROJECT_ID: "VERTEX_AI_GCP_PROJECT_ID", + VERTEX_AI_GCP_REGION: "VERTEX_AI_GCP_REGION" +}; + +/** + * Reads test configuration from environment variables, Apps Script properties, + * or legacy global constants. This helper never logs credential values. + */ +function getAuthTestConfigValue(name, defaultValue) { + if (typeof process !== "undefined" && process.env && process.env[name] !== undefined) { + return process.env[name]; + } + + try { + const scriptProperties = PropertiesService.getScriptProperties(); + const propertyValue = scriptProperties.getProperty(name); + if (propertyValue !== null && propertyValue !== undefined) { + return propertyValue; + } + } + catch (err) { + // PropertiesService is unavailable outside Apps Script. Fall through to + // constants/defaults so local syntax checks can still load this file. + } + + if (name === AUTH_TEST_CONFIG_KEYS.ENABLE_API_KEY_AUTH_TESTS && typeof ENABLE_API_KEY_AUTH_TESTS !== "undefined") { + return ENABLE_API_KEY_AUTH_TESTS; + } + if (name === AUTH_TEST_CONFIG_KEYS.ENABLE_VERTEX_AI_AUTH_TESTS && typeof ENABLE_VERTEX_AI_AUTH_TESTS !== "undefined") { + return ENABLE_VERTEX_AI_AUTH_TESTS; + } + if (name === AUTH_TEST_CONFIG_KEYS.GEMINI_API_KEY && typeof GEMINI_API_KEY !== "undefined") { + return GEMINI_API_KEY; + } + if (name === AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_PROJECT_ID && typeof VERTEX_AI_GCP_PROJECT_ID !== "undefined") { + return VERTEX_AI_GCP_PROJECT_ID; + } + if (name === AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_REGION && typeof VERTEX_AI_GCP_REGION !== "undefined") { + return VERTEX_AI_GCP_REGION; + } + + return defaultValue; +} + +function getAuthTestBoolean(name, defaultValue) { + const rawValue = getAuthTestConfigValue(name, defaultValue ? "true" : "false"); + if (typeof rawValue === "boolean") { + return rawValue; + } + return String(rawValue).toLowerCase() === "true"; +} + +function requireAuthTestCredential(name, modeName) { + const value = getAuthTestConfigValue(name, ""); + if (!value) { + throw new Error(`[GenAIApp tests] ${modeName} tests are enabled, but required credential/configuration ${name} is missing.`); + } + return value; +} + +function skipAuthTest(modeName, reason) { + console.log(`[GenAIApp tests] Skipping ${modeName} authentication tests: ${reason}`); +} + +/** + * Stores auth-test switches in script properties before running the Apps Script + * test entrypoint. Do not pass raw secrets through this function. + */ +function configureAuthTestSwitches(enableApiKeyAuthTests, enableVertexAiAuthTests) { + PropertiesService.getScriptProperties().setProperties({ + ENABLE_API_KEY_AUTH_TESTS: String(enableApiKeyAuthTests), + ENABLE_VERTEX_AI_AUTH_TESTS: String(enableVertexAiAuthTests) + }); +} + +/** + * Entrypoint for authentication coverage. Set ENABLE_API_KEY_AUTH_TESTS and + * ENABLE_VERTEX_AI_AUTH_TESTS independently to run API-key auth, Vertex AI + * auth, both, or neither. Disabled modes log a clear skip. Enabled modes fail + * before calling the API when required credentials/configuration are absent. + */ +function testConfiguredAuthenticationModes() { + const runApiKeyAuthTests = getAuthTestBoolean(AUTH_TEST_CONFIG_KEYS.ENABLE_API_KEY_AUTH_TESTS, true); + const runVertexAiAuthTests = getAuthTestBoolean(AUTH_TEST_CONFIG_KEYS.ENABLE_VERTEX_AI_AUTH_TESTS, false); + + if (!runApiKeyAuthTests && !runVertexAiAuthTests) { + console.log("[GenAIApp tests] All Gemini authentication-mode tests are disabled."); + return; + } + + if (runApiKeyAuthTests) { + testApiKeyAuthentication(); + } + else { + skipAuthTest("API key", "ENABLE_API_KEY_AUTH_TESTS is not true"); + } + + if (runVertexAiAuthTests) { + testVertexAiAuthentication(); + } + else { + skipAuthTest("Vertex AI", "ENABLE_VERTEX_AI_AUTH_TESTS is not true"); + } +} + +function testApiKeyAuthentication() { + const geminiApiKey = requireAuthTestCredential(AUTH_TEST_CONFIG_KEYS.GEMINI_API_KEY, "API key authentication"); + + // Force the Gemini public API-key path and clear Vertex settings so this + // test cannot accidentally pass with Vertex AI credentials. + GenAIApp.setGeminiAuth(null, null); + GenAIApp.setGeminiAPIKey(geminiApiKey); + + const chat = GenAIApp.newChat(); + chat.addMessage("Reply with exactly: API key authentication ok"); + const response = chat.run({ model: GEMINI_MODEL, max_tokens: 64 }); + console.log(`[GenAIApp tests] API key authentication completed with response length ${String(response).length}.`); +} + +function testVertexAiAuthentication() { + const projectId = requireAuthTestCredential(AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_PROJECT_ID, "Vertex AI authentication"); + const vertexRegion = getAuthTestConfigValue(AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_REGION, ""); + + // Force the Vertex AI path and clear any Gemini API key so this test cannot + // accidentally pass through the Generative Language API. + GenAIApp.setGeminiAPIKey(null); + GenAIApp.setGeminiAuth(projectId, vertexRegion); + + const chat = GenAIApp.newChat(); + chat.addMessage("Reply with exactly: Vertex AI authentication ok"); + const response = chat.run({ model: GEMINI_MODEL, max_tokens: 64 }); + console.log(`[GenAIApp tests] Vertex AI authentication completed with response length ${String(response).length}.`); +} + // Run all tests function testAll() { + testConfiguredAuthenticationModes(); testSimpleChatInstance(); testFunctionCalling(); testFunctionCallingEndWithResult(); From 90e84edd427ab39655bc20ceb9e75ba16987f267 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 12:03:26 +0000 Subject: [PATCH 09/15] fix: apply CodeRabbit auto-fixes Fixed 2 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit --- src/code.gs | 11 +++++++++++ src/testFunctions.gs | 1 + 2 files changed, 12 insertions(+) diff --git a/src/code.gs b/src/code.gs index 16f0d27..c46caaa 100644 --- a/src/code.gs +++ b/src/code.gs @@ -2669,6 +2669,17 @@ const GenAIApp = (function () { */ setPrivateInstanceBaseUrl: function (baseUrl) { privateInstanceBaseUrl = baseUrl; + }, + + /** + * Resets Gemini authentication state to default values. + * Clears API key, GCP project ID, and region settings. + * Useful for test isolation to prevent auth state from leaking between tests. + */ + resetGeminiAuthState: function () { + geminiKey = ""; + gcpProjectId = ""; + region = ""; } } })(); \ No newline at end of file diff --git a/src/testFunctions.gs b/src/testFunctions.gs index 130ad65..fbc72fd 100644 --- a/src/testFunctions.gs +++ b/src/testFunctions.gs @@ -143,6 +143,7 @@ function testVertexAiAuthentication() { // Run all tests function testAll() { testConfiguredAuthenticationModes(); + GenAIApp.resetGeminiAuthState(); testSimpleChatInstance(); testFunctionCalling(); testFunctionCallingEndWithResult(); From e6377bcde82be52057821894bdf852dd7bc58604 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 12:45:01 +0000 Subject: [PATCH 10/15] fix: apply CodeRabbit auto-fixes Fixed 1 file(s) based on 1 unresolved review comment. Co-authored-by: CodeRabbit --- src/testFunctions.gs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/testFunctions.gs b/src/testFunctions.gs index fbc72fd..285cb62 100644 --- a/src/testFunctions.gs +++ b/src/testFunctions.gs @@ -59,7 +59,7 @@ function getAuthTestBoolean(name, defaultValue) { } function requireAuthTestCredential(name, modeName) { - const value = getAuthTestConfigValue(name, ""); + const value = getAuthTestConfigValue(name, "").trim(); if (!value) { throw new Error(`[GenAIApp tests] ${modeName} tests are enabled, but required credential/configuration ${name} is missing.`); } From 7f1b941c6e7cf10685345a494b8c1c92d2c0f394 Mon Sep 17 00:00:00 2001 From: Paul Aubry Date: Mon, 8 Jun 2026 15:38:59 +0200 Subject: [PATCH 11/15] fixed bug --- src/testFunctions.gs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/testFunctions.gs b/src/testFunctions.gs index 285cb62..8b508c0 100644 --- a/src/testFunctions.gs +++ b/src/testFunctions.gs @@ -1,6 +1,6 @@ const GPT_MODEL = "gpt-5.4"; const REASONING_MODEL = "o4-mini"; -const GEMINI_MODEL = "gemini-2.5-pro"; +const GEMINI_MODEL = "gemini-3.5-flash"; const AUTH_TEST_CONFIG_KEYS = { ENABLE_API_KEY_AUTH_TESTS: "ENABLE_API_KEY_AUTH_TESTS", @@ -150,7 +150,7 @@ function testAll() { testFunctionCallingOnlyReturnArguments(); testBrowsing(); testKnowledgeLink(); - testVision(); + //testVision(); testMaximumAPICalls(); testInputTokenWarning(); } @@ -158,9 +158,16 @@ function testAll() { // Helper to set API keys and run tests across models function runTestAcrossModels(testName, setupFunction, runOptions = {}) { - // Set API keys once per batch - GenAIApp.setGeminiAPIKey(GEMINI_API_KEY); - GenAIApp.setOpenAIAPIKey(OPEN_AI_API_KEY); + const geminiApiKey = getAuthTestConfigValue("GEMINI_API_KEY", ""); + const openAiApiKey = getAuthTestConfigValue("OPEN_AI_API_KEY", ""); + + if (geminiApiKey) { + GenAIApp.setGeminiAPIKey(geminiApiKey); + } + + if (openAiApiKey) { + GenAIApp.setOpenAIAPIKey(openAiApiKey); + } const models = [ { name: GPT_MODEL, label: "GPT" }, @@ -183,7 +190,7 @@ function testSimpleChatInstance() { chat .addMessage("You're name is Tom, you're a Google Developper Expert and always willing to give useful tips. Always answer in a friendly manner, and include one joke at the end of your messages.", true) .addMessage("What are the best pratices to document a project?"); - }, { max_tokens: 1000 }); + }, { max_tokens: 10000 }); } function testFunctionCalling() { @@ -196,7 +203,7 @@ function testFunctionCalling() { chat .addMessage("What's the weather in Lyon and Paris today?") .addFunction(weatherFunction); - }, { max_tokens: 1000 }); + }, { max_tokens: 10000 }); } function testFunctionCallingEndWithResult() { From 85e87d685bea9222aaeb1427d7adbf23fbfde95a Mon Sep 17 00:00:00 2001 From: Paul Aubry Date: Tue, 9 Jun 2026 10:14:20 +0200 Subject: [PATCH 12/15] hotfix --- src/code.gs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code.gs b/src/code.gs index 1bad76d..8d23f52 100644 --- a/src/code.gs +++ b/src/code.gs @@ -540,7 +540,7 @@ const GenAIApp = (function () { } } } - if (model.includes("gemini") && !geminiKey && payload?.tool_config?.includeServerSideToolInvocations === false) { // VertexAI does not support server-side tool invocation metadata in the response at the moment + if (model.includes("gemini") && !geminiKey && !payload?.tool_config?.includeServerSideToolInvocations) { // VertexAI does not support server-side tool invocation metadata in the response at the moment responseMessage = _callVertexAi(endpointUrl, payload); } else { From 37f9e9c30527ecc8f240bb48e6df5e8bbc289ee7 Mon Sep 17 00:00:00 2001 From: Paul Aubry Date: Tue, 9 Jun 2026 10:42:42 +0200 Subject: [PATCH 13/15] hotfix --- src/code.gs | 4 +++- src/testFunctions.gs | 14 +++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/code.gs b/src/code.gs index 8d23f52..f454461 100644 --- a/src/code.gs +++ b/src/code.gs @@ -320,7 +320,9 @@ const GenAIApp = (function () { if (containerId) { this._codeInterpreterContainerId = containerId; } - + return this; + }; + /** OPTIONAL * * Enable or disable server-side tool invocations for Gemini (Tool Combination). diff --git a/src/testFunctions.gs b/src/testFunctions.gs index 9b68609..ab39b77 100644 --- a/src/testFunctions.gs +++ b/src/testFunctions.gs @@ -1,13 +1,16 @@ const GPT_MODEL = "gpt-5.4"; const REASONING_MODEL = "o4-mini"; const GEMINI_MODEL = "gemini-3.5-flash"; +const TEST_CODE_INTERPRETER_XLSX_DRIVE_FILE_ID = ""; +const TEST_CODE_INTERPRETER_PDF_DRIVE_FILE_ID = ""; const AUTH_TEST_CONFIG_KEYS = { ENABLE_API_KEY_AUTH_TESTS: "ENABLE_API_KEY_AUTH_TESTS", ENABLE_VERTEX_AI_AUTH_TESTS: "ENABLE_VERTEX_AI_AUTH_TESTS", GEMINI_API_KEY: "GEMINI_API_KEY", VERTEX_AI_GCP_PROJECT_ID: "VERTEX_AI_GCP_PROJECT_ID", - VERTEX_AI_GCP_REGION: "VERTEX_AI_GCP_REGION" + VERTEX_AI_GCP_REGION: "VERTEX_AI_GCP_REGION", + OPEN_AI_API_KEY: "OPEN_AI_API_KEY" }; /** @@ -46,6 +49,9 @@ function getAuthTestConfigValue(name, defaultValue) { if (name === AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_REGION && typeof VERTEX_AI_GCP_REGION !== "undefined") { return VERTEX_AI_GCP_REGION; } + if (name === AUTH_TEST_CONFIG_KEYS.OPEN_AI_API_KEY && typeof OPEN_AI_API_KEY !== "undefined") { + return OPEN_AI_API_KEY; + } return defaultValue; } @@ -113,15 +119,17 @@ function testConfiguredAuthenticationModes() { function testApiKeyAuthentication() { const geminiApiKey = requireAuthTestCredential(AUTH_TEST_CONFIG_KEYS.GEMINI_API_KEY, "API key authentication"); + const openAiApiKey = requireAuthTestCredential(AUTH_TEST_CONFIG_KEYS.OPEN_AI_API_KEY, "API key authentication"); // Force the Gemini public API-key path and clear Vertex settings so this // test cannot accidentally pass with Vertex AI credentials. GenAIApp.setGeminiAuth(null, null); GenAIApp.setGeminiAPIKey(geminiApiKey); + GenAIApp.setOpenAIAPIKey(openAiApiKey); const chat = GenAIApp.newChat(); chat.addMessage("Reply with exactly: API key authentication ok"); - const response = chat.run({ model: GEMINI_MODEL, max_tokens: 64 }); + const response = chat.run({ model: GEMINI_MODEL, max_tokens: 512 }); console.log(`[GenAIApp tests] API key authentication completed with response length ${String(response).length}.`); } @@ -136,7 +144,7 @@ function testVertexAiAuthentication() { const chat = GenAIApp.newChat(); chat.addMessage("Reply with exactly: Vertex AI authentication ok"); - const response = chat.run({ model: GEMINI_MODEL, max_tokens: 64 }); + const response = chat.run({ model: GEMINI_MODEL, max_tokens: 512 }); console.log(`[GenAIApp tests] Vertex AI authentication completed with response length ${String(response).length}.`); } From b1a1d0860d736e2afcb2d483b0cd06b71435dde3 Mon Sep 17 00:00:00 2001 From: aubrypaul <62645653+aubrypaul@users.noreply.github.com> Date: Tue, 9 Jun 2026 10:59:25 +0200 Subject: [PATCH 14/15] Update src/testFunctions.gs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/testFunctions.gs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/testFunctions.gs b/src/testFunctions.gs index ab39b77..d4447f0 100644 --- a/src/testFunctions.gs +++ b/src/testFunctions.gs @@ -119,13 +119,11 @@ function testConfiguredAuthenticationModes() { function testApiKeyAuthentication() { const geminiApiKey = requireAuthTestCredential(AUTH_TEST_CONFIG_KEYS.GEMINI_API_KEY, "API key authentication"); - const openAiApiKey = requireAuthTestCredential(AUTH_TEST_CONFIG_KEYS.OPEN_AI_API_KEY, "API key authentication"); // Force the Gemini public API-key path and clear Vertex settings so this // test cannot accidentally pass with Vertex AI credentials. GenAIApp.setGeminiAuth(null, null); GenAIApp.setGeminiAPIKey(geminiApiKey); - GenAIApp.setOpenAIAPIKey(openAiApiKey); const chat = GenAIApp.newChat(); chat.addMessage("Reply with exactly: API key authentication ok"); From 0cbf5947206d2cbc10bb36491b3eb1e759032e0c Mon Sep 17 00:00:00 2001 From: Paul Aubry Date: Wed, 10 Jun 2026 17:05:00 +0200 Subject: [PATCH 15/15] Revert change on tests (different PR) --- src/testFunctions.gs | 167 +++---------------------------------------- 1 file changed, 8 insertions(+), 159 deletions(-) diff --git a/src/testFunctions.gs b/src/testFunctions.gs index d4447f0..c8c9492 100644 --- a/src/testFunctions.gs +++ b/src/testFunctions.gs @@ -1,162 +1,18 @@ const GPT_MODEL = "gpt-5.4"; const REASONING_MODEL = "o4-mini"; -const GEMINI_MODEL = "gemini-3.5-flash"; +const GEMINI_MODEL = "gemini-2.5-pro"; const TEST_CODE_INTERPRETER_XLSX_DRIVE_FILE_ID = ""; const TEST_CODE_INTERPRETER_PDF_DRIVE_FILE_ID = ""; -const AUTH_TEST_CONFIG_KEYS = { - ENABLE_API_KEY_AUTH_TESTS: "ENABLE_API_KEY_AUTH_TESTS", - ENABLE_VERTEX_AI_AUTH_TESTS: "ENABLE_VERTEX_AI_AUTH_TESTS", - GEMINI_API_KEY: "GEMINI_API_KEY", - VERTEX_AI_GCP_PROJECT_ID: "VERTEX_AI_GCP_PROJECT_ID", - VERTEX_AI_GCP_REGION: "VERTEX_AI_GCP_REGION", - OPEN_AI_API_KEY: "OPEN_AI_API_KEY" -}; - -/** - * Reads test configuration from environment variables, Apps Script properties, - * or legacy global constants. This helper never logs credential values. - */ -function getAuthTestConfigValue(name, defaultValue) { - if (typeof process !== "undefined" && process.env && process.env[name] !== undefined) { - return process.env[name]; - } - - try { - const scriptProperties = PropertiesService.getScriptProperties(); - const propertyValue = scriptProperties.getProperty(name); - if (propertyValue !== null && propertyValue !== undefined) { - return propertyValue; - } - } - catch (err) { - // PropertiesService is unavailable outside Apps Script. Fall through to - // constants/defaults so local syntax checks can still load this file. - } - - if (name === AUTH_TEST_CONFIG_KEYS.ENABLE_API_KEY_AUTH_TESTS && typeof ENABLE_API_KEY_AUTH_TESTS !== "undefined") { - return ENABLE_API_KEY_AUTH_TESTS; - } - if (name === AUTH_TEST_CONFIG_KEYS.ENABLE_VERTEX_AI_AUTH_TESTS && typeof ENABLE_VERTEX_AI_AUTH_TESTS !== "undefined") { - return ENABLE_VERTEX_AI_AUTH_TESTS; - } - if (name === AUTH_TEST_CONFIG_KEYS.GEMINI_API_KEY && typeof GEMINI_API_KEY !== "undefined") { - return GEMINI_API_KEY; - } - if (name === AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_PROJECT_ID && typeof VERTEX_AI_GCP_PROJECT_ID !== "undefined") { - return VERTEX_AI_GCP_PROJECT_ID; - } - if (name === AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_REGION && typeof VERTEX_AI_GCP_REGION !== "undefined") { - return VERTEX_AI_GCP_REGION; - } - if (name === AUTH_TEST_CONFIG_KEYS.OPEN_AI_API_KEY && typeof OPEN_AI_API_KEY !== "undefined") { - return OPEN_AI_API_KEY; - } - - return defaultValue; -} - -function getAuthTestBoolean(name, defaultValue) { - const rawValue = getAuthTestConfigValue(name, defaultValue ? "true" : "false"); - if (typeof rawValue === "boolean") { - return rawValue; - } - return String(rawValue).toLowerCase() === "true"; -} - -function requireAuthTestCredential(name, modeName) { - const value = getAuthTestConfigValue(name, "").trim(); - if (!value) { - throw new Error(`[GenAIApp tests] ${modeName} tests are enabled, but required credential/configuration ${name} is missing.`); - } - return value; -} - -function skipAuthTest(modeName, reason) { - console.log(`[GenAIApp tests] Skipping ${modeName} authentication tests: ${reason}`); -} - -/** - * Stores auth-test switches in script properties before running the Apps Script - * test entrypoint. Do not pass raw secrets through this function. - */ -function configureAuthTestSwitches(enableApiKeyAuthTests, enableVertexAiAuthTests) { - PropertiesService.getScriptProperties().setProperties({ - ENABLE_API_KEY_AUTH_TESTS: String(enableApiKeyAuthTests), - ENABLE_VERTEX_AI_AUTH_TESTS: String(enableVertexAiAuthTests) - }); -} - -/** - * Entrypoint for authentication coverage. Set ENABLE_API_KEY_AUTH_TESTS and - * ENABLE_VERTEX_AI_AUTH_TESTS independently to run API-key auth, Vertex AI - * auth, both, or neither. Disabled modes log a clear skip. Enabled modes fail - * before calling the API when required credentials/configuration are absent. - */ -function testConfiguredAuthenticationModes() { - const runApiKeyAuthTests = getAuthTestBoolean(AUTH_TEST_CONFIG_KEYS.ENABLE_API_KEY_AUTH_TESTS, true); - const runVertexAiAuthTests = getAuthTestBoolean(AUTH_TEST_CONFIG_KEYS.ENABLE_VERTEX_AI_AUTH_TESTS, false); - - if (!runApiKeyAuthTests && !runVertexAiAuthTests) { - console.log("[GenAIApp tests] All Gemini authentication-mode tests are disabled."); - return; - } - - if (runApiKeyAuthTests) { - testApiKeyAuthentication(); - } - else { - skipAuthTest("API key", "ENABLE_API_KEY_AUTH_TESTS is not true"); - } - - if (runVertexAiAuthTests) { - testVertexAiAuthentication(); - } - else { - skipAuthTest("Vertex AI", "ENABLE_VERTEX_AI_AUTH_TESTS is not true"); - } -} - -function testApiKeyAuthentication() { - const geminiApiKey = requireAuthTestCredential(AUTH_TEST_CONFIG_KEYS.GEMINI_API_KEY, "API key authentication"); - - // Force the Gemini public API-key path and clear Vertex settings so this - // test cannot accidentally pass with Vertex AI credentials. - GenAIApp.setGeminiAuth(null, null); - GenAIApp.setGeminiAPIKey(geminiApiKey); - - const chat = GenAIApp.newChat(); - chat.addMessage("Reply with exactly: API key authentication ok"); - const response = chat.run({ model: GEMINI_MODEL, max_tokens: 512 }); - console.log(`[GenAIApp tests] API key authentication completed with response length ${String(response).length}.`); -} - -function testVertexAiAuthentication() { - const projectId = requireAuthTestCredential(AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_PROJECT_ID, "Vertex AI authentication"); - const vertexRegion = getAuthTestConfigValue(AUTH_TEST_CONFIG_KEYS.VERTEX_AI_GCP_REGION, ""); - - // Force the Vertex AI path and clear any Gemini API key so this test cannot - // accidentally pass through the Generative Language API. - GenAIApp.setGeminiAPIKey(null); - GenAIApp.setGeminiAuth(projectId, vertexRegion); - - const chat = GenAIApp.newChat(); - chat.addMessage("Reply with exactly: Vertex AI authentication ok"); - const response = chat.run({ model: GEMINI_MODEL, max_tokens: 512 }); - console.log(`[GenAIApp tests] Vertex AI authentication completed with response length ${String(response).length}.`); -} - // Run all tests function testAll() { - testConfiguredAuthenticationModes(); - GenAIApp.resetGeminiAuthState(); testSimpleChatInstance(); testFunctionCalling(); testFunctionCallingEndWithResult(); testFunctionCallingOnlyReturnArguments(); testBrowsing(); testKnowledgeLink(); - //testVision(); + testVision(); testMaximumAPICalls(); testInputTokenWarning(); // OpenAI-only tests - require valid Drive file IDs. @@ -171,16 +27,9 @@ function testAll() { // Helper to set API keys and run tests across models function runTestAcrossModels(testName, setupFunction, runOptions = {}) { - const geminiApiKey = getAuthTestConfigValue("GEMINI_API_KEY", ""); - const openAiApiKey = getAuthTestConfigValue("OPEN_AI_API_KEY", ""); - - if (geminiApiKey) { - GenAIApp.setGeminiAPIKey(geminiApiKey); - } - - if (openAiApiKey) { - GenAIApp.setOpenAIAPIKey(openAiApiKey); - } + // Set API keys once per batch + GenAIApp.setGeminiAPIKey(GEMINI_API_KEY); + GenAIApp.setOpenAIAPIKey(OPEN_AI_API_KEY); const models = [ { name: GPT_MODEL, label: "GPT" }, @@ -203,7 +52,7 @@ function testSimpleChatInstance() { chat .addMessage("You're name is Tom, you're a Google Developper Expert and always willing to give useful tips. Always answer in a friendly manner, and include one joke at the end of your messages.", true) .addMessage("What are the best pratices to document a project?"); - }, { max_tokens: 10000 }); + }, { max_tokens: 1000 }); } function testFunctionCalling() { @@ -216,7 +65,7 @@ function testFunctionCalling() { chat .addMessage("What's the weather in Lyon and Paris today?") .addFunction(weatherFunction); - }, { max_tokens: 10000 }); + }, { max_tokens: 1000 }); } function testFunctionCallingEndWithResult() { @@ -338,4 +187,4 @@ function testCodeInterpreterPDF(driveFileId) { // Weather function implementation function getWeather(cityName) { return `The weather in ${cityName} is 19°C today.`; -} +} \ No newline at end of file