mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
fix: Allow string values for ToolTrajectoryCriterion.match_type
Adds a Pydantic field validator to ToolTrajectoryCriterion to automatically convert string inputs for the match_type field into the corresponding MatchType enum member Close #3711 Co-authored-by: George Weale <gweale@google.com> PiperOrigin-RevId: 852415560
This commit is contained in:
committed by
Copybara-Service
parent
e850e9c9ba
commit
93d6e4c888
@@ -24,6 +24,7 @@ from pydantic import alias_generators
|
||||
from pydantic import BaseModel
|
||||
from pydantic import ConfigDict
|
||||
from pydantic import Field
|
||||
from pydantic import field_validator
|
||||
from pydantic.json_schema import SkipJsonSchema
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
@@ -225,6 +226,17 @@ class ToolTrajectoryCriterion(BaseCriterion):
|
||||
),
|
||||
)
|
||||
|
||||
@field_validator("match_type", mode="before")
|
||||
@classmethod
|
||||
def _coerce_match_type(cls, value: object) -> object:
|
||||
if isinstance(value, cls.MatchType):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
normalized = value.strip().upper().replace("-", "_").replace(" ", "_")
|
||||
if normalized in cls.MatchType.__members__:
|
||||
return cls.MatchType[normalized]
|
||||
return value
|
||||
|
||||
|
||||
class LlmBackedUserSimulatorCriterion(LlmAsAJudgeCriterion):
|
||||
"""Criterion for LLM-backed User Simulator Evaluators."""
|
||||
|
||||
@@ -23,6 +23,7 @@ from google.adk.evaluation.eval_metrics import ToolTrajectoryCriterion
|
||||
from google.adk.evaluation.evaluator import EvalStatus
|
||||
from google.adk.evaluation.trajectory_evaluator import TrajectoryEvaluator
|
||||
from google.genai import types as genai_types
|
||||
from pydantic import ValidationError
|
||||
import pytest
|
||||
|
||||
_USER_CONTENT = genai_types.Content(
|
||||
@@ -30,6 +31,71 @@ _USER_CONTENT = genai_types.Content(
|
||||
)
|
||||
|
||||
|
||||
def test_tool_trajectory_criterion_accepts_string_match_type():
|
||||
criterion = ToolTrajectoryCriterion(threshold=0.5, match_type="in_order")
|
||||
assert criterion.match_type == ToolTrajectoryCriterion.MatchType.IN_ORDER
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("match_type", "expected"),
|
||||
[
|
||||
("exact", ToolTrajectoryCriterion.MatchType.EXACT),
|
||||
("EXACT", ToolTrajectoryCriterion.MatchType.EXACT),
|
||||
(" exact ", ToolTrajectoryCriterion.MatchType.EXACT),
|
||||
("in order", ToolTrajectoryCriterion.MatchType.IN_ORDER),
|
||||
("IN ORDER", ToolTrajectoryCriterion.MatchType.IN_ORDER),
|
||||
("In OrDeR", ToolTrajectoryCriterion.MatchType.IN_ORDER),
|
||||
("in-order", ToolTrajectoryCriterion.MatchType.IN_ORDER),
|
||||
("IN-ORDER", ToolTrajectoryCriterion.MatchType.IN_ORDER),
|
||||
("in_order", ToolTrajectoryCriterion.MatchType.IN_ORDER),
|
||||
("any order", ToolTrajectoryCriterion.MatchType.ANY_ORDER),
|
||||
("ANY ORDER", ToolTrajectoryCriterion.MatchType.ANY_ORDER),
|
||||
("any-order", ToolTrajectoryCriterion.MatchType.ANY_ORDER),
|
||||
("ANY-ORDER", ToolTrajectoryCriterion.MatchType.ANY_ORDER),
|
||||
("any_order", ToolTrajectoryCriterion.MatchType.ANY_ORDER),
|
||||
],
|
||||
)
|
||||
def test_tool_trajectory_criterion_normalizes_string_match_type(
|
||||
match_type: str, expected: ToolTrajectoryCriterion.MatchType
|
||||
):
|
||||
criterion = ToolTrajectoryCriterion(threshold=0.5, match_type=match_type)
|
||||
assert criterion.match_type == expected
|
||||
|
||||
|
||||
def test_tool_trajectory_criterion_rejects_unknown_string_match_type():
|
||||
with pytest.raises(ValidationError):
|
||||
ToolTrajectoryCriterion(threshold=0.5, match_type="random string")
|
||||
|
||||
|
||||
def test_trajectory_evaluator_accepts_string_match_type_from_eval_metric_dict():
|
||||
eval_metric = EvalMetric(
|
||||
threshold=0.5,
|
||||
metric_name=PrebuiltMetrics.TOOL_TRAJECTORY_AVG_SCORE.value,
|
||||
criterion={
|
||||
"threshold": 0.5,
|
||||
"match_type": "ANY_ORDER",
|
||||
},
|
||||
)
|
||||
evaluator = TrajectoryEvaluator(eval_metric=eval_metric)
|
||||
|
||||
tool_call1 = genai_types.FunctionCall(name="test_func1", args={})
|
||||
tool_call2 = genai_types.FunctionCall(name="test_func2", args={})
|
||||
|
||||
actual_invocation = Invocation(
|
||||
user_content=_USER_CONTENT,
|
||||
intermediate_data=IntermediateData(tool_uses=[tool_call1, tool_call2]),
|
||||
)
|
||||
expected_invocation = Invocation(
|
||||
user_content=_USER_CONTENT,
|
||||
intermediate_data=IntermediateData(tool_uses=[tool_call2, tool_call1]),
|
||||
)
|
||||
|
||||
result = evaluator.evaluate_invocations(
|
||||
[actual_invocation], [expected_invocation]
|
||||
)
|
||||
assert result.overall_score == 1.0
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def evaluator() -> TrajectoryEvaluator:
|
||||
"""Returns a TrajectoryEvaluator."""
|
||||
|
||||
Reference in New Issue
Block a user