Skip to content

Commit 82a4816

Browse files
jsondaicopybara-github
authored andcommitted
chore: GenAI Client(evals) - Pin all RubricMetric properties to v1
PiperOrigin-RevId: 919325887
1 parent 6c6a047 commit 82a4816

2 files changed

Lines changed: 125 additions & 35 deletions

File tree

agentplatform/_genai/_evals_metric_loaders.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ def _get_latest_version_uri(self, api_client: Any, metric_gcs_dir: str) -> str:
9292

9393
blobs = gcs_utils.storage_client.list_blobs(bucket_name, prefix=prefix)
9494

95-
version_files: list[dict[str, Union[list[int], str]]] = (
96-
[]
97-
) # {'version_parts': [1,0,0], 'filename': 'v1.0.0.yaml'}
95+
version_files: list[
96+
dict[str, Union[list[int], str]]
97+
] = [] # {'version_parts': [1,0,0], 'filename': 'v1.0.0.yaml'}
9898

9999
version_pattern = re.compile(
100100
r"v(\d+)(?:\.(\d+))?(?:\.(\d+))?\.(yaml|yml|json)$", re.IGNORECASE
@@ -277,27 +277,27 @@ def __getattr__(
277277

278278
@property
279279
def GENERAL_QUALITY(self) -> LazyLoadedPrebuiltMetric:
280-
return self.__getattr__("GENERAL_QUALITY")
280+
return self.__getattr__("GENERAL_QUALITY", version="v1")
281281

282282
@property
283283
def TEXT_QUALITY(self) -> LazyLoadedPrebuiltMetric:
284-
return self.__getattr__("TEXT_QUALITY")
284+
return self.__getattr__("TEXT_QUALITY", version="v1")
285285

286286
@property
287287
def INSTRUCTION_FOLLOWING(self) -> LazyLoadedPrebuiltMetric:
288-
return self.__getattr__("INSTRUCTION_FOLLOWING")
288+
return self.__getattr__("INSTRUCTION_FOLLOWING", version="v1")
289289

290290
@property
291291
def SAFETY(self) -> LazyLoadedPrebuiltMetric:
292-
return self.__getattr__("SAFETY")
292+
return self.__getattr__("SAFETY", version="v1")
293293

294294
@property
295295
def MULTI_TURN_GENERAL_QUALITY(self) -> LazyLoadedPrebuiltMetric:
296-
return self.__getattr__("MULTI_TURN_GENERAL_QUALITY")
296+
return self.__getattr__("MULTI_TURN_GENERAL_QUALITY", version="v1")
297297

298298
@property
299299
def MULTI_TURN_TEXT_QUALITY(self) -> LazyLoadedPrebuiltMetric:
300-
return self.__getattr__("MULTI_TURN_TEXT_QUALITY")
300+
return self.__getattr__("MULTI_TURN_TEXT_QUALITY", version="v1")
301301

302302
@property
303303
def MULTI_TURN_TOOL_USE_QUALITY(self) -> LazyLoadedPrebuiltMetric:
@@ -317,55 +317,72 @@ def FINAL_RESPONSE_MATCH(self) -> LazyLoadedPrebuiltMetric:
317317

318318
@property
319319
def FINAL_RESPONSE_REFERENCE_FREE(self) -> LazyLoadedPrebuiltMetric:
320-
return self.__getattr__("FINAL_RESPONSE_REFERENCE_FREE")
320+
return self.__getattr__("FINAL_RESPONSE_REFERENCE_FREE", version="v1")
321321

322322
@property
323323
def COHERENCE(self) -> LazyLoadedPrebuiltMetric:
324-
return self.__getattr__("COHERENCE")
324+
return self.__getattr__("COHERENCE", version="v1")
325325

326326
@property
327327
def FLUENCY(self) -> LazyLoadedPrebuiltMetric:
328-
return self.__getattr__("FLUENCY")
328+
return self.__getattr__("FLUENCY", version="v1")
329329

330330
@property
331331
def VERBOSITY(self) -> LazyLoadedPrebuiltMetric:
332-
return self.__getattr__("VERBOSITY")
332+
return self.__getattr__("VERBOSITY", version="v1")
333333

334334
@property
335335
def SUMMARIZATION_QUALITY(self) -> LazyLoadedPrebuiltMetric:
336-
return self.__getattr__("SUMMARIZATION_QUALITY")
336+
return self.__getattr__("SUMMARIZATION_QUALITY", version="v1")
337337

338338
@property
339339
def QUESTION_ANSWERING_QUALITY(self) -> LazyLoadedPrebuiltMetric:
340-
return self.__getattr__("QUESTION_ANSWERING_QUALITY")
340+
return self.__getattr__("QUESTION_ANSWERING_QUALITY", version="v1")
341341

342342
@property
343343
def MULTI_TURN_CHAT_QUALITY(self) -> LazyLoadedPrebuiltMetric:
344-
return self.__getattr__("MULTI_TURN_CHAT_QUALITY")
344+
return self.__getattr__("MULTI_TURN_CHAT_QUALITY", version="v1")
345345

346346
@property
347347
def MULTI_TURN_SAFETY(self) -> LazyLoadedPrebuiltMetric:
348-
return self.__getattr__("MULTI_TURN_SAFETY")
348+
return self.__getattr__("MULTI_TURN_SAFETY", version="v1")
349349

350350
@property
351351
def FINAL_RESPONSE_QUALITY(self) -> LazyLoadedPrebuiltMetric:
352-
return self.__getattr__("FINAL_RESPONSE_QUALITY")
352+
return self.__getattr__("FINAL_RESPONSE_QUALITY", version="v1")
353353

354354
@property
355355
def HALLUCINATION(self) -> LazyLoadedPrebuiltMetric:
356-
return self.__getattr__("HALLUCINATION")
356+
return self.__getattr__("HALLUCINATION", version="v1")
357+
358+
@property
359+
def GROUNDING(self) -> LazyLoadedPrebuiltMetric: # pylint: disable=invalid-name
360+
return self.__getattr__("GROUNDING", version="v1")
361+
362+
@property
363+
def GROUNDEDNESS(self) -> LazyLoadedPrebuiltMetric: # pylint: disable=invalid-name
364+
logger.warning(
365+
"RubricMetric.GROUNDEDNESS is a deprecated alias and now maps to"
366+
" RubricMetric.GROUNDING (grounding_v1). Note that the input"
367+
" contract changed: legacy GROUNDEDNESS scored 'response' against"
368+
" 'prompt'; grounding_v1 scores 'response' sentence-by-sentence"
369+
" against an additional 'context' field. Add a 'context' field to"
370+
" your dataset, otherwise scores will silently collapse to 0."
371+
" Update your code to use RubricMetric.GROUNDING directly."
372+
)
373+
return self.__getattr__("GROUNDING", version="v1")
357374

358375
@property
359376
def TOOL_USE_QUALITY(self) -> LazyLoadedPrebuiltMetric:
360-
return self.__getattr__("TOOL_USE_QUALITY")
377+
return self.__getattr__("TOOL_USE_QUALITY", version="v1")
361378

362379
@property
363380
def GECKO_TEXT2IMAGE(self) -> LazyLoadedPrebuiltMetric:
364-
return self.__getattr__("GECKO_TEXT2IMAGE")
381+
return self.__getattr__("GECKO_TEXT2IMAGE", version="v1")
365382

366383
@property
367384
def GECKO_TEXT2VIDEO(self) -> LazyLoadedPrebuiltMetric:
368-
return self.__getattr__("GECKO_TEXT2VIDEO")
385+
return self.__getattr__("GECKO_TEXT2VIDEO", version="v1")
369386

370387

371388
PrebuiltMetric = PrebuiltMetricLoader()

tests/unit/agentplatform/genai/test_evals.py

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def mock_api_client_fixture():
123123

124124
@pytest.fixture
125125
def mock_eval_dependencies(mock_api_client_fixture):
126+
_evals_metric_loaders.LazyLoadedPrebuiltMetric._cache.clear()
126127
# fmt: off
127128
with (
128129
mock.patch("google.cloud.storage.Client") as mock_storage_client,
@@ -1239,9 +1240,9 @@ def test_enrich_scenario_e2e_simulation(self):
12391240
api_response, eval_result
12401241
)
12411242
example = api_response.results[0].clusters[0].examples[0]
1242-
assert (
1243-
"scenario_preview" in example.evaluation_result
1244-
), f"scenario_preview not found. evaluation_result keys: {list(example.evaluation_result.keys())}"
1243+
assert "scenario_preview" in example.evaluation_result, (
1244+
f"scenario_preview not found. evaluation_result keys: {list(example.evaluation_result.keys())}"
1245+
)
12451246
assert (
12461247
"I need to book a flight" in example.evaluation_result["scenario_preview"]
12471248
)
@@ -1253,9 +1254,9 @@ def test_enrich_scenario_e2e_simulation(self):
12531254
json_str = json.dumps(result_dump)
12541255
parsed = json.loads(json_str)
12551256
ex_parsed = parsed["results"][0]["clusters"][0]["examples"][0]
1256-
assert "scenario_preview" in ex_parsed.get(
1257-
"evaluation_result", {}
1258-
), f"scenario_preview missing after serialization. Keys: {list(ex_parsed.get('evaluation_result', {}).keys())}"
1257+
assert "scenario_preview" in ex_parsed.get("evaluation_result", {}), (
1258+
f"scenario_preview missing after serialization. Keys: {list(ex_parsed.get('evaluation_result', {}).keys())}"
1259+
)
12591260
assert (
12601261
"I need to book a flight"
12611262
in ex_parsed["evaluation_result"]["scenario_preview"]
@@ -6355,6 +6356,80 @@ def test_metric_to_yaml_file_raises_importerror_if_yaml_is_none(self):
63556356
metric_obj.to_yaml_file("/fake/path/error.yaml")
63566357

63576358

6359+
class TestPrebuiltMetricLoaderGroundedness:
6360+
"""Unit tests for legacy RubricMetric.GROUNDEDNESS alias to grounding_v1."""
6361+
6362+
def test_grounding_resolves_to_grounding_v1(self):
6363+
lazy_metric = agentplatform_genai_types.RubricMetric.GROUNDING
6364+
assert lazy_metric.name == "GROUNDING"
6365+
assert lazy_metric._get_api_metric_spec_name() == "grounding_v1"
6366+
6367+
def test_groundedness_aliases_grounding_v1(self):
6368+
lazy_metric = agentplatform_genai_types.RubricMetric.GROUNDEDNESS
6369+
assert lazy_metric.name == "GROUNDING"
6370+
assert lazy_metric._get_api_metric_spec_name() == "grounding_v1"
6371+
6372+
def test_groundedness_logs_field_difference_warning(self, caplog):
6373+
loader_logger = (
6374+
"agentplatform._genai._evals_metric_loaders"
6375+
)
6376+
with caplog.at_level("WARNING", logger=loader_logger):
6377+
_ = agentplatform_genai_types.RubricMetric.GROUNDEDNESS
6378+
messages = [r.getMessage() for r in caplog.records if r.name == loader_logger]
6379+
assert any("GROUNDEDNESS" in m for m in messages)
6380+
assert any("grounding_v1" in m for m in messages)
6381+
assert any("context" in m for m in messages)
6382+
6383+
def test_groundedness_resolve_returns_grounding_v1_metric(self):
6384+
lazy_metric = agentplatform_genai_types.RubricMetric.GROUNDEDNESS
6385+
resolved = lazy_metric.resolve(api_client=mock.MagicMock())
6386+
assert isinstance(resolved, agentplatform_genai_types.Metric)
6387+
assert resolved.name == "grounding_v1"
6388+
6389+
6390+
class TestPrebuiltMetricLoaderVersionPinning:
6391+
"""Verifies explicit version pinning for all RubricMetric properties."""
6392+
6393+
@pytest.mark.parametrize(
6394+
"prop_name,expected_spec",
6395+
[
6396+
("GENERAL_QUALITY", "general_quality_v1"),
6397+
("TEXT_QUALITY", "text_quality_v1"),
6398+
("INSTRUCTION_FOLLOWING", "instruction_following_v1"),
6399+
("SAFETY", "safety_v1"),
6400+
("MULTI_TURN_GENERAL_QUALITY", "multi_turn_general_quality_v1"),
6401+
("MULTI_TURN_TEXT_QUALITY", "multi_turn_text_quality_v1"),
6402+
("FINAL_RESPONSE_REFERENCE_FREE", "final_response_reference_free_v1"),
6403+
("FINAL_RESPONSE_QUALITY", "final_response_quality_v1"),
6404+
("HALLUCINATION", "hallucination_v1"),
6405+
("TOOL_USE_QUALITY", "tool_use_quality_v1"),
6406+
("GECKO_TEXT2IMAGE", "gecko_text2image_v1"),
6407+
("GECKO_TEXT2VIDEO", "gecko_text2video_v1"),
6408+
],
6409+
)
6410+
def test_predefined_property_pins_to_v1(self, prop_name, expected_spec):
6411+
lazy_metric = getattr(agentplatform_genai_types.RubricMetric, prop_name)
6412+
assert lazy_metric.version == "v1"
6413+
assert lazy_metric._get_api_metric_spec_name() == expected_spec
6414+
6415+
@pytest.mark.parametrize(
6416+
"prop_name",
6417+
[
6418+
"COHERENCE",
6419+
"FLUENCY",
6420+
"VERBOSITY",
6421+
"SUMMARIZATION_QUALITY",
6422+
"QUESTION_ANSWERING_QUALITY",
6423+
"MULTI_TURN_CHAT_QUALITY",
6424+
"MULTI_TURN_SAFETY",
6425+
],
6426+
)
6427+
def test_gcs_backed_property_pins_to_v1(self, prop_name):
6428+
lazy_metric = getattr(agentplatform_genai_types.RubricMetric, prop_name)
6429+
assert lazy_metric.version == "v1"
6430+
assert lazy_metric._get_api_metric_spec_name() is None
6431+
6432+
63586433
class TestMergeResponseDatasets:
63596434
"""Unit tests for the merge_response_datasets_into_canonical_format function."""
63606435

@@ -8310,13 +8385,11 @@ def test_execute_evaluation_deduplicates_candidate_names(
83108385
candidate_name="gemini-pro",
83118386
)
83128387

8313-
mock_eval_dependencies["mock_evaluate_instances"].return_value = (
8314-
agentplatform_genai_types.EvaluateInstancesResponse(
8315-
exact_match_results=agentplatform_genai_types.ExactMatchResults(
8316-
exact_match_metric_values=[
8317-
genai_types.ExactMatchMetricValue(score=1.0)
8318-
]
8319-
)
8388+
mock_eval_dependencies[
8389+
"mock_evaluate_instances"
8390+
].return_value = agentplatform_genai_types.EvaluateInstancesResponse(
8391+
exact_match_results=agentplatform_genai_types.ExactMatchResults(
8392+
exact_match_metric_values=[genai_types.ExactMatchMetricValue(score=1.0)]
83208393
)
83218394
)
83228395

0 commit comments

Comments
 (0)