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.
- google/adk/agents/callback_context.py +2 -1
- google/adk/agents/readonly_context.py +3 -1
- google/adk/auth/auth_credential.py +4 -1
- google/adk/cli/browser/index.html +4 -4
- google/adk/cli/browser/{main-QOEMUXM4.js → main-PKDNKWJE.js} +59 -59
- google/adk/cli/browser/polyfills-B6TNHZQ6.js +17 -0
- google/adk/cli/cli.py +3 -2
- google/adk/cli/cli_eval.py +6 -85
- google/adk/cli/cli_tools_click.py +39 -10
- google/adk/cli/fast_api.py +53 -184
- google/adk/cli/utils/agent_loader.py +137 -0
- google/adk/cli/utils/cleanup.py +40 -0
- google/adk/cli/utils/evals.py +2 -1
- google/adk/cli/utils/logs.py +2 -7
- google/adk/code_executors/code_execution_utils.py +2 -1
- google/adk/code_executors/container_code_executor.py +0 -1
- google/adk/code_executors/vertex_ai_code_executor.py +6 -8
- google/adk/evaluation/eval_case.py +3 -1
- google/adk/evaluation/eval_metrics.py +74 -0
- google/adk/evaluation/eval_result.py +86 -0
- google/adk/evaluation/eval_set.py +2 -0
- google/adk/evaluation/eval_set_results_manager.py +47 -0
- google/adk/evaluation/eval_sets_manager.py +2 -1
- google/adk/evaluation/evaluator.py +2 -0
- google/adk/evaluation/local_eval_set_results_manager.py +113 -0
- google/adk/evaluation/local_eval_sets_manager.py +4 -4
- google/adk/evaluation/response_evaluator.py +2 -1
- google/adk/evaluation/trajectory_evaluator.py +3 -2
- google/adk/examples/base_example_provider.py +1 -0
- google/adk/flows/llm_flows/base_llm_flow.py +4 -6
- google/adk/flows/llm_flows/contents.py +3 -1
- google/adk/flows/llm_flows/instructions.py +7 -77
- google/adk/flows/llm_flows/single_flow.py +1 -1
- google/adk/models/base_llm.py +2 -1
- google/adk/models/base_llm_connection.py +2 -0
- google/adk/models/google_llm.py +4 -1
- google/adk/models/lite_llm.py +3 -2
- google/adk/models/llm_response.py +2 -1
- google/adk/runners.py +36 -4
- google/adk/sessions/_session_util.py +2 -1
- google/adk/sessions/database_session_service.py +5 -8
- google/adk/sessions/vertex_ai_session_service.py +28 -13
- google/adk/telemetry.py +4 -2
- google/adk/tools/agent_tool.py +1 -1
- google/adk/tools/apihub_tool/apihub_toolset.py +1 -1
- google/adk/tools/apihub_tool/clients/apihub_client.py +10 -3
- google/adk/tools/apihub_tool/clients/secret_client.py +1 -0
- google/adk/tools/application_integration_tool/application_integration_toolset.py +6 -2
- google/adk/tools/application_integration_tool/clients/connections_client.py +8 -1
- google/adk/tools/application_integration_tool/clients/integration_client.py +3 -1
- google/adk/tools/application_integration_tool/integration_connector_tool.py +1 -1
- google/adk/tools/base_toolset.py +40 -2
- google/adk/tools/bigquery/__init__.py +28 -0
- google/adk/tools/bigquery/bigquery_credentials.py +216 -0
- google/adk/tools/bigquery/bigquery_tool.py +116 -0
- google/adk/tools/function_parameter_parse_util.py +7 -0
- google/adk/tools/function_tool.py +33 -3
- google/adk/tools/get_user_choice_tool.py +1 -0
- google/adk/tools/google_api_tool/__init__.py +17 -11
- google/adk/tools/google_api_tool/google_api_tool.py +1 -1
- google/adk/tools/google_api_tool/google_api_toolset.py +0 -14
- google/adk/tools/google_api_tool/google_api_toolsets.py +8 -2
- google/adk/tools/google_search_tool.py +2 -2
- google/adk/tools/mcp_tool/conversion_utils.py +6 -2
- google/adk/tools/mcp_tool/mcp_session_manager.py +62 -188
- google/adk/tools/mcp_tool/mcp_tool.py +27 -24
- google/adk/tools/mcp_tool/mcp_toolset.py +76 -131
- google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +1 -3
- google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +6 -7
- google/adk/tools/openapi_tool/common/common.py +5 -1
- google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +7 -2
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +2 -7
- google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +5 -1
- google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +11 -1
- google/adk/tools/toolbox_toolset.py +31 -3
- google/adk/utils/__init__.py +13 -0
- google/adk/utils/instructions_utils.py +131 -0
- google/adk/version.py +1 -1
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/METADATA +12 -15
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/RECORD +83 -78
- google/adk/agents/base_agent.py.orig +0 -330
- google/adk/cli/browser/polyfills-FFHMD2TL.js +0 -18
- google/adk/cli/fast_api.py.orig +0 -822
- google/adk/memory/base_memory_service.py.orig +0 -76
- google/adk/models/google_llm.py.orig +0 -305
- google/adk/tools/_built_in_code_execution_tool.py +0 -70
- google/adk/tools/mcp_tool/mcp_session_manager.py.orig +0 -322
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/WHEEL +0 -0
- {google_adk-1.0.0.dist-info → google_adk-1.1.0.dist-info}/entry_points.txt +0 -0
- {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
|
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='
|
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='
|
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(
|
174
|
+
mime_type, _ = mimetypes.guess_type(output_file['name'])
|
177
175
|
saved_files.append(
|
178
176
|
File(
|
179
|
-
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
|
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
|
@@ -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
|
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
|
@@ -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,
|
186
|
-
self.
|
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.
|
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.
|
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
|
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
|
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
|
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:
|
@@ -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,
|
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(
|
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
|
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
|
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
|
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__)
|
google/adk/models/base_llm.py
CHANGED
@@ -14,7 +14,8 @@
|
|
14
14
|
from __future__ import annotations
|
15
15
|
|
16
16
|
from abc import abstractmethod
|
17
|
-
from typing import AsyncGenerator
|
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
|