google-adk 0.4.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google/adk/agents/active_streaming_tool.py +1 -0
- google/adk/agents/base_agent.py +91 -47
- google/adk/agents/base_agent.py.orig +330 -0
- google/adk/agents/callback_context.py +4 -9
- google/adk/agents/invocation_context.py +1 -0
- google/adk/agents/langgraph_agent.py +1 -0
- google/adk/agents/live_request_queue.py +1 -0
- google/adk/agents/llm_agent.py +172 -35
- google/adk/agents/loop_agent.py +1 -1
- google/adk/agents/parallel_agent.py +7 -0
- google/adk/agents/readonly_context.py +7 -1
- google/adk/agents/run_config.py +5 -1
- google/adk/agents/sequential_agent.py +31 -0
- google/adk/agents/transcription_entry.py +5 -2
- google/adk/artifacts/base_artifact_service.py +5 -10
- google/adk/artifacts/gcs_artifact_service.py +9 -9
- google/adk/artifacts/in_memory_artifact_service.py +6 -6
- google/adk/auth/auth_credential.py +9 -5
- google/adk/auth/auth_preprocessor.py +7 -1
- google/adk/auth/auth_tool.py +3 -4
- google/adk/cli/agent_graph.py +5 -5
- google/adk/cli/browser/index.html +2 -2
- google/adk/cli/browser/{main-HWIBUY2R.js → main-QOEMUXM4.js} +58 -58
- google/adk/cli/cli.py +7 -7
- google/adk/cli/cli_deploy.py +7 -2
- google/adk/cli/cli_eval.py +181 -106
- google/adk/cli/cli_tools_click.py +147 -62
- google/adk/cli/fast_api.py +340 -158
- google/adk/cli/fast_api.py.orig +822 -0
- google/adk/cli/utils/common.py +23 -0
- google/adk/cli/utils/evals.py +83 -1
- google/adk/cli/utils/logs.py +13 -5
- google/adk/code_executors/__init__.py +3 -1
- google/adk/code_executors/built_in_code_executor.py +52 -0
- google/adk/evaluation/__init__.py +1 -1
- google/adk/evaluation/agent_evaluator.py +168 -128
- google/adk/evaluation/eval_case.py +102 -0
- google/adk/evaluation/eval_set.py +37 -0
- google/adk/evaluation/eval_sets_manager.py +42 -0
- google/adk/evaluation/evaluation_constants.py +1 -0
- google/adk/evaluation/evaluation_generator.py +89 -114
- google/adk/evaluation/evaluator.py +56 -0
- google/adk/evaluation/local_eval_sets_manager.py +264 -0
- google/adk/evaluation/response_evaluator.py +107 -3
- google/adk/evaluation/trajectory_evaluator.py +83 -2
- google/adk/events/event.py +7 -1
- google/adk/events/event_actions.py +7 -1
- google/adk/examples/example.py +1 -0
- google/adk/examples/example_util.py +3 -2
- google/adk/flows/__init__.py +0 -1
- google/adk/flows/llm_flows/_code_execution.py +19 -11
- google/adk/flows/llm_flows/audio_transcriber.py +4 -3
- google/adk/flows/llm_flows/base_llm_flow.py +86 -22
- google/adk/flows/llm_flows/basic.py +3 -0
- google/adk/flows/llm_flows/functions.py +10 -9
- google/adk/flows/llm_flows/instructions.py +28 -9
- google/adk/flows/llm_flows/single_flow.py +1 -1
- google/adk/memory/__init__.py +1 -1
- google/adk/memory/_utils.py +23 -0
- google/adk/memory/base_memory_service.py +25 -21
- google/adk/memory/base_memory_service.py.orig +76 -0
- google/adk/memory/in_memory_memory_service.py +59 -27
- google/adk/memory/memory_entry.py +37 -0
- google/adk/memory/vertex_ai_rag_memory_service.py +40 -17
- google/adk/models/anthropic_llm.py +36 -11
- google/adk/models/base_llm.py +45 -4
- google/adk/models/gemini_llm_connection.py +15 -2
- google/adk/models/google_llm.py +9 -44
- google/adk/models/google_llm.py.orig +305 -0
- google/adk/models/lite_llm.py +94 -38
- google/adk/models/llm_request.py +1 -1
- google/adk/models/llm_response.py +15 -3
- google/adk/models/registry.py +1 -1
- google/adk/runners.py +68 -44
- google/adk/sessions/__init__.py +1 -1
- google/adk/sessions/_session_util.py +14 -0
- google/adk/sessions/base_session_service.py +8 -32
- google/adk/sessions/database_session_service.py +58 -61
- google/adk/sessions/in_memory_session_service.py +108 -26
- google/adk/sessions/session.py +4 -0
- google/adk/sessions/vertex_ai_session_service.py +23 -45
- google/adk/telemetry.py +3 -0
- google/adk/tools/__init__.py +4 -7
- google/adk/tools/{built_in_code_execution_tool.py → _built_in_code_execution_tool.py} +11 -0
- google/adk/tools/_memory_entry_utils.py +30 -0
- google/adk/tools/agent_tool.py +16 -13
- google/adk/tools/apihub_tool/apihub_toolset.py +55 -74
- google/adk/tools/application_integration_tool/application_integration_toolset.py +107 -85
- google/adk/tools/application_integration_tool/clients/connections_client.py +29 -25
- google/adk/tools/application_integration_tool/clients/integration_client.py +6 -6
- google/adk/tools/application_integration_tool/integration_connector_tool.py +69 -26
- google/adk/tools/base_toolset.py +58 -0
- google/adk/tools/enterprise_search_tool.py +65 -0
- google/adk/tools/function_parameter_parse_util.py +2 -2
- google/adk/tools/google_api_tool/__init__.py +18 -70
- google/adk/tools/google_api_tool/google_api_tool.py +11 -5
- google/adk/tools/google_api_tool/google_api_toolset.py +126 -0
- google/adk/tools/google_api_tool/google_api_toolsets.py +102 -0
- google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +40 -42
- google/adk/tools/langchain_tool.py +96 -49
- google/adk/tools/load_artifacts_tool.py +4 -4
- google/adk/tools/load_memory_tool.py +16 -5
- google/adk/tools/mcp_tool/__init__.py +3 -2
- google/adk/tools/mcp_tool/conversion_utils.py +1 -1
- google/adk/tools/mcp_tool/mcp_session_manager.py +167 -16
- google/adk/tools/mcp_tool/mcp_session_manager.py.orig +322 -0
- google/adk/tools/mcp_tool/mcp_tool.py +12 -12
- google/adk/tools/mcp_tool/mcp_toolset.py +155 -195
- google/adk/tools/openapi_tool/common/common.py +2 -5
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +32 -7
- google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +43 -33
- google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +1 -1
- google/adk/tools/preload_memory_tool.py +27 -18
- google/adk/tools/retrieval/__init__.py +1 -1
- google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +1 -1
- google/adk/tools/tool_context.py +4 -4
- google/adk/tools/toolbox_toolset.py +79 -0
- google/adk/tools/transfer_to_agent_tool.py +0 -1
- google/adk/version.py +1 -1
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/METADATA +7 -5
- google_adk-1.0.0.dist-info/RECORD +195 -0
- google/adk/agents/remote_agent.py +0 -50
- google/adk/tools/google_api_tool/google_api_tool_set.py +0 -110
- google/adk/tools/google_api_tool/google_api_tool_sets.py +0 -112
- google/adk/tools/toolbox_tool.py +0 -46
- google_adk-0.4.0.dist-info/RECORD +0 -179
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/WHEEL +0 -0
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/entry_points.txt +0 -0
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/licenses/LICENSE +0 -0
google/adk/tools/__init__.py
CHANGED
@@ -11,31 +11,28 @@
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
|
-
|
15
|
-
from .base_tool import BaseTool
|
14
|
+
|
16
15
|
|
17
16
|
from ..auth.auth_tool import AuthToolArguments
|
18
17
|
from .apihub_tool.apihub_toolset import APIHubToolset
|
19
|
-
from .
|
20
|
-
from .google_search_tool import google_search
|
21
|
-
from .vertex_ai_search_tool import VertexAiSearchTool
|
18
|
+
from .base_tool import BaseTool
|
22
19
|
from .example_tool import ExampleTool
|
23
20
|
from .exit_loop_tool import exit_loop
|
24
21
|
from .function_tool import FunctionTool
|
25
22
|
from .get_user_choice_tool import get_user_choice_tool as get_user_choice
|
23
|
+
from .google_search_tool import google_search
|
26
24
|
from .load_artifacts_tool import load_artifacts_tool as load_artifacts
|
27
25
|
from .load_memory_tool import load_memory_tool as load_memory
|
28
26
|
from .long_running_tool import LongRunningFunctionTool
|
29
27
|
from .preload_memory_tool import preload_memory_tool as preload_memory
|
30
28
|
from .tool_context import ToolContext
|
31
29
|
from .transfer_to_agent_tool import transfer_to_agent
|
32
|
-
|
30
|
+
from .vertex_ai_search_tool import VertexAiSearchTool
|
33
31
|
|
34
32
|
__all__ = [
|
35
33
|
'APIHubToolset',
|
36
34
|
'AuthToolArguments',
|
37
35
|
'BaseTool',
|
38
|
-
'built_in_code_execution',
|
39
36
|
'google_search',
|
40
37
|
'VertexAiSearchTool',
|
41
38
|
'ExampleTool',
|
@@ -14,8 +14,10 @@
|
|
14
14
|
|
15
15
|
from __future__ import annotations
|
16
16
|
|
17
|
+
import logging
|
17
18
|
from typing import TYPE_CHECKING
|
18
19
|
|
20
|
+
from deprecated import deprecated
|
19
21
|
from google.genai import types
|
20
22
|
from typing_extensions import override
|
21
23
|
|
@@ -25,7 +27,12 @@ from .tool_context import ToolContext
|
|
25
27
|
if TYPE_CHECKING:
|
26
28
|
from ..models import LlmRequest
|
27
29
|
|
30
|
+
logger = logging.getLogger('google_adk.' + __name__)
|
28
31
|
|
32
|
+
|
33
|
+
@deprecated(
|
34
|
+
'No longer supported. Please use the new BuiltInCodeExecutor instead.'
|
35
|
+
)
|
29
36
|
class BuiltInCodeExecutionTool(BaseTool):
|
30
37
|
"""A built-in code execution tool that is automatically invoked by Gemini 2 models.
|
31
38
|
|
@@ -44,6 +51,10 @@ class BuiltInCodeExecutionTool(BaseTool):
|
|
44
51
|
tool_context: ToolContext,
|
45
52
|
llm_request: LlmRequest,
|
46
53
|
) -> None:
|
54
|
+
logger.warning(
|
55
|
+
'BuiltInCodeExecutionTool is deprecated and will be removed in 1.1.0.'
|
56
|
+
' Please use the new BuiltInCodeExecutor instead.'
|
57
|
+
)
|
47
58
|
if llm_request.model and llm_request.model.startswith('gemini-2'):
|
48
59
|
llm_request.config = llm_request.config or types.GenerateContentConfig()
|
49
60
|
llm_request.config.tools = llm_request.config.tools or []
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright 2025 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
from __future__ import annotations
|
17
|
+
|
18
|
+
from typing import TYPE_CHECKING
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from ..memory.memory_entry import MemoryEntry
|
22
|
+
|
23
|
+
|
24
|
+
def extract_text(memory: MemoryEntry, splitter: str = ' ') -> str:
|
25
|
+
"""Extracts the text from the memory entry."""
|
26
|
+
if not memory.content.parts:
|
27
|
+
return ''
|
28
|
+
return splitter.join(
|
29
|
+
[part.text for part in memory.content.parts if part.text]
|
30
|
+
)
|
google/adk/tools/agent_tool.py
CHANGED
@@ -129,7 +129,7 @@ class AgentTool(BaseTool):
|
|
129
129
|
session_service=InMemorySessionService(),
|
130
130
|
memory_service=InMemoryMemoryService(),
|
131
131
|
)
|
132
|
-
session = runner.session_service.create_session(
|
132
|
+
session = await runner.session_service.create_session(
|
133
133
|
app_name=self.agent.name,
|
134
134
|
user_id='tmp_user',
|
135
135
|
state=tool_context.state.to_dict(),
|
@@ -146,30 +146,33 @@ class AgentTool(BaseTool):
|
|
146
146
|
|
147
147
|
if runner.artifact_service:
|
148
148
|
# Forward all artifacts to parent session.
|
149
|
-
|
149
|
+
artifact_names = await runner.artifact_service.list_artifact_keys(
|
150
150
|
app_name=session.app_name,
|
151
151
|
user_id=session.user_id,
|
152
152
|
session_id=session.id,
|
153
|
-
)
|
154
|
-
|
153
|
+
)
|
154
|
+
for artifact_name in artifact_names:
|
155
|
+
if artifact := await runner.artifact_service.load_artifact(
|
155
156
|
app_name=session.app_name,
|
156
157
|
user_id=session.user_id,
|
157
158
|
session_id=session.id,
|
158
159
|
filename=artifact_name,
|
159
160
|
):
|
160
|
-
tool_context.save_artifact(
|
161
|
+
await tool_context.save_artifact(
|
162
|
+
filename=artifact_name, artifact=artifact
|
163
|
+
)
|
161
164
|
|
162
|
-
if
|
163
|
-
not last_event
|
164
|
-
or not last_event.content
|
165
|
-
or not last_event.content.parts
|
166
|
-
or not last_event.content.parts[0].text
|
167
|
-
):
|
165
|
+
if not last_event or not last_event.content or not last_event.content.parts:
|
168
166
|
return ''
|
169
167
|
if isinstance(self.agent, LlmAgent) and self.agent.output_schema:
|
168
|
+
merged_text = '\n'.join(
|
169
|
+
[p.text for p in last_event.content.parts if p.text]
|
170
|
+
)
|
170
171
|
tool_result = self.agent.output_schema.model_validate_json(
|
171
|
-
|
172
|
+
merged_text
|
172
173
|
).model_dump(exclude_none=True)
|
173
174
|
else:
|
174
|
-
tool_result =
|
175
|
+
tool_result = '\n'.join(
|
176
|
+
[p.text for p in last_event.content.parts if p.text]
|
177
|
+
)
|
175
178
|
return tool_result
|
@@ -13,19 +13,25 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
|
16
|
-
from typing import
|
16
|
+
from typing import List
|
17
|
+
from typing import Optional
|
18
|
+
from typing import Union
|
17
19
|
|
20
|
+
from typing_extensions import override
|
18
21
|
import yaml
|
19
22
|
|
23
|
+
from ...agents.readonly_context import ReadonlyContext
|
20
24
|
from ...auth.auth_credential import AuthCredential
|
21
25
|
from ...auth.auth_schemes import AuthScheme
|
26
|
+
from ..base_toolset import BaseToolset
|
27
|
+
from ..base_toolset import ToolPredicate
|
22
28
|
from ..openapi_tool.common.common import to_snake_case
|
23
29
|
from ..openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
|
24
30
|
from ..openapi_tool.openapi_spec_parser.rest_api_tool import RestApiTool
|
25
31
|
from .clients.apihub_client import APIHubClient
|
26
32
|
|
27
33
|
|
28
|
-
class APIHubToolset:
|
34
|
+
class APIHubToolset(BaseToolset):
|
29
35
|
"""APIHubTool generates tools from a given API Hub resource.
|
30
36
|
|
31
37
|
Examples:
|
@@ -34,16 +40,13 @@ class APIHubToolset:
|
|
34
40
|
apihub_toolset = APIHubToolset(
|
35
41
|
apihub_resource_name="projects/test-project/locations/us-central1/apis/test-api",
|
36
42
|
service_account_json="...",
|
43
|
+
tool_filter=lambda tool, ctx=None: tool.name in ('my_tool',
|
44
|
+
'my_other_tool')
|
37
45
|
)
|
38
46
|
|
39
47
|
# Get all available tools
|
40
|
-
agent = LlmAgent(tools=apihub_toolset
|
48
|
+
agent = LlmAgent(tools=apihub_toolset)
|
41
49
|
|
42
|
-
# Get a specific tool
|
43
|
-
agent = LlmAgent(tools=[
|
44
|
-
...
|
45
|
-
apihub_toolset.get_tool('my_tool'),
|
46
|
-
])
|
47
50
|
```
|
48
51
|
|
49
52
|
**apihub_resource_name** is the resource name from API Hub. It must include
|
@@ -70,6 +73,7 @@ class APIHubToolset:
|
|
70
73
|
auth_credential: Optional[AuthCredential] = None,
|
71
74
|
# Optionally, you can provide a custom API Hub client
|
72
75
|
apihub_client: Optional[APIHubClient] = None,
|
76
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
73
77
|
):
|
74
78
|
"""Initializes the APIHubTool with the given parameters.
|
75
79
|
|
@@ -81,12 +85,17 @@ class APIHubToolset:
|
|
81
85
|
)
|
82
86
|
|
83
87
|
# Get all available tools
|
84
|
-
agent = LlmAgent(tools=apihub_toolset
|
88
|
+
agent = LlmAgent(tools=[apihub_toolset])
|
85
89
|
|
90
|
+
apihub_toolset = APIHubToolset(
|
91
|
+
apihub_resource_name="projects/test-project/locations/us-central1/apis/test-api",
|
92
|
+
service_account_json="...",
|
93
|
+
tool_filter = ['my_tool']
|
94
|
+
)
|
86
95
|
# Get a specific tool
|
87
96
|
agent = LlmAgent(tools=[
|
88
|
-
|
89
|
-
apihub_toolset
|
97
|
+
...,
|
98
|
+
apihub_toolset,
|
90
99
|
])
|
91
100
|
```
|
92
101
|
|
@@ -118,82 +127,49 @@ class APIHubToolset:
|
|
118
127
|
lazy_load_spec: If True, the spec will be loaded lazily when needed.
|
119
128
|
Otherwise, the spec will be loaded immediately and the tools will be
|
120
129
|
generated during initialization.
|
130
|
+
tool_filter: The filter used to filter the tools in the toolset. It can
|
131
|
+
be either a tool predicate or a list of tool names of the tools to
|
132
|
+
expose.
|
121
133
|
"""
|
122
134
|
self.name = name
|
123
135
|
self.description = description
|
124
|
-
self.
|
125
|
-
self.
|
126
|
-
self.
|
136
|
+
self._apihub_resource_name = apihub_resource_name
|
137
|
+
self._lazy_load_spec = lazy_load_spec
|
138
|
+
self._apihub_client = apihub_client or APIHubClient(
|
127
139
|
access_token=access_token,
|
128
140
|
service_account_json=service_account_json,
|
129
141
|
)
|
130
142
|
|
131
|
-
self.
|
132
|
-
self.
|
133
|
-
self.
|
134
|
-
|
135
|
-
if not self.lazy_load_spec:
|
136
|
-
self._prepare_tools()
|
137
|
-
|
138
|
-
def get_tool(self, name: str) -> Optional[RestApiTool]:
|
139
|
-
"""Retrieves a specific tool by its name.
|
140
|
-
|
141
|
-
Example:
|
142
|
-
```
|
143
|
-
apihub_tool = apihub_toolset.get_tool('my_tool')
|
144
|
-
```
|
145
|
-
|
146
|
-
Args:
|
147
|
-
name: The name of the tool to retrieve.
|
148
|
-
|
149
|
-
Returns:
|
150
|
-
The tool with the given name, or None if no such tool exists.
|
151
|
-
"""
|
152
|
-
if not self._are_tools_ready():
|
153
|
-
self._prepare_tools()
|
143
|
+
self._openapi_toolset = None
|
144
|
+
self._auth_scheme = auth_scheme
|
145
|
+
self._auth_credential = auth_credential
|
146
|
+
self.tool_filter = tool_filter
|
154
147
|
|
155
|
-
|
148
|
+
if not self._lazy_load_spec:
|
149
|
+
self._prepare_toolset()
|
156
150
|
|
157
|
-
|
151
|
+
@override
|
152
|
+
async def get_tools(
|
153
|
+
self, readonly_context: Optional[ReadonlyContext] = None
|
154
|
+
) -> List[RestApiTool]:
|
158
155
|
"""Retrieves all available tools.
|
159
156
|
|
160
157
|
Returns:
|
161
158
|
A list of all available RestApiTool objects.
|
162
159
|
"""
|
163
|
-
if not self.
|
164
|
-
self.
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
def _are_tools_ready(self) -> bool:
|
169
|
-
return not self.lazy_load_spec or self.generated_tools
|
170
|
-
|
171
|
-
def _prepare_tools(self) -> str:
|
172
|
-
"""Fetches the spec from API Hub and generates the tools.
|
160
|
+
if not self._openapi_toolset:
|
161
|
+
self._prepare_toolset()
|
162
|
+
if not self._openapi_toolset:
|
163
|
+
return []
|
164
|
+
return await self._openapi_toolset.get_tools(readonly_context)
|
173
165
|
|
174
|
-
|
175
|
-
|
176
|
-
"""
|
166
|
+
def _prepare_toolset(self) -> None:
|
167
|
+
"""Fetches the spec from API Hub and generates the toolset."""
|
177
168
|
# For each API, get the first version and the first spec of that version.
|
178
|
-
|
179
|
-
self.generated_tools: Dict[str, RestApiTool] = {}
|
180
|
-
|
181
|
-
tools = self._parse_spec_to_tools(spec)
|
182
|
-
for tool in tools:
|
183
|
-
self.generated_tools[tool.name] = tool
|
184
|
-
|
185
|
-
def _parse_spec_to_tools(self, spec_str: str) -> List[RestApiTool]:
|
186
|
-
"""Parses the spec string to a list of RestApiTool.
|
187
|
-
|
188
|
-
Args:
|
189
|
-
spec_str: The spec string to parse.
|
190
|
-
|
191
|
-
Returns:
|
192
|
-
A list of RestApiTool objects.
|
193
|
-
"""
|
169
|
+
spec_str = self._apihub_client.get_spec_content(self._apihub_resource_name)
|
194
170
|
spec_dict = yaml.safe_load(spec_str)
|
195
171
|
if not spec_dict:
|
196
|
-
return
|
172
|
+
return
|
197
173
|
|
198
174
|
self.name = self.name or to_snake_case(
|
199
175
|
spec_dict.get('info', {}).get('title', 'unnamed')
|
@@ -201,9 +177,14 @@ class APIHubToolset:
|
|
201
177
|
self.description = self.description or spec_dict.get('info', {}).get(
|
202
178
|
'description', ''
|
203
179
|
)
|
204
|
-
|
180
|
+
self._openapi_toolset = OpenAPIToolset(
|
205
181
|
spec_dict=spec_dict,
|
206
|
-
auth_credential=self.
|
207
|
-
auth_scheme=self.
|
208
|
-
|
209
|
-
|
182
|
+
auth_credential=self._auth_credential,
|
183
|
+
auth_scheme=self._auth_scheme,
|
184
|
+
tool_filter=self.tool_filter,
|
185
|
+
)
|
186
|
+
|
187
|
+
@override
|
188
|
+
async def close(self):
|
189
|
+
if self._openapi_toolset:
|
190
|
+
await self._openapi_toolset.close()
|
@@ -12,14 +12,22 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
15
|
+
import logging
|
16
|
+
from typing import List
|
17
|
+
from typing import Optional
|
18
|
+
from typing import Union
|
16
19
|
|
17
20
|
from fastapi.openapi.models import HTTPBearer
|
21
|
+
from typing_extensions import override
|
18
22
|
|
23
|
+
from ...agents.readonly_context import ReadonlyContext
|
19
24
|
from ...auth.auth_credential import AuthCredential
|
20
25
|
from ...auth.auth_credential import AuthCredentialTypes
|
21
26
|
from ...auth.auth_credential import ServiceAccount
|
22
27
|
from ...auth.auth_credential import ServiceAccountCredential
|
28
|
+
from ...auth.auth_schemes import AuthScheme
|
29
|
+
from ..base_toolset import BaseToolset
|
30
|
+
from ..base_toolset import ToolPredicate
|
23
31
|
from ..openapi_tool.auth.auth_helpers import service_account_scheme_credential
|
24
32
|
from ..openapi_tool.openapi_spec_parser.openapi_spec_parser import OpenApiSpecParser
|
25
33
|
from ..openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
|
@@ -28,9 +36,11 @@ from .clients.connections_client import ConnectionsClient
|
|
28
36
|
from .clients.integration_client import IntegrationClient
|
29
37
|
from .integration_connector_tool import IntegrationConnectorTool
|
30
38
|
|
39
|
+
logger = logging.getLogger("google_adk." + __name__)
|
40
|
+
|
31
41
|
|
32
42
|
# TODO(cheliu): Apply a common toolset interface
|
33
|
-
class ApplicationIntegrationToolset:
|
43
|
+
class ApplicationIntegrationToolset(BaseToolset):
|
34
44
|
"""ApplicationIntegrationToolset generates tools from a given Application
|
35
45
|
|
36
46
|
Integration or Integration Connector resource.
|
@@ -42,7 +52,7 @@ class ApplicationIntegrationToolset:
|
|
42
52
|
project="test-project",
|
43
53
|
location="us-central1"
|
44
54
|
integration="test-integration",
|
45
|
-
|
55
|
+
triggers=["api_trigger/test_trigger"],
|
46
56
|
service_account_credentials={...},
|
47
57
|
)
|
48
58
|
|
@@ -63,10 +73,10 @@ class ApplicationIntegrationToolset:
|
|
63
73
|
service_account_credentials={...},
|
64
74
|
)
|
65
75
|
|
66
|
-
#
|
76
|
+
# Feed the toolset to agent
|
67
77
|
agent = LlmAgent(tools=[
|
68
|
-
|
69
|
-
|
78
|
+
...,
|
79
|
+
application_integration_toolset,
|
70
80
|
])
|
71
81
|
```
|
72
82
|
"""
|
@@ -76,100 +86,74 @@ class ApplicationIntegrationToolset:
|
|
76
86
|
project: str,
|
77
87
|
location: str,
|
78
88
|
integration: Optional[str] = None,
|
79
|
-
|
89
|
+
triggers: Optional[List[str]] = None,
|
80
90
|
connection: Optional[str] = None,
|
81
91
|
entity_operations: Optional[str] = None,
|
82
92
|
actions: Optional[str] = None,
|
83
93
|
# Optional parameter for the toolset. This is prepended to the generated
|
84
94
|
# tool/python function name.
|
85
|
-
|
95
|
+
tool_name_prefix: Optional[str] = "",
|
86
96
|
# Optional parameter for the toolset. This is appended to the generated
|
87
97
|
# tool/python function description.
|
88
98
|
tool_instructions: Optional[str] = "",
|
89
99
|
service_account_json: Optional[str] = None,
|
100
|
+
auth_scheme: Optional[AuthScheme] = None,
|
101
|
+
auth_credential: Optional[AuthCredential] = None,
|
102
|
+
tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
|
90
103
|
):
|
91
|
-
"""
|
92
|
-
|
93
|
-
Example Usage:
|
94
|
-
```
|
95
|
-
# Get all available tools for an integration with api trigger
|
96
|
-
application_integration_toolset = ApplicationIntegrationToolset(
|
97
|
-
|
98
|
-
project="test-project",
|
99
|
-
location="us-central1"
|
100
|
-
integration="test-integration",
|
101
|
-
trigger="api_trigger/test_trigger",
|
102
|
-
service_account_credentials={...},
|
103
|
-
)
|
104
|
-
|
105
|
-
# Get all available tools for a connection using entity operations and
|
106
|
-
# actions
|
107
|
-
# Note: Find the list of supported entity operations and actions for a
|
108
|
-
connection
|
109
|
-
# using integration connector apis:
|
110
|
-
#
|
111
|
-
https://cloud.google.com/integration-connectors/docs/reference/rest/v1/projects.locations.connections.connectionSchemaMetadata
|
112
|
-
application_integration_toolset = ApplicationIntegrationToolset(
|
113
|
-
project="test-project",
|
114
|
-
location="us-central1"
|
115
|
-
connection="test-connection",
|
116
|
-
entity_operations=["EntityId1": ["LIST","CREATE"], "EntityId2": []],
|
117
|
-
#empty list for actions means all operations on the entity are supported
|
118
|
-
actions=["action1"],
|
119
|
-
service_account_credentials={...},
|
120
|
-
)
|
121
|
-
|
122
|
-
# Get all available tools
|
123
|
-
agent = LlmAgent(tools=[
|
124
|
-
...
|
125
|
-
*application_integration_toolset.get_tools(),
|
126
|
-
])
|
127
|
-
```
|
104
|
+
"""Args:
|
128
105
|
|
129
106
|
Args:
|
130
107
|
project: The GCP project ID.
|
131
108
|
location: The GCP location.
|
132
109
|
integration: The integration name.
|
133
|
-
|
110
|
+
triggers: The list of trigger names in the integration.
|
134
111
|
connection: The connection name.
|
135
112
|
entity_operations: The entity operations supported by the connection.
|
136
113
|
actions: The actions supported by the connection.
|
137
|
-
|
114
|
+
tool_name_prefix: The name prefix of the generated tools.
|
138
115
|
tool_instructions: The instructions for the tool.
|
139
116
|
service_account_json: The service account configuration as a dictionary.
|
140
117
|
Required if not using default service credential. Used for fetching
|
141
118
|
the Application Integration or Integration Connector resource.
|
119
|
+
tool_filter: The filter used to filter the tools in the toolset. It can
|
120
|
+
be either a tool predicate or a list of tool names of the tools to
|
121
|
+
expose.
|
142
122
|
|
143
123
|
Raises:
|
144
|
-
ValueError: If
|
145
|
-
|
124
|
+
ValueError: If none of the following conditions are met:
|
125
|
+
- `integration` is provided.
|
126
|
+
- `connection` is provided and at least one of `entity_operations`
|
127
|
+
or `actions` is provided.
|
146
128
|
Exception: If there is an error during the initialization of the
|
147
129
|
integration or connection client.
|
148
130
|
"""
|
149
131
|
self.project = project
|
150
132
|
self.location = location
|
151
|
-
self.
|
152
|
-
self.
|
153
|
-
self.
|
154
|
-
self.
|
155
|
-
self.
|
156
|
-
self.
|
157
|
-
self.
|
158
|
-
self.
|
159
|
-
self.
|
133
|
+
self._integration = integration
|
134
|
+
self._triggers = triggers
|
135
|
+
self._connection = connection
|
136
|
+
self._entity_operations = entity_operations
|
137
|
+
self._actions = actions
|
138
|
+
self._tool_name_prefix = tool_name_prefix
|
139
|
+
self._tool_instructions = tool_instructions
|
140
|
+
self._service_account_json = service_account_json
|
141
|
+
self._auth_scheme = auth_scheme
|
142
|
+
self._auth_credential = auth_credential
|
143
|
+
self.tool_filter = tool_filter
|
160
144
|
|
161
145
|
integration_client = IntegrationClient(
|
162
146
|
project,
|
163
147
|
location,
|
164
148
|
integration,
|
165
|
-
|
149
|
+
triggers,
|
166
150
|
connection,
|
167
151
|
entity_operations,
|
168
152
|
actions,
|
169
153
|
service_account_json,
|
170
154
|
)
|
171
155
|
connection_details = {}
|
172
|
-
if integration
|
156
|
+
if integration:
|
173
157
|
spec = integration_client.get_openapi_spec_for_integration()
|
174
158
|
elif connection and (entity_operations or actions):
|
175
159
|
connections_client = ConnectionsClient(
|
@@ -177,21 +161,23 @@ class ApplicationIntegrationToolset:
|
|
177
161
|
)
|
178
162
|
connection_details = connections_client.get_connection_details()
|
179
163
|
spec = integration_client.get_openapi_spec_for_connection(
|
180
|
-
|
164
|
+
tool_name_prefix,
|
181
165
|
tool_instructions,
|
182
166
|
)
|
183
167
|
else:
|
184
168
|
raise ValueError(
|
185
|
-
"Either
|
169
|
+
"Invalid request, Either integration or (connection and"
|
186
170
|
" (entity_operations or actions)) should be provided."
|
187
171
|
)
|
188
|
-
self.
|
172
|
+
self._openapi_toolset = None
|
173
|
+
self._tools = []
|
174
|
+
self._parse_spec_to_toolset(spec, connection_details)
|
189
175
|
|
190
|
-
def
|
191
|
-
"""Parses the spec dict to
|
192
|
-
if self.
|
176
|
+
def _parse_spec_to_toolset(self, spec_dict, connection_details):
|
177
|
+
"""Parses the spec dict to OpenAPI toolset."""
|
178
|
+
if self._service_account_json:
|
193
179
|
sa_credential = ServiceAccountCredential.model_validate_json(
|
194
|
-
self.
|
180
|
+
self._service_account_json
|
195
181
|
)
|
196
182
|
service_account = ServiceAccount(
|
197
183
|
service_account_credential=sa_credential,
|
@@ -210,14 +196,13 @@ class ApplicationIntegrationToolset:
|
|
210
196
|
)
|
211
197
|
auth_scheme = HTTPBearer(bearerFormat="JWT")
|
212
198
|
|
213
|
-
if self.
|
214
|
-
|
199
|
+
if self._integration:
|
200
|
+
self._openapi_toolset = OpenAPIToolset(
|
215
201
|
spec_dict=spec_dict,
|
216
202
|
auth_credential=auth_credential,
|
217
203
|
auth_scheme=auth_scheme,
|
218
|
-
|
219
|
-
|
220
|
-
self.generated_tools[tool.name] = tool
|
204
|
+
tool_filter=self.tool_filter,
|
205
|
+
)
|
221
206
|
return
|
222
207
|
|
223
208
|
operations = OpenApiSpecParser().parse(spec_dict)
|
@@ -235,18 +220,55 @@ class ApplicationIntegrationToolset:
|
|
235
220
|
rest_api_tool.configure_auth_scheme(auth_scheme)
|
236
221
|
if auth_credential:
|
237
222
|
rest_api_tool.configure_auth_credential(auth_credential)
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
connection_name=connection_details["name"],
|
242
|
-
connection_host=connection_details["host"],
|
243
|
-
connection_service_name=connection_details["serviceName"],
|
244
|
-
entity=entity,
|
245
|
-
action=action,
|
246
|
-
operation=operation,
|
247
|
-
rest_api_tool=rest_api_tool,
|
223
|
+
|
224
|
+
auth_override_enabled = connection_details.get(
|
225
|
+
"authOverrideEnabled", False
|
248
226
|
)
|
249
|
-
self.generated_tools[tool.name] = tool
|
250
227
|
|
251
|
-
|
252
|
-
|
228
|
+
if (
|
229
|
+
self._auth_scheme
|
230
|
+
and self._auth_credential
|
231
|
+
and not auth_override_enabled
|
232
|
+
):
|
233
|
+
# Case: Auth provided, but override is OFF. Don't use provided auth.
|
234
|
+
logger.warning(
|
235
|
+
"Authentication schema and credentials are not used because"
|
236
|
+
" authOverrideEnabled is not enabled in the connection."
|
237
|
+
)
|
238
|
+
connector_auth_scheme = None
|
239
|
+
connector_auth_credential = None
|
240
|
+
else:
|
241
|
+
connector_auth_scheme = self._auth_scheme
|
242
|
+
connector_auth_credential = self._auth_credential
|
243
|
+
|
244
|
+
self._tools.append(
|
245
|
+
IntegrationConnectorTool(
|
246
|
+
name=rest_api_tool.name,
|
247
|
+
description=rest_api_tool.description,
|
248
|
+
connection_name=connection_details["name"],
|
249
|
+
connection_host=connection_details["host"],
|
250
|
+
connection_service_name=connection_details["serviceName"],
|
251
|
+
entity=entity,
|
252
|
+
action=action,
|
253
|
+
operation=operation,
|
254
|
+
rest_api_tool=rest_api_tool,
|
255
|
+
auth_scheme=connector_auth_scheme,
|
256
|
+
auth_credential=connector_auth_credential,
|
257
|
+
)
|
258
|
+
)
|
259
|
+
|
260
|
+
@override
|
261
|
+
async def get_tools(
|
262
|
+
self,
|
263
|
+
readonly_context: Optional[ReadonlyContext] = None,
|
264
|
+
) -> List[RestApiTool]:
|
265
|
+
return (
|
266
|
+
self._tools
|
267
|
+
if self._openapi_toolset is None
|
268
|
+
else await self._openapi_toolset.get_tools(readonly_context)
|
269
|
+
)
|
270
|
+
|
271
|
+
@override
|
272
|
+
async def close(self) -> None:
|
273
|
+
if self._openapi_toolset:
|
274
|
+
await self._openapi_toolset.close()
|