google-adk 1.1.0__py3-none-any.whl → 1.2.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 (72) hide show
  1. google/adk/agents/base_agent.py +0 -2
  2. google/adk/agents/invocation_context.py +3 -3
  3. google/adk/agents/parallel_agent.py +17 -7
  4. google/adk/agents/sequential_agent.py +8 -8
  5. google/adk/auth/auth_preprocessor.py +18 -17
  6. google/adk/cli/agent_graph.py +165 -23
  7. google/adk/cli/browser/assets/ADK-512-color.svg +9 -0
  8. google/adk/cli/browser/index.html +2 -2
  9. google/adk/cli/browser/{main-PKDNKWJE.js → main-CS5OLUMF.js} +59 -59
  10. google/adk/cli/browser/polyfills-FFHMD2TL.js +17 -0
  11. google/adk/cli/cli.py +9 -9
  12. google/adk/cli/cli_deploy.py +157 -0
  13. google/adk/cli/cli_tools_click.py +228 -99
  14. google/adk/cli/fast_api.py +119 -34
  15. google/adk/cli/utils/agent_loader.py +60 -44
  16. google/adk/cli/utils/envs.py +1 -1
  17. google/adk/code_executors/unsafe_local_code_executor.py +11 -0
  18. google/adk/errors/__init__.py +13 -0
  19. google/adk/errors/not_found_error.py +28 -0
  20. google/adk/evaluation/agent_evaluator.py +1 -1
  21. google/adk/evaluation/eval_sets_manager.py +36 -6
  22. google/adk/evaluation/evaluation_generator.py +5 -4
  23. google/adk/evaluation/local_eval_sets_manager.py +101 -6
  24. google/adk/flows/llm_flows/agent_transfer.py +2 -2
  25. google/adk/flows/llm_flows/base_llm_flow.py +19 -0
  26. google/adk/flows/llm_flows/contents.py +4 -4
  27. google/adk/flows/llm_flows/functions.py +140 -127
  28. google/adk/memory/vertex_ai_rag_memory_service.py +2 -2
  29. google/adk/models/anthropic_llm.py +7 -10
  30. google/adk/models/google_llm.py +46 -18
  31. google/adk/models/lite_llm.py +63 -26
  32. google/adk/py.typed +0 -0
  33. google/adk/sessions/_session_util.py +10 -16
  34. google/adk/sessions/database_session_service.py +81 -66
  35. google/adk/sessions/vertex_ai_session_service.py +32 -6
  36. google/adk/telemetry.py +91 -24
  37. google/adk/tools/_automatic_function_calling_util.py +31 -25
  38. google/adk/tools/{function_parameter_parse_util.py → _function_parameter_parse_util.py} +9 -3
  39. google/adk/tools/_gemini_schema_util.py +158 -0
  40. google/adk/tools/apihub_tool/apihub_toolset.py +3 -2
  41. google/adk/tools/application_integration_tool/clients/connections_client.py +7 -0
  42. google/adk/tools/application_integration_tool/integration_connector_tool.py +5 -7
  43. google/adk/tools/base_tool.py +4 -8
  44. google/adk/tools/bigquery/__init__.py +11 -1
  45. google/adk/tools/bigquery/bigquery_credentials.py +9 -4
  46. google/adk/tools/bigquery/bigquery_toolset.py +86 -0
  47. google/adk/tools/bigquery/client.py +33 -0
  48. google/adk/tools/bigquery/metadata_tool.py +249 -0
  49. google/adk/tools/bigquery/query_tool.py +76 -0
  50. google/adk/tools/function_tool.py +4 -4
  51. google/adk/tools/langchain_tool.py +20 -13
  52. google/adk/tools/load_memory_tool.py +1 -0
  53. google/adk/tools/mcp_tool/conversion_utils.py +4 -2
  54. google/adk/tools/mcp_tool/mcp_session_manager.py +63 -5
  55. google/adk/tools/mcp_tool/mcp_tool.py +3 -2
  56. google/adk/tools/mcp_tool/mcp_toolset.py +15 -8
  57. google/adk/tools/openapi_tool/common/common.py +4 -43
  58. google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +0 -2
  59. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py +4 -2
  60. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +4 -2
  61. google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +7 -127
  62. google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +2 -7
  63. google/adk/tools/transfer_to_agent_tool.py +8 -1
  64. google/adk/tools/vertex_ai_search_tool.py +8 -1
  65. google/adk/utils/variant_utils.py +51 -0
  66. google/adk/version.py +1 -1
  67. {google_adk-1.1.0.dist-info → google_adk-1.2.0.dist-info}/METADATA +7 -7
  68. {google_adk-1.1.0.dist-info → google_adk-1.2.0.dist-info}/RECORD +71 -61
  69. google/adk/cli/browser/polyfills-B6TNHZQ6.js +0 -17
  70. {google_adk-1.1.0.dist-info → google_adk-1.2.0.dist-info}/WHEEL +0 -0
  71. {google_adk-1.1.0.dist-info → google_adk-1.2.0.dist-info}/entry_points.txt +0 -0
  72. {google_adk-1.1.0.dist-info → google_adk-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -12,14 +12,17 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
15
16
 
16
17
  from contextlib import AsyncExitStack
18
+ from datetime import timedelta
17
19
  import functools
18
20
  import logging
19
21
  import sys
20
22
  from typing import Any
21
23
  from typing import Optional
22
24
  from typing import TextIO
25
+ from typing import Union
23
26
 
24
27
  import anyio
25
28
  from pydantic import BaseModel
@@ -29,6 +32,7 @@ try:
29
32
  from mcp import StdioServerParameters
30
33
  from mcp.client.sse import sse_client
31
34
  from mcp.client.stdio import stdio_client
35
+ from mcp.client.streamable_http import streamablehttp_client
32
36
  except ImportError as e:
33
37
  import sys
34
38
 
@@ -56,6 +60,20 @@ class SseServerParams(BaseModel):
56
60
  sse_read_timeout: float = 60 * 5
57
61
 
58
62
 
63
+ class StreamableHTTPServerParams(BaseModel):
64
+ """Parameters for the MCP SSE connection.
65
+
66
+ See MCP SSE Client documentation for more details.
67
+ https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/client/streamable_http.py
68
+ """
69
+
70
+ url: str
71
+ headers: dict[str, Any] | None = None
72
+ timeout: float = 5
73
+ sse_read_timeout: float = 60 * 5
74
+ terminate_on_close: bool = True
75
+
76
+
59
77
  def retry_on_closed_resource(async_reinit_func_name: str):
60
78
  """Decorator to automatically reinitialize session and retry action.
61
79
 
@@ -123,13 +141,17 @@ class MCPSessionManager:
123
141
 
124
142
  def __init__(
125
143
  self,
126
- connection_params: StdioServerParameters | SseServerParams,
144
+ connection_params: Union[
145
+ StdioServerParameters, SseServerParams, StreamableHTTPServerParams
146
+ ],
127
147
  errlog: TextIO = sys.stderr,
128
148
  ):
129
149
  """Initializes the MCP session manager.
130
150
 
131
151
  Args:
132
- connection_params: Parameters for the MCP connection (Stdio or SSE).
152
+ connection_params: Parameters for the MCP connection (Stdio, SSE or
153
+ Streamable HTTP). Stdio by default also has a 5s read timeout as other
154
+ parameters but it's not configurable for now.
133
155
  errlog: (Optional) TextIO stream for error logging. Use only for
134
156
  initializing a local stdio MCP session.
135
157
  """
@@ -153,6 +175,9 @@ class MCPSessionManager:
153
175
 
154
176
  try:
155
177
  if isinstance(self._connection_params, StdioServerParameters):
178
+ # So far timeout is not configurable. Given MCP is still evolving, we
179
+ # would expect stdio_client to evolve to accept timeout parameter like
180
+ # other client.
156
181
  client = stdio_client(
157
182
  server=self._connection_params, errlog=self._errlog
158
183
  )
@@ -163,6 +188,16 @@ class MCPSessionManager:
163
188
  timeout=self._connection_params.timeout,
164
189
  sse_read_timeout=self._connection_params.sse_read_timeout,
165
190
  )
191
+ elif isinstance(self._connection_params, StreamableHTTPServerParams):
192
+ client = streamablehttp_client(
193
+ url=self._connection_params.url,
194
+ headers=self._connection_params.headers,
195
+ timeout=timedelta(seconds=self._connection_params.timeout),
196
+ sse_read_timeout=timedelta(
197
+ seconds=self._connection_params.sse_read_timeout
198
+ ),
199
+ terminate_on_close=self._connection_params.terminate_on_close,
200
+ )
166
201
  else:
167
202
  raise ValueError(
168
203
  'Unable to initialize connection. Connection should be'
@@ -171,9 +206,32 @@ class MCPSessionManager:
171
206
  )
172
207
 
173
208
  transports = await self._exit_stack.enter_async_context(client)
174
- session = await self._exit_stack.enter_async_context(
175
- ClientSession(*transports)
176
- )
209
+ # The streamable http client returns a GetSessionCallback in addition to the read/write MemoryObjectStreams
210
+ # needed to build the ClientSession, we limit then to the two first values to be compatible with all clients.
211
+ # The StdioServerParameters does not provide a timeout parameter for the
212
+ # session, so we need to set a default timeout for it. Other clients
213
+ # (SseServerParams and StreamableHTTPServerParams) already provide a
214
+ # timeout parameter in their configuration.
215
+ if isinstance(self._connection_params, StdioServerParameters):
216
+ # Default timeout for MCP session is 5 seconds, same as SseServerParams
217
+ # and StreamableHTTPServerParams.
218
+ # TODO :
219
+ # 1. make timeout configurable
220
+ # 2. Add StdioConnectionParams to include StdioServerParameters as a
221
+ # field and rename other two params to XXXXConnetionParams. Ohter
222
+ # two params are actually connection params, while stdio is
223
+ # special, stdio_client takes the resposibility of starting the
224
+ # server and working as a client.
225
+ session = await self._exit_stack.enter_async_context(
226
+ ClientSession(
227
+ *transports[:2],
228
+ read_timeout_seconds=timedelta(seconds=5),
229
+ )
230
+ )
231
+ else:
232
+ session = await self._exit_stack.enter_async_context(
233
+ ClientSession(*transports[:2])
234
+ )
177
235
  await session.initialize()
178
236
 
179
237
  self._session = session
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
15
16
 
16
17
  import logging
17
18
  from typing import Optional
@@ -19,6 +20,7 @@ from typing import Optional
19
20
  from google.genai.types import FunctionDeclaration
20
21
  from typing_extensions import override
21
22
 
23
+ from .._gemini_schema_util import _to_gemini_schema
22
24
  from .mcp_session_manager import MCPSessionManager
23
25
  from .mcp_session_manager import retry_on_closed_resource
24
26
 
@@ -41,7 +43,6 @@ except ImportError as e:
41
43
  from ...auth.auth_credential import AuthCredential
42
44
  from ...auth.auth_schemes import AuthScheme
43
45
  from ..base_tool import BaseTool
44
- from ..openapi_tool.openapi_spec_parser.rest_api_tool import to_gemini_schema
45
46
  from ..tool_context import ToolContext
46
47
 
47
48
  logger = logging.getLogger("google_adk." + __name__)
@@ -98,7 +99,7 @@ class MCPTool(BaseTool):
98
99
  FunctionDeclaration: The Gemini function declaration for the tool.
99
100
  """
100
101
  schema_dict = self._mcp_tool.inputSchema
101
- parameters = to_gemini_schema(schema_dict)
102
+ parameters = _to_gemini_schema(schema_dict)
102
103
  function_decl = FunctionDeclaration(
103
104
  name=self.name, description=self.description, parameters=parameters
104
105
  )
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import logging
16
18
  import sys
17
19
  from typing import List
@@ -26,6 +28,7 @@ from ..base_toolset import ToolPredicate
26
28
  from .mcp_session_manager import MCPSessionManager
27
29
  from .mcp_session_manager import retry_on_closed_resource
28
30
  from .mcp_session_manager import SseServerParams
31
+ from .mcp_session_manager import StreamableHTTPServerParams
29
32
 
30
33
  # Attempt to import MCP Tool from the MCP library, and hints user to upgrade
31
34
  # their Python version to 3.10 if it fails.
@@ -82,20 +85,23 @@ class MCPToolset(BaseToolset):
82
85
  def __init__(
83
86
  self,
84
87
  *,
85
- connection_params: StdioServerParameters | SseServerParams,
88
+ connection_params: (
89
+ StdioServerParameters | SseServerParams | StreamableHTTPServerParams
90
+ ),
86
91
  tool_filter: Optional[Union[ToolPredicate, List[str]]] = None,
87
92
  errlog: TextIO = sys.stderr,
88
93
  ):
89
94
  """Initializes the MCPToolset.
90
95
 
91
96
  Args:
92
- connection_params: The connection parameters to the MCP server. Can be:
93
- `StdioServerParameters` for using local mcp server (e.g. using `npx` or
94
- `python3`); or `SseServerParams` for a local/remote SSE server.
95
- tool_filter: Optional filter to select specific tools. Can be either:
96
- - A list of tool names to include
97
- - A ToolPredicate function for custom filtering logic
98
- errlog: TextIO stream for error logging.
97
+ connection_params: The connection parameters to the MCP server. Can be:
98
+ `StdioServerParameters` for using local mcp server (e.g. using `npx` or
99
+ `python3`); or `SseServerParams` for a local/remote SSE server; or
100
+ `StreamableHTTPServerParams` for local/remote Streamable http server.
101
+ tool_filter: Optional filter to select specific tools. Can be either:
102
+ - A list of tool names to include
103
+ - A ToolPredicate function for custom filtering logic
104
+ errlog: TextIO stream for error logging.
99
105
  """
100
106
  super().__init__(tool_filter=tool_filter)
101
107
 
@@ -110,6 +116,7 @@ class MCPToolset(BaseToolset):
110
116
  connection_params=self._connection_params,
111
117
  errlog=self._errlog,
112
118
  )
