Feat: Honor GOOGLE_GENAI_USE_ENTERPRISE in getExpressModeApiKey() (adk-python parity) - #569
Conversation
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.
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.
There was a problem hiding this comment.
GOOGLE_GENAI_USE_VERTEXAI used all over the code source, please update all the references to use isEnterpriseModeEnabled instead
There was a problem hiding this comment.
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(), whichApigeeLlmalso 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:
-
The deprecation warning is now emitted once per process.
getGoogleLlmVariant()is reached fromBaseTool.apiVarianton every request, so warning on each read would print a line per tool per request for everyone still on the legacy variable. Python'swarnings.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. -
I did not touch the two places that write the variable —
dev/src/cli/cli_create.ts(the.envfromadk create) anddev/src/cli/deploy/deploy_utils.ts(ENV GOOGLE_GENAI_USE_VERTEXAI=1in 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@latestbut takes@google/adkfrom the user's copiedpackage.json/node_modules, so the container can run a core that only understands the old name — and becausegeminiInitParamsforwards the resolved value to the SDK as an explicitvertexaiflag, the SDK's ownGOOGLE_GENAI_USE_ENTERPRISEsupport 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.
…deEnabled 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.
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.
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.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
N/A — no existing issue.
Problem: ADK Python has moved the "am I running against the enterprise / Vertex AI surface?" decision off
GOOGLE_GENAI_USE_VERTEXAIand ontoGOOGLE_GENAI_USE_ENTERPRISE, keeping the old variable alive only as a deprecated fallback (src/google/adk/utils/env_utils.py::is_enterprise_mode_enabled()). ADK JS had no awareness ofGOOGLE_GENAI_USE_ENTERPRISEat all: anyone who had already migrated their environment gotundefinedout ofgetExpressModeApiKey()and then hitEither (Project ID and Location) or an expressModeApiKey is required.fromVertexAiSessionService/VertexAiMemoryBankService, with nothing pointing at the variable name as the cause.Solution: Add
isEnterpriseModeEnabled()next togetBooleanEnvVarincore/src/utils/env_aware_utils.ts, resolving the surface with the same presence-ordered algorithm as Python:GOOGLE_GENAI_USE_ENTERPRISEis present, its boolean value decides —GOOGLE_GENAI_USE_VERTEXAIis never consulted and nothing is logged.GOOGLE_GENAI_USE_VERTEXAIis present, log a deprecation warning and use its boolean value.Precedence is by presence, not truthiness:
GOOGLE_GENAI_USE_ENTERPRISE=falsemeans off and must not fall through to a staleGOOGLE_GENAI_USE_VERTEXAI=true. This mirrors Python'sif 'GOOGLE_GENAI_USE_ENTERPRISE' in os.environ.Every place that reads the variable now goes through that helper (review feedback). The first revision converted only
getExpressModeApiKey, which left the repo inconsistent:GOOGLE_GENAI_USE_ENTERPRISE=trueproduced an express-mode key whilegetGoogleLlmVariant()still reportedGEMINI_APIandgeminiInitParams()still setvertexai: false. 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(), whichApigeeLlmalso routes throughAfter this change no
GOOGLE_GENAI_USE_VERTEXAIread remains outside the helper.Notes on the details:
getGoogleLlmVariant()is reached fromBaseTool.apiVarianton every request, so warning unconditionally would emit a line per tool per request for everyone still on the legacy variable. Python'swarnings.warn(..., DeprecationWarning)is deduplicated by the default filter, so once-per-process is the parity behaviour rather than a shortcut. It costs one module-level flag; the helper's tests reload the module (and the logger it warns to) per test so the flag cannot leak between them.dev/src/cli/cli_create.ts(the.envgenerated byadk create) anddev/src/cli/deploy/deploy_utils.ts(ENV GOOGLE_GENAI_USE_VERTEXAI=1in the generated Dockerfile). They emit rather than read, so they cannot use the helper, and renaming what they emit is not backwards compatible: the generated image installs@google/adk-devtools@latestbut takes@google/adkfrom the user's copiedpackage.json/node_modules, so the container can run a core that only understands the legacy name — and sincegeminiInitParamsforwards the resolved value to the SDK as an explicitvertexaiflag, the SDK's ownGOOGLE_GENAI_USE_ENTERPRISEsupport cannot cover for it. Both keep working unchanged (they take the deprecated path). ADK Python does emit the new name from its scaffolding; happy to follow suit here in a separate change once a release that reads the new variable is out, or to emit both names — let me know which you prefer.GOOGLE_GENAI_USE_VERTEXAIstill selects the enterprise surface everywhere it did before, it just also logs one deprecation warning. The single intentional behaviour change is that an environment setting both variables to conflicting values now resolves toGOOGLE_GENAI_USE_ENTERPRISE— the same precedence@google/genai(^2.9.0) applies internally.isEnterpriseModeEnabledis package-internal, likegetBooleanEnvVar), and no new dependency.Testing Plan
Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.
Unit Tests:
[x] I have added or updated unit tests for my change.
[x] All unit tests pass locally.
New coverage, per call site:
core/test/utils/env_aware_utils_test.ts— the helper itself: neither variable set, the new variable enabled, both precedence directions, set-but-empty (proves precedence is by presence, not truthiness), the deprecated fallback when enabled and when disabled (both warn), and warn-once across repeated reads.core/test/utils/variant_utils_test.ts—getGoogleLlmVariant()honours the new variable, and a disabled new variable beats an enabled legacy one.core/test/models/google_llm_test.ts— the same two cases throughgeminiInitParams().core/test/utils/vertex_ai_utils_test.ts— the same two cases throughgetExpressModeApiKey(), plus the set-but-empty case.All 13 new cases fail against the unmodified
core/srcand pass with it (verified by revertingcore/srcand re-running). New lines and branches inenv_aware_utils.tsare at 100% statements / branches / functions / lines.Pre-existing tests were all kept, and edited only where this change makes them non-deterministic or where they were vacuous — disclosed explicitly:
variant_utils_test.ts,vertex_ai_utils_test.ts,google_llm_test.tsandapigee_llm_test.tsnow clearGOOGLE_GENAI_USE_ENTERPRISEin setup/teardown. Without that, an ambient value of the new variable — including''orfalse— silently flips those suites, because precedence is by presence.variant_utils_test.tsalso moves from replacingprocess.envwholesale to per-key assignment for the same reason; no assertion changed.vertex_ai_utils_test.tscases gained aGOOGLE_API_KEYvalue, which strengthens assertions that previously passed for the wrong reason (they returnedundefinedbecause no key existed, not because the surface was off).Manual End-to-End (E2E) Tests:
Please provide instructions on how to manually test your changes, including any necessary setup or configuration.
The helper does no I/O, so the unit tests drive it with real environment variables and no mocks. To observe it by hand against the built package (
npm run buildfirst) — each command below was run on this branch and the output shown is the output produced:Checklist
[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[x] I have commented my code, particularly in hard-to-understand areas.
[x] I have added tests that prove my fix is effective or that my feature works.
[x] New and existing unit tests pass locally with my changes.