google-adk 1.0.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.
- google/adk/agents/callback_context.py +2 -1
- google/adk/agents/readonly_context.py +3 -1
- google/adk/auth/auth_credential.py +4 -1
- google/adk/cli/browser/index.html +4 -4
- google/adk/cli/browser/{main-QOEMUXM4.js → main-PKDNKWJE.js} +59 -59
- google/adk/cli/browser/polyfills-B6TNHZQ6.js +17 -0
- google/adk/cli/cli.py +3 -2
- google/adk/cli/cli_eval.py +6 -85
- google/adk/cli/cli_tools_click.py +39 -10
- google/adk/cli/fast_api.py +53 -184
- google/adk/cli/utils/agent_loader.py +137 -0
- google/adk/cli/utils/cleanup.py +40 -0
- google/adk/cli/utils/evals.py +2 -1
- google/adk/cli/utils/logs.py +2 -7
- google/adk/code_executors/code_execution_utils.py +2 -1
- google/adk/code_executors/container_code_executor.py +0 -1
- google/adk/code_executors/vertex_ai_code_executor.py +6 -8
- google/adk/evaluation/eval_case.py +3 -1
- google/adk/evaluation/eval_metrics.py +74 -0
- google/adk/evaluation/eval_result.py +86 -0
- google/adk/evaluation/eval_set.py +2 -0
- google/adk/evaluation/eval_set_results_manager.py +47 -0
- google/adk/evaluation/eval_sets_manager.py +2 -1
- google/adk/evaluation/evaluator.py +2 -0
- google/adk/evaluation/local_eval_set_results_manager.py +113 -0
- google/adk/evaluation/local_eval_sets_manager.py +4 -4
- google/adk/evaluation/response_evaluator.py +2 -1
- google/adk/evaluation/trajectory_evaluator.py +3 -2
- google/adk/examples/base_example_provider.py +1 -0
- google/adk/flows/llm_flows/base_llm_flow.py +4 -6
- google/adk/flows/llm_flows/contents.py +3 -1
- google/adk/flows/llm_flows/instructions.py +7 -77
- google/adk/flows/llm_flows/single_flow.py +1 -1
- google/adk/models/base_llm.py +2 -1
- google/adk/models/base_llm_connection.py +2 -0
- google/adk/models/google_llm.py +4 -1
- google/adk/models/lite_llm.py +3 -2
- google/adk/models/llm_response.py +2 -1
- google/adk/runners.py +36 -4
- google/adk/sessions/_session_util.py +2 -1
- google/adk/sessions/database_session_service.py +5 -8
- google/adk/sessions/vertex_ai_session_service.py +28 -13
- google/adk/telemetry.py +4 -2
- google/adk/tools/agent_tool.py +1 -1
- google/adk/tools/apihub_tool/apihub_toolset.py +1 -1
- google/adk/tools/apihub_tool/clients/apihub_client.py +10 -3
- google/adk/tools/apihub_tool/clients/secret_client.py +1 -0
- google/adk/tools/application_integration_tool/application_integration_toolset.py +6 -2
- google/adk/tools/application_integration_tool/clients/connections_client.py +8 -1
- google/adk/tools/application_integration_tool/clients/integration_client.py +3 -1
- google/adk/tools/application_integration_tool/integration_connector_tool.py +1 -1
- google/adk/tools/base_toolset.py +40 -2
- google/adk/tools/bigquery/__init__.py +28 -0
- google/adk/tools/bigquery/bigquery_credentials.py +216 -0
- google/adk/tools/bigquery/bigquery_tool.py +116 -0
- google/adk/tools/function_parameter_parse_util.py +7 -0
- google/adk/tools/function_tool.py +33 -3
- google/adk/tools/get_user_choice_tool.py +1 -0
- google/adk/tools/google_api_tool/__init__.py +17 -11
- google/adk/tools/google_api_tool/google_api_tool.py +1 -1
- google/adk/tools/google_api_tool/google_api_toolset.py +0 -14
- google/adk/tools/google_api_tool/google_api_toolsets.py +8 -2
- google/adk/tools/google_search_tool.py +2 -2
- google/adk/tools/mcp_tool/conversion_utils.py +6 -2
- google/adk/tools/mcp_tool/mcp_session_manager.py +62 -188
- google/adk/tools/mcp_tool/mcp_tool.py +27 -24
- google/adk/tools/mcp_tool/mcp_toolset.py +76 -131
- google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +1 -3
- google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +6 -7
- google/adk/tools/openapi_tool/common/common.py +5 -1
- google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +7 -2
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +2 -7
- google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +5 -1
- google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +11 -1
- google/adk/tools/toolbox_toolset.py +31 -3
- google/adk/utils/__init__.py +13 -0
- google/adk/utils/instructions_utils.py +131 -0
- google/adk/version.py +1 -1
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/METADATA +12 -15
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/RECORD +83 -78
- google/adk/agents/base_agent.py.orig +0 -330
- google/adk/cli/browser/polyfills-FFHMD2TL.js +0 -18
- google/adk/cli/fast_api.py.orig +0 -822
- google/adk/memory/base_memory_service.py.orig +0 -76
- google/adk/models/google_llm.py.orig +0 -305
- google/adk/tools/_built_in_code_execution_tool.py +0 -70
- google/adk/tools/mcp_tool/mcp_session_manager.py.orig +0 -322
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/WHEEL +0 -0
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/entry_points.txt +0 -0
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,216 @@
|
|
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 List
|
18
|
+
from typing import Optional
|
19
|
+
|
20
|
+
from fastapi.openapi.models import OAuth2
|
21
|
+
from fastapi.openapi.models import OAuthFlowAuthorizationCode
|
22
|
+
from fastapi.openapi.models import OAuthFlows
|
23
|
+
from google.auth.exceptions import RefreshError
|
24
|
+
from google.auth.transport.requests import Request
|
25
|
+
from google.oauth2.credentials import Credentials
|
26
|
+
from pydantic import BaseModel
|
27
|
+
from pydantic import model_validator
|
28
|
+
|
29
|
+
from ...auth import AuthConfig
|
30
|
+
from ...auth import AuthCredential
|
31
|
+
from ...auth import AuthCredentialTypes
|
32
|
+
from ...auth import OAuth2Auth
|
33
|
+
from ..tool_context import ToolContext
|
34
|
+
|
35
|
+
BIGQUERY_TOKEN_CACHE_KEY = "bigquery_token_cache"
|
36
|
+
|
37
|
+
|
38
|
+
class BigQueryCredentialsConfig(BaseModel):
|
39
|
+
"""Configuration for Google API tools. (Experimental)"""
|
40
|
+
|
41
|
+
# Configure the model to allow arbitrary types like Credentials
|
42
|
+
model_config = {"arbitrary_types_allowed": True}
|
43
|
+
|
44
|
+
credentials: Optional[Credentials] = None
|
45
|
+
"""the existing oauth credentials to use. If set,this credential will be used
|
46
|
+
for every end user, end users don't need to be involved in the oauthflow. This
|
47
|
+
field is mutually exclusive with client_id, client_secret and scopes.
|
48
|
+
Don't set this field unless you are sure this credential has the permission to
|
49
|
+
access every end user's data.
|
50
|
+
|
51
|
+
Example usage: when the agent is deployed in Google Cloud environment and
|
52
|
+
the service account (used as application default credentials) has access to
|
53
|
+
all the required BigQuery resource. Setting this credential to allow user to
|
54
|
+
access the BigQuery resource without end users going through oauth flow.
|
55
|
+
|
56
|
+
To get application default credential: `google.auth.default(...)`. See more
|
57
|
+
details in https://cloud.google.com/docs/authentication/application-default-credentials.
|
58
|
+
|
59
|
+
When the deployed environment cannot provide a pre-existing credential,
|
60
|
+
consider setting below client_id, client_secret and scope for end users to go
|
61
|
+
through oauth flow, so that agent can access the user data.
|
62
|
+
"""
|
63
|
+
client_id: Optional[str] = None
|
64
|
+
"""the oauth client ID to use."""
|
65
|
+
client_secret: Optional[str] = None
|
66
|
+
"""the oauth client secret to use."""
|
67
|
+
scopes: Optional[List[str]] = None
|
68
|
+
"""the scopes to use.
|
69
|
+
"""
|
70
|
+
|
71
|
+
@model_validator(mode="after")
|
72
|
+
def __post_init__(self) -> BigQueryCredentialsConfig:
|
73
|
+
"""Validate that either credentials or client ID/secret are provided."""
|
74
|
+
if not self.credentials and (not self.client_id or not self.client_secret):
|
75
|
+
raise ValueError(
|
76
|
+
"Must provide either credentials or client_id abd client_secret pair."
|
77
|
+
)
|
78
|
+
if self.credentials and (
|
79
|
+
self.client_id or self.client_secret or self.scopes
|
80
|
+
):
|
81
|
+
raise ValueError(
|
82
|
+
"Cannot provide both existing credentials and"
|
83
|
+
" client_id/client_secret/scopes."
|
84
|
+
)
|
85
|
+
|
86
|
+
if self.credentials:
|
87
|
+
self.client_id = self.credentials.client_id
|
88
|
+
self.client_secret = self.credentials.client_secret
|
89
|
+
self.scopes = self.credentials.scopes
|
90
|
+
return self
|
91
|
+
|
92
|
+
|
93
|
+
class BigQueryCredentialsManager:
|
94
|
+
"""Manages Google API credentials with automatic refresh and OAuth flow handling.
|
95
|
+
|
96
|
+
This class centralizes credential management so multiple tools can share
|
97
|
+
the same authenticated session without duplicating OAuth logic.
|
98
|
+
"""
|
99
|
+
|
100
|
+
def __init__(self, credentials_config: BigQueryCredentialsConfig):
|
101
|
+
"""Initialize the credential manager.
|
102
|
+
|
103
|
+
Args:
|
104
|
+
credentials_config: Credentials containing client id and client secrete
|
105
|
+
or default credentials
|
106
|
+
"""
|
107
|
+
self.credentials_config = credentials_config
|
108
|
+
|
109
|
+
async def get_valid_credentials(
|
110
|
+
self, tool_context: ToolContext
|
111
|
+
) -> Optional[Credentials]:
|
112
|
+
"""Get valid credentials, handling refresh and OAuth flow as needed.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
tool_context: The tool context for OAuth flow and state management
|
116
|
+
|
117
|
+
Returns:
|
118
|
+
Valid Credentials object, or None if OAuth flow is needed
|
119
|
+
"""
|
120
|
+
# First, try to get credentials from the tool context
|
121
|
+
creds_json = tool_context.state.get(BIGQUERY_TOKEN_CACHE_KEY, None)
|
122
|
+
creds = (
|
123
|
+
Credentials.from_authorized_user_info(
|
124
|
+
creds_json, self.credentials_config.scopes
|
125
|
+
)
|
126
|
+
if creds_json
|
127
|
+
else None
|
128
|
+
)
|
129
|
+
|
130
|
+
# If credentails are empty use the default credential
|
131
|
+
if not creds:
|
132
|
+
creds = self.credentials_config.credentials
|
133
|
+
|
134
|
+
# Check if we have valid credentials
|
135
|
+
if creds and creds.valid:
|
136
|
+
return creds
|
137
|
+
|
138
|
+
# Try to refresh expired credentials
|
139
|
+
if creds and creds.expired and creds.refresh_token:
|
140
|
+
try:
|
141
|
+
creds.refresh(Request())
|
142
|
+
if creds.valid:
|
143
|
+
# Cache the refreshed credentials
|
144
|
+
tool_context.state[BIGQUERY_TOKEN_CACHE_KEY] = creds.to_json()
|
145
|
+
return creds
|
146
|
+
except RefreshError:
|
147
|
+
# Refresh failed, need to re-authenticate
|
148
|
+
pass
|
149
|
+
|
150
|
+
# Need to perform OAuth flow
|
151
|
+
return await self._perform_oauth_flow(tool_context)
|
152
|
+
|
153
|
+
async def _perform_oauth_flow(
|
154
|
+
self, tool_context: ToolContext
|
155
|
+
) -> Optional[Credentials]:
|
156
|
+
"""Perform OAuth flow to get new credentials.
|
157
|
+
|
158
|
+
Args:
|
159
|
+
tool_context: The tool context for OAuth flow
|
160
|
+
required_scopes: Set of required OAuth scopes
|
161
|
+
|
162
|
+
Returns:
|
163
|
+
New Credentials object, or None if flow is in progress
|
164
|
+
"""
|
165
|
+
|
166
|
+
# Create OAuth configuration
|
167
|
+
auth_scheme = OAuth2(
|
168
|
+
flows=OAuthFlows(
|
169
|
+
authorizationCode=OAuthFlowAuthorizationCode(
|
170
|
+
authorizationUrl="https://accounts.google.com/o/oauth2/auth",
|
171
|
+
tokenUrl="https://oauth2.googleapis.com/token",
|
172
|
+
scopes={
|
173
|
+
scope: f"Access to {scope}"
|
174
|
+
for scope in self.credentials_config.scopes
|
175
|
+
},
|
176
|
+
)
|
177
|
+
)
|
178
|
+
)
|
179
|
+
|
180
|
+
auth_credential = AuthCredential(
|
181
|
+
auth_type=AuthCredentialTypes.OAUTH2,
|
182
|
+
oauth2=OAuth2Auth(
|
183
|
+
client_id=self.credentials_config.client_id,
|
184
|
+
client_secret=self.credentials_config.client_secret,
|
185
|
+
),
|
186
|
+
)
|
187
|
+
|
188
|
+
# Check if OAuth response is available
|
189
|
+
auth_response = tool_context.get_auth_response(
|
190
|
+
AuthConfig(auth_scheme=auth_scheme, raw_auth_credential=auth_credential)
|
191
|
+
)
|
192
|
+
|
193
|
+
if auth_response:
|
194
|
+
# OAuth flow completed, create credentials
|
195
|
+
creds = Credentials(
|
196
|
+
token=auth_response.oauth2.access_token,
|
197
|
+
refresh_token=auth_response.oauth2.refresh_token,
|
198
|
+
token_uri=auth_scheme.flows.authorizationCode.tokenUrl,
|
199
|
+
client_id=self.credentials_config.client_id,
|
200
|
+
client_secret=self.credentials_config.client_secret,
|
201
|
+
scopes=list(self.credentials_config.scopes),
|
202
|
+
)
|
203
|
+
|
204
|
+
# Cache the new credentials
|
205
|
+
tool_context.state[BIGQUERY_TOKEN_CACHE_KEY] = creds.to_json()
|
206
|
+
|
207
|
+
return creds
|
208
|
+
else:
|
209
|
+
# Request OAuth flow
|
210
|
+
tool_context.request_credential(
|
211
|
+
AuthConfig(
|
212
|
+
auth_scheme=auth_scheme,
|
213
|
+
raw_auth_credential=auth_credential,
|
214
|
+
)
|
215
|
+
)
|
216
|
+
return None
|
@@ -0,0 +1,116 @@
|
|
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 inspect
|
17
|
+
from typing import Any
|
18
|
+
from typing import Callable
|
19
|
+
from typing import Optional
|
20
|
+
|
21
|
+
from google.oauth2.credentials import Credentials
|
22
|
+
from typing_extensions import override
|
23
|
+
|
24
|
+
from ..function_tool import FunctionTool
|
25
|
+
from ..tool_context import ToolContext
|
26
|
+
from .bigquery_credentials import BigQueryCredentialsConfig
|
27
|
+
from .bigquery_credentials import BigQueryCredentialsManager
|
28
|
+
|
29
|
+
|
30
|
+
class BigQueryTool(FunctionTool):
|
31
|
+
"""GoogleApiTool class for tools that call Google APIs.
|
32
|
+
|
33
|
+
This class is for developers to handcraft customized Google API tools rather
|
34
|
+
than auto generate Google API tools based on API specs.
|
35
|
+
|
36
|
+
This class handles all the OAuth complexity, credential management,
|
37
|
+
and common Google API patterns so subclasses can focus on their
|
38
|
+
specific functionality.
|
39
|
+
"""
|
40
|
+
|
41
|
+
def __init__(
|
42
|
+
self,
|
43
|
+
func: Callable[..., Any],
|
44
|
+
credentials: Optional[BigQueryCredentialsConfig] = None,
|
45
|
+
):
|
46
|
+
"""Initialize the Google API tool.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
func: callable that impelments the tool's logic, can accept one
|
50
|
+
'credential" parameter
|
51
|
+
credentials: credentials used to call Google API. If None, then we don't
|
52
|
+
hanlde the auth logic
|
53
|
+
"""
|
54
|
+
super().__init__(func=func)
|
55
|
+
self._ignore_params.append("credentials")
|
56
|
+
self.credentials_manager = (
|
57
|
+
BigQueryCredentialsManager(credentials) if credentials else None
|
58
|
+
)
|
59
|
+
|
60
|
+
@override
|
61
|
+
async def run_async(
|
62
|
+
self, *, args: dict[str, Any], tool_context: ToolContext
|
63
|
+
) -> Any:
|
64
|
+
"""Main entry point for tool execution with credential handling.
|
65
|
+
|
66
|
+
This method handles all the OAuth complexity and then delegates
|
67
|
+
to the subclass's run_async_with_credential method.
|
68
|
+
"""
|
69
|
+
try:
|
70
|
+
# Get valid credentials
|
71
|
+
credentials = (
|
72
|
+
await self.credentials_manager.get_valid_credentials(tool_context)
|
73
|
+
if self.credentials_manager
|
74
|
+
else None
|
75
|
+
)
|
76
|
+
|
77
|
+
if credentials is None and self.credentials_manager:
|
78
|
+
# OAuth flow in progress
|
79
|
+
return (
|
80
|
+
"User authorization is required to access Google services for"
|
81
|
+
f" {self.name}. Please complete the authorization flow."
|
82
|
+
)
|
83
|
+
|
84
|
+
# Execute the tool's specific logic with valid credentials
|
85
|
+
|
86
|
+
return await self._run_async_with_credential(
|
87
|
+
credentials, args, tool_context
|
88
|
+
)
|
89
|
+
|
90
|
+
except Exception as ex:
|
91
|
+
return {
|
92
|
+
"status": "ERROR",
|
93
|
+
"error_details": str(ex),
|
94
|
+
}
|
95
|
+
|
96
|
+
async def _run_async_with_credential(
|
97
|
+
self,
|
98
|
+
credentials: Credentials,
|
99
|
+
args: dict[str, Any],
|
100
|
+
tool_context: ToolContext,
|
101
|
+
) -> Any:
|
102
|
+
"""Execute the tool's specific logic with valid credentials.
|
103
|
+
|
104
|
+
Args:
|
105
|
+
credentials: Valid Google OAuth credentials
|
106
|
+
args: Arguments passed to the tool
|
107
|
+
tool_context: Tool execution context
|
108
|
+
|
109
|
+
Returns:
|
110
|
+
The result of the tool execution
|
111
|
+
"""
|
112
|
+
args_to_call = args.copy()
|
113
|
+
signature = inspect.signature(self.func)
|
114
|
+
if "credentials" in signature.parameters:
|
115
|
+
args_to_call["credentials"] = credentials
|
116
|
+
return await super().run_async(args=args_to_call, tool_context=tool_context)
|
@@ -289,6 +289,13 @@ def _parse_schema_from_parameter(
|
|
289
289
|
)
|
290
290
|
_raise_if_schema_unsupported(variant, schema)
|
291
291
|
return schema
|
292
|
+
if param.annotation is None:
|
293
|
+
# https://swagger.io/docs/specification/v3_0/data-models/data-types/#null
|
294
|
+
# null is not a valid type in schema, use object instead.
|
295
|
+
schema.type = types.Type.OBJECT
|
296
|
+
schema.nullable = True
|
297
|
+
_raise_if_schema_unsupported(variant, schema)
|
298
|
+
return schema
|
292
299
|
raise ValueError(
|
293
300
|
f'Failed to parse the parameter {param} of function {func_name} for'
|
294
301
|
' automatic function calling. Automatic function calling works best with'
|
@@ -33,8 +33,31 @@ class FunctionTool(BaseTool):
|
|
33
33
|
"""
|
34
34
|
|
35
35
|
def __init__(self, func: Callable[..., Any]):
|
36
|
-
|
36
|
+
"""Extract metadata from a callable object."""
|
37
|
+
name = ''
|
38
|
+
doc = ''
|
39
|
+
# Handle different types of callables
|
40
|
+
if hasattr(func, '__name__'):
|
41
|
+
# Regular functions, unbound methods, etc.
|
42
|
+
name = func.__name__
|
43
|
+
elif hasattr(func, '__class__'):
|
44
|
+
# Callable objects, bound methods, etc.
|
45
|
+
name = func.__class__.__name__
|
46
|
+
|
47
|
+
# Get documentation (prioritize direct __doc__ if available)
|
48
|
+
if hasattr(func, '__doc__') and func.__doc__:
|
49
|
+
doc = func.__doc__
|
50
|
+
elif (
|
51
|
+
hasattr(func, '__call__')
|
52
|
+
and hasattr(func.__call__, '__doc__')
|
53
|
+
and func.__call__.__doc__
|
54
|
+
):
|
55
|
+
# For callable objects, try to get docstring from __call__ method
|
56
|
+
doc = func.__call__.__doc__
|
57
|
+
|
58
|
+
super().__init__(name=name, description=doc)
|
37
59
|
self.func = func
|
60
|
+
self._ignore_params = ['tool_context', 'input_stream']
|
38
61
|
|
39
62
|
@override
|
40
63
|
def _get_declaration(self) -> Optional[types.FunctionDeclaration]:
|
@@ -43,7 +66,7 @@ class FunctionTool(BaseTool):
|
|
43
66
|
func=self.func,
|
44
67
|
# The model doesn't understand the function context.
|
45
68
|
# input_stream is for streaming tool
|
46
|
-
ignore_params=
|
69
|
+
ignore_params=self._ignore_params,
|
47
70
|
variant=self._api_variant,
|
48
71
|
)
|
49
72
|
)
|
@@ -76,7 +99,14 @@ class FunctionTool(BaseTool):
|
|
76
99
|
You could retry calling this tool, but it is IMPORTANT for you to provide all the mandatory parameters."""
|
77
100
|
return {'error': error_str}
|
78
101
|
|
79
|
-
|
102
|
+
# Functions are callable objects, but not all callable objects are functions
|
103
|
+
# checking coroutine function is not enough. We also need to check whether
|
104
|
+
# Callable's __call__ function is a coroutine funciton
|
105
|
+
if (
|
106
|
+
inspect.iscoroutinefunction(self.func)
|
107
|
+
or hasattr(self.func, '__call__')
|
108
|
+
and inspect.iscoroutinefunction(self.func.__call__)
|
109
|
+
):
|
80
110
|
return await self.func(**args_to_call) or {}
|
81
111
|
else:
|
82
112
|
return self.func(**args_to_call) or {}
|
@@ -11,18 +11,12 @@
|
|
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
|
-
__all__ = [
|
15
|
-
'BigQueryToolset',
|
16
|
-
'CalendarToolset',
|
17
|
-
'GmailToolset',
|
18
|
-
'YoutubeToolset',
|
19
|
-
'SlidesToolset',
|
20
|
-
'SheetsToolset',
|
21
|
-
'DocsToolset',
|
22
|
-
'GoogleApiToolset',
|
23
|
-
'GoogleApiTool',
|
24
|
-
]
|
25
14
|
|
15
|
+
"""Auto-generated tools and toolsets for Google APIs.
|
16
|
+
|
17
|
+
These tools and toolsets are auto-generated based on the API specifications
|
18
|
+
provided by the Google API Discovery API.
|
19
|
+
"""
|
26
20
|
|
27
21
|
from .google_api_tool import GoogleApiTool
|
28
22
|
from .google_api_toolset import GoogleApiToolset
|
@@ -33,3 +27,15 @@ from .google_api_toolsets import GmailToolset
|
|
33
27
|
from .google_api_toolsets import SheetsToolset
|
34
28
|
from .google_api_toolsets import SlidesToolset
|
35
29
|
from .google_api_toolsets import YoutubeToolset
|
30
|
+
|
31
|
+
__all__ = [
|
32
|
+
'BigQueryToolset',
|
33
|
+
'CalendarToolset',
|
34
|
+
'GmailToolset',
|
35
|
+
'YoutubeToolset',
|
36
|
+
'SlidesToolset',
|
37
|
+
'SheetsToolset',
|
38
|
+
'DocsToolset',
|
39
|
+
'GoogleApiToolset',
|
40
|
+
'GoogleApiTool',
|
41
|
+
]
|
@@ -19,10 +19,10 @@ from typing import Optional
|
|
19
19
|
from google.genai.types import FunctionDeclaration
|
20
20
|
from typing_extensions import override
|
21
21
|
|
22
|
+
from .. import BaseTool
|
22
23
|
from ...auth import AuthCredential
|
23
24
|
from ...auth import AuthCredentialTypes
|
24
25
|
from ...auth import OAuth2Auth
|
25
|
-
from .. import BaseTool
|
26
26
|
from ..openapi_tool import RestApiTool
|
27
27
|
from ..tool_context import ToolContext
|
28
28
|
|
@@ -56,20 +56,6 @@ class GoogleApiToolset(BaseToolset):
|
|
56
56
|
self._openapi_toolset = self._load_toolset_with_oidc_auth()
|
57
57
|
self.tool_filter = tool_filter
|
58
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
59
|
@override
|
74
60
|
async def get_tools(
|
75
61
|
self, readonly_context: Optional[ReadonlyContext] = None
|
@@ -18,14 +18,14 @@ from typing import List
|
|
18
18
|
from typing import Optional
|
19
19
|
from typing import Union
|
20
20
|
|
21
|
-
from
|
22
|
-
|
21
|
+
from ..base_toolset import ToolPredicate
|
23
22
|
from .google_api_toolset import GoogleApiToolset
|
24
23
|
|
25
24
|
logger = logging.getLogger("google_adk." + __name__)
|
26
25
|
|
27
26
|
|
28
27
|
class BigQueryToolset(GoogleApiToolset):
|
28
|
+
"""Auto-generated Bigquery toolset based on Google BigQuery API v2 spec exposed by Google API discovery API"""
|
29
29
|
|
30
30
|
def __init__(
|
31
31
|
self,
|
@@ -37,6 +37,7 @@ class BigQueryToolset(GoogleApiToolset):
|
|
37
37
|
|
38
38
|
|
39
39
|
class CalendarToolset(GoogleApiToolset):
|
40
|
+
"""Auto-generated Calendar toolset based on Google Calendar API v3 spec exposed by Google API discovery API"""
|
40
41
|
|
41
42
|
def __init__(
|
42
43
|
self,
|
@@ -48,6 +49,7 @@ class CalendarToolset(GoogleApiToolset):
|
|
48
49
|
|
49
50
|
|
50
51
|
class GmailToolset(GoogleApiToolset):
|
52
|
+
"""Auto-generated Gmail toolset based on Google Gmail API v1 spec exposed by Google API discovery API"""
|
51
53
|
|
52
54
|
def __init__(
|
53
55
|
self,
|
@@ -59,6 +61,7 @@ class GmailToolset(GoogleApiToolset):
|
|
59
61
|
|
60
62
|
|
61
63
|
class YoutubeToolset(GoogleApiToolset):
|
64
|
+
"""Auto-generated Youtube toolset based on Youtube API v3 spec exposed by Google API discovery API"""
|
62
65
|
|
63
66
|
def __init__(
|
64
67
|
self,
|
@@ -70,6 +73,7 @@ class YoutubeToolset(GoogleApiToolset):
|
|
70
73
|
|
71
74
|
|
72
75
|
class SlidesToolset(GoogleApiToolset):
|
76
|
+
"""Auto-generated Slides toolset based on Google Slides API v1 spec exposed by Google API discovery API"""
|
73
77
|
|
74
78
|
def __init__(
|
75
79
|
self,
|
@@ -81,6 +85,7 @@ class SlidesToolset(GoogleApiToolset):
|
|
81
85
|
|
82
86
|
|
83
87
|
class SheetsToolset(GoogleApiToolset):
|
88
|
+
"""Auto-generated Sheets toolset based on Google Sheets API v4 spec exposed by Google API discovery API"""
|
84
89
|
|
85
90
|
def __init__(
|
86
91
|
self,
|
@@ -92,6 +97,7 @@ class SheetsToolset(GoogleApiToolset):
|
|
92
97
|
|
93
98
|
|
94
99
|
class DocsToolset(GoogleApiToolset):
|
100
|
+
"""Auto-generated Docs toolset based on Google Docs API v1 spec exposed by Google API discovery API"""
|
95
101
|
|
96
102
|
def __init__(
|
97
103
|
self,
|
@@ -46,7 +46,7 @@ class GoogleSearchTool(BaseTool):
|
|
46
46
|
) -> None:
|
47
47
|
llm_request.config = llm_request.config or types.GenerateContentConfig()
|
48
48
|
llm_request.config.tools = llm_request.config.tools or []
|
49
|
-
if llm_request.model and
|
49
|
+
if llm_request.model and 'gemini-1' in llm_request.model:
|
50
50
|
if llm_request.config.tools:
|
51
51
|
print(llm_request.config.tools)
|
52
52
|
raise ValueError(
|
@@ -55,7 +55,7 @@ class GoogleSearchTool(BaseTool):
|
|
55
55
|
llm_request.config.tools.append(
|
56
56
|
types.Tool(google_search_retrieval=types.GoogleSearchRetrieval())
|
57
57
|
)
|
58
|
-
elif llm_request.model and
|
58
|
+
elif llm_request.model and 'gemini-2' in llm_request.model:
|
59
59
|
llm_request.config.tools.append(
|
60
60
|
types.Tool(google_search=types.GoogleSearch())
|
61
61
|
)
|
@@ -12,9 +12,13 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
from typing import Any
|
16
|
-
from
|
15
|
+
from typing import Any
|
16
|
+
from typing import Dict
|
17
|
+
|
18
|
+
from google.genai.types import Schema
|
19
|
+
from google.genai.types import Type
|
17
20
|
import mcp.types as mcp_types
|
21
|
+
|
18
22
|
from ..base_tool import BaseTool
|
19
23
|
|
20
24
|
|