google-adk 0.5.0__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. google/adk/agents/base_agent.py +76 -30
  2. google/adk/agents/callback_context.py +2 -6
  3. google/adk/agents/llm_agent.py +122 -30
  4. google/adk/agents/loop_agent.py +1 -1
  5. google/adk/agents/parallel_agent.py +7 -0
  6. google/adk/agents/readonly_context.py +8 -0
  7. google/adk/agents/run_config.py +1 -1
  8. google/adk/agents/sequential_agent.py +31 -0
  9. google/adk/agents/transcription_entry.py +4 -2
  10. google/adk/artifacts/gcs_artifact_service.py +1 -1
  11. google/adk/artifacts/in_memory_artifact_service.py +1 -1
  12. google/adk/auth/auth_credential.py +10 -2
  13. google/adk/auth/auth_preprocessor.py +7 -1
  14. google/adk/auth/auth_tool.py +3 -4
  15. google/adk/cli/agent_graph.py +5 -5
  16. google/adk/cli/browser/index.html +4 -4
  17. google/adk/cli/browser/{main-ULN5R5I5.js → main-PKDNKWJE.js} +59 -60
  18. google/adk/cli/browser/polyfills-B6TNHZQ6.js +17 -0
  19. google/adk/cli/cli.py +10 -9
  20. google/adk/cli/cli_deploy.py +7 -2
  21. google/adk/cli/cli_eval.py +109 -115
  22. google/adk/cli/cli_tools_click.py +179 -67
  23. google/adk/cli/fast_api.py +248 -197
  24. google/adk/cli/utils/agent_loader.py +137 -0
  25. google/adk/cli/utils/cleanup.py +40 -0
  26. google/adk/cli/utils/common.py +23 -0
  27. google/adk/cli/utils/evals.py +83 -0
  28. google/adk/cli/utils/logs.py +8 -5
  29. google/adk/code_executors/__init__.py +3 -1
  30. google/adk/code_executors/built_in_code_executor.py +52 -0
  31. google/adk/code_executors/code_execution_utils.py +2 -1
  32. google/adk/code_executors/container_code_executor.py +0 -1
  33. google/adk/code_executors/vertex_ai_code_executor.py +6 -8
  34. google/adk/evaluation/__init__.py +1 -1
  35. google/adk/evaluation/agent_evaluator.py +168 -128
  36. google/adk/evaluation/eval_case.py +104 -0
  37. google/adk/evaluation/eval_metrics.py +74 -0
  38. google/adk/evaluation/eval_result.py +86 -0
  39. google/adk/evaluation/eval_set.py +39 -0
  40. google/adk/evaluation/eval_set_results_manager.py +47 -0
  41. google/adk/evaluation/eval_sets_manager.py +43 -0
  42. google/adk/evaluation/evaluation_generator.py +88 -113
  43. google/adk/evaluation/evaluator.py +58 -0
  44. google/adk/evaluation/local_eval_set_results_manager.py +113 -0
  45. google/adk/evaluation/local_eval_sets_manager.py +264 -0
  46. google/adk/evaluation/response_evaluator.py +106 -1
  47. google/adk/evaluation/trajectory_evaluator.py +84 -2
  48. google/adk/events/event.py +6 -1
  49. google/adk/events/event_actions.py +6 -1
  50. google/adk/examples/base_example_provider.py +1 -0
  51. google/adk/examples/example_util.py +3 -2
  52. google/adk/flows/llm_flows/_code_execution.py +9 -1
  53. google/adk/flows/llm_flows/audio_transcriber.py +4 -3
  54. google/adk/flows/llm_flows/base_llm_flow.py +58 -21
  55. google/adk/flows/llm_flows/contents.py +3 -1
  56. google/adk/flows/llm_flows/functions.py +9 -8
  57. google/adk/flows/llm_flows/instructions.py +18 -80
  58. google/adk/flows/llm_flows/single_flow.py +2 -2
  59. google/adk/memory/__init__.py +1 -1
  60. google/adk/memory/_utils.py +23 -0
  61. google/adk/memory/base_memory_service.py +23 -21
  62. google/adk/memory/in_memory_memory_service.py +57 -25
  63. google/adk/memory/memory_entry.py +37 -0
  64. google/adk/memory/vertex_ai_rag_memory_service.py +38 -15
  65. google/adk/models/anthropic_llm.py +16 -9
  66. google/adk/models/base_llm.py +2 -1
  67. google/adk/models/base_llm_connection.py +2 -0
  68. google/adk/models/gemini_llm_connection.py +11 -11
  69. google/adk/models/google_llm.py +12 -2
  70. google/adk/models/lite_llm.py +80 -23
  71. google/adk/models/llm_response.py +16 -3
  72. google/adk/models/registry.py +1 -1
  73. google/adk/runners.py +98 -42
  74. google/adk/sessions/__init__.py +1 -1
  75. google/adk/sessions/_session_util.py +2 -1
  76. google/adk/sessions/base_session_service.py +6 -33
  77. google/adk/sessions/database_session_service.py +57 -67
  78. google/adk/sessions/in_memory_session_service.py +106 -24
  79. google/adk/sessions/session.py +3 -0
  80. google/adk/sessions/vertex_ai_session_service.py +44 -51
  81. google/adk/telemetry.py +7 -2
  82. google/adk/tools/__init__.py +4 -7
  83. google/adk/tools/_memory_entry_utils.py +30 -0
  84. google/adk/tools/agent_tool.py +10 -10
  85. google/adk/tools/apihub_tool/apihub_toolset.py +55 -74
  86. google/adk/tools/apihub_tool/clients/apihub_client.py +10 -3
  87. google/adk/tools/apihub_tool/clients/secret_client.py +1 -0
  88. google/adk/tools/application_integration_tool/application_integration_toolset.py +111 -85
  89. google/adk/tools/application_integration_tool/clients/connections_client.py +28 -1
  90. google/adk/tools/application_integration_tool/clients/integration_client.py +7 -5
  91. google/adk/tools/application_integration_tool/integration_connector_tool.py +69 -26
  92. google/adk/tools/base_toolset.py +96 -0
  93. google/adk/tools/bigquery/__init__.py +28 -0
  94. google/adk/tools/bigquery/bigquery_credentials.py +216 -0
  95. google/adk/tools/bigquery/bigquery_tool.py +116 -0
  96. google/adk/tools/{built_in_code_execution_tool.py → enterprise_search_tool.py} +17 -11
  97. google/adk/tools/function_parameter_parse_util.py +9 -2
  98. google/adk/tools/function_tool.py +33 -3
  99. google/adk/tools/get_user_choice_tool.py +1 -0
  100. google/adk/tools/google_api_tool/__init__.py +24 -70
  101. google/adk/tools/google_api_tool/google_api_tool.py +12 -6
  102. google/adk/tools/google_api_tool/{google_api_tool_set.py → google_api_toolset.py} +57 -55
  103. google/adk/tools/google_api_tool/google_api_toolsets.py +108 -0
  104. google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +40 -42
  105. google/adk/tools/google_search_tool.py +2 -2
  106. google/adk/tools/langchain_tool.py +96 -49
  107. google/adk/tools/load_memory_tool.py +14 -5
  108. google/adk/tools/mcp_tool/__init__.py +3 -2
  109. google/adk/tools/mcp_tool/conversion_utils.py +6 -2
  110. google/adk/tools/mcp_tool/mcp_session_manager.py +80 -69
  111. google/adk/tools/mcp_tool/mcp_tool.py +35 -32
  112. google/adk/tools/mcp_tool/mcp_toolset.py +99 -194
  113. google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +1 -3
  114. google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +6 -7
  115. google/adk/tools/openapi_tool/common/common.py +5 -1
  116. google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +7 -2
  117. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +27 -7
  118. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +36 -32
  119. google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +11 -1
  120. google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +1 -1
  121. google/adk/tools/preload_memory_tool.py +27 -18
  122. google/adk/tools/retrieval/__init__.py +1 -1
  123. google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +1 -1
  124. google/adk/tools/toolbox_toolset.py +107 -0
  125. google/adk/tools/transfer_to_agent_tool.py +0 -1
  126. google/adk/utils/__init__.py +13 -0
  127. google/adk/utils/instructions_utils.py +131 -0
  128. google/adk/version.py +1 -1
  129. {google_adk-0.5.0.dist-info → google_adk-1.1.0.dist-info}/METADATA +18 -19
  130. google_adk-1.1.0.dist-info/RECORD +200 -0
  131. google/adk/agents/remote_agent.py +0 -50
  132. google/adk/cli/browser/polyfills-FFHMD2TL.js +0 -18
  133. google/adk/cli/fast_api.py.orig +0 -728
  134. google/adk/tools/google_api_tool/google_api_tool_sets.py +0 -112
  135. google/adk/tools/toolbox_tool.py +0 -46
  136. google_adk-0.5.0.dist-info/RECORD +0 -180
  137. {google_adk-0.5.0.dist-info → google_adk-1.1.0.dist-info}/WHEEL +0 -0
  138. {google_adk-0.5.0.dist-info → google_adk-1.1.0.dist-info}/entry_points.txt +0 -0
  139. {google_adk-0.5.0.dist-info → google_adk-1.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,137 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import importlib
18
+ import logging
19
+ import sys
20
+
21
+ from . import envs
22
+ from ...agents.base_agent import BaseAgent
23
+
24
+ logger = logging.getLogger("google_adk." + __name__)
25
+
26
+
27
+ class AgentLoader:
28
+ """Centralized agent loading with proper isolation, caching, and .env loading.
29
+ Support loading agents from below folder/file structures:
30
+ a) agents_dir/agent_name.py (with root_agent or agent.root_agent in it)
31
+ b) agents_dir/agent_name_folder/__init__.py (with root_agent or agent.root_agent in the package)
32
+ c) agents_dir/agent_name_folder/agent.py (where agent.py has root_agent)
33
+ """
34
+
35
+ def __init__(self, agents_dir: str):
36
+ self.agents_dir = agents_dir.rstrip("/")
37
+ self._original_sys_path = None
38
+ self._agent_cache: dict[str, BaseAgent] = {}
39
+
40
+ def _load_from_module_or_package(self, agent_name: str) -> BaseAgent:
41
+ # Load for case: Import "<agent_name>" (as a package or module)
42
+ # Covers structures:
43
+ # a) agents_dir/agent_name.py (with root_agent or agent.root_agent in it)
44
+ # b) agents_dir/agent_name_folder/__init__.py (with root_agent or agent.root_agent in the package)
45
+ try:
46
+ module_candidate = importlib.import_module(agent_name)
47
+ # Check for "root_agent" directly in "<agent_name>" module/package
48
+ if hasattr(module_candidate, "root_agent"):
49
+ logger.debug("Found root_agent directly in %s", agent_name)
50
+ return module_candidate.root_agent
51
+ # Check for "<agent_name>.agent.root_agent" structure (e.g. agent_name is a package,
52
+ # and it has an 'agent' submodule/attribute which in turn has 'root_agent')
53
+ if hasattr(module_candidate, "agent") and hasattr(
54
+ module_candidate.agent, "root_agent"
55
+ ):
56
+ logger.debug("Found root_agent in %s.agent attribute", agent_name)
57
+ if isinstance(module_candidate.agent, BaseAgent):
58
+ return module_candidate.agent.root_agent
59
+ else:
60
+ logger.warning(
61
+ "Root agent found is not an instance of BaseAgent. But a type %s",
62
+ type(module_candidate.agent),
63
+ )
64
+ except ModuleNotFoundError:
65
+ logger.debug("Module %s itself not found.", agent_name)
66
+ # Re-raise as ValueError to be caught by the final error message construction
67
+ raise ValueError(
68
+ f"Module {agent_name} not found during import attempts."
69
+ ) from None
70
+ except ImportError as e:
71
+ logger.warning("Error importing %s: %s", agent_name, e)
72
+
73
+ return None
74
+
75
+ def _load_from_submodule(self, agent_name: str) -> BaseAgent:
76
+ # Load for case: Import "<agent_name>.agent" and look for "root_agent"
77
+ # Covers structure: agents_dir/agent_name_folder/agent.py (where agent.py has root_agent)
78
+ try:
79
+ module_candidate = importlib.import_module(f"{agent_name}.agent")
80
+ if hasattr(module_candidate, "root_agent"):
81
+ logger.debug("Found root_agent in %s.agent", agent_name)
82
+ if isinstance(module_candidate.root_agent, BaseAgent):
83
+ return module_candidate.root_agent
84
+ else:
85
+ logger.warning(
86
+ "Root agent found is not an instance of BaseAgent. But a type %s",
87
+ type(module_candidate.root_agent),
88
+ )
89
+ except ModuleNotFoundError:
90
+ logger.debug(
91
+ "Module %s.agent not found, trying next pattern.", agent_name
92
+ )
93
+ except ImportError as e:
94
+ logger.warning("Error importing %s.agent: %s", agent_name, e)
95
+
96
+ return None
97
+
98
+ def _perform_load(self, agent_name: str) -> BaseAgent:
99
+ """Internal logic to load an agent"""
100
+ # Add self.agents_dir to sys.path
101
+ if self.agents_dir not in sys.path:
102
+ sys.path.insert(0, self.agents_dir)
103
+
104
+ logger.debug(
105
+ "Loading .env for agent %s from %s", agent_name, self.agents_dir
106
+ )
107
+ envs.load_dotenv_for_agent(agent_name, str(self.agents_dir))
108
+
109
+ root_agent = self._load_from_module_or_package(agent_name)
110
+ if root_agent:
111
+ return root_agent
112
+
113
+ root_agent = self._load_from_submodule(agent_name)
114
+ if root_agent:
115
+ return root_agent
116
+
117
+ # If no root_agent was found by any pattern
118
+ raise ValueError(
119
+ f"No root_agent found for '{agent_name}'. Searched in"
120
+ f" '{agent_name}.agent.root_agent', '{agent_name}.root_agent', and"
121
+ f" via an 'agent' attribute within the '{agent_name}' module/package."
122
+ f" Ensure '{self.agents_dir}/{agent_name}' is structured correctly,"
123
+ " an .env file can be loaded if present, and a root_agent is"
124
+ " exposed."
125
+ )
126
+
127
+ def load_agent(self, agent_name: str) -> BaseAgent:
128
+ """Load an agent module (with caching & .env) and return its root_agent (asynchronously)."""
129
+ if agent_name in self._agent_cache:
130
+ logger.debug("Returning cached agent for %s (async)", agent_name)
131
+ return self._agent_cache[agent_name]
132
+
133
+ logger.debug("Loading agent %s - not in cache.", agent_name)
134
+ # Assumes this method is called when the context manager (`with self:`) is active
135
+ agent = self._perform_load(agent_name)
136
+ self._agent_cache[agent_name] = agent
137
+ return agent
@@ -0,0 +1,40 @@
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
+ import asyncio
16
+ import logging
17
+ from typing import List
18
+
19
+ from ...runners import Runner
20
+
21
+ logger = logging.getLogger("google_adk." + __name__)
22
+
23
+
24
+ async def close_runners(runners: List[Runner]) -> None:
25
+ cleanup_tasks = [asyncio.create_task(runner.close()) for runner in runners]
26
+ if cleanup_tasks:
27
+ # Wait for all cleanup tasks with timeout
28
+ done, pending = await asyncio.wait(
29
+ cleanup_tasks,
30
+ timeout=30.0, # 30 second timeout for cleanup
31
+ return_when=asyncio.ALL_COMPLETED,
32
+ )
33
+
34
+ # If any tasks are still pending, log it
35
+ if pending:
36
+ logger.warning(
37
+ "%s runner close tasks didn't complete in time", len(pending)
38
+ )
39
+ for task in pending:
40
+ task.cancel()
@@ -0,0 +1,23 @@
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
+ import pydantic
16
+ from pydantic import alias_generators
17
+
18
+
19
+ class BaseModel(pydantic.BaseModel):
20
+ model_config = pydantic.ConfigDict(
21
+ alias_generator=alias_generators.to_camel,
22
+ populate_by_name=True,
23
+ )
@@ -13,10 +13,17 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from typing import Any
16
+ from typing import Tuple
16
17
 
18
+ from deprecated import deprecated
19
+ from google.genai import types as genai_types
20
+
21
+ from ...evaluation.eval_case import IntermediateData
22
+ from ...evaluation.eval_case import Invocation
17
23
  from ...sessions.session import Session
18
24
 
19
25
 
26
+ @deprecated(reason='Use convert_session_to_eval_invocations instead.')
20
27
  def convert_session_to_eval_format(session: Session) -> list[dict[str, Any]]:
21
28
  """Converts a session data into eval format.
22
29
 
@@ -91,3 +98,79 @@ def convert_session_to_eval_format(session: Session) -> list[dict[str, Any]]:
91
98
  })
92
99
 
93
100
  return eval_case
101
+
102
+
103
+ def convert_session_to_eval_invocations(session: Session) -> list[Invocation]:
104
+ """Converts a session data into a list of Invocation.
105
+
106
+ Args:
107
+ session: The session that should be converted.
108
+
109
+ Returns:
110
+ list: A list of invocation.
111
+ """
112
+ invocations: list[Invocation] = []
113
+ events = session.events if session and session.events else []
114
+
115
+ for event in events:
116
+ if event.author == 'user':
117
+ if not event.content or not event.content.parts:
118
+ continue
119
+
120
+ # The content present in this event is the user content.
121
+ user_content = event.content
122
+ invocation_id = event.invocation_id
123
+ invocaton_timestamp = event.timestamp
124
+
125
+ # Find the corresponding tool usage or response for the query
126
+ tool_uses: list[genai_types.FunctionCall] = []
127
+ intermediate_responses: list[Tuple[str, list[genai_types.Part]]] = []
128
+
129
+ # Check subsequent events to extract tool uses or responses for this turn.
130
+ for subsequent_event in events[events.index(event) + 1 :]:
131
+ event_author = subsequent_event.author or 'agent'
132
+ if event_author == 'user':
133
+ # We found an event where the author was the user. This means that a
134
+ # new turn has started. So close this turn here.
135
+ break
136
+
137
+ if not subsequent_event.content or not subsequent_event.content.parts:
138
+ continue
139
+
140
+ intermediate_response_parts = []
141
+ for subsequent_part in subsequent_event.content.parts:
142
+ # Some events have both function call and reference
143
+ if subsequent_part.function_call:
144
+ tool_uses.append(subsequent_part.function_call)
145
+ elif subsequent_part.text:
146
+ # Also keep track of all the natural language responses that
147
+ # agent (or sub agents) generated.
148
+ intermediate_response_parts.append(subsequent_part)
149
+
150
+ if intermediate_response_parts:
151
+ # Only add an entry if there any intermediate entries.
152
+ intermediate_responses.append(
153
+ (event_author, intermediate_response_parts)
154
+ )
155
+
156
+ # If we are here then either we are done reading all the events or we
157
+ # encountered an event that had content authored by the end-user.
158
+ # This, basically means an end of turn.
159
+ # We assume that the last natural language intermediate response is the
160
+ # final response from the agent/model. We treat that as a reference.
161
+ invocations.append(
162
+ Invocation(
163
+ user_content=user_content,
164
+ invocation_id=invocation_id,
165
+ creation_timestamp=invocaton_timestamp,
166
+ intermediate_data=IntermediateData(
167
+ tool_uses=tool_uses,
168
+ intermediate_responses=intermediate_responses[:-1],
169
+ ),
170
+ final_response=genai_types.Content(
171
+ parts=intermediate_responses[-1][1]
172
+ ),
173
+ )
174
+ )
175
+
176
+ return invocations
@@ -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 os
17
19
  import tempfile
@@ -22,11 +24,12 @@ LOGGING_FORMAT = (
22
24
  )
23
25
 
24
26
 
25
- def log_to_stderr(level=logging.INFO):
26
- logging.basicConfig(
27
- level=level,
28
- format=LOGGING_FORMAT,
29
- )
27
+ def setup_adk_logger(level=logging.INFO):
28
+ # Configure the root logger format and level.
29
+ logging.basicConfig(level=level, format=LOGGING_FORMAT)
30
+
31
+ adk_logger = logging.getLogger('google_adk')
32
+ adk_logger.setLevel(level)
30
33
 
31
34
 
32
35
  def log_to_tmp_folder(
@@ -15,13 +15,15 @@
15
15
  import logging
16
16
 
17
17
  from .base_code_executor import BaseCodeExecutor
18
+ from .built_in_code_executor import BuiltInCodeExecutor
18
19
  from .code_executor_context import CodeExecutorContext
19
20
  from .unsafe_local_code_executor import UnsafeLocalCodeExecutor
20
21
 
21
- logger = logging.getLogger(__name__)
22
+ logger = logging.getLogger('google_adk.' + __name__)
22
23
 
23
24
  __all__ = [
24
25
  'BaseCodeExecutor',
26
+ 'BuiltInCodeExecutor',
25
27
  'CodeExecutorContext',
26
28
  'UnsafeLocalCodeExecutor',
27
29
  ]
@@ -0,0 +1,52 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from google.genai import types
16
+ from typing_extensions import override
17
+
18
+ from ..agents.invocation_context import InvocationContext
19
+ from ..models import LlmRequest
20
+ from .base_code_executor import BaseCodeExecutor
21
+ from .code_execution_utils import CodeExecutionInput
22
+ from .code_execution_utils import CodeExecutionResult
23
+
24
+
25
+ class BuiltInCodeExecutor(BaseCodeExecutor):
26
+ """A code executor that uses the Model's built-in code executor.
27
+
28
+ Currently only supports Gemini 2.0+ models, but will be expanded to
29
+ other models.
30
+ """
31
+
32
+ @override
33
+ def execute_code(
34
+ self,
35
+ invocation_context: InvocationContext,
36
+ code_execution_input: CodeExecutionInput,
37
+ ) -> CodeExecutionResult:
38
+ pass
39
+
40
+ def process_llm_request(self, llm_request: LlmRequest) -> None:
41
+ """Pre-process the LLM request for Gemini 2.0+ models to use the code execution tool."""
42
+ if llm_request.model and llm_request.model.startswith("gemini-2"):
43
+ llm_request.config = llm_request.config or types.GenerateContentConfig()
44
+ llm_request.config.tools = llm_request.config.tools or []
45
+ llm_request.config.tools.append(
46
+ types.Tool(code_execution=types.ToolCodeExecution())
47
+ )
48
+ return
49
+ raise ValueError(
50
+ "Gemini code execution tool is not supported for model"
51
+ f" {llm_request.model}"
52
+ )
@@ -19,7 +19,8 @@ import binascii
19
19
  import copy
20
20
  import dataclasses
21
21
  import re
22
- from typing import List, Optional
22
+ from typing import List
23
+ from typing import Optional
23
24
 
24
25
  from google.genai import types
25
26
 
@@ -27,7 +27,6 @@ from .base_code_executor import BaseCodeExecutor
27
27
  from .code_execution_utils import CodeExecutionInput
28
28
  from .code_execution_utils import CodeExecutionResult
29
29
 
30
-
31
30
  DEFAULT_IMAGE_TAG = 'adk-code-executor:latest'
32
31
 
33
32
 
@@ -15,7 +15,8 @@
15
15
  import datetime
16
16
  import mimetypes
17
17
  import os
18
- from typing import Any, Optional
18
+ from typing import Any
19
+ from typing import Optional
19
20
 
20
21
  from typing_extensions import override
21
22
  from vertexai.preview.extensions import Extension
@@ -147,18 +148,15 @@ class VertexAiCodeExecutor(BaseCodeExecutor):
147
148
  )
148
149
 
149
150
  # Save output file as artifacts.
150
- current_timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
151
- file_name_prefix = '%s_' % str(current_timestamp)
152
151
  saved_files = []
153
152
  file_count = 0
154
153
  for output_file in code_execution_result['output_files']:
155
154
  file_type = output_file['name'].split('.')[-1]
156
- file_name = file_name_prefix + '%d.%s' % (file_count, file_type)
157
155
  if file_type in _SUPPORTED_IMAGE_TYPES:
158
156
  file_count += 1
159
157
  saved_files.append(
160
158
  File(
161
- name='plot_' + file_name,
159
+ name=output_file['name'],
162
160
  content=output_file['contents'],
163
161
  mime_type=f'image/{file_type}',
164
162
  )
@@ -167,16 +165,16 @@ class VertexAiCodeExecutor(BaseCodeExecutor):
167
165
  file_count += 1
168
166
  saved_files.append(
169
167
  File(
170
- name='data_' + file_name,
168
+ name=output_file['name'],
171
169
  content=output_file['contents'],
172
170
  mime_type=f'text/{file_type}',
173
171
  )
174
172
  )
175
173
  else:
176
- mime_type, _ = mimetypes.guess_type(file_name)
174
+ mime_type, _ = mimetypes.guess_type(output_file['name'])
177
175
  saved_files.append(
178
176
  File(
179
- name=file_name,
177
+ name=output_file['name'],
180
178
  content=output_file['contents'],
181
179
  mime_type=mime_type,
182
180
  )
@@ -14,7 +14,7 @@
14
14
 
15
15
  import logging
16
16
 
17
- logger = logging.getLogger(__name__)
17
+ logger = logging.getLogger('google_adk.' + __name__)
18
18
 
19
19
  __all__ = []
20
20