google-adk 0.5.0__py3-none-any.whl → 1.1.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.
Files changed (139) hide show
  1. google/adk/agents/base_agent.py +76 -30
  2. google/adk/agents/callback_context.py +2 -6
  3. google/adk/agents/llm_agent.py +122 -30
  4. google/adk/agents/loop_agent.py +1 -1
  5. google/adk/agents/parallel_agent.py +7 -0
  6. google/adk/agents/readonly_context.py +8 -0
  7. google/adk/agents/run_config.py +1 -1
  8. google/adk/agents/sequential_agent.py +31 -0
  9. google/adk/agents/transcription_entry.py +4 -2
  10. google/adk/artifacts/gcs_artifact_service.py +1 -1
  11. google/adk/artifacts/in_memory_artifact_service.py +1 -1
  12. google/adk/auth/auth_credential.py +10 -2
  13. google/adk/auth/auth_preprocessor.py +7 -1
  14. google/adk/auth/auth_tool.py +3 -4
  15. google/adk/cli/agent_graph.py +5 -5
  16. google/adk/cli/browser/index.html +4 -4
  17. google/adk/cli/browser/{main-ULN5R5I5.js → main-PKDNKWJE.js} +59 -60
  18. google/adk/cli/browser/polyfills-B6TNHZQ6.js +17 -0
  19. google/adk/cli/cli.py +10 -9
  20. google/adk/cli/cli_deploy.py +7 -2
  21. google/adk/cli/cli_eval.py +109 -115
  22. google/adk/cli/cli_tools_click.py +179 -67
  23. google/adk/cli/fast_api.py +248 -197
  24. google/adk/cli/utils/agent_loader.py +137 -0
  25. google/adk/cli/utils/cleanup.py +40 -0
  26. google/adk/cli/utils/common.py +23 -0
  27. google/adk/cli/utils/evals.py +83 -0
  28. google/adk/cli/utils/logs.py +8 -5
  29. google/adk/code_executors/__init__.py +3 -1
  30. google/adk/code_executors/built_in_code_executor.py +52 -0
  31. google/adk/code_executors/code_execution_utils.py +2 -1
  32. google/adk/code_executors/container_code_executor.py +0 -1
  33. google/adk/code_executors/vertex_ai_code_executor.py +6 -8
  34. google/adk/evaluation/__init__.py +1 -1
  35. google/adk/evaluation/agent_evaluator.py +168 -128
  36. google/adk/evaluation/eval_case.py +104 -0
  37. google/adk/evaluation/eval_metrics.py +74 -0
  38. google/adk/evaluation/eval_result.py +86 -0
  39. google/adk/evaluation/eval_set.py +39 -0
  40. google/adk/evaluation/eval_set_results_manager.py +47 -0
  41. google/adk/evaluation/eval_sets_manager.py +43 -0
  42. google/adk/evaluation/evaluation_generator.py +88 -113
  43. google/adk/evaluation/evaluator.py +58 -0
  44. google/adk/evaluation/local_eval_set_results_manager.py +113 -0
  45. google/adk/evaluation/local_eval_sets_manager.py +264 -0
  46. google/adk/evaluation/response_evaluator.py +106 -1
  47. google/adk/evaluation/trajectory_evaluator.py +84 -2
  48. google/adk/events/event.py +6 -1
  49. google/adk/events/event_actions.py +6 -1
  50. google/adk/examples/base_example_provider.py +1 -0
  51. google/adk/examples/example_util.py +3 -2
  52. google/adk/flows/llm_flows/_code_execution.py +9 -1
  53. google/adk/flows/llm_flows/audio_transcriber.py +4 -3
  54. google/adk/flows/llm_flows/base_llm_flow.py +58 -21
  55. google/adk/flows/llm_flows/contents.py +3 -1
  56. google/adk/flows/llm_flows/functions.py +9 -8
  57. google/adk/flows/llm_flows/instructions.py +18 -80
  58. google/adk/flows/llm_flows/single_flow.py +2 -2
  59. google/adk/memory/__init__.py +1 -1
  60. google/adk/memory/_utils.py +23 -0
  61. google/adk/memory/base_memory_service.py +23 -21
  62. google/adk/memory/in_memory_memory_service.py +57 -25
  63. google/adk/memory/memory_entry.py +37 -0
  64. google/adk/memory/vertex_ai_rag_memory_service.py +38 -15
  65. google/adk/models/anthropic_llm.py +16 -9
  66. google/adk/models/base_llm.py +2 -1
  67. google/adk/models/base_llm_connection.py +2 -0
  68. google/adk/models/gemini_llm_connection.py +11 -11
  69. google/adk/models/google_llm.py +12 -2
  70. google/adk/models/lite_llm.py +80 -23
  71. google/adk/models/llm_response.py +16 -3
  72. google/adk/models/registry.py +1 -1
  73. google/adk/runners.py +98 -42
  74. google/adk/sessions/__init__.py +1 -1
  75. google/adk/sessions/_session_util.py +2 -1
  76. google/adk/sessions/base_session_service.py +6 -33
  77. google/adk/sessions/database_session_service.py +57 -67
  78. google/adk/sessions/in_memory_session_service.py +106 -24
  79. google/adk/sessions/session.py +3 -0
  80. google/adk/sessions/vertex_ai_session_service.py +44 -51
  81. google/adk/telemetry.py +7 -2
  82. google/adk/tools/__init__.py +4 -7
  83. google/adk/tools/_memory_entry_utils.py +30 -0
  84. google/adk/tools/agent_tool.py +10 -10
  85. google/adk/tools/apihub_tool/apihub_toolset.py +55 -74
  86. google/adk/tools/apihub_tool/clients/apihub_client.py +10 -3
  87. google/adk/tools/apihub_tool/clients/secret_client.py +1 -0
  88. google/adk/tools/application_integration_tool/application_integration_toolset.py +111 -85
  89. google/adk/tools/application_integration_tool/clients/connections_client.py +28 -1
  90. google/adk/tools/application_integration_tool/clients/integration_client.py +7 -5
  91. google/adk/tools/application_integration_tool/integration_connector_tool.py +69 -26
  92. google/adk/tools/base_toolset.py +96 -0
  93. google/adk/tools/bigquery/__init__.py +28 -0
  94. google/adk/tools/bigquery/bigquery_credentials.py +216 -0
  95. google/adk/tools/bigquery/bigquery_tool.py +116 -0
  96. google/adk/tools/{built_in_code_execution_tool.py → enterprise_search_tool.py} +17 -11
  97. google/adk/tools/function_parameter_parse_util.py +9 -2
  98. google/adk/tools/function_tool.py +33 -3
  99. google/adk/tools/get_user_choice_tool.py +1 -0
  100. google/adk/tools/google_api_tool/__init__.py +24 -70
  101. google/adk/tools/google_api_tool/google_api_tool.py +12 -6
  102. google/adk/tools/google_api_tool/{google_api_tool_set.py → google_api_toolset.py} +57 -55
  103. google/adk/tools/google_api_tool/google_api_toolsets.py +108 -0
  104. google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +40 -42
  105. google/adk/tools/google_search_tool.py +2 -2
  106. google/adk/tools/langchain_tool.py +96 -49
  107. google/adk/tools/load_memory_tool.py +14 -5
  108. google/adk/tools/mcp_tool/__init__.py +3 -2
  109. google/adk/tools/mcp_tool/conversion_utils.py +6 -2
  110. google/adk/tools/mcp_tool/mcp_session_manager.py +80 -69
  111. google/adk/tools/mcp_tool/mcp_tool.py +35 -32
  112. google/adk/tools/mcp_tool/mcp_toolset.py +99 -194
  113. google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +1 -3
  114. google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +6 -7
  115. google/adk/tools/openapi_tool/common/common.py +5 -1
  116. google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +7 -2
  117. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +27 -7
  118. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +36 -32
  119. google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +11 -1
  120. google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +1 -1
  121. google/adk/tools/preload_memory_tool.py +27 -18
  122. google/adk/tools/retrieval/__init__.py +1 -1
  123. google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +1 -1
  124. google/adk/tools/toolbox_toolset.py +107 -0
  125. google/adk/tools/transfer_to_agent_tool.py +0 -1
  126. google/adk/utils/__init__.py +13 -0
  127. google/adk/utils/instructions_utils.py +131 -0
  128. google/adk/version.py +1 -1
  129. {google_adk-0.5.0.dist-info → google_adk-1.1.0.dist-info}/METADATA +18 -19
  130. google_adk-1.1.0.dist-info/RECORD +200 -0
  131. google/adk/agents/remote_agent.py +0 -50
  132. google/adk/cli/browser/polyfills-FFHMD2TL.js +0 -18
  133. google/adk/cli/fast_api.py.orig +0 -728
  134. google/adk/tools/google_api_tool/google_api_tool_sets.py +0 -112
  135. google/adk/tools/toolbox_tool.py +0 -46
  136. google_adk-0.5.0.dist-info/RECORD +0 -180
  137. {google_adk-0.5.0.dist-info → google_adk-1.1.0.dist-info}/WHEEL +0 -0
  138. {google_adk-0.5.0.dist-info → google_adk-1.1.0.dist-info}/entry_points.txt +0 -0
  139. {google_adk-0.5.0.dist-info → google_adk-1.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -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 session.
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]:
@@ -102,11 +167,13 @@ class InMemorySessionService(BaseSessionService):
102
167
  break
