active_harness 0.2.14 → 0.2.15
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/lib/active_harness/tribunal/dsl.rb +35 -2
- data/lib/active_harness/tribunal/processing.rb +44 -0
- data/lib/active_harness/tribunal.rb +24 -12
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc551806a5848e9157c21ee668ee38c139a55c46fb7e8c2a6a2e8022200c90cb
|
|
4
|
+
data.tar.gz: f6110846af88ce73b7fe7a30221d18960a58e80adeaf01e2b19b5e14fac0640d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8884d36a5da2581707584068acc7315a00d417bd88a7776d1702c375e05470257c21cedb6c323fec5aa75562e6f118d635ae1b1a2053d3e7d60b48b0aca8d3f
|
|
7
|
+
data.tar.gz: 02fa25dca8678816e76b2c2b2fad6131e26a7aa8b5a97cb78a5bcee84d7a9923507eb5981d0508663ecced5a6eb85c5f92799a00d80b59098252dc08438ff22f
|
|
@@ -9,13 +9,46 @@ module ActiveHarness
|
|
|
9
9
|
tribunal_config[:agents] = list.flatten
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
# Class-level process block — defines how to compute the verdict from results.
|
|
12
|
+
# Class-level process block — defines how to compute the verdict from all results.
|
|
13
|
+
# Receives the full results array; return value becomes #verdict.
|
|
14
|
+
# Takes priority over +verdict+ strategy if both are declared.
|
|
13
15
|
#
|
|
14
16
|
# process { |results| results.all? { |r| r.parsed["result"] == true } }
|
|
15
|
-
# process { |results| results.count { |r| r.parsed["result"] == true } >= 2 }
|
|
16
17
|
def process(&block)
|
|
17
18
|
tribunal_config[:process] = block
|
|
18
19
|
end
|
|
20
|
+
|
|
21
|
+
# Declarative verdict — built-in aggregation strategy with a per-result evaluator.
|
|
22
|
+
#
|
|
23
|
+
# Strategies:
|
|
24
|
+
# :unanimous — verdict true when every successful result evaluates to true
|
|
25
|
+
# :majority — verdict true when more than half of successful results evaluate to true
|
|
26
|
+
#
|
|
27
|
+
# Options:
|
|
28
|
+
# may_fail: N — tolerate up to N agent errors before raising AllAgentsFailed
|
|
29
|
+
# (default: nil — raise only when all agents fail, preserving legacy behavior)
|
|
30
|
+
#
|
|
31
|
+
# The block receives a single Result and must return a truthy/falsy value.
|
|
32
|
+
#
|
|
33
|
+
# verdict :unanimous do |result|
|
|
34
|
+
# result.parsed["result"] == true
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# verdict :majority, may_fail: 1 do |result|
|
|
38
|
+
# result.parsed["result"] == true
|
|
39
|
+
# end
|
|
40
|
+
VALID_STRATEGIES = %i[unanimous majority].freeze
|
|
41
|
+
|
|
42
|
+
def verdict(strategy, may_fail: nil, &block)
|
|
43
|
+
unless VALID_STRATEGIES.include?(strategy)
|
|
44
|
+
raise ArgumentError,
|
|
45
|
+
"Unknown verdict strategy :#{strategy}. Valid strategies: #{VALID_STRATEGIES.map { |s| ":#{s}" }.join(", ")}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
tribunal_config[:strategy] = strategy
|
|
49
|
+
tribunal_config[:may_fail] = may_fail
|
|
50
|
+
tribunal_config[:evaluate_block] = block
|
|
51
|
+
end
|
|
19
52
|
end
|
|
20
53
|
end
|
|
21
54
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module ActiveHarness
|
|
2
|
+
class Tribunal
|
|
3
|
+
# Instance-level process block — overrides class-level block.
|
|
4
|
+
#
|
|
5
|
+
# tribunal.process { |results| results.count { |r| r.parsed["ok"] } >= 2 }
|
|
6
|
+
def process(&block)
|
|
7
|
+
@process_block = block
|
|
8
|
+
self
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
# Selects and executes the verdict computation in order of priority:
|
|
14
|
+
# 1. Instance-level process block (set via tribunal.process { ... })
|
|
15
|
+
# 2. Class-level process block (set via `process do ... end` in subclass)
|
|
16
|
+
# 3. Class-level strategy (set via `verdict :unanimous/:majority`)
|
|
17
|
+
# 4. nil (no verdict computation declared)
|
|
18
|
+
def compute_verdict(results)
|
|
19
|
+
if @process_block
|
|
20
|
+
@process_block.call(results)
|
|
21
|
+
elsif @strategy
|
|
22
|
+
apply_strategy(@strategy, results)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Built-in aggregation strategies.
|
|
27
|
+
#
|
|
28
|
+
# Without an evaluate_block every successful result is treated as a positive vote.
|
|
29
|
+
# With an evaluate_block the block decides whether each result counts as positive.
|
|
30
|
+
#
|
|
31
|
+
# :unanimous — all positive votes required
|
|
32
|
+
# :majority — more than 50% positive votes required
|
|
33
|
+
def apply_strategy(strategy, results)
|
|
34
|
+
evaluate = @evaluate_block || ->(r) { r }
|
|
35
|
+
votes = results.map { |r| evaluate.call(r) ? true : false }
|
|
36
|
+
positive = votes.count(true)
|
|
37
|
+
|
|
38
|
+
case strategy
|
|
39
|
+
when :unanimous then positive == votes.size
|
|
40
|
+
when :majority then positive > votes.size / 2.0
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -50,7 +50,8 @@ module ActiveHarness
|
|
|
50
50
|
attr_reader :results, :errors, :verdict, :execution_time, :agent_execution_times
|
|
51
51
|
|
|
52
52
|
def initialize(input: nil, context: {}, agents: nil, timeout: 7,
|
|
53
|
-
stream: nil, agent_event_stream: nil, tribunal_event_stream: nil
|
|
53
|
+
stream: nil, agent_event_stream: nil, tribunal_event_stream: nil,
|
|
54
|
+
may_fail: :_unset)
|
|
54
55
|
config = self.class.tribunal_config
|
|
55
56
|
|
|
56
57
|
@input = input
|
|
@@ -58,6 +59,9 @@ module ActiveHarness
|
|
|
58
59
|
@agents = agents || config[:agents]
|
|
59
60
|
@timeout = timeout
|
|
60
61
|
@process_block = config[:process]
|
|
62
|
+
@strategy = config[:strategy]
|
|
63
|
+
@evaluate_block = config[:evaluate_block]
|
|
64
|
+
@may_fail = may_fail == :_unset ? config[:may_fail] : may_fail
|
|
61
65
|
@hooks = config[:hooks].dup
|
|
62
66
|
@stream = stream
|
|
63
67
|
@agent_event_stream = agent_event_stream
|
|
@@ -69,12 +73,6 @@ module ActiveHarness
|
|
|
69
73
|
@agent_execution_times = []
|
|
70
74
|
end
|
|
71
75
|
|
|
72
|
-
# Instance-level process block — overrides class-level block.
|
|
73
|
-
def process(&block)
|
|
74
|
-
@process_block = block
|
|
75
|
-
self
|
|
76
|
-
end
|
|
77
|
-
|
|
78
76
|
# Run all agents in parallel, then compute the verdict.
|
|
79
77
|
# Returns self so calls can be chained: tribunal.call.verdict
|
|
80
78
|
#
|
|
@@ -125,13 +123,13 @@ module ActiveHarness
|
|
|
125
123
|
|
|
126
124
|
run_hook(:after_call, @results, @errors)
|
|
127
125
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
end
|
|
126
|
+
# If all agents failed, raise an exception.
|
|
127
|
+
# Otherwise, compute the verdict based on successful results.
|
|
128
|
+
check_failure_threshold!
|
|
132
129
|
|
|
133
130
|
verdict_input = transform_hook(:before_verdict, @results)
|
|
134
|
-
@verdict
|
|
131
|
+
@verdict = compute_verdict(verdict_input)
|
|
132
|
+
|
|
135
133
|
run_hook(:after_verdict, @verdict)
|
|
136
134
|
|
|
137
135
|
self
|
|
@@ -139,6 +137,19 @@ module ActiveHarness
|
|
|
139
137
|
|
|
140
138
|
private
|
|
141
139
|
|
|
140
|
+
def check_failure_threshold!
|
|
141
|
+
if !@may_fail.nil? && @errors.size > @may_fail
|
|
142
|
+
raise Errors::AllAgentsFailed,
|
|
143
|
+
"Too many agents failed (#{@errors.size} > may_fail: #{@may_fail}) — #{error_summary}"
|
|
144
|
+
elsif @results.empty?
|
|
145
|
+
raise Errors::AllAgentsFailed, "All agents failed — #{error_summary}"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def error_summary
|
|
150
|
+
@errors.map { |e| "#{e[:agent]}: #{e[:error].message}" }.join("; ")
|
|
151
|
+
end
|
|
152
|
+
|
|
142
153
|
def resolve_agents
|
|
143
154
|
@agents.map do |agent|
|
|
144
155
|
if agent.is_a?(Class)
|
|
@@ -161,3 +172,4 @@ end
|
|
|
161
172
|
|
|
162
173
|
require_relative "tribunal/hooks"
|
|
163
174
|
require_relative "tribunal/dsl"
|
|
175
|
+
require_relative "tribunal/processing"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_harness
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- the-teacher
|
|
@@ -74,6 +74,7 @@ files:
|
|
|
74
74
|
- lib/active_harness/tribunal.rb
|
|
75
75
|
- lib/active_harness/tribunal/dsl.rb
|
|
76
76
|
- lib/active_harness/tribunal/hooks.rb
|
|
77
|
+
- lib/active_harness/tribunal/processing.rb
|
|
77
78
|
- lib/generators/active_harness/agent/agent_generator.rb
|
|
78
79
|
- lib/generators/active_harness/agent/templates/agent.rb.tt
|
|
79
80
|
- lib/generators/active_harness/install/install_generator.rb
|