119
+
113
120
  self._session = None
114
121
 
115
122
  @retry_on_closed_resource("_reinitialize_session")
@@ -12,8 +12,9 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import keyword
16
- import re
17
18
  from typing import Any
18
19
  from typing import Dict
19
20
  from typing import List
@@ -26,47 +27,7 @@ from pydantic import BaseModel
26
27
  from pydantic import Field
27
28
  from pydantic import model_serializer
28
29
 
29
-
30
- def to_snake_case(text: str) -> str:
31
- """Converts a string into snake_case.
32
-
33
- Handles lowerCamelCase, UpperCamelCase, or space-separated case, acronyms
34
- (e.g., "REST API") and consecutive uppercase letters correctly. Also handles
35
- mixed cases with and without spaces.
36
-
37
- Examples:
38
- ```
39
- to_snake_case('camelCase') -> 'camel_case'
40
- to_snake_case('UpperCamelCase') -> 'upper_camel_case'
41
- to_snake_case('space separated') -> 'space_separated'
42
- ```
43
-
44
- Args:
45
- text: The input string.
46
-
47
- Returns:
48
- The snake_case version of the string.
49
- """
50
-
51
- # Handle spaces and non-alphanumeric characters (replace with underscores)
52
- text = re.sub(r'[^a-zA-Z0-9]+', '_', text)
53
-
54
- # Insert underscores before uppercase letters (handling both CamelCases)
55
- text = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', text) # lowerCamelCase
56
- text = re.sub(
57
- r'([A-Z]+)([A-Z][a-z])', r'\1_\2', text
58
- ) # UpperCamelCase and acronyms
59
-
60
- # Convert to lowercase
61
- text = text.lower()
62
-
63
- # Remove consecutive underscores (clean up extra underscores)
64
- text = re.sub(r'_+', '_', text)
65
-
66
- # Remove leading and trailing underscores
67
- text = text.strip('_')
68
-
69
- return text
30
+ from ..._gemini_schema_util import _to_snake_case
70
31
 
