Class: Vangrail::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/vangrail/engine.rb

Overview

Runs ordered rails over text and reports one Result.

The rules are short enough to state in full:

  • Rails run in the order given. The first :blocked ends the pass.
  • A :modified result replaces the text for every rail after it, and the engine reports :modified unless something later blocks.
  • A rail that raises is not a rail that passed. on_error: :allow (the default) keeps going and marks the pass uncertain; :block stops.
  • An empty rail list returns :passed with certain false. Nothing ran.

Threading rewrites through later rails is the part worth being explicit about: a redaction rail that runs before a policy rail should have the policy rail judge the redacted text, not the original.

Constant Summary collapse

Screening =
Vangrail::Screening
Triage =
Vangrail::Triage

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input: [], context: [], output: [], on_error: :allow, cache: true) ⇒ Engine

Returns a new instance of Engine.

Raises:

  • (ArgumentError)


31
32
33
34
35
36
37
38
39
# File 'lib/vangrail/engine.rb', line 31

def initialize(input: [], context: [], output: [], on_error: :allow, cache: true)
  @input_rails = Array(input)
  @context_rails = Array(context)
  @output_rails = Array(output)
  @on_error = on_error.to_sym
  raise ArgumentError, 'on_error must be :allow or :block' unless %i[allow block].include?(@on_error)

  @cache = cache.is_a?(ResultCache) ? cache : (ResultCache.new if cache)
end

Instance Attribute Details

#cacheObject (readonly)

Returns the value of attribute cache.



29
30
31
# File 'lib/vangrail/engine.rb', line 29

def cache
  @cache
end

#context_railsObject (readonly)

Returns the value of attribute context_rails.



29
30
31
# File 'lib/vangrail/engine.rb', line 29

def context_rails
  @context_rails
end

#input_railsObject (readonly)

Returns the value of attribute input_rails.



29
30
31
# File 'lib/vangrail/engine.rb', line 29

def input_rails
  @input_rails
end

#on_errorObject (readonly)

Returns the value of attribute on_error.



29
30
31
# File 'lib/vangrail/engine.rb', line 29

def on_error
  @on_error
end

#output_railsObject (readonly)

Returns the value of attribute output_rails.



29
30
31
# File 'lib/vangrail/engine.rb', line 29

def output_rails
  @output_rails
end

Instance Method Details

#assessObject



56
# File 'lib/vangrail/engine.rb', line 56

def assess(...) = Assessor.new(self).assess(...)

#check_context(text, **context) ⇒ Object

One retrieved document, before it goes anywhere near a prompt.



50
51
52
# File 'lib/vangrail/engine.rb', line 50

def check_context(text, **context)
  run(:context, context_rails, text, context)
end

#check_input(text, context = {}) ⇒ Object



41
42
43
# File 'lib/vangrail/engine.rb', line 41

def check_input(text, context = {})
  run(:input, input_rails, text, context)
end

#check_output(text, user_input: nil, passages: nil, **context) ⇒ Object



45
46
47
# File 'lib/vangrail/engine.rb', line 45

def check_output(text, user_input: nil, passages: nil, **context)
  run(:output, output_rails, text, context.merge(user_input: user_input, passages: passages))
end

#describeObject



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/vangrail/engine.rb', line 95

def describe
  return 'no rails' if empty?

  parts = []
  parts << "input=#{rail_names(:input).join('+')}" unless input_rails.empty?
  parts << "context=#{rail_names(:context).join('+')}" unless context_rails.empty?
  parts << "output=#{rail_names(:output).join('+')}" unless output_rails.empty?
  parts << "on_error=#{on_error}"
  parts << 'offline' if offline?
  parts.join(' ')
end

#empty?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/vangrail/engine.rb', line 80

def empty?
  input_rails.empty? && context_rails.empty? && output_rails.empty?
end

#invoke(rail, text, ctx) ⇒ Object

Public so Assessor can run one rail without going through #run.



108
109
110
# File 'lib/vangrail/engine.rb', line 108

def invoke(rail, text, ctx)
  memoized(rail, text, ctx) { call_rail(rail, text, ctx) }
end

#offline?Boolean

True when every configured rail decides without a network call, which is the only case where an unreachable endpoint cannot weaken the check.

Returns:

  • (Boolean)


75
76
77
78
# File 'lib/vangrail/engine.rb', line 75

def offline?
  all = input_rails + context_rails + output_rails
  !all.empty? && all.all?(&:offline?)
end

#rail_names(side) ⇒ Object



69
70
71
# File 'lib/vangrail/engine.rb', line 69

def rail_names(side)
  rails(side).map(&:name)
end

#rails(side) ⇒ Object



60
61
62
63
64
65
66
67
# File 'lib/vangrail/engine.rb', line 60

def rails(side)
  case side.to_sym
  when :input then input_rails
  when :context then context_rails
  when :output then output_rails
  else raise ArgumentError, "unknown side: #{side}"
  end
end

#screenObject



54
# File 'lib/vangrail/engine.rb', line 54

def screen(...) = Screening.run(self, ...)

#to_hObject



84
85
86
87
88
89
90
91
92
93
# File 'lib/vangrail/engine.rb', line 84

def to_h
  {
    'input' => rail_names(:input),
    'context' => (rail_names(:context) unless context_rails.empty?),
    'output' => rail_names(:output),
    'on_error' => on_error.to_s,
    'offline' => offline?,
    'cache' => cache&.to_h,
  }.compact
end

#triageObject



58
# File 'lib/vangrail/engine.rb', line 58

def triage(...) = Assessor.new(self).triage(...)