google-adk 1.4.0__py3-none-any.whl → 1.4.2__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/a2a/converters/event_converter.py +382 -0
- google/adk/a2a/converters/part_converter.py +16 -3
- google/adk/a2a/converters/utils.py +34 -0
- google/adk/auth/auth_credential.py +0 -1
- google/adk/auth/credential_manager.py +1 -5
- google/adk/auth/exchanger/__init__.py +0 -2
- google/adk/auth/refresher/oauth2_credential_refresher.py +2 -30
- google/adk/flows/llm_flows/functions.py +1 -2
- google/adk/sessions/vertex_ai_session_service.py +18 -2
- google/adk/tools/bigquery/bigquery_credentials.py +28 -11
- google/adk/tools/bigquery/bigquery_tool.py +1 -1
- google/adk/tools/bigquery/client.py +1 -1
- google/adk/tools/bigquery/metadata_tool.py +1 -1
- google/adk/tools/bigquery/query_tool.py +1 -1
- google/adk/tools/mcp_tool/mcp_tool.py +2 -8
- google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +0 -1
- google/adk/version.py +1 -1
- {google_adk-1.4.0.dist-info → google_adk-1.4.2.dist-info}/METADATA +1 -1
- {google_adk-1.4.0.dist-info → google_adk-1.4.2.dist-info}/RECORD +22 -21
- google/adk/auth/exchanger/service_account_credential_exchanger.py +0 -104
- {google_adk-1.4.0.dist-info → google_adk-1.4.2.dist-info}/WHEEL +0 -0
- {google_adk-1.4.0.dist-info → google_adk-1.4.2.dist-info}/entry_points.txt +0 -0
- {google_adk-1.4.0.dist-info → google_adk-1.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,382 @@
|
|
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
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
import datetime
|
18
|
+
import logging
|
19
|
+
from typing import Any
|
20
|
+
from typing import Dict
|
21
|
+
from typing import List
|
22
|
+
from typing import Optional
|
23
|
+
import uuid
|
24
|
+
|
25
|
+
from a2a.server.events import Event as A2AEvent
|
26
|
+
from a2a.types import Artifact
|
27
|
+
from a2a.types import DataPart
|
28
|
+
from a2a.types import Message
|
29
|
+
from a2a.types import Role
|
30
|
+
from a2a.types import TaskArtifactUpdateEvent
|
31
|
+
from a2a.types import TaskState
|
32
|
+
from a2a.types import TaskStatus
|
33
|
+
from a2a.types import TaskStatusUpdateEvent
|
34
|
+
from a2a.types import TextPart
|
35
|
+
|
36
|
+
from ...agents.invocation_context import InvocationContext
|
37
|
+
from ...events.event import Event
|
38
|
+
from ...utils.feature_decorator import working_in_progress
|
39
|
+
from .part_converter import A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
|
40
|
+
from .part_converter import A2A_DATA_PART_METADATA_TYPE_KEY
|
41
|
+
from .part_converter import convert_genai_part_to_a2a_part
|
42
|
+
from .utils import _get_adk_metadata_key
|
43
|
+
|
44
|
+
# Constants
|
45
|
+
|
46
|
+
ARTIFACT_ID_SEPARATOR = "-"
|
47
|
+
DEFAULT_ERROR_MESSAGE = "An error occurred during processing"
|
48
|
+
|
49
|
+
# Logger
|
50
|
+
logger = logging.getLogger("google_adk." + __name__)
|
51
|
+
|
52
|
+
|
53
|
+
def _serialize_metadata_value(value: Any) -> str:
|
54
|
+
"""Safely serializes metadata values to string format.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
value: The value to serialize.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
String representation of the value.
|
61
|
+
"""
|
62
|
+
if hasattr(value, "model_dump"):
|
63
|
+
try:
|
64
|
+
return value.model_dump(exclude_none=True, by_alias=True)
|
65
|
+
except Exception as e:
|
66
|
+
logger.warning("Failed to serialize metadata value: %s", e)
|
67
|
+
return str(value)
|
68
|
+
return str(value)
|
69
|
+
|
70
|
+
|
71
|
+
def _get_context_metadata(
|
72
|
+
event: Event, invocation_context: InvocationContext
|
73
|
+
) -> Dict[str, str]:
|
74
|
+
"""Gets the context metadata for the event.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
event: The ADK event to extract metadata from.
|
78
|
+
invocation_context: The invocation context containing session information.
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
A dictionary containing the context metadata.
|
82
|
+
|
83
|
+
Raises:
|
84
|
+
ValueError: If required fields are missing from event or context.
|
85
|
+
"""
|
86
|
+
if not event:
|
87
|
+
raise ValueError("Event cannot be None")
|
88
|
+
if not invocation_context:
|
89
|
+
raise ValueError("Invocation context cannot be None")
|
90
|
+
|
91
|
+
try:
|
92
|
+
metadata = {
|
93
|
+
_get_adk_metadata_key("app_name"): invocation_context.app_name,
|
94
|
+
_get_adk_metadata_key("user_id"): invocation_context.user_id,
|
95
|
+
_get_adk_metadata_key("session_id"): invocation_context.session.id,
|
96
|
+
_get_adk_metadata_key("invocation_id"): event.invocation_id,
|
97
|
+
_get_adk_metadata_key("author"): event.author,
|
98
|
+
}
|
99
|
+
|
100
|
+
# Add optional metadata fields if present
|
101
|
+
optional_fields = [
|
102
|
+
("branch", event.branch),
|
103
|
+
("grounding_metadata", event.grounding_metadata),
|
104
|
+
("custom_metadata", event.custom_metadata),
|
105
|
+
("usage_metadata", event.usage_metadata),
|
106
|
+
("error_code", event.error_code),
|
107
|
+
]
|
108
|
+
|
109
|
+
for field_name, field_value in optional_fields:
|
110
|
+
if field_value is not None:
|
111
|
+
metadata[_get_adk_metadata_key(field_name)] = _serialize_metadata_value(
|
112
|
+
field_value
|
113
|
+
)
|
114
|
+
|
115
|
+
return metadata
|
116
|
+
|
117
|
+
except Exception as e:
|
118
|
+
logger.error("Failed to create context metadata: %s", e)
|
119
|
+
raise
|
120
|
+
|
121
|
+
|
122
|
+
def _create_artifact_id(
|
123
|
+
app_name: str, user_id: str, session_id: str, filename: str, version: int
|
124
|
+
) -> str:
|
125
|
+
"""Creates a unique artifact ID.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
app_name: The application name.
|
129
|
+
user_id: The user ID.
|
130
|
+
session_id: The session ID.
|
131
|
+
filename: The artifact filename.
|
132
|
+
version: The artifact version.
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
A unique artifact ID string.
|
136
|
+
"""
|
137
|
+
components = [app_name, user_id, session_id, filename, str(version)]
|
138
|
+
return ARTIFACT_ID_SEPARATOR.join(components)
|
139
|
+
|
140
|
+
|
141
|
+
def _convert_artifact_to_a2a_events(
|
142
|
+
event: Event,
|
143
|
+
invocation_context: InvocationContext,
|
144
|
+
filename: str,
|
145
|
+
version: int,
|
146
|
+
) -> TaskArtifactUpdateEvent:
|
147
|
+
"""Converts a new artifact version to an A2A TaskArtifactUpdateEvent.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
event: The ADK event containing the artifact information.
|
151
|
+
invocation_context: The invocation context.
|
152
|
+
filename: The name of the artifact file.
|
153
|
+
version: The version number of the artifact.
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
A TaskArtifactUpdateEvent representing the artifact update.
|
157
|
+
|
158
|
+
Raises:
|
159
|
+
ValueError: If required parameters are invalid.
|
160
|
+
RuntimeError: If artifact loading fails.
|
161
|
+
"""
|
162
|
+
if not filename:
|
163
|
+
raise ValueError("Filename cannot be empty")
|
164
|
+
if version < 0:
|
165
|
+
raise ValueError("Version must be non-negative")
|
166
|
+
|
167
|
+
try:
|
168
|
+
artifact_part = invocation_context.artifact_service.load_artifact(
|
169
|
+
app_name=invocation_context.app_name,
|
170
|
+
user_id=invocation_context.user_id,
|
171
|
+
session_id=invocation_context.session.id,
|
172
|
+
filename=filename,
|
173
|
+
version=version,
|
174
|
+
)
|
175
|
+
|
176
|
+
converted_part = convert_genai_part_to_a2a_part(part=artifact_part)
|
177
|
+
if not converted_part:
|
178
|
+
raise RuntimeError(f"Failed to convert artifact part for {filename}")
|
179
|
+
|
180
|
+
artifact_id = _create_artifact_id(
|
181
|
+
invocation_context.app_name,
|
182
|
+
invocation_context.user_id,
|
183
|
+
invocation_context.session.id,
|
184
|
+
filename,
|
185
|
+
version,
|
186
|
+
)
|
187
|
+
|
188
|
+
return TaskArtifactUpdateEvent(
|
189
|
+
taskId=str(uuid.uuid4()),
|
190
|
+
append=False,
|
191
|
+
contextId=invocation_context.session.id,
|
192
|
+
lastChunk=True,
|
193
|
+
artifact=Artifact(
|
194
|
+
artifactId=artifact_id,
|
195
|
+
name=filename,
|
196
|
+
metadata={
|
197
|
+
"filename": filename,
|
198
|
+
"version": version,
|
199
|
+
},
|
200
|
+
parts=[converted_part],
|
201
|
+
),
|
202
|
+
)
|
203
|
+
except Exception as e:
|
204
|
+
logger.error(
|
205
|
+
"Failed to convert artifact for %s, version %s: %s",
|
206
|
+
filename,
|
207
|
+
version,
|
208
|
+
e,
|
209
|
+
)
|
210
|
+
raise RuntimeError(f"Artifact conversion failed: {e}") from e
|
211
|
+
|
212
|
+
|
213
|
+
def _process_long_running_tool(a2a_part, event: Event) -> None:
|
214
|
+
"""Processes long-running tool metadata for an A2A part.
|
215
|
+
|
216
|
+
Args:
|
217
|
+
a2a_part: The A2A part to potentially mark as long-running.
|
218
|
+
event: The ADK event containing long-running tool information.
|
219
|
+
"""
|
220
|
+
if (
|
221
|
+
isinstance(a2a_part.root, DataPart)
|
222
|
+
and event.long_running_tool_ids
|
223
|
+
and a2a_part.root.metadata.get(
|
224
|
+
_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)
|
225
|
+
)
|
226
|
+
== A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
|
227
|
+
and a2a_part.root.metadata.get("id") in event.long_running_tool_ids
|
228
|
+
):
|
229
|
+
a2a_part.root.metadata[_get_adk_metadata_key("is_long_running")] = True
|
230
|
+
|
231
|
+
|
232
|
+
@working_in_progress
|
233
|
+
def convert_event_to_a2a_status_message(
|
234
|
+
event: Event, invocation_context: InvocationContext
|
235
|
+
) -> Optional[Message]:
|
236
|
+
"""Converts an ADK event to an A2A message.
|
237
|
+
|
238
|
+
Args:
|
239
|
+
event: The ADK event to convert.
|
240
|
+
invocation_context: The invocation context.
|
241
|
+
|
242
|
+
Returns:
|
243
|
+
An A2A Message if the event has content, None otherwise.
|
244
|
+
|
245
|
+
Raises:
|
246
|
+
ValueError: If required parameters are invalid.
|
247
|
+
"""
|
248
|
+
if not event:
|
249
|
+
raise ValueError("Event cannot be None")
|
250
|
+
if not invocation_context:
|
251
|
+
raise ValueError("Invocation context cannot be None")
|
252
|
+
|
253
|
+
if not event.content or not event.content.parts:
|
254
|
+
return None
|
255
|
+
|
256
|
+
try:
|
257
|
+
a2a_parts = []
|
258
|
+
for part in event.content.parts:
|
259
|
+
a2a_part = convert_genai_part_to_a2a_part(part)
|
260
|
+
if a2a_part:
|
261
|
+
a2a_parts.append(a2a_part)
|
262
|
+
_process_long_running_tool(a2a_part, event)
|
263
|
+
|
264
|
+
if a2a_parts:
|
265
|
+
return Message(
|
266
|
+
messageId=str(uuid.uuid4()), role=Role.agent, parts=a2a_parts
|
267
|
+
)
|
268
|
+
|
269
|
+
except Exception as e:
|
270
|
+
logger.error("Failed to convert event to status message: %s", e)
|
271
|
+
raise
|
272
|
+
|
273
|
+
return None
|
274
|
+
|
275
|
+
|
276
|
+
def _create_error_status_event(
|
277
|
+
event: Event, invocation_context: InvocationContext
|
278
|
+
) -> TaskStatusUpdateEvent:
|
279
|
+
"""Creates a TaskStatusUpdateEvent for error scenarios.
|
280
|
+
|
281
|
+
Args:
|
282
|
+
event: The ADK event containing error information.
|
283
|
+
invocation_context: The invocation context.
|
284
|
+
|
285
|
+
Returns:
|
286
|
+
A TaskStatusUpdateEvent with FAILED state.
|
287
|
+
"""
|
288
|
+
error_message = getattr(event, "error_message", None) or DEFAULT_ERROR_MESSAGE
|
289
|
+
|
290
|
+
return TaskStatusUpdateEvent(
|
291
|
+
taskId=str(uuid.uuid4()),
|
292
|
+
contextId=invocation_context.session.id,
|
293
|
+
final=False,
|
294
|
+
metadata=_get_context_metadata(event, invocation_context),
|
295
|
+
status=TaskStatus(
|
296
|
+
state=TaskState.failed,
|
297
|
+
message=Message(
|
298
|
+
messageId=str(uuid.uuid4()),
|
299
|
+
role=Role.agent,
|
300
|
+
parts=[TextPart(text=error_message)],
|
301
|
+
),
|
302
|
+
timestamp=datetime.datetime.now().isoformat(),
|
303
|
+
),
|
304
|
+
)
|
305
|
+
|
306
|
+
|
307
|
+
def _create_running_status_event(
|
308
|
+
message: Message, invocation_context: InvocationContext, event: Event
|
309
|
+
) -> TaskStatusUpdateEvent:
|
310
|
+
"""Creates a TaskStatusUpdateEvent for running scenarios.
|
311
|
+
|
312
|
+
Args:
|
313
|
+
message: The A2A message to include.
|
314
|
+
invocation_context: The invocation context.
|
315
|
+
event: The ADK event.
|
316
|
+
|
317
|
+
Returns:
|
318
|
+
A TaskStatusUpdateEvent with RUNNING state.
|
319
|
+
"""
|
320
|
+
return TaskStatusUpdateEvent(
|
321
|
+
taskId=str(uuid.uuid4()),
|
322
|
+
contextId=invocation_context.session.id,
|
323
|
+
final=False,
|
324
|
+
status=TaskStatus(
|
325
|
+
state=TaskState.working,
|
326
|
+
message=message,
|
327
|
+
timestamp=datetime.datetime.now().isoformat(),
|
328
|
+
),
|
329
|
+
metadata=_get_context_metadata(event, invocation_context),
|
330
|
+
)
|
331
|
+
|
332
|
+
|
333
|
+
@working_in_progress
|
334
|
+
def convert_event_to_a2a_events(
|
335
|
+
event: Event, invocation_context: InvocationContext
|
336
|
+
) -> List[A2AEvent]:
|
337
|
+
"""Converts a GenAI event to a list of A2A events.
|
338
|
+
|
339
|
+
Args:
|
340
|
+
event: The ADK event to convert.
|
341
|
+
invocation_context: The invocation context.
|
342
|
+
|
343
|
+
Returns:
|
344
|
+
A list of A2A events representing the converted ADK event.
|
345
|
+
|
346
|
+
Raises:
|
347
|
+
ValueError: If required parameters are invalid.
|
348
|
+
"""
|
349
|
+
if not event:
|
350
|
+
raise ValueError("Event cannot be None")
|
351
|
+
if not invocation_context:
|
352
|
+
raise ValueError("Invocation context cannot be None")
|
353
|
+
|
354
|
+
a2a_events = []
|
355
|
+
|
356
|
+
try:
|
357
|
+
# Handle artifact deltas
|
358
|
+
if event.actions.artifact_delta:
|
359
|
+
for filename, version in event.actions.artifact_delta.items():
|
360
|
+
artifact_event = _convert_artifact_to_a2a_events(
|
361
|
+
event, invocation_context, filename, version
|
362
|
+
)
|
363
|
+
a2a_events.append(artifact_event)
|
364
|
+
|
365
|
+
# Handle error scenarios
|
366
|
+
if event.error_code:
|
367
|
+
error_event = _create_error_status_event(event, invocation_context)
|
368
|
+
a2a_events.append(error_event)
|
369
|
+
|
370
|
+
# Handle regular message content
|
371
|
+
message = convert_event_to_a2a_status_message(event, invocation_context)
|
372
|
+
if message:
|
373
|
+
running_event = _create_running_status_event(
|
374
|
+
message, invocation_context, event
|
375
|
+
)
|
376
|
+
a2a_events.append(running_event)
|
377
|
+
|
378
|
+
except Exception as e:
|
379
|
+
logger.error("Failed to convert event to A2A events: %s", e)
|
380
|
+
raise
|
381
|
+
|
382
|
+
return a2a_events
|
@@ -20,9 +20,22 @@ from __future__ import annotations
|
|
20
20
|
|
21
21
|
import json
|
22
22
|
import logging
|
23
|
+
import sys
|
23
24
|
from typing import Optional
|
24
25
|
|
25
|
-
from
|
26
|
+
from .utils import _get_adk_metadata_key
|
27
|
+
|
28
|
+
try:
|
29
|
+
from a2a import types as a2a_types
|
30
|
+
except ImportError as e:
|
31
|
+
if sys.version_info < (3, 10):
|
32
|
+
raise ImportError(
|
33
|
+
'A2A Tool requires Python 3.10 or above. Please upgrade your Python'
|
34
|
+
' version.'
|
35
|
+
) from e
|
36
|
+
else:
|
37
|
+
raise e
|
38
|
+
|
26
39
|
from google.genai import types as genai_types
|
27
40
|
|
28
41
|
from ...utils.feature_decorator import working_in_progress
|
@@ -73,7 +86,7 @@ def convert_a2a_part_to_genai_part(
|
|
73
86
|
# logic accordinlgy
|
74
87
|
if part.metadata and A2A_DATA_PART_METADATA_TYPE_KEY in part.metadata:
|
75
88
|
if (
|
76
|
-
part.metadata[A2A_DATA_PART_METADATA_TYPE_KEY]
|
89
|
+
part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
|
77
90
|
== A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
|
78
91
|
):
|
79
92
|
return genai_types.Part(
|
@@ -82,7 +95,7 @@ def convert_a2a_part_to_genai_part(
|
|
82
95
|
)
|
83
96
|
)
|
84
97
|
if (
|
85
|
-
part.metadata[A2A_DATA_PART_METADATA_TYPE_KEY]
|
98
|
+
part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
|
86
99
|
== A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
|
87
100
|
):
|
88
101
|
return genai_types.Part(
|
@@ -0,0 +1,34 @@
|
|
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
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
ADK_METADATA_KEY_PREFIX = "adk_"
|
18
|
+
|
19
|
+
|
20
|
+
def _get_adk_metadata_key(key: str) -> str:
|
21
|
+
"""Gets the A2A event metadata key for the given key.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
key: The metadata key to prefix.
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
The prefixed metadata key.
|
28
|
+
|
29
|
+
Raises:
|
30
|
+
ValueError: If key is empty or None.
|
31
|
+
"""
|
32
|
+
if not key:
|
33
|
+
raise ValueError("Metadata key cannot be empty or None")
|
34
|
+
return f"{ADK_METADATA_KEY_PREFIX}{key}"
|
@@ -76,11 +76,7 @@ class CredentialManager:
|
|
76
76
|
self._refresher_registry = CredentialRefresherRegistry()
|
77
77
|
|
78
78
|
# Register default exchangers and refreshers
|
79
|
-
|
80
|
-
|
81
|
-
self._exchanger_registry.register(
|
82
|
-
AuthCredentialTypes.SERVICE_ACCOUNT, ServiceAccountCredentialExchanger()
|
83
|
-
)
|
79
|
+
# TODO: support service account credential exchanger
|
84
80
|
from .refresher.oauth2_credential_refresher import OAuth2CredentialRefresher
|
85
81
|
|
86
82
|
oauth2_refresher = OAuth2CredentialRefresher()
|
@@ -15,9 +15,7 @@
|
|
15
15
|
"""Credential exchanger module."""
|
16
16
|
|
17
17
|
from .base_credential_exchanger import BaseCredentialExchanger
|
18
|
-
from .service_account_credential_exchanger import ServiceAccountCredentialExchanger
|
19
18
|
|
20
19
|
__all__ = [
|
21
20
|
"BaseCredentialExchanger",
|
22
|
-
"ServiceAccountCredentialExchanger",
|
23
21
|
]
|
@@ -60,27 +60,12 @@ class OAuth2CredentialRefresher(BaseCredentialRefresher):
|
|
60
60
|
Returns:
|
61
61
|
True if the credential needs to be refreshed, False otherwise.
|
62
62
|
"""
|
63
|
-
# Handle Google OAuth2 credentials (from service account exchange)
|
64
|
-
if auth_credential.google_oauth2_json:
|
65
|
-
try:
|
66
|
-
google_credential = Credentials.from_authorized_user_info(
|
67
|
-
json.loads(auth_credential.google_oauth2_json)
|
68
|
-
)
|
69
|
-
return google_credential.expired and bool(
|
70
|
-
google_credential.refresh_token
|
71
|
-
)
|
72
|
-
except Exception as e:
|
73
|
-
logger.warning("Failed to parse Google OAuth2 JSON credential: %s", e)
|
74
|
-
return False
|
75
63
|
|
76
64
|
# Handle regular OAuth2 credentials
|
77
|
-
|
65
|
+
if auth_credential.oauth2:
|
78
66
|
if not AUTHLIB_AVIALABLE:
|
79
67
|
return False
|
80
68
|
|
81
|
-
if not auth_credential.oauth2:
|
82
|
-
return False
|
83
|
-
|
84
69
|
return OAuth2Token({
|
85
70
|
"expires_at": auth_credential.oauth2.expires_at,
|
86
71
|
"expires_in": auth_credential.oauth2.expires_in,
|
@@ -105,22 +90,9 @@ class OAuth2CredentialRefresher(BaseCredentialRefresher):
|
|
105
90
|
The refreshed credential.
|
106
91
|
|
107
92
|
"""
|
108
|
-
# Handle Google OAuth2 credentials (from service account exchange)
|
109
|
-
if auth_credential.google_oauth2_json:
|
110
|
-
try:
|
111
|
-
google_credential = Credentials.from_authorized_user_info(
|
112
|
-
json.loads(auth_credential.google_oauth2_json)
|
113
|
-
)
|
114
|
-
if google_credential.expired and google_credential.refresh_token:
|
115
|
-
google_credential.refresh(Request())
|
116
|
-
auth_credential.google_oauth2_json = google_credential.to_json()
|
117
|
-
logger.info("Successfully refreshed Google OAuth2 JSON credential")
|
118
|
-
except Exception as e:
|
119
|
-
# TODO reconsider whether we should raise error when refresh failed.
|
120
|
-
logger.error("Failed to refresh Google OAuth2 JSON credential: %s", e)
|
121
93
|
|
122
94
|
# Handle regular OAuth2 credentials
|
123
|
-
|
95
|
+
if auth_credential.oauth2 and auth_scheme:
|
124
96
|
if not AUTHLIB_AVIALABLE:
|
125
97
|
return auth_credential
|
126
98
|
|
@@ -288,8 +288,7 @@ async def handle_function_calls_live(
|
|
288
288
|
trace_tool_call(
|
289
289
|
tool=tool,
|
290
290
|
args=function_args,
|
291
|
-
|
292
|
-
function_response=function_response,
|
291
|
+
function_response_event=function_response_event,
|
293
292
|
)
|
294
293
|
function_response_events.append(function_response_event)
|
295
294
|
|
@@ -14,6 +14,7 @@
|
|
14
14
|
from __future__ import annotations
|
15
15
|
|
16
16
|
import asyncio
|
17
|
+
import json
|
17
18
|
import logging
|
18
19
|
import re
|
19
20
|
from typing import Any
|
@@ -87,6 +88,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
87
88
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions',
|
88
89
|
request_dict=session_json_dict,
|
89
90
|
)
|
91
|
+
api_response = _convert_api_response(api_response)
|
90
92
|
logger.info(f'Create Session response {api_response}')
|
91
93
|
|
92
94
|
session_id = api_response['name'].split('/')[-3]
|
@@ -100,6 +102,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
100
102
|
path=f'operations/{operation_id}',
|
101
103
|
request_dict={},
|
102
104
|
)
|
105
|
+
lro_response = _convert_api_response(lro_response)
|
103
106
|
|
104
107
|
if lro_response.get('done', None):
|
105
108
|
break
|
@@ -118,6 +121,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
118
121
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}',
|
119
122
|
request_dict={},
|
120
123
|
)
|
124
|
+
get_session_api_response = _convert_api_response(get_session_api_response)
|
121
125
|
|
122
126
|
update_timestamp = isoparse(
|
123
127
|
get_session_api_response['updateTime']
|
@@ -149,6 +153,7 @@ class VertexAiSessionService(BaseSessionService):
|
|
149
153
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}',
|
150
154
|
request_dict={},
|
151
155
|
)
|
156
|
+
get_session_api_response = _convert_api_response(get_session_api_response)
|
152
157
|
|
153
158
|
session_id = get_session_api_response['name'].split('/')[-1]
|
154
159
|
update_timestamp = isoparse(
|
@@ -167,9 +172,12 @@ class VertexAiSessionService(BaseSessionService):
|
|
167
172
|
path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}/events',
|
168
173
|
request_dict={},
|
169
174
|
)
|
175
|
+
list_events_api_response = _convert_api_response(list_events_api_response)
|
170
176
|
|
171
177
|
# Handles empty response case
|
172
|
-
if list_events_api_response.get(
|
178
|
+
if not list_events_api_response or list_events_api_response.get(
|
179
|
+
'httpHeaders', None
|
180
|
+
):
|
173
181
|
return session
|
174
182
|
|
175
183
|
session.events += [
|
@@ -226,9 +234,10 @@ class VertexAiSessionService(BaseSessionService):
|
|
226
234
|
path=path,
|
227
235
|
request_dict={},
|
228
236
|
)
|
237
|
+
api_response = _convert_api_response(api_response)
|
229
238
|
|
230
239
|
# Handles empty response case
|
231
|
-
if api_response.get('httpHeaders', None):
|
240
|
+
if not api_response or api_response.get('httpHeaders', None):
|
232
241
|
return ListSessionsResponse()
|
233
242
|
|
234
243
|
sessions = []
|
@@ -303,6 +312,13 @@ class VertexAiSessionService(BaseSessionService):
|
|
303
312
|
return client._api_client
|
304
313
|
|
305
314
|
|
315
|
+
def _convert_api_response(api_response):
|
316
|
+
"""Converts the API response to a JSON object based on the type."""
|
317
|
+
if hasattr(api_response, 'body'):
|
318
|
+
return json.loads(api_response.body)
|
319
|
+
return api_response
|
320
|
+
|
321
|
+
|
306
322
|
def _convert_event_to_json(event: Event) -> Dict[str, Any]:
|
307
323
|
metadata_json = {
|
308
324
|
'partial': event.partial,
|
@@ -21,9 +21,10 @@ from typing import Optional
|
|
21
21
|
from fastapi.openapi.models import OAuth2
|
22
22
|
from fastapi.openapi.models import OAuthFlowAuthorizationCode
|
23
23
|
from fastapi.openapi.models import OAuthFlows
|
24
|
+
import google.auth.credentials
|
24
25
|
from google.auth.exceptions import RefreshError
|
25
26
|
from google.auth.transport.requests import Request
|
26
|
-
|
27
|
+
import google.oauth2.credentials
|
27
28
|
from pydantic import BaseModel
|
28
29
|
from pydantic import model_validator
|
29
30
|
|
@@ -40,26 +41,35 @@ BIGQUERY_DEFAULT_SCOPE = ["https://www.googleapis.com/auth/bigquery"]
|
|
40
41
|
|
41
42
|
@experimental
|
42
43
|
class BigQueryCredentialsConfig(BaseModel):
|
43
|
-
"""Configuration for Google API tools
|
44
|
+
"""Configuration for Google API tools (Experimental).
|
45
|
+
|
46
|
+
Please do not use this in production, as it may be deprecated later.
|
47
|
+
"""
|
44
48
|
|
45
49
|
# Configure the model to allow arbitrary types like Credentials
|
46
50
|
model_config = {"arbitrary_types_allowed": True}
|
47
51
|
|
48
|
-
credentials: Optional[Credentials] = None
|
49
|
-
"""
|
52
|
+
credentials: Optional[google.auth.credentials.Credentials] = None
|
53
|
+
"""The existing auth credentials to use. If set, this credential will be used
|
50
54
|
for every end user, end users don't need to be involved in the oauthflow. This
|
51
55
|
field is mutually exclusive with client_id, client_secret and scopes.
|
52
56
|
Don't set this field unless you are sure this credential has the permission to
|
53
57
|
access every end user's data.
|
54
58
|
|
55
|
-
Example usage:
|
59
|
+
Example usage 1: When the agent is deployed in Google Cloud environment and
|
56
60
|
the service account (used as application default credentials) has access to
|
57
61
|
all the required BigQuery resource. Setting this credential to allow user to
|
58
62
|
access the BigQuery resource without end users going through oauth flow.
|
59
63
|
|
60
|
-
To get application default credential: `google.auth.default(...)`. See more
|
64
|
+
To get application default credential, use: `google.auth.default(...)`. See more
|
61
65
|
details in https://cloud.google.com/docs/authentication/application-default-credentials.
|
62
66
|
|
67
|
+
Example usage 2: When the agent wants to access the user's BigQuery resources
|
68
|
+
using the service account key credentials.
|
69
|
+
|
70
|
+
To load service account key credentials, use: `google.auth.load_credentials_from_file(...)`.
|
71
|
+
See more details in https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys.
|
72
|
+
|
63
73
|
When the deployed environment cannot provide a pre-existing credential,
|
64
74
|
consider setting below client_id, client_secret and scope for end users to go
|
65
75
|
through oauth flow, so that agent can access the user data.
|
@@ -86,7 +96,9 @@ class BigQueryCredentialsConfig(BaseModel):
|
|
86
96
|
" client_id/client_secret/scopes."
|
87
97
|
)
|
88
98
|
|
89
|
-
if self.credentials
|
99
|
+
if self.credentials and isinstance(
|
100
|
+
self.credentials, google.oauth2.credentials.Credentials
|
101
|
+
):
|
90
102
|
self.client_id = self.credentials.client_id
|
91
103
|
self.client_secret = self.credentials.client_secret
|
92
104
|
self.scopes = self.credentials.scopes
|
@@ -115,7 +127,7 @@ class BigQueryCredentialsManager:
|
|
115
127
|
|
116
128
|
async def get_valid_credentials(
|
117
129
|
self, tool_context: ToolContext
|
118
|
-
) -> Optional[Credentials]:
|
130
|
+
) -> Optional[google.auth.credentials.Credentials]:
|
119
131
|
"""Get valid credentials, handling refresh and OAuth flow as needed.
|
120
132
|
|
121
133
|
Args:
|
@@ -127,7 +139,7 @@ class BigQueryCredentialsManager:
|
|
127
139
|
# First, try to get credentials from the tool context
|
128
140
|
creds_json = tool_context.state.get(BIGQUERY_TOKEN_CACHE_KEY, None)
|
129
141
|
creds = (
|
130
|
-
Credentials.from_authorized_user_info(
|
142
|
+
google.oauth2.credentials.Credentials.from_authorized_user_info(
|
131
143
|
json.loads(creds_json), self.credentials_config.scopes
|
132
144
|
)
|
133
145
|
if creds_json
|
@@ -138,6 +150,11 @@ class BigQueryCredentialsManager:
|
|
138
150
|
if not creds:
|
139
151
|
creds = self.credentials_config.credentials
|
140
152
|
|
153
|
+
# If non-oauth credentials are provided then use them as is. This helps
|
154
|
+
# in flows such as service account keys
|
155
|
+
if creds and not isinstance(creds, google.oauth2.credentials.Credentials):
|
156
|
+
return creds
|
157
|
+
|
141
158
|
# Check if we have valid credentials
|
142
159
|
if creds and creds.valid:
|
143
160
|
return creds
|
@@ -159,7 +176,7 @@ class BigQueryCredentialsManager:
|
|
159
176
|
|
160
177
|
async def _perform_oauth_flow(
|
161
178
|
self, tool_context: ToolContext
|
162
|
-
) -> Optional[Credentials]:
|
179
|
+
) -> Optional[google.oauth2.credentials.Credentials]:
|
163
180
|
"""Perform OAuth flow to get new credentials.
|
164
181
|
|
165
182
|
Args:
|
@@ -199,7 +216,7 @@ class BigQueryCredentialsManager:
|
|
199
216
|
|
200
217
|
if auth_response:
|
201
218
|
# OAuth flow completed, create credentials
|
202
|
-
creds = Credentials(
|
219
|
+
creds = google.oauth2.credentials.Credentials(
|
203
220
|
token=auth_response.oauth2.access_token,
|
204
221
|
refresh_token=auth_response.oauth2.refresh_token,
|
205
222
|
token_uri=auth_scheme.flows.authorizationCode.tokenUrl,
|
@@ -19,7 +19,7 @@ from typing import Any
|
|
19
19
|
from typing import Callable
|
20
20
|
from typing import Optional
|
21
21
|
|
22
|
-
from google.
|
22
|
+
from google.auth.credentials import Credentials
|
23
23
|
from typing_extensions import override
|
24
24
|
|
25
25
|
from ...utils.feature_decorator import experimental
|
@@ -12,8 +12,8 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
+
from google.auth.credentials import Credentials
|
15
16
|
from google.cloud import bigquery
|
16
|
-
from google.oauth2.credentials import Credentials
|
17
17
|
|
18
18
|
from . import client
|
19
19
|
|
@@ -16,8 +16,8 @@ import functools
|
|
16
16
|
import types
|
17
17
|
from typing import Callable
|
18
18
|
|
19
|
+
from google.auth.credentials import Credentials
|
19
20
|
from google.cloud import bigquery
|
20
|
-
from google.oauth2.credentials import Credentials
|
21
21
|
|
22
22
|
from . import client
|
23
23
|
from .config import BigQueryToolConfig
|
@@ -138,11 +138,6 @@ class MCPTool(BaseAuthenticatedTool):
|
|
138
138
|
if credential:
|
139
139
|
if credential.oauth2:
|
140
140
|
headers = {"Authorization": f"Bearer {credential.oauth2.access_token}"}
|
141
|
-
elif credential.google_oauth2_json:
|
142
|
-
google_credential = Credentials.from_authorized_user_info(
|
143
|
-
json.loads(credential.google_oauth2_json)
|
144
|
-
)
|
145
|
-
headers = {"Authorization": f"Bearer {google_credential.token}"}
|
146
141
|
elif credential.http:
|
147
142
|
# Handle HTTP authentication schemes
|
148
143
|
if (
|
@@ -178,10 +173,9 @@ class MCPTool(BaseAuthenticatedTool):
|
|
178
173
|
headers = {"X-API-Key": credential.api_key}
|
179
174
|
elif credential.service_account:
|
180
175
|
# Service accounts should be exchanged for access tokens before reaching this point
|
181
|
-
# If we reach here, we can try to use google_oauth2_json or log a warning
|
182
176
|
logger.warning(
|
183
|
-
"Service account credentials should be exchanged
|
184
|
-
"
|
177
|
+
"Service account credentials should be exchanged before MCP"
|
178
|
+
" session creation"
|
185
179
|
)
|
186
180
|
|
187
181
|
return headers
|
google/adk/version.py
CHANGED
@@ -2,10 +2,12 @@ google/adk/__init__.py,sha256=sSPQK3r0tW8ahl-k8SXkZvMcbiTbGICCtrw6KkFucyg,726
|
|
2
2
|
google/adk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
google/adk/runners.py,sha256=Tqv3HrulZGBO5YZ9bNbtAihDl-SsPv1SnW5nzUBRfZs,18558
|
4
4
|
google/adk/telemetry.py,sha256=0ZHioyg4GD-A4xd2TPB_W1uW2_m5kMQGHosPwCu9cIc,8641
|
5
|
-
google/adk/version.py,sha256=
|
5
|
+
google/adk/version.py,sha256=oy2jnAjqK4WVwWkp2NI0T0DqExGIXma2q1zr_CLnGO8,626
|
6
6
|
google/adk/a2a/__init__.py,sha256=Q9FlRO2IfSE9yEaiAYzWkOMBJPCaNYqh4ihcp0t0BQs,574
|
7
7
|
google/adk/a2a/converters/__init__.py,sha256=Q9FlRO2IfSE9yEaiAYzWkOMBJPCaNYqh4ihcp0t0BQs,574
|
8
|
-
google/adk/a2a/converters/
|
8
|
+
google/adk/a2a/converters/event_converter.py,sha256=8n6_j7mkxCZlDMPhEfzG6THCCA0qSqV3PN0s3C-AT60,11183
|
9
|
+
google/adk/a2a/converters/part_converter.py,sha256=JAkL6x7zPhKvLNDSSacpdQl2v0YwzVZS804--q1yi3U,5374
|
10
|
+
google/adk/a2a/converters/utils.py,sha256=pMhM3k-KZuSprmB-ELH-sdRKLC5SZnRYJke6lCJJdJI,1011
|
9
11
|
google/adk/agents/__init__.py,sha256=WsCiBlvI-ISWrcntboo_sULvVJNwLNxXCe42UGPLKdY,1041
|
10
12
|
google/adk/agents/active_streaming_tool.py,sha256=vFuh_PkdF5EyyneBBJ7Al8ojeTIR3OtsxLjckr9DbXE,1194
|
11
13
|
google/adk/agents/base_agent.py,sha256=fCwAcR12IVVx8qXWGVf773zOmQEKnXDPwmoYYQwUfR4,12168
|
@@ -25,25 +27,24 @@ google/adk/artifacts/base_artifact_service.py,sha256=H-t5nckLTfr330utj8vxjH45z81
|
|
25
27
|
google/adk/artifacts/gcs_artifact_service.py,sha256=-YU4NhZiGMnHHCg00aJWgKq4JWkQLh7EH5OuGusM5bE,5608
|
26
28
|
google/adk/artifacts/in_memory_artifact_service.py,sha256=Iw34Ja89JwGgd3sulbxxk5pVMqzEZJCt4F2m15MC37U,4059
|
27
29
|
google/adk/auth/__init__.py,sha256=GoFe0aZGdp0ExNE4rXNn1RuXLaB64j7Z-2C5e2Hsh8c,908
|
28
|
-
google/adk/auth/auth_credential.py,sha256=
|
30
|
+
google/adk/auth/auth_credential.py,sha256=F1cMHe_gda5N6RXlyB0IYLLos-Jz-WcNFkIm9SjSiGQ,7012
|
29
31
|
google/adk/auth/auth_handler.py,sha256=aUJrY8fzPvSZwi1rtB_N7fHuHt6DE-H8D8HnkDUCyZQ,6828
|
30
32
|
google/adk/auth/auth_preprocessor.py,sha256=RleOG5I7L1EWVRdX_bC1WtKnt0FDKAcXSSh1RexJqtE,4309
|
31
33
|
google/adk/auth/auth_schemes.py,sha256=dxx9bxjOWoae1fSVxbpaVTwa0I4v76_QJJFEX--1ueA,2260
|
32
34
|
google/adk/auth/auth_tool.py,sha256=2N-lR5UrgBhwPaErwRusT1MrNDQGb8fuiMHO5x3cmck,3674
|
33
|
-
google/adk/auth/credential_manager.py,sha256=
|
35
|
+
google/adk/auth/credential_manager.py,sha256=G5iqpStGHl4wZxkeqNDNljrEt1L_XNSfNVM9mmOqUhg,9503
|
34
36
|
google/adk/auth/oauth2_credential_util.py,sha256=gIb1OUGA4NZRnIeLLk29T_Q9J8ibTxe7-357UhCW3FY,3340
|
35
37
|
google/adk/auth/credential_service/__init__.py,sha256=Q9FlRO2IfSE9yEaiAYzWkOMBJPCaNYqh4ihcp0t0BQs,574
|
36
38
|
google/adk/auth/credential_service/base_credential_service.py,sha256=4SK1UW9NiLNLrc_grpG3xyIPXySGpl0DIv9bTm-onWE,2325
|
37
39
|
google/adk/auth/credential_service/in_memory_credential_service.py,sha256=xXG-3_uq3CU1Eilp8NGBhje0X77ROlAT5BKyJmLLyHg,2161
|
38
|
-
google/adk/auth/exchanger/__init__.py,sha256=
|
40
|
+
google/adk/auth/exchanger/__init__.py,sha256=RHCK_Zg7hXzBKvz2Vrwvbx_cMXWittidIZToszaL3Nc,720
|
39
41
|
google/adk/auth/exchanger/base_credential_exchanger.py,sha256=Uqzs_NhEDmuH5n0U_ES5fHlMSagyYEc5JKu-5GdOC_A,1644
|
40
42
|
google/adk/auth/exchanger/credential_exchanger_registry.py,sha256=Nsk9BMmhFbZsXQwPckm8elXfbk6iIRSrvegR6DpcONo,1855
|
41
43
|
google/adk/auth/exchanger/oauth2_credential_exchanger.py,sha256=FA0EYC-zfD1gO9CV9xDYYfPxU2EAJdYwVyWjVy3C6Bw,3649
|
42
|
-
google/adk/auth/exchanger/service_account_credential_exchanger.py,sha256=AYVZcvOuOhNKPgwwgSOcxyZegi6xq4IW8vvNs_IMvXE,3804
|
43
44
|
google/adk/auth/refresher/__init__.py,sha256=DEEkESlvEteCpO4QcDExm6K8S8y7l_oS-A2TK1Oh1xU,720
|
44
45
|
google/adk/auth/refresher/base_credential_refresher.py,sha256=oDWBAuSnt5-00f8LwTrwTptuwkkUXknad8Zbp2mmOA4,2192
|
45
46
|
google/adk/auth/refresher/credential_refresher_registry.py,sha256=ioHclgxCtpSeiUcMq9jwzPn0IzzxrQLpWSpHy90folA,1877
|
46
|
-
google/adk/auth/refresher/oauth2_credential_refresher.py,sha256=
|
47
|
+
google/adk/auth/refresher/oauth2_credential_refresher.py,sha256=HARpIwwXNxqo4_CNDpRFrN-p7RsnShgu21hgRFVwv4Q,4035
|
47
48
|
google/adk/cli/__init__.py,sha256=ouPYnIY02VmGNfpA6IT8oSQdfeZd1LHVoDSt_x8zQPU,609
|
48
49
|
google/adk/cli/__main__.py,sha256=gN8rRWlkh_3gLI-oYByxrKpCW9BIfDwrr0YuyisxmHo,646
|
49
50
|
google/adk/cli/agent_graph.py,sha256=Kj5_a4UE1QXmqdRv4i4LI4hKHOrLkBS22Q759F3aRug,9879
|
@@ -116,7 +117,7 @@ google/adk/flows/llm_flows/auto_flow.py,sha256=CnuFelyZhB_ns4U_5_dW0x_KQlzu02My7
|
|
116
117
|
google/adk/flows/llm_flows/base_llm_flow.py,sha256=uGqJ-b_Xygqgae1dndNh9tgTaj3luMKq3Qj4_jDf278,22660
|
117
118
|
google/adk/flows/llm_flows/basic.py,sha256=263pfk6PIqKO-7BWo--bsBbpZJ8u5R9OK__WhpBgIbM,2848
|
118
119
|
google/adk/flows/llm_flows/contents.py,sha256=bAklBI8YWctd0pGQCRwCVDqDxASiCNV_t8tJChPLbFg,13055
|
119
|
-
google/adk/flows/llm_flows/functions.py,sha256=
|
120
|
+
google/adk/flows/llm_flows/functions.py,sha256=NRzs9MfqCI8JjKBJCPk3iO_xXn0PzaBfy7NjQrdVsUU,17512
|
120
121
|
google/adk/flows/llm_flows/identity.py,sha256=X4CRg12NvnopmydU9gbFJI4lW1_otN-w_GOAuPvKrXo,1651
|
121
122
|
google/adk/flows/llm_flows/instructions.py,sha256=sO2dQ5hn6ybjXs2fWYWvEFVtACdpiiP0yKf9eNVjhhM,2879
|
122
123
|
google/adk/flows/llm_flows/single_flow.py,sha256=gC677SxxammKx1XkZBzUdgBjDzeymKRcRQQxFGIur8Y,1904
|
@@ -151,7 +152,7 @@ google/adk/sessions/database_session_service.py,sha256=zPqH1pY_euvSA9I3f3BTfvBxL
|
|
151
152
|
google/adk/sessions/in_memory_session_service.py,sha256=5mU882L-0zobyWoldAkuBMmGKTQV_XlhL0A2_OySPzU,9080
|
152
153
|
google/adk/sessions/session.py,sha256=fwJ3D4rUQ1N5cLMpFrE_BstEz6Ct637FlF52MfkxZCk,1861
|
153
154
|
google/adk/sessions/state.py,sha256=con9G5nfJpa95J5LKTAnZ3KMPkXdaTbrdwRdKg6d6B4,2299
|
154
|
-
google/adk/sessions/vertex_ai_session_service.py,sha256=
|
155
|
+
google/adk/sessions/vertex_ai_session_service.py,sha256=ZzQeCd3lE-7htfNZjWB_pDV2C5fNksHQ7c-RvJjSEnc,13564
|
155
156
|
google/adk/tools/__init__.py,sha256=_5JFmTAPBmXlOY8CRaYJlZubvwxoxhi2esXusovv5ME,1752
|
156
157
|
google/adk/tools/_automatic_function_calling_util.py,sha256=p5An_BklKDo1w5_DigZGgf0p023lIDKPaZPS_-iJN6k,11100
|
157
158
|
google/adk/tools/_forwarding_artifact_service.py,sha256=MHOfc8ntSuHLcA4jp218FP0k0qWAu3-6MSQCNWZ__S4,3022
|
@@ -192,13 +193,13 @@ google/adk/tools/application_integration_tool/integration_connector_tool.py,sha2
|
|
192
193
|
google/adk/tools/application_integration_tool/clients/connections_client.py,sha256=zspLmrx2DvOg2r5B2DWxeK3fKdQovIu8t3z5Xip7AGo,31428
|
193
194
|
google/adk/tools/application_integration_tool/clients/integration_client.py,sha256=hLM8n97hsmvgYBtmF_KMYwr_mnlhfPvsDXzE2cI5SqE,10654
|
194
195
|
google/adk/tools/bigquery/__init__.py,sha256=tkXX7IoTzXgZjv82fjqa0_TTufxijiIr6nPsaqH1o5Y,1446
|
195
|
-
google/adk/tools/bigquery/bigquery_credentials.py,sha256=
|
196
|
-
google/adk/tools/bigquery/bigquery_tool.py,sha256=
|
196
|
+
google/adk/tools/bigquery/bigquery_credentials.py,sha256=cs610IowXAhpwovRpMjspPQ6Bb9TB23IBqdUorHNQzI,8690
|
197
|
+
google/adk/tools/bigquery/bigquery_tool.py,sha256=Hvw5ijtX9FGJfUxvBCTL51JxAS4-G5wFtewMUcSACxk,4208
|
197
198
|
google/adk/tools/bigquery/bigquery_toolset.py,sha256=4eIj_bbUeC4bhhNN9RA8i5661Xi3qM9pqfYoN4XBQME,2831
|
198
|
-
google/adk/tools/bigquery/client.py,sha256=
|
199
|
+
google/adk/tools/bigquery/client.py,sha256=CUFsxf6Ma5dTfofk1L888EnUfo2zlI6-_NFs-v3Hcr0,1166
|
199
200
|
google/adk/tools/bigquery/config.py,sha256=GNiPUX4sVKg7UfoJ-xo8RCReYym4qF3blMUzY9O5CRE,1390
|
200
|
-
google/adk/tools/bigquery/metadata_tool.py,sha256=
|
201
|
-
google/adk/tools/bigquery/query_tool.py,sha256
|
201
|
+
google/adk/tools/bigquery/metadata_tool.py,sha256=BZ5VPXERZgVJDp1VqT6vSmFkDYPbo0bXBZsE5RgL3XY,8263
|
202
|
+
google/adk/tools/bigquery/query_tool.py,sha256=-dsqZbuQxwYb6uCHJ5_-6Yt94QlvQ4ktDov6JKdsnvc,6156
|
202
203
|
google/adk/tools/google_api_tool/__init__.py,sha256=a_Bco5SyTQ89yb1t6Bs6NQrTsJgV22mn1quRNygVZXw,1385
|
203
204
|
google/adk/tools/google_api_tool/google_api_tool.py,sha256=vDA7YnDZCl8-ucBvIzXS-uJWX1m2KY_csTz5M3nw_Ys,2029
|
204
205
|
google/adk/tools/google_api_tool/google_api_toolset.py,sha256=M27lSCXOka4yHGGH50UVJvoEvZbWT-QBVaR1tW018tY,3773
|
@@ -207,7 +208,7 @@ google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py,sha256=mo1ew3
|
|
207
208
|
google/adk/tools/mcp_tool/__init__.py,sha256=UIXmz81_7s-kpZNSTOhpWXtQeMxXpjTwMRoDPDbCTkQ,1515
|
208
209
|
google/adk/tools/mcp_tool/conversion_utils.py,sha256=PfPSBAPzAgBsEWk2goKOFHz4fM-9ralW-nMkCbs0b38,5339
|
209
210
|
google/adk/tools/mcp_tool/mcp_session_manager.py,sha256=9QRcprgzfpUHBTePXE2rrUb3LKRQeGo38-Gb6LPbH2w,12946
|
210
|
-
google/adk/tools/mcp_tool/mcp_tool.py,sha256=
|
211
|
+
google/adk/tools/mcp_tool/mcp_tool.py,sha256=9tHuqpjBKqcMeI8WvVXfWuWvxov8kzuG85REboblpL8,6081
|
211
212
|
google/adk/tools/mcp_tool/mcp_toolset.py,sha256=dp3IhfdrK07CvZ5LERVtUBzlbbUOhnrmI0ND8Fn2eGk,6323
|
212
213
|
google/adk/tools/openapi_tool/__init__.py,sha256=UMsewNCQjd-r1GBX1OMuUJTzJ0AlQuegIc98g04-0oU,724
|
213
214
|
google/adk/tools/openapi_tool/auth/__init__.py,sha256=NVRXscqN4V0CSCvIp8J_ee8Xyw4m-OGoZn7SmrtOsQk,637
|
@@ -224,7 +225,7 @@ google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py,sha256=
|
|
224
225
|
google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py,sha256=zxQtizsdW8FIqhpSzGfhkQLbBXIDZOK_0h8TrXayYZ4,5532
|
225
226
|
google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py,sha256=PhgkKRtSQi-gZa2RBeEzCX0A0Aekk2kLIo_cuf9aAQ0,9078
|
226
227
|
google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py,sha256=TMLDr-ODSEiok0l1ObPmuwcTOvOAjuxoMNBw_QwG5Pg,14650
|
227
|
-
google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py,sha256=
|
228
|
+
google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py,sha256=9bqnlvmcr6i8Dab73ntzbynkFFWCYMhk7R-DS-jkOz4,10013
|
228
229
|
google/adk/tools/retrieval/__init__.py,sha256=0euJjx0ReH8JmUI5-JU8kWRswqLxobRCDjx5zvX4rHY,1188
|
229
230
|
google/adk/tools/retrieval/base_retrieval_tool.py,sha256=4aar8Kg-6rQG7Ht1n18D5fvJnuffodFdSjeCp-GzA7w,1174
|
230
231
|
google/adk/tools/retrieval/files_retrieval.py,sha256=UvxXjs3t8O2VO7o4wagHah2ydHT6sl0bLMsKxDVTOHU,1271
|
@@ -234,8 +235,8 @@ google/adk/utils/__init__.py,sha256=Q9FlRO2IfSE9yEaiAYzWkOMBJPCaNYqh4ihcp0t0BQs,
|
|
234
235
|
google/adk/utils/feature_decorator.py,sha256=DzGHMTStf4-S9BNiA4EqcCJbrdKijhgeSUSPdzM44H8,5048
|
235
236
|
google/adk/utils/instructions_utils.py,sha256=al9Z-P8qOrbxNju8cqkeH7qRg0oQH7hfmvTG-0oSAQk,3996
|
236
237
|
google/adk/utils/variant_utils.py,sha256=u9IuOn2aXG3ibDYshgLoogBXqH9Gd84ixArQoeLQiE8,1463
|
237
|
-
google_adk-1.4.
|
238
|
-
google_adk-1.4.
|
239
|
-
google_adk-1.4.
|
240
|
-
google_adk-1.4.
|
241
|
-
google_adk-1.4.
|
238
|
+
google_adk-1.4.2.dist-info/entry_points.txt,sha256=zL9CU-6V2yQ2oc5lrcyj55ROHrpiIePsvQJ4H6SL-zI,43
|
239
|
+
google_adk-1.4.2.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
240
|
+
google_adk-1.4.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
241
|
+
google_adk-1.4.2.dist-info/METADATA,sha256=GFu7dUG7xYrhvSed3cY-WARhxOCrTlFmUMyzygCL2Ko,9981
|
242
|
+
google_adk-1.4.2.dist-info/RECORD,,
|
@@ -1,104 +0,0 @@
|
|
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
|
-
"""Credential fetcher for Google Service Account."""
|
16
|
-
|
17
|
-
from __future__ import annotations
|
18
|
-
|
19
|
-
from typing import Optional
|
20
|
-
|
21
|
-
import google.auth
|
22
|
-
from google.auth.transport.requests import Request
|
23
|
-
from google.oauth2 import service_account
|
24
|
-
from typing_extensions import override
|
25
|
-
|
26
|
-
from ...utils.feature_decorator import experimental
|
27
|
-
from ..auth_credential import AuthCredential
|
28
|
-
from ..auth_credential import AuthCredentialTypes
|
29
|
-
from ..auth_schemes import AuthScheme
|
30
|
-
from .base_credential_exchanger import BaseCredentialExchanger
|
31
|
-
|
32
|
-
|
33
|
-
@experimental
|
34
|
-
class ServiceAccountCredentialExchanger(BaseCredentialExchanger):
|
35
|
-
"""Exchanges Google Service Account credentials for an access token.
|
36
|
-
|
37
|
-
Uses the default service credential if `use_default_credential = True`.
|
38
|
-
Otherwise, uses the service account credential provided in the auth
|
39
|
-
credential.
|
40
|
-
"""
|
41
|
-
|
42
|
-
@override
|
43
|
-
async def exchange(
|
44
|
-
self,
|
45
|
-
auth_credential: AuthCredential,
|
46
|
-
auth_scheme: Optional[AuthScheme] = None,
|
47
|
-
) -> AuthCredential:
|
48
|
-
"""Exchanges the service account auth credential for an access token.
|
49
|
-
|
50
|
-
If the AuthCredential contains a service account credential, it will be used
|
51
|
-
to exchange for an access token. Otherwise, if use_default_credential is True,
|
52
|
-
the default application credential will be used for exchanging an access token.
|
53
|
-
|
54
|
-
Args:
|
55
|
-
auth_scheme: The authentication scheme.
|
56
|
-
auth_credential: The credential to exchange.
|
57
|
-
|
58
|
-
Returns:
|
59
|
-
An AuthCredential in OAUTH2 format, containing the exchanged credential JSON.
|
60
|
-
|
61
|
-
Raises:
|
62
|
-
ValueError: If service account credentials are missing or invalid.
|
63
|
-
Exception: If credential exchange or refresh fails.
|
64
|
-
"""
|
65
|
-
if auth_credential is None:
|
66
|
-
raise ValueError("Credential cannot be None.")
|
67
|
-
|
68
|
-
if auth_credential.auth_type != AuthCredentialTypes.SERVICE_ACCOUNT:
|
69
|
-
raise ValueError("Credential is not a service account credential.")
|
70
|
-
|
71
|
-
if auth_credential.service_account is None:
|
72
|
-
raise ValueError(
|
73
|
-
"Service account credentials are missing. Please provide them."
|
74
|
-
)
|
75
|
-
|
76
|
-
if (
|
77
|
-
auth_credential.service_account.service_account_credential is None
|
78
|
-
and not auth_credential.service_account.use_default_credential
|
79
|
-
):
|
80
|
-
raise ValueError(
|
81
|
-
"Service account credentials are invalid. Please set the"
|
82
|
-
" service_account_credential field or set `use_default_credential ="
|
83
|
-
" True` to use application default credential in a hosted service"
|
84
|
-
" like Google Cloud Run."
|
85
|
-
)
|
86
|
-
|
87
|
-
try:
|
88
|
-
if auth_credential.service_account.use_default_credential:
|
89
|
-
credentials, _ = google.auth.default()
|
90
|
-
else:
|
91
|
-
config = auth_credential.service_account
|
92
|
-
credentials = service_account.Credentials.from_service_account_info(
|
93
|
-
config.service_account_credential.model_dump(), scopes=config.scopes
|
94
|
-
)
|
95
|
-
|
96
|
-
# Refresh credentials to ensure we have a valid access token
|
97
|
-
credentials.refresh(Request())
|
98
|
-
|
99
|
-
return AuthCredential(
|
100
|
-
auth_type=AuthCredentialTypes.OAUTH2,
|
101
|
-
google_oauth2_json=credentials.to_json(),
|
102
|
-
)
|
103
|
-
except Exception as e:
|
104
|
-
raise ValueError(f"Failed to exchange service account token: {e}") from e
|
File without changes
|
File without changes
|
File without changes
|