google-adk 0.4.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google/adk/agents/active_streaming_tool.py +1 -0
- google/adk/agents/base_agent.py +91 -47
- google/adk/agents/base_agent.py.orig +330 -0
- google/adk/agents/callback_context.py +4 -9
- google/adk/agents/invocation_context.py +1 -0
- google/adk/agents/langgraph_agent.py +1 -0
- google/adk/agents/live_request_queue.py +1 -0
- google/adk/agents/llm_agent.py +172 -35
- google/adk/agents/loop_agent.py +1 -1
- google/adk/agents/parallel_agent.py +7 -0
- google/adk/agents/readonly_context.py +7 -1
- google/adk/agents/run_config.py +5 -1
- google/adk/agents/sequential_agent.py +31 -0
- google/adk/agents/transcription_entry.py +5 -2
- google/adk/artifacts/base_artifact_service.py +5 -10
- google/adk/artifacts/gcs_artifact_service.py +9 -9
- google/adk/artifacts/in_memory_artifact_service.py +6 -6
- google/adk/auth/auth_credential.py +9 -5
- google/adk/auth/auth_preprocessor.py +7 -1
- google/adk/auth/auth_tool.py +3 -4
- google/adk/cli/agent_graph.py +5 -5
- google/adk/cli/browser/index.html +2 -2
- google/adk/cli/browser/{main-HWIBUY2R.js → main-QOEMUXM4.js} +58 -58
- google/adk/cli/cli.py +7 -7
- google/adk/cli/cli_deploy.py +7 -2
- google/adk/cli/cli_eval.py +181 -106
- google/adk/cli/cli_tools_click.py +147 -62
- google/adk/cli/fast_api.py +340 -158
- google/adk/cli/fast_api.py.orig +822 -0
- google/adk/cli/utils/common.py +23 -0
- google/adk/cli/utils/evals.py +83 -1
- google/adk/cli/utils/logs.py +13 -5
- google/adk/code_executors/__init__.py +3 -1
- google/adk/code_executors/built_in_code_executor.py +52 -0
- google/adk/evaluation/__init__.py +1 -1
- google/adk/evaluation/agent_evaluator.py +168 -128
- google/adk/evaluation/eval_case.py +102 -0
- google/adk/evaluation/eval_set.py +37 -0
- google/adk/evaluation/eval_sets_manager.py +42 -0
- google/adk/evaluation/evaluation_constants.py +1 -0
- google/adk/evaluation/evaluation_generator.py +89 -114
- google/adk/evaluation/evaluator.py +56 -0
- google/adk/evaluation/local_eval_sets_manager.py +264 -0
- google/adk/evaluation/response_evaluator.py +107 -3
- google/adk/evaluation/trajectory_evaluator.py +83 -2
- google/adk/events/event.py +7 -1
- google/adk/events/event_actions.py +7 -1
- google/adk/examples/example.py +1 -0
- google/adk/examples/example_util.py +3 -2
- google/adk/flows/__init__.py +0 -1
- google/adk/flows/llm_flows/_code_execution.py +19 -11
- google/adk/flows/llm_flows/audio_transcriber.py +4 -3
- google/adk/flows/llm_flows/base_llm_flow.py +86 -22
- google/adk/flows/llm_flows/basic.py +3 -0
- google/adk/flows/llm_flows/functions.py +10 -9
- google/adk/flows/llm_flows/instructions.py +28 -9
- google/adk/flows/llm_flows/single_flow.py +1 -1
- google/adk/memory/__init__.py +1 -1
- google/adk/memory/_utils.py +23 -0
- google/adk/memory/base_memory_service.py +25 -21
- google/adk/memory/base_memory_service.py.orig +76 -0
- google/adk/memory/in_memory_memory_service.py +59 -27
- google/adk/memory/memory_entry.py +37 -0
- google/adk/memory/vertex_ai_rag_memory_service.py +40 -17
- google/adk/models/anthropic_llm.py +36 -11
- google/adk/models/base_llm.py +45 -4
- google/adk/models/gemini_llm_connection.py +15 -2
- google/adk/models/google_llm.py +9 -44
- google/adk/models/google_llm.py.orig +305 -0
- google/adk/models/lite_llm.py +94 -38
- google/adk/models/llm_request.py +1 -1
- google/adk/models/llm_response.py +15 -3
- google/adk/models/registry.py +1 -1
- google/adk/runners.py +68 -44
- google/adk/sessions/__init__.py +1 -1
- google/adk/sessions/_session_util.py +14 -0
- google/adk/sessions/base_session_service.py +8 -32
- google/adk/sessions/database_session_service.py +58 -61
- google/adk/sessions/in_memory_session_service.py +108 -26
- google/adk/sessions/session.py +4 -0
- google/adk/sessions/vertex_ai_session_service.py +23 -45
- google/adk/telemetry.py +3 -0
- google/adk/tools/__init__.py +4 -7
- google/adk/tools/{built_in_code_execution_tool.py → _built_in_code_execution_tool.py} +11 -0
- google/adk/tools/_memory_entry_utils.py +30 -0
- google/adk/tools/agent_tool.py +16 -13
- google/adk/tools/apihub_tool/apihub_toolset.py +55 -74
- google/adk/tools/application_integration_tool/application_integration_toolset.py +107 -85
- google/adk/tools/application_integration_tool/clients/connections_client.py +29 -25
- google/adk/tools/application_integration_tool/clients/integration_client.py +6 -6
- google/adk/tools/application_integration_tool/integration_connector_tool.py +69 -26
- google/adk/tools/base_toolset.py +58 -0
- google/adk/tools/enterprise_search_tool.py +65 -0
- google/adk/tools/function_parameter_parse_util.py +2 -2
- google/adk/tools/google_api_tool/__init__.py +18 -70
- google/adk/tools/google_api_tool/google_api_tool.py +11 -5
- google/adk/tools/google_api_tool/google_api_toolset.py +126 -0
- google/adk/tools/google_api_tool/google_api_toolsets.py +102 -0
- google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +40 -42
- google/adk/tools/langchain_tool.py +96 -49
- google/adk/tools/load_artifacts_tool.py +4 -4
- google/adk/tools/load_memory_tool.py +16 -5
- google/adk/tools/mcp_tool/__init__.py +3 -2
- google/adk/tools/mcp_tool/conversion_utils.py +1 -1
- google/adk/tools/mcp_tool/mcp_session_manager.py +167 -16
- google/adk/tools/mcp_tool/mcp_session_manager.py.orig +322 -0
- google/adk/tools/mcp_tool/mcp_tool.py +12 -12
- google/adk/tools/mcp_tool/mcp_toolset.py +155 -195
- google/adk/tools/openapi_tool/common/common.py +2 -5
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +32 -7
- google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +43 -33
- google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +1 -1
- google/adk/tools/preload_memory_tool.py +27 -18
- google/adk/tools/retrieval/__init__.py +1 -1
- google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +1 -1
- google/adk/tools/tool_context.py +4 -4
- google/adk/tools/toolbox_toolset.py +79 -0
- google/adk/tools/transfer_to_agent_tool.py +0 -1
- google/adk/version.py +1 -1
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/METADATA +7 -5
- google_adk-1.0.0.dist-info/RECORD +195 -0
- google/adk/agents/remote_agent.py +0 -50
- google/adk/tools/google_api_tool/google_api_tool_set.py +0 -110
- google/adk/tools/google_api_tool/google_api_tool_sets.py +0 -112
- google/adk/tools/toolbox_tool.py +0 -46
- google_adk-0.4.0.dist-info/RECORD +0 -179
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/WHEEL +0 -0
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/entry_points.txt +0 -0
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -13,9 +13,11 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
import copy
|
15
15
|
from datetime import datetime
|
16
|
+
from datetime import timezone
|
16
17
|
import json
|
17
18
|
import logging
|
18
|
-
from typing import Any
|
19
|
+
from typing import Any
|
20
|
+
from typing import Optional
|
19
21
|
import uuid
|
20
22
|
|
21
23
|
from sqlalchemy import Boolean
|
@@ -49,23 +51,18 @@ from ..events.event import Event
|
|
49
51
|
from . import _session_util
|
50
52
|
from .base_session_service import BaseSessionService
|
51
53
|
from .base_session_service import GetSessionConfig
|
52
|
-
from .base_session_service import ListEventsResponse
|
53
54
|
from .base_session_service import ListSessionsResponse
|
54
55
|
from .session import Session
|
55
56
|
from .state import State
|
56
57
|
|
57
|
-
|
58
|
-
logger = logging.getLogger(__name__)
|
58
|
+
logger = logging.getLogger("google_adk." + __name__)
|
59
59
|
|
60
60
|
DEFAULT_MAX_KEY_LENGTH = 128
|
61
61
|
DEFAULT_MAX_VARCHAR_LENGTH = 256
|
62
62
|
|
63
63
|
|
64
64
|
class DynamicJSON(TypeDecorator):
|
65
|
-
"""A JSON-like type that uses JSONB on PostgreSQL and TEXT with JSON
|
66
|
-
|
67
|
-
serialization for other databases.
|
68
|
-
"""
|
65
|
+
"""A JSON-like type that uses JSONB on PostgreSQL and TEXT with JSON serialization for other databases."""
|
69
66
|
|
70
67
|
impl = Text # Default implementation is TEXT
|
71
68
|
|
@@ -219,6 +216,7 @@ class StorageAppState(Base):
|
|
219
216
|
DateTime(), default=func.now(), onupdate=func.now()
|
220
217
|
)
|
221
218
|
|
219
|
+
|
222
220
|
class StorageUserState(Base):
|
223
221
|
"""Represents a user state stored in the database."""
|
224
222
|
|
@@ -242,10 +240,7 @@ class DatabaseSessionService(BaseSessionService):
|
|
242
240
|
"""A session service that uses a database for storage."""
|
243
241
|
|
244
242
|
def __init__(self, db_url: str):
|
245
|
-
"""
|
246
|
-
Args:
|
247
|
-
db_url: The database URL to connect to.
|
248
|
-
"""
|
243
|
+
"""Initializes the database session service with a database URL."""
|
249
244
|
# 1. Create DB engine for db connection
|
250
245
|
# 2. Create all tables based on schema
|
251
246
|
# 3. Initialize all properties
|
@@ -274,7 +269,7 @@ class DatabaseSessionService(BaseSessionService):
|
|
274
269
|
self.inspector = inspect(self.db_engine)
|
275
270
|
|
276
271
|
# DB session factory method
|
277
|
-
self.
|
272
|
+
self.database_session_factory: sessionmaker[DatabaseSessionFactory] = (
|
278
273
|
sessionmaker(bind=self.db_engine)
|
279
274
|
)
|
280
275
|
|
@@ -283,7 +278,7 @@ class DatabaseSessionService(BaseSessionService):
|
|
283
278
|
Base.metadata.create_all(self.db_engine)
|
284
279
|
|
285
280
|
@override
|
286
|
-
def create_session(
|
281
|
+
async def create_session(
|
287
282
|
self,
|
288
283
|
*,
|
289
284
|
app_name: str,
|
@@ -297,11 +292,11 @@ class DatabaseSessionService(BaseSessionService):
|
|
297
292
|
# 4. Build the session object with generated id
|
298
293
|
# 5. Return the session
|
299
294
|
|
300
|
-
with self.
|
295
|
+
with self.database_session_factory() as session_factory:
|
301
296
|
|
302
297
|
# Fetch app and user states from storage
|
303
|
-
storage_app_state =
|
304
|
-
storage_user_state =
|
298
|
+
storage_app_state = session_factory.get(StorageAppState, (app_name))
|
299
|
+
storage_user_state = session_factory.get(
|
305
300
|
StorageUserState, (app_name, user_id)
|
306
301
|
)
|
307
302
|
|
@@ -311,12 +306,12 @@ class DatabaseSessionService(BaseSessionService):
|
|
311
306
|
# Create state tables if not exist
|
312
307
|
if not storage_app_state:
|
313
308
|
storage_app_state = StorageAppState(app_name=app_name, state={})
|
314
|
-
|
309
|
+
session_factory.add(storage_app_state)
|
315
310
|
if not storage_user_state:
|
316
311
|
storage_user_state = StorageUserState(
|
317
312
|
app_name=app_name, user_id=user_id, state={}
|
318
313
|
)
|
319
|
-
|
314
|
+
session_factory.add(storage_user_state)
|
320
315
|
|
321
316
|
# Extract state deltas
|
322
317
|
app_state_delta, user_state_delta, session_state = _extract_state_delta(
|
@@ -340,10 +335,10 @@ class DatabaseSessionService(BaseSessionService):
|
|
340
335
|
id=session_id,
|
341
336
|
state=session_state,
|
342
337
|
)
|
343
|
-
|
344
|
-
|
338
|
+
session_factory.add(storage_session)
|
339
|
+
session_factory.commit()
|
345
340
|
|
346
|
-
|
341
|
+
session_factory.refresh(storage_session)
|
347
342
|
|
348
343
|
# Merge states for response
|
349
344
|
merged_state = _merge_state(app_state, user_state, session_state)
|
@@ -357,7 +352,7 @@ class DatabaseSessionService(BaseSessionService):
|
|
357
352
|
return session
|
358
353
|
|
359
354
|
@override
|
360
|
-
def get_session(
|
355
|
+
async def get_session(
|
361
356
|
self,
|
362
357
|
*,
|
363
358
|
app_name: str,
|
@@ -368,29 +363,37 @@ class DatabaseSessionService(BaseSessionService):
|
|
368
363
|
# 1. Get the storage session entry from session table
|
369
364
|
# 2. Get all the events based on session id and filtering config
|
370
365
|
# 3. Convert and return the session
|
371
|
-
with self.
|
372
|
-
storage_session =
|
366
|
+
with self.database_session_factory() as session_factory:
|
367
|
+
storage_session = session_factory.get(
|
373
368
|
StorageSession, (app_name, user_id, session_id)
|
374
369
|
)
|
375
370
|
if storage_session is None:
|
376
371
|
return None
|
377
372
|
|
373
|
+
if config and config.after_timestamp:
|
374
|
+
after_dt = datetime.fromtimestamp(
|
375
|
+
config.after_timestamp, tz=timezone.utc
|
376
|
+
)
|
377
|
+
timestamp_filter = StorageEvent.timestamp > after_dt
|
378
|
+
else:
|
379
|
+
timestamp_filter = True
|
380
|
+
|
378
381
|
storage_events = (
|
379
|
-
|
382
|
+
session_factory.query(StorageEvent)
|
380
383
|
.filter(StorageEvent.session_id == storage_session.id)
|
381
|
-
.filter(
|
382
|
-
StorageEvent.timestamp < config.after_timestamp
|
383
|
-
if config
|
384
|
-
else True
|
385
|
-
)
|
386
|
-
.limit(config.num_recent_events if config else None)
|
384
|
+
.filter(timestamp_filter)
|
387
385
|
.order_by(StorageEvent.timestamp.asc())
|
386
|
+
.limit(
|
387
|
+
config.num_recent_events
|
388
|
+
if config and config.num_recent_events
|
389
|
+
else None
|
390
|
+
)
|
388
391
|
.all()
|
389
392
|
)
|
390
393
|
|
391
394
|
# Fetch states from storage
|
392
|
-
storage_app_state =
|
393
|
-
storage_user_state =
|
395
|
+
storage_app_state = session_factory.get(StorageAppState, (app_name))
|
396
|
+
storage_user_state = session_factory.get(
|
394
397
|
StorageUserState, (app_name, user_id)
|
395
398
|
)
|
396
399
|
|
@@ -431,12 +434,12 @@ class DatabaseSessionService(BaseSessionService):
|
|
431
434
|
return session
|
432
435
|
|
433
436
|
@override
|
434
|
-
def list_sessions(
|
437
|
+
async def list_sessions(
|
435
438
|
self, *, app_name: str, user_id: str
|
436
439
|
) -> ListSessionsResponse:
|
437
|
-
with self.
|
440
|
+
with self.database_session_factory() as session_factory:
|
438
441
|
results = (
|
439
|
-
|
442
|
+
session_factory.query(StorageSession)
|
440
443
|
.filter(StorageSession.app_name == app_name)
|
441
444
|
.filter(StorageSession.user_id == user_id)
|
442
445
|
.all()
|
@@ -454,20 +457,20 @@ class DatabaseSessionService(BaseSessionService):
|
|
454
457
|
return ListSessionsResponse(sessions=sessions)
|
455
458
|
|
456
459
|
@override
|
457
|
-
def delete_session(
|
460
|
+
async def delete_session(
|
458
461
|
self, app_name: str, user_id: str, session_id: str
|
459
462
|
) -> None:
|
460
|
-
with self.
|
463
|
+
with self.database_session_factory() as session_factory:
|
461
464
|
stmt = delete(StorageSession).where(
|
462
465
|
StorageSession.app_name == app_name,
|
463
466
|
StorageSession.user_id == user_id,
|
464
467
|
StorageSession.id == session_id,
|
465
468
|
)
|
466
|
-
|
467
|
-
|
469
|
+
session_factory.execute(stmt)
|
470
|
+
session_factory.commit()
|
468
471
|
|
469
472
|
@override
|
470
|
-
def append_event(self, session: Session, event: Event) -> Event:
|
473
|
+
async def append_event(self, session: Session, event: Event) -> Event:
|
471
474
|
logger.info(f"Append event: {event} to session {session.id}")
|
472
475
|
|
473
476
|
if event.partial:
|
@@ -476,22 +479,25 @@ class DatabaseSessionService(BaseSessionService):
|
|
476
479
|
# 1. Check if timestamp is stale
|
477
480
|
# 2. Update session attributes based on event config
|
478
481
|
# 3. Store event to table
|
479
|
-
with self.
|
480
|
-
storage_session =
|
482
|
+
with self.database_session_factory() as session_factory:
|
483
|
+
storage_session = session_factory.get(
|
481
484
|
StorageSession, (session.app_name, session.user_id, session.id)
|
482
485
|
)
|
483
486
|
|
484
487
|
if storage_session.update_time.timestamp() > session.last_update_time:
|
485
488
|
raise ValueError(
|
486
|
-
|
487
|
-
f"
|
489
|
+
"The last_update_time provided in the session object"
|
490
|
+
f" {datetime.fromtimestamp(session.last_update_time):'%Y-%m-%d %H:%M:%S'} is"
|
491
|
+
" earlier than the update_time in the storage_session"
|
492
|
+
f" {storage_session.update_time:'%Y-%m-%d %H:%M:%S'}. Please check"
|
493
|
+
" if it is a stale session."
|
488
494
|
)
|
489
495
|
|
490
496
|
# Fetch states from storage
|
491
|
-
storage_app_state =
|
497
|
+
storage_app_state = session_factory.get(
|
492
498
|
StorageAppState, (session.app_name)
|
493
499
|
)
|
494
|
-
storage_user_state =
|
500
|
+
storage_user_state = session_factory.get(
|
495
501
|
StorageUserState, (session.app_name, session.user_id)
|
496
502
|
)
|
497
503
|
|
@@ -540,27 +546,18 @@ class DatabaseSessionService(BaseSessionService):
|
|
540
546
|
if event.content:
|
541
547
|
storage_event.content = _session_util.encode_content(event.content)
|
542
548
|
|
543
|
-
|
549
|
+
session_factory.add(storage_event)
|
544
550
|
|
545
|
-
|
546
|
-
|
551
|
+
session_factory.commit()
|
552
|
+
session_factory.refresh(storage_session)
|
547
553
|
|
548
554
|
# Update timestamp with commit time
|
549
555
|
session.last_update_time = storage_session.update_time.timestamp()
|
550
556
|
|
551
557
|
# Also update the in-memory session
|
552
|
-
super().append_event(session=session, event=event)
|
558
|
+
await super().append_event(session=session, event=event)
|
553
559
|
return event
|
554
560
|
|
555
|
-
@override
|
556
|
-
def list_events(
|
557
|
-
self,
|
558
|
-
*,
|
559
|
-
app_name: str,
|
560
|
-
user_id: str,
|
561
|
-
session_id: str,
|
562
|
-
) -> ListEventsResponse:
|
563
|
-
raise NotImplementedError()
|
564
561
|
|
565
562
|
def convert_event(event: StorageEvent) -> Event:
|
566
563
|
"""Converts a storage event to an event."""
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
import copy
|
16
|
+
import logging
|
16
17
|
import time
|
17
18
|
from typing import Any
|
18
19
|
from typing import Optional
|
@@ -23,17 +24,19 @@ from typing_extensions import override
|
|
23
24
|
from ..events.event import Event
|
24
25
|
from .base_session_service import BaseSessionService
|
25
26
|
from .base_session_service import GetSessionConfig
|
26
|
-
from .base_session_service import ListEventsResponse
|
27
27
|
from .base_session_service import ListSessionsResponse
|
28
28
|
from .session import Session
|
29
29
|
from .state import State
|
30
30
|
|
31
|
+
logger = logging.getLogger('google_adk.' + __name__)
|
32
|
+
|
31
33
|
|
32
34
|
class InMemorySessionService(BaseSessionService):
|
33
35
|
"""An in-memory implementation of the session service."""
|
34
36
|
|
35
37
|
def __init__(self):
|
36
|
-
# A map from app name to a map from user ID to a map from session ID to
|
38
|
+
# A map from app name to a map from user ID to a map from session ID to
|
39
|
+
# session.
|
37
40
|
self.sessions: dict[str, dict[str, dict[str, Session]]] = {}
|
38
41
|
# A map from app name to a map from user ID to a map from key to the value.
|
39
42
|
self.user_state: dict[str, dict[str, dict[str, Any]]] = {}
|
@@ -41,7 +44,38 @@ class InMemorySessionService(BaseSessionService):
|
|
41
44
|
self.app_state: dict[str, dict[str, Any]] = {}
|
42
45
|
|
43
46
|
@override
|
44
|
-
def create_session(
|
47
|
+
async def create_session(
|
48
|
+
self,
|
49
|
+
*,
|
50
|
+
app_name: str,
|
51
|
+
user_id: str,
|
52
|
+
state: Optional[dict[str, Any]] = None,
|
53
|
+
session_id: Optional[str] = None,
|
54
|
+
) -> Session:
|
55
|
+
return self._create_session_impl(
|
56
|
+
app_name=app_name,
|
57
|
+
user_id=user_id,
|
58
|
+
state=state,
|
59
|
+
session_id=session_id,
|
60
|
+
)
|
61
|
+
|
62
|
+
def create_session_sync(
|
63
|
+
self,
|
64
|
+
*,
|
65
|
+
app_name: str,
|
66
|
+
user_id: str,
|
67
|
+
state: Optional[dict[str, Any]] = None,
|
68
|
+
session_id: Optional[str] = None,
|
69
|
+
) -> Session:
|
70
|
+
logger.warning('Deprecated. Please migrate to the async method.')
|
71
|
+
return self._create_session_impl(
|
72
|
+
app_name=app_name,
|
73
|
+
user_id=user_id,
|
74
|
+
state=state,
|
75
|
+
session_id=session_id,
|
76
|
+
)
|
77
|
+
|
78
|
+
def _create_session_impl(
|
45
79
|
self,
|
46
80
|
*,
|
47
81
|
app_name: str,
|
@@ -72,14 +106,45 @@ class InMemorySessionService(BaseSessionService):
|
|
72
106
|
return self._merge_state(app_name, user_id, copied_session)
|
73
107
|
|
74
108
|
@override
|
75
|
-
def get_session(
|
109
|
+
async def get_session(
|
76
110
|
self,
|
77
111
|
*,
|
78
112
|
app_name: str,
|
79
113
|
user_id: str,
|
80
114
|
session_id: str,
|
81
115
|
config: Optional[GetSessionConfig] = None,
|
82
|
-
) -> Session:
|
116
|
+
) -> Optional[Session]:
|
117
|
+
return self._get_session_impl(
|
118
|
+
app_name=app_name,
|
119
|
+
user_id=user_id,
|
120
|
+
session_id=session_id,
|
121
|
+
config=config,
|
122
|
+
)
|
123
|
+
|
124
|
+
def get_session_sync(
|
125
|
+
self,
|
126
|
+
*,
|
127
|
+
app_name: str,
|
128
|
+
user_id: str,
|
129
|
+
session_id: str,
|
130
|
+
config: Optional[GetSessionConfig] = None,
|
131
|
+
) -> Optional[Session]:
|
132
|
+
logger.warning('Deprecated. Please migrate to the async method.')
|
133
|
+
return self._get_session_impl(
|
134
|
+
app_name=app_name,
|
135
|
+
user_id=user_id,
|
136
|
+
session_id=session_id,
|
137
|
+
config=config,
|
138
|
+
)
|
139
|
+
|
140
|
+
def _get_session_impl(
|
141
|
+
self,
|
142
|
+
*,
|
143
|
+
app_name: str,
|
144
|
+
user_id: str,
|
145
|
+
session_id: str,
|
146
|
+
config: Optional[GetSessionConfig] = None,
|
147
|
+
) -> Optional[Session]:
|
83
148
|
if app_name not in self.sessions:
|
84
149
|
return None
|
85
150
|
if user_id not in self.sessions[app_name]:
|
@@ -95,18 +160,20 @@ class InMemorySessionService(BaseSessionService):
|
|
95
160
|
copied_session.events = copied_session.events[
|
96
161
|
-config.num_recent_events :
|
97
162
|
]
|
98
|
-
|
99
|
-
i = len(
|
163
|
+
if config.after_timestamp:
|
164
|
+
i = len(copied_session.events) - 1
|
100
165
|
while i >= 0:
|
101
166
|
if copied_session.events[i].timestamp < config.after_timestamp:
|
102
167
|
break
|
103
168
|
i -= 1
|
104
169
|
if i >= 0:
|
105
|
-
copied_session.events = copied_session.events[i:]
|
170
|
+
copied_session.events = copied_session.events[i + 1 :]
|
106
171
|
|
107
172
|
return self._merge_state(app_name, user_id, copied_session)
|
108
173
|
|
109
|
-
def _merge_state(
|
174
|
+
def _merge_state(
|
175
|
+
self, app_name: str, user_id: str, copied_session: Session
|
176
|
+
) -> Session:
|
110
177
|
# Merge app state
|
111
178
|
if app_name in self.app_state:
|
112
179
|
for key in self.app_state[app_name].keys():
|
@@ -128,7 +195,18 @@ class InMemorySessionService(BaseSessionService):
|
|
128
195
|
return copied_session
|
129
196
|
|
130
197
|
@override
|
131
|
-
def list_sessions(
|
198
|
+
async def list_sessions(
|
199
|
+
self, *, app_name: str, user_id: str
|
200
|
+
) -> ListSessionsResponse:
|
201
|
+
return self._list_sessions_impl(app_name=app_name, user_id=user_id)
|
202
|
+
|
203
|
+
def list_sessions_sync(
|
204
|
+
self, *, app_name: str, user_id: str
|
205
|
+
) -> ListSessionsResponse:
|
206
|
+
logger.warning('Deprecated. Please migrate to the async method.')
|
207
|
+
return self._list_sessions_impl(app_name=app_name, user_id=user_id)
|
208
|
+
|
209
|
+
def _list_sessions_impl(
|
132
210
|
self, *, app_name: str, user_id: str
|
133
211
|
) -> ListSessionsResponse:
|
134
212
|
empty_response = ListSessionsResponse()
|
@@ -145,12 +223,26 @@ class InMemorySessionService(BaseSessionService):
|
|
145
223
|
sessions_without_events.append(copied_session)
|
146
224
|
return ListSessionsResponse(sessions=sessions_without_events)
|
147
225
|
|
148
|
-
|
149
|
-
|
226
|
+
async def delete_session(
|
227
|
+
self, *, app_name: str, user_id: str, session_id: str
|
228
|
+
) -> None:
|
229
|
+
self._delete_session_impl(
|
230
|
+
app_name=app_name, user_id=user_id, session_id=session_id
|
231
|
+
)
|
232
|
+
|
233
|
+
def delete_session_sync(
|
234
|
+
self, *, app_name: str, user_id: str, session_id: str
|
235
|
+
) -> None:
|
236
|
+
logger.warning('Deprecated. Please migrate to the async method.')
|
237
|
+
self._delete_session_impl(
|
238
|
+
app_name=app_name, user_id=user_id, session_id=session_id
|
239
|
+
)
|
240
|
+
|
241
|
+
def _delete_session_impl(
|
150
242
|
self, *, app_name: str, user_id: str, session_id: str
|
151
243
|
) -> None:
|
152
244
|
if (
|
153
|
-
self.
|
245
|
+
self._get_session_impl(
|
154
246
|
app_name=app_name, user_id=user_id, session_id=session_id
|
155
247
|
)
|
156
248
|
is None
|
@@ -160,9 +252,9 @@ class InMemorySessionService(BaseSessionService):
|
|
160
252
|
self.sessions[app_name][user_id].pop(session_id)
|
161
253
|
|
162
254
|
@override
|
163
|
-
def append_event(self, session: Session, event: Event) -> Event:
|
255
|
+
async def append_event(self, session: Session, event: Event) -> Event:
|
164
256
|
# Update the in-memory session.
|
165
|
-
super().append_event(session=session, event=event)
|
257
|
+
await super().append_event(session=session, event=event)
|
166
258
|
session.last_update_time = event.timestamp
|
167
259
|
|
168
260
|
# Update the storage session
|
@@ -189,18 +281,8 @@ class InMemorySessionService(BaseSessionService):
|
|
189
281
|
] = event.actions.state_delta[key]
|
190
282
|
|
191
283
|
storage_session = self.sessions[app_name][user_id].get(session_id)
|
192
|
-
super().append_event(session=storage_session, event=event)
|
284
|
+
await super().append_event(session=storage_session, event=event)
|
193
285
|
|
194
286
|
storage_session.last_update_time = event.timestamp
|
195
287
|
|
196
288
|
return event
|
197
|
-
|
198
|
-
@override
|
199
|
-
def list_events(
|
200
|
-
self,
|
201
|
-
*,
|
202
|
-
app_name: str,
|
203
|
-
user_id: str,
|
204
|
-
session_id: str,
|
205
|
-
) -> ListEventsResponse:
|
206
|
-
raise NotImplementedError()
|
google/adk/sessions/session.py
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
from typing import Any
|
16
16
|
|
17
|
+
from pydantic import alias_generators
|
17
18
|
from pydantic import BaseModel
|
18
19
|
from pydantic import ConfigDict
|
19
20
|
from pydantic import Field
|
@@ -37,7 +38,10 @@ class Session(BaseModel):
|
|
37
38
|
model_config = ConfigDict(
|
38
39
|
extra='forbid',
|
39
40
|
arbitrary_types_allowed=True,
|
41
|
+
alias_generator=alias_generators.to_camel,
|
42
|
+
populate_by_name=True,
|
40
43
|
)
|
44
|
+
"""The pydantic model config."""
|
41
45
|
|
42
46
|
id: str
|
43
47
|
"""The unique identifier of the session."""
|
@@ -14,7 +14,8 @@
|
|
14
14
|
import logging
|
15
15
|
import re
|
16
16
|
import time
|
17
|
-
from typing import Any
|
17
|
+
from typing import Any
|
18
|
+
from typing import Optional
|
18
19
|
|
19
20
|
from dateutil import parser
|
20
21
|
from google import genai
|
@@ -25,13 +26,11 @@ from ..events.event_actions import EventActions
|
|
25
26
|
from . import _session_util
|
26
27
|
from .base_session_service import BaseSessionService
|
27
28
|
from .base_session_service import GetSessionConfig
|
28
|
-
from .base_session_service import ListEventsResponse
|
29
29
|
from .base_session_service import ListSessionsResponse
|
30
30
|
from .session import Session
|
31
31
|
|
32
|
-
|
33
32
|
isoparse = parser.isoparse
|
34
|
-
logger = logging.getLogger(__name__)
|
33
|
+
logger = logging.getLogger('google_adk.' + __name__)
|
35
34
|
|
36
35
|
|
37
36
|
class VertexAiSessionService(BaseSessionService):
|
@@ -49,7 +48,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
49
48
|
self.api_client = client._api_client
|
50
49
|
|
51
50
|
@override
|
52
|
-
def create_session(
|
51
|
+
async def create_session(
|
53
52
|
self,
|
54
53
|
*,
|
55
54
|
app_name: str,
|
@@ -57,13 +56,19 @@ class VertexAiSessionService(BaseSessionService):
|
|
57
56
|
state: Optional[dict[str, Any]] = None,
|
58
57
|
session_id: Optional[str] = None,
|
59
58
|
) -> Session:
|
59
|
+
if session_id:
|
60
|
+
raise ValueError(
|
61
|
+
'User-provided Session id is not supported for'
|
62
|
+
' VertexAISessionService.'
|
63
|
+
)
|
64
|
+
|
60
65
|
reasoning_engine_id = _parse_reasoning_engine_id(app_name)
|
61
66
|
|
62
67
|
session_json_dict = {'user_id': user_id}
|
63
68
|
if state:
|
64
69
|
session_json_dict['session_state'] = state
|
65
70
|
|
66
|
-
api_response = self.api_client.
|
71
|
+
api_response = await self.api_client.async_request(
|
67
72
|
http_method='POST',
|
68
73
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions',
|
69
74
|
request_dict=session_json_dict,
|
@@ -75,7 +80,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
75
80
|
|
76
81
|
max_retry_attempt = 5
|
77
82
|
while max_retry_attempt >= 0:
|
78
|
-
lro_response = self.api_client.
|
83
|
+
lro_response = await self.api_client.async_request(
|
79
84
|
http_method='GET',
|
80
85
|
path=f'operations/{operation_id}',
|
81
86
|
request_dict={},
|
@@ -88,7 +93,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
88
93
|
max_retry_attempt -= 1
|
89
94
|
|
90
95
|
# Get session resource
|
91
|
-
get_session_api_response = self.api_client.
|
96
|
+
get_session_api_response = await self.api_client.async_request(
|
92
97
|
http_method='GET',
|
93
98
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}',
|
94
99
|
request_dict={},
|
@@ -107,18 +112,18 @@ class VertexAiSessionService(BaseSessionService):
|
|
107
112
|
return session
|
108
113
|
|
109
114
|
@override
|
110
|
-
def get_session(
|
115
|
+
async def get_session(
|
111
116
|
self,
|
112
117
|
*,
|
113
118
|
app_name: str,
|
114
119
|
user_id: str,
|
115
120
|
session_id: str,
|
116
121
|
config: Optional[GetSessionConfig] = None,
|
117
|
-
) -> Session:
|
122
|
+
) -> Optional[Session]:
|
118
123
|
reasoning_engine_id = _parse_reasoning_engine_id(app_name)
|
119
124
|
|
120
125
|
# Get session resource
|
121
|
-
get_session_api_response = self.api_client.
|
126
|
+
get_session_api_response = await self.api_client.async_request(
|
122
127
|
http_method='GET',
|
123
128
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}',
|
124
129
|
request_dict={},
|
@@ -136,7 +141,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
136
141
|
last_update_time=update_timestamp,
|
137
142
|
)
|
138
143
|
|
139
|
-
list_events_api_response = self.api_client.
|
144
|
+
list_events_api_response = await self.api_client.async_request(
|
140
145
|
http_method='GET',
|
141
146
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}/events',
|
142
147
|
request_dict={},
|
@@ -170,7 +175,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
170
175
|
return session
|
171
176
|
|
172
177
|
@override
|
173
|
-
def list_sessions(
|
178
|
+
async def list_sessions(
|
174
179
|
self, *, app_name: str, user_id: str
|
175
180
|
) -> ListSessionsResponse:
|
176
181
|
reasoning_engine_id = _parse_reasoning_engine_id(app_name)
|
@@ -197,50 +202,23 @@ class VertexAiSessionService(BaseSessionService):
|
|
197
202
|
sessions.append(session)
|
198
203
|
return ListSessionsResponse(sessions=sessions)
|
199
204
|
|
200
|
-
def delete_session(
|
205
|
+
async def delete_session(
|
201
206
|
self, *, app_name: str, user_id: str, session_id: str
|
202
207
|
) -> None:
|
203
208
|
reasoning_engine_id = _parse_reasoning_engine_id(app_name)
|
204
|
-
self.api_client.
|
209
|
+
await self.api_client.async_request(
|
205
210
|
http_method='DELETE',
|
206
211
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}',
|
207
212
|
request_dict={},
|
208
213
|
)
|
209
214
|
|
210
215
|
@override
|
211
|
-
def
|
212
|
-
self,
|
213
|
-
*,
|
214
|
-
app_name: str,
|
215
|
-
user_id: str,
|
216
|
-
session_id: str,
|
217
|
-
) -> ListEventsResponse:
|
218
|
-
reasoning_engine_id = _parse_reasoning_engine_id(app_name)
|
219
|
-
api_response = self.api_client.request(
|
220
|
-
http_method='GET',
|
221
|
-
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}/events',
|
222
|
-
request_dict={},
|
223
|
-
)
|
224
|
-
|
225
|
-
logger.info(f'List events response {api_response}')
|
226
|
-
|
227
|
-
# Handles empty response case
|
228
|
-
if api_response.get('httpHeaders', None):
|
229
|
-
return ListEventsResponse()
|
230
|
-
|
231
|
-
session_events = api_response['sessionEvents']
|
232
|
-
|
233
|
-
return ListEventsResponse(
|
234
|
-
events=[_from_api_event(event) for event in session_events]
|
235
|
-
)
|
236
|
-
|
237
|
-
@override
|
238
|
-
def append_event(self, session: Session, event: Event) -> Event:
|
216
|
+
async def append_event(self, session: Session, event: Event) -> Event:
|
239
217
|
# Update the in-memory session.
|
240
|
-
super().append_event(session=session, event=event)
|
218
|
+
await super().append_event(session=session, event=event)
|
241
219
|
|
242
220
|
reasoning_engine_id = _parse_reasoning_engine_id(session.app_name)
|
243
|
-
self.api_client.
|
221
|
+
await self.api_client.async_request(
|
244
222
|
http_method='POST',
|
245
223
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session.id}:appendEvent',
|
246
224
|
request_dict=_convert_event_to_json(event),
|
google/adk/telemetry.py
CHANGED
@@ -111,6 +111,9 @@ def trace_call_llm(
|
|
111
111
|
span.set_attribute(
|
112
112
|
'gcp.vertex.agent.invocation_id', invocation_context.invocation_id
|
113
113
|
)
|
114
|
+
span.set_attribute(
|
115
|
+
'gcp.vertex.agent.session_id', invocation_context.session.id
|
116
|
+
)
|
114
117
|
span.set_attribute('gcp.vertex.agent.event_id', event_id)
|
115
118
|
# Consider removing once GenAI SDK provides a way to record this info.
|
116
119
|
span.set_attribute(
|