ec2b666284
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
526 lines
20 KiB
Python
526 lines
20 KiB
Python
# Copyright 2026 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Testings for the Trajectory Evaluator."""
|
|
|
|
from google.adk.evaluation.eval_case import IntermediateData
|
|
from google.adk.evaluation.eval_case import Invocation
|
|
from google.adk.evaluation.eval_case import InvocationEvent
|
|
from google.adk.evaluation.eval_case import InvocationEvents
|
|
from google.adk.evaluation.eval_metrics import EvalMetric
|
|
from google.adk.evaluation.eval_metrics import PrebuiltMetrics
|
|
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(
|
|
parts=[genai_types.Part(text="User input here.")]
|
|
)
|
|
|
|
|
|
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."""
|
|
return TrajectoryEvaluator(
|
|
eval_metric=EvalMetric(
|
|
threshold=0.5,
|
|
metric_name=PrebuiltMetrics.TOOL_TRAJECTORY_AVG_SCORE.value,
|
|
criterion=ToolTrajectoryCriterion(
|
|
threshold=0.5,
|
|
match_type=ToolTrajectoryCriterion.MatchType.EXACT,
|
|
),
|
|
)
|
|
)
|
|
|
|
|
|
def test_evaluate_invocations_equal_tool_calls(evaluator: TrajectoryEvaluator):
|
|
"""Tests evaluate_invocations with equal tool calls."""
|
|
tool_call = genai_types.FunctionCall(name="test_func", args={"arg1": "val1"})
|
|
intermediate_data = IntermediateData(tool_uses=[tool_call])
|
|
invocation = Invocation(
|
|
user_content=_USER_CONTENT, intermediate_data=intermediate_data
|
|
)
|
|
result = evaluator.evaluate_invocations([invocation], [invocation])
|
|
assert result.overall_score == 1.0
|
|
assert result.overall_eval_status == EvalStatus.PASSED
|
|
assert len(result.per_invocation_results) == 1
|
|
assert result.per_invocation_results[0].score == 1.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.PASSED
|
|
|
|
|
|
def test_evaluate_invocations_different_tool_call_names(
|
|
evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with different tool call names."""
|
|
tool_call1 = genai_types.FunctionCall(
|
|
name="test_func1", args={"arg1": "val1"}
|
|
)
|
|
tool_call2 = genai_types.FunctionCall(
|
|
name="test_func2", args={"arg1": "val1"}
|
|
)
|
|
invocation1 = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call1]),
|
|
)
|
|
invocation2 = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call2]),
|
|
)
|
|
result = evaluator.evaluate_invocations([invocation1], [invocation2])
|
|
assert result.overall_score == 0.0
|
|
assert result.overall_eval_status == EvalStatus.FAILED
|
|
assert result.per_invocation_results[0].score == 0.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.FAILED
|
|
|
|
|
|
def test_evaluate_invocations_different_tool_call_args(
|
|
evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with different tool call args."""
|
|
tool_call1 = genai_types.FunctionCall(name="test_func", args={"arg1": "val1"})
|
|
tool_call2 = genai_types.FunctionCall(name="test_func", args={"arg1": "val2"})
|
|
invocation1 = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call1]),
|
|
)
|
|
invocation2 = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call2]),
|
|
)
|
|
result = evaluator.evaluate_invocations([invocation1], [invocation2])
|
|
assert result.overall_score == 0.0
|
|
assert result.overall_eval_status == EvalStatus.FAILED
|
|
assert result.per_invocation_results[0].score == 0.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.FAILED
|
|
|
|
|
|
def test_evaluate_invocations_different_number_of_tool_calls(
|
|
evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with different number of tool calls."""
|
|
tool_call1 = genai_types.FunctionCall(name="test_func", args={"arg1": "val1"})
|
|
tool_call2 = genai_types.FunctionCall(name="test_func", args={"arg1": "val1"})
|
|
invocation1 = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call1]),
|
|
)
|
|
invocation2 = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call1, tool_call2]),
|
|
)
|
|
result = evaluator.evaluate_invocations([invocation1], [invocation2])
|
|
assert result.overall_score == 0.0
|
|
assert result.overall_eval_status == EvalStatus.FAILED
|
|
assert result.per_invocation_results[0].score == 0.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.FAILED
|
|
|
|
|
|
def test_evaluate_invocations_no_tool_calls(evaluator: TrajectoryEvaluator):
|
|
"""Tests evaluate_invocations with no tool calls."""
|
|
invocation = Invocation(
|
|
user_content=_USER_CONTENT, intermediate_data=IntermediateData()
|
|
)
|
|
result = evaluator.evaluate_invocations([invocation], [invocation])
|
|
assert result.overall_score == 1.0
|
|
assert result.overall_eval_status == EvalStatus.PASSED
|
|
assert result.per_invocation_results[0].score == 1.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.PASSED
|
|
|
|
|
|
def test_evaluate_invocations_multiple_invocations(
|
|
evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with multiple invocations."""
|
|
tool_call1 = genai_types.FunctionCall(
|
|
name="test_func1", args={"arg1": "val1"}
|
|
)
|
|
tool_call2 = genai_types.FunctionCall(
|
|
name="test_func2", args={"arg1": "val1"}
|
|
)
|
|
inv1_actual = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call1]),
|
|
)
|
|
inv1_expected = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call1]),
|
|
)
|
|
inv2_actual = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call1]),
|
|
)
|
|
inv2_expected = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[tool_call2]),
|
|
)
|
|
result = evaluator.evaluate_invocations(
|
|
[inv1_actual, inv2_actual], [inv1_expected, inv2_expected]
|
|
)
|
|
assert result.overall_score == 0.5
|
|
assert result.overall_eval_status == EvalStatus.PASSED
|
|
assert len(result.per_invocation_results) == 2
|
|
assert result.per_invocation_results[0].score == 1.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.PASSED
|
|
assert result.per_invocation_results[1].score == 0.0
|
|
assert result.per_invocation_results[1].eval_status == EvalStatus.FAILED
|
|
|
|
|
|
@pytest.fixture
|
|
def in_order_evaluator() -> TrajectoryEvaluator:
|
|
"""Returns a TrajectoryEvaluator for IN_ORDER match."""
|
|
return TrajectoryEvaluator(
|
|
eval_metric=EvalMetric(
|
|
threshold=0.5,
|
|
metric_name=PrebuiltMetrics.TOOL_TRAJECTORY_AVG_SCORE.value,
|
|
criterion=ToolTrajectoryCriterion(
|
|
threshold=0.5,
|
|
match_type=ToolTrajectoryCriterion.MatchType.IN_ORDER,
|
|
),
|
|
)
|
|
)
|
|
|
|
|
|
def test_evaluate_invocations_in_order_match_with_extra_tool_calls(
|
|
in_order_evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with IN_ORDER match type and extra tool calls."""
|
|
t1 = genai_types.FunctionCall(name="t1", args={})
|
|
t1_1 = genai_types.FunctionCall(name="t1_1", args={})
|
|
t2 = genai_types.FunctionCall(name="t2", args={})
|
|
t2_1 = genai_types.FunctionCall(name="t2_1", args={})
|
|
t3 = genai_types.FunctionCall(name="t3", args={})
|
|
t3_1 = genai_types.FunctionCall(name="t3_1", args={})
|
|
actual_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(
|
|
tool_uses=[t1, t1_1, t2, t2_1, t3, t3_1]
|
|
),
|
|
)
|
|
expected_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t3]),
|
|
)
|
|
result = in_order_evaluator.evaluate_invocations(
|
|
[actual_invocation], [expected_invocation]
|
|
)
|
|
assert result.overall_score == 1.0
|
|
assert result.overall_eval_status == EvalStatus.PASSED
|
|
assert result.per_invocation_results[0].score == 1.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.PASSED
|
|
|
|
|
|
def test_evaluate_invocations_in_order_match_fails_with_missing_tool_call(
|
|
in_order_evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with IN_ORDER match type and missing tool call."""
|
|
t1 = genai_types.FunctionCall(name="t1", args={})
|
|
t1_1 = genai_types.FunctionCall(name="t1_1", args={})
|
|
t2 = genai_types.FunctionCall(name="t2", args={})
|
|
t2_1 = genai_types.FunctionCall(name="t2_1", args={})
|
|
t3_1 = genai_types.FunctionCall(name="t3_1", args={})
|
|
t4 = genai_types.FunctionCall(name="t4", args={})
|
|
actual_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t1_1, t2, t2_1, t3_1]),
|
|
)
|
|
expected_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t4]),
|
|
)
|
|
result = in_order_evaluator.evaluate_invocations(
|
|
[actual_invocation], [expected_invocation]
|
|
)
|
|
assert result.overall_score == 0.0
|
|
assert result.overall_eval_status == EvalStatus.FAILED
|
|
assert result.per_invocation_results[0].score == 0.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.FAILED
|
|
|
|
|
|
def test_evaluate_invocations_in_order_match_fails_with_wrong_order(
|
|
in_order_evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with IN_ORDER match type and wrong order."""
|
|
t1 = genai_types.FunctionCall(name="t1", args={})
|
|
t2 = genai_types.FunctionCall(name="t2", args={})
|
|
t3 = genai_types.FunctionCall(name="t3", args={})
|
|
actual_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t3, t2]),
|
|
)
|
|
expected_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t3]),
|
|
)
|
|
result = in_order_evaluator.evaluate_invocations(
|
|
[actual_invocation], [expected_invocation]
|
|
)
|
|
assert result.overall_score == 0.0
|
|
assert result.overall_eval_status == EvalStatus.FAILED
|
|
assert result.per_invocation_results[0].score == 0.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.FAILED
|
|
|
|
|
|
@pytest.fixture
|
|
def any_order_evaluator() -> TrajectoryEvaluator:
|
|
"""Returns a TrajectoryEvaluator for ANY_ORDER match."""
|
|
return TrajectoryEvaluator(
|
|
eval_metric=EvalMetric(
|
|
threshold=0.5,
|
|
metric_name=PrebuiltMetrics.TOOL_TRAJECTORY_AVG_SCORE.value,
|
|
criterion=ToolTrajectoryCriterion(
|
|
threshold=0.5,
|
|
match_type=ToolTrajectoryCriterion.MatchType.ANY_ORDER,
|
|
),
|
|
)
|
|
)
|
|
|
|
|
|
def test_evaluate_invocations_any_order_match_with_extra_tool_calls_different_order(
|
|
any_order_evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with ANY_ORDER match type and extra tool calls."""
|
|
t1 = genai_types.FunctionCall(name="t1", args={})
|
|
t1_1 = genai_types.FunctionCall(name="t1_1", args={})
|
|
t2 = genai_types.FunctionCall(name="t2", args={})
|
|
t2_1 = genai_types.FunctionCall(name="t2_1", args={})
|
|
t3 = genai_types.FunctionCall(name="t3", args={})
|
|
t3_1 = genai_types.FunctionCall(name="t3_1", args={})
|
|
actual_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(
|
|
tool_uses=[t2, t2_1, t1, t1_1, t3, t3_1]
|
|
),
|
|
)
|
|
expected_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t3]),
|
|
)
|
|
result = any_order_evaluator.evaluate_invocations(
|
|
[actual_invocation], [expected_invocation]
|
|
)
|
|
assert result.overall_score == 1.0
|
|
assert result.overall_eval_status == EvalStatus.PASSED
|
|
assert result.per_invocation_results[0].score == 1.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.PASSED
|
|
|
|
|
|
def test_evaluate_invocations_any_order_match_fails_with_missing_tool_call(
|
|
any_order_evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with ANY_ORDER match type and missing tool call."""
|
|
t1 = genai_types.FunctionCall(name="t1", args={})
|
|
t1_1 = genai_types.FunctionCall(name="t1_1", args={})
|
|
t2 = genai_types.FunctionCall(name="t2", args={})
|
|
t2_1 = genai_types.FunctionCall(name="t2_1", args={})
|
|
t3_1 = genai_types.FunctionCall(name="t3_1", args={})
|
|
t4 = genai_types.FunctionCall(name="t4", args={})
|
|
actual_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t1_1, t2, t2_1, t3_1]),
|
|
)
|
|
expected_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t4]),
|
|
)
|
|
result = any_order_evaluator.evaluate_invocations(
|
|
[actual_invocation], [expected_invocation]
|
|
)
|
|
assert result.overall_score == 0.0
|
|
assert result.overall_eval_status == EvalStatus.FAILED
|
|
assert result.per_invocation_results[0].score == 0.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.FAILED
|
|
|
|
|
|
def test_evaluate_invocations_any_order_match_with_duplicates(
|
|
any_order_evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with ANY_ORDER match type with duplicates."""
|
|
t1 = genai_types.FunctionCall(name="t1", args={})
|
|
t2 = genai_types.FunctionCall(name="t2", args={})
|
|
t3 = genai_types.FunctionCall(name="t3", args={})
|
|
actual_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t3, t1]),
|
|
)
|
|
expected_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t1]),
|
|
)
|
|
result = any_order_evaluator.evaluate_invocations(
|
|
[actual_invocation], [expected_invocation]
|
|
)
|
|
assert result.overall_score == 1.0
|
|
assert result.overall_eval_status == EvalStatus.PASSED
|
|
assert result.per_invocation_results[0].score == 1.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.PASSED
|
|
|
|
|
|
def test_evaluate_invocations_any_order_match_fails_with_duplicates_missing(
|
|
any_order_evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""Tests evaluate_invocations with ANY_ORDER match type with missing duplicates."""
|
|
t1 = genai_types.FunctionCall(name="t1", args={})
|
|
t2 = genai_types.FunctionCall(name="t2", args={})
|
|
t3 = genai_types.FunctionCall(name="t3", args={})
|
|
actual_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t3]),
|
|
)
|
|
expected_invocation = Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=IntermediateData(tool_uses=[t1, t2, t1]),
|
|
)
|
|
result = any_order_evaluator.evaluate_invocations(
|
|
[actual_invocation], [expected_invocation]
|
|
)
|
|
assert result.overall_score == 0.0
|
|
assert result.overall_eval_status == EvalStatus.FAILED
|
|
assert result.per_invocation_results[0].score == 0.0
|
|
assert result.per_invocation_results[0].eval_status == EvalStatus.FAILED
|
|
|
|
|
|
def test_evaluate_invocations_no_invocations(evaluator: TrajectoryEvaluator):
|
|
"""Tests evaluate_invocations with no invocations."""
|
|
result = evaluator.evaluate_invocations([], [])
|
|
assert result.overall_score is None
|
|
assert result.overall_eval_status == EvalStatus.NOT_EVALUATED
|
|
assert not result.per_invocation_results
|
|
|
|
|
|
def _make_invocation_events(
|
|
*tool_calls: genai_types.FunctionCall,
|
|
) -> Invocation:
|
|
"""Returns an Invocation using InvocationEvents intermediate_data format."""
|
|
return Invocation(
|
|
user_content=_USER_CONTENT,
|
|
intermediate_data=InvocationEvents(
|
|
invocation_events=[
|
|
InvocationEvent(
|
|
author="agent",
|
|
content=genai_types.Content(
|
|
parts=[genai_types.Part(function_call=tc)]
|
|
),
|
|
)
|
|
for tc in tool_calls
|
|
]
|
|
),
|
|
)
|
|
|
|
|
|
def test_evaluate_invocations_invocation_events_format_exact_match(
|
|
evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""InvocationEvents intermediate_data format should score 1.0 on exact match.
|
|
|
|
Regression test for #5410: tool_trajectory_avg_score returned 0.0 even when
|
|
tool name and args were identical because function-call events with
|
|
skip_summarization=True were incorrectly excluded from invocation_events.
|
|
"""
|
|
tool_call = genai_types.FunctionCall(
|
|
id="toolu_01", name="execute_sql", args={"query": "SELECT 1"}
|
|
)
|
|
expected_tool_call = genai_types.FunctionCall(
|
|
name="execute_sql", args={"query": "SELECT 1"}
|
|
)
|
|
actual = _make_invocation_events(tool_call)
|
|
expected = _make_invocation_events(expected_tool_call)
|
|
|
|
result = evaluator.evaluate_invocations([actual], [expected])
|
|
assert result.overall_score == 1.0
|
|
assert result.overall_eval_status == EvalStatus.PASSED
|
|
|
|
|
|
def test_evaluate_invocations_invocation_events_format_mismatch(
|
|
evaluator: TrajectoryEvaluator,
|
|
):
|
|
"""InvocationEvents format should score 0.0 when tool calls differ."""
|
|
actual = _make_invocation_events(
|
|
genai_types.FunctionCall(name="tool_a", args={"x": "1"})
|
|
)
|
|
expected = _make_invocation_events(
|
|
genai_types.FunctionCall(name="tool_b", args={"x": "1"})
|
|
)
|
|
|
|
result = evaluator.evaluate_invocations([actual], [expected])
|
|
assert result.overall_score == 0.0
|
|
assert result.overall_eval_status == EvalStatus.FAILED
|