Skip to content

Commit 60c55ad

Browse files
google-genai-botcopybara-github
authored andcommitted
fix: fix vertex_ai_session_service crashing when Agent Engine passes full resource names instead of short session IDs
PiperOrigin-RevId: 932964014
1 parent f8e9195 commit 60c55ad

2 files changed

Lines changed: 86 additions & 2 deletions

File tree

src/google/adk/sessions/vertex_ai_session_service.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,29 @@
5050
_SESSION_ID_PATTERN = re.compile(r'^[A-Za-z0-9_-]+$')
5151

5252

53+
def _extract_short_session_id(
54+
session_id: str, expected_engine_id: str | None = None
55+
) -> str:
56+
"""Extracts the short session ID if a full resource name is provided."""
57+
if isinstance(session_id, str) and '/' in session_id:
58+
parts = session_id.split('/')
59+
if len(parts) >= 2 and parts[-2] == 'sessions':
60+
if (
61+
len(parts) >= 4
62+
and parts[-4] == 'reasoningEngines'
63+
and expected_engine_id
64+
):
65+
passed_engine_id = parts[-3]
66+
if passed_engine_id != expected_engine_id:
67+
raise ValueError(
68+
'Session resource name mismatch: session belongs to '
69+
f'reasoningEngine {passed_engine_id!r}, but service is '
70+
f'configured for {expected_engine_id!r}.'
71+
)
72+
return parts[-1]
73+
return session_id
74+
75+
5376
def _validate_session_id(session_id: str) -> None:
5477
"""Rejects session IDs that could escape the URL path segment."""
5578
if not isinstance(session_id, str) or not _SESSION_ID_PATTERN.fullmatch(
@@ -146,6 +169,9 @@ async def create_session(
146169

147170
config = {'session_state': state} if state else {}
148171
if session_id:
172+
session_id = _extract_short_session_id(
173+
session_id, expected_engine_id=reasoning_engine_id
174+
)
149175
_validate_session_id(session_id)
150176
config['session_id'] = session_id
151177
config.update(kwargs)
@@ -177,8 +203,11 @@ async def get_session(
177203
session_id: str,
178204
config: Optional[GetSessionConfig] = None,
179205
) -> Optional[Session]:
180-
_validate_session_id(session_id)
181206
reasoning_engine_id = self._get_reasoning_engine_id(app_name)
207+
session_id = _extract_short_session_id(
208+
session_id, expected_engine_id=reasoning_engine_id
209+
)
210+
_validate_session_id(session_id)
182211
session_resource_name = (
183212
f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}'
184213
)
@@ -277,8 +306,11 @@ async def list_sessions(
277306
async def delete_session(
278307
self, *, app_name: str, user_id: str, session_id: str
279308
) -> None:
280-
_validate_session_id(session_id)
281309
reasoning_engine_id = self._get_reasoning_engine_id(app_name)
310+
session_id = _extract_short_session_id(
311+
session_id, expected_engine_id=reasoning_engine_id
312+
)
313+
_validate_session_id(session_id)
282314
session_resource_name = (
283315
f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}'
284316
)

tests/unittests/sessions/test_vertex_ai_session_service.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
from google.adk.models.cache_metadata import CacheMetadata
3232
from google.adk.sessions.base_session_service import GetSessionConfig
3333
from google.adk.sessions.session import Session
34+
from google.adk.sessions.vertex_ai_session_service import _extract_short_session_id
35+
from google.adk.sessions.vertex_ai_session_service import _validate_session_id
3436
from google.adk.sessions.vertex_ai_session_service import VertexAiSessionService
3537
from google.api_core import exceptions as api_core_exceptions
3638
from google.genai import types as genai_types
@@ -1350,3 +1352,53 @@ class DummyModel(pydantic.BaseModel):
13501352

13511353
assert appended_event.actions.compaction is not None
13521354
assert appended_event.actions.compaction.start_timestamp == 1000.0
1355+
1356+
1357+
def test_extract_short_session_id_short_id():
1358+
assert _extract_short_session_id('123') == '123'
1359+
assert _extract_short_session_id('session-123_abc') == 'session-123_abc'
1360+
1361+
1362+
def test_extract_short_session_id_strips_full_resource_name():
1363+
resource_name = 'projects/123/locations/us-east4/reasoningEngines/456/sessions/session-123'
1364+
assert _extract_short_session_id(resource_name) == 'session-123'
1365+
assert (
1366+
_extract_short_session_id(resource_name, expected_engine_id='456')
1367+
== 'session-123'
1368+
)
1369+
1370+
1371+
def test_extract_short_session_id_mismatch():
1372+
resource_name = 'projects/123/locations/us-east4/reasoningEngines/wrong/sessions/session-123'
1373+
with pytest.raises(ValueError, match='Session resource name mismatch'):
1374+
_extract_short_session_id(resource_name, expected_engine_id='right')
1375+
1376+
1377+
def test_validate_session_id_rejects_invalid_chars():
1378+
with pytest.raises(ValueError, match='Invalid session_id'):
1379+
_validate_session_id('invalid@id')
1380+
with pytest.raises(ValueError, match='Invalid session_id'):
1381+
_validate_session_id('invalid/id')
1382+
1383+
1384+
@pytest.mark.asyncio
1385+
@pytest.mark.usefixtures('mock_get_api_client')
1386+
async def test_get_session_strips_full_resource_name(
1387+
mock_api_client_instance,
1388+
):
1389+
session_service = mock_vertex_ai_session_service()
1390+
mock_api_client_instance.session_dict['session-123'] = {
1391+
'name': (
1392+
'projects/123/locations/us-east4/reasoningEngines/123/sessions/session-123'
1393+
),
1394+
'update_time': '2023-01-01T00:00:00Z',
1395+
'user_id': 'user',
1396+
}
1397+
resource_name = 'projects/123/locations/us-east4/reasoningEngines/123/sessions/session-123'
1398+
session = await session_service.get_session(
1399+
app_name='123', user_id='user', session_id=resource_name
1400+
)
1401+
assert session.id == 'session-123'
1402+
mock_api_client_instance.agent_engines.sessions.get.assert_called_once_with(
1403+
name='reasoningEngines/123/sessions/session-123'
1404+
)

0 commit comments

Comments
 (0)