riffer 0.12.0 → 0.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0cf5738c3a643db6e1626862456336f6b4bba78bbb1e0efa599ebf61e0b80ee5
4
- data.tar.gz: 683586055701aa2ffb62dd88559ceac640b6e0221120dee6fa589ef3e44a6cb4
3
+ metadata.gz: a7a4f27800f9f5a266d408a1b8b8d6d96f6c59307b4761d34d3a1365b9b71bb8
4
+ data.tar.gz: 6719e5013f8d83c78f2647808dd0d27a024ec432085311af02b1dc7cfbdaacfb
5
5
  SHA512:
6
- metadata.gz: 2c3fed6e20f275474ac0e9388a9b5200eab9b899c32534c7ba67b2df5e804097774c35821045e1dfbdaf65db426f7d56a827cd86de2eb32cbd8b65cb7aa8d343
7
- data.tar.gz: 3db7dd0aa5fde3a20f97bd462b634ef01d8221db5f9c1d6c71a6430d915dc17b2ddebdd625d38514731ba63d6803cab6e94f5f61205e69cbf8d8d47237a3ee42
6
+ metadata.gz: 24356e56e024eaf5223c91c97b72bf4e90a9dd74721d12dac6b5d0f294bea305914d6f074467e915dfd64adbf3eea5a07484147a394f51613f7fe16e828e4c36
7
+ data.tar.gz: 6c8b8d464d94b56f044196f81ec298b01824dea114aca266b82de020c82521ce975451c643c5307d532c23f6fb85e2d38f4931f931cf13dee5e54b53ba5e8211
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.12.0"
2
+ ".": "0.13.0"
3
3
  }
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.13.0](https://github.com/janeapp/riffer/compare/riffer/v0.12.0...riffer/v0.13.0) (2026-02-12)
9
+
10
+
11
+ ### Features
12
+
13
+ * remove identifiers from evals and guardrails ([#112](https://github.com/janeapp/riffer/issues/112)) ([7b60707](https://github.com/janeapp/riffer/commit/7b60707206e53451f5bee2faf1c12a75eaf26d98))
14
+
8
15
  ## [0.12.0](https://github.com/janeapp/riffer/compare/riffer/v0.11.0...riffer/v0.12.0) (2026-02-11)
9
16
 
10
17
 
data/docs/08_EVALS.md CHANGED
@@ -24,7 +24,7 @@ module QualityEvals
24
24
  include Riffer::Evals::Profile
25
25
 
26
26
  ai_evals do
27
- metric :answer_relevancy, min: 0.85
27
+ metric Riffer::Evals::Evaluators::AnswerRelevancy, min: 0.85
28
28
  end
29
29
  end
30
30
 
@@ -53,7 +53,7 @@ The judge model is the LLM that evaluates agent outputs. You can use any configu
53
53
 
54
54
  ## Built-in Evaluators
55
55
 
56
- ### answer_relevancy
56
+ ### AnswerRelevancy
57
57
 
58
58
  Evaluates how well a response addresses the input question.
59
59
 
@@ -67,7 +67,7 @@ Evaluates how well a response addresses the input question.
67
67
 
68
68
  ```ruby
69
69
  ai_evals do
70
- metric :answer_relevancy, min: 0.85
70
+ metric Riffer::Evals::Evaluators::AnswerRelevancy, min: 0.85
71
71
  end
72
72
  ```
73
73
 
@@ -82,7 +82,7 @@ module QualityEvals
82
82
  include Riffer::Evals::Profile
83
83
 
84
84
  ai_evals do
85
- metric :answer_relevancy, min: 0.85
85
+ metric Riffer::Evals::Evaluators::AnswerRelevancy, min: 0.85
86
86
  end
87
87
  end
88
88
  ```
@@ -95,7 +95,7 @@ end
95
95
 
96
96
  ```ruby
97
97
  ai_evals do
98
- metric :answer_relevancy, min: 0.85, weight: 2.0 # Weighted more heavily
98
+ metric Riffer::Evals::Evaluators::AnswerRelevancy, min: 0.85, weight: 2.0 # Weighted more heavily
99
99
  end
100
100
  ```
101
101
 
@@ -138,7 +138,7 @@ result.to_h # => Hash representation
138
138
  Individual evaluation results:
139
139
 
140
140
  ```ruby
141
- result.results.first.evaluator # => "answer_relevancy"
141
+ result.results.first.evaluator # => Riffer::Evals::Evaluators::AnswerRelevancy
142
142
  result.results.first.score # => 0.92
143
143
  result.results.first.reason # => "The response directly addresses..."
144
144
  result.results.first.higher_is_better # => true
@@ -151,7 +151,6 @@ Create evaluators by subclassing `Riffer::Evals::Evaluator`:
151
151
  ```ruby
152
152
  # app/evals/medical_accuracy_evaluator.rb
153
153
  class MedicalAccuracyEvaluator < Riffer::Evals::Evaluator
154
- identifier "medical_accuracy"
155
154
  description "Evaluates medical information accuracy"
156
155
  higher_is_better true
157
156
  judge_model "anthropic/claude-opus-4-5-20251101" # Optional override
@@ -179,20 +178,20 @@ class MedicalAccuracyEvaluator < Riffer::Evals::Evaluator
179
178
  end
180
179
  ```
181
180
 
182
- ### Registering Custom Evaluators
181
+ ### Using Custom Evaluators
183
182
 
184
- Register custom evaluators in your app initialization. Built-in evaluators are always available.
183
+ Reference your custom evaluator class directly in eval profiles:
185
184
 
186
185
  ```ruby
187
- # config/initializers/riffer.rb
188
- Riffer::Evals::Evaluators::Repository.register(:medical_accuracy, MedicalAccuracyEvaluator)
186
+ ai_evals do
187
+ metric MedicalAccuracyEvaluator, min: 0.9
188
+ end
189
189
  ```
190
190
 
191
191
  ### Evaluator DSL
192
192
 
193
193
  Class methods:
194
194
 
195
- - `identifier(value)` - Set the evaluator identifier (defaults to snake_case class name)
196
195
  - `description(value)` - Human-readable description
197
196
  - `higher_is_better(value)` - Whether higher scores are better (default: true)
198
197
  - `judge_model(value)` - Override the global judge model
@@ -231,7 +230,6 @@ Evaluators don't have to use LLM-as-judge:
231
230
 
232
231
  ```ruby
233
232
  class LengthEvaluator < Riffer::Evals::Evaluator
234
- identifier "response_length"
235
233
  description "Checks response is within expected length"
236
234
  higher_is_better true
237
235
 
@@ -289,7 +287,7 @@ module QualityEvals
289
287
  include Riffer::Evals::Profile
290
288
 
291
289
  ai_evals do
292
- metric :answer_relevancy, min: 0.85, weight: 2.0
290
+ metric Riffer::Evals::Evaluators::AnswerRelevancy, min: 0.85, weight: 2.0
293
291
  end
294
292
  end
295
293
 
@@ -41,20 +41,6 @@ class ContentFilterGuardrail < Riffer::Guardrail
41
41
  end
42
42
  ```
43
43
 
44
- ## Configuration Methods
45
-
46
- ### identifier
47
-
48
- Sets a custom identifier (defaults to snake_case class name):
49
-
50
- ```ruby
51
- class MyGuardrail < Riffer::Guardrail
52
- identifier "my_custom_guardrail"
53
- end
54
-
55
- MyGuardrail.identifier # => "my_custom_guardrail"
56
- ```
57
-
58
44
  ## Processing Methods
59
45
 
60
46
  ### process_input
@@ -186,7 +172,7 @@ response = MyAgent.generate("Hello")
186
172
  if response.blocked?
187
173
  puts "Blocked: #{response.tripwire.reason}"
188
174
  puts "Phase: #{response.tripwire.phase}"
189
- puts "Guardrail: #{response.tripwire.guardrail_id}"
175
+ puts "Guardrail: #{response.tripwire.guardrail}"
190
176
  else
191
177
  puts response.content
192
178
  end
@@ -201,7 +187,7 @@ response = MyAgent.generate("Hello")
201
187
 
202
188
  if response.modified?
203
189
  response.modifications.each do |mod|
204
- puts "Guardrail: #{mod.guardrail_id}"
190
+ puts "Guardrail: #{mod.guardrail}"
205
191
  puts "Phase: #{mod.phase}"
206
192
  puts "Changed indices: #{mod.message_indices}"
207
193
  end
@@ -214,7 +200,7 @@ During streaming, `GuardrailModification` events are emitted when transforms occ
214
200
  MyAgent.stream("Hello").each do |event|
215
201
  case event
216
202
  when Riffer::StreamEvents::GuardrailModification
217
- puts "Modified by: #{event.guardrail_id} (#{event.phase})"
203
+ puts "Modified by: #{event.guardrail} (#{event.phase})"
218
204
  when Riffer::StreamEvents::TextDelta
219
205
  print event.content
220
206
  end
@@ -264,8 +250,6 @@ end
264
250
 
265
251
  ```ruby
266
252
  class UnicodeNormalizer < Riffer::Guardrail
267
- identifier "unicode_normalizer"
268
-
269
253
  def process_input(messages, context:)
270
254
  normalized = messages.map do |msg|
271
255
  if msg.respond_to?(:content) && msg.content
@@ -296,8 +280,6 @@ end
296
280
 
297
281
  ```ruby
298
282
  class TokenLimiter < Riffer::Guardrail
299
- identifier "token_limiter"
300
-
301
283
  def initialize(limit:, strategy: :truncate)
302
284
  super()
303
285
  @limit = limit
@@ -339,8 +321,6 @@ end
339
321
 
340
322
  ```ruby
341
323
  class ContentPolicyFilter < Riffer::Guardrail
342
- identifier "content_policy"
343
-
344
324
  BLOCKED_PATTERNS = [
345
325
  /pattern1/i,
346
326
  /pattern2/i
@@ -9,7 +9,6 @@
9
9
  # See Riffer::Evals::Evaluators.
10
10
  #
11
11
  # class MyEvaluator < Riffer::Evals::Evaluator
12
- # identifier "my_evaluator"
13
12
  # description "Evaluates response quality"
14
13
  # higher_is_better true
15
14
  # judge_model "anthropic/claude-opus-4-5-20251101"
@@ -25,16 +24,6 @@
25
24
  #
26
25
  class Riffer::Evals::Evaluator
27
26
  class << self
28
- include Riffer::Helpers::ClassNameConverter
29
-
30
- # Gets or sets the evaluator identifier.
31
- #
32
- #: (?String?) -> String
33
- def identifier(value = nil)
34
- return @identifier || class_name_to_identifier(name) if value.nil?
35
- @identifier = value.to_s
36
- end
37
-
38
27
  # Gets or sets the evaluator description.
39
28
  #
40
29
  #: (?String?) -> String?
@@ -58,16 +47,6 @@ class Riffer::Evals::Evaluator
58
47
  return @judge_model if value.nil?
59
48
  @judge_model = value.to_s
60
49
  end
61
-
62
- private
63
-
64
- #: (String?) -> String?
65
- def class_name_to_identifier(name)
66
- return nil if name.nil?
67
- class_name = name.split("::").last
68
- return nil if class_name.nil?
69
- class_name_to_path(class_name).sub(/_evaluator$/, "")
70
- end
71
50
  end
72
51
 
73
52
  # Evaluates an input/output pair.
@@ -97,7 +76,7 @@ class Riffer::Evals::Evaluator
97
76
  #: (score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped]) -> Riffer::Evals::Result
98
77
  def result(score:, reason: nil, metadata: {})
99
78
  Riffer::Evals::Result.new(
100
- evaluator: self.class.identifier,
79
+ evaluator: self.class,
101
80
  score: score,
102
81
  reason: reason,
103
82
  metadata: metadata,
@@ -14,7 +14,6 @@
14
14
  # result.score # => 0.95
15
15
  #
16
16
  class Riffer::Evals::Evaluators::AnswerRelevancy < Riffer::Evals::Evaluator
17
- identifier "answer_relevancy"
18
17
  description "Evaluates how well the response addresses the input question"
19
18
  higher_is_better true
20
19
 
@@ -1,59 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  # rbs_inline: enabled
3
3
 
4
- # Namespace for built-in evaluators and the evaluator repository.
5
- #
6
- # See Riffer::Evals::Evaluators::Repository for registering custom evaluators.
4
+ # Namespace for built-in evaluators.
7
5
  module Riffer::Evals::Evaluators
8
- # Repository for looking up evaluators by identifier.
9
- #
10
- # Built-in evaluators are always available. Custom evaluators
11
- # can be registered using Repository.register in your app initialization.
12
- #
13
- # # Register a custom evaluator (config/initializers/riffer.rb)
14
- # Riffer::Evals::Evaluators::Repository.register(:my_evaluator, MyEvaluator)
15
- #
16
- # # Find an evaluator
17
- # Riffer::Evals::Evaluators::Repository.find(:answer_relevancy)
18
- # # => Riffer::Evals::Evaluators::AnswerRelevancy
19
- #
20
- class Repository
21
- # Built-in evaluators (always available).
22
- BUILT_IN = {
23
- answer_relevancy: -> { Riffer::Evals::Evaluators::AnswerRelevancy }
24
- }.freeze #: Hash[Symbol, ^() -> singleton(Riffer::Evals::Evaluator)]
25
-
26
- @custom = {}
27
-
28
- class << self
29
- # Registers a custom evaluator class with an identifier.
30
- #
31
- #: ((String | Symbol), singleton(Riffer::Evals::Evaluator)) -> void
32
- def register(identifier, evaluator_class)
33
- @custom[identifier.to_sym] = -> { evaluator_class }
34
- end
35
-
36
- # Finds an evaluator class by identifier.
37
- #
38
- #: ((String | Symbol)) -> singleton(Riffer::Evals::Evaluator)?
39
- def find(identifier)
40
- id = identifier.to_sym
41
- (@custom[id] || BUILT_IN[id])&.call
42
- end
43
-
44
- # Returns all registered evaluators (built-in and custom).
45
- #
46
- #: () -> Hash[Symbol, singleton(Riffer::Evals::Evaluator)]
47
- def all
48
- BUILT_IN.merge(@custom).transform_values(&:call)
49
- end
50
-
51
- # Clears custom registrations (for testing). Built-ins remain.
52
- #
53
- #: () -> void
54
- def clear
55
- @custom = {}
56
- end
57
- end
58
- end
59
6
  end
@@ -6,7 +6,7 @@
6
6
  # Metrics define which evaluator to use and what thresholds determine pass/fail.
7
7
  #
8
8
  # metric = Riffer::Evals::Metric.new(
9
- # evaluator_identifier: "answer_relevancy",
9
+ # evaluator_class: Riffer::Evals::Evaluators::AnswerRelevancy,
10
10
  # min: 0.85,
11
11
  # weight: 1.0
12
12
  # )
@@ -14,8 +14,8 @@
14
14
  # metric.passes?(result) # => true/false based on thresholds
15
15
  #
16
16
  class Riffer::Evals::Metric
17
- # The identifier of the evaluator to use.
18
- attr_reader :evaluator_identifier #: String
17
+ # The evaluator class to use.
18
+ attr_reader :evaluator_class #: singleton(Riffer::Evals::Evaluator)
19
19
 
20
20
  # Minimum acceptable score (for higher_is_better evaluators).
21
21
  attr_reader :min #: Float?
@@ -28,21 +28,20 @@ class Riffer::Evals::Metric
28
28
 
29
29
  # Initializes a new metric.
30
30
  #
31
- #: (evaluator_identifier: String, ?min: Float?, ?max: Float?, ?weight: Float) -> void
32
- def initialize(evaluator_identifier:, min: nil, max: nil, weight: 1.0)
33
- @evaluator_identifier = evaluator_identifier.to_s
31
+ # Raises Riffer::ArgumentError if evaluator_class is not a subclass of Riffer::Evals::Evaluator.
32
+ #
33
+ #: (evaluator_class: singleton(Riffer::Evals::Evaluator), ?min: Float?, ?max: Float?, ?weight: Float) -> void
34
+ def initialize(evaluator_class:, min: nil, max: nil, weight: 1.0)
35
+ unless evaluator_class.is_a?(Class) && evaluator_class < Riffer::Evals::Evaluator
36
+ raise Riffer::ArgumentError, "evaluator_class must be a subclass of Riffer::Evals::Evaluator, got #{evaluator_class.inspect}"
37
+ end
38
+
39
+ @evaluator_class = evaluator_class
34
40
  @min = min&.to_f
35
41
  @max = max&.to_f
36
42
  @weight = weight.to_f
37
43
  end
38
44
 
39
- # Returns the evaluator class for this metric.
40
- #
41
- #: () -> singleton(Riffer::Evals::Evaluator)?
42
- def evaluator_class
43
- Riffer::Evals::Evaluators::Repository.find(evaluator_identifier)
44
- end
45
-
46
45
  # Checks if a result passes this metric's thresholds.
47
46
  #
48
47
  #: (Riffer::Evals::Result) -> bool
@@ -57,7 +56,7 @@ class Riffer::Evals::Metric
57
56
  #: () -> Hash[Symbol, untyped]
58
57
  def to_h
59
58
  {
60
- evaluator_identifier: evaluator_identifier,
59
+ evaluator_class: evaluator_class,
61
60
  min: min,
62
61
  max: max,
63
62
  weight: weight
@@ -10,8 +10,7 @@
10
10
  # include Riffer::Evals::Profile
11
11
  #
12
12
  # ai_evals do
13
- # metric :answer_relevancy, min: 0.85
14
- # metric :hallucination, max: 0.10
13
+ # metric Riffer::Evals::Evaluators::AnswerRelevancy, min: 0.85
15
14
  # end
16
15
  # end
17
16
  #
@@ -49,10 +48,10 @@ module Riffer::Evals::Profile
49
48
 
50
49
  # Defines a metric with thresholds.
51
50
  #
52
- #: ((Symbol | String), ?min: Float?, ?max: Float?, ?weight: Float) -> void
53
- def metric(identifier, min: nil, max: nil, weight: 1.0)
51
+ #: (singleton(Riffer::Evals::Evaluator), ?min: Float?, ?max: Float?, ?weight: Float) -> void
52
+ def metric(evaluator_class, min: nil, max: nil, weight: 1.0)
54
53
  metrics << Riffer::Evals::Metric.new(
55
- evaluator_identifier: identifier,
54
+ evaluator_class: evaluator_class,
56
55
  min: min,
57
56
  max: max,
58
57
  weight: weight
@@ -6,19 +6,19 @@
6
6
  # Contains the score, reason, and metadata from running an evaluator.
7
7
  #
8
8
  # result = Riffer::Evals::Result.new(
9
- # evaluator: "answer_relevancy",
9
+ # evaluator: Riffer::Evals::Evaluators::AnswerRelevancy,
10
10
  # score: 0.85,
11
11
  # reason: "The response addresses the question directly.",
12
12
  # higher_is_better: true
13
13
  # )
14
14
  #
15
15
  # result.score # => 0.85
16
- # result.evaluator # => "answer_relevancy"
16
+ # result.evaluator # => Riffer::Evals::Evaluators::AnswerRelevancy
17
17
  # result.higher_is_better # => true
18
18
  #
19
19
  class Riffer::Evals::Result
20
- # The identifier of the evaluator that produced this result.
21
- attr_reader :evaluator #: String
20
+ # The evaluator class that produced this result.
21
+ attr_reader :evaluator #: singleton(Riffer::Evals::Evaluator)
22
22
 
23
23
  # The evaluation score (0.0 to 1.0).
24
24
  attr_reader :score #: Float
@@ -36,7 +36,7 @@ class Riffer::Evals::Result
36
36
  #
37
37
  # Raises Riffer::ArgumentError if score is not between 0.0 and 1.0.
38
38
  #
39
- #: (evaluator: String, score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
39
+ #: (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
40
40
  def initialize(evaluator:, score:, reason: nil, metadata: {}, higher_is_better: true)
41
41
  @evaluator = evaluator
42
42
  @score = score.to_f
@@ -51,7 +51,7 @@ class Riffer::Evals::Result
51
51
  #: () -> Hash[Symbol, untyped]
52
52
  def to_h
53
53
  {
54
- evaluator: evaluator,
54
+ evaluator: evaluator.name,
55
55
  score: score,
56
56
  reason: reason,
57
57
  metadata: metadata,
@@ -29,10 +29,7 @@ class Riffer::Evals::Runner
29
29
  #: (input: String, output: String, ?context: Hash[Symbol, untyped]?) -> Riffer::Evals::RunResult
30
30
  def run(input:, output:, context: nil)
31
31
  results = metrics.map do |metric|
32
- evaluator_class = metric.evaluator_class
33
- raise Riffer::ArgumentError, "Evaluator not found: #{metric.evaluator_identifier}" unless evaluator_class
34
-
35
- evaluator = evaluator_class.new
32
+ evaluator = metric.evaluator_class.new
36
33
  evaluator.evaluate(input: input, output: output, context: context)
37
34
  end
38
35
 
@@ -6,8 +6,6 @@
6
6
  # Subclass this to create custom guardrails:
7
7
  #
8
8
  # class MyGuardrail < Riffer::Guardrail
9
- # identifier "my_guardrail"
10
- #
11
9
  # def process_input(messages, context:)
12
10
  # # Return pass(messages), transform(modified_messages), or block(reason)
13
11
  # pass(messages)
@@ -19,29 +17,6 @@
19
17
  # end
20
18
  # end
21
19
  class Riffer::Guardrail
22
- include Riffer::Helpers::ClassNameConverter
23
-
24
- class << self
25
- include Riffer::Helpers::ClassNameConverter
26
-
27
- # Gets or sets the guardrail identifier.
28
- #
29
- # +value+ - the identifier to set, or nil to get.
30
- #
31
- #: (?String?) -> String
32
- def identifier(value = nil)
33
- return @identifier || class_name_to_path(name) if value.nil?
34
- @identifier = value.to_s
35
- end
36
- end
37
-
38
- # Returns the instance's identifier.
39
- #
40
- #: () -> String
41
- def identifier
42
- self.class.identifier
43
- end
44
-
45
20
  # Processes input messages before they are sent to the LLM.
46
21
  #
47
22
  # Override this method in subclasses to implement input processing.
@@ -8,8 +8,6 @@
8
8
  # guardrail :before, with: Riffer::Guardrails::MaxLength, max: 1000
9
9
  # guardrail :after, with: Riffer::Guardrails::MaxLength, max: 5000
10
10
  class Riffer::Guardrails::MaxLength < Riffer::Guardrail
11
- identifier "riffer/guardrails/max_length"
12
-
13
11
  DEFAULT_MAX = 10_000 #: Integer
14
12
 
15
13
  # The maximum allowed character length.
@@ -7,8 +7,8 @@
7
7
  # created to record which guardrail made the change, in which phase, and
8
8
  # which message indices were affected.
9
9
  class Riffer::Guardrails::Modification
10
- # The identifier of the guardrail that transformed data.
11
- attr_reader :guardrail_id #: String
10
+ # The guardrail class that transformed data.
11
+ attr_reader :guardrail #: singleton(Riffer::Guardrail)
12
12
 
13
13
  # The phase when the transformation occurred (:before or :after).
14
14
  attr_reader :phase #: Symbol
@@ -18,13 +18,13 @@ class Riffer::Guardrails::Modification
18
18
 
19
19
  # Creates a new modification record.
20
20
  #
21
- # +guardrail_id+ - identifier of the guardrail that transformed.
21
+ # +guardrail+ - the guardrail class that transformed.
22
22
  # +phase+ - :before or :after.
23
23
  # +message_indices+ - indices of changed messages.
24
24
  #
25
- #: (guardrail_id: String, phase: Symbol, message_indices: Array[Integer]) -> void
26
- def initialize(guardrail_id:, phase:, message_indices:)
27
- @guardrail_id = guardrail_id
25
+ #: (guardrail: singleton(Riffer::Guardrail), phase: Symbol, message_indices: Array[Integer]) -> void
26
+ def initialize(guardrail:, phase:, message_indices:)
27
+ @guardrail = guardrail
28
28
  @phase = phase
29
29
  @message_indices = message_indices
30
30
  end
@@ -33,6 +33,10 @@ class Riffer::Guardrails::Modification
33
33
  #
34
34
  #: () -> Hash[Symbol, untyped]
35
35
  def to_h
36
- {guardrail_id: guardrail_id, phase: phase, message_indices: message_indices}
36
+ {
37
+ guardrail: guardrail.name,
38
+ phase: phase,
39
+ message_indices: message_indices
40
+ }
37
41
  end
38
42
  end
@@ -52,7 +52,7 @@ class Riffer::Guardrails::Runner
52
52
  if result.block?
53
53
  tripwire = Riffer::Guardrails::Tripwire.new(
54
54
  reason: result.data,
55
- guardrail_id: guardrail.identifier,
55
+ guardrail: guardrail.class,
56
56
  phase: phase,
57
57
  metadata: result.metadata
58
58
  )
@@ -61,7 +61,7 @@ class Riffer::Guardrails::Runner
61
61
 
62
62
  if result.transform?
63
63
  modifications << Riffer::Guardrails::Modification.new(
64
- guardrail_id: guardrail.identifier,
64
+ guardrail: guardrail.class,
65
65
  phase: phase,
66
66
  message_indices: detect_changed_indices(current_data, result.data)
67
67
  )
@@ -8,7 +8,7 @@
8
8
  #
9
9
  # tripwire = Tripwire.new(
10
10
  # reason: "PII detected in input",
11
- # guardrail_id: "pii_redactor",
11
+ # guardrail: PiiRedactor,
12
12
  # phase: :before,
13
13
  # metadata: { detected_types: [:email, :phone] }
14
14
  # )
@@ -18,8 +18,8 @@ class Riffer::Guardrails::Tripwire
18
18
  # The reason for blocking.
19
19
  attr_reader :reason #: String
20
20
 
21
- # The identifier of the guardrail that triggered the block.
22
- attr_reader :guardrail_id #: String
21
+ # The guardrail class that triggered the block.
22
+ attr_reader :guardrail #: singleton(Riffer::Guardrail)
23
23
 
24
24
  # The phase when the block occurred (:before or :after).
25
25
  attr_reader :phase #: Symbol
@@ -30,18 +30,18 @@ class Riffer::Guardrails::Tripwire
30
30
  # Creates a new tripwire.
31
31
  #
32
32
  # +reason+ - the reason for blocking.
33
- # +guardrail_id+ - identifier of the guardrail that blocked.
33
+ # +guardrail+ - the guardrail class that blocked.
34
34
  # +phase+ - :before or :after.
35
35
  # +metadata+ - optional additional information.
36
36
  #
37
37
  # Raises Riffer::ArgumentError if the phase is invalid.
38
38
  #
39
- #: (reason: String, guardrail_id: String, phase: Symbol, ?metadata: Hash[Symbol, untyped]?) -> void
40
- def initialize(reason:, guardrail_id:, phase:, metadata: nil)
39
+ #: (reason: String, guardrail: singleton(Riffer::Guardrail), phase: Symbol, ?metadata: Hash[Symbol, untyped]?) -> void
40
+ def initialize(reason:, guardrail:, phase:, metadata: nil)
41
41
  raise Riffer::ArgumentError, "Invalid phase: #{phase}" unless PHASES.include?(phase)
42
42
 
43
43
  @reason = reason
44
- @guardrail_id = guardrail_id
44
+ @guardrail = guardrail
45
45
  @phase = phase
46
46
  @metadata = metadata
47
47
  end
@@ -52,7 +52,7 @@ class Riffer::Guardrails::Tripwire
52
52
  def to_h
53
53
  {
54
54
  reason: reason,
55
- guardrail_id: guardrail_id,
55
+ guardrail: guardrail.name,
56
56
  phase: phase,
57
57
  metadata: metadata
58
58
  }
@@ -19,10 +19,10 @@ class Riffer::StreamEvents::GuardrailModification < Riffer::StreamEvents::Base
19
19
  @modification = modification
20
20
  end
21
21
 
22
- # The guardrail identifier that made the transformation.
22
+ # The guardrail class that made the transformation.
23
23
  #
24
- #: () -> String
25
- def guardrail_id = modification.guardrail_id
24
+ #: () -> singleton(Riffer::Guardrail)
25
+ def guardrail = modification.guardrail
26
26
 
27
27
  # The phase when the transformation occurred.
28
28
  #
@@ -33,11 +33,11 @@ class Riffer::StreamEvents::GuardrailTripwire < Riffer::StreamEvents::Base
33
33
  tripwire.phase
34
34
  end
35
35
 
36
- # The guardrail identifier that triggered the block.
36
+ # The guardrail class that triggered the block.
37
37
  #
38
- #: () -> String
39
- def guardrail_id
40
- tripwire.guardrail_id
38
+ #: () -> singleton(Riffer::Guardrail)
39
+ def guardrail
40
+ tripwire.guardrail
41
41
  end
42
42
 
43
43
  # Converts the event to a hash.
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module Riffer
5
- VERSION = "0.12.0" #: String
5
+ VERSION = "0.13.0" #: String
6
6
  end
@@ -8,7 +8,6 @@
8
8
  # See Riffer::Evals::Evaluators.
9
9
  #
10
10
  # class MyEvaluator < Riffer::Evals::Evaluator
11
- # identifier "my_evaluator"
12
11
  # description "Evaluates response quality"
13
12
  # higher_is_better true
14
13
  # judge_model "anthropic/claude-opus-4-5-20251101"
@@ -22,13 +21,6 @@
22
21
  # end
23
22
  # end
24
23
  class Riffer::Evals::Evaluator
25
- include Riffer::Helpers::ClassNameConverter
26
-
27
- # Gets or sets the evaluator identifier.
28
- #
29
- # : (?String?) -> String
30
- def self.identifier: (?String?) -> String
31
-
32
24
  # Gets or sets the evaluator description.
33
25
  #
34
26
  # : (?String?) -> String?
@@ -44,9 +36,6 @@ class Riffer::Evals::Evaluator
44
36
  # : (?String?) -> String?
45
37
  def self.judge_model: (?String?) -> String?
46
38
 
47
- # : (String?) -> String?
48
- private def self.class_name_to_identifier: (String?) -> String?
49
-
50
39
  # Evaluates an input/output pair.
51
40
  #
52
41
  # Raises NotImplementedError if not implemented by subclass.
@@ -1,42 +1,5 @@
1
1
  # Generated from lib/riffer/evals/evaluators.rb with RBS::Inline
2
2
 
3
- # Namespace for built-in evaluators and the evaluator repository.
4
- #
5
- # See Riffer::Evals::Evaluators::Repository for registering custom evaluators.
3
+ # Namespace for built-in evaluators.
6
4
  module Riffer::Evals::Evaluators
7
- # Repository for looking up evaluators by identifier.
8
- #
9
- # Built-in evaluators are always available. Custom evaluators
10
- # can be registered using Repository.register in your app initialization.
11
- #
12
- # # Register a custom evaluator (config/initializers/riffer.rb)
13
- # Riffer::Evals::Evaluators::Repository.register(:my_evaluator, MyEvaluator)
14
- #
15
- # # Find an evaluator
16
- # Riffer::Evals::Evaluators::Repository.find(:answer_relevancy)
17
- # # => Riffer::Evals::Evaluators::AnswerRelevancy
18
- class Repository
19
- # Built-in evaluators (always available).
20
- BUILT_IN: Hash[Symbol, ^() -> singleton(Riffer::Evals::Evaluator)]
21
-
22
- # Registers a custom evaluator class with an identifier.
23
- #
24
- # : ((String | Symbol), singleton(Riffer::Evals::Evaluator)) -> void
25
- def self.register: (String | Symbol, singleton(Riffer::Evals::Evaluator)) -> void
26
-
27
- # Finds an evaluator class by identifier.
28
- #
29
- # : ((String | Symbol)) -> singleton(Riffer::Evals::Evaluator)?
30
- def self.find: (String | Symbol) -> singleton(Riffer::Evals::Evaluator)?
31
-
32
- # Returns all registered evaluators (built-in and custom).
33
- #
34
- # : () -> Hash[Symbol, singleton(Riffer::Evals::Evaluator)]
35
- def self.all: () -> Hash[Symbol, singleton(Riffer::Evals::Evaluator)]
36
-
37
- # Clears custom registrations (for testing). Built-ins remain.
38
- #
39
- # : () -> void
40
- def self.clear: () -> void
41
- end
42
5
  end
@@ -5,15 +5,15 @@
5
5
  # Metrics define which evaluator to use and what thresholds determine pass/fail.
6
6
  #
7
7
  # metric = Riffer::Evals::Metric.new(
8
- # evaluator_identifier: "answer_relevancy",
8
+ # evaluator_class: Riffer::Evals::Evaluators::AnswerRelevancy,
9
9
  # min: 0.85,
10
10
  # weight: 1.0
11
11
  # )
12
12
  #
13
13
  # metric.passes?(result) # => true/false based on thresholds
14
14
  class Riffer::Evals::Metric
15
- # The identifier of the evaluator to use.
16
- attr_reader evaluator_identifier: String
15
+ # The evaluator class to use.
16
+ attr_reader evaluator_class: singleton(Riffer::Evals::Evaluator)
17
17
 
18
18
  # Minimum acceptable score (for higher_is_better evaluators).
19
19
  attr_reader min: Float?
@@ -26,13 +26,10 @@ class Riffer::Evals::Metric
26
26
 
27
27
  # Initializes a new metric.
28
28
  #
29
- # : (evaluator_identifier: String, ?min: Float?, ?max: Float?, ?weight: Float) -> void
30
- def initialize: (evaluator_identifier: String, ?min: Float?, ?max: Float?, ?weight: Float) -> void
31
-
32
- # Returns the evaluator class for this metric.
29
+ # Raises Riffer::ArgumentError if evaluator_class is not a subclass of Riffer::Evals::Evaluator.
33
30
  #
34
- # : () -> singleton(Riffer::Evals::Evaluator)?
35
- def evaluator_class: () -> singleton(Riffer::Evals::Evaluator)?
31
+ # : (evaluator_class: singleton(Riffer::Evals::Evaluator), ?min: Float?, ?max: Float?, ?weight: Float) -> void
32
+ def initialize: (evaluator_class: singleton(Riffer::Evals::Evaluator), ?min: Float?, ?max: Float?, ?weight: Float) -> void
36
33
 
37
34
  # Checks if a result passes this metric's thresholds.
38
35
  #
@@ -9,8 +9,7 @@
9
9
  # include Riffer::Evals::Profile
10
10
  #
11
11
  # ai_evals do
12
- # metric :answer_relevancy, min: 0.85
13
- # metric :hallucination, max: 0.10
12
+ # metric Riffer::Evals::Evaluators::AnswerRelevancy, min: 0.85
14
13
  # end
15
14
  # end
16
15
  #
@@ -35,8 +34,8 @@ module Riffer::Evals::Profile
35
34
 
36
35
  # Defines a metric with thresholds.
37
36
  #
38
- # : ((Symbol | String), ?min: Float?, ?max: Float?, ?weight: Float) -> void
39
- def metric: (Symbol | String, ?min: Float?, ?max: Float?, ?weight: Float) -> void
37
+ # : (singleton(Riffer::Evals::Evaluator), ?min: Float?, ?max: Float?, ?weight: Float) -> void
38
+ def metric: (singleton(Riffer::Evals::Evaluator), ?min: Float?, ?max: Float?, ?weight: Float) -> void
40
39
  end
41
40
 
42
41
  module ClassMethods
@@ -5,18 +5,18 @@
5
5
  # Contains the score, reason, and metadata from running an evaluator.
6
6
  #
7
7
  # result = Riffer::Evals::Result.new(
8
- # evaluator: "answer_relevancy",
8
+ # evaluator: Riffer::Evals::Evaluators::AnswerRelevancy,
9
9
  # score: 0.85,
10
10
  # reason: "The response addresses the question directly.",
11
11
  # higher_is_better: true
12
12
  # )
13
13
  #
14
14
  # result.score # => 0.85
15
- # result.evaluator # => "answer_relevancy"
15
+ # result.evaluator # => Riffer::Evals::Evaluators::AnswerRelevancy
16
16
  # result.higher_is_better # => true
17
17
  class Riffer::Evals::Result
18
- # The identifier of the evaluator that produced this result.
19
- attr_reader evaluator: String
18
+ # The evaluator class that produced this result.
19
+ attr_reader evaluator: singleton(Riffer::Evals::Evaluator)
20
20
 
21
21
  # The evaluation score (0.0 to 1.0).
22
22
  attr_reader score: Float
@@ -34,8 +34,8 @@ class Riffer::Evals::Result
34
34
  #
35
35
  # Raises Riffer::ArgumentError if score is not between 0.0 and 1.0.
36
36
  #
37
- # : (evaluator: String, score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
38
- def initialize: (evaluator: String, score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
37
+ # : (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
38
+ def initialize: (evaluator: singleton(Riffer::Evals::Evaluator), score: Float, ?reason: String?, ?metadata: Hash[Symbol, untyped], ?higher_is_better: bool) -> void
39
39
 
40
40
  # Returns a hash representation of the result.
41
41
  #
@@ -5,8 +5,6 @@
5
5
  # Subclass this to create custom guardrails:
6
6
  #
7
7
  # class MyGuardrail < Riffer::Guardrail
8
- # identifier "my_guardrail"
9
- #
10
8
  # def process_input(messages, context:)
11
9
  # # Return pass(messages), transform(modified_messages), or block(reason)
12
10
  # pass(messages)
@@ -18,22 +16,6 @@
18
16
  # end
19
17
  # end
20
18
  class Riffer::Guardrail
21
- include Riffer::Helpers::ClassNameConverter
22
-
23
- include Riffer::Helpers::ClassNameConverter
24
-
25
- # Gets or sets the guardrail identifier.
26
- #
27
- # +value+ - the identifier to set, or nil to get.
28
- #
29
- # : (?String?) -> String
30
- def self.identifier: (?String?) -> String
31
-
32
- # Returns the instance's identifier.
33
- #
34
- # : () -> String
35
- def identifier: () -> String
36
-
37
19
  # Processes input messages before they are sent to the LLM.
38
20
  #
39
21
  # Override this method in subclasses to implement input processing.
@@ -6,8 +6,8 @@
6
6
  # created to record which guardrail made the change, in which phase, and
7
7
  # which message indices were affected.
8
8
  class Riffer::Guardrails::Modification
9
- # The identifier of the guardrail that transformed data.
10
- attr_reader guardrail_id: String
9
+ # The guardrail class that transformed data.
10
+ attr_reader guardrail: singleton(Riffer::Guardrail)
11
11
 
12
12
  # The phase when the transformation occurred (:before or :after).
13
13
  attr_reader phase: Symbol
@@ -17,12 +17,12 @@ class Riffer::Guardrails::Modification
17
17
 
18
18
  # Creates a new modification record.
19
19
  #
20
- # +guardrail_id+ - identifier of the guardrail that transformed.
20
+ # +guardrail+ - the guardrail class that transformed.
21
21
  # +phase+ - :before or :after.
22
22
  # +message_indices+ - indices of changed messages.
23
23
  #
24
- # : (guardrail_id: String, phase: Symbol, message_indices: Array[Integer]) -> void
25
- def initialize: (guardrail_id: String, phase: Symbol, message_indices: Array[Integer]) -> void
24
+ # : (guardrail: singleton(Riffer::Guardrail), phase: Symbol, message_indices: Array[Integer]) -> void
25
+ def initialize: (guardrail: singleton(Riffer::Guardrail), phase: Symbol, message_indices: Array[Integer]) -> void
26
26
 
27
27
  # Converts the modification to a hash.
28
28
  #
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # tripwire = Tripwire.new(
9
9
  # reason: "PII detected in input",
10
- # guardrail_id: "pii_redactor",
10
+ # guardrail: PiiRedactor,
11
11
  # phase: :before,
12
12
  # metadata: { detected_types: [:email, :phone] }
13
13
  # )
@@ -17,8 +17,8 @@ class Riffer::Guardrails::Tripwire
17
17
  # The reason for blocking.
18
18
  attr_reader reason: String
19
19
 
20
- # The identifier of the guardrail that triggered the block.
21
- attr_reader guardrail_id: String
20
+ # The guardrail class that triggered the block.
21
+ attr_reader guardrail: singleton(Riffer::Guardrail)
22
22
 
23
23
  # The phase when the block occurred (:before or :after).
24
24
  attr_reader phase: Symbol
@@ -29,14 +29,14 @@ class Riffer::Guardrails::Tripwire
29
29
  # Creates a new tripwire.
30
30
  #
31
31
  # +reason+ - the reason for blocking.
32
- # +guardrail_id+ - identifier of the guardrail that blocked.
32
+ # +guardrail+ - the guardrail class that blocked.
33
33
  # +phase+ - :before or :after.
34
34
  # +metadata+ - optional additional information.
35
35
  #
36
36
  # Raises Riffer::ArgumentError if the phase is invalid.
37
37
  #
38
- # : (reason: String, guardrail_id: String, phase: Symbol, ?metadata: Hash[Symbol, untyped]?) -> void
39
- def initialize: (reason: String, guardrail_id: String, phase: Symbol, ?metadata: Hash[Symbol, untyped]?) -> void
38
+ # : (reason: String, guardrail: singleton(Riffer::Guardrail), phase: Symbol, ?metadata: Hash[Symbol, untyped]?) -> void
39
+ def initialize: (reason: String, guardrail: singleton(Riffer::Guardrail), phase: Symbol, ?metadata: Hash[Symbol, untyped]?) -> void
40
40
 
41
41
  # Converts the tripwire to a hash.
42
42
  #
@@ -15,10 +15,10 @@ class Riffer::StreamEvents::GuardrailModification < Riffer::StreamEvents::Base
15
15
  # : (Riffer::Guardrails::Modification, ?role: Symbol) -> void
16
16
  def initialize: (Riffer::Guardrails::Modification, ?role: Symbol) -> void
17
17
 
18
- # The guardrail identifier that made the transformation.
18
+ # The guardrail class that made the transformation.
19
19
  #
20
- # : () -> String
21
- def guardrail_id: () -> String
20
+ # : () -> singleton(Riffer::Guardrail)
21
+ def guardrail: () -> singleton(Riffer::Guardrail)
22
22
 
23
23
  # The phase when the transformation occurred.
24
24
  #
@@ -25,10 +25,10 @@ class Riffer::StreamEvents::GuardrailTripwire < Riffer::StreamEvents::Base
25
25
  # : () -> Symbol
26
26
  def phase: () -> Symbol
27
27
 
28
- # The guardrail identifier that triggered the block.
28
+ # The guardrail class that triggered the block.
29
29
  #
30
- # : () -> String
31
- def guardrail_id: () -> String
30
+ # : () -> singleton(Riffer::Guardrail)
31
+ def guardrail: () -> singleton(Riffer::Guardrail)
32
32
 
33
33
  # Converts the event to a hash.
34
34
  #
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.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall