Skip to content

Commit 9014a84

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Add api key argument to Vertex Session and Memory services for Express Mode support
We also change VertexAiSessionService and VertexAiMemoryBankService to both use keyword arguments for project, location, agent engine id, and express mode api key PiperOrigin-RevId: 825719331
1 parent d45b31f commit 9014a84

File tree

6 files changed

+258
-26
lines changed

6 files changed

+258
-26
lines changed

src/google/adk/memory/vertex_ai_memory_bank_service.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from typing_extensions import override
2323
import vertexai
2424

25+
from ..utils.vertex_ai_utils import get_express_mode_api_key
2526
from .base_memory_service import BaseMemoryService
2627
from .base_memory_service import SearchMemoryResponse
2728
from .memory_entry import MemoryEntry
@@ -40,6 +41,8 @@ def __init__(
4041
project: Optional[str] = None,
4142
location: Optional[str] = None,
4243
agent_engine_id: Optional[str] = None,
44+
*,
45+
express_mode_api_key: Optional[str] = None,
4346
):
4447
"""Initializes a VertexAiMemoryBankService.
4548
@@ -49,10 +52,19 @@ def __init__(
4952
agent_engine_id: The ID of the agent engine to use for the Memory Bank.
5053
e.g. '456' in
5154
'projects/my-project/locations/us-central1/reasoningEngines/456'.
55+
express_mode_api_key: The API key to use for Express Mode. If not
56+
provided, the API key from the GOOGLE_API_KEY environment variable will
57+
be used. It will only be used if GOOGLE_GENAI_USE_VERTEXAI is true.
58+
Do not use Google AI Studio API key for this field. For more details,
59+
visit
60+
https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview
5261
"""
5362
self._project = project
5463
self._location = location
5564
self._agent_engine_id = agent_engine_id
65+
self._express_mode_api_key = get_express_mode_api_key(
66+
project, location, express_mode_api_key
67+
)
5668

5769
@override
5870
async def add_session_to_memory(self, session: Session):
@@ -123,11 +135,14 @@ def _get_api_client(self):
123135
124136
It needs to be instantiated inside each request so that the event loop
125137
management can be properly propagated.
126-
127138
Returns:
128-
An API client for the given project and location.
139+
An API client for the given project and location or express mode api key.
129140
"""
130-
return vertexai.Client(project=self._project, location=self._location)
141+
return vertexai.Client(
142+
project=self._project,
143+
location=self._location,
144+
api_key=self._express_mode_api_key,
145+
)
131146

132147

133148
def _should_filter_out_event(content: types.Content) -> bool:

src/google/adk/sessions/vertex_ai_session_service.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import datetime
1818
import json
1919
import logging
20-
import os
2120
import re
2221
from typing import Any
2322
from typing import Optional
@@ -35,7 +34,8 @@
3534
from . import _session_util
3635
from ..events.event import Event
3736
from ..events.event_actions import EventActions
38-
from ..utils.env_utils import is_env_enabled
37+
from ..utils.vertex_ai_utils import get_express_mode_api_key
38+
from ..utils.vertex_ai_utils import is_vertex_express_mode
3939
from .base_session_service import BaseSessionService
4040
from .base_session_service import GetSessionConfig
4141
from .base_session_service import ListSessionsResponse
@@ -55,17 +55,28 @@ def __init__(
5555
project: Optional[str] = None,
5656
location: Optional[str] = None,
5757
agent_engine_id: Optional[str] = None,
58+
*,
59+
express_mode_api_key: Optional[str] = None,
5860
):
5961
"""Initializes the VertexAiSessionService.
6062
6163
Args:
6264
project: The project id of the project to use.
6365
location: The location of the project to use.
6466
agent_engine_id: The resource ID of the agent engine to use.
67+
express_mode_api_key: The API key to use for Express Mode. If not
68+
provided, the API key from the GOOGLE_API_KEY environment variable will
69+
be used. It will only be used if GOOGLE_GENAI_USE_VERTEXAI is true.
70+
Do not use Google AI Studio API key for this field. For more details,
71+
visit
72+
https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview
6573
"""
6674
self._project = project
6775
self._location = location
6876
self._agent_engine_id = agent_engine_id
77+
self._express_mode_api_key = get_express_mode_api_key(
78+
project, location, express_mode_api_key
79+
)
6980

7081
@override
7182
async def create_session(
@@ -104,7 +115,9 @@ async def create_session(
104115
config = {'session_state': state} if state else {}
105116
config.update(kwargs)
106117

107-
if _is_vertex_express_mode(self._project, self._location):
118+
if is_vertex_express_mode(
119+
self._project, self._location, self._express_mode_api_key
120+
):
108121
config['wait_for_completion'] = False
109122
api_response = await api_client.aio.agent_engines.sessions.create(
110123
name=f'reasoningEngines/{reasoning_engine_id}',
@@ -351,27 +364,16 @@ def _get_api_client(self) -> vertexai.Client:
351364
"""Instantiates an API client for the given project and location.
352365
353366
Returns:
354-
An API client for the given project and location.
367+
An API client for the given project and location or express mode api key.
355368
"""
356369
return vertexai.Client(
357370
project=self._project,
358371
location=self._location,
359372
http_options=self._api_client_http_options_override(),
373+
api_key=self._express_mode_api_key,
360374
)
361375

362376

363-
def _is_vertex_express_mode(
364-
project: Optional[str], location: Optional[str]
365-
) -> bool:
366-
"""Check if Vertex AI and API key are both enabled replacing project and location, meaning the user is using the Vertex Express Mode."""
367-
return (
368-
is_env_enabled('GOOGLE_GENAI_USE_VERTEXAI')
369-
and os.environ.get('GOOGLE_API_KEY', None) is not None
370-
and project is None
371-
and location is None
372-
)
373-
374-
375377
def _from_api_event(api_event_obj: vertexai.types.SessionEvent) -> Event:
376378
"""Converts an API event object to an Event object."""
377379
actions = getattr(api_event_obj, 'actions', None)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Utilities for Vertex AI. Includes helper functions for Express Mode.
16+
17+
This module is for ADK internal use only.
18+
Please do not rely on the implementation details.
19+
"""
20+
21+
from __future__ import annotations
22+
23+
import os
24+
from typing import Optional
25+
26+
from ..utils.env_utils import is_env_enabled
27+
28+
29+
def is_vertex_express_mode(
30+
project: Optional[str], location: Optional[str], api_key: Optional[str]
31+
) -> bool:
32+
"""Check if Vertex AI and API key are both enabled replacing project and location, meaning the user is using the Vertex Express Mode."""
33+
return (
34+
is_env_enabled('GOOGLE_GENAI_USE_VERTEXAI')
35+
and api_key is not None
36+
and project is None
37+
and location is None
38+
)
39+
40+
41+
def get_express_mode_api_key(
42+
project: Optional[str],
43+
location: Optional[str],
44+
express_mode_api_key: Optional[str],
45+
) -> Optional[str]:
46+
"""Validates and returns the API key for Express Mode."""
47+
if (project or location) and express_mode_api_key:
48+
raise ValueError(
49+
'Cannot specify project or location and express_mode_api_key. '
50+
'Either use project and location, or just the express_mode_api_key.'
51+
)
52+
if is_env_enabled('GOOGLE_GENAI_USE_VERTEXAI'):
53+
return express_mode_api_key or os.environ.get('GOOGLE_API_KEY', None)
54+
else:
55+
return None

