Class: ActiveHarness::Tribunal
- Inherits:
-
Object
- Object
- ActiveHarness::Tribunal
- Defined in:
- lib/active_harness/tribunal.rb
Overview
Can be used directly or subclassed with a class-level DSL.
Direct usage:
tribunal = ActiveHarness::Tribunal.new(
input: "Is this message toxic?",
context: { user_id: 42 },
agents: [ToxicityAgent, BiasAgent, SpamAgent],
timeout: 7
)
tribunal.on(:after_agent) { |result| puts result.model }
tribunal.process { |results| results.all? { |r| r.parsed["result"] == true } }
tribunal.call
Subclass with DSL:
class ContentQualityTribunal < ActiveHarness::Tribunal
agents PolitenessAgent, ConstructivenessAgent
on(:after_agent) { |result| puts result.model }
process { |results| results.all? { |r| r.parsed["result"] == true } }
end
ContentQualityTribunal.new(input: "...").call
Direct Known Subclasses
Constant Summary collapse
- VALID_HOOKS =
%i[ before_call after_agent agent_error after_call before_verdict after_verdict ].freeze
Instance Attribute Summary collapse
-
#agent_execution_times ⇒ Object
readonly
Returns the value of attribute agent_execution_times.
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#execution_time ⇒ Object
readonly
Returns the value of attribute execution_time.
-
#input ⇒ Object
Returns the value of attribute input.
-
#results ⇒ Object
readonly
Returns the value of attribute results.
-
#verdict ⇒ Object
readonly
Returns the value of attribute verdict.
Class Method Summary collapse
- .after(event, &block) ⇒ Object
-
.agents(*list) ⇒ Object
Declare agents at the class level.
-
.before(event, &block) ⇒ Object
Rails-style aliases for
on:. - .callback(event, &block) ⇒ Object
- .inherited(subclass) ⇒ Object
-
.on(event, &block) ⇒ Object
Class-level hook registration.
-
.process(&block) ⇒ Object
Class-level process block.
- .tribunal_config ⇒ Object
Instance Method Summary collapse
-
#call ⇒ Object
Run all agents in parallel, then compute the verdict.
-
#initialize(input: nil, context: {}, agents: nil, timeout: 7) ⇒ Tribunal
constructor
A new instance of Tribunal.
-
#on(event, &block) ⇒ Object
Instance-level hook registration — overrides class-level hooks.
-
#process(&block) ⇒ Object
Instance-level process block — overrides class-level block.
Constructor Details
#initialize(input: nil, context: {}, agents: nil, timeout: 7) ⇒ Tribunal
Returns a new instance of Tribunal.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/active_harness/tribunal.rb', line 100 def initialize(input: nil, context: {}, agents: nil, timeout: 7) config = self.class.tribunal_config @input = input @context = context @agents = agents || config[:agents] @timeout = timeout @process_block = config[:process] @hooks = config[:hooks].dup @results = [] @errors = [] @verdict = nil @execution_time = nil @agent_execution_times = [] end |
Instance Attribute Details
#agent_execution_times ⇒ Object (readonly)
Returns the value of attribute agent_execution_times.
98 99 100 |
# File 'lib/active_harness/tribunal.rb', line 98 def agent_execution_times @agent_execution_times end |
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
98 99 100 |
# File 'lib/active_harness/tribunal.rb', line 98 def errors @errors end |
#execution_time ⇒ Object (readonly)
Returns the value of attribute execution_time.
98 99 100 |
# File 'lib/active_harness/tribunal.rb', line 98 def execution_time @execution_time end |
#input ⇒ Object
Returns the value of attribute input.
97 98 99 |
# File 'lib/active_harness/tribunal.rb', line 97 def input @input end |
#results ⇒ Object (readonly)
Returns the value of attribute results.
98 99 100 |
# File 'lib/active_harness/tribunal.rb', line 98 def results @results end |
#verdict ⇒ Object (readonly)
Returns the value of attribute verdict.
98 99 100 |
# File 'lib/active_harness/tribunal.rb', line 98 def verdict @verdict end |
Class Method Details
.after(event, &block) ⇒ Object
74 75 76 |
# File 'lib/active_harness/tribunal.rb', line 74 def after(event, &block) on(:"after_#{event}", &block) end |
.agents(*list) ⇒ Object
Declare agents at the class level.
agents PolitenessAgent, ConstructivenessAgent
47 48 49 |
# File 'lib/active_harness/tribunal.rb', line 47 def agents(*list) tribunal_config[:agents] = list.flatten end |
.before(event, &block) ⇒ Object
Rails-style aliases for on:
before :call do ... end # → on :before_call
before :agent do ... end # → on :before_agent (not used yet)
before :verdict do |r| end # → on :before_verdict
after :call do ... end # → on :after_call
after :agent do |r| end # → on :after_agent
after :verdict do |v| end # → on :after_verdict
callback :agent_error do |n,e| end # → on :agent_error
70 71 72 |
# File 'lib/active_harness/tribunal.rb', line 70 def before(event, &block) on(:"before_#{event}", &block) end |
.callback(event, &block) ⇒ Object
78 79 80 |
# File 'lib/active_harness/tribunal.rb', line 78 def callback(event, &block) on(event, &block) end |
.inherited(subclass) ⇒ Object
92 93 94 |
# File 'lib/active_harness/tribunal.rb', line 92 def inherited(subclass) subclass.instance_variable_set(:@tribunal_config, { agents: [], hooks: {} }) end |
.on(event, &block) ⇒ Object
Class-level hook registration.
on(:after_agent) { |result| puts result.model }
53 54 55 56 57 58 59 |
# File 'lib/active_harness/tribunal.rb', line 53 def on(event, &block) unless VALID_HOOKS.include?(event) raise ArgumentError, "Unknown Tribunal hook :#{event}. Valid hooks: #{VALID_HOOKS.join(", ")}" end tribunal_config[:hooks][event] = block end |
.process(&block) ⇒ Object
Class-level process block.
process { |results| results.all? { |r| r.parsed["result"] == true } }
84 85 86 |
# File 'lib/active_harness/tribunal.rb', line 84 def process(&block) tribunal_config[:process] = block end |
.tribunal_config ⇒ Object
88 89 90 |
# File 'lib/active_harness/tribunal.rb', line 88 def tribunal_config @tribunal_config ||= { agents: [], hooks: {} } end |
Instance Method Details
#call ⇒ Object
Run all agents in parallel, then compute the verdict. Returns self so calls can be chained: tribunal.call.verdict
Behaviour on failure:
- If some agents fail/timeout, their errors are in #errors and
#results contains only successful results.
- If ALL agents fail/timeout, raises Errors::AllAgentsFailed.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/active_harness/tribunal.rb', line 140 def call agents = resolve_agents run_hook(:before_call) started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) futures = agents.map do |agent| t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) future = Concurrent::Future.execute { agent.call } [future, t0] end @results = [] @errors = [] @agent_execution_times = [] futures.each_with_index do |(future, t0), index| future.wait(@timeout) elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0).round(3) @agent_execution_times << { agent: agents[index].class.name, time: elapsed } if future.fulfilled? @results << future.value run_hook(:after_agent, future.value) elsif future.incomplete? error = Errors::TimeoutError.new( "Agent #{agents[index].class.name} timed out after #{@timeout}s" ) @errors << { agent: agents[index].class.name, error: error } run_hook(:agent_error, agents[index].class.name, error) else @errors << { agent: agents[index].class.name, error: future.reason } run_hook(:agent_error, agents[index].class.name, future.reason) end end @execution_time = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at).round(3) run_hook(:after_call, @results, @errors) if @results.empty? = @errors.map { |e| "#{e[:agent]}: #{e[:error].}" }.join("; ") raise Errors::AllAgentsFailed, "All agents failed — #{}" end verdict_input = transform_hook(:before_verdict, @results) @verdict = @process_block ? @process_block.call(verdict_input) : nil run_hook(:after_verdict, @verdict) self end |
#on(event, &block) ⇒ Object
Instance-level hook registration — overrides class-level hooks. :before_verdict is a transform hook: its return value replaces the results array.
118 119 120 121 122 123 124 125 |
# File 'lib/active_harness/tribunal.rb', line 118 def on(event, &block) unless VALID_HOOKS.include?(event) raise ArgumentError, "Unknown Tribunal hook :#{event}. Valid hooks: #{VALID_HOOKS.join(", ")}" end @hooks[event] = block self end |
#process(&block) ⇒ Object
Instance-level process block — overrides class-level block.
128 129 130 131 |
# File 'lib/active_harness/tribunal.rb', line 128 def process(&block) @process_block = block self end |