Skip to content

Evaluators

Built-in evaluators for comparing expected vs actual tool calls.

evaluators

Built-in evaluators for comparing expected vs actual tool calls.

ExactEvaluator

ExactEvaluator(*, match_order: bool = False, ignore_extra_args: bool = False, ignore_extra_calls: bool = True)

Evaluates tool calls by exact name + arguments match.

Supports optional config for relaxed matching: - match_order: If True, tool calls must appear in the same order. - ignore_extra_args: If True, actual calls may contain extra arguments. - ignore_extra_calls: If True, extra actual calls don't cause failure.

Usage

evaluator = ExactEvaluator() result = evaluator.evaluate(expected=[...], actual=[...])

Source code in src/russo/evaluators/exact.py
def __init__(
    self,
    *,
    match_order: bool = False,
    ignore_extra_args: bool = False,
    ignore_extra_calls: bool = True,
) -> None:
    self.match_order = match_order
    self.ignore_extra_args = ignore_extra_args
    self.ignore_extra_calls = ignore_extra_calls

evaluate

evaluate(expected: list[ToolCall], actual: list[ToolCall]) -> EvalResult

Compare expected tool calls against actual ones.

Source code in src/russo/evaluators/exact.py
def evaluate(self, expected: list[ToolCall], actual: list[ToolCall]) -> EvalResult:
    """Compare expected tool calls against actual ones."""
    if not expected:
        return EvalResult(passed=True, expected=expected, actual=actual, matches=[])

    matches: list[ToolCallMatch] = []
    remaining_actual = list(actual)

    for i, exp in enumerate(expected):
        match = self._find_match(exp, remaining_actual, index=i if self.match_order else None)
        matches.append(match)
        if match.matched and match.actual in remaining_actual:
            remaining_actual.remove(match.actual)

    all_matched = all(m.matched for m in matches)

    extra_calls_ok = self.ignore_extra_calls or len(remaining_actual) == 0
    passed = all_matched and extra_calls_ok

    if not extra_calls_ok and all_matched:
        for leftover in remaining_actual:
            matches.append(
                ToolCallMatch(
                    expected=ToolCall(name="(none)", arguments={}),
                    actual=leftover,
                    matched=False,
                    details=f"Unexpected extra tool call: {leftover.name}",
                )
            )

    return EvalResult(passed=passed, expected=expected, actual=actual, matches=matches)