71
32
 
72
33
  def rename_python_keywords(s: str, prefix: str = 'param_') -> str:
@@ -106,7 +67,7 @@ class ApiParameter(BaseModel):
106
67
  self.py_name = (
107
68
  self.py_name
108
69
  if self.py_name
109
- else rename_python_keywords(to_snake_case(self.original_name))
70
+ else rename_python_keywords(_to_snake_case(self.original_name))
110
71
  )
111
72
  if isinstance(self.param_schema, str):
112
73
  self.param_schema = Schema.model_validate_json(self.param_schema)
@@ -20,7 +20,6 @@ from .operation_parser import OperationParser
20
20
  from .rest_api_tool import AuthPreparationState
21
21
  from .rest_api_tool import RestApiTool
22
22
  from .rest_api_tool import snake_to_lower_camel
23
- from .rest_api_tool import to_gemini_schema
24
23
  from .tool_auth_handler import ToolAuthHandler
25
24
 
26
25
  __all__ = [
@@ -30,7 +29,6 @@ __all__ = [
30
29
  'OpenAPIToolset',
31
30
  'OperationParser',
32
31
  'RestApiTool',
33
- 'to_gemini_schema',
34
32
  'snake_to_lower_camel',
35
33
  'AuthPreparationState',
36
34
  'ToolAuthHandler',
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import copy
16
18
  from typing import Any
17
19
  from typing import Dict
@@ -23,8 +25,8 @@ from pydantic import BaseModel
23
25
 
24
26
  from ....auth.auth_credential import AuthCredential
25
27
  from ....auth.auth_schemes import AuthScheme
28
+ from ..._gemini_schema_util import _to_snake_case
26
29
  from ..common.common import ApiParameter
27
- from ..common.common import to_snake_case
28
30
  from .operation_parser import OperationParser
29
31
 
30
32
 
@@ -112,7 +114,7 @@ class OpenApiSpecParser:
112
114
  # If operation ID is missing, assign an operation id based on path
113
115
  # and method
114
116
  if "operationId" not in operation_dict:
115
- temp_id = to_snake_case(f"{path}_{method}")
117
+ temp_id = _to_snake_case(f"{path}_{method}")
116
118
  operation_dict["operationId"] = temp_id
117
119
 
118
120
  url = OperationEndpoint(base_url=base_url, path=path, method=method)
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import inspect
16
18
  from textwrap import dedent
17
19
  from typing import Any
@@ -25,9 +27,9 @@ from fastapi.openapi.models import Operation
25
27
  from fastapi.openapi.models import Parameter
26
28
  from fastapi.openapi.models import Schema
27
29
 
30
+ from ..._gemini_schema_util import _to_snake_case
28
31
  from ..common.common import ApiParameter
29
32
  from ..common.common import PydocHelper
30
- from ..common.common import to_snake_case
31
33
 
32
34
 
33
35
  class OperationParser:
@@ -189,7 +191,7 @@ class OperationParser:
189
191
  operation_id = self._operation.operationId
190
192
  if not operation_id:
191
193
  raise ValueError('Operation ID is missing')
192
- return to_snake_case(operation_id)[:60]
194
+ return _to_snake_case(operation_id)[:60]
193
195
 
194
196
  def get_return_type_hint(self) -> str:
195
197
  """Returns the return type hint string (like 'str', 'int', etc.)."""
@@ -12,45 +12,36 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  from typing import Any
16
18
  from typing import Dict
17
19
  from typing import List
18
20
  from typing import Literal
19
21
  from typing import Optional
20
- from typing import Sequence
21
22
  from typing import Tuple
22
23
  from typing import Union
23
24
 
24
25
  from fastapi.openapi.models import Operation
25
26
  from google.genai.types import FunctionDeclaration
26
- from google.genai.types import Schema
27
27
  import requests
28
28
  from typing_extensions import override
29
29
 
30
30
  from ....auth.auth_credential import AuthCredential
31
31
  from ....auth.auth_schemes import AuthScheme
32
- from ....tools.base_tool import BaseTool
32
+ from ..._gemini_schema_util import _to_gemini_schema
33
+ from ..._gemini_schema_util import _to_snake_case
34
+ from ...base_tool import BaseTool
33
35
  from ...tool_context import ToolContext
34
36
  from ..auth.auth_helpers import credential_to_param
35
37
  from ..auth.auth_helpers import dict_to_auth_scheme
36
38
  from ..auth.credential_exchangers.auto_auth_credential_exchanger import AutoAuthCredentialExchanger
37
39
  from ..common.common import ApiParameter
38
- from ..common.common import to_snake_case
39
40
  from .openapi_spec_parser import OperationEndpoint
40
41
  from .openapi_spec_parser import ParsedOperation
41
42
  from .operation_parser import OperationParser
42
43
  from .tool_auth_handler import ToolAuthHandler
43
44
 
44
- # Not supported by the Gemini API
45
- _OPENAPI_SCHEMA_IGNORE_FIELDS = (
46
- "title",
47
- "default",
48
- "format",
49
- "additional_properties",
50
- "ref",
51
- "def",
52
- )
53
-
54
45
 
55
46
  def snake_to_lower_camel(snake_case_string: str):
56
47
  """Converts a snake_case string to a lower_camel_case string.
@@ -70,117 +61,6 @@ def snake_to_lower_camel(snake_case_string: str):
70
61
  ])
71
62
 
72
63
 
73
- # TODO: Switch to Gemini `from_json_schema` util when it is released
74
- # in Gemini SDK.
75
- def normalize_json_schema_type(
76
- json_schema_type: Optional[Union[str, Sequence[str]]],
77
- ) -> tuple[Optional[str], bool]:
78
- """Converts a JSON Schema Type into Gemini Schema type.
79
-
80
- Adopted and modified from Gemini SDK. This gets the first available schema
81
- type from JSON Schema, and use it to mark Gemini schema type. If JSON Schema
82
- contains a list of types, the first non null type is used.
83
-
84
- Remove this after switching to Gemini `from_json_schema`.
85
- """
86
- if json_schema_type is None:
87
- return None, False
88
- if isinstance(json_schema_type, str):
89
- if json_schema_type == "null":
90
- return None, True
91
- return json_schema_type, False
92
-
93
- non_null_types = []
94
- nullable = False
95
- # If json schema type is an array, pick the first non null type.
96
- for type_value in json_schema_type:
97
- if type_value == "null":
98
- nullable = True
99
- else:
100
- non_null_types.append(type_value)
101
- non_null_type = non_null_types[0] if non_null_types else None
102
- return non_null_type, nullable
103
-
104
-
105
- # TODO: Switch to Gemini `from_json_schema` util when it is released
106
- # in Gemini SDK.
107
- def to_gemini_schema(openapi_schema: Optional[Dict[str, Any]] = None) -> Schema:
108
- """Converts an OpenAPI schema dictionary to a Gemini Schema object.
109
-
110
- Args:
111
- openapi_schema: The OpenAPI schema dictionary.
112
-
113
- Returns:
114
- A Pydantic Schema object. Returns None if input is None.
115
- Raises TypeError if input is not a dict.
116
- """
117
- if openapi_schema is None:
118
- return None
119
-
120
- if not isinstance(openapi_schema, dict):
121
- raise TypeError("openapi_schema must be a dictionary")
122
-
123
- pydantic_schema_data = {}
124
-
125
- # Adding this to force adding a type to an empty dict
126
- # This avoid "... one_of or any_of must specify a type" error
127
- if not openapi_schema.get("type"):
128
- openapi_schema["type"] = "object"
129
-
130
- for key, value in openapi_schema.items():
131
- snake_case_key = to_snake_case(key)
132
- # Check if the snake_case_key exists in the Schema model's fields.
133
- if snake_case_key in Schema.model_fields:
134
- if snake_case_key in _OPENAPI_SCHEMA_IGNORE_FIELDS:
135
- # Ignore these fields as Gemini backend doesn't recognize them, and will
136
- # throw exception if they appear in the schema.
137
- # Format: properties[expiration].format: only 'enum' and 'date-time' are
138
- # supported for STRING type
139
- continue
140
- elif snake_case_key == "type":
141
- schema_type, nullable = normalize_json_schema_type(
142
- openapi_schema.get("type", None)
143
- )
144
- # Adding this to force adding a type to an empty dict
145
- # This avoid "... one_of or any_of must specify a type" error
146
- pydantic_schema_data["type"] = schema_type if schema_type else "object"
147
- pydantic_schema_data["type"] = pydantic_schema_data["type"].upper()
148
- if nullable:
149
- pydantic_schema_data["nullable"] = True
150
- elif snake_case_key == "properties" and isinstance(value, dict):
151
- pydantic_schema_data[snake_case_key] = {
152
- k: to_gemini_schema(v) for k, v in value.items()
153
- }
154
- elif snake_case_key == "items" and isinstance(value, dict):
155
- pydantic_schema_data[snake_case_key] = to_gemini_schema(value)
156
- elif snake_case_key == "any_of" and isinstance(value, list):
157
- pydantic_schema_data[snake_case_key] = [
158
- to_gemini_schema(item) for item in value
159
- ]
160
- # Important: Handle cases where the OpenAPI schema might contain lists
161
- # or other structures that need to be recursively processed.
162
- elif isinstance(value, list) and snake_case_key not in (
163
- "enum",
164
- "required",
165
- "property_ordering",
166
- ):
167
- new_list = []
168
- for item in value:
169
- if isinstance(item, dict):
170
- new_list.append(to_gemini_schema(item))
171
- else:
172
- new_list.append(item)
173
- pydantic_schema_data[snake_case_key] = new_list
174
- elif isinstance(value, dict) and snake_case_key not in ("properties"):
175
- # Handle dictionary which is neither properties or items
176
- pydantic_schema_data[snake_case_key] = to_gemini_schema(value)
177
- else:
178
- # Simple value assignment (int, str, bool, etc.)
179
- pydantic_schema_data[snake_case_key] = value
180
-
181
- return Schema(**pydantic_schema_data)
182
-
183
-
184
64
  AuthPreparationState = Literal["pending", "done"]
185
65
 
186
66
 
@@ -273,7 +153,7 @@ class RestApiTool(BaseTool):
273
153
  parsed.operation, parsed.parameters, parsed.return_value
274
154
  )
275
155
 
276
- tool_name = to_snake_case(operation_parser.get_function_name())
156
+ tool_name = _to_snake_case(operation_parser.get_function_name())
277
157
  generated = cls(
278
158
  name=tool_name,
279
159
  description=parsed.operation.description
@@ -306,7 +186,7 @@ class RestApiTool(BaseTool):
306
186
  def _get_declaration(self) -> FunctionDeclaration:
307
187
  """Returns the function declaration in the Gemini Schema format."""
308
188
  schema_dict = self._operation_parser.get_json_schema()
309
- parameters = to_gemini_schema(schema_dict)
189
+ parameters = _to_gemini_schema(schema_dict)
310
190
  function_decl = FunctionDeclaration(
311
191
  name=self.name, description=self.description, parameters=parameters
312
192
  )
@@ -185,7 +185,7 @@ class ToolAuthHandler:
185
185
  )
186
186
  self.credential_store.store_credential(key, auth_credential)
187
187
 
188
- def _reqeust_credential(self) -> None:
188
+ def _request_credential(self) -> None:
189
189
  """Handles the case where an OpenID Connect or OAuth2 authentication request is needed."""
190
190
  if self.auth_scheme.type_ in (
191
191
  AuthSchemeType.openIdConnect,
@@ -223,11 +223,6 @@ class ToolAuthHandler:
223
223
  )
224
224
  )
225
225
 
226
- def _request_credential(self, auth_config: AuthConfig):
227
- if not self.tool_context:
228
- return
229
- self.tool_context.request_credential(auth_config)
230
-
231
226
  def prepare_auth_credentials(
232
227
  self,
233
228
  ) -> AuthPreparationResult:
@@ -260,7 +255,7 @@ class ToolAuthHandler:
260
255
  auth_credential=exchanged_credential,
261
256
  )
262
257
  else:
263
- self._reqeust_credential()
258
+ self._request_credential()
264
259
  return AuthPreparationResult(
265
260
  state="pending",
266
261
  auth_scheme=self.auth_scheme,
@@ -16,5 +16,12 @@ from .tool_context import ToolContext
16
16
 
17
17
 
18
18
  def transfer_to_agent(agent_name: str, tool_context: ToolContext):
19
- """Transfer the question to another agent."""
19
+ """Transfer the question to another agent.
20
+
21
+ This tool hands off control to another agent when it's more suitable to
22
+ answer the user's question according to the agent's description.
23
+
24
+ Args:
25
+ agent_name: the agent name to transfer to.
26
+ """
20
27
  tool_context.actions.transfer_to_agent = agent_name
@@ -40,6 +40,8 @@ class VertexAiSearchTool(BaseTool):
40
40
  *,
41
41
  data_store_id: Optional[str] = None,
42
42
  search_engine_id: Optional[str] = None,
43
+ filter: Optional[str] = None,
44
+ max_results: Optional[int] = None,
43
45
  ):
44
46
  """Initializes the Vertex AI Search tool.
45
47
 
@@ -64,6 +66,8 @@ class VertexAiSearchTool(BaseTool):
64
66
  )
65
67
  self.data_store_id = data_store_id
66
68
  self.search_engine_id = search_engine_id
69
+ self.filter = filter
70
+ self.max_results = max_results
67
71
 
68
72
  @override
69
73
  async def process_llm_request(
@@ -84,7 +88,10 @@ class VertexAiSearchTool(BaseTool):
84
88
  types.Tool(
85
89
  retrieval=types.Retrieval(
86
90
  vertex_ai_search=types.VertexAISearch(
87
- datastore=self.data_store_id, engine=self.search_engine_id
91
+ datastore=self.data_store_id,
92
+ engine=self.search_engine_id,
93
+ filter=self.filter,
94
+ max_results=self.max_results,
88
95
  )
89
96
  )
90
97
  )
@@ -0,0 +1,51 @@
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
+ """Utilities for Google LLM variants.
16
+
17
+ This module is for ADK internal use only.
18
+ Please do not rely on the implementation details.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from enum import Enum
24
+ import os
25
+
26
+ _GOOGLE_LLM_VARIANT_VERTEX_AI = 'VERTEX_AI'
27
+ _GOOGLE_LLM_VARIANT_GEMINI_API = 'GEMINI_API'
28
+
29
+
30
+ class GoogleLLMVariant(Enum):
31
+ """
32
+ The Google LLM variant to use.
33
+ see https://google.github.io/adk-docs/get-started/quickstart/#set-up-the-model
34
+ """
35
+
36
+ VERTEX_AI = _GOOGLE_LLM_VARIANT_VERTEX_AI
37
+ """For using credentials from Google Vertex AI"""
38
+ GEMINI_API = _GOOGLE_LLM_VARIANT_GEMINI_API
39
+ """For using API Key from Google AI Studio"""
40
+
41
+
42
+ def get_google_llm_variant() -> str:
43
+ return (
44
+ GoogleLLMVariant.VERTEX_AI
45
+ if os.environ.get('GOOGLE_GENAI_USE_VERTEXAI', '0').lower()
46
+ in [
47
+ 'true',
48
+ '1',
49
+ ]
50
+ else GoogleLLMVariant.GEMINI_API
51
+ )
google/adk/version.py CHANGED
@@ -13,4 +13,4 @@
13
13
  # limitations under the License.
14
14
 
15
15
  # version: date+base_cl
16
- __version__ = "1.1.0"
16
+ __version__ = "1.2.0"