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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. google/adk/agents/callback_context.py +2 -1
  2. google/adk/agents/readonly_context.py +3 -1
  3. google/adk/auth/auth_credential.py +4 -1
  4. google/adk/cli/browser/index.html +4 -4
  5. google/adk/cli/browser/{main-QOEMUXM4.js → main-PKDNKWJE.js} +59 -59
  6. google/adk/cli/browser/polyfills-B6TNHZQ6.js +17 -0
  7. google/adk/cli/cli.py +3 -2
  8. google/adk/cli/cli_eval.py +6 -85
  9. google/adk/cli/cli_tools_click.py +39 -10
  10. google/adk/cli/fast_api.py +53 -184
  11. google/adk/cli/utils/agent_loader.py +137 -0
  12. google/adk/cli/utils/cleanup.py +40 -0
  13. google/adk/cli/utils/evals.py +2 -1
  14. google/adk/cli/utils/logs.py +2 -7
  15. google/adk/code_executors/code_execution_utils.py +2 -1
  16. google/adk/code_executors/container_code_executor.py +0 -1
  17. google/adk/code_executors/vertex_ai_code_executor.py +6 -8
  18. google/adk/evaluation/eval_case.py +3 -1
  19. google/adk/evaluation/eval_metrics.py +74 -0
  20. google/adk/evaluation/eval_result.py +86 -0
  21. google/adk/evaluation/eval_set.py +2 -0
  22. google/adk/evaluation/eval_set_results_manager.py +47 -0
  23. google/adk/evaluation/eval_sets_manager.py +2 -1
  24. google/adk/evaluation/evaluator.py +2 -0
  25. google/adk/evaluation/local_eval_set_results_manager.py +113 -0
  26. google/adk/evaluation/local_eval_sets_manager.py +4 -4
  27. google/adk/evaluation/response_evaluator.py +2 -1
  28. google/adk/evaluation/trajectory_evaluator.py +3 -2
  29. google/adk/examples/base_example_provider.py +1 -0
  30. google/adk/flows/llm_flows/base_llm_flow.py +4 -6
  31. google/adk/flows/llm_flows/contents.py +3 -1
  32. google/adk/flows/llm_flows/instructions.py +7 -77
  33. google/adk/flows/llm_flows/single_flow.py +1 -1
  34. google/adk/models/base_llm.py +2 -1
  35. google/adk/models/base_llm_connection.py +2 -0
  36. google/adk/models/google_llm.py +4 -1
  37. google/adk/models/lite_llm.py +3 -2
  38. google/adk/models/llm_response.py +2 -1
  39. google/adk/runners.py +36 -4
  40. google/adk/sessions/_session_util.py +2 -1
  41. google/adk/sessions/database_session_service.py +5 -8
  42. google/adk/sessions/vertex_ai_session_service.py +28 -13
  43. google/adk/telemetry.py +4 -2
  44. google/adk/tools/agent_tool.py +1 -1
  45. google/adk/tools/apihub_tool/apihub_toolset.py +1 -1
  46. google/adk/tools/apihub_tool/clients/apihub_client.py +10 -3
  47. google/adk/tools/apihub_tool/clients/secret_client.py +1 -0
  48. google/adk/tools/application_integration_tool/application_integration_toolset.py +6 -2
  49. google/adk/tools/application_integration_tool/clients/connections_client.py +8 -1
  50. google/adk/tools/application_integration_tool/clients/integration_client.py +3 -1
  51. google/adk/tools/application_integration_tool/integration_connector_tool.py +1 -1
  52. google/adk/tools/base_toolset.py +40 -2
  53. google/adk/tools/bigquery/__init__.py +28 -0
  54. google/adk/tools/bigquery/bigquery_credentials.py +216 -0
  55. google/adk/tools/bigquery/bigquery_tool.py +116 -0
  56. google/adk/tools/function_parameter_parse_util.py +7 -0
  57. google/adk/tools/function_tool.py +33 -3
  58. google/adk/tools/get_user_choice_tool.py +1 -0
  59. google/adk/tools/google_api_tool/__init__.py +17 -11
  60. google/adk/tools/google_api_tool/google_api_tool.py +1 -1
  61. google/adk/tools/google_api_tool/google_api_toolset.py +0 -14
  62. google/adk/tools/google_api_tool/google_api_toolsets.py +8 -2
  63. google/adk/tools/google_search_tool.py +2 -2
  64. google/adk/tools/mcp_tool/conversion_utils.py +6 -2
  65. google/adk/tools/mcp_tool/mcp_session_manager.py +62 -188
  66. google/adk/tools/mcp_tool/mcp_tool.py +27 -24
  67. google/adk/tools/mcp_tool/mcp_toolset.py +76 -131
  68. google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +1 -3
  69. google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +6 -7
  70. google/adk/tools/openapi_tool/common/common.py +5 -1
  71. google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +7 -2
  72. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +2 -7
  73. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +5 -1
  74. google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +11 -1
  75. google/adk/tools/toolbox_toolset.py +31 -3
  76. google/adk/utils/__init__.py +13 -0
  77. google/adk/utils/instructions_utils.py +131 -0
  78. google/adk/version.py +1 -1
  79. {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/METADATA +12 -15
  80. {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/RECORD +83 -78
  81. google/adk/agents/base_agent.py.orig +0 -330
  82. google/adk/cli/browser/polyfills-FFHMD2TL.js +0 -18
  83. google/adk/cli/fast_api.py.orig +0 -822
  84. google/adk/memory/base_memory_service.py.orig +0 -76
  85. google/adk/models/google_llm.py.orig +0 -305
  86. google/adk/tools/_built_in_code_execution_tool.py +0 -70
  87. google/adk/tools/mcp_tool/mcp_session_manager.py.orig +0 -322
  88. {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/WHEEL +0 -0
  89. {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/entry_points.txt +0 -0
  90. {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -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
  )
@@ -13,7 +13,9 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
- from typing import Any, Optional, Tuple
16
+ from typing import Any
17
+ from typing import Optional
18
+ from typing import Tuple
17
19
 
18
20
  from google.genai import types as genai_types
19
21
  from pydantic import alias_generators
@@ -0,0 +1,74 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Optional
18
+
19
+ from pydantic import alias_generators
20
+ from pydantic import BaseModel
21
+ from pydantic import ConfigDict
22
+
23
+ from .eval_case import Invocation
24
+ from .evaluator import EvalStatus
25
+
26
+
27
+ class EvalMetric(BaseModel):
28
+ """A metric used to evaluate a particular aspect of an eval case."""
29
+
30
+ model_config = ConfigDict(
31
+ alias_generator=alias_generators.to_camel,
32
+ populate_by_name=True,
33
+ )
34
+
35
+ model_config = ConfigDict(
36
+ alias_generator=alias_generators.to_camel,
37
+ populate_by_name=True,
38
+ )
39
+
40
+ metric_name: str
41
+ """The name of the metric."""
42
+
43
+ threshold: float
44
+ """A threshold value. Each metric decides how to interpret this threshold."""
45
+
46
+
47
+ class EvalMetricResult(EvalMetric):
48
+ """The actual computed score/value of a particular EvalMetric."""
49
+
50
+ model_config = ConfigDict(
51
+ alias_generator=alias_generators.to_camel,
52
+ populate_by_name=True,
53
+ )
54
+
55
+ score: Optional[float] = None
56
+ eval_status: EvalStatus
57
+
58
+
59
+ class EvalMetricResultPerInvocation(BaseModel):
60
+ """Eval metric results per invocation."""
61
+
62
+ model_config = ConfigDict(
63
+ alias_generator=alias_generators.to_camel,
64
+ populate_by_name=True,
65
+ )
66
+
67
+ actual_invocation: Invocation
68
+ """The actual invocation, usually obtained by inferencing the agent."""
69
+
70
+ expected_invocation: Invocation
71
+ """The expected invocation, usually the reference or golden invocation."""
72
+
73
+ eval_metric_results: list[EvalMetricResult] = []
74
+ """Eval resutls for each applicable metric."""
@@ -0,0 +1,86 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Optional
18
+
19
+ from pydantic import alias_generators
20
+ from pydantic import BaseModel
21
+ from pydantic import ConfigDict
22
+ from pydantic import Field
23
+
24
+ from ..sessions.session import Session
25
+ from .eval_metrics import EvalMetric
26
+ from .eval_metrics import EvalMetricResult
27
+ from .eval_metrics import EvalMetricResultPerInvocation
28
+ from .evaluator import EvalStatus
29
+
30
+
31
+ class EvalCaseResult(BaseModel):
32
+ """Case level evaluation results."""
33
+
34
+ model_config = ConfigDict(
35
+ alias_generator=alias_generators.to_camel,
36
+ populate_by_name=True,
37
+ )
38
+
39
+ eval_set_file: str = Field(
40
+ deprecated=True,
41
+ description="This field is deprecated, use eval_set_id instead.",
42
+ )
43
+ eval_set_id: str = ""
44
+ """The eval set id."""
45
+
46
+ eval_id: str = ""
47
+ """The eval case id."""
48
+
49
+ final_eval_status: EvalStatus
50
+ """Final eval status for this eval case."""
51
+
52
+ eval_metric_results: list[tuple[EvalMetric, EvalMetricResult]] = Field(
53
+ deprecated=True,
54
+ description=(
55
+ "This field is deprecated, use overall_eval_metric_results instead."
56
+ ),
57
+ )
58
+
59
+ overall_eval_metric_results: list[EvalMetricResult]
60
+ """Overall result for each metric for the entire eval case."""
61
+
62
+ eval_metric_result_per_invocation: list[EvalMetricResultPerInvocation]
63
+ """Result for each metric on a per invocation basis."""
64
+
65
+ session_id: str
66
+ """Session id of the session generated as result of inferencing/scraping stage of the eval."""
67
+
68
+ session_details: Optional[Session] = None
69
+ """Session generated as result of inferencing/scraping stage of the eval."""
70
+
71
+ user_id: Optional[str] = None
72
+ """User id used during inferencing/scraping stage of the eval."""
73
+
74
+
75
+ class EvalSetResult(BaseModel):
76
+ """Eval set level evaluation results."""
77
+
78
+ model_config = ConfigDict(
79
+ alias_generator=alias_generators.to_camel,
80
+ populate_by_name=True,
81
+ )
82
+ eval_set_result_id: str
83
+ eval_set_result_name: str
84
+ eval_set_id: str
85
+ eval_case_results: list[EvalCaseResult] = Field(default_factory=list)
86
+ creation_timestamp: float = 0.0
@@ -13,7 +13,9 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from typing import Optional
16
+
16
17
  from pydantic import BaseModel
18
+
17
19
  from .eval_case import EvalCase
18
20
 
19
21
 
@@ -0,0 +1,47 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from abc import ABC
18
+ from abc import abstractmethod
19
+
20
+ from .eval_result import EvalCaseResult
21
+ from .eval_result import EvalSetResult
22
+
23
+
24
+ class EvalSetResultsManager(ABC):
25
+ """An interface to manage Eval Set Results."""
26
+
27
+ @abstractmethod
28
+ def save_eval_set_result(
29
+ self,
30
+ app_name: str,
31
+ eval_set_id: str,
32
+ eval_case_results: list[EvalCaseResult],
33
+ ) -> None:
34
+ """Creates and saves a new EvalSetResult given eval_case_results."""
35
+ raise NotImplementedError()
36
+
37
+ @abstractmethod
38
+ def get_eval_set_result(
39
+ self, app_name: str, eval_set_result_id: str
40
+ ) -> EvalSetResult:
41
+ """Returns an EvalSetResult identified by app_name and eval_set_result_id."""
42
+ raise NotImplementedError()
43
+
44
+ @abstractmethod
45
+ def list_eval_set_results(self, app_name: str) -> list[str]:
46
+ """Returns the eval result ids that belong to the given app_name."""
47
+ raise NotImplementedError()
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from abc import ABC, abstractmethod
15
+ from abc import ABC
16
+ from abc import abstractmethod
16
17
 
17
18
  from .eval_case import EvalCase
18
19
  from .eval_set import EvalSet
@@ -15,7 +15,9 @@
15
15
  from abc import ABC
16
16
  from enum import Enum
17
17
  from typing import Optional
18
+
18
19
  from pydantic import BaseModel
20
+
19
21
  from .eval_case import Invocation
20
22
 
21
23
 
@@ -0,0 +1,113 @@
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 json
18
+ import logging
19
+ import os
20
+ import time
21
+
22
+ from typing_extensions import override
23
+
24
+ from .eval_result import EvalCaseResult
25
+ from .eval_result import EvalSetResult
26
+ from .eval_set_results_manager import EvalSetResultsManager
27
+
28
+ logger = logging.getLogger("google_adk." + __name__)
29
+
30
+ _ADK_EVAL_HISTORY_DIR = ".adk/eval_history"
31
+ _EVAL_SET_RESULT_FILE_EXTENSION = ".evalset_result.json"
32
+
33
+
34
+ def _sanitize_eval_set_result_name(eval_set_result_name: str) -> str:
35
+ return eval_set_result_name.replace("/", "_")
36
+
37
+
38
+ class LocalEvalSetResultsManager(EvalSetResultsManager):
39
+ """An EvalSetResult manager that stores eval set results locally on disk."""
40
+
41
+ def __init__(self, agents_dir: str):
42
+ self._agents_dir = agents_dir
43
+
44
+ @override
45
+ def save_eval_set_result(
46
+ self,
47
+ app_name: str,
48
+ eval_set_id: str,
49
+ eval_case_results: list[EvalCaseResult],
50
+ ) -> None:
51
+ """Creates and saves a new EvalSetResult given eval_case_results."""
52
+ timestamp = time.time()
53
+ eval_set_result_id = app_name + "_" + eval_set_id + "_" + str(timestamp)
54
+ eval_set_result_name = _sanitize_eval_set_result_name(eval_set_result_id)
55
+ eval_set_result = EvalSetResult(
56
+ eval_set_result_id=eval_set_result_id,
57
+ eval_set_result_name=eval_set_result_name,
58
+ eval_set_id=eval_set_id,
59
+ eval_case_results=eval_case_results,
60
+ creation_timestamp=timestamp,
61
+ )
62
+ # Write eval result file, with eval_set_result_name.
63
+ app_eval_history_dir = self._get_eval_history_dir(app_name)
64
+ if not os.path.exists(app_eval_history_dir):
65
+ os.makedirs(app_eval_history_dir)
66
+ # Convert to json and write to file.
67
+ eval_set_result_json = eval_set_result.model_dump_json()
68
+ eval_set_result_file_path = os.path.join(
69
+ app_eval_history_dir,
70
+ eval_set_result_name + _EVAL_SET_RESULT_FILE_EXTENSION,
71
+ )
72
+ logger.info("Writing eval result to file: %s", eval_set_result_file_path)
73
+ with open(eval_set_result_file_path, "w") as f:
74
+ f.write(json.dumps(eval_set_result_json, indent=2))
75
+
76
+ @override
77
+ def get_eval_set_result(
78
+ self, app_name: str, eval_set_result_id: str
79
+ ) -> EvalSetResult:
80
+ """Returns an EvalSetResult identified by app_name and eval_set_result_id."""
81
+ # Load the eval set result file data.
82
+ maybe_eval_result_file_path = (
83
+ os.path.join(
84
+ self._get_eval_history_dir(app_name),
85
+ eval_set_result_id,
86
+ )
87
+ + _EVAL_SET_RESULT_FILE_EXTENSION
88
+ )
89
+ if not os.path.exists(maybe_eval_result_file_path):
90
+ raise ValueError(
91
+ f"Eval set result `{eval_set_result_id}` does not exist."
92
+ )
93
+ with open(maybe_eval_result_file_path, "r") as file:
94
+ eval_result_data = json.load(file)
95
+ return EvalSetResult.model_validate_json(eval_result_data)
96
+
97
+ @override
98
+ def list_eval_set_results(self, app_name: str) -> list[str]:
99
+ """Returns the eval result ids that belong to the given app_name."""
100
+ app_eval_history_directory = self._get_eval_history_dir(app_name)
101
+
102
+ if not os.path.exists(app_eval_history_directory):
103
+ return []
104
+
105
+ eval_result_files = [
106
+ file.removesuffix(_EVAL_SET_RESULT_FILE_EXTENSION)
107
+ for file in os.listdir(app_eval_history_directory)
108
+ if file.endswith(_EVAL_SET_RESULT_FILE_EXTENSION)
109
+ ]
110
+ return eval_result_files
111
+
112
+ def _get_eval_history_dir(self, app_name: str) -> str:
113
+ return os.path.join(self._agents_dir, app_name, _ADK_EVAL_HISTORY_DIR)
@@ -182,8 +182,8 @@ def load_eval_set_from_file(
182
182
  class LocalEvalSetsManager(EvalSetsManager):
183
183
  """An EvalSets manager that stores eval sets locally on disk."""
184
184
 
185
- def __init__(self, agent_dir: str):
186
- self._agent_dir = agent_dir
185
+ def __init__(self, agents_dir: str):
186
+ self._agents_dir = agents_dir
187
187
 
188
188
  @override
189
189
  def get_eval_set(self, app_name: str, eval_set_id: str) -> EvalSet:
@@ -216,7 +216,7 @@ class LocalEvalSetsManager(EvalSetsManager):
216
216
  @override
217
217
  def list_eval_sets(self, app_name: str) -> list[str]:
218
218
  """Returns a list of EvalSets that belong to the given app_name."""
219
- eval_set_file_path = os.path.join(self._agent_dir, app_name)
219
+ eval_set_file_path = os.path.join(self._agents_dir, app_name)
220
220
  eval_sets = []
221
221
  for file in os.listdir(eval_set_file_path):
222
222
  if file.endswith(_EVAL_SET_FILE_EXTENSION):
@@ -247,7 +247,7 @@ class LocalEvalSetsManager(EvalSetsManager):
247
247
 
248
248
  def _get_eval_set_file_path(self, app_name: str, eval_set_id: str) -> str:
249
249
  return os.path.join(
250
- self._agent_dir,
250
+ self._agents_dir,
251
251
  app_name,
252
252
  eval_set_id + _EVAL_SET_FILE_EXTENSION,
253
253
  )
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Any, Optional
15
+ from typing import Any
16
+ from typing import Optional
16
17
 
17
18
  from deprecated import deprecated
18
19
  from google.genai import types as genai_types
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Any, cast
15
+ from typing import Any
16
+ from typing import cast
16
17
 
17
18
  from deprecated import deprecated
18
19
  from google.genai import types as genai_types
@@ -115,7 +116,7 @@ class TrajectoryEvaluator(Evaluator):
115
116
  tool use trajectories. An exact match scores a 1, 0 otherwise. The final
116
117
  number is an average of these individual scores.
117
118
 
118
- Value range: [0, 1], where 0 is means none of the too use entries aligned,
119
+ Value range: [0, 1], where 0 means none of the tool use entries aligned,
119
120
  and 1 would mean all of them aligned. Higher value is good.
120
121
 
121
122
  Args:
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import abc
16
+
16
17
  from .example import Example
17
18
 
18
19
 
@@ -25,6 +25,7 @@ from typing import TYPE_CHECKING
25
25
 
26
26
  from websockets.exceptions import ConnectionClosedOK
27
27
 
28
+ from . import functions
28
29
  from ...agents.base_agent import BaseAgent
29
30
  from ...agents.callback_context import CallbackContext
30
31
  from ...agents.invocation_context import InvocationContext
@@ -40,7 +41,6 @@ from ...telemetry import trace_call_llm
40
41
  from ...telemetry import trace_send_data
41
42
  from ...telemetry import tracer
42
43
  from ...tools.tool_context import ToolContext
43
- from . import functions
44
44
 
45
45
  if TYPE_CHECKING:
46
46
  from ...agents.llm_agent import LlmAgent
@@ -472,14 +472,12 @@ class BaseLlmFlow(ABC):
472
472
  yield event
473
473
 
474
474
  def _get_agent_to_run(
475
- self, invocation_context: InvocationContext, transfer_to_agent
475
+ self, invocation_context: InvocationContext, agent_name: str
476
476
  ) -> BaseAgent:
477
477
  root_agent = invocation_context.agent.root_agent
478
- agent_to_run = root_agent.find_agent(transfer_to_agent)
478
+ agent_to_run = root_agent.find_agent(agent_name)
479
479
  if not agent_to_run:
480
- raise ValueError(
481
- f'Agent {transfer_to_agent} not found in the agent tree.'
482
- )
480
+ raise ValueError(f'Agent {agent_name} not found in the agent tree.')
483
481
  return agent_to_run
484
482
 
485
483
  async def _call_llm_async(
@@ -15,7 +15,9 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import copy
18
- from typing import AsyncGenerator, Generator, Optional
18
+ from typing import AsyncGenerator
19
+ from typing import Generator
20
+ from typing import Optional
19
21
 
20
22
  from google.genai import types
21
23
  from typing_extensions import override
@@ -26,6 +26,7 @@ from typing_extensions import override
26
26
  from ...agents.readonly_context import ReadonlyContext
27
27
  from ...events.event import Event
28
28
  from ...sessions.state import State
29
+ from ...utils import instructions_utils
29
30
  from ._base_llm_processor import BaseLlmRequestProcessor
30
31
 
31
32
  if TYPE_CHECKING:
@@ -60,7 +61,9 @@ class _InstructionsLlmRequestProcessor(BaseLlmRequestProcessor):
60
61
  )
61
62
  si = raw_si
62
63
  if not bypass_state_injection:
63
- si = await _populate_values(raw_si, invocation_context)
64
+ si = await instructions_utils.inject_session_state(
65
+ raw_si, ReadonlyContext(invocation_context)
66
+ )
64
67
  llm_request.append_instructions([si])
65
68
 
66
69
  # Appends agent instructions if set.
@@ -70,7 +73,9 @@ class _InstructionsLlmRequestProcessor(BaseLlmRequestProcessor):
70
73
  )
71
74
  si = raw_si
72
75
  if not bypass_state_injection:
73
- si = await _populate_values(raw_si, invocation_context)
76
+ si = await instructions_utils.inject_session_state(
77
+ raw_si, ReadonlyContext(invocation_context)
78
+ )
74
79
  llm_request.append_instructions([si])
75
80
 
76
81
  # Maintain async generator behavior
@@ -79,78 +84,3 @@ class _InstructionsLlmRequestProcessor(BaseLlmRequestProcessor):
79
84
 
80
85
 
81
86
  request_processor = _InstructionsLlmRequestProcessor()
82
-
83
-
84
- async def _populate_values(
85
- instruction_template: str,
86
- context: InvocationContext,
87
- ) -> str:
88
- """Populates values in the instruction template, e.g. state, artifact, etc."""
89
-
90
- async def _async_sub(pattern, repl_async_fn, string) -> str:
91
- result = []
92
- last_end = 0
93
- for match in re.finditer(pattern, string):
94
- result.append(string[last_end : match.start()])
95
- replacement = await repl_async_fn(match)
96
- result.append(replacement)
97
- last_end = match.end()
98
- result.append(string[last_end:])
99
- return ''.join(result)
100
-
101
- async def _replace_match(match) -> str:
102
- var_name = match.group().lstrip('{').rstrip('}').strip()
103
- optional = False
104
- if var_name.endswith('?'):
105
- optional = True
106
- var_name = var_name.removesuffix('?')
107
- if var_name.startswith('artifact.'):
108
- var_name = var_name.removeprefix('artifact.')
109
- if context.artifact_service is None:
110
- raise ValueError('Artifact service is not initialized.')
111
- artifact = await context.artifact_service.load_artifact(
112
- app_name=context.session.app_name,
113
- user_id=context.session.user_id,
114
- session_id=context.session.id,
115
- filename=var_name,
116
- )
117
- if not var_name:
118
- raise KeyError(f'Artifact {var_name} not found.')
119
- return str(artifact)
120
- else:
121
- if not _is_valid_state_name(var_name):
122
- return match.group()
123
- if var_name in context.session.state:
124
- return str(context.session.state[var_name])
125
- else:
126
- if optional:
127
- return ''
128
- else:
129
- raise KeyError(f'Context variable not found: `{var_name}`.')
130
-
131
- return await _async_sub(r'{+[^{}]*}+', _replace_match, instruction_template)
132
-
133
-
134
- def _is_valid_state_name(var_name):
135
- """Checks if the variable name is a valid state name.
136
-
137
- Valid state is either:
138
- - Valid identifier
139
- - <Valid prefix>:<Valid identifier>
140
- All the others will just return as it is.
141
-
142
- Args:
143
- var_name: The variable name to check.
144
-
145
- Returns:
146
- True if the variable name is a valid state name, False otherwise.
147
- """
148
- parts = var_name.split(':')
149
- if len(parts) == 1:
150
- return var_name.isidentifier()
151
-
152
- if len(parts) == 2:
153
- prefixes = [State.APP_PREFIX, State.USER_PREFIX, State.TEMP_PREFIX]
154
- if (parts[0] + ':') in prefixes:
155
- return parts[1].isidentifier()
156
- return False
@@ -16,13 +16,13 @@
16
16
 
17
17
  import logging
18
18
 
19
- from ...auth import auth_preprocessor
20
19
  from . import _code_execution
21
20
  from . import _nl_planning
22
21
  from . import basic
23
22
  from . import contents
24
23
  from . import identity
25
24
  from . import instructions
25
+ from ...auth import auth_preprocessor
26
26
  from .base_llm_flow import BaseLlmFlow
27
27
 
28
28
  logger = logging.getLogger('google_adk.' + __name__)
@@ -14,7 +14,8 @@
14
14
  from __future__ import annotations
15
15
 
16
16
  from abc import abstractmethod
17
- from typing import AsyncGenerator, TYPE_CHECKING
17
+ from typing import AsyncGenerator
18
+ from typing import TYPE_CHECKING
18
19
 
19
20
  from google.genai import types
20
21
  from pydantic import BaseModel
@@ -14,7 +14,9 @@
14
14
 
15
15
  from abc import abstractmethod
16
16
  from typing import AsyncGenerator
17
+
17
18
  from google.genai import types
19
+
18
20
  from .llm_response import LlmResponse
19
21
 
20
22