google-cloud-agentplatform 2.1.3__tar.gz → 2.4.0__tar.gz
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_cloud_agentplatform-2.1.3/google_cloud_agentplatform.egg-info → google_cloud_agentplatform-2.4.0}/PKG-INFO +1 -1
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/__init__.py +11 -2
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_evals_common.py +48 -20
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_evals_data_converters.py +306 -1
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_evals_metric_handlers.py +21 -5
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_evals_metric_loaders.py +9 -4
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_evals_visualization.py +3 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_transformers.py +5 -1
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/client.py +26 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/evals.py +28 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/memory_banks.py +7 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/runtimes.py +15 -2
- google_cloud_agentplatform-2.4.0/agentplatform/_genai/serving_profiles.py +1251 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/types/__init__.py +92 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/types/common.py +765 -31
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/frameworks/adk.py +35 -18
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/version.py +1 -1
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0/google_cloud_agentplatform.egg-info}/PKG-INFO +1 -1
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/google_cloud_agentplatform.egg-info/SOURCES.txt +1 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/LICENSE +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/README.md +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/__init__.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_bigquery_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_datasets_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_evals_builtin_tools.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_evals_constant.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_evals_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_gcs_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_logging_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_memory_bank_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_observability_data_converter.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_operations_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_prompt_management_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_prompt_optimizer_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_runtimes_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/_skills_utils.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/a2a_task_events.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/a2a_tasks.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/datasets.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/endpoints.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/example_stores.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/feedback_contexts.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/feedback_entries.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/live.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/live_runtimes.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/memories.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/memory_revisions.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/model_garden.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/prompt_optimizer.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/prompts.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/rag.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/runtime_revisions.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/sandbox_snapshots.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/sandbox_templates.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/sandboxes.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/session_events.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/sessions.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/skill_revisions.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/skills.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/types/agent_engines.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/types/evals.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/types/prompt_optimizer.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/_genai/types/prompts.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/frameworks/__init__.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/frameworks/a2a.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/frameworks/ag2.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/frameworks/langchain.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/frameworks/langgraph.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/frameworks/llama_index.py +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/google_cloud_agentplatform.egg-info/dependency_links.txt +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/google_cloud_agentplatform.egg-info/requires.txt +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/google_cloud_agentplatform.egg-info/top_level.txt +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/pyproject.toml +0 -0
- {google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/setup.cfg +0 -0
{google_cloud_agentplatform-2.1.3 → google_cloud_agentplatform-2.4.0}/agentplatform/__init__.py
RENAMED
|
@@ -54,8 +54,17 @@ def __getattr__(name): # type: ignore[no-untyped-def]
|
|
|
54
54
|
global _genai_types
|
|
55
55
|
if _genai_types is None:
|
|
56
56
|
_genai_types = importlib.import_module("._genai.types", __name__)
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
# `types` is an alias for `._genai.types` rather than a real submodule,
|
|
58
|
+
# so register it to keep
|
|
59
|
+
# `from agentplatform.types import TypeName`
|
|
60
|
+
# working without a prior attribute access. Spell it the google3 way:
|
|
61
|
+
# Copybara rewrites that prefix in both directions, and the external
|
|
62
|
+
# spelling fails its reversibility check. This key was `vertexai.types`,
|
|
63
|
+
# which both misnamed this package's own alias and shadowed the real
|
|
64
|
+
# `vertexai.types` module.
|
|
65
|
+
types_module_name = f"{__name__}.types"
|
|
66
|
+
if types_module_name not in sys.modules:
|
|
67
|
+
sys.modules[types_module_name] = _genai_types
|
|
59
68
|
return _genai_types
|
|
60
69
|
|
|
61
70
|
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
@@ -37,6 +37,11 @@ from google.genai._gaos.types.interactions import functioncallstep
|
|
|
37
37
|
from google.genai._gaos.types.interactions import functionresultstep
|
|
38
38
|
from google.genai._gaos.types.interactions import modeloutputstep
|
|
39
39
|
from google.genai._gaos.types.interactions import userinputstep
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
from google.genai._gaos.utils.serializers import ALLOW_UNKNOWN_UNION_VARIANTS
|
|
43
|
+
except (ImportError, AttributeError):
|
|
44
|
+
ALLOW_UNKNOWN_UNION_VARIANTS = "speakeasy_allow_unknown_union_variants"
|
|
40
45
|
from google.genai.models import Models
|
|
41
46
|
import pandas as pd
|
|
42
47
|
from tqdm import tqdm
|
|
@@ -775,7 +780,16 @@ def _interaction_dict_to_agent_data(
|
|
|
775
780
|
Returns:
|
|
776
781
|
An AgentData object with one or more ConversationTurns.
|
|
777
782
|
"""
|
|
778
|
-
|
|
783
|
+
# A server response, so unknown step types must degrade rather than raise.
|
|
784
|
+
# `Step` is an open discriminated union: parse_open_union only falls back to
|
|
785
|
+
# the Unknown variant when the validation context carries
|
|
786
|
+
# ALLOW_UNKNOWN_UNION_VARIANTS, and raises without it so that a
|
|
787
|
+
# user-constructed request payload surfaces its mistakes locally. This
|
|
788
|
+
# payload comes off the Interactions API, so it is on the tolerant side of
|
|
789
|
+
# that line. _interaction_steps_to_events already drops steps it cannot map.
|
|
790
|
+
typed_interaction = interaction_types.Interaction.model_validate(
|
|
791
|
+
interaction, context={ALLOW_UNKNOWN_UNION_VARIANTS: True}
|
|
792
|
+
)
|
|
779
793
|
all_events = _interaction_steps_to_events(typed_interaction.steps or [])
|
|
780
794
|
|
|
781
795
|
# Group events into turns. Each UserInputStep starts a new turn.
|
|
@@ -1689,8 +1703,12 @@ def _resolve_interactions_to_eval_cases(
|
|
|
1689
1703
|
break
|
|
1690
1704
|
interaction_dict = json.loads(response.body)
|
|
1691
1705
|
try:
|
|
1706
|
+
# Also a server response -- see _interaction_dict_to_agent_data.
|
|
1707
|
+
# Without the context a single step type this client does not
|
|
1708
|
+
# know about raised, and the except below turned that into a
|
|
1709
|
+
# `break`, silently truncating the interaction history.
|
|
1692
1710
|
typed_interaction = interaction_types.Interaction.model_validate(
|
|
1693
|
-
interaction_dict
|
|
1711
|
+
interaction_dict, context={ALLOW_UNKNOWN_UNION_VARIANTS: True}
|
|
1694
1712
|
)
|
|
1695
1713
|
except Exception as e:
|
|
1696
1714
|
logger.warning("Failed to validate interaction model: %s", e)
|
|
@@ -2636,7 +2654,7 @@ def _execute_inference(
|
|
|
2636
2654
|
"Gemini Agent inference completed in %.2f seconds.",
|
|
2637
2655
|
end_time - start_time,
|
|
2638
2656
|
)
|
|
2639
|
-
|
|
2657
|
+
evaluation_dataset = types.EvaluationDataset(
|
|
2640
2658
|
eval_dataset_df=results_df,
|
|
2641
2659
|
candidate_name=gemini_agent.split("/")[-1],
|
|
2642
2660
|
)
|
|
@@ -2741,7 +2759,12 @@ def _execute_inference(
|
|
|
2741
2759
|
results_df.to_json(full_dest_path, orient="records", lines=True)
|
|
2742
2760
|
logger.info("Results saved locally to: %s", full_dest_path)
|
|
2743
2761
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
2744
|
-
logger.error(
|
|
2762
|
+
logger.error(
|
|
2763
|
+
"Failed to save results to %s. Error: %s",
|
|
2764
|
+
full_dest_path,
|
|
2765
|
+
e,
|
|
2766
|
+
exc_info=True,
|
|
2767
|
+
)
|
|
2745
2768
|
|
|
2746
2769
|
return evaluation_dataset
|
|
2747
2770
|
|
|
@@ -2868,6 +2891,21 @@ def _resolve_dataset_inputs(
|
|
|
2868
2891
|
return processed_eval_dataset, num_response_candidates
|
|
2869
2892
|
|
|
2870
2893
|
|
|
2894
|
+
def _prebuilt_evaluation_run_metric(
|
|
2895
|
+
resolved_metric: types.Metric,
|
|
2896
|
+
) -> types.EvaluationRunMetric:
|
|
2897
|
+
"""Builds the evaluation run metric for a resolved RubricMetric."""
|
|
2898
|
+
if resolved_metric.name in _evals_constant.SUPPORTED_PREDEFINED_METRICS:
|
|
2899
|
+
metric_config = t.t_metrics([resolved_metric])[0]
|
|
2900
|
+
else:
|
|
2901
|
+
metric_config = {
|
|
2902
|
+
"predefined_metric_spec": {"metric_spec_name": resolved_metric.name}
|
|
2903
|
+
}
|
|
2904
|
+
return types.EvaluationRunMetric(
|
|
2905
|
+
metric=resolved_metric.name, metric_config=metric_config
|
|
2906
|
+
)
|
|
2907
|
+
|
|
2908
|
+
|
|
2871
2909
|
def _resolve_evaluation_run_metrics(
|
|
2872
2910
|
metrics: Union[list[types.EvaluationRunMetric], list[types.Metric]], api_client: Any
|
|
2873
2911
|
) -> list[types.EvaluationRunMetric]:
|
|
@@ -2885,14 +2923,7 @@ def _resolve_evaluation_run_metrics(
|
|
|
2885
2923
|
resolved_metric = metric_instance.resolve(api_client=api_client)
|
|
2886
2924
|
if resolved_metric.name:
|
|
2887
2925
|
resolved_metrics_list.append(
|
|
2888
|
-
|
|
2889
|
-
metric=resolved_metric.name,
|
|
2890
|
-
metric_config=types.UnifiedMetric(
|
|
2891
|
-
predefined_metric_spec=genai_types.PredefinedMetricSpec(
|
|
2892
|
-
metric_spec_name=resolved_metric.name,
|
|
2893
|
-
)
|
|
2894
|
-
),
|
|
2895
|
-
)
|
|
2926
|
+
_prebuilt_evaluation_run_metric(resolved_metric)
|
|
2896
2927
|
)
|
|
2897
2928
|
except Exception as e:
|
|
2898
2929
|
logger.error(
|
|
@@ -2926,14 +2957,7 @@ def _resolve_evaluation_run_metrics(
|
|
|
2926
2957
|
)
|
|
2927
2958
|
if resolved_metric.name:
|
|
2928
2959
|
resolved_metrics_list.append(
|
|
2929
|
-
|
|
2930
|
-
metric=resolved_metric.name,
|
|
2931
|
-
metric_config=types.UnifiedMetric(
|
|
2932
|
-
predefined_metric_spec=genai_types.PredefinedMetricSpec(
|
|
2933
|
-
metric_spec_name=resolved_metric.name,
|
|
2934
|
-
)
|
|
2935
|
-
),
|
|
2936
|
-
)
|
|
2960
|
+
_prebuilt_evaluation_run_metric(resolved_metric)
|
|
2937
2961
|
)
|
|
2938
2962
|
else:
|
|
2939
2963
|
raise TypeError(
|
|
@@ -3002,6 +3026,7 @@ def _execute_evaluation( # type: ignore[no-untyped-def]
|
|
|
3002
3026
|
dest: Optional[str] = None,
|
|
3003
3027
|
location: Optional[str] = None,
|
|
3004
3028
|
evaluation_service_qps: Optional[float] = None,
|
|
3029
|
+
allow_cross_region_model: Optional[bool] = None,
|
|
3005
3030
|
**kwargs,
|
|
3006
3031
|
) -> types.EvaluationResult:
|
|
3007
3032
|
"""Evaluates a dataset using the provided metrics.
|
|
@@ -3017,6 +3042,8 @@ def _execute_evaluation( # type: ignore[no-untyped-def]
|
|
|
3017
3042
|
evaluation_service_qps: The rate limit (queries per second) for calls
|
|
3018
3043
|
to the evaluation service. Defaults to 10. Increase this value if
|
|
3019
3044
|
your project has a higher EvaluateInstances API quota.
|
|
3045
|
+
allow_cross_region_model: Opt-in flag to authorize cross-region
|
|
3046
|
+
routing for judge models.
|
|
3020
3047
|
**kwargs: Extra arguments to pass to evaluation, such as `agent_info`.
|
|
3021
3048
|
|
|
3022
3049
|
Returns:
|
|
@@ -3099,6 +3126,7 @@ def _execute_evaluation( # type: ignore[no-untyped-def]
|
|
|
3099
3126
|
evaluation_result = _evals_metric_handlers.compute_metrics_and_aggregate(
|
|
3100
3127
|
evaluation_run_config,
|
|
3101
3128
|
evaluation_service_qps=evaluation_service_qps,
|
|
3129
|
+
allow_cross_region_model=allow_cross_region_model,
|
|
3102
3130
|
)
|
|
3103
3131
|
t2 = time.perf_counter()
|
|
3104
3132
|
logger.info("Evaluation took: %f seconds", t2 - t1)
|
|
@@ -15,15 +15,18 @@
|
|
|
15
15
|
"""Dataset converters for evals."""
|
|
16
16
|
|
|
17
17
|
import copy
|
|
18
|
+
import datetime
|
|
18
19
|
import json
|
|
19
20
|
import logging
|
|
20
|
-
from typing import Any, Optional, Union
|
|
21
|
+
from typing import Any, Optional, TypeVar, Union
|
|
21
22
|
|
|
22
23
|
from google.genai import _common
|
|
23
24
|
from google.genai import types as genai_types
|
|
25
|
+
from pydantic import alias_generators
|
|
24
26
|
from pydantic import ValidationError
|
|
25
27
|
from typing_extensions import override
|
|
26
28
|
|
|
29
|
+
from . import _evals_constant
|
|
27
30
|
from . import _evals_utils
|
|
28
31
|
from . import _observability_data_converter
|
|
29
32
|
from . import types
|
|
@@ -605,6 +608,308 @@ class _OpenAIDataConverter(_evals_utils.EvalDataConverter):
|
|
|
605
608
|
return types.EvaluationDataset(eval_cases=eval_cases)
|
|
606
609
|
|
|
607
610
|
|
|
611
|
+
_ADK_EVAL_CASE_FIELDS = frozenset(
|
|
612
|
+
{
|
|
613
|
+
"eval_id",
|
|
614
|
+
"conversation",
|
|
615
|
+
"conversation_scenario",
|
|
616
|
+
"session_input",
|
|
617
|
+
"creation_timestamp",
|
|
618
|
+
"rubrics",
|
|
619
|
+
"final_session_state",
|
|
620
|
+
}
|
|
621
|
+
)
|
|
622
|
+
_ADK_EVAL_CASE_KEYS = _ADK_EVAL_CASE_FIELDS | {
|
|
623
|
+
alias_generators.to_camel(field) for field in _ADK_EVAL_CASE_FIELDS
|
|
624
|
+
}
|
|
625
|
+
_ADK_SESSION_INPUT_FIELDS = ("app_name", "user_id", "state")
|
|
626
|
+
_ADK_DEFAULT_RUBRIC_GROUP = "adk_rubrics"
|
|
627
|
+
_REFERENCE_TRAJECTORY = "reference_trajectory"
|
|
628
|
+
|
|
629
|
+
_GenaiModel = TypeVar("_GenaiModel", bound=_common.BaseModel)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def _get_adk_field(data: dict[str, Any], name: str) -> Any:
|
|
633
|
+
"""Returns an ADK eval set field, which ADK accepts in snake or camel case."""
|
|
634
|
+
if not isinstance(data, dict):
|
|
635
|
+
raise TypeError(f"Expected a JSON object for '{name}', got {type(data)}.")
|
|
636
|
+
if name in data:
|
|
637
|
+
return data[name]
|
|
638
|
+
return data.get(alias_generators.to_camel(name))
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def _validate_adk_genai(model: type[_GenaiModel], data: Any) -> _GenaiModel:
|
|
642
|
+
"""Validates ADK eval set JSON as a genai type.
|
|
643
|
+
|
|
644
|
+
Validating in JSON mode decodes base64 bytes fields, such as inline data and
|
|
645
|
+
thought signatures, the same way ADK does when it reads an eval set file.
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
model: The genai type to validate as.
|
|
649
|
+
data: The parsed JSON value from the eval set.
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
The validated genai object.
|
|
653
|
+
"""
|
|
654
|
+
return model.model_validate_json(json.dumps(data))
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def _adk_content(data: dict[str, Any], default_role: str) -> genai_types.Content:
|
|
658
|
+
"""Validates an ADK Content, filling in the role when the file omits it."""
|
|
659
|
+
content = _validate_adk_genai(genai_types.Content, data)
|
|
660
|
+
if not content.role:
|
|
661
|
+
content.role = default_role
|
|
662
|
+
return content
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def _adk_user_content(invocation: dict[str, Any]) -> genai_types.Content:
|
|
666
|
+
"""Returns the user content of an ADK invocation."""
|
|
667
|
+
user_content = _get_adk_field(invocation, "user_content")
|
|
668
|
+
if user_content is None:
|
|
669
|
+
raise ValueError("Every invocation must have user_content.")
|
|
670
|
+
return _adk_content(user_content, "user")
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
def _adk_intermediate_events(
|
|
674
|
+
intermediate_data: Optional[dict[str, Any]],
|
|
675
|
+
) -> list[tuple[Optional[str], genai_types.Content]]:
|
|
676
|
+
"""Returns (author, content) pairs from ADK IntermediateData or InvocationEvents."""
|
|
677
|
+
if not intermediate_data:
|
|
678
|
+
return []
|
|
679
|
+
invocation_events = _get_adk_field(intermediate_data, "invocation_events")
|
|
680
|
+
if invocation_events is not None:
|
|
681
|
+
return [
|
|
682
|
+
(
|
|
683
|
+
_get_adk_field(event, "author"),
|
|
684
|
+
_validate_adk_genai(
|
|
685
|
+
genai_types.Content, _get_adk_field(event, "content")
|
|
686
|
+
),
|
|
687
|
+
)
|
|
688
|
+
for event in invocation_events
|
|
689
|
+
if _get_adk_field(event, "content")
|
|
690
|
+
]
|
|
691
|
+
|
|
692
|
+
events: list[tuple[Optional[str], genai_types.Content]] = []
|
|
693
|
+
tool_uses = _get_adk_field(intermediate_data, "tool_uses")
|
|
694
|
+
if tool_uses:
|
|
695
|
+
events.append(
|
|
696
|
+
(
|
|
697
|
+
None,
|
|
698
|
+
genai_types.Content(
|
|
699
|
+
role="model",
|
|
700
|
+
parts=[
|
|
701
|
+
genai_types.Part(
|
|
702
|
+
function_call=_validate_adk_genai(
|
|
703
|
+
genai_types.FunctionCall, tool_use
|
|
704
|
+
)
|
|
705
|
+
)
|
|
706
|
+
for tool_use in tool_uses
|
|
707
|
+
],
|
|
708
|
+
),
|
|
709
|
+
)
|
|
710
|
+
)
|
|
711
|
+
tool_responses = _get_adk_field(intermediate_data, "tool_responses")
|
|
712
|
+
if tool_responses:
|
|
713
|
+
events.append(
|
|
714
|
+
(
|
|
715
|
+
None,
|
|
716
|
+
genai_types.Content(
|
|
717
|
+
role="user",
|
|
718
|
+
parts=[
|
|
719
|
+
genai_types.Part(
|
|
720
|
+
function_response=_validate_adk_genai(
|
|
721
|
+
genai_types.FunctionResponse, tool_response
|
|
722
|
+
)
|
|
723
|
+
)
|
|
724
|
+
for tool_response in tool_responses
|
|
725
|
+
],
|
|
726
|
+
),
|
|
727
|
+
)
|
|
728
|
+
)
|
|
729
|
+
for author, parts in (
|
|
730
|
+
_get_adk_field(intermediate_data, "intermediate_responses") or []
|
|
731
|
+
):
|
|
732
|
+
events.append(
|
|
733
|
+
(
|
|
734
|
+
author,
|
|
735
|
+
genai_types.Content(
|
|
736
|
+
role="model",
|
|
737
|
+
parts=[
|
|
738
|
+
_validate_adk_genai(genai_types.Part, part) for part in parts
|
|
739
|
+
],
|
|
740
|
+
),
|
|
741
|
+
)
|
|
742
|
+
)
|
|
743
|
+
return events
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
class AdkEvalSetConverter(_evals_utils.EvalDataConverter):
|
|
747
|
+
"""Converter for ADK eval sets (the `.evalset.json` format).
|
|
748
|
+
|
|
749
|
+
Reads the eval set JSON directly, so google-adk does not need to be
|
|
750
|
+
installed.
|
|
751
|
+
"""
|
|
752
|
+
|
|
753
|
+
def _invocation_to_messages(
|
|
754
|
+
self, invocation: dict[str, Any], turn_index: int
|
|
755
|
+
) -> list[types.evals.Message]:
|
|
756
|
+
"""Converts an earlier ADK invocation into conversation history messages."""
|
|
757
|
+
turn_id = _get_adk_field(invocation, "invocation_id") or str(turn_index)
|
|
758
|
+
timestamp = _get_adk_field(invocation, "creation_timestamp")
|
|
759
|
+
messages = [
|
|
760
|
+
types.evals.Message(
|
|
761
|
+
turn_id=turn_id,
|
|
762
|
+
author="user",
|
|
763
|
+
content=_adk_user_content(invocation),
|
|
764
|
+
creation_timestamp=(
|
|
765
|
+
datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
|
|
766
|
+
if timestamp
|
|
767
|
+
else None
|
|
768
|
+
),
|
|
769
|
+
)
|
|
770
|
+
]
|
|
771
|
+
messages.extend(
|
|
772
|
+
types.evals.Message(turn_id=turn_id, author=author, content=content)
|
|
773
|
+
for author, content in _adk_intermediate_events(
|
|
774
|
+
_get_adk_field(invocation, "intermediate_data")
|
|
775
|
+
)
|
|
776
|
+
)
|
|
777
|
+
final_response = _get_adk_field(invocation, "final_response")
|
|
778
|
+
if final_response:
|
|
779
|
+
messages.append(
|
|
780
|
+
types.evals.Message(
|
|
781
|
+
turn_id=turn_id, content=_adk_content(final_response, "model")
|
|
782
|
+
)
|
|
783
|
+
)
|
|
784
|
+
return messages
|
|
785
|
+
|
|
786
|
+
def _convert_rubrics(
|
|
787
|
+
self, rubrics: list[dict[str, Any]]
|
|
788
|
+
) -> dict[str, types.RubricGroup]:
|
|
789
|
+
"""Groups ADK rubrics by their type, which ADK uses to select rubrics."""
|
|
790
|
+
groups: dict[str, list[types.evals.Rubric]] = {}
|
|
791
|
+
for rubric in rubrics:
|
|
792
|
+
rubric_type = _get_adk_field(rubric, "type")
|
|
793
|
+
rubric_content = _get_adk_field(rubric, "rubric_content") or {}
|
|
794
|
+
groups.setdefault(rubric_type or _ADK_DEFAULT_RUBRIC_GROUP, []).append(
|
|
795
|
+
types.evals.Rubric(
|
|
796
|
+
rubric_id=_get_adk_field(rubric, "rubric_id"),
|
|
797
|
+
type=rubric_type,
|
|
798
|
+
content=types.evals.RubricContent(
|
|
799
|
+
property=types.evals.RubricContentProperty(
|
|
800
|
+
description=_get_adk_field(rubric_content, "text_property")
|
|
801
|
+
)
|
|
802
|
+
),
|
|
803
|
+
)
|
|
804
|
+
)
|
|
805
|
+
return {
|
|
806
|
+
name: types.RubricGroup(rubrics=group) for name, group in groups.items()
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
def _convert_eval_case(self, case: dict[str, Any]) -> types.EvalCase:
|
|
810
|
+
"""Converts one ADK EvalCase into an EvalCase."""
|
|
811
|
+
if not isinstance(case, dict):
|
|
812
|
+
raise TypeError(f"Expected an ADK EvalCase object, got {type(case)}.")
|
|
813
|
+
conversation = _get_adk_field(case, "conversation")
|
|
814
|
+
scenario = _get_adk_field(case, "conversation_scenario")
|
|
815
|
+
if (conversation is None) == (scenario is None):
|
|
816
|
+
raise ValueError(
|
|
817
|
+
"Exactly one of conversation and conversation_scenario must be set."
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
fields: dict[str, Any] = {"eval_case_id": _get_adk_field(case, "eval_id")}
|
|
821
|
+
rubrics = list(_get_adk_field(case, "rubrics") or [])
|
|
822
|
+
if scenario is not None:
|
|
823
|
+
fields["user_scenario"] = types.evals.UserScenario(
|
|
824
|
+
starting_prompt=_get_adk_field(scenario, "starting_prompt"),
|
|
825
|
+
conversation_plan=_get_adk_field(scenario, "conversation_plan"),
|
|
826
|
+
)
|
|
827
|
+
else:
|
|
828
|
+
if not conversation:
|
|
829
|
+
raise ValueError("conversation must have at least one invocation.")
|
|
830
|
+
*earlier_invocations, last_invocation = conversation
|
|
831
|
+
fields["prompt"] = _adk_user_content(last_invocation)
|
|
832
|
+
history = []
|
|
833
|
+
for turn_index, invocation in enumerate(earlier_invocations):
|
|
834
|
+
history.extend(self._invocation_to_messages(invocation, turn_index))
|
|
835
|
+
if history:
|
|
836
|
+
fields["conversation_history"] = history
|
|
837
|
+
final_response = _get_adk_field(last_invocation, "final_response")
|
|
838
|
+
if final_response:
|
|
839
|
+
fields["reference"] = types.ResponseCandidate(
|
|
840
|
+
response=_adk_content(final_response, "model")
|
|
841
|
+
)
|
|
842
|
+
reference_trajectory: list[genai_types.Content] = []
|
|
843
|
+
for _, content in _adk_intermediate_events(
|
|
844
|
+
_get_adk_field(last_invocation, "intermediate_data")
|
|
845
|
+
):
|
|
846
|
+
reference_trajectory.extend(
|
|
847
|
+
genai_types.Content(
|
|
848
|
+
role="model",
|
|
849
|
+
parts=[genai_types.Part(function_call=part.function_call)],
|
|
850
|
+
)
|
|
851
|
+
for part in content.parts or []
|
|
852
|
+
if part.function_call
|
|
853
|
+
)
|
|
854
|
+
if reference_trajectory:
|
|
855
|
+
fields[_REFERENCE_TRAJECTORY] = reference_trajectory
|
|
856
|
+
rubrics.extend(_get_adk_field(last_invocation, "rubrics") or [])
|
|
857
|
+
|
|
858
|
+
if rubrics:
|
|
859
|
+
fields["rubric_groups"] = self._convert_rubrics(rubrics)
|
|
860
|
+
session_input = _get_adk_field(case, "session_input")
|
|
861
|
+
if session_input:
|
|
862
|
+
fields[_evals_constant.SESSION_INPUT] = {
|
|
863
|
+
name: value
|
|
864
|
+
for name in _ADK_SESSION_INPUT_FIELDS
|
|
865
|
+
if (value := _get_adk_field(session_input, name)) is not None
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
reserved_keys = set(fields)
|
|
869
|
+
for field_name in types.EvalCase.model_fields:
|
|
870
|
+
reserved_keys.update((field_name, alias_generators.to_camel(field_name)))
|
|
871
|
+
for key, value in case.items():
|
|
872
|
+
if key in _ADK_EVAL_CASE_KEYS:
|
|
873
|
+
continue
|
|
874
|
+
if key in reserved_keys:
|
|
875
|
+
logger.warning(
|
|
876
|
+
"Skipping ADK eval case field '%s' because it conflicts with"
|
|
877
|
+
" an EvalCase field.",
|
|
878
|
+
key,
|
|
879
|
+
)
|
|
880
|
+
continue
|
|
881
|
+
fields[key] = value
|
|
882
|
+
return types.EvalCase(**fields)
|
|
883
|
+
|
|
884
|
+
@override
|
|
885
|
+
def convert(self, raw_data: dict[str, Any]) -> types.EvaluationDataset:
|
|
886
|
+
"""Converts a parsed ADK EvalSet into an EvaluationDataset."""
|
|
887
|
+
if not isinstance(raw_data, dict):
|
|
888
|
+
raise ValueError(
|
|
889
|
+
"Expected an ADK EvalSet JSON object, got"
|
|
890
|
+
f" {type(raw_data)}. For the legacy list format, load the"
|
|
891
|
+
" file with google.adk.evaluation.local_eval_sets_manager"
|
|
892
|
+
".load_eval_set_from_file() and pass"
|
|
893
|
+
" eval_set.model_dump(mode='json') instead."
|
|
894
|
+
)
|
|
895
|
+
adk_eval_cases = _get_adk_field(raw_data, "eval_cases")
|
|
896
|
+
if not isinstance(adk_eval_cases, list):
|
|
897
|
+
raise ValueError("Expected an ADK EvalSet with an 'eval_cases' list.")
|
|
898
|
+
|
|
899
|
+
eval_cases = []
|
|
900
|
+
for i, case in enumerate(adk_eval_cases):
|
|
901
|
+
try:
|
|
902
|
+
eval_cases.append(self._convert_eval_case(case))
|
|
903
|
+
except (TypeError, ValueError) as e:
|
|
904
|
+
case_id = (
|
|
905
|
+
_get_adk_field(case, "eval_id") if isinstance(case, dict) else None
|
|
906
|
+
)
|
|
907
|
+
raise ValueError(
|
|
908
|
+
f"Failed to convert ADK eval case '{case_id or i}': {e}"
|
|
909
|
+
) from e
|
|
910
|
+
return types.EvaluationDataset(eval_cases=eval_cases)
|
|
911
|
+
|
|
912
|
+
|
|
608
913
|
def auto_detect_dataset_schema(
|
|
609
914
|
raw_dataset: list[dict[str, Any]],
|
|
610
915
|
) -> Union[EvalDatasetSchema, str]:
|
|
@@ -288,6 +288,7 @@ class MetricHandler(abc.ABC, Generic[T]):
|
|
|
288
288
|
def __init__(self, module: "evals.Evals", metric: T):
|
|
289
289
|
self.module = module
|
|
290
290
|
self.metric: T = metric
|
|
291
|
+
self.allow_cross_region_model: Optional[bool] = None
|
|
291
292
|
|
|
292
293
|
@property
|
|
293
294
|
@abc.abstractmethod
|
|
@@ -761,6 +762,7 @@ class LLMMetricHandler(MetricHandler[types.LLMMetric]):
|
|
|
761
762
|
lambda: self.module._evaluate_instances(
|
|
762
763
|
metrics=[self.metric],
|
|
763
764
|
instance=instance,
|
|
765
|
+
allow_cross_region_model=self.allow_cross_region_model,
|
|
764
766
|
),
|
|
765
767
|
self.metric_name,
|
|
766
768
|
)
|
|
@@ -981,7 +983,7 @@ class PredefinedMetricHandler(MetricHandler[types.Metric]):
|
|
|
981
983
|
raise ValueError(
|
|
982
984
|
f"Metric '{self.metric.name}' is not a supported predefined metric."
|
|
983
985
|
)
|
|
984
|
-
if (
|
|
986
|
+
if self.metric.name.startswith("multi_turn") and (
|
|
985
987
|
self.metric.judge_model
|
|
986
988
|
or self.metric.judge_model_generation_config
|
|
987
989
|
or self.metric.judge_model_sampling_count
|
|
@@ -989,7 +991,8 @@ class PredefinedMetricHandler(MetricHandler[types.Metric]):
|
|
|
989
991
|
logger.warning(
|
|
990
992
|
"Autorater config settings (judge_model, "
|
|
991
993
|
"judge_model_generation_config, judge_model_sampling_count) "
|
|
992
|
-
"are ignored for
|
|
994
|
+
"are ignored for multi-turn metric '%s'. Use "
|
|
995
|
+
"judge_model_step_configs to set its judges.",
|
|
993
996
|
self.metric.name,
|
|
994
997
|
)
|
|
995
998
|
|
|
@@ -1071,6 +1074,7 @@ class PredefinedMetricHandler(MetricHandler[types.Metric]):
|
|
|
1071
1074
|
metrics=[self.metric],
|
|
1072
1075
|
instance=payload.get("instance"),
|
|
1073
1076
|
autorater_config=payload.get("autorater_config"),
|
|
1077
|
+
allow_cross_region_model=self.allow_cross_region_model,
|
|
1074
1078
|
),
|
|
1075
1079
|
metric_name,
|
|
1076
1080
|
)
|
|
@@ -1318,6 +1322,7 @@ class RegisteredMetricHandler(MetricHandler[types.Metric]):
|
|
|
1318
1322
|
metric_sources=[metric_source],
|
|
1319
1323
|
instance=payload.get("instance"),
|
|
1320
1324
|
autorater_config=payload.get("autorater_config"),
|
|
1325
|
+
allow_cross_region_model=self.allow_cross_region_model,
|
|
1321
1326
|
),
|
|
1322
1327
|
metric_name,
|
|
1323
1328
|
)
|
|
@@ -1404,12 +1409,16 @@ MetricHandlerType = TypeVar(
|
|
|
1404
1409
|
|
|
1405
1410
|
|
|
1406
1411
|
def get_handler_for_metric(
|
|
1407
|
-
module: "evals.Evals",
|
|
1412
|
+
module: "evals.Evals",
|
|
1413
|
+
metric: types.Metric,
|
|
1414
|
+
allow_cross_region_model: Optional[bool] = None,
|
|
1408
1415
|
) -> Union[MetricHandlerType, Any]:
|
|
1409
1416
|
"""Returns a metric handler for the given metric."""
|
|
1410
1417
|
for condition, handler_class in _METRIC_HANDLER_MAPPING:
|
|
1411
1418
|
if condition(metric): # type: ignore[no-untyped-call]
|
|
1412
|
-
|
|
1419
|
+
handler = handler_class(module=module, metric=metric)
|
|
1420
|
+
handler.allow_cross_region_model = allow_cross_region_model
|
|
1421
|
+
return handler
|
|
1413
1422
|
raise ValueError(f"Unsupported metric: {metric.name}")
|
|
1414
1423
|
|
|
1415
1424
|
|
|
@@ -1548,6 +1557,7 @@ def _rate_limited_get_metric_result(
|
|
|
1548
1557
|
def compute_metrics_and_aggregate(
|
|
1549
1558
|
evaluation_run_config: EvaluationRunConfig,
|
|
1550
1559
|
evaluation_service_qps: Optional[float] = None,
|
|
1560
|
+
allow_cross_region_model: Optional[bool] = None,
|
|
1551
1561
|
) -> types.EvaluationResult:
|
|
1552
1562
|
"""Computes metrics and aggregates them for a given evaluation run config.
|
|
1553
1563
|
|
|
@@ -1556,6 +1566,8 @@ def compute_metrics_and_aggregate(
|
|
|
1556
1566
|
evaluation_service_qps: Optional QPS limit for the evaluation service.
|
|
1557
1567
|
Defaults to _DEFAULT_EVAL_SERVICE_QPS (10). Users with higher
|
|
1558
1568
|
quotas can increase this value.
|
|
1569
|
+
allow_cross_region_model: Opt-in flag to authorize cross-region
|
|
1570
|
+
routing for judge models.
|
|
1559
1571
|
"""
|
|
1560
1572
|
metric_handlers = []
|
|
1561
1573
|
all_futures = []
|
|
@@ -1574,7 +1586,11 @@ def compute_metrics_and_aggregate(
|
|
|
1574
1586
|
|
|
1575
1587
|
for eval_metric in evaluation_run_config.metrics:
|
|
1576
1588
|
metric_handlers.append(
|
|
1577
|
-
get_handler_for_metric(
|
|
1589
|
+
get_handler_for_metric(
|
|
1590
|
+
evaluation_run_config.evals_module,
|
|
1591
|
+
eval_metric,
|
|
1592
|
+
allow_cross_region_model=allow_cross_region_model,
|
|
1593
|
+
)
|
|
1578
1594
|
)
|
|
1579
1595
|
|
|
1580
1596
|
eval_case_count = len(evaluation_run_config.dataset.eval_cases)
|
|
@@ -215,8 +215,11 @@ class LazyLoadedPrebuiltMetric:
|
|
|
215
215
|
if self._resolved_metric:
|
|
216
216
|
return self._resolved_metric
|
|
217
217
|
|
|
218
|
+
# The shared cache is keyed by name and version only, so a metric with
|
|
219
|
+
# overrides (such as judge_model_step_configs) must bypass it.
|
|
220
|
+
use_cache = not self.metric_kwargs
|
|
218
221
|
cache_key = f"{self.name}@{self.version or 'default'}"
|
|
219
|
-
if cache_key in LazyLoadedPrebuiltMetric._cache:
|
|
222
|
+
if use_cache and cache_key in LazyLoadedPrebuiltMetric._cache:
|
|
220
223
|
self._resolved_metric = LazyLoadedPrebuiltMetric._cache[cache_key]
|
|
221
224
|
logger.debug("Metric '%s' found in cache.", cache_key)
|
|
222
225
|
return self._resolved_metric
|
|
@@ -225,7 +228,8 @@ class LazyLoadedPrebuiltMetric:
|
|
|
225
228
|
api_metric = self._resolve_api_predefined()
|
|
226
229
|
if api_metric:
|
|
227
230
|
self._resolved_metric = api_metric
|
|
228
|
-
|
|
231
|
+
if use_cache:
|
|
232
|
+
LazyLoadedPrebuiltMetric._cache[cache_key] = self._resolved_metric
|
|
229
233
|
return self._resolved_metric
|
|
230
234
|
|
|
231
235
|
# Fallback to GCS loading for custom LLM-based Prebuilt Metrics
|
|
@@ -234,8 +238,9 @@ class LazyLoadedPrebuiltMetric:
|
|
|
234
238
|
)
|
|
235
239
|
try:
|
|
236
240
|
gcs_metric = self._fetch_and_parse(api_client)
|
|
237
|
-
|
|
238
|
-
|
|
241
|
+
if use_cache:
|
|
242
|
+
final_cache_key = f"{self.name}@{self.version}"
|
|
243
|
+
LazyLoadedPrebuiltMetric._cache[final_cache_key] = gcs_metric
|
|
239
244
|
self._resolved_metric = gcs_metric
|
|
240
245
|
return self._resolved_metric
|
|
241
246
|
except Exception as e:
|
|
@@ -232,6 +232,9 @@ def _extract_dataset_rows(dataset: types.EvaluationDataset) -> list[dict[str, An
|
|
|
232
232
|
return processed_rows
|
|
233
233
|
|
|
234
234
|
|
|
235
|
+
extract_dataset_rows = _extract_dataset_rows
|
|
236
|
+
|
|
237
|
+
|
|
235
238
|
def get_evaluation_html(eval_result_json: str) -> str:
|
|
236
239
|
"""Returns a self-contained HTML for single evaluation visualization."""
|
|
237
240
|
payload_b64 = _encode_to_base64(eval_result_json)
|
|
@@ -64,10 +64,14 @@ def t_metrics(
|
|
|
64
64
|
elif (
|
|
65
65
|
metric_name and metric_name in _evals_constant.SUPPORTED_PREDEFINED_METRICS
|
|
66
66
|
):
|
|
67
|
-
|
|
67
|
+
predefined_spec: dict[str, Any] = {
|
|
68
68
|
"metric_spec_name": metric_name,
|
|
69
69
|
"metric_spec_parameters": metric.metric_spec_parameters,
|
|
70
70
|
}
|
|
71
|
+
step_configs = getv(metric, ["judge_model_step_configs"])
|
|
72
|
+
if step_configs:
|
|
73
|
+
predefined_spec["step_autorater_configs"] = step_configs
|
|
74
|
+
metric_payload_item["predefined_metric_spec"] = predefined_spec
|
|
71
75
|
# Custom Code Execution Metric
|
|
72
76
|
elif (
|
|
73
77
|
hasattr(metric, "remote_custom_function") and metric.remote_custom_function
|