Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/src/models/google_llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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) {
Expand Down
56 changes: 56 additions & 0 deletions core/src/utils/env_aware_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -131,3 +135,55 @@ export function getBooleanEnvVar(envVar: string): boolean {

return ['true', '1'].includes(envVarValue);
}

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.
*
* 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 (deprecatedIsSet) {
warnDeprecatedEnterpriseModeEnvVar(
`${DEPRECATED_ENTERPRISE_MODE_ENV_VAR} is deprecated, please use ` +
`${ENTERPRISE_MODE_ENV_VAR} instead`,
);
return getBooleanEnvVar(DEPRECATED_ENTERPRISE_MODE_ENV_VAR);
}

return false;
}
4 changes: 2 additions & 2 deletions core/src/utils/variant_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
}
8 changes: 6 additions & 2 deletions core/src/utils/vertex_ai_utils.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GOOGLE_GENAI_USE_VERTEXAI used all over the code source, please update all the references to use isEnterpriseModeEnabled instead

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — every read of GOOGLE_GENAI_USE_VERTEXAI now goes through isEnterpriseModeEnabled.

I moved the helper out of vertex_ai_utils.ts and exported it from core/src/utils/env_aware_utils.ts, right next to getBooleanEnvVar (this mirrors adk-python, where is_enterprise_mode_enabled and is_env_enabled both live in utils/env_utils.py). Converted call sites:

  • core/src/utils/vertex_ai_utils.ts — getExpressModeApiKey()
  • core/src/utils/variant_utils.ts — getGoogleLlmVariant()
  • core/src/models/google_llm.ts — geminiInitParams(), which ApigeeLlm also routes through

No GOOGLE_GENAI_USE_VERTEXAI read remains outside the helper. You were right that the first revision was incoherent: with only getExpressModeApiKey converted, GOOGLE_GENAI_USE_ENTERPRISE=true returned an express-mode key while getGoogleLlmVariant() still reported GEMINI_API and geminiInitParams() still set vertexai: false.

Two things I did differently than a literal sweep — please push back if you disagree:

  1. The deprecation warning is now emitted once per process. getGoogleLlmVariant() is reached from BaseTool.apiVariant on every request, so warning on each read would print a line per tool per request for everyone still on the legacy variable. Python's warnings.warn(..., DeprecationWarning) is deduplicated by the default filter, so once-per-process is the parity behaviour; it costs one module-level flag, and the helper's tests reload the module (and its logger) per test so the flag cannot leak between them.

  2. I did not touch the two places that write the variable — dev/src/cli/cli_create.ts (the .env from adk create) and dev/src/cli/deploy/deploy_utils.ts (ENV GOOGLE_GENAI_USE_VERTEXAI=1 in the generated Dockerfile). They emit rather than read, so they can't use the helper, and renaming what they emit is not backwards compatible: the generated image installs @google/adk-devtools@latest but takes @google/adk from the user's copied package.json/node_modules, so the container can run a core that only understands the old name — and because geminiInitParams forwards the resolved value to the SDK as an explicit vertexai flag, the SDK's own GOOGLE_GENAI_USE_ENTERPRISE support can't cover for it. adk-python does emit the new name from its scaffolding, so I'm happy to follow suit — either in this PR, or as a follow-up once a release that reads the new variable is out, or by emitting both names. Your call.

Tests: new cases per call site in env_aware_utils_test.ts, variant_utils_test.ts, google_llm_test.ts and vertex_ai_utils_test.ts (13 in total, each verified to fail against the unmodified core/src), including warn-once on repeated reads. The four suites that touch these variables now also clear GOOGLE_GENAI_USE_ENTERPRISE in setup — since precedence is by presence, an ambient value of any kind would otherwise flip them.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import {getBooleanEnvVar} from './env_aware_utils.js';
import {isEnterpriseModeEnabled} from './env_aware_utils.js';

export const EXPRESS_MODE_UNSUPPORTED_MESSAGE =
'Vertex AI Express Mode (expressModeApiKey / GOOGLE_API_KEY) is not ' +
Expand All @@ -15,6 +15,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.
Expand All @@ -32,7 +36,7 @@ export function getExpressModeApiKey(
);
}

if (getBooleanEnvVar('GOOGLE_GENAI_USE_VERTEXAI')) {
if (isEnterpriseModeEnabled()) {
return expressModeApiKey || process.env.GOOGLE_API_KEY;
}

Expand Down
1 change: 1 addition & 0 deletions core/test/models/apigee_llm_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
});
Expand Down
23 changes: 23 additions & 0 deletions core/test/models/google_llm_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
};

Expand Down Expand Up @@ -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',
Expand Down
96 changes: 95 additions & 1 deletion core/test/utils/env_aware_utils_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@
* 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 type {Logger} from '../../src/utils/logger.js';

describe('env_aware_utils', () => {
describe('getBooleanEnvVar', () => {
Expand Down Expand Up @@ -135,4 +144,89 @@ describe('env_aware_utils', () => {
);
});
});

describe('isEnterpriseModeEnabled', () => {
const originalEnv = process.env;
let isEnterpriseModeEnabled: () => boolean;
let warnSpy: MockInstance<Logger['warn']>;

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(() => {
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);
});

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);
});

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', () => {
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();
});

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();
});
});
});
29 changes: 24 additions & 5 deletions core/test/utils/variant_utils_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
Expand Down
Loading
Loading