riffer 0.38.1 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +7 -0
- data/docs/11_EVALS.md +20 -11
- data/lib/riffer/evals/evaluator.rb +5 -4
- data/lib/riffer/evals/evaluator_runner.rb +3 -1
- data/lib/riffer/evals/judge.rb +2 -1
- data/lib/riffer/evals/result.rb +9 -3
- data/lib/riffer/evals/run_result.rb +21 -1
- data/lib/riffer/evals/scenario_result.rb +18 -3
- data/lib/riffer/version.rb +1 -1
- data/sig/generated/riffer/evals/evaluator.rbs +2 -2
- data/sig/generated/riffer/evals/result.rbs +6 -2
- data/sig/generated/riffer/evals/run_result.rbs +14 -0
- data/sig/generated/riffer/evals/scenario_result.rbs +12 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1bf92e3bef065a4c07aadb273a7f6770b711c0f15f268726133c841cb3fd510
|
|
4
|
+
data.tar.gz: d7361c029b98ca819932e2c3d2d53113710bb434ad60092ee0578179fa844c5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f503377743f6629a1423234afb158570dc33ee225c82c0bc46e28938a839fe25b5bdfad6f61da274a8bf81e1e8dd6c505ad826593c5eb269251d47667fe9d7c
|
|
7
|
+
data.tar.gz: eddabf85e5416c777c3a9a441ce99cd1cb902ec1c339fa7338890ae8cb7e78c5e78628e1b886aea44cdc6228b99299fa3983816e10075d8c28b59229e4c0b376
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.39.0](https://github.com/janeapp/riffer/compare/riffer/v0.38.1...riffer/v0.39.0) (2026-07-29)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **evals:** expose token usage on evaluation results ([#380](https://github.com/janeapp/riffer/issues/380)) ([32d576e](https://github.com/janeapp/riffer/commit/32d576e492809845c36f44ebf37de3af3b6785b0))
|
|
14
|
+
|
|
8
15
|
## [0.38.1](https://github.com/janeapp/riffer/compare/riffer/v0.38.0...riffer/v0.38.1) (2026-07-29)
|
|
9
16
|
|
|
10
17
|
|
data/docs/11_EVALS.md
CHANGED
|
@@ -108,9 +108,11 @@ Per-scenario `context` overrides the top-level value. Scenarios without their ow
|
|
|
108
108
|
The runner returns a `Riffer::Evals::RunResult`:
|
|
109
109
|
|
|
110
110
|
```ruby
|
|
111
|
-
result.scores
|
|
112
|
-
result.scenario_results
|
|
113
|
-
result.
|
|
111
|
+
result.scores # => { EvaluatorClass => avg_score } across all scenarios
|
|
112
|
+
result.scenario_results # => Array of ScenarioResult objects
|
|
113
|
+
result.token_usage # => TokenUsage the agent under test spent, summed across scenarios (nil if none reported)
|
|
114
|
+
result.evaluator_token_usage # => TokenUsage the judges spent, summed across scenarios (nil if no judge ran)
|
|
115
|
+
result.to_h # => Hash representation
|
|
114
116
|
```
|
|
115
117
|
|
|
116
118
|
### ScenarioResult
|
|
@@ -119,13 +121,15 @@ Each scenario produces a `Riffer::Evals::ScenarioResult`:
|
|
|
119
121
|
|
|
120
122
|
```ruby
|
|
121
123
|
scenario = result.scenario_results.first
|
|
122
|
-
scenario.input
|
|
123
|
-
scenario.output
|
|
124
|
-
scenario.ground_truth
|
|
125
|
-
scenario.scores
|
|
126
|
-
scenario.results
|
|
127
|
-
scenario.messages
|
|
128
|
-
scenario.
|
|
124
|
+
scenario.input # => "What is the capital of France?"
|
|
125
|
+
scenario.output # => "The capital of France is Paris."
|
|
126
|
+
scenario.ground_truth # => "Paris"
|
|
127
|
+
scenario.scores # => { EvaluatorClass => score } for this scenario
|
|
128
|
+
scenario.results # => Array of Result objects
|
|
129
|
+
scenario.messages # => Array of Message objects (system, user, assistant, tool)
|
|
130
|
+
scenario.token_usage # => TokenUsage the agent under test spent on this scenario (nil if not reported)
|
|
131
|
+
scenario.evaluator_token_usage # => TokenUsage the judges spent on this scenario (nil if no judge ran)
|
|
132
|
+
scenario.to_h # => Hash representation
|
|
129
133
|
```
|
|
130
134
|
|
|
131
135
|
### Result
|
|
@@ -138,8 +142,13 @@ r.evaluator # => AnswerRelevancyEvaluator
|
|
|
138
142
|
r.score # => 0.92
|
|
139
143
|
r.reason # => "The response directly addresses..."
|
|
140
144
|
r.higher_is_better # => true
|
|
145
|
+
r.token_usage # => TokenUsage for the judge call (nil for rule-based evaluators)
|
|
141
146
|
```
|
|
142
147
|
|
|
148
|
+
`token_usage` is a `Riffer::Providers::TokenUsage`, the same type the agent exposes on `Agent::Response`, so eval usage accumulates with the `+` operator just like agent usage. On a `Result` it's populated for LLM-as-judge evaluators and `nil` for rule-based ones that never call an LLM.
|
|
149
|
+
|
|
150
|
+
`ScenarioResult` and `RunResult` keep the two sources separate rather than exposing a single total: `token_usage` is what the agent under test spent generating the output, and `evaluator_token_usage` is what the judges spent scoring it. Add them yourself if you want a combined figure.
|
|
151
|
+
|
|
143
152
|
## Defining Custom Evaluators
|
|
144
153
|
|
|
145
154
|
Create evaluators by subclassing `Riffer::Evals::Evaluator`. The simplest approach uses the `instructions` DSL — the base class handles calling the judge automatically:
|
|
@@ -190,7 +199,7 @@ Instance methods:
|
|
|
190
199
|
|
|
191
200
|
- `evaluate(input:, output:, ground_truth:, messages:)` - Override for custom logic; default calls judge with `instructions`
|
|
192
201
|
- `judge` - Returns a Judge instance for LLM-as-judge calls
|
|
193
|
-
- `result(score:, reason:, metadata:)` - Helper to build Result objects
|
|
202
|
+
- `result(score:, reason:, metadata:, token_usage:)` - Helper to build Result objects
|
|
194
203
|
|
|
195
204
|
### Advanced: Custom Evaluate Override
|
|
196
205
|
|
|
@@ -66,7 +66,7 @@ class Riffer::Evals::Evaluator
|
|
|
66
66
|
ground_truth: ground_truth
|
|
67
67
|
)
|
|
68
68
|
|
|
69
|
-
result(score: evaluation[:score], reason: evaluation[:reason])
|
|
69
|
+
result(score: evaluation[:score], reason: evaluation[:reason], token_usage: evaluation[:token_usage])
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
private
|
|
@@ -105,14 +105,15 @@ class Riffer::Evals::Evaluator
|
|
|
105
105
|
|
|
106
106
|
# Builds a Result for this evaluator.
|
|
107
107
|
#--
|
|
108
|
-
#: (score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped]) -> Riffer::Evals::Result
|
|
109
|
-
def result(score:, reason: nil, metadata: {})
|
|
108
|
+
#: (score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?token_usage: Riffer::Providers::TokenUsage?) -> Riffer::Evals::Result
|
|
109
|
+
def result(score:, reason: nil, metadata: {}, token_usage: nil)
|
|
110
110
|
Riffer::Evals::Result.new(
|
|
111
111
|
evaluator: self.class,
|
|
112
112
|
score: score,
|
|
113
113
|
reason: reason,
|
|
114
114
|
metadata: metadata,
|
|
115
|
-
higher_is_better: self.class.higher_is_better
|
|
115
|
+
higher_is_better: self.class.higher_is_better,
|
|
116
|
+
token_usage: token_usage
|
|
116
117
|
)
|
|
117
118
|
end
|
|
118
119
|
end
|
|
@@ -62,6 +62,7 @@ module Riffer::Evals::EvaluatorRunner
|
|
|
62
62
|
response = agent.generate(input, context: resolved_context)
|
|
63
63
|
output = response.content
|
|
64
64
|
messages = response.messages
|
|
65
|
+
token_usage = response.token_usage
|
|
65
66
|
|
|
66
67
|
results = evaluators.map do |evaluator_class|
|
|
67
68
|
evaluator_class.new.evaluate(input: input, output: output, ground_truth: ground_truth, messages: messages)
|
|
@@ -72,7 +73,8 @@ module Riffer::Evals::EvaluatorRunner
|
|
|
72
73
|
output: output,
|
|
73
74
|
ground_truth: ground_truth,
|
|
74
75
|
results: results,
|
|
75
|
-
messages: messages
|
|
76
|
+
messages: messages,
|
|
77
|
+
token_usage: token_usage
|
|
76
78
|
)
|
|
77
79
|
end
|
|
78
80
|
end
|
data/lib/riffer/evals/judge.rb
CHANGED
data/lib/riffer/evals/result.rb
CHANGED
|
@@ -18,16 +18,21 @@ class Riffer::Evals::Result
|
|
|
18
18
|
# Whether higher scores are better for this evaluator.
|
|
19
19
|
attr_reader :higher_is_better #: bool
|
|
20
20
|
|
|
21
|
+
# Token usage for the judge call that produced this result, when the
|
|
22
|
+
# evaluator used an LLM. Nil for rule-based evaluators.
|
|
23
|
+
attr_reader :token_usage #: Riffer::Providers::TokenUsage?
|
|
24
|
+
|
|
21
25
|
# Raises Riffer::ArgumentError if +score+ is not between 0.0 and 1.0.
|
|
22
26
|
#--
|
|
23
|
-
#: (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
|
|
24
|
-
def initialize(evaluator:, score:, reason: nil, metadata: {}, higher_is_better: true)
|
|
27
|
+
#: (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool, ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
28
|
+
def initialize(evaluator:, score:, reason: nil, metadata: {}, higher_is_better: true, token_usage: nil)
|
|
25
29
|
@evaluator = evaluator
|
|
26
30
|
@score = score.to_f
|
|
27
31
|
validate_score!
|
|
28
32
|
@reason = reason
|
|
29
33
|
@metadata = metadata
|
|
30
34
|
@higher_is_better = higher_is_better
|
|
35
|
+
@token_usage = token_usage
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
# Returns a hash representation of the result.
|
|
@@ -40,7 +45,8 @@ class Riffer::Evals::Result
|
|
|
40
45
|
score: score,
|
|
41
46
|
reason: reason,
|
|
42
47
|
metadata: metadata,
|
|
43
|
-
higher_is_better: higher_is_better
|
|
48
|
+
higher_is_better: higher_is_better,
|
|
49
|
+
token_usage: token_usage&.to_h
|
|
44
50
|
}
|
|
45
51
|
end
|
|
46
52
|
|
|
@@ -35,6 +35,24 @@ class Riffer::Evals::RunResult
|
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# Returns the summed token usage the agents under test spent across every
|
|
39
|
+
# scenario, or nil when none reported usage.
|
|
40
|
+
#
|
|
41
|
+
#--
|
|
42
|
+
#: () -> Riffer::Providers::TokenUsage?
|
|
43
|
+
def token_usage
|
|
44
|
+
scenario_results.map(&:token_usage).compact.reduce(:+)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns the summed token usage across every scenario's LLM-as-judge
|
|
48
|
+
# evaluators, or nil when none reported usage.
|
|
49
|
+
#
|
|
50
|
+
#--
|
|
51
|
+
#: () -> Riffer::Providers::TokenUsage?
|
|
52
|
+
def evaluator_token_usage
|
|
53
|
+
scenario_results.map(&:evaluator_token_usage).compact.reduce(:+)
|
|
54
|
+
end
|
|
55
|
+
|
|
38
56
|
# Returns a hash representation of the run result.
|
|
39
57
|
#
|
|
40
58
|
#--
|
|
@@ -42,7 +60,9 @@ class Riffer::Evals::RunResult
|
|
|
42
60
|
def to_h
|
|
43
61
|
{
|
|
44
62
|
scores: scores.transform_keys(&:name),
|
|
45
|
-
scenario_results: scenario_results.map(&:to_h)
|
|
63
|
+
scenario_results: scenario_results.map(&:to_h),
|
|
64
|
+
token_usage: token_usage&.to_h,
|
|
65
|
+
evaluator_token_usage: evaluator_token_usage&.to_h
|
|
46
66
|
}
|
|
47
67
|
end
|
|
48
68
|
end
|
|
@@ -18,14 +18,18 @@ class Riffer::Evals::ScenarioResult
|
|
|
18
18
|
# The full message history from the agent conversation.
|
|
19
19
|
attr_reader :messages #: Array[Riffer::Messages::Base]
|
|
20
20
|
|
|
21
|
+
# Token usage the agent under test spent generating this scenario's output.
|
|
22
|
+
attr_reader :token_usage #: Riffer::Providers::TokenUsage?
|
|
23
|
+
|
|
21
24
|
#--
|
|
22
|
-
#: (input: String, output: String, ground_truth: String?, results: Array[Riffer::Evals::Result], ?messages: Array[Riffer::Messages::Base]) -> void
|
|
23
|
-
def initialize(input:, output:, ground_truth:, results:, messages: [])
|
|
25
|
+
#: (input: String, output: String, ground_truth: String?, results: Array[Riffer::Evals::Result], ?messages: Array[Riffer::Messages::Base], ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
26
|
+
def initialize(input:, output:, ground_truth:, results:, messages: [], token_usage: nil)
|
|
24
27
|
@input = input
|
|
25
28
|
@output = output
|
|
26
29
|
@ground_truth = ground_truth
|
|
27
30
|
@results = results
|
|
28
31
|
@messages = messages
|
|
32
|
+
@token_usage = token_usage
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
# Returns scores keyed by evaluator class.
|
|
@@ -39,6 +43,15 @@ class Riffer::Evals::ScenarioResult
|
|
|
39
43
|
end
|
|
40
44
|
end
|
|
41
45
|
|
|
46
|
+
# Returns the summed token usage across this scenario's LLM-as-judge
|
|
47
|
+
# evaluators, or nil when none reported usage.
|
|
48
|
+
#
|
|
49
|
+
#--
|
|
50
|
+
#: () -> Riffer::Providers::TokenUsage?
|
|
51
|
+
def evaluator_token_usage
|
|
52
|
+
results.map(&:token_usage).compact.reduce(:+)
|
|
53
|
+
end
|
|
54
|
+
|
|
42
55
|
# Returns a hash representation of the scenario result.
|
|
43
56
|
#
|
|
44
57
|
#--
|
|
@@ -50,7 +63,9 @@ class Riffer::Evals::ScenarioResult
|
|
|
50
63
|
ground_truth: ground_truth,
|
|
51
64
|
scores: scores.transform_keys(&:name),
|
|
52
65
|
results: results.map(&:to_h),
|
|
53
|
-
messages: messages.map(&:to_h)
|
|
66
|
+
messages: messages.map(&:to_h),
|
|
67
|
+
token_usage: token_usage&.to_h,
|
|
68
|
+
evaluator_token_usage: evaluator_token_usage&.to_h
|
|
54
69
|
}
|
|
55
70
|
end
|
|
56
71
|
end
|
data/lib/riffer/version.rb
CHANGED
|
@@ -57,6 +57,6 @@ class Riffer::Evals::Evaluator
|
|
|
57
57
|
|
|
58
58
|
# Builds a Result for this evaluator.
|
|
59
59
|
# --
|
|
60
|
-
# : (score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped]) -> Riffer::Evals::Result
|
|
61
|
-
def result: (score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped]) -> Riffer::Evals::Result
|
|
60
|
+
# : (score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?token_usage: Riffer::Providers::TokenUsage?) -> Riffer::Evals::Result
|
|
61
|
+
def result: (score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?token_usage: Riffer::Providers::TokenUsage?) -> Riffer::Evals::Result
|
|
62
62
|
end
|
|
@@ -17,10 +17,14 @@ class Riffer::Evals::Result
|
|
|
17
17
|
# Whether higher scores are better for this evaluator.
|
|
18
18
|
attr_reader higher_is_better: bool
|
|
19
19
|
|
|
20
|
+
# Token usage for the judge call that produced this result, when the
|
|
21
|
+
# evaluator used an LLM. Nil for rule-based evaluators.
|
|
22
|
+
attr_reader token_usage: Riffer::Providers::TokenUsage?
|
|
23
|
+
|
|
20
24
|
# Raises Riffer::ArgumentError if +score+ is not between 0.0 and 1.0.
|
|
21
25
|
# --
|
|
22
|
-
# : (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
|
|
23
|
-
def initialize: (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
|
|
26
|
+
# : (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool, ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
27
|
+
def initialize: (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool, ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
24
28
|
|
|
25
29
|
# Returns a hash representation of the result.
|
|
26
30
|
#
|
|
@@ -15,6 +15,20 @@ class Riffer::Evals::RunResult
|
|
|
15
15
|
# : () -> Hash[singleton(Riffer::Evals::Evaluator), Float]
|
|
16
16
|
def scores: () -> Hash[singleton(Riffer::Evals::Evaluator), Float]
|
|
17
17
|
|
|
18
|
+
# Returns the summed token usage the agents under test spent across every
|
|
19
|
+
# scenario, or nil when none reported usage.
|
|
20
|
+
#
|
|
21
|
+
# --
|
|
22
|
+
# : () -> Riffer::Providers::TokenUsage?
|
|
23
|
+
def token_usage: () -> Riffer::Providers::TokenUsage?
|
|
24
|
+
|
|
25
|
+
# Returns the summed token usage across every scenario's LLM-as-judge
|
|
26
|
+
# evaluators, or nil when none reported usage.
|
|
27
|
+
#
|
|
28
|
+
# --
|
|
29
|
+
# : () -> Riffer::Providers::TokenUsage?
|
|
30
|
+
def evaluator_token_usage: () -> Riffer::Providers::TokenUsage?
|
|
31
|
+
|
|
18
32
|
# Returns a hash representation of the run result.
|
|
19
33
|
#
|
|
20
34
|
# --
|
|
@@ -17,9 +17,12 @@ class Riffer::Evals::ScenarioResult
|
|
|
17
17
|
# The full message history from the agent conversation.
|
|
18
18
|
attr_reader messages: Array[Riffer::Messages::Base]
|
|
19
19
|
|
|
20
|
+
# Token usage the agent under test spent generating this scenario's output.
|
|
21
|
+
attr_reader token_usage: Riffer::Providers::TokenUsage?
|
|
22
|
+
|
|
20
23
|
# --
|
|
21
|
-
# : (input: String, output: String, ground_truth: String?, results: Array[Riffer::Evals::Result], ?messages: Array[Riffer::Messages::Base]) -> void
|
|
22
|
-
def initialize: (input: String, output: String, ground_truth: String?, results: Array[Riffer::Evals::Result], ?messages: Array[Riffer::Messages::Base]) -> void
|
|
24
|
+
# : (input: String, output: String, ground_truth: String?, results: Array[Riffer::Evals::Result], ?messages: Array[Riffer::Messages::Base], ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
25
|
+
def initialize: (input: String, output: String, ground_truth: String?, results: Array[Riffer::Evals::Result], ?messages: Array[Riffer::Messages::Base], ?token_usage: Riffer::Providers::TokenUsage?) -> void
|
|
23
26
|
|
|
24
27
|
# Returns scores keyed by evaluator class.
|
|
25
28
|
#
|
|
@@ -27,6 +30,13 @@ class Riffer::Evals::ScenarioResult
|
|
|
27
30
|
# : () -> Hash[singleton(Riffer::Evals::Evaluator), Float]
|
|
28
31
|
def scores: () -> Hash[singleton(Riffer::Evals::Evaluator), Float]
|
|
29
32
|
|
|
33
|
+
# Returns the summed token usage across this scenario's LLM-as-judge
|
|
34
|
+
# evaluators, or nil when none reported usage.
|
|
35
|
+
#
|
|
36
|
+
# --
|
|
37
|
+
# : () -> Riffer::Providers::TokenUsage?
|
|
38
|
+
def evaluator_token_usage: () -> Riffer::Providers::TokenUsage?
|
|
39
|
+
|
|
30
40
|
# Returns a hash representation of the scenario result.
|
|
31
41
|
#
|
|
32
42
|
# --
|