Skip to content

Commit ae2392c

Browse files
wanlin31copybara-github
authored andcommitted
feat(python): Support GEMINI_API_KEY as environment variable for setting API key.
PiperOrigin-RevId: 767169011
1 parent c3977d1 commit ae2392c

File tree

2 files changed

+146
-5
lines changed

2 files changed

+146
-5
lines changed

google/genai/_api_client.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ class EphemeralTokenAPIKeyError(ValueError):
6868
"""Error raised when the API key is invalid."""
6969

7070

71+
# This method checks for the API key in the environment variables. Google API
72+
# key is precedenced over Gemini API key.
73+
def _get_env_api_key() -> Optional[str]:
74+
"""Gets the API key from environment variables, prioritizing GOOGLE_API_KEY.
75+
76+
Returns:
77+
The API key string if found, otherwise None. Empty string is considered
78+
invalid.
79+
"""
80+
env_google_api_key = os.environ.get('GOOGLE_API_KEY', None)
81+
env_gemini_api_key = os.environ.get('GEMINI_API_KEY', None)
82+
if env_google_api_key and env_gemini_api_key:
83+
logger.warning(
84+
'Both GOOGLE_API_KEY and GEMINI_API_KEY are set. Using'
85+
' GOOGLE_API_KEY.'
86+
)
87+
88+
return env_google_api_key or env_gemini_api_key or None
89+
90+
7191
def _append_library_version_headers(headers: dict[str, str]) -> None:
7292
"""Appends the telemetry header to the headers dict."""
7393
library_label = f'google-genai-sdk/{version.__version__}'
@@ -371,7 +391,7 @@ def __init__(
371391
# Retrieve implicitly set values from the environment.
372392
env_project = os.environ.get('GOOGLE_CLOUD_PROJECT', None)
373393
env_location = os.environ.get('GOOGLE_CLOUD_LOCATION', None)
374-
env_api_key = os.environ.get('GOOGLE_API_KEY', None)
394+
env_api_key = _get_env_api_key()
375395
self.project = project or env_project
376396
self.location = location or env_location
377397
self.api_key = api_key or env_api_key

google/genai/tests/client/test_client_initialization.py

Lines changed: 125 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,34 @@
3131
from ... import Client
3232

3333

34-
def test_ml_dev_from_env(monkeypatch):
34+
def test_ml_dev_from_gemini_env_only(monkeypatch):
35+
api_key = "gemini_api_key"
36+
monkeypatch.setenv("GEMINI_API_KEY", api_key)
37+
monkeypatch.delenv("GOOGLE_API_KEY", raising=False)
38+
39+
client = Client()
40+
41+
assert not client.models._api_client.vertexai
42+
assert client.models._api_client.api_key == api_key
43+
assert isinstance(client.models._api_client, api_client.BaseApiClient)
44+
45+
46+
def test_ml_dev_from_gemini_env_with_google_env_empty(monkeypatch):
47+
api_key = "gemini_api_key"
48+
monkeypatch.setenv("GEMINI_API_KEY", api_key)
49+
monkeypatch.setenv("GOOGLE_API_KEY", "")
50+
51+
client = Client()
52+
53+
assert not client.models._api_client.vertexai
54+
assert client.models._api_client.api_key == api_key
55+
assert isinstance(client.models._api_client, api_client.BaseApiClient)
56+
57+
58+
def test_ml_dev_from_google_env_only(monkeypatch):
3559
api_key = "google_api_key"
3660
monkeypatch.setenv("GOOGLE_API_KEY", api_key)
61+
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
3762

3863
client = Client()
3964

@@ -42,6 +67,24 @@ def test_ml_dev_from_env(monkeypatch):
4267
assert isinstance(client.models._api_client, api_client.BaseApiClient)
4368

4469

70+
def test_ml_dev_both_env_key_set(monkeypatch, caplog):
71+
caplog.set_level(logging.DEBUG, logger="google_genai._api_client")
72+
google_api_key = "google_api_key"
73+
gemini_api_key = "gemini_api_key"
74+
monkeypatch.setenv("GOOGLE_API_KEY", google_api_key)
75+
monkeypatch.setenv("GEMINI_API_KEY", gemini_api_key)
76+
77+
client = Client()
78+
79+
assert not client.models._api_client.vertexai
80+
assert client.models._api_client.api_key == google_api_key
81+
assert isinstance(client.models._api_client, api_client.BaseApiClient)
82+
assert (
83+
"Both GOOGLE_API_KEY and GEMINI_API_KEY are set. Using GOOGLE_API_KEY."
84+
in caplog.text
85+
)
86+
87+
4588
def test_ml_dev_from_constructor():
4689
api_key = "google_api_key"
4790

@@ -276,6 +319,7 @@ def test_invalid_vertexai_constructor_empty(monkeypatch):
276319
monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "")
277320
monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "")
278321
monkeypatch.setenv("GOOGLE_API_KEY", "")
322+
monkeypatch.setenv("GEMINI_API_KEY", "")
279323

280324
def mock_auth_default(scopes=None):
281325
return None, None
@@ -287,6 +331,7 @@ def mock_auth_default(scopes=None):
287331
def test_invalid_mldev_constructor_empty(monkeypatch):
288332
with pytest.raises(ValueError):
289333
monkeypatch.setenv("GOOGLE_API_KEY", "")
334+
monkeypatch.setenv("GEMINI_API_KEY", "")
290335
Client()
291336

