active_harness 0.2.18 → 0.2.20
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/agent/hooks.rb +10 -0
- data/lib/active_harness/agent/output_parser.rb +1 -1
- data/lib/active_harness/agent/prompt.rb +2 -2
- data/lib/active_harness/agent.rb +18 -16
- data/lib/active_harness/pipeline.rb +38 -37
- data/lib/active_harness/tribunal.rb +12 -15
- data/lib/active_harness.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d1188ca468f31f46c40d118b7e719a849925aa11ddab3a4a87ea4df0fd9bdb84
|
|
4
|
+
data.tar.gz: 1ed36314efd943e6f4e62bbc43b2ec00d0b0486bdbf015e6b590341d240ffcdc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37e82033bddd4069f26619d2bfc5faec831c32618216bedb6cb9ce19ca7fbbaac1057e829809ee15f1d88d1a6fc3b3832cd2c02d1f112c850a974a26205d3727
|
|
7
|
+
data.tar.gz: 3a4fafd8184d55f116689e28c4f411d102f7279d290093c9cc32dafabfc9eafe9aa55abde59a04cec37985e74da94a8ddb14153febce35aa41c2eb0404eed2c6
|
|
@@ -71,5 +71,15 @@ module ActiveHarness
|
|
|
71
71
|
instance_eval(&hooks[event])
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
|
+
|
|
75
|
+
# Unified internal method: fires the DSL hook AND the external event_stream lambda.
|
|
76
|
+
# Consistent with Tribunal#fire and Pipeline#fire.
|
|
77
|
+
def fire(event, *args)
|
|
78
|
+
result = run_hook(event, *args)
|
|
79
|
+
@event_stream&.call(event, *args)
|
|
80
|
+
result
|
|
81
|
+
rescue IOError, ActionController::Live::ClientDisconnected
|
|
82
|
+
result
|
|
83
|
+
end
|
|
74
84
|
end
|
|
75
85
|
end
|
|
@@ -49,7 +49,7 @@ module ActiveHarness
|
|
|
49
49
|
# puts "Parse failed: #{error.message}"
|
|
50
50
|
# { "result" => nil, "reason" => "parse error" } # fallback
|
|
51
51
|
# end
|
|
52
|
-
fallback =
|
|
52
|
+
fallback = fire(:parse_error, raw, e)
|
|
53
53
|
fallback.nil? ? raise : fallback
|
|
54
54
|
end
|
|
55
55
|
end
|
|
@@ -22,7 +22,7 @@ module ActiveHarness
|
|
|
22
22
|
public :system_prompt
|
|
23
23
|
|
|
24
24
|
def resolve_system_prompt
|
|
25
|
-
|
|
25
|
+
fire(:before_system_prompt)
|
|
26
26
|
|
|
27
27
|
sp = @config[:system_prompt]
|
|
28
28
|
|
|
@@ -45,7 +45,7 @@ module ActiveHarness
|
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
fire(:after_system_prompt, prompt)
|
|
49
49
|
prompt
|
|
50
50
|
end
|
|
51
51
|
|
data/lib/active_harness/agent.rb
CHANGED
|
@@ -11,8 +11,8 @@ module ActiveHarness
|
|
|
11
11
|
# SupportAgent.call(input: "Hi")
|
|
12
12
|
# SupportAgent.call(input: "Hi", context: { user_id: 42 })
|
|
13
13
|
# SupportAgent.call(input: "Hi", memory: memory)
|
|
14
|
-
def call(input: nil, context: {}, models: nil, memory: nil,
|
|
15
|
-
new(input: input, context: context, models: models, memory: memory,
|
|
14
|
+
def call(input: nil, context: {}, models: nil, memory: nil, streams: {})
|
|
15
|
+
new(input: input, context: context, models: models, memory: memory, streams: streams).call
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# Each subclass gets its own isolated config hash.
|
|
@@ -36,8 +36,8 @@ module ActiveHarness
|
|
|
36
36
|
# -------------------------------------------------------------------------
|
|
37
37
|
# Instance API
|
|
38
38
|
# -------------------------------------------------------------------------
|
|
39
|
-
attr_accessor :input, :context
|
|
40
|
-
attr_reader :
|
|
39
|
+
attr_accessor :input, :context
|
|
40
|
+
attr_reader :result, :token_stream, :event_stream
|
|
41
41
|
|
|
42
42
|
def models=(list)
|
|
43
43
|
@models_override = Array(list)
|
|
@@ -48,18 +48,17 @@ module ActiveHarness
|
|
|
48
48
|
@memory = obj
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def initialize(input: nil, context: {}, models: nil, memory: nil,
|
|
51
|
+
def initialize(input: nil, context: {}, models: nil, memory: nil, streams: {})
|
|
52
52
|
@input = input
|
|
53
53
|
@config = self.class.agent_config
|
|
54
54
|
normalize_input!
|
|
55
55
|
@context = context
|
|
56
56
|
@models_override = Array(models) if models
|
|
57
|
-
@
|
|
58
|
-
@
|
|
59
|
-
@event_stream = event_stream
|
|
57
|
+
@token_stream = streams[:token]
|
|
58
|
+
@event_stream = streams[:agent]
|
|
60
59
|
# memory: can be passed directly or via context[:memory]
|
|
61
60
|
@memory = memory || @context[:memory]
|
|
62
|
-
|
|
61
|
+
fire(:setup)
|
|
63
62
|
end
|
|
64
63
|
|
|
65
64
|
# Attempts each model in order, returns the first successful Result.
|
|
@@ -68,15 +67,18 @@ module ActiveHarness
|
|
|
68
67
|
# Optionally accepts input and stream callback inline:
|
|
69
68
|
# agent.call("What is the capital of Japan?")
|
|
70
69
|
# agent.call("...", stream: ->(token) { print token })
|
|
71
|
-
def call(input = nil,
|
|
70
|
+
def call(input = nil, streams: nil)
|
|
72
71
|
if input
|
|
73
72
|
@input = input
|
|
74
73
|
normalize_input!
|
|
75
74
|
end
|
|
76
|
-
|
|
75
|
+
if streams
|
|
76
|
+
@token_stream = streams[:token] if streams.key?(:token)
|
|
77
|
+
@event_stream = streams[:agent] if streams.key?(:agent)
|
|
78
|
+
end
|
|
77
79
|
@memory&.load
|
|
78
80
|
@system_prompt = resolve_system_prompt
|
|
79
|
-
|
|
81
|
+
fire(:before_call)
|
|
80
82
|
attempts = []
|
|
81
83
|
|
|
82
84
|
cfg = ActiveHarness.config
|
|
@@ -91,22 +93,22 @@ module ActiveHarness
|
|
|
91
93
|
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0).round(3)
|
|
92
94
|
result = build_result(response, entry, attempts, elapsed)
|
|
93
95
|
save_to_memory(result)
|
|
94
|
-
|
|
96
|
+
fire(:after_call, result)
|
|
95
97
|
@result = result
|
|
96
98
|
return self
|
|
97
99
|
rescue *RETRYABLE_ERRORS => e
|
|
98
100
|
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0).round(3)
|
|
99
101
|
attempts << attempt_entry(entry, e, elapsed)
|
|
100
|
-
|
|
102
|
+
fire(:retry, entry, e)
|
|
101
103
|
next
|
|
102
104
|
rescue *STOP_ERRORS => e
|
|
103
105
|
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0).round(3)
|
|
104
106
|
attempts << attempt_entry(entry, e, elapsed)
|
|
105
|
-
|
|
107
|
+
fire(:retry, entry, e)
|
|
106
108
|
raise
|
|
107
109
|
end
|
|
108
110
|
|
|
109
|
-
|
|
111
|
+
fire(:failure, attempts)
|
|
110
112
|
raise Errors::AllModelsFailed, "All models failed. Attempts: #{attempts.inspect}"
|
|
111
113
|
end
|
|
112
114
|
|
|
@@ -129,23 +129,21 @@ module ActiveHarness
|
|
|
129
129
|
@payload = value
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
def initialize(input:, context: {}, memory: nil,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
@
|
|
136
|
-
@
|
|
137
|
-
@
|
|
138
|
-
@
|
|
139
|
-
@
|
|
140
|
-
@
|
|
141
|
-
@
|
|
142
|
-
@
|
|
143
|
-
@
|
|
144
|
-
@
|
|
145
|
-
@
|
|
146
|
-
@
|
|
147
|
-
@execution_time = nil
|
|
148
|
-
@output = nil
|
|
132
|
+
def initialize(input:, context: {}, memory: nil, streams: {})
|
|
133
|
+
@original_input = input
|
|
134
|
+
@payload = input
|
|
135
|
+
@context = context.dup
|
|
136
|
+
@memory = memory
|
|
137
|
+
@token_stream = streams[:token]
|
|
138
|
+
@agent_event_stream = streams[:agent]
|
|
139
|
+
@tribunal_event_stream = streams[:tribunal]
|
|
140
|
+
@pipeline_event_stream = streams[:pipeline]
|
|
141
|
+
@step_results = {}
|
|
142
|
+
@stopped = false
|
|
143
|
+
@stopped_at = nil
|
|
144
|
+
@stop_reason = nil
|
|
145
|
+
@execution_time = nil
|
|
146
|
+
@output = nil
|
|
149
147
|
end
|
|
150
148
|
|
|
151
149
|
def stopped?
|
|
@@ -160,7 +158,7 @@ module ActiveHarness
|
|
|
160
158
|
@memory&.load
|
|
161
159
|
|
|
162
160
|
config[:steps].each do |step|
|
|
163
|
-
|
|
161
|
+
fire(:before_step, step.name, @payload, config)
|
|
164
162
|
fire_step(:before_step, step.name, @payload, config)
|
|
165
163
|
|
|
166
164
|
result = execute_step(step)
|
|
@@ -169,7 +167,7 @@ module ActiveHarness
|
|
|
169
167
|
@context[step.name] = result
|
|
170
168
|
@payload = result.output if step.transform?
|
|
171
169
|
|
|
172
|
-
|
|
170
|
+
fire(:after_step, step.name, result, config)
|
|
173
171
|
fire_step(:after_step, step.name, result, config)
|
|
174
172
|
|
|
175
173
|
if step.stop_if && step.stop_if.call(result)
|
|
@@ -178,6 +176,7 @@ module ActiveHarness
|
|
|
178
176
|
@stop_reason = result
|
|
179
177
|
blk = config[:hooks][:stopped]
|
|
180
178
|
instance_exec(step.name, result, &blk) if blk
|
|
179
|
+
@pipeline_event_stream&.call(:stopped, step.name, result)
|
|
181
180
|
break
|
|
182
181
|
end
|
|
183
182
|
end
|
|
@@ -195,6 +194,7 @@ module ActiveHarness
|
|
|
195
194
|
last_result = @step_results[@step_results.keys.last]
|
|
196
195
|
blk = config[:hooks][:complete]
|
|
197
196
|
instance_exec(last_result, &blk) if blk
|
|
197
|
+
@pipeline_event_stream&.call(:complete, last_result)
|
|
198
198
|
end
|
|
199
199
|
|
|
200
200
|
self
|
|
@@ -204,32 +204,33 @@ module ActiveHarness
|
|
|
204
204
|
|
|
205
205
|
def execute_step(step)
|
|
206
206
|
if step.tribunal?
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
)
|
|
214
|
-
agent.call
|
|
207
|
+
agent_streams = { token: @token_stream, agent: @agent_event_stream, tribunal: @tribunal_event_stream }.compact
|
|
208
|
+
step.agent_class.new(
|
|
209
|
+
input: @payload,
|
|
210
|
+
context: @context.dup,
|
|
211
|
+
streams: agent_streams
|
|
212
|
+
).call
|
|
215
213
|
else
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
)
|
|
222
|
-
agent.call.result
|
|
214
|
+
agent_streams = { token: @token_stream, agent: @agent_event_stream }.compact
|
|
215
|
+
step.agent_class.new(
|
|
216
|
+
input: @payload,
|
|
217
|
+
context: @context.dup,
|
|
218
|
+
streams: agent_streams
|
|
219
|
+
).call.result
|
|
223
220
|
end
|
|
224
221
|
end
|
|
225
222
|
|
|
226
|
-
#
|
|
227
|
-
def
|
|
223
|
+
# Fires global hook AND pipeline_event_stream. Consistent with Agent#fire and Tribunal#fire.
|
|
224
|
+
def fire(event, step_name, data, config)
|
|
228
225
|
blk = config[:hooks][event]
|
|
229
226
|
instance_exec(step_name, data, &blk) if blk
|
|
227
|
+
@pipeline_event_stream&.call(event, step_name, data)
|
|
228
|
+
rescue IOError, ActionController::Live::ClientDisconnected
|
|
229
|
+
nil
|
|
230
230
|
end
|
|
231
231
|
|
|
232
|
-
# Per-step hook: receives (data) only
|
|
232
|
+
# Per-step hook: receives (data) only — not forwarded to pipeline_event_stream
|
|
233
|
+
# (global fire already covers the step event with step_name context).
|
|
233
234
|
def fire_step(event, step_name, data, config)
|
|
234
235
|
blk = config[:step_hooks][step_name]&.dig(event)
|
|
235
236
|
instance_exec(data, &blk) if blk
|
|
@@ -46,11 +46,12 @@ module ActiveHarness
|
|
|
46
46
|
# -------------------------------------------------------------------------
|
|
47
47
|
# Instance API
|
|
48
48
|
# -------------------------------------------------------------------------
|
|
49
|
-
attr_accessor :input, :context
|
|
50
|
-
attr_reader :results, :errors, :verdict, :execution_time, :agent_execution_times
|
|
49
|
+
attr_accessor :input, :context
|
|
50
|
+
attr_reader :results, :errors, :verdict, :execution_time, :agent_execution_times,
|
|
51
|
+
:token_stream, :agent_event_stream, :tribunal_event_stream
|
|
51
52
|
|
|
52
53
|
def initialize(input: nil, context: {}, agents: nil, timeout: 7,
|
|
53
|
-
|
|
54
|
+
streams: {},
|
|
54
55
|
may_fail: :_unset)
|
|
55
56
|
config = self.class.tribunal_config
|
|
56
57
|
|
|
@@ -63,9 +64,9 @@ module ActiveHarness
|
|
|
63
64
|
@evaluate_block = config[:evaluate_block]
|
|
64
65
|
@may_fail = may_fail == :_unset ? config[:may_fail] : may_fail
|
|
65
66
|
@hooks = config[:hooks].dup
|
|
66
|
-
@
|
|
67
|
-
@agent_event_stream =
|
|
68
|
-
@tribunal_event_stream =
|
|
67
|
+
@token_stream = streams[:token]
|
|
68
|
+
@agent_event_stream = streams[:agent]
|
|
69
|
+
@tribunal_event_stream = streams[:tribunal]
|
|
69
70
|
@results = []
|
|
70
71
|
@errors = []
|
|
71
72
|
@verdict = nil
|
|
@@ -151,18 +152,14 @@ module ActiveHarness
|
|
|
151
152
|
end
|
|
152
153
|
|
|
153
154
|
def resolve_agents
|
|
155
|
+
agent_streams = { token: @token_stream, agent: @agent_event_stream }.compact
|
|
154
156
|
@agents.map do |agent|
|
|
155
157
|
if agent.is_a?(Class)
|
|
156
|
-
agent.new(
|
|
157
|
-
input: @input,
|
|
158
|
-
context: @context.dup,
|
|
159
|
-
stream: @stream,
|
|
160
|
-
event_stream: @agent_event_stream
|
|
161
|
-
)
|
|
158
|
+
agent.new(input: @input, context: @context.dup, streams: agent_streams)
|
|
162
159
|
else
|
|
163
|
-
agent.input
|
|
164
|
-
agent.
|
|
165
|
-
agent.event_stream
|
|
160
|
+
agent.input = @input if @input
|
|
161
|
+
agent.instance_variable_set(:@token_stream, @token_stream) if @token_stream
|
|
162
|
+
agent.instance_variable_set(:@event_stream, @agent_event_stream) if @agent_event_stream
|
|
166
163
|
agent
|
|
167
164
|
end
|
|
168
165
|
end
|
data/lib/active_harness.rb
CHANGED