103
168
  i -= 1
104
169
  if i >= 0:
105
- copied_session.events = copied_session.events[i + 1:]
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(self, app_name: str, user_id: str, copied_session: Session):
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
- @override
149
- def delete_session(
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.get_session(
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()
@@ -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,6 +38,8 @@ 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
  )
41
44
  """The pydantic model config."""
42
45
 
@@ -11,27 +11,27 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ import asyncio
14
15
  import logging
15
16
  import re
16
- import time
17
- from typing import Any, Optional
17
+ from typing import Any
18
+ from typing import Optional
18
19
 
19
20
  from dateutil import parser
20
- from google import genai
21
21
  from typing_extensions import override
22
22
 
23
+ from google import genai
24
+
25
+ from . import _session_util
23
26
  from ..events.event import Event
24
27
  from ..events.event_actions import EventActions
25
- from . import _session_util
26
28
  from .base_session_service import BaseSessionService
27
29
  from .base_session_service import GetSessionConfig
28
- from .base_session_service import ListEventsResponse
29
30
  from .base_session_service import ListSessionsResponse
30
31
  from .session import Session
31
32
 
32
-
33
33
  isoparse = parser.isoparse
34
- logger = logging.getLogger(__name__)
34
+ logger = logging.getLogger('google_adk.' + __name__)
35
35
 
36
36
 
37
37
  class VertexAiSessionService(BaseSessionService):
@@ -49,7 +49,7 @@ class VertexAiSessionService(BaseSessionService):
49
49
  self.api_client = client._api_client
50
50
 
51
51
  @override
52
- def create_session(
52
+ async def create_session(
53
53
  self,
54
54
  *,
55
55
  app_name: str,
@@ -57,13 +57,20 @@ class VertexAiSessionService(BaseSessionService):
57
57
  state: Optional[dict[str, Any]] = None,
58
58
  session_id: Optional[str] = None,
59
59
  ) -> Session:
60
+ if session_id:
61
+ raise ValueError(
62
+ 'User-provided Session id is not supported for'
63
+ ' VertexAISessionService.'
64
+ )
65
+
60
66
  reasoning_engine_id = _parse_reasoning_engine_id(app_name)
61
67
 
62
68
  session_json_dict = {'user_id': user_id}
63
69
  if state:
64
70
  session_json_dict['session_state'] = state
65
71
 
66
- api_response = self.api_client.request(
72
+ api_client = _get_api_client(self.project, self.location)
73
+ api_response = await api_client.async_request(
67
74
  http_method='POST',
68
75
  path=f'reasoningEngines/{reasoning_engine_id}/sessions',
69
76
  request_dict=session_json_dict,
@@ -75,7 +82,7 @@ class VertexAiSessionService(BaseSessionService):
75
82
 
76
83
  max_retry_attempt = 5
77
84
  while max_retry_attempt >= 0:
78
- lro_response = self.api_client.request(
85
+ lro_response = await api_client.async_request(
79
86
  http_method='GET',
80
87
  path=f'operations/{operation_id}',
81
88
  request_dict={},
@@ -84,11 +91,11 @@ class VertexAiSessionService(BaseSessionService):
84
91
  if lro_response.get('done', None):
85
92
  break
86
93
 
87
- time.sleep(1)
94
+ await asyncio.sleep(1)
88
95
  max_retry_attempt -= 1
89
96
 
90
97
  # Get session resource
91
- get_session_api_response = self.api_client.request(
98
+ get_session_api_response = await api_client.async_request(
92
99
  http_method='GET',
93
100
  path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}',
94
101
  request_dict={},
@@ -107,18 +114,19 @@ class VertexAiSessionService(BaseSessionService):
107
114
  return session
108
115
 
109
116
  @override
110
- def get_session(
117
+ async def get_session(
111
118
  self,
112
119
  *,
113
120
  app_name: str,
114
121
  user_id: str,
115
122
  session_id: str,
116
123
  config: Optional[GetSessionConfig] = None,
117
- ) -> Session:
124
+ ) -> Optional[Session]:
118
125
  reasoning_engine_id = _parse_reasoning_engine_id(app_name)
119
126
 
120
127
  # Get session resource
121
- get_session_api_response = self.api_client.request(
128
+ api_client = _get_api_client(self.project, self.location)
129
+ get_session_api_response = await api_client.async_request(
122
130
  http_method='GET',
123
131
  path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}',
124
132
  request_dict={},
@@ -136,7 +144,7 @@ class VertexAiSessionService(BaseSessionService):
136
144
  last_update_time=update_timestamp,
137
145
  )
138
146
 
139
- list_events_api_response = self.api_client.request(
147
+ list_events_api_response = await api_client.async_request(
140
148
  http_method='GET',
141
149
  path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}/events',
142
150
  request_dict={},
@@ -170,12 +178,13 @@ class VertexAiSessionService(BaseSessionService):
170
178
  return session
171
179
 
172
180
  @override
173
- def list_sessions(
181
+ async def list_sessions(
174
182
  self, *, app_name: str, user_id: str
175
183
  ) -> ListSessionsResponse:
176
184
  reasoning_engine_id = _parse_reasoning_engine_id(app_name)
177
185
 
178
- api_response = self.api_client.request(
186
+ api_client = _get_api_client(self.project, self.location)
187
+ api_response = await api_client.async_request(
179
188
  http_method='GET',
180
189
  path=f'reasoningEngines/{reasoning_engine_id}/sessions?filter=user_id={user_id}',
181
190
  request_dict={},
@@ -197,58 +206,42 @@ class VertexAiSessionService(BaseSessionService):
197
206
  sessions.append(session)
198
207
  return ListSessionsResponse(sessions=sessions)
199
208
 
200
- def delete_session(
209
+ async def delete_session(
201
210
  self, *, app_name: str, user_id: str, session_id: str
202
211
  ) -> None:
203
212
  reasoning_engine_id = _parse_reasoning_engine_id(app_name)
204
- self.api_client.request(
213
+ api_client = _get_api_client(self.project, self.location)
214
+ await api_client.async_request(
205
215
  http_method='DELETE',
206
216
  path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}',
207
217
  request_dict={},
208
218
  )
209
219
 
210
220
  @override
211
- def list_events(
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:
221
+ async def append_event(self, session: Session, event: Event) -> Event:
239
222
  # Update the in-memory session.
240
- super().append_event(session=session, event=event)
223
+ await super().append_event(session=session, event=event)
241
224
 
242
225
  reasoning_engine_id = _parse_reasoning_engine_id(session.app_name)
243
- self.api_client.request(
226
+ api_client = _get_api_client(self.project, self.location)
227
+ await api_client.async_request(
244
228
  http_method='POST',
245
229
  path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session.id}:appendEvent',
246
230
  request_dict=_convert_event_to_json(event),
247
231
  )
248
-
249
232
  return event
250
233
 
251
234
 
235
+ def _get_api_client(project: str, location: str):
236
+ """Instantiates an API client for the given project and location.
237
+
238
+ It needs to be instantiated inside each request so that the event loop
239
+ management.
240
+ """
241
+ client = genai.Client(vertexai=True, project=project, location=location)
242
+ return client._api_client
243
+
244
+
252
245
  def _convert_event_to_json(event: Event):
253
246
  metadata_json = {
254
247
  'partial': event.partial,
google/adk/telemetry.py CHANGED
@@ -32,7 +32,6 @@ from .events.event import Event
32
32
  from .models.llm_request import LlmRequest
33
33
  from .models.llm_response import LlmResponse
34
34
 
35
-
36
35
  tracer = trace.get_tracer('gcp.vertex.agent')
37
36
 
38
37
 
@@ -111,11 +110,17 @@ def trace_call_llm(
111
110
  span.set_attribute(
112
111
  'gcp.vertex.agent.invocation_id', invocation_context.invocation_id
113
112
  )
113
+ span.set_attribute(
114
+ 'gcp.vertex.agent.session_id', invocation_context.session.id
115
+ )
114
116
  span.set_attribute('gcp.vertex.agent.event_id', event_id)
115
117
  # Consider removing once GenAI SDK provides a way to record this info.
116
118
  span.set_attribute(
117
119
  'gcp.vertex.agent.llm_request',
118
- json.dumps(_build_llm_request_for_trace(llm_request)),
120
+ json.dumps(
121
+ _build_llm_request_for_trace(llm_request),
122
+ default=lambda o: '<not serializable>',
123
+ ),
119
124
  )
120
125
  # Consider removing once GenAI SDK provides a way to record this info.
121
126
  span.set_attribute(
@@ -11,31 +11,28 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- # pylint: disable=g-bad-import-order
15
- from .base_tool import BaseTool
14
+
16
15
 
17
16
  from ..auth.auth_tool import AuthToolArguments
18
17
  from .apihub_tool.apihub_toolset import APIHubToolset
19
- from .built_in_code_execution_tool import built_in_code_execution
20
- from .google_search_tool import google_search
21
- from .vertex_ai_search_tool import VertexAiSearchTool
18
+ from .base_tool import BaseTool
22
19
  from .example_tool import ExampleTool
23
20
  from .exit_loop_tool import exit_loop
24
21
  from .function_tool import FunctionTool
25
22
  from .get_user_choice_tool import get_user_choice_tool as get_user_choice
23
+ from .google_search_tool import google_search
26
24
  from .load_artifacts_tool import load_artifacts_tool as load_artifacts
27
25
  from .load_memory_tool import load_memory_tool as load_memory
28
26
  from .long_running_tool import LongRunningFunctionTool
29
27
  from .preload_memory_tool import preload_memory_tool as preload_memory
30
28
  from .tool_context import ToolContext
31
29
  from .transfer_to_agent_tool import transfer_to_agent
32
-
30
+ from .vertex_ai_search_tool import VertexAiSearchTool
33
31
 
34
32
  __all__ = [
35
33
  'APIHubToolset',
36
34
  'AuthToolArguments',
37
35
  'BaseTool',
38
- 'built_in_code_execution',
39
36
  'google_search',
40
37
  'VertexAiSearchTool',
41
38
  'ExampleTool',
@@ -0,0 +1,30 @@
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
+
16
+ from __future__ import annotations
17
+
18
+ from typing import TYPE_CHECKING
19
+
20
+ if TYPE_CHECKING:
21
+ from ..memory.memory_entry import MemoryEntry
22
+
23
+
24
+ def extract_text(memory: MemoryEntry, splitter: str = ' ') -> str:
25
+ """Extracts the text from the memory entry."""
26
+ if not memory.content.parts:
27
+ return ''
28
+ return splitter.join(
29
+ [part.text for part in memory.content.parts if part.text]
30
+ )
@@ -21,10 +21,10 @@ from google.genai import types
21
21
  from pydantic import model_validator
22
22
  from typing_extensions import override
23
23
 
24
+ from . import _automatic_function_calling_util
24
25
  from ..memory.in_memory_memory_service import InMemoryMemoryService
25
26
  from ..runners import Runner
26
27
  from ..sessions.in_memory_session_service import InMemorySessionService
27
- from . import _automatic_function_calling_util
28
28
  from .base_tool import BaseTool
29
29
  from .tool_context import ToolContext
30
30
 
@@ -129,7 +129,7 @@ class AgentTool(BaseTool):
129
129
  session_service=InMemorySessionService(),
130
130
  memory_service=InMemoryMemoryService(),
131
131
  )
132
- session = runner.session_service.create_session(
132
+ session = await runner.session_service.create_session(
133
133
  app_name=self.agent.name,
134
134
  user_id='tmp_user',
135
135
  state=tool_context.state.to_dict(),
@@ -162,17 +162,17 @@ class AgentTool(BaseTool):
162
162
  filename=artifact_name, artifact=artifact
163
163
  )
164
164
 
165
- if (
166
- not last_event
167
- or not last_event.content
168
- or not last_event.content.parts
169
- or not last_event.content.parts[0].text
170
- ):
165
+ if not last_event or not last_event.content or not last_event.content.parts:
171
166
  return ''
172
167
  if isinstance(self.agent, LlmAgent) and self.agent.output_schema:
168
+ merged_text = '\n'.join(
169
+ [p.text for p in last_event.content.parts if p.text]
170
+ )
173
171
  tool_result = self.agent.output_schema.model_validate_json(
174
- last_event.content.parts[0].text
172
+ merged_text
175
173
  ).model_dump(exclude_none=True)
176
174
  else:
177
- tool_result = last_event.content.parts[0].text
175
+ tool_result = '\n'.join(
176
+ [p.text for p in last_event.content.parts if p.text]
177
+ )
178
178
  return tool_result