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.
Files changed (129) hide show
  1. google/adk/agents/active_streaming_tool.py +1 -0
  2. google/adk/agents/base_agent.py +91 -47
  3. google/adk/agents/base_agent.py.orig +330 -0
  4. google/adk/agents/callback_context.py +4 -9
  5. google/adk/agents/invocation_context.py +1 -0
  6. google/adk/agents/langgraph_agent.py +1 -0
  7. google/adk/agents/live_request_queue.py +1 -0
  8. google/adk/agents/llm_agent.py +172 -35
  9. google/adk/agents/loop_agent.py +1 -1
  10. google/adk/agents/parallel_agent.py +7 -0
  11. google/adk/agents/readonly_context.py +7 -1
  12. google/adk/agents/run_config.py +5 -1
  13. google/adk/agents/sequential_agent.py +31 -0
  14. google/adk/agents/transcription_entry.py +5 -2
  15. google/adk/artifacts/base_artifact_service.py +5 -10
  16. google/adk/artifacts/gcs_artifact_service.py +9 -9
  17. google/adk/artifacts/in_memory_artifact_service.py +6 -6
  18. google/adk/auth/auth_credential.py +9 -5
  19. google/adk/auth/auth_preprocessor.py +7 -1
  20. google/adk/auth/auth_tool.py +3 -4
  21. google/adk/cli/agent_graph.py +5 -5
  22. google/adk/cli/browser/index.html +2 -2
  23. google/adk/cli/browser/{main-HWIBUY2R.js → main-QOEMUXM4.js} +58 -58
  24. google/adk/cli/cli.py +7 -7
  25. google/adk/cli/cli_deploy.py +7 -2
  26. google/adk/cli/cli_eval.py +181 -106
  27. google/adk/cli/cli_tools_click.py +147 -62
  28. google/adk/cli/fast_api.py +340 -158
  29. google/adk/cli/fast_api.py.orig +822 -0
  30. google/adk/cli/utils/common.py +23 -0
  31. google/adk/cli/utils/evals.py +83 -1
  32. google/adk/cli/utils/logs.py +13 -5
  33. google/adk/code_executors/__init__.py +3 -1
  34. google/adk/code_executors/built_in_code_executor.py +52 -0
  35. google/adk/evaluation/__init__.py +1 -1
  36. google/adk/evaluation/agent_evaluator.py +168 -128
  37. google/adk/evaluation/eval_case.py +102 -0
  38. google/adk/evaluation/eval_set.py +37 -0
  39. google/adk/evaluation/eval_sets_manager.py +42 -0
  40. google/adk/evaluation/evaluation_constants.py +1 -0
  41. google/adk/evaluation/evaluation_generator.py +89 -114
  42. google/adk/evaluation/evaluator.py +56 -0
  43. google/adk/evaluation/local_eval_sets_manager.py +264 -0
  44. google/adk/evaluation/response_evaluator.py +107 -3
  45. google/adk/evaluation/trajectory_evaluator.py +83 -2
  46. google/adk/events/event.py +7 -1
  47. google/adk/events/event_actions.py +7 -1
  48. google/adk/examples/example.py +1 -0
  49. google/adk/examples/example_util.py +3 -2
  50. google/adk/flows/__init__.py +0 -1
  51. google/adk/flows/llm_flows/_code_execution.py +19 -11
  52. google/adk/flows/llm_flows/audio_transcriber.py +4 -3
  53. google/adk/flows/llm_flows/base_llm_flow.py +86 -22
  54. google/adk/flows/llm_flows/basic.py +3 -0
  55. google/adk/flows/llm_flows/functions.py +10 -9
  56. google/adk/flows/llm_flows/instructions.py +28 -9
  57. google/adk/flows/llm_flows/single_flow.py +1 -1
  58. google/adk/memory/__init__.py +1 -1
  59. google/adk/memory/_utils.py +23 -0
  60. google/adk/memory/base_memory_service.py +25 -21
  61. google/adk/memory/base_memory_service.py.orig +76 -0
  62. google/adk/memory/in_memory_memory_service.py +59 -27
  63. google/adk/memory/memory_entry.py +37 -0
  64. google/adk/memory/vertex_ai_rag_memory_service.py +40 -17
  65. google/adk/models/anthropic_llm.py +36 -11
  66. google/adk/models/base_llm.py +45 -4
  67. google/adk/models/gemini_llm_connection.py +15 -2
  68. google/adk/models/google_llm.py +9 -44
  69. google/adk/models/google_llm.py.orig +305 -0
  70. google/adk/models/lite_llm.py +94 -38
  71. google/adk/models/llm_request.py +1 -1
  72. google/adk/models/llm_response.py +15 -3
  73. google/adk/models/registry.py +1 -1
  74. google/adk/runners.py +68 -44
  75. google/adk/sessions/__init__.py +1 -1
  76. google/adk/sessions/_session_util.py +14 -0
  77. google/adk/sessions/base_session_service.py +8 -32
  78. google/adk/sessions/database_session_service.py +58 -61
  79. google/adk/sessions/in_memory_session_service.py +108 -26
  80. google/adk/sessions/session.py +4 -0
  81. google/adk/sessions/vertex_ai_session_service.py +23 -45
  82. google/adk/telemetry.py +3 -0
  83. google/adk/tools/__init__.py +4 -7
  84. google/adk/tools/{built_in_code_execution_tool.py → _built_in_code_execution_tool.py} +11 -0
  85. google/adk/tools/_memory_entry_utils.py +30 -0
  86. google/adk/tools/agent_tool.py +16 -13
  87. google/adk/tools/apihub_tool/apihub_toolset.py +55 -74
  88. google/adk/tools/application_integration_tool/application_integration_toolset.py +107 -85
  89. google/adk/tools/application_integration_tool/clients/connections_client.py +29 -25
  90. google/adk/tools/application_integration_tool/clients/integration_client.py +6 -6
  91. google/adk/tools/application_integration_tool/integration_connector_tool.py +69 -26
  92. google/adk/tools/base_toolset.py +58 -0
  93. google/adk/tools/enterprise_search_tool.py +65 -0
  94. google/adk/tools/function_parameter_parse_util.py +2 -2
  95. google/adk/tools/google_api_tool/__init__.py +18 -70
  96. google/adk/tools/google_api_tool/google_api_tool.py +11 -5
  97. google/adk/tools/google_api_tool/google_api_toolset.py +126 -0
  98. google/adk/tools/google_api_tool/google_api_toolsets.py +102 -0
  99. google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +40 -42
  100. google/adk/tools/langchain_tool.py +96 -49
  101. google/adk/tools/load_artifacts_tool.py +4 -4
  102. google/adk/tools/load_memory_tool.py +16 -5
  103. google/adk/tools/mcp_tool/__init__.py +3 -2
  104. google/adk/tools/mcp_tool/conversion_utils.py +1 -1
  105. google/adk/tools/mcp_tool/mcp_session_manager.py +167 -16
  106. google/adk/tools/mcp_tool/mcp_session_manager.py.orig +322 -0
  107. google/adk/tools/mcp_tool/mcp_tool.py +12 -12
  108. google/adk/tools/mcp_tool/mcp_toolset.py +155 -195
  109. google/adk/tools/openapi_tool/common/common.py +2 -5
  110. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +32 -7
  111. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +43 -33
  112. google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +1 -1
  113. google/adk/tools/preload_memory_tool.py +27 -18
  114. google/adk/tools/retrieval/__init__.py +1 -1
  115. google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +1 -1
  116. google/adk/tools/tool_context.py +4 -4
  117. google/adk/tools/toolbox_toolset.py +79 -0
  118. google/adk/tools/transfer_to_agent_tool.py +0 -1
  119. google/adk/version.py +1 -1
  120. {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/METADATA +7 -5
  121. google_adk-1.0.0.dist-info/RECORD +195 -0
  122. google/adk/agents/remote_agent.py +0 -50
  123. google/adk/tools/google_api_tool/google_api_tool_set.py +0 -110
  124. google/adk/tools/google_api_tool/google_api_tool_sets.py +0 -112
  125. google/adk/tools/toolbox_tool.py +0 -46
  126. google_adk-0.4.0.dist-info/RECORD +0 -179
  127. {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/WHEEL +0 -0
  128. {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/entry_points.txt +0 -0
  129. {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -11,31 +11,28 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- # pylint: disable=g-bad-import-order
15
- from .base_tool import BaseTool
14
+
16
15
 
17
16
  from ..auth.auth_tool import AuthToolArguments
18
17
  from .apihub_tool.apihub_toolset import APIHubToolset
19
- from .built_in_code_execution_tool import built_in_code_execution
20
- from .google_search_tool import google_search
21
- from .vertex_ai_search_tool import VertexAiSearchTool
18
+ from .base_tool import BaseTool
22
19
  from .example_tool import ExampleTool
23
20
  from .exit_loop_tool import exit_loop
24
21
  from .function_tool import FunctionTool
25
22
  from .get_user_choice_tool import get_user_choice_tool as get_user_choice
23
+ from .google_search_tool import google_search
26
24
  from .load_artifacts_tool import load_artifacts_tool as load_artifacts
27
25
  from .load_memory_tool import load_memory_tool as load_memory
28
26
  from .long_running_tool import LongRunningFunctionTool
29
27
  from .preload_memory_tool import preload_memory_tool as preload_memory
30
28
  from .tool_context import ToolContext
31
29
  from .transfer_to_agent_tool import transfer_to_agent
32
-
30
+ from .vertex_ai_search_tool import VertexAiSearchTool
33
31
 
34
32
  __all__ = [
35
33
  'APIHubToolset',
36
34
  'AuthToolArguments',
37
35
  'BaseTool',
38
- 'built_in_code_execution',
39
36
  'google_search',
40
37
  'VertexAiSearchTool',
41
38
  'ExampleTool',
@@ -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
+ )
@@ -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
- for artifact_name in runner.artifact_service.list_artifact_keys(
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
- if artifact := runner.artifact_service.load_artifact(
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(filename=artifact_name, artifact=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
- last_event.content.parts[0].text
172
+ merged_text
172
173
  ).model_dump(exclude_none=True)
173
174
  else:
174
- tool_result = last_event.content.parts[0].text
175
+ tool_result = '\n'.join(
176
+ [p.text for p in last_event.content.parts if p.text]
177
+ )
175
178
  return tool_result
@@ -13,19 +13,25 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
- from typing import Dict, List, Optional
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.get_tools())
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.get_tools())
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.get_tool('my_tool'),
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.apihub_resource_name = apihub_resource_name
125
- self.lazy_load_spec = lazy_load_spec
126
- self.apihub_client = apihub_client or APIHubClient(
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.generated_tools: Dict[str, RestApiTool] = {}
132
- self.auth_scheme = auth_scheme
133
- self.auth_credential = auth_credential
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
- return self.generated_tools[name] if name in self.generated_tools else None
148
+ if not self._lazy_load_spec:
149
+ self._prepare_toolset()
156
150
 
157
- def get_tools(self) -> List[RestApiTool]:
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._are_tools_ready():
164
- self._prepare_tools()
165
-
166
- return list(self.generated_tools.values())
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
- Returns:
175
- True if the tools are ready, False otherwise.
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
- spec = self.apihub_client.get_spec_content(self.apihub_resource_name)
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
- tools = OpenAPIToolset(
180
+ self._openapi_toolset = OpenAPIToolset(
205
181
  spec_dict=spec_dict,
206
- auth_credential=self.auth_credential,
207
- auth_scheme=self.auth_scheme,
208
- ).get_tools()
209
- return tools
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
- from typing import Dict, List, Optional
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
- trigger="api_trigger/test_trigger",
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
- # Get all available tools
76
+ # Feed the toolset to agent
67
77
  agent = LlmAgent(tools=[
68
- ...
69
- *application_integration_toolset.get_tools(),
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
- trigger: Optional[str] = None,
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
- tool_name: Optional[str] = "",
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
- """Initializes the ApplicationIntegrationToolset.
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
- trigger: The trigger name.
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
- tool_name: The name of the tool.
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 neither integration and trigger nor connection and
145
- (entity_operations or actions) is provided.
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.integration = integration
152
- self.trigger = trigger
153
- self.connection = connection
154
- self.entity_operations = entity_operations
155
- self.actions = actions
156
- self.tool_name = tool_name
157
- self.tool_instructions = tool_instructions
158
- self.service_account_json = service_account_json
159
- self.generated_tools: Dict[str, RestApiTool] = {}
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
- trigger,
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 and trigger:
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
- tool_name,
164
+ tool_name_prefix,
181
165
  tool_instructions,
182
166
  )
183
167
  else:
184
168
  raise ValueError(
185
- "Either (integration and trigger) or (connection and"
169
+ "Invalid request, Either integration or (connection and"
186
170
  " (entity_operations or actions)) should be provided."
187
171
  )
188
- self._parse_spec_to_tools(spec, connection_details)
172
+ self._openapi_toolset = None
173
+ self._tools = []
174
+ self._parse_spec_to_toolset(spec, connection_details)
189
175
 
190
- def _parse_spec_to_tools(self, spec_dict, connection_details):
191
- """Parses the spec dict to a list of RestApiTool."""
192
- if self.service_account_json:
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.service_account_json
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.integration and self.trigger:
214
- tools = OpenAPIToolset(
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
- ).get_tools()
219
- for tool in tools:
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
- tool = IntegrationConnectorTool(
239
- name=rest_api_tool.name,
240
- description=rest_api_tool.description,
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
- def get_tools(self) -> List[RestApiTool]:
252
- return list(self.generated_tools.values())
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()