292337

@@ -379,16 +424,22 @@ def test_invalid_mldev_constructor():
379424
assert isinstance(e, ValueError)
380425

381426

382-
def test_mldev_explicit_arg_precedence(monkeypatch):
427+
def test_mldev_explicit_arg_precedence(monkeypatch, caplog):
428+
caplog.set_level(logging.DEBUG, logger="google_genai._api_client")
383429
api_key = "constructor_api_key"
384430

385-
monkeypatch.setenv("GOOGLE_API_KEY", "env_api_key")
431+
monkeypatch.setenv("GOOGLE_API_KEY", "google_env_api_key")
432+
monkeypatch.setenv("GEMINI_API_KEY", "gemini_env_api_key")
386433

387434
client = Client(api_key=api_key)
388435

389436
assert not client.models._api_client.vertexai
390437
assert client.models._api_client.api_key == api_key
391438
assert isinstance(client.models._api_client, api_client.BaseApiClient)
439+
assert (
440+
"Both GOOGLE_API_KEY and GEMINI_API_KEY are set. Using GOOGLE_API_KEY."
441+
in caplog.text
442+
)
392443

393444

394445
def test_replay_client_ml_dev_from_env(monkeypatch, use_vertex: bool):
@@ -464,10 +515,11 @@ def test_vertexai_apikey_from_constructor(monkeypatch):
464515
assert isinstance(client.models._api_client, api_client.BaseApiClient)
465516

466517

467-
def test_vertexai_apikey_from_env(monkeypatch):
518+
def test_vertexai_apikey_from_env_google_api_key_only(monkeypatch):
468519
# Vertex AI Express mode uses API key on Vertex AI.
469520
api_key = "vertexai_api_key"
470521
monkeypatch.setenv("GOOGLE_API_KEY", api_key)
522+
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
471523

472524
# Due to proj/location taking precedence, need to clear proj/location env
473525
# variables.
@@ -484,6 +536,75 @@ def test_vertexai_apikey_from_env(monkeypatch):
484536
assert isinstance(client.models._api_client, api_client.BaseApiClient)
485537

486538

539+
def test_vertexai_apikey_from_env_gemini_api_key_only(monkeypatch):
540+
# Vertex AI Express mode uses API key on Vertex AI.
541+
api_key = "vertexai_api_key"
542+
monkeypatch.setenv("GEMINI_API_KEY", api_key)
543+
monkeypatch.delenv("GOOGLE_API_KEY", raising=False)
544+
545+
# Due to proj/location taking precedence, need to clear proj/location env
546+
# variables.
547+
monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "")
548+
monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "")
549+
550+
client = Client(vertexai=True)
551+
552+
assert client.models._api_client.vertexai
553+
assert client.models._api_client.api_key == api_key
554+
assert not client.models._api_client.project
555+
assert not client.models._api_client.location
556+
assert "aiplatform" in client._api_client._http_options.base_url
557+
assert isinstance(client.models._api_client, api_client.BaseApiClient)
558+
559+
560+
def test_vertexai_apikey_from_env_gemini_api_key_with_google_api_key_empty(monkeypatch):
561+
# Vertex AI Express mode uses API key on Vertex AI.
562+
api_key = "vertexai_api_key"
563+
monkeypatch.setenv("GEMINI_API_KEY", api_key)
564+
monkeypatch.setenv("GOOGLE_API_KEY", "")
565+
566+
# Due to proj/location taking precedence, need to clear proj/location env
567+
# variables.
568+
monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "")
569+
monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "")
570+
571+
client = Client(vertexai=True)
572+
573+
assert client.models._api_client.vertexai
574+
assert client.models._api_client.api_key == api_key
575+
assert not client.models._api_client.project
576+
assert not client.models._api_client.location
577+
assert "aiplatform" in client._api_client._http_options.base_url
578+
assert isinstance(client.models._api_client, api_client.BaseApiClient)
579+
580+
581+
def test_vertexai_apikey_from_env_both_api_keys(monkeypatch, caplog):
582+
caplog.set_level(logging.DEBUG, logger="google_genai._api_client")
583+
# Vertex AI Express mode uses API key on Vertex AI.
584+
google_api_key = "google_api_key"
585+
gemini_api_key = "vertexai_api_key"
586+
monkeypatch.setenv("GEMINI_API_KEY", gemini_api_key)
587+
monkeypatch.setenv("GOOGLE_API_KEY", google_api_key)
588+
589+
# Due to proj/location taking precedence, need to clear proj/location env
590+
# variables.
591+
monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "")
592+
monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "")
593+
594+
client = Client(vertexai=True)
595+
596+
assert client.models._api_client.vertexai
597+
assert client.models._api_client.api_key == google_api_key
598+
assert not client.models._api_client.project
599+
assert not client.models._api_client.location
600+
assert "aiplatform" in client._api_client._http_options.base_url
601+
assert isinstance(client.models._api_client, api_client.BaseApiClient)
602+
assert (
603+
"Both GOOGLE_API_KEY and GEMINI_API_KEY are set. Using GOOGLE_API_KEY."
604+
in caplog.text
605+
)
606+
607+
487608
def test_vertexai_apikey_invalid_constructor1():
488609
# Vertex AI Express mode uses API key on Vertex AI.
489610
api_key = "vertexai_api_key"

0 commit comments

Comments
 (0)