From 17eeab9a8579b13a68b5e315bdbe385e27c974e7 Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Wed, 29 Jul 2026 03:24:40 -0700 Subject: [PATCH 1/6] feat(utils): honor GOOGLE_GENAI_USE_ENTERPRISE in getExpressModeApiKey Express mode key resolution only recognized GOOGLE_GENAI_USE_VERTEXAI, so environments already migrated to GOOGLE_GENAI_USE_ENTERPRISE got undefined and then failed with "Either (Project ID and Location) or an expressModeApiKey is required." Resolve enterprise mode by presence, matching adk-python's is_enterprise_mode_enabled(): GOOGLE_GENAI_USE_ENTERPRISE decides whenever it is set (even to a falsy value), GOOGLE_GENAI_USE_VERTEXAI is only consulted when the new variable is absent and logs a deprecation warning. --- core/src/utils/vertex_ai_utils.ts | 34 ++++++- core/test/utils/vertex_ai_utils_test.ts | 112 +++++++++++++++++++++++- 2 files changed, 142 insertions(+), 4 deletions(-) diff --git a/core/src/utils/vertex_ai_utils.ts b/core/src/utils/vertex_ai_utils.ts index fcc68eda1f..ddf1762167 100644 --- a/core/src/utils/vertex_ai_utils.ts +++ b/core/src/utils/vertex_ai_utils.ts @@ -5,6 +5,34 @@ */ import {getBooleanEnvVar} from './env_aware_utils.js'; +import {logger} from './logger.js'; + +const ENTERPRISE_MODE_ENV_VAR = 'GOOGLE_GENAI_USE_ENTERPRISE'; +const DEPRECATED_ENTERPRISE_MODE_ENV_VAR = 'GOOGLE_GENAI_USE_VERTEXAI'; + +/** + * Returns whether Google GenAI enterprise mode is enabled. + * + * `GOOGLE_GENAI_USE_ENTERPRISE` takes precedence whenever it is set, even when + * it is set to a falsy value. `GOOGLE_GENAI_USE_VERTEXAI` is only consulted + * when `GOOGLE_GENAI_USE_ENTERPRISE` is absent, and using it logs a deprecation + * warning. + */ +function isEnterpriseModeEnabled(): boolean { + if (process.env?.[ENTERPRISE_MODE_ENV_VAR] !== undefined) { + return getBooleanEnvVar(ENTERPRISE_MODE_ENV_VAR); + } + + if (process.env?.[DEPRECATED_ENTERPRISE_MODE_ENV_VAR] !== undefined) { + logger.warn( + `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` + + `${ENTERPRISE_MODE_ENV_VAR} instead`, + ); + return getBooleanEnvVar(DEPRECATED_ENTERPRISE_MODE_ENV_VAR); + } + + return false; +} export const EXPRESS_MODE_UNSUPPORTED_MESSAGE = 'Vertex AI Express Mode (expressModeApiKey / GOOGLE_API_KEY) is not ' + @@ -15,6 +43,10 @@ export const EXPRESS_MODE_UNSUPPORTED_MESSAGE = /** * Validates and returns the API key for Express Mode. * + * The key is only returned when enterprise mode is enabled via + * `GOOGLE_GENAI_USE_ENTERPRISE` (or the deprecated + * `GOOGLE_GENAI_USE_VERTEXAI`). + * * @param project The project id. * @param location The location. * @param expressModeApiKey The API key for Express Mode. @@ -32,7 +64,7 @@ export function getExpressModeApiKey( ); } - if (getBooleanEnvVar('GOOGLE_GENAI_USE_VERTEXAI')) { + if (isEnterpriseModeEnabled()) { return expressModeApiKey || process.env.GOOGLE_API_KEY; } diff --git a/core/test/utils/vertex_ai_utils_test.ts b/core/test/utils/vertex_ai_utils_test.ts index d397f143c1..59f6207bff 100644 --- a/core/test/utils/vertex_ai_utils_test.ts +++ b/core/test/utils/vertex_ai_utils_test.ts @@ -4,19 +4,34 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {afterEach, beforeEach, describe, expect, it} from 'vitest'; +import { + afterEach, + beforeEach, + describe, + expect, + it, + vi, + type MockInstance, +} from 'vitest'; +import {logger} from '../../src/utils/logger.js'; import {getExpressModeApiKey} from '../../src/utils/vertex_ai_utils.js'; describe('vertex_ai_utils', () => { describe('getExpressModeApiKey', () => { const originalEnv = process.env; + let warnSpy: MockInstance; beforeEach(() => { process.env = {...originalEnv}; + delete process.env['GOOGLE_GENAI_USE_ENTERPRISE']; + delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; + delete process.env['GOOGLE_API_KEY']; + warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); }); afterEach(() => { process.env = originalEnv; + vi.restoreAllMocks(); }); it('should throw when both project and expressModeApiKey are provided', () => { @@ -38,7 +53,6 @@ describe('vertex_ai_utils', () => { }); it('should return undefined when GOOGLE_GENAI_USE_VERTEXAI is not set', () => { - delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; const result = getExpressModeApiKey(); expect(result).toBeUndefined(); }); @@ -64,9 +78,101 @@ describe('vertex_ai_utils', () => { it('should return undefined when GOOGLE_GENAI_USE_VERTEXAI is true but no key available', () => { process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; - delete process.env['GOOGLE_API_KEY']; const result = getExpressModeApiKey(); expect(result).toBeUndefined(); }); + + it('should return expressModeApiKey when GOOGLE_GENAI_USE_ENTERPRISE is true', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; + const result = getExpressModeApiKey(undefined, undefined, 'my-api-key'); + expect(result).toBe('my-api-key'); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should return GOOGLE_API_KEY from env when GOOGLE_GENAI_USE_ENTERPRISE is true and no key provided', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; + process.env['GOOGLE_API_KEY'] = 'env-api-key'; + const result = getExpressModeApiKey(); + expect(result).toBe('env-api-key'); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should treat GOOGLE_GENAI_USE_ENTERPRISE=1 as enabled', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = '1'; + const result = getExpressModeApiKey(undefined, undefined, 'my-api-key'); + expect(result).toBe('my-api-key'); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should treat GOOGLE_GENAI_USE_ENTERPRISE case-insensitively', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'TRUE'; + const result = getExpressModeApiKey(undefined, undefined, 'my-api-key'); + expect(result).toBe('my-api-key'); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should return undefined when GOOGLE_GENAI_USE_ENTERPRISE is false', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'false'; + process.env['GOOGLE_API_KEY'] = 'env-api-key'; + const result = getExpressModeApiKey(); + expect(result).toBeUndefined(); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should not fall back to GOOGLE_GENAI_USE_VERTEXAI when GOOGLE_GENAI_USE_ENTERPRISE is set but empty', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = ''; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + process.env['GOOGLE_API_KEY'] = 'env-api-key'; + const result = getExpressModeApiKey(); + expect(result).toBeUndefined(); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should let a false GOOGLE_GENAI_USE_ENTERPRISE override a true GOOGLE_GENAI_USE_VERTEXAI', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'false'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + process.env['GOOGLE_API_KEY'] = 'env-api-key'; + const result = getExpressModeApiKey(); + expect(result).toBeUndefined(); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should let a true GOOGLE_GENAI_USE_ENTERPRISE override a false GOOGLE_GENAI_USE_VERTEXAI', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; + process.env['GOOGLE_API_KEY'] = 'env-api-key'; + const result = getExpressModeApiKey(); + expect(result).toBe('env-api-key'); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should warn when falling back to a true GOOGLE_GENAI_USE_VERTEXAI', () => { + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + process.env['GOOGLE_API_KEY'] = 'env-api-key'; + const result = getExpressModeApiKey(); + expect(result).toBe('env-api-key'); + expect(warnSpy).toHaveBeenCalledOnce(); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('GOOGLE_GENAI_USE_VERTEXAI is deprecated'), + ); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('GOOGLE_GENAI_USE_ENTERPRISE'), + ); + }); + + it('should warn when falling back to a false GOOGLE_GENAI_USE_VERTEXAI', () => { + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; + process.env['GOOGLE_API_KEY'] = 'env-api-key'; + const result = getExpressModeApiKey(); + expect(result).toBeUndefined(); + expect(warnSpy).toHaveBeenCalledOnce(); + }); + + it('should return undefined and not warn when neither enterprise mode variable is set', () => { + process.env['GOOGLE_API_KEY'] = 'env-api-key'; + const result = getExpressModeApiKey(); + expect(result).toBeUndefined(); + expect(warnSpy).not.toHaveBeenCalled(); + }); }); }); From 85b79bd11bb8007de0b4bdd6e36eaba9f6c3e97e Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Wed, 29 Jul 2026 03:34:29 -0700 Subject: [PATCH 2/6] test(utils): trim express-mode enterprise cases to distinct branches Drop cases that re-tested getBooleanEnvVar's value parser (already covered by env_aware_utils_test.ts) and fold the deprecation-warning assertions into the existing GOOGLE_GENAI_USE_VERTEXAI tests instead of duplicating them. Coverage of vertex_ai_utils.ts stays at 100% statements/branches/functions/lines. --- core/test/utils/vertex_ai_utils_test.ts | 88 +++++-------------------- 1 file changed, 15 insertions(+), 73 deletions(-) diff --git a/core/test/utils/vertex_ai_utils_test.ts b/core/test/utils/vertex_ai_utils_test.ts index 59f6207bff..fb5969812e 100644 --- a/core/test/utils/vertex_ai_utils_test.ts +++ b/core/test/utils/vertex_ai_utils_test.ts @@ -52,15 +52,19 @@ describe('vertex_ai_utils', () => { ).toThrow(); }); - it('should return undefined when GOOGLE_GENAI_USE_VERTEXAI is not set', () => { + it('should return undefined and not warn when neither enterprise mode variable is set', () => { + process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBeUndefined(); + expect(warnSpy).not.toHaveBeenCalled(); }); it('should return undefined when GOOGLE_GENAI_USE_VERTEXAI is false', () => { process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; + process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBeUndefined(); + expect(warnSpy).toHaveBeenCalledOnce(); }); it('should return expressModeApiKey when GOOGLE_GENAI_USE_VERTEXAI is true', () => { @@ -74,6 +78,13 @@ describe('vertex_ai_utils', () => { process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBe('env-api-key'); + expect(warnSpy).toHaveBeenCalledOnce(); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'GOOGLE_GENAI_USE_VERTEXAI is deprecated, please use ' + + 'GOOGLE_GENAI_USE_ENTERPRISE', + ), + ); }); it('should return undefined when GOOGLE_GENAI_USE_VERTEXAI is true but no key available', () => { @@ -86,58 +97,9 @@ describe('vertex_ai_utils', () => { process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; const result = getExpressModeApiKey(undefined, undefined, 'my-api-key'); expect(result).toBe('my-api-key'); - expect(warnSpy).not.toHaveBeenCalled(); - }); - - it('should return GOOGLE_API_KEY from env when GOOGLE_GENAI_USE_ENTERPRISE is true and no key provided', () => { - process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; - process.env['GOOGLE_API_KEY'] = 'env-api-key'; - const result = getExpressModeApiKey(); - expect(result).toBe('env-api-key'); - expect(warnSpy).not.toHaveBeenCalled(); - }); - - it('should treat GOOGLE_GENAI_USE_ENTERPRISE=1 as enabled', () => { - process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = '1'; - const result = getExpressModeApiKey(undefined, undefined, 'my-api-key'); - expect(result).toBe('my-api-key'); - expect(warnSpy).not.toHaveBeenCalled(); - }); - - it('should treat GOOGLE_GENAI_USE_ENTERPRISE case-insensitively', () => { - process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'TRUE'; - const result = getExpressModeApiKey(undefined, undefined, 'my-api-key'); - expect(result).toBe('my-api-key'); - expect(warnSpy).not.toHaveBeenCalled(); - }); - - it('should return undefined when GOOGLE_GENAI_USE_ENTERPRISE is false', () => { - process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'false'; - process.env['GOOGLE_API_KEY'] = 'env-api-key'; - const result = getExpressModeApiKey(); - expect(result).toBeUndefined(); - expect(warnSpy).not.toHaveBeenCalled(); - }); - - it('should not fall back to GOOGLE_GENAI_USE_VERTEXAI when GOOGLE_GENAI_USE_ENTERPRISE is set but empty', () => { - process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = ''; - process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; - process.env['GOOGLE_API_KEY'] = 'env-api-key'; - const result = getExpressModeApiKey(); - expect(result).toBeUndefined(); - expect(warnSpy).not.toHaveBeenCalled(); }); - it('should let a false GOOGLE_GENAI_USE_ENTERPRISE override a true GOOGLE_GENAI_USE_VERTEXAI', () => { - process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'false'; - process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; - process.env['GOOGLE_API_KEY'] = 'env-api-key'; - const result = getExpressModeApiKey(); - expect(result).toBeUndefined(); - expect(warnSpy).not.toHaveBeenCalled(); - }); - - it('should let a true GOOGLE_GENAI_USE_ENTERPRISE override a false GOOGLE_GENAI_USE_VERTEXAI', () => { + it('should prefer an enabled GOOGLE_GENAI_USE_ENTERPRISE over GOOGLE_GENAI_USE_VERTEXAI', () => { process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; process.env['GOOGLE_API_KEY'] = 'env-api-key'; @@ -146,29 +108,9 @@ describe('vertex_ai_utils', () => { expect(warnSpy).not.toHaveBeenCalled(); }); - it('should warn when falling back to a true GOOGLE_GENAI_USE_VERTEXAI', () => { + it('should not fall back to GOOGLE_GENAI_USE_VERTEXAI when GOOGLE_GENAI_USE_ENTERPRISE is set but disabled', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = ''; process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; - process.env['GOOGLE_API_KEY'] = 'env-api-key'; - const result = getExpressModeApiKey(); - expect(result).toBe('env-api-key'); - expect(warnSpy).toHaveBeenCalledOnce(); - expect(warnSpy).toHaveBeenCalledWith( - expect.stringContaining('GOOGLE_GENAI_USE_VERTEXAI is deprecated'), - ); - expect(warnSpy).toHaveBeenCalledWith( - expect.stringContaining('GOOGLE_GENAI_USE_ENTERPRISE'), - ); - }); - - it('should warn when falling back to a false GOOGLE_GENAI_USE_VERTEXAI', () => { - process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; - process.env['GOOGLE_API_KEY'] = 'env-api-key'; - const result = getExpressModeApiKey(); - expect(result).toBeUndefined(); - expect(warnSpy).toHaveBeenCalledOnce(); - }); - - it('should return undefined and not warn when neither enterprise mode variable is set', () => { process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBeUndefined(); From 1352ca7dca49ce2071d87be5557579f2bdd98c27 Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Thu, 30 Jul 2026 22:18:31 -0700 Subject: [PATCH 3/6] refactor: route every enterprise-mode env read through isEnterpriseModeEnabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewer feedback on the express-mode change: GOOGLE_GENAI_USE_VERTEXAI is read in several places, so resolving it in getExpressModeApiKey() alone left the rest of the repo on the deprecated variable — GOOGLE_GENAI_USE_ENTERPRISE=true would hand back an express-mode key while getGoogleLlmVariant() still reported GEMINI_API and geminiInitParams() still set vertexai=false. Hoist isEnterpriseModeEnabled() next to getBooleanEnvVar in env_aware_utils.ts and use it from every reader: vertex_ai_utils, variant_utils and google_llm. The adk create / cloud run scaffolding now writes GOOGLE_GENAI_USE_ENTERPRISE too, so generated projects no longer trip the deprecation warning; @google/genai ^2.9.0 resolves that variable with the same precedence, so the SDK's own detection still works. --- core/src/models/google_llm.ts | 4 +- core/src/utils/env_aware_utils.ts | 30 +++++++++ core/src/utils/variant_utils.ts | 4 +- core/src/utils/vertex_ai_utils.ts | 30 +-------- core/test/models/google_llm_test.ts | 23 +++++++ core/test/utils/env_aware_utils_test.ts | 77 ++++++++++++++++++++++- core/test/utils/variant_utils_test.ts | 29 +++++++-- dev/src/cli/cli_create.ts | 4 +- dev/src/cli/deploy/deploy_utils.ts | 2 +- dev/test/cli/cli_create_test.ts | 6 +- dev/test/cli/cli_deploy_cloud_run_test.ts | 6 ++ 11 files changed, 171 insertions(+), 44 deletions(-) diff --git a/core/src/models/google_llm.ts b/core/src/models/google_llm.ts index 0bc30049e0..5ae52c079e 100644 --- a/core/src/models/google_llm.ts +++ b/core/src/models/google_llm.ts @@ -13,7 +13,7 @@ import { LiveServerMessage, } from '@google/genai'; -import {getBooleanEnvVar, isBrowser} from '../utils/env_aware_utils.js'; +import {isBrowser, isEnterpriseModeEnabled} from '../utils/env_aware_utils.js'; import {logger} from '../utils/logger.js'; import {GoogleLLMVariant} from '../utils/variant_utils.js'; @@ -373,7 +373,7 @@ export function geminiInitParams({ params.vertexai = !!vertexai; if (!params.vertexai && !isBrowser()) { - params.vertexai = getBooleanEnvVar('GOOGLE_GENAI_USE_VERTEXAI'); + params.vertexai = isEnterpriseModeEnabled(); } if (params.vertexai) { diff --git a/core/src/utils/env_aware_utils.ts b/core/src/utils/env_aware_utils.ts index e2acc16e6a..36f7eb77a2 100644 --- a/core/src/utils/env_aware_utils.ts +++ b/core/src/utils/env_aware_utils.ts @@ -5,6 +5,10 @@ */ import {randomUUID as nodeRandomUUID} from 'node:crypto'; +import {logger} from './logger.js'; + +const ENTERPRISE_MODE_ENV_VAR = 'GOOGLE_GENAI_USE_ENTERPRISE'; +const DEPRECATED_ENTERPRISE_MODE_ENV_VAR = 'GOOGLE_GENAI_USE_VERTEXAI'; /** * Returns true if the environment is a browser. @@ -131,3 +135,29 @@ export function getBooleanEnvVar(envVar: string): boolean { return ['true', '1'].includes(envVarValue); } + +/** + * Returns whether Google GenAI enterprise mode is enabled. + * + * `GOOGLE_GENAI_USE_ENTERPRISE` takes precedence whenever it is set, even when + * it is set to a falsy value. `GOOGLE_GENAI_USE_VERTEXAI` is only consulted + * when `GOOGLE_GENAI_USE_ENTERPRISE` is absent, and using it logs a deprecation + * warning. + * + * @return True if enterprise mode is enabled, false otherwise. + */ +export function isEnterpriseModeEnabled(): boolean { + if (process.env?.[ENTERPRISE_MODE_ENV_VAR] !== undefined) { + return getBooleanEnvVar(ENTERPRISE_MODE_ENV_VAR); + } + + if (process.env?.[DEPRECATED_ENTERPRISE_MODE_ENV_VAR] !== undefined) { + logger.warn( + `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` + + `${ENTERPRISE_MODE_ENV_VAR} instead`, + ); + return getBooleanEnvVar(DEPRECATED_ENTERPRISE_MODE_ENV_VAR); + } + + return false; +} diff --git a/core/src/utils/variant_utils.ts b/core/src/utils/variant_utils.ts index ba3c085431..bc4c53fee8 100644 --- a/core/src/utils/variant_utils.ts +++ b/core/src/utils/variant_utils.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {getBooleanEnvVar} from './env_aware_utils.js'; +import {isEnterpriseModeEnabled} from './env_aware_utils.js'; /** * The Google LLM variant to use. @@ -27,7 +27,7 @@ export enum GoogleLLMVariant { * Gets the Google LLM variant to use. */ export function getGoogleLlmVariant() { - return getBooleanEnvVar('GOOGLE_GENAI_USE_VERTEXAI') + return isEnterpriseModeEnabled() ? GoogleLLMVariant.VERTEX_AI : GoogleLLMVariant.GEMINI_API; } diff --git a/core/src/utils/vertex_ai_utils.ts b/core/src/utils/vertex_ai_utils.ts index ddf1762167..930d8466d4 100644 --- a/core/src/utils/vertex_ai_utils.ts +++ b/core/src/utils/vertex_ai_utils.ts @@ -4,35 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {getBooleanEnvVar} from './env_aware_utils.js'; -import {logger} from './logger.js'; - -const ENTERPRISE_MODE_ENV_VAR = 'GOOGLE_GENAI_USE_ENTERPRISE'; -const DEPRECATED_ENTERPRISE_MODE_ENV_VAR = 'GOOGLE_GENAI_USE_VERTEXAI'; - -/** - * Returns whether Google GenAI enterprise mode is enabled. - * - * `GOOGLE_GENAI_USE_ENTERPRISE` takes precedence whenever it is set, even when - * it is set to a falsy value. `GOOGLE_GENAI_USE_VERTEXAI` is only consulted - * when `GOOGLE_GENAI_USE_ENTERPRISE` is absent, and using it logs a deprecation - * warning. - */ -function isEnterpriseModeEnabled(): boolean { - if (process.env?.[ENTERPRISE_MODE_ENV_VAR] !== undefined) { - return getBooleanEnvVar(ENTERPRISE_MODE_ENV_VAR); - } - - if (process.env?.[DEPRECATED_ENTERPRISE_MODE_ENV_VAR] !== undefined) { - logger.warn( - `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` + - `${ENTERPRISE_MODE_ENV_VAR} instead`, - ); - return getBooleanEnvVar(DEPRECATED_ENTERPRISE_MODE_ENV_VAR); - } - - return false; -} +import {isEnterpriseModeEnabled} from './env_aware_utils.js'; export const EXPRESS_MODE_UNSUPPORTED_MESSAGE = 'Vertex AI Express Mode (expressModeApiKey / GOOGLE_API_KEY) is not ' + diff --git a/core/test/models/google_llm_test.ts b/core/test/models/google_llm_test.ts index 6db026d567..ef0aa90c3e 100644 --- a/core/test/models/google_llm_test.ts +++ b/core/test/models/google_llm_test.ts @@ -64,6 +64,7 @@ describe('GoogleLlm', () => { delete process.env['GOOGLE_GENAI_API_KEY']; delete process.env['GEMINI_API_KEY']; delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; + delete process.env['GOOGLE_GENAI_USE_ENTERPRISE']; delete process.env['GOOGLE_CLOUD_AGENT_ENGINE_ID']; }; @@ -335,6 +336,28 @@ describe('GoogleLlm', () => { expect(params.vertexai).toBe(true); }); + it('should detect Vertex AI from GOOGLE_GENAI_USE_ENTERPRISE', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; + process.env['GOOGLE_CLOUD_PROJECT'] = 'env-project'; + process.env['GOOGLE_CLOUD_LOCATION'] = 'env-location'; + const input = { + model: 'gemini-1.5-flash', + }; + const params = geminiInitParams(input); + expect(params.vertexai).toBe(true); + }); + + it('should not use Vertex AI when GOOGLE_GENAI_USE_ENTERPRISE disables it', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'false'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + const input = { + model: 'gemini-1.5-flash', + apiKey: 'test-key', + }; + const params = geminiInitParams(input); + expect(params.vertexai).toBe(false); + }); + it('should throw error if project is missing for Vertex AI', () => { const input = { model: 'gemini-1.5-flash', diff --git a/core/test/utils/env_aware_utils_test.ts b/core/test/utils/env_aware_utils_test.ts index bcfdddfb06..93eade2fed 100644 --- a/core/test/utils/env_aware_utils_test.ts +++ b/core/test/utils/env_aware_utils_test.ts @@ -4,9 +4,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {afterEach, describe, expect, it} from 'vitest'; +import { + afterEach, + beforeEach, + describe, + expect, + it, + vi, + type MockInstance, +} from 'vitest'; import {randomUUID as shimRandomUUID} from '../../src/utils/crypto_shim.js'; -import {getBooleanEnvVar, randomUUID} from '../../src/utils/env_aware_utils.js'; +import { + getBooleanEnvVar, + isEnterpriseModeEnabled, + randomUUID, +} from '../../src/utils/env_aware_utils.js'; +import {logger} from '../../src/utils/logger.js'; describe('env_aware_utils', () => { describe('getBooleanEnvVar', () => { @@ -135,4 +148,64 @@ describe('env_aware_utils', () => { ); }); }); + + describe('isEnterpriseModeEnabled', () => { + const originalEnv = process.env; + let warnSpy: MockInstance; + + beforeEach(() => { + process.env = {...originalEnv}; + delete process.env['GOOGLE_GENAI_USE_ENTERPRISE']; + delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; + warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); + }); + + afterEach(() => { + process.env = originalEnv; + vi.restoreAllMocks(); + }); + + it('should return false and not warn when neither variable is set', () => { + expect(isEnterpriseModeEnabled()).toBe(false); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should return true when GOOGLE_GENAI_USE_ENTERPRISE is enabled', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; + expect(isEnterpriseModeEnabled()).toBe(true); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should prefer an enabled GOOGLE_GENAI_USE_ENTERPRISE over GOOGLE_GENAI_USE_VERTEXAI', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; + expect(isEnterpriseModeEnabled()).toBe(true); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should not fall back to GOOGLE_GENAI_USE_VERTEXAI when GOOGLE_GENAI_USE_ENTERPRISE is set but disabled', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = ''; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + expect(isEnterpriseModeEnabled()).toBe(false); + expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should fall back to an enabled GOOGLE_GENAI_USE_VERTEXAI and warn', () => { + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + expect(isEnterpriseModeEnabled()).toBe(true); + expect(warnSpy).toHaveBeenCalledOnce(); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'GOOGLE_GENAI_USE_VERTEXAI is deprecated, please use ' + + 'GOOGLE_GENAI_USE_ENTERPRISE', + ), + ); + }); + + it('should warn when GOOGLE_GENAI_USE_VERTEXAI is present but disabled', () => { + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; + expect(isEnterpriseModeEnabled()).toBe(false); + expect(warnSpy).toHaveBeenCalledOnce(); + }); + }); }); diff --git a/core/test/utils/variant_utils_test.ts b/core/test/utils/variant_utils_test.ts index 4f949e4334..a7915ff625 100644 --- a/core/test/utils/variant_utils_test.ts +++ b/core/test/utils/variant_utils_test.ts @@ -5,34 +5,53 @@ */ import {GoogleLLMVariant} from '@google/adk'; -import {afterEach, describe, expect, it} from 'vitest'; +import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'; +import {logger} from '../../src/utils/logger.js'; import {getGoogleLlmVariant} from '../../src/utils/variant_utils.js'; describe('variant_utils', () => { describe('getGoogleLlmVariant', () => { const originalEnv = process.env; + beforeEach(() => { + process.env = {...originalEnv}; + delete process.env['GOOGLE_GENAI_USE_ENTERPRISE']; + delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; + vi.spyOn(logger, 'warn').mockImplementation(() => {}); + }); + afterEach(() => { process.env = originalEnv; + vi.restoreAllMocks(); }); it('should return GEMINI_API by default (when env var is not set)', () => { - delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; expect(getGoogleLlmVariant()).toBe(GoogleLLMVariant.GEMINI_API); }); it('should return VERTEX_AI when GOOGLE_GENAI_USE_VERTEXAI is "true"', () => { - process.env = {...originalEnv, 'GOOGLE_GENAI_USE_VERTEXAI': 'true'}; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; expect(getGoogleLlmVariant()).toBe(GoogleLLMVariant.VERTEX_AI); }); it('should return VERTEX_AI when GOOGLE_GENAI_USE_VERTEXAI is "1"', () => { - process.env = {...originalEnv, 'GOOGLE_GENAI_USE_VERTEXAI': '1'}; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = '1'; expect(getGoogleLlmVariant()).toBe(GoogleLLMVariant.VERTEX_AI); }); it('should return GEMINI_API when GOOGLE_GENAI_USE_VERTEXAI is "false"', () => { - process.env = {...originalEnv, 'GOOGLE_GENAI_USE_VERTEXAI': 'false'}; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; + expect(getGoogleLlmVariant()).toBe(GoogleLLMVariant.GEMINI_API); + }); + + it('should return VERTEX_AI when GOOGLE_GENAI_USE_ENTERPRISE is "true"', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; + expect(getGoogleLlmVariant()).toBe(GoogleLLMVariant.VERTEX_AI); + }); + + it('should return GEMINI_API when GOOGLE_GENAI_USE_ENTERPRISE is "false" and GOOGLE_GENAI_USE_VERTEXAI is "true"', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'false'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; expect(getGoogleLlmVariant()).toBe(GoogleLLMVariant.GEMINI_API); }); }); diff --git a/dev/src/cli/cli_create.ts b/dev/src/cli/cli_create.ts index 86f985c40f..fbab848056 100644 --- a/dev/src/cli/cli_create.ts +++ b/dev/src/cli/cli_create.ts @@ -168,7 +168,7 @@ function generateEnvFile(options: AgentCreationOptions): string { // core). GOOGLE_API_KEY is only consulted for Vertex AI Express Mode, so // writing it here left every scaffolded project unable to authenticate. lines.push(`GOOGLE_GENAI_API_KEY=${options.apiKey}`); - lines.push(`GOOGLE_GENAI_USE_VERTEXAI=0`); + lines.push(`GOOGLE_GENAI_USE_ENTERPRISE=0`); } if (options.project) { lines.push(`GOOGLE_CLOUD_PROJECT=${options.project}`); @@ -177,7 +177,7 @@ function generateEnvFile(options: AgentCreationOptions): string { lines.push(`GOOGLE_CLOUD_LOCATION=${options.region}`); } if (options.region && options.project) { - lines.push(`GOOGLE_GENAI_USE_VERTEXAI=1`); + lines.push(`GOOGLE_GENAI_USE_ENTERPRISE=1`); } return lines.join('\n'); } diff --git a/dev/src/cli/deploy/deploy_utils.ts b/dev/src/cli/deploy/deploy_utils.ts index ff405d5a92..d29e36cf91 100644 --- a/dev/src/cli/deploy/deploy_utils.ts +++ b/dev/src/cli/deploy/deploy_utils.ts @@ -157,7 +157,7 @@ USER myuser # Set up environment variables - Start ENV PATH="/home/myuser/.local/bin:$PATH" -ENV GOOGLE_GENAI_USE_VERTEXAI=1 +ENV GOOGLE_GENAI_USE_ENTERPRISE=1 ENV GOOGLE_CLOUD_PROJECT=${options.project} ENV GOOGLE_CLOUD_LOCATION=${options.region} # Set up environment variables - End diff --git a/dev/test/cli/cli_create_test.ts b/dev/test/cli/cli_create_test.ts index 867f38e841..ffec9ddee7 100644 --- a/dev/test/cli/cli_create_test.ts +++ b/dev/test/cli/cli_create_test.ts @@ -137,7 +137,7 @@ describe('createAgent', () => { ); expect(saveToFile).toHaveBeenCalledWith( expect.stringContaining('.env'), - expect.stringContaining('GOOGLE_GENAI_USE_VERTEXAI=1'), + expect.stringContaining('GOOGLE_GENAI_USE_ENTERPRISE=1'), ); }); @@ -152,6 +152,10 @@ describe('createAgent', () => { expect.stringContaining('.env'), expect.stringContaining('GOOGLE_GENAI_API_KEY=my-api-key'), ); + expect(saveToFile).toHaveBeenCalledWith( + expect.stringContaining('.env'), + expect.stringContaining('GOOGLE_GENAI_USE_ENTERPRISE=0'), + ); }); }); diff --git a/dev/test/cli/cli_deploy_cloud_run_test.ts b/dev/test/cli/cli_deploy_cloud_run_test.ts index e24ff4bc32..0297ca3609 100644 --- a/dev/test/cli/cli_deploy_cloud_run_test.ts +++ b/dev/test/cli/cli_deploy_cloud_run_test.ts @@ -81,6 +81,12 @@ describe('createDockerFileContent', () => { expect(content).not.toContain('--a2a'); }); + it('should enable enterprise mode with the non-deprecated env var', () => { + const content = createDockerFileContent(defaultOptions); + expect(content).toContain('ENV GOOGLE_GENAI_USE_ENTERPRISE=1'); + expect(content).not.toContain('GOOGLE_GENAI_USE_VERTEXAI'); + }); + it('should create Dockerfile content with --a2a when option is true', () => { const content = createDockerFileContent({ ...defaultOptions, From 8664978bef20d092242548ab1d6c1c2ec12eb134 Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Thu, 30 Jul 2026 22:30:51 -0700 Subject: [PATCH 4/6] refactor: keep the enterprise-mode sweep to env readers Revert the adk create / cloud run scaffolding to writing GOOGLE_GENAI_USE_VERTEXAI. Those two sites emit the variable rather than read it, and the generated Dockerfile installs the dev server fresh while taking @google/adk from the user's copied node_modules, so a container can run a core that only understands the legacy name. Since geminiInitParams forwards the resolved value to the SDK as an explicit vertexai flag, the SDK's own GOOGLE_GENAI_USE_ENTERPRISE support cannot cover for it, and the deploy would fail at startup. Also clear GOOGLE_GENAI_USE_ENTERPRISE in the apigee suite, which drives the same env branch, and drop the deprecation-warning assertions from the getExpressModeApiKey tests now that the helper owns that behavior and tests it directly. --- core/test/models/apigee_llm_test.ts | 1 + core/test/utils/vertex_ai_utils_test.ts | 26 +++-------------------- dev/src/cli/cli_create.ts | 4 ++-- dev/src/cli/deploy/deploy_utils.ts | 2 +- dev/test/cli/cli_create_test.ts | 6 +----- dev/test/cli/cli_deploy_cloud_run_test.ts | 6 ------ 6 files changed, 8 insertions(+), 37 deletions(-) diff --git a/core/test/models/apigee_llm_test.ts b/core/test/models/apigee_llm_test.ts index 4a88d0324b..94b67cfc2e 100644 --- a/core/test/models/apigee_llm_test.ts +++ b/core/test/models/apigee_llm_test.ts @@ -25,6 +25,7 @@ describe('ApigeeLlm', () => { delete process.env['APIGEE_PROXY_URL']; delete process.env['GOOGLE_GENAI_API_KEY']; delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; + delete process.env['GOOGLE_GENAI_USE_ENTERPRISE']; delete process.env['GOOGLE_CLOUD_PROJECT']; delete process.env['GOOGLE_CLOUD_LOCATION']; }); diff --git a/core/test/utils/vertex_ai_utils_test.ts b/core/test/utils/vertex_ai_utils_test.ts index fb5969812e..287d2fbebd 100644 --- a/core/test/utils/vertex_ai_utils_test.ts +++ b/core/test/utils/vertex_ai_utils_test.ts @@ -4,29 +4,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - afterEach, - beforeEach, - describe, - expect, - it, - vi, - type MockInstance, -} from 'vitest'; +import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'; import {logger} from '../../src/utils/logger.js'; import {getExpressModeApiKey} from '../../src/utils/vertex_ai_utils.js'; describe('vertex_ai_utils', () => { describe('getExpressModeApiKey', () => { const originalEnv = process.env; - let warnSpy: MockInstance; beforeEach(() => { process.env = {...originalEnv}; delete process.env['GOOGLE_GENAI_USE_ENTERPRISE']; delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; delete process.env['GOOGLE_API_KEY']; - warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); + vi.spyOn(logger, 'warn').mockImplementation(() => {}); }); afterEach(() => { @@ -52,11 +43,10 @@ describe('vertex_ai_utils', () => { ).toThrow(); }); - it('should return undefined and not warn when neither enterprise mode variable is set', () => { + it('should return undefined when neither enterprise mode variable is set', () => { process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBeUndefined(); - expect(warnSpy).not.toHaveBeenCalled(); }); it('should return undefined when GOOGLE_GENAI_USE_VERTEXAI is false', () => { @@ -64,7 +54,6 @@ describe('vertex_ai_utils', () => { process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBeUndefined(); - expect(warnSpy).toHaveBeenCalledOnce(); }); it('should return expressModeApiKey when GOOGLE_GENAI_USE_VERTEXAI is true', () => { @@ -78,13 +67,6 @@ describe('vertex_ai_utils', () => { process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBe('env-api-key'); - expect(warnSpy).toHaveBeenCalledOnce(); - expect(warnSpy).toHaveBeenCalledWith( - expect.stringContaining( - 'GOOGLE_GENAI_USE_VERTEXAI is deprecated, please use ' + - 'GOOGLE_GENAI_USE_ENTERPRISE', - ), - ); }); it('should return undefined when GOOGLE_GENAI_USE_VERTEXAI is true but no key available', () => { @@ -105,7 +87,6 @@ describe('vertex_ai_utils', () => { process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBe('env-api-key'); - expect(warnSpy).not.toHaveBeenCalled(); }); it('should not fall back to GOOGLE_GENAI_USE_VERTEXAI when GOOGLE_GENAI_USE_ENTERPRISE is set but disabled', () => { @@ -114,7 +95,6 @@ describe('vertex_ai_utils', () => { process.env['GOOGLE_API_KEY'] = 'env-api-key'; const result = getExpressModeApiKey(); expect(result).toBeUndefined(); - expect(warnSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/dev/src/cli/cli_create.ts b/dev/src/cli/cli_create.ts index fbab848056..86f985c40f 100644 --- a/dev/src/cli/cli_create.ts +++ b/dev/src/cli/cli_create.ts @@ -168,7 +168,7 @@ function generateEnvFile(options: AgentCreationOptions): string { // core). GOOGLE_API_KEY is only consulted for Vertex AI Express Mode, so // writing it here left every scaffolded project unable to authenticate. lines.push(`GOOGLE_GENAI_API_KEY=${options.apiKey}`); - lines.push(`GOOGLE_GENAI_USE_ENTERPRISE=0`); + lines.push(`GOOGLE_GENAI_USE_VERTEXAI=0`); } if (options.project) { lines.push(`GOOGLE_CLOUD_PROJECT=${options.project}`); @@ -177,7 +177,7 @@ function generateEnvFile(options: AgentCreationOptions): string { lines.push(`GOOGLE_CLOUD_LOCATION=${options.region}`); } if (options.region && options.project) { - lines.push(`GOOGLE_GENAI_USE_ENTERPRISE=1`); + lines.push(`GOOGLE_GENAI_USE_VERTEXAI=1`); } return lines.join('\n'); } diff --git a/dev/src/cli/deploy/deploy_utils.ts b/dev/src/cli/deploy/deploy_utils.ts index d29e36cf91..ff405d5a92 100644 --- a/dev/src/cli/deploy/deploy_utils.ts +++ b/dev/src/cli/deploy/deploy_utils.ts @@ -157,7 +157,7 @@ USER myuser # Set up environment variables - Start ENV PATH="/home/myuser/.local/bin:$PATH" -ENV GOOGLE_GENAI_USE_ENTERPRISE=1 +ENV GOOGLE_GENAI_USE_VERTEXAI=1 ENV GOOGLE_CLOUD_PROJECT=${options.project} ENV GOOGLE_CLOUD_LOCATION=${options.region} # Set up environment variables - End diff --git a/dev/test/cli/cli_create_test.ts b/dev/test/cli/cli_create_test.ts index ffec9ddee7..867f38e841 100644 --- a/dev/test/cli/cli_create_test.ts +++ b/dev/test/cli/cli_create_test.ts @@ -137,7 +137,7 @@ describe('createAgent', () => { ); expect(saveToFile).toHaveBeenCalledWith( expect.stringContaining('.env'), - expect.stringContaining('GOOGLE_GENAI_USE_ENTERPRISE=1'), + expect.stringContaining('GOOGLE_GENAI_USE_VERTEXAI=1'), ); }); @@ -152,10 +152,6 @@ describe('createAgent', () => { expect.stringContaining('.env'), expect.stringContaining('GOOGLE_GENAI_API_KEY=my-api-key'), ); - expect(saveToFile).toHaveBeenCalledWith( - expect.stringContaining('.env'), - expect.stringContaining('GOOGLE_GENAI_USE_ENTERPRISE=0'), - ); }); }); diff --git a/dev/test/cli/cli_deploy_cloud_run_test.ts b/dev/test/cli/cli_deploy_cloud_run_test.ts index 0297ca3609..e24ff4bc32 100644 --- a/dev/test/cli/cli_deploy_cloud_run_test.ts +++ b/dev/test/cli/cli_deploy_cloud_run_test.ts @@ -81,12 +81,6 @@ describe('createDockerFileContent', () => { expect(content).not.toContain('--a2a'); }); - it('should enable enterprise mode with the non-deprecated env var', () => { - const content = createDockerFileContent(defaultOptions); - expect(content).toContain('ENV GOOGLE_GENAI_USE_ENTERPRISE=1'); - expect(content).not.toContain('GOOGLE_GENAI_USE_VERTEXAI'); - }); - it('should create Dockerfile content with --a2a when option is true', () => { const content = createDockerFileContent({ ...defaultOptions, From 3aa940ebcadfe1b51646c83e23dc710eade1e7a8 Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Thu, 30 Jul 2026 22:38:42 -0700 Subject: [PATCH 5/6] fix(utils): log the enterprise-mode deprecation warning only once getGoogleLlmVariant() is read per request (BaseTool.apiVariant), so routing it through isEnterpriseModeEnabled turned the deprecation notice into a log line per tool per request for everyone still on GOOGLE_GENAI_USE_VERTEXAI. Guard it with a module-level flag, matching Python's warnings.warn, which the default filter emits once per location. The helper's tests reload the module (and the logger it warns to) per test so the flag cannot leak between them. --- core/src/utils/env_aware_utils.ts | 15 +++++++++----- core/test/utils/env_aware_utils_test.ts | 26 +++++++++++++++++-------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/core/src/utils/env_aware_utils.ts b/core/src/utils/env_aware_utils.ts index 36f7eb77a2..4828c31665 100644 --- a/core/src/utils/env_aware_utils.ts +++ b/core/src/utils/env_aware_utils.ts @@ -136,13 +136,15 @@ export function getBooleanEnvVar(envVar: string): boolean { return ['true', '1'].includes(envVarValue); } +let warnedDeprecatedEnterpriseModeEnvVar = false; + /** * Returns whether Google GenAI enterprise mode is enabled. * * `GOOGLE_GENAI_USE_ENTERPRISE` takes precedence whenever it is set, even when * it is set to a falsy value. `GOOGLE_GENAI_USE_VERTEXAI` is only consulted * when `GOOGLE_GENAI_USE_ENTERPRISE` is absent, and using it logs a deprecation - * warning. + * warning. This is read per request, so the warning is logged only once. * * @return True if enterprise mode is enabled, false otherwise. */ @@ -152,10 +154,13 @@ export function isEnterpriseModeEnabled(): boolean { } if (process.env?.[DEPRECATED_ENTERPRISE_MODE_ENV_VAR] !== undefined) { - logger.warn( - `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` + - `${ENTERPRISE_MODE_ENV_VAR} instead`, - ); + if (!warnedDeprecatedEnterpriseModeEnvVar) { + warnedDeprecatedEnterpriseModeEnvVar = true; + logger.warn( + `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` + + `${ENTERPRISE_MODE_ENV_VAR} instead`, + ); + } return getBooleanEnvVar(DEPRECATED_ENTERPRISE_MODE_ENV_VAR); } diff --git a/core/test/utils/env_aware_utils_test.ts b/core/test/utils/env_aware_utils_test.ts index 93eade2fed..eb36fe3883 100644 --- a/core/test/utils/env_aware_utils_test.ts +++ b/core/test/utils/env_aware_utils_test.ts @@ -14,12 +14,8 @@ import { type MockInstance, } from 'vitest'; import {randomUUID as shimRandomUUID} from '../../src/utils/crypto_shim.js'; -import { - getBooleanEnvVar, - isEnterpriseModeEnabled, - randomUUID, -} from '../../src/utils/env_aware_utils.js'; -import {logger} from '../../src/utils/logger.js'; +import {getBooleanEnvVar, randomUUID} from '../../src/utils/env_aware_utils.js'; +import type {Logger} from '../../src/utils/logger.js'; describe('env_aware_utils', () => { describe('getBooleanEnvVar', () => { @@ -151,13 +147,20 @@ describe('env_aware_utils', () => { describe('isEnterpriseModeEnabled', () => { const originalEnv = process.env; - let warnSpy: MockInstance; + let isEnterpriseModeEnabled: () => boolean; + let warnSpy: MockInstance; - beforeEach(() => { + beforeEach(async () => { process.env = {...originalEnv}; delete process.env['GOOGLE_GENAI_USE_ENTERPRISE']; delete process.env['GOOGLE_GENAI_USE_VERTEXAI']; + // The deprecation warning is emitted once per module instance, so each + // test needs its own copy of the module and of the logger it warns to. + vi.resetModules(); + const {logger} = await import('../../src/utils/logger.js'); warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); + ({isEnterpriseModeEnabled} = + await import('../../src/utils/env_aware_utils.js')); }); afterEach(() => { @@ -207,5 +210,12 @@ describe('env_aware_utils', () => { expect(isEnterpriseModeEnabled()).toBe(false); expect(warnSpy).toHaveBeenCalledOnce(); }); + + it('should warn only once even when read repeatedly', () => { + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + expect(isEnterpriseModeEnabled()).toBe(true); + expect(isEnterpriseModeEnabled()).toBe(true); + expect(warnSpy).toHaveBeenCalledOnce(); + }); }); }); From 1d555373bd8724dfc6c82d6c653a622140ffbe8e Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Thu, 13 Aug 2026 16:05:14 -0700 Subject: [PATCH 6/6] feat(utils): warn when GOOGLE_GENAI_USE_VERTEXAI is set but ignored When both GOOGLE_GENAI_USE_ENTERPRISE and the deprecated GOOGLE_GENAI_USE_VERTEXAI are set, GOOGLE_GENAI_USE_ENTERPRISE takes precedence. Previously the deprecated variable was silently ignored in that case, so a stale value produced no signal. Now the deprecation warning also fires when the variable is overridden, stating that it is ignored (still logged once per process). Two precedence tests added in this PR asserted no warning for the both-set case; that assertion encoded the old behaviour and is removed. A new test covers the override warning. Addresses review feedback from ScottMansfield. --- core/src/utils/env_aware_utils.ts | 41 +++++++++++++++++++------ core/test/utils/env_aware_utils_test.ts | 15 +++++++-- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/core/src/utils/env_aware_utils.ts b/core/src/utils/env_aware_utils.ts index 4828c31665..600f7e19ac 100644 --- a/core/src/utils/env_aware_utils.ts +++ b/core/src/utils/env_aware_utils.ts @@ -138,29 +138,50 @@ export function getBooleanEnvVar(envVar: string): boolean { let warnedDeprecatedEnterpriseModeEnvVar = false; +function warnDeprecatedEnterpriseModeEnvVar(message: string): void { + if (warnedDeprecatedEnterpriseModeEnvVar) { + return; + } + warnedDeprecatedEnterpriseModeEnvVar = true; + logger.warn(message); +} + /** * Returns whether Google GenAI enterprise mode is enabled. * * `GOOGLE_GENAI_USE_ENTERPRISE` takes precedence whenever it is set, even when * it is set to a falsy value. `GOOGLE_GENAI_USE_VERTEXAI` is only consulted - * when `GOOGLE_GENAI_USE_ENTERPRISE` is absent, and using it logs a deprecation - * warning. This is read per request, so the warning is logged only once. + * when `GOOGLE_GENAI_USE_ENTERPRISE` is absent. + * + * Setting the deprecated `GOOGLE_GENAI_USE_VERTEXAI` logs a deprecation + * warning, whether it is used or overridden. When both variables are set the + * warning also states that `GOOGLE_GENAI_USE_VERTEXAI` is ignored, so a stale + * value silently overridden by the new variable is still surfaced. This is + * read per request, so the warning is logged only once. * * @return True if enterprise mode is enabled, false otherwise. */ export function isEnterpriseModeEnabled(): boolean { + const deprecatedIsSet = + process.env?.[DEPRECATED_ENTERPRISE_MODE_ENV_VAR] !== undefined; + if (process.env?.[ENTERPRISE_MODE_ENV_VAR] !== undefined) { + if (deprecatedIsSet) { + warnDeprecatedEnterpriseModeEnvVar( + `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is set but ignored because ` + + `${ENTERPRISE_MODE_ENV_VAR} takes precedence. ` + + `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` + + `${ENTERPRISE_MODE_ENV_VAR} instead.`, + ); + } return getBooleanEnvVar(ENTERPRISE_MODE_ENV_VAR); } - if (process.env?.[DEPRECATED_ENTERPRISE_MODE_ENV_VAR] !== undefined) { - if (!warnedDeprecatedEnterpriseModeEnvVar) { - warnedDeprecatedEnterpriseModeEnvVar = true; - logger.warn( - `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` + - `${ENTERPRISE_MODE_ENV_VAR} instead`, - ); - } + if (deprecatedIsSet) { + warnDeprecatedEnterpriseModeEnvVar( + `${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` + + `${ENTERPRISE_MODE_ENV_VAR} instead`, + ); return getBooleanEnvVar(DEPRECATED_ENTERPRISE_MODE_ENV_VAR); } diff --git a/core/test/utils/env_aware_utils_test.ts b/core/test/utils/env_aware_utils_test.ts index eb36fe3883..4d6c7a6eab 100644 --- a/core/test/utils/env_aware_utils_test.ts +++ b/core/test/utils/env_aware_utils_test.ts @@ -183,14 +183,25 @@ describe('env_aware_utils', () => { process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'false'; expect(isEnterpriseModeEnabled()).toBe(true); - expect(warnSpy).not.toHaveBeenCalled(); }); it('should not fall back to GOOGLE_GENAI_USE_VERTEXAI when GOOGLE_GENAI_USE_ENTERPRISE is set but disabled', () => { process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = ''; process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; expect(isEnterpriseModeEnabled()).toBe(false); - expect(warnSpy).not.toHaveBeenCalled(); + }); + + it('should warn that GOOGLE_GENAI_USE_VERTEXAI is ignored when both variables are set', () => { + process.env['GOOGLE_GENAI_USE_ENTERPRISE'] = 'true'; + process.env['GOOGLE_GENAI_USE_VERTEXAI'] = 'true'; + expect(isEnterpriseModeEnabled()).toBe(true); + expect(warnSpy).toHaveBeenCalledOnce(); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'GOOGLE_GENAI_USE_VERTEXAI is set but ignored because ' + + 'GOOGLE_GENAI_USE_ENTERPRISE takes precedence', + ), + ); }); it('should fall back to an enabled GOOGLE_GENAI_USE_VERTEXAI and warn', () => {