riffer 0.38.0 → 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 +14 -0
- data/docs/11_EVALS.md +20 -11
- data/lib/riffer/agent/run.rb +7 -3
- 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/providers/amazon_bedrock.rb +9 -4
- data/lib/riffer/providers/anthropic.rb +13 -8
- 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 +9 -9
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,20 @@ 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
|
+
|
|
15
|
+
## [0.38.1](https://github.com/janeapp/riffer/compare/riffer/v0.38.0...riffer/v0.38.1) (2026-07-29)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Performance Improvements
|
|
19
|
+
|
|
20
|
+
* avoid O(n^2) text accumulation in streaming providers ([#378](https://github.com/janeapp/riffer/issues/378)) ([d344fa7](https://github.com/janeapp/riffer/commit/d344fa7122492d14ee1c91c0be3a88501170e255))
|
|
21
|
+
|
|
8
22
|
## [0.38.0](https://github.com/janeapp/riffer/compare/riffer/v0.37.1...riffer/v0.38.0) (2026-07-21)
|
|
9
23
|
|
|
10
24
|
|
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
|
|
data/lib/riffer/agent/run.rb
CHANGED
|
@@ -121,7 +121,7 @@ module Riffer::Agent::Run
|
|
|
121
121
|
#--
|
|
122
122
|
#: (Riffer::Agent, Enumerator::Yielder, ?Hash[String, String]) -> Riffer::Messages::Assistant
|
|
123
123
|
def accumulate_streamed_response(agent, stream_yielder, tags = {})
|
|
124
|
-
accumulated_content = ""
|
|
124
|
+
accumulated_content = +""
|
|
125
125
|
accumulated_tool_calls = [] #: Array[Riffer::Messages::Assistant::ToolCall]
|
|
126
126
|
accumulated_token_usage = nil #: Riffer::Providers::TokenUsage?
|
|
127
127
|
accumulated_finish_reason = nil #: Symbol?
|
|
@@ -131,9 +131,13 @@ module Riffer::Agent::Run
|
|
|
131
131
|
|
|
132
132
|
case event
|
|
133
133
|
when Riffer::StreamEvents::TextDelta
|
|
134
|
-
|
|
134
|
+
# Append in place rather than += (which reallocates and copies the whole
|
|
135
|
+
# buffer per delta, O(n^2) over a stream). accumulated_content stays an
|
|
136
|
+
# owned buffer; replace (not =) on TextDone keeps it that way so a later
|
|
137
|
+
# delta's << can never mutate the string held by a TextDone event.
|
|
138
|
+
accumulated_content << event.content
|
|
135
139
|
when Riffer::StreamEvents::TextDone
|
|
136
|
-
accumulated_content
|
|
140
|
+
accumulated_content.replace(event.content)
|
|
137
141
|
when Riffer::StreamEvents::ToolCallDone
|
|
138
142
|
accumulated_tool_calls << Riffer::Messages::Assistant::ToolCall.new(
|
|
139
143
|
call_id: event.call_id,
|
|
@@ -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
|
|
@@ -267,7 +267,7 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
267
267
|
state[:tool_call] = {
|
|
268
268
|
id: typed_event.start.tool_use.tool_use_id,
|
|
269
269
|
name: decode_tool_name(typed_event.start.tool_use.name, tools: @current_tools),
|
|
270
|
-
arguments: ""
|
|
270
|
+
arguments: +""
|
|
271
271
|
}
|
|
272
272
|
end
|
|
273
273
|
|
|
@@ -276,8 +276,13 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
276
276
|
def handle_content_block_delta_text_delta(event, state:, yielder:)
|
|
277
277
|
typed_event = event #: Aws::BedrockRuntime::Types::ContentBlockDeltaEvent
|
|
278
278
|
delta_text = typed_event.delta.text
|
|
279
|
-
|
|
280
|
-
state[:text]
|
|
279
|
+
# Mutating append: += would reallocate and copy the whole accumulated
|
|
280
|
+
# buffer on every delta (O(n^2) per content block). state[:text] is handed
|
|
281
|
+
# off to TextDone and then cleared on block stop, so nothing reads the
|
|
282
|
+
# pre-append string, making in-place mutation safe. Seed with an unfrozen
|
|
283
|
+
# String (+"") so << does not raise under frozen_string_literal.
|
|
284
|
+
state[:text] ||= +""
|
|
285
|
+
state[:text] << delta_text
|
|
281
286
|
yielder << Riffer::StreamEvents::TextDelta.new(delta_text)
|
|
282
287
|
end
|
|
283
288
|
|
|
@@ -287,7 +292,7 @@ class Riffer::Providers::AmazonBedrock < Riffer::Providers::Base
|
|
|
287
292
|
typed_event = event #: Aws::BedrockRuntime::Types::ContentBlockDeltaEvent
|
|
288
293
|
input_delta = typed_event.delta.tool_use.input
|
|
289
294
|
|
|
290
|
-
state[:tool_call][:arguments]
|
|
295
|
+
state[:tool_call][:arguments] << input_delta
|
|
291
296
|
|
|
292
297
|
yielder << Riffer::StreamEvents::ToolCallDelta.new(
|
|
293
298
|
item_id: state[:tool_call][:id],
|
|
@@ -233,7 +233,7 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
233
233
|
content_block = event.content_block
|
|
234
234
|
if content_block.type.to_s == "server_tool_use" && content_block.name.to_s == "web_search"
|
|
235
235
|
state[:web_search_index] = event.index
|
|
236
|
-
state[:web_search_json] = ""
|
|
236
|
+
state[:web_search_json] = +""
|
|
237
237
|
end
|
|
238
238
|
end
|
|
239
239
|
|
|
@@ -243,22 +243,27 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
243
243
|
return unless state[:web_search_index] == event.index
|
|
244
244
|
|
|
245
245
|
delta = event.delta
|
|
246
|
-
state[:web_search_json]
|
|
246
|
+
state[:web_search_json] << delta.partial_json if delta.respond_to?(:partial_json)
|
|
247
247
|
end
|
|
248
248
|
|
|
249
249
|
#--
|
|
250
250
|
#: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
251
251
|
def handle_text_event(event, state:, yielder:)
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
# Mutating append instead of += to avoid reallocating and copying the whole
|
|
253
|
+
# accumulated buffer on every delta (O(n^2) per content block). The buffer
|
|
254
|
+
# is handed to TextDone and cleared on block stop, so no reader observes the
|
|
255
|
+
# pre-append string. Seed with an unfrozen String so << is legal under
|
|
256
|
+
# frozen_string_literal.
|
|
257
|
+
state[:text] ||= +""
|
|
258
|
+
state[:text] << event.text
|
|
254
259
|
yielder << Riffer::StreamEvents::TextDelta.new(event.text)
|
|
255
260
|
end
|
|
256
261
|
|
|
257
262
|
#--
|
|
258
263
|
#: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
259
264
|
def handle_thinking_event(event, state:, yielder:)
|
|
260
|
-
state[:reasoning] ||= ""
|
|
261
|
-
state[:reasoning]
|
|
265
|
+
state[:reasoning] ||= +""
|
|
266
|
+
state[:reasoning] << event.thinking
|
|
262
267
|
yielder << Riffer::StreamEvents::ReasoningDelta.new(event.thinking)
|
|
263
268
|
end
|
|
264
269
|
|
|
@@ -266,9 +271,9 @@ class Riffer::Providers::Anthropic < Riffer::Providers::Base
|
|
|
266
271
|
#: (untyped, state: Hash[Symbol, untyped], yielder: Riffer::Providers::_EventSink) -> void
|
|
267
272
|
def handle_input_json_event(event, state:, yielder:)
|
|
268
273
|
if state[:tool_call].nil?
|
|
269
|
-
state[:tool_call] = {id: nil, name: nil, arguments: ""}
|
|
274
|
+
state[:tool_call] = {id: nil, name: nil, arguments: +""}
|
|
270
275
|
end
|
|
271
|
-
state[:tool_call][:arguments]
|
|
276
|
+
state[:tool_call][:arguments] << event.partial_json
|
|
272
277
|
yielder << Riffer::StreamEvents::ToolCallDelta.new(
|
|
273
278
|
item_id: state[:tool_call][:id] || "pending",
|
|
274
279
|
name: state[:tool_call][:name],
|
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
|
# --
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: riffer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.39.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jake Bottrall
|
|
@@ -35,14 +35,14 @@ dependencies:
|
|
|
35
35
|
requirements:
|
|
36
36
|
- - "~>"
|
|
37
37
|
- !ruby/object:Gem::Version
|
|
38
|
-
version: 1.
|
|
38
|
+
version: 1.59.0
|
|
39
39
|
type: :development
|
|
40
40
|
prerelease: false
|
|
41
41
|
version_requirements: !ruby/object:Gem::Requirement
|
|
42
42
|
requirements:
|
|
43
43
|
- - "~>"
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: 1.
|
|
45
|
+
version: 1.59.0
|
|
46
46
|
- !ruby/object:Gem::Dependency
|
|
47
47
|
name: aws-sdk-bedrockruntime
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -77,28 +77,28 @@ dependencies:
|
|
|
77
77
|
requirements:
|
|
78
78
|
- - "~>"
|
|
79
79
|
- !ruby/object:Gem::Version
|
|
80
|
-
version: '0
|
|
80
|
+
version: '1.0'
|
|
81
81
|
type: :development
|
|
82
82
|
prerelease: false
|
|
83
83
|
version_requirements: !ruby/object:Gem::Requirement
|
|
84
84
|
requirements:
|
|
85
85
|
- - "~>"
|
|
86
86
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: '0
|
|
87
|
+
version: '1.0'
|
|
88
88
|
- !ruby/object:Gem::Dependency
|
|
89
89
|
name: openai
|
|
90
90
|
requirement: !ruby/object:Gem::Requirement
|
|
91
91
|
requirements:
|
|
92
92
|
- - "~>"
|
|
93
93
|
- !ruby/object:Gem::Version
|
|
94
|
-
version: 0.
|
|
94
|
+
version: 0.72.0
|
|
95
95
|
type: :development
|
|
96
96
|
prerelease: false
|
|
97
97
|
version_requirements: !ruby/object:Gem::Requirement
|
|
98
98
|
requirements:
|
|
99
99
|
- - "~>"
|
|
100
100
|
- !ruby/object:Gem::Version
|
|
101
|
-
version: 0.
|
|
101
|
+
version: 0.72.0
|
|
102
102
|
- !ruby/object:Gem::Dependency
|
|
103
103
|
name: async
|
|
104
104
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,7 +108,7 @@ dependencies:
|
|
|
108
108
|
version: '2.25'
|
|
109
109
|
- - "<"
|
|
110
110
|
- !ruby/object:Gem::Version
|
|
111
|
-
version: '2.
|
|
111
|
+
version: '2.44'
|
|
112
112
|
type: :development
|
|
113
113
|
prerelease: false
|
|
114
114
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -118,7 +118,7 @@ dependencies:
|
|
|
118
118
|
version: '2.25'
|
|
119
119
|
- - "<"
|
|
120
120
|
- !ruby/object:Gem::Version
|
|
121
|
-
version: '2.
|
|
121
|
+
version: '2.44'
|
|
122
122
|
- !ruby/object:Gem::Dependency
|
|
123
123
|
name: io-event
|
|
124
124
|
requirement: !ruby/object:Gem::Requirement
|