tests/unittests/memory/test_vertex_ai_memory_bank_service.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
from datetime import datetime
16+
from typing import Optional
1617
from unittest import mock
1718

1819
from google.adk.events.event import Event
@@ -69,12 +70,18 @@
6970
)
7071

7172

72-
def mock_vertex_ai_memory_bank_service():
73+
def mock_vertex_ai_memory_bank_service(
74+
project: Optional[str] = 'test-project',
75+
location: Optional[str] = 'test-location',
76+
agent_engine_id: Optional[str] = '123',
77+
express_mode_api_key: Optional[str] = None,
78+
):
7379
"""Creates a mock Vertex AI Memory Bank service for testing."""
7480
return VertexAiMemoryBankService(
75-
project='test-project',
76-
location='test-location',
77-
agent_engine_id='123',
81+
project=project,
82+
location=location,
83+
agent_engine_id=agent_engine_id,
84+
express_mode_api_key=express_mode_api_key,
7885
)
7986

8087

@@ -90,6 +97,21 @@ def mock_vertexai_client():
9097
yield mock_client
9198

9299

100+
@pytest.mark.asyncio
101+
async def test_initialize_with_project_location_and_api_key_error():
102+
with pytest.raises(ValueError) as excinfo:
103+
mock_vertex_ai_memory_bank_service(
104+
project='test-project',
105+
location='test-location',
106+
express_mode_api_key='test-api-key',
107+
)
108+
assert (
109+
'Cannot specify project or location and express_mode_api_key. Either use'
110+
' project and location, or just the express_mode_api_key.'
111+
in str(excinfo.value)
112+
)
113+
114+
93115
@pytest.mark.asyncio
94116
async def test_add_session_to_memory(mock_vertexai_client):
95117
memory_service = mock_vertex_ai_memory_bank_service()

tests/unittests/sessions/test_vertex_ai_session_service.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,12 +356,18 @@ async def _append_event(
356356
self.event_dict[session_id] = ([event_json], None)
357357

358358

359-
def mock_vertex_ai_session_service(agent_engine_id: Optional[str] = None):
359+
def mock_vertex_ai_session_service(
360+
project: Optional[str] = 'test-project',
361+
location: Optional[str] = 'test-location',
362+
agent_engine_id: Optional[str] = None,
363+
express_mode_api_key: Optional[str] = None,
364+
):
360365
"""Creates a mock Vertex AI Session service for testing."""
361366
return VertexAiSessionService(
362-
project='test-project',
363-
location='test-location',
367+
project=project,
368+
location=location,
364369
agent_engine_id=agent_engine_id,
370+
express_mode_api_key=express_mode_api_key,
365371
)
366372

367373

@@ -393,6 +399,21 @@ def mock_get_api_client(mock_api_client_instance):
393399
yield
394400

395401

402+
@pytest.mark.asyncio
403+
async def test_initialize_with_project_location_and_api_key_error():
404+
with pytest.raises(ValueError) as excinfo:
405+
mock_vertex_ai_session_service(
406+
project='test-project',
407+
location='test-location',
408+
express_mode_api_key='test-api-key',
409+
)
410+
assert (
411+
'Cannot specify project or location and express_mode_api_key. Either use'
412+
' project and location, or just the express_mode_api_key.'
413+
in str(excinfo.value)
414+
)
415+
416+
396417
@pytest.mark.asyncio
397418
@pytest.mark.usefixtures('mock_get_api_client')
398419
@pytest.mark.parametrize('agent_engine_id', [None, '123'])

0 commit comments

Comments
 (0)