google-adk 0.5.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/base_agent.py +76 -30
- google/adk/agents/base_agent.py.orig +330 -0
- google/adk/agents/callback_context.py +0 -5
- google/adk/agents/llm_agent.py +122 -30
- 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 +1 -1
- google/adk/agents/sequential_agent.py +31 -0
- google/adk/agents/transcription_entry.py +4 -2
- google/adk/artifacts/gcs_artifact_service.py +1 -1
- google/adk/artifacts/in_memory_artifact_service.py +1 -1
- google/adk/auth/auth_credential.py +6 -1
- 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-ULN5R5I5.js → main-QOEMUXM4.js} +44 -45
- google/adk/cli/cli.py +7 -7
- google/adk/cli/cli_deploy.py +7 -2
- google/adk/cli/cli_eval.py +172 -99
- google/adk/cli/cli_tools_click.py +147 -64
- google/adk/cli/fast_api.py +330 -148
- google/adk/cli/fast_api.py.orig +174 -80
- 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_generator.py +88 -113
- google/adk/evaluation/evaluator.py +56 -0
- google/adk/evaluation/local_eval_sets_manager.py +264 -0
- google/adk/evaluation/response_evaluator.py +106 -2
- google/adk/evaluation/trajectory_evaluator.py +83 -2
- google/adk/events/event.py +6 -1
- google/adk/events/event_actions.py +6 -1
- google/adk/examples/example_util.py +3 -2
- google/adk/flows/llm_flows/_code_execution.py +9 -1
- google/adk/flows/llm_flows/audio_transcriber.py +4 -3
- google/adk/flows/llm_flows/base_llm_flow.py +54 -15
- google/adk/flows/llm_flows/functions.py +9 -8
- google/adk/flows/llm_flows/instructions.py +13 -5
- 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 +23 -21
- google/adk/memory/base_memory_service.py.orig +76 -0
- google/adk/memory/in_memory_memory_service.py +57 -25
- google/adk/memory/memory_entry.py +37 -0
- google/adk/memory/vertex_ai_rag_memory_service.py +38 -15
- google/adk/models/anthropic_llm.py +16 -9
- google/adk/models/gemini_llm_connection.py +11 -11
- google/adk/models/google_llm.py +9 -2
- google/adk/models/google_llm.py.orig +305 -0
- google/adk/models/lite_llm.py +77 -21
- google/adk/models/llm_response.py +14 -2
- google/adk/models/registry.py +1 -1
- google/adk/runners.py +65 -41
- google/adk/sessions/__init__.py +1 -1
- google/adk/sessions/base_session_service.py +6 -33
- google/adk/sessions/database_session_service.py +58 -65
- google/adk/sessions/in_memory_session_service.py +106 -24
- google/adk/sessions/session.py +3 -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 +9 -9
- 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 +20 -0
- 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_memory_tool.py +14 -5
- google/adk/tools/mcp_tool/__init__.py +3 -2
- google/adk/tools/mcp_tool/mcp_session_manager.py +153 -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/openapi_spec_parser/openapi_toolset.py +32 -7
- google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +31 -31
- 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/toolbox_toolset.py +79 -0
- google/adk/tools/transfer_to_agent_tool.py +0 -1
- google/adk/version.py +1 -1
- {google_adk-0.5.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.5.0.dist-info/RECORD +0 -180
- {google_adk-0.5.0.dist-info → google_adk-1.0.0.dist-info}/WHEEL +0 -0
- {google_adk-0.5.0.dist-info → google_adk-1.0.0.dist-info}/entry_points.txt +0 -0
- {google_adk-0.5.0.dist-info → google_adk-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -12,21 +12,24 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
16
15
|
import logging
|
17
16
|
from typing import Any
|
18
17
|
from typing import Dict
|
19
18
|
from typing import Optional
|
19
|
+
from typing import Union
|
20
20
|
|
21
|
-
from google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool import RestApiTool
|
22
|
-
from google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool import to_gemini_schema
|
23
21
|
from google.genai.types import FunctionDeclaration
|
24
22
|
from typing_extensions import override
|
25
23
|
|
24
|
+
from ...auth.auth_credential import AuthCredential
|
25
|
+
from ...auth.auth_schemes import AuthScheme
|
26
26
|
from .. import BaseTool
|
27
|
+
from ..openapi_tool.openapi_spec_parser.rest_api_tool import RestApiTool
|
28
|
+
from ..openapi_tool.openapi_spec_parser.rest_api_tool import to_gemini_schema
|
29
|
+
from ..openapi_tool.openapi_spec_parser.tool_auth_handler import ToolAuthHandler
|
27
30
|
from ..tool_context import ToolContext
|
28
31
|
|
29
|
-
logger = logging.getLogger(__name__)
|
32
|
+
logger = logging.getLogger('google_adk.' + __name__)
|
30
33
|
|
31
34
|
|
32
35
|
class IntegrationConnectorTool(BaseTool):
|
@@ -56,6 +59,7 @@ class IntegrationConnectorTool(BaseTool):
|
|
56
59
|
'entity',
|
57
60
|
'operation',
|
58
61
|
'action',
|
62
|
+
'dynamic_auth_config',
|
59
63
|
]
|
60
64
|
|
61
65
|
OPTIONAL_FIELDS = [
|
@@ -75,6 +79,8 @@ class IntegrationConnectorTool(BaseTool):
|
|
75
79
|
operation: str,
|
76
80
|
action: str,
|
77
81
|
rest_api_tool: RestApiTool,
|
82
|
+
auth_scheme: Optional[Union[AuthScheme, str]] = None,
|
83
|
+
auth_credential: Optional[Union[AuthCredential, str]] = None,
|
78
84
|
):
|
79
85
|
"""Initializes the ApplicationIntegrationTool.
|
80
86
|
|
@@ -101,18 +107,20 @@ class IntegrationConnectorTool(BaseTool):
|
|
101
107
|
name=name,
|
102
108
|
description=description,
|
103
109
|
)
|
104
|
-
self.
|
105
|
-
self.
|
106
|
-
self.
|
107
|
-
self.
|
108
|
-
self.
|
109
|
-
self.
|
110
|
-
self.
|
110
|
+
self._connection_name = connection_name
|
111
|
+
self._connection_host = connection_host
|
112
|
+
self._connection_service_name = connection_service_name
|
113
|
+
self._entity = entity
|
114
|
+
self._operation = operation
|
115
|
+
self._action = action
|
116
|
+
self._rest_api_tool = rest_api_tool
|
117
|
+
self._auth_scheme = auth_scheme
|
118
|
+
self._auth_credential = auth_credential
|
111
119
|
|
112
120
|
@override
|
113
121
|
def _get_declaration(self) -> FunctionDeclaration:
|
114
122
|
"""Returns the function declaration in the Gemini Schema format."""
|
115
|
-
schema_dict = self.
|
123
|
+
schema_dict = self._rest_api_tool._operation_parser.get_json_schema()
|
116
124
|
for field in self.EXCLUDE_FIELDS:
|
117
125
|
if field in schema_dict['properties']:
|
118
126
|
del schema_dict['properties'][field]
|
@@ -126,34 +134,69 @@ class IntegrationConnectorTool(BaseTool):
|
|
126
134
|
)
|
127
135
|
return function_decl
|
128
136
|
|
137
|
+
def _prepare_dynamic_euc(self, auth_credential: AuthCredential) -> str:
|
138
|
+
if (
|
139
|
+
auth_credential
|
140
|
+
and auth_credential.http
|
141
|
+
and auth_credential.http.credentials
|
142
|
+
and auth_credential.http.credentials.token
|
143
|
+
):
|
144
|
+
return auth_credential.http.credentials.token
|
145
|
+
return None
|
146
|
+
|
129
147
|
@override
|
130
148
|
async def run_async(
|
131
149
|
self, *, args: dict[str, Any], tool_context: Optional[ToolContext]
|
132
150
|
) -> Dict[str, Any]:
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
151
|
+
|
152
|
+
tool_auth_handler = ToolAuthHandler.from_tool_context(
|
153
|
+
tool_context, self._auth_scheme, self._auth_credential
|
154
|
+
)
|
155
|
+
auth_result = tool_auth_handler.prepare_auth_credentials()
|
156
|
+
|
157
|
+
if auth_result.state == 'pending':
|
158
|
+
return {
|
159
|
+
'pending': True,
|
160
|
+
'message': 'Needs your authorization to access your data.',
|
161
|
+
}
|
162
|
+
|
163
|
+
# Attach parameters from auth into main parameters list
|
164
|
+
if auth_result.auth_credential:
|
165
|
+
# Attach parameters from auth into main parameters list
|
166
|
+
auth_credential_token = self._prepare_dynamic_euc(
|
167
|
+
auth_result.auth_credential
|
168
|
+
)
|
169
|
+
if auth_credential_token:
|
170
|
+
args['dynamic_auth_config'] = {
|
171
|
+
'oauth2_auth_code_flow.access_token': auth_credential_token
|
172
|
+
}
|
173
|
+
else:
|
174
|
+
args['dynamic_auth_config'] = {'oauth2_auth_code_flow.access_token': {}}
|
175
|
+
|
176
|
+
args['connection_name'] = self._connection_name
|
177
|
+
args['service_name'] = self._connection_service_name
|
178
|
+
args['host'] = self._connection_host
|
179
|
+
args['entity'] = self._entity
|
180
|
+
args['operation'] = self._operation
|
181
|
+
args['action'] = self._action
|
139
182
|
logger.info('Running tool: %s with args: %s', self.name, args)
|
140
|
-
return self.
|
183
|
+
return self._rest_api_tool.call(args=args, tool_context=tool_context)
|
141
184
|
|
142
185
|
def __str__(self):
|
143
186
|
return (
|
144
187
|
f'ApplicationIntegrationTool(name="{self.name}",'
|
145
188
|
f' description="{self.description}",'
|
146
|
-
f' connection_name="{self.
|
147
|
-
f' operation="{self.
|
189
|
+
f' connection_name="{self._connection_name}", entity="{self._entity}",'
|
190
|
+
f' operation="{self._operation}", action="{self._action}")'
|
148
191
|
)
|
149
192
|
|
150
193
|
def __repr__(self):
|
151
194
|
return (
|
152
195
|
f'ApplicationIntegrationTool(name="{self.name}",'
|
153
196
|
f' description="{self.description}",'
|
154
|
-
f' connection_name="{self.
|
155
|
-
f' connection_host="{self.
|
156
|
-
f' connection_service_name="{self.
|
157
|
-
f' entity="{self.
|
158
|
-
f' action="{self.
|
197
|
+
f' connection_name="{self._connection_name}",'
|
198
|
+
f' connection_host="{self._connection_host}",'
|
199
|
+
f' connection_service_name="{self._connection_service_name}",'
|
200
|
+
f' entity="{self._entity}", operation="{self._operation}",'
|
201
|
+
f' action="{self._action}", rest_api_tool={repr(self._rest_api_tool)})'
|
159
202
|
)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from abc import ABC
|
2
|
+
from abc import abstractmethod
|
3
|
+
from typing import Optional, runtime_checkable
|
4
|
+
from typing import Protocol
|
5
|
+
|
6
|
+
from ..agents.readonly_context import ReadonlyContext
|
7
|
+
from .base_tool import BaseTool
|
8
|
+
|
9
|
+
|
10
|
+
@runtime_checkable
|
11
|
+
class ToolPredicate(Protocol):
|
12
|
+
"""Base class for a predicate that defines the interface to decide whether a
|
13
|
+
|
14
|
+
tool should be exposed to LLM. Toolset implementer could consider whether to
|
15
|
+
accept such instance in the toolset's constructor and apply the predicate in
|
16
|
+
get_tools method.
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __call__(
|
20
|
+
self, tool: BaseTool, readonly_context: Optional[ReadonlyContext] = None
|
21
|
+
) -> bool:
|
22
|
+
"""Decide whether the passed-in tool should be exposed to LLM based on the
|
23
|
+
|
24
|
+
current context. True if the tool is usable by the LLM.
|
25
|
+
|
26
|
+
It's used to filter tools in the toolset.
|
27
|
+
"""
|
28
|
+
|
29
|
+
|
30
|
+
class BaseToolset(ABC):
|
31
|
+
"""Base class for toolset.
|
32
|
+
|
33
|
+
A toolset is a collection of tools that can be used by an agent.
|
34
|
+
"""
|
35
|
+
|
36
|
+
@abstractmethod
|
37
|
+
async def get_tools(
|
38
|
+
self, readonly_context: Optional[ReadonlyContext] = None
|
39
|
+
) -> list[BaseTool]:
|
40
|
+
"""Return all tools in the toolset based on the provided context.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
readony_context (ReadonlyContext, optional): Context used to filter tools
|
44
|
+
available to the agent. If None, all tools in the toolset are returned.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
list[BaseTool]: A list of tools available under the specified context.
|
48
|
+
"""
|
49
|
+
|
50
|
+
@abstractmethod
|
51
|
+
async def close(self) -> None:
|
52
|
+
"""Performs cleanup and releases resources held by the toolset.
|
53
|
+
|
54
|
+
NOTE: This method is invoked, for example, at the end of an agent server's
|
55
|
+
lifecycle or when the toolset is no longer needed. Implementations
|
56
|
+
should ensure that any open connections, files, or other managed
|
57
|
+
resources are properly released to prevent leaks.
|
58
|
+
"""
|
@@ -0,0 +1,65 @@
|
|
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
|
+
from typing import TYPE_CHECKING
|
18
|
+
|
19
|
+
from google.genai import types
|
20
|
+
from typing_extensions import override
|
21
|
+
|
22
|
+
from .base_tool import BaseTool
|
23
|
+
from .tool_context import ToolContext
|
24
|
+
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from ..models import LlmRequest
|
27
|
+
|
28
|
+
|
29
|
+
class EnterpriseWebSearchTool(BaseTool):
|
30
|
+
"""A Gemini 2+ built-in tool using web grounding for Enterprise compliance.
|
31
|
+
|
32
|
+
See the documentation for more details:
|
33
|
+
https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/web-grounding-enterprise.
|
34
|
+
"""
|
35
|
+
|
36
|
+
def __init__(self):
|
37
|
+
"""Initializes the Vertex AI Search tool."""
|
38
|
+
# Name and description are not used because this is a model built-in tool.
|
39
|
+
super().__init__(
|
40
|
+
name='enterprise_web_search', description='enterprise_web_search'
|
41
|
+
)
|
42
|
+
|
43
|
+
@override
|
44
|
+
async def process_llm_request(
|
45
|
+
self,
|
46
|
+
*,
|
47
|
+
tool_context: ToolContext,
|
48
|
+
llm_request: LlmRequest,
|
49
|
+
) -> None:
|
50
|
+
if llm_request.model and llm_request.model.startswith('gemini-'):
|
51
|
+
if llm_request.model.startswith('gemini-1') and llm_request.config.tools:
|
52
|
+
raise ValueError(
|
53
|
+
'Enterprise web search tool can not be used with other tools in'
|
54
|
+
' Gemini 1.x.'
|
55
|
+
)
|
56
|
+
llm_request.config = llm_request.config or types.GenerateContentConfig()
|
57
|
+
llm_request.config.tools = llm_request.config.tools or []
|
58
|
+
llm_request.config.tools.append(
|
59
|
+
types.Tool(enterprise_web_search=types.EnterpriseWebSearch())
|
60
|
+
)
|
61
|
+
else:
|
62
|
+
raise ValueError(
|
63
|
+
'Enterprise web search tool is not supported for model'
|
64
|
+
f' {llm_request.model}'
|
65
|
+
)
|
@@ -35,7 +35,7 @@ _py_builtin_type_to_schema_type = {
|
|
35
35
|
dict: types.Type.OBJECT,
|
36
36
|
}
|
37
37
|
|
38
|
-
logger = logging.getLogger(__name__)
|
38
|
+
logger = logging.getLogger('google_adk.' + __name__)
|
39
39
|
|
40
40
|
|
41
41
|
def _is_builtin_primitive_or_compound(
|
@@ -292,7 +292,7 @@ def _parse_schema_from_parameter(
|
|
292
292
|
raise ValueError(
|
293
293
|
f'Failed to parse the parameter {param} of function {func_name} for'
|
294
294
|
' automatic function calling. Automatic function calling works best with'
|
295
|
-
' simpler function signature schema,consider manually
|
295
|
+
' simpler function signature schema, consider manually parsing your'
|
296
296
|
f' function declaration for function {func_name}.'
|
297
297
|
)
|
298
298
|
|
@@ -12,76 +12,24 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
__all__ = [
|
15
|
-
'
|
16
|
-
'
|
17
|
-
'
|
18
|
-
'
|
19
|
-
'
|
20
|
-
'
|
21
|
-
'
|
15
|
+
'BigQueryToolset',
|
16
|
+
'CalendarToolset',
|
17
|
+
'GmailToolset',
|
18
|
+
'YoutubeToolset',
|
19
|
+
'SlidesToolset',
|
20
|
+
'SheetsToolset',
|
21
|
+
'DocsToolset',
|
22
|
+
'GoogleApiToolset',
|
23
|
+
'GoogleApiTool',
|
22
24
|
]
|
23
25
|
|
24
|
-
# Nothing is imported here automatically
|
25
|
-
# Each tool set will only be imported when accessed
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def __getattr__(name):
|
37
|
-
global _bigquery_tool_set, _calendar_tool_set, _gmail_tool_set, _youtube_tool_set, _slides_tool_set, _sheets_tool_set, _docs_tool_set
|
38
|
-
|
39
|
-
match name:
|
40
|
-
case 'bigquery_tool_set':
|
41
|
-
if _bigquery_tool_set is None:
|
42
|
-
from .google_api_tool_sets import bigquery_tool_set as bigquery
|
43
|
-
|
44
|
-
_bigquery_tool_set = bigquery
|
45
|
-
return _bigquery_tool_set
|
46
|
-
|
47
|
-
case 'calendar_tool_set':
|
48
|
-
if _calendar_tool_set is None:
|
49
|
-
from .google_api_tool_sets import calendar_tool_set as calendar
|
50
|
-
|
51
|
-
_calendar_tool_set = calendar
|
52
|
-
return _calendar_tool_set
|
53
|
-
|
54
|
-
case 'gmail_tool_set':
|
55
|
-
if _gmail_tool_set is None:
|
56
|
-
from .google_api_tool_sets import gmail_tool_set as gmail
|
57
|
-
|
58
|
-
_gmail_tool_set = gmail
|
59
|
-
return _gmail_tool_set
|
60
|
-
|
61
|
-
case 'youtube_tool_set':
|
62
|
-
if _youtube_tool_set is None:
|
63
|
-
from .google_api_tool_sets import youtube_tool_set as youtube
|
64
|
-
|
65
|
-
_youtube_tool_set = youtube
|
66
|
-
return _youtube_tool_set
|
67
|
-
|
68
|
-
case 'slides_tool_set':
|
69
|
-
if _slides_tool_set is None:
|
70
|
-
from .google_api_tool_sets import slides_tool_set as slides
|
71
|
-
|
72
|
-
_slides_tool_set = slides
|
73
|
-
return _slides_tool_set
|
74
|
-
|
75
|
-
case 'sheets_tool_set':
|
76
|
-
if _sheets_tool_set is None:
|
77
|
-
from .google_api_tool_sets import sheets_tool_set as sheets
|
78
|
-
|
79
|
-
_sheets_tool_set = sheets
|
80
|
-
return _sheets_tool_set
|
81
|
-
|
82
|
-
case 'docs_tool_set':
|
83
|
-
if _docs_tool_set is None:
|
84
|
-
from .google_api_tool_sets import docs_tool_set as docs
|
85
|
-
|
86
|
-
_docs_tool_set = docs
|
87
|
-
return _docs_tool_set
|
27
|
+
from .google_api_tool import GoogleApiTool
|
28
|
+
from .google_api_toolset import GoogleApiToolset
|
29
|
+
from .google_api_toolsets import BigQueryToolset
|
30
|
+
from .google_api_toolsets import CalendarToolset
|
31
|
+
from .google_api_toolsets import DocsToolset
|
32
|
+
from .google_api_toolsets import GmailToolset
|
33
|
+
from .google_api_toolsets import SheetsToolset
|
34
|
+
from .google_api_toolsets import SlidesToolset
|
35
|
+
from .google_api_toolsets import YoutubeToolset
|
@@ -29,28 +29,34 @@ from ..tool_context import ToolContext
|
|
29
29
|
|
30
30
|
class GoogleApiTool(BaseTool):
|
31
31
|
|
32
|
-
def __init__(
|
32
|
+
def __init__(
|
33
|
+
self,
|
34
|
+
rest_api_tool: RestApiTool,
|
35
|
+
client_id: Optional[str] = None,
|
36
|
+
client_secret: Optional[str] = None,
|
37
|
+
):
|
33
38
|
super().__init__(
|
34
39
|
name=rest_api_tool.name,
|
35
40
|
description=rest_api_tool.description,
|
36
41
|
is_long_running=rest_api_tool.is_long_running,
|
37
42
|
)
|
38
|
-
self.
|
43
|
+
self._rest_api_tool = rest_api_tool
|
44
|
+
self.configure_auth(client_id, client_secret)
|
39
45
|
|
40
46
|
@override
|
41
47
|
def _get_declaration(self) -> FunctionDeclaration:
|
42
|
-
return self.
|
48
|
+
return self._rest_api_tool._get_declaration()
|
43
49
|
|
44
50
|
@override
|
45
51
|
async def run_async(
|
46
52
|
self, *, args: dict[str, Any], tool_context: Optional[ToolContext]
|
47
53
|
) -> Dict[str, Any]:
|
48
|
-
return await self.
|
54
|
+
return await self._rest_api_tool.run_async(
|
49
55
|
args=args, tool_context=tool_context
|
50
56
|
)
|
51
57
|
|
52
58
|
def configure_auth(self, client_id: str, client_secret: str):
|
53
|
-
self.
|
59
|
+
self._rest_api_tool.auth_credential = AuthCredential(
|
54
60
|
auth_type=AuthCredentialTypes.OPEN_ID_CONNECT,
|
55
61
|
oauth2=OAuth2Auth(
|
56
62
|
client_id=client_id,
|
@@ -0,0 +1,126 @@
|
|
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 inspect
|
18
|
+
import os
|
19
|
+
from typing import Any
|
20
|
+
from typing import List
|
21
|
+
from typing import Optional
|
22
|
+
from typing import Type
|
23
|
+
from typing import Union
|
24
|
+
|
25
|
+
from typing_extensions import override
|
26
|
+
|
27
|
+
from ...agents.readonly_context import ReadonlyContext
|
28
|
+
from ...auth import OpenIdConnectWithConfig
|
29
|
+
from ...tools.base_toolset import BaseToolset
|
30
|
+
from ...tools.base_toolset import ToolPredicate
|
31
|
+
from ..openapi_tool import OpenAPIToolset
|
32
|
+
from .google_api_tool import GoogleApiTool
|
33
|
+
from .googleapi_to_openapi_converter import GoogleApiToOpenApiConverter
|
34
|
+
|
35
|
+
|
36
|
+
class GoogleApiToolset(BaseToolset):
|
37
|
+
"""Google API Toolset contains tools for interacting with Google APIs.
|
38
|
+
|
39
|
+
Usually one toolsets will contains tools only related to one Google API, e.g.
|
40
|
+
Google Bigquery API toolset will contains tools only related to Google
|
41
|
+
Bigquery API, like list dataset tool, list table tool etc.
|
42
|
+
"""
|
43
|
+
|
44
|
+
def __init__(
|
45
|
+
self,
|
46
|
+
api_name: str,
|
47
|
+
api_version: str,
|
48
|
+
client_id: Optional[str] = None,
|
49
|
+
client_secret: Optional[str] = None,
|
50
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
51
|
+
):
|
52
|
+
self.api_name = api_name
|
53
|
+
self.api_version = api_version
|
54
|
+
self._client_id = client_id
|
55
|
+
self._client_secret = client_secret
|
56
|
+
self._openapi_toolset = self._load_toolset_with_oidc_auth()
|
57
|
+
self.tool_filter = tool_filter
|
58
|
+
|
59
|
+
def _is_tool_selected(
|
60
|
+
self, tool: GoogleApiTool, readonly_context: ReadonlyContext
|
61
|
+
) -> bool:
|
62
|
+
if not self.tool_filter:
|
63
|
+
return True
|
64
|
+
|
65
|
+
if isinstance(self.tool_filter, ToolPredicate):
|
66
|
+
return self.tool_filter(tool, readonly_context)
|
67
|
+
|
68
|
+
if isinstance(self.tool_filter, list):
|
69
|
+
return tool.name in self.tool_filter
|
70
|
+
|
71
|
+
return False
|
72
|
+
|
73
|
+
@override
|
74
|
+
async def get_tools(
|
75
|
+
self, readonly_context: Optional[ReadonlyContext] = None
|
76
|
+
) -> List[GoogleApiTool]:
|
77
|
+
"""Get all tools in the toolset."""
|
78
|
+
tools = []
|
79
|
+
|
80
|
+
return [
|
81
|
+
GoogleApiTool(tool, self._client_id, self._client_secret)
|
82
|
+
for tool in await self._openapi_toolset.get_tools(readonly_context)
|
83
|
+
if self._is_tool_selected(tool, readonly_context)
|
84
|
+
]
|
85
|
+
|
86
|
+
def set_tool_filter(self, tool_filter: Union[ToolPredicate, List[str]]):
|
87
|
+
self.tool_filter = tool_filter
|
88
|
+
|
89
|
+
def _load_toolset_with_oidc_auth(self) -> OpenAPIToolset:
|
90
|
+
spec_dict = GoogleApiToOpenApiConverter(
|
91
|
+
self.api_name, self.api_version
|
92
|
+
).convert()
|
93
|
+
scope = list(
|
94
|
+
spec_dict['components']['securitySchemes']['oauth2']['flows'][
|
95
|
+
'authorizationCode'
|
96
|
+
]['scopes'].keys()
|
97
|
+
)[0]
|
98
|
+
return OpenAPIToolset(
|
99
|
+
spec_dict=spec_dict,
|
100
|
+
spec_str_type='yaml',
|
101
|
+
auth_scheme=OpenIdConnectWithConfig(
|
102
|
+
authorization_endpoint=(
|
103
|
+
'https://accounts.google.com/o/oauth2/v2/auth'
|
104
|
+
),
|
105
|
+
token_endpoint='https://oauth2.googleapis.com/token',
|
106
|
+
userinfo_endpoint=(
|
107
|
+
'https://openidconnect.googleapis.com/v1/userinfo'
|
108
|
+
),
|
109
|
+
revocation_endpoint='https://oauth2.googleapis.com/revoke',
|
110
|
+
token_endpoint_auth_methods_supported=[
|
111
|
+
'client_secret_post',
|
112
|
+
'client_secret_basic',
|
113
|
+
],
|
114
|
+
grant_types_supported=['authorization_code'],
|
115
|
+
scopes=[scope],
|
116
|
+
),
|
117
|
+
)
|
118
|
+
|
119
|
+
def configure_auth(self, client_id: str, client_secret: str):
|
120
|
+
self._client_id = client_id
|
121
|
+
self._client_secret = client_secret
|
122
|
+
|
123
|
+
@override
|
124
|
+
async def close(self):
|
125
|
+
if self._openapi_toolset:
|
126
|
+
await self._openapi_toolset.close()
|
@@ -0,0 +1,102 @@
|
|
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
|
+
import logging
|
17
|
+
from typing import List
|
18
|
+
from typing import Optional
|
19
|
+
from typing import Union
|
20
|
+
|
21
|
+
from google.adk.tools.base_toolset import ToolPredicate
|
22
|
+
|
23
|
+
from .google_api_toolset import GoogleApiToolset
|
24
|
+
|
25
|
+
logger = logging.getLogger("google_adk." + __name__)
|
26
|
+
|
27
|
+
|
28
|
+
class BigQueryToolset(GoogleApiToolset):
|
29
|
+
|
30
|
+
def __init__(
|
31
|
+
self,
|
32
|
+
client_id: str = None,
|
33
|
+
client_secret: str = None,
|
34
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
35
|
+
):
|
36
|
+
super().__init__("bigquery", "v2", client_id, client_secret, tool_filter)
|
37
|
+
|
38
|
+
|
39
|
+
class CalendarToolset(GoogleApiToolset):
|
40
|
+
|
41
|
+
def __init__(
|
42
|
+
self,
|
43
|
+
client_id: str = None,
|
44
|
+
client_secret: str = None,
|
45
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
46
|
+
):
|
47
|
+
super().__init__("calendar", "v3", client_id, client_secret, tool_filter)
|
48
|
+
|
49
|
+
|
50
|
+
class GmailToolset(GoogleApiToolset):
|
51
|
+
|
52
|
+
def __init__(
|
53
|
+
self,
|
54
|
+
client_id: str = None,
|
55
|
+
client_secret: str = None,
|
56
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
57
|
+
):
|
58
|
+
super().__init__("gmail", "v1", client_id, client_secret, tool_filter)
|
59
|
+
|
60
|
+
|
61
|
+
class YoutubeToolset(GoogleApiToolset):
|
62
|
+
|
63
|
+
def __init__(
|
64
|
+
self,
|
65
|
+
client_id: str = None,
|
66
|
+
client_secret: str = None,
|
67
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
68
|
+
):
|
69
|
+
super().__init__("youtube", "v3", client_id, client_secret, tool_filter)
|
70
|
+
|
71
|
+
|
72
|
+
class SlidesToolset(GoogleApiToolset):
|
73
|
+
|
74
|
+
def __init__(
|
75
|
+
self,
|
76
|
+
client_id: str = None,
|
77
|
+
client_secret: str = None,
|
78
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
79
|
+
):
|
80
|
+
super().__init__("slides", "v1", client_id, client_secret, tool_filter)
|
81
|
+
|
82
|
+
|
83
|
+
class SheetsToolset(GoogleApiToolset):
|
84
|
+
|
85
|
+
def __init__(
|
86
|
+
self,
|
87
|
+
client_id: str = None,
|
88
|
+
client_secret: str = None,
|
89
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
90
|
+
):
|
91
|
+
super().__init__("sheets", "v4", client_id, client_secret, tool_filter)
|
92
|
+
|
93
|
+
|
94
|
+
class DocsToolset(GoogleApiToolset):
|
95
|
+
|
96
|
+
def __init__(
|
97
|
+
self,
|
98
|
+
client_id: str = None,
|
99
|
+
client_secret: str = None,
|
100
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
101
|
+
):
|
102
|
+
super().__init__("docs", "v1", client_id, client_secret, tool_filter)
|