Module: Raif::Evals::EvalSets::Matchers
- Included in:
- Raif::Evals::EvalSet
- Defined in:
- lib/raif/evals/eval_sets/matchers.rb
Overview
Matchers for evals with a known-correct answer, typically an EvalCase's expected.
Each one wraps #expect. What they add over a hand-written block is normalization applied
the same way in every eval set, and metadata: a hand-written include? records only that
it failed, where these record what was produced and what was wanted.
Constant Summary collapse
- MAX_METADATA_LENGTH =
Both sides of a comparison land in the results JSON for every case of every repeat, and an untruncated response is most of the file.
500
Instance Method Summary collapse
-
#expect_exact_match(actual, expected, ignore_case: true, strip: true, label: nil, result_metadata: {}) ⇒ ExpectationResult
Compares a value against ground truth, ignoring the differences a case or whitespace change makes.
-
#expect_includes(actual, expected, ignore_case: true, label: nil, result_metadata: {}) ⇒ ExpectationResult
Asserts that text appears in a value.
-
#expect_matches(actual, pattern, label: nil, result_metadata: {}) ⇒ ExpectationResult
Asserts that a value matches a pattern.
-
#expect_within(actual, expected, delta: nil, percent: nil, label: nil, result_metadata: {}) ⇒ ExpectationResult
Asserts that a number is close enough to ground truth.
Instance Method Details
#expect_exact_match(actual, expected, ignore_case: true, strip: true, label: nil, result_metadata: {}) ⇒ ExpectationResult
Compares a value against ground truth, ignoring the differences a case or whitespace change makes. Strings are normalized and compared; anything else is compared with ==, so a boolean or numeric answer is not coerced through to_s first.
35 36 37 38 39 40 41 42 43 44 |
# File 'lib/raif/evals/eval_sets/matchers.rb', line 35 def expect_exact_match(actual, expected, ignore_case: true, strip: true, label: nil, result_metadata: {}) normalized_actual = normalize_matcher_string(actual, ignore_case: ignore_case, strip: strip) normalized_expected = normalize_matcher_string(expected, ignore_case: ignore_case, strip: strip) = .merge((actual, expected)) expect label || "exact match", result_metadata: do normalized_actual == normalized_expected end end |
#expect_includes(actual, expected, ignore_case: true, label: nil, result_metadata: {}) ⇒ ExpectationResult
Asserts that text appears in a value. An Array of expected texts requires all of them, which is the common shape of "the summary has to mention each of these".
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/raif/evals/eval_sets/matchers.rb', line 59 def expect_includes(actual, expected, ignore_case: true, label: nil, result_metadata: {}) haystack = actual.to_s haystack = haystack.downcase if ignore_case needles = Array(expected).map(&:to_s) # Recorded rather than merely counted: which of five keywords went missing is what # makes the failure debuggable. missing = needles.reject do |needle| haystack.include?(ignore_case ? needle.downcase : needle) end = .merge((actual, expected)).merge(missing: missing) expect label || "includes expected text", result_metadata: do needles.any? && missing.empty? end end |
#expect_matches(actual, pattern, label: nil, result_metadata: {}) ⇒ ExpectationResult
Asserts that a value matches a pattern. A String pattern is compiled, so a dataset row can carry one.
89 90 91 92 93 94 95 96 |
# File 'lib/raif/evals/eval_sets/matchers.rb', line 89 def expect_matches(actual, pattern, label: nil, result_metadata: {}) regexp = pattern.is_a?(Regexp) ? pattern : Regexp.new(pattern.to_s) = .merge((actual, regexp.source)).merge(pattern: regexp.inspect) expect label || "matches expected pattern", result_metadata: do regexp.match?(actual.to_s) end end |
#expect_within(actual, expected, delta: nil, percent: nil, label: nil, result_metadata: {}) ⇒ ExpectationResult
Asserts that a number is close enough to ground truth. Mirrors RSpec's
be_within(delta).of(expected), with percent: as the relative alternative.
A non-numeric actual fails rather than raises: a model that answered "about forty" when the eval asked for a number produced a wrong answer, not a broken eval. A non-numeric expected raises, because only the eval's author can have put it there.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/raif/evals/eval_sets/matchers.rb', line 119 def expect_within(actual, expected, delta: nil, percent: nil, label: nil, result_metadata: {}) if delta.nil? == percent.nil? raise ArgumentError, "expect_within needs exactly one of delta: and percent:, and was given " \ "#{delta.nil? ? "neither" : "both"}." end unless expected.is_a?(Numeric) raise ArgumentError, "expect_within was given #{expected.inspect} as the expected value; it must be numeric." end # A percentage of zero is zero, so a zero expected admits only an exact zero. tolerance = delta || (expected.abs * percent.to_f / 100.0) difference = (actual - expected).abs if actual.is_a?(Numeric) = .merge((actual, expected)) .merge({ tolerance: tolerance.to_f, difference: difference&.to_f }.compact) description = delta ? "within #{delta} of expected" : "within #{percent}% of expected" expect label || description, result_metadata: do !difference.nil? && difference <= tolerance end end |