active_harness 0.2.22 → 0.2.23
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.rb +1 -28
- data/lib/active_harness/pipeline.rb +55 -5
- data/lib/active_harness.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0bf8ac57d79582b07a2d82b29be4541fcaf10b3ee8492d362d6ac10f2843537d
|
|
4
|
+
data.tar.gz: cabffcc74dd594c5222a8e0233aa9d5f7276d65cfa06848d3e2684e19efe393e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '018677f5498399d22c2a1b497649e4676023eae7b2578b8b0c13962b1c443970f20e2af5e7ca15051f75d2903287ed9f12012407cf9a671ad1d5d688d8def153'
|
|
7
|
+
data.tar.gz: 5219e6c2a208ff1a2215a71d924c24ade46b6abebc94e9a21c7a36d931d9e32919e7ddc5b054ff7bb616b46057761138aa1a01ce21e66ef6978bb5802304cabe
|
data/lib/active_harness/agent.rb
CHANGED
|
@@ -10,13 +10,11 @@ module ActiveHarness
|
|
|
10
10
|
#
|
|
11
11
|
# SupportAgent.call(input: "Hi")
|
|
12
12
|
# SupportAgent.call(input: "Hi", context: { user_id: 42 })
|
|
13
|
-
# SupportAgent.call(input: "Hi", memory: memory)
|
|
14
13
|
def call(
|
|
15
14
|
input: nil,
|
|
16
15
|
context: {},
|
|
17
16
|
params: {},
|
|
18
17
|
models: nil,
|
|
19
|
-
memory: nil,
|
|
20
18
|
streams: {}
|
|
21
19
|
)
|
|
22
20
|
new(
|
|
@@ -24,7 +22,6 @@ module ActiveHarness
|
|
|
24
22
|
context: context,
|
|
25
23
|
params: params,
|
|
26
24
|
models: models,
|
|
27
|
-
memory: memory,
|
|
28
25
|
streams: streams
|
|
29
26
|
).call
|
|
30
27
|
end
|
|
@@ -62,16 +59,11 @@ module ActiveHarness
|
|
|
62
59
|
@model_list_proxy = nil
|
|
63
60
|
end
|
|
64
61
|
|
|
65
|
-
def memory=(obj)
|
|
66
|
-
@memory = obj
|
|
67
|
-
end
|
|
68
|
-
|
|
69
62
|
def initialize(
|
|
70
63
|
input: nil,
|
|
71
64
|
context: {},
|
|
72
65
|
params: {},
|
|
73
66
|
models: nil,
|
|
74
|
-
memory: nil,
|
|
75
67
|
streams: {}
|
|
76
68
|
)
|
|
77
69
|
@input = input
|
|
@@ -82,8 +74,6 @@ module ActiveHarness
|
|
|
82
74
|
@models_override = Array(models) if models
|
|
83
75
|
@token_stream = streams[:token]
|
|
84
76
|
@event_stream = streams[:agent]
|
|
85
|
-
# memory: can be passed directly or via context[:memory]
|
|
86
|
-
@memory = memory || @context[:memory]
|
|
87
77
|
fire(:setup)
|
|
88
78
|
end
|
|
89
79
|
|
|
@@ -102,9 +92,8 @@ module ActiveHarness
|
|
|
102
92
|
@token_stream = streams[:token] if streams.key?(:token)
|
|
103
93
|
@event_stream = streams[:agent] if streams.key?(:agent)
|
|
104
94
|
end
|
|
105
|
-
@memory&.load
|
|
106
|
-
@system_prompt = resolve_system_prompt
|
|
107
95
|
fire(:before_call)
|
|
96
|
+
@system_prompt = resolve_system_prompt
|
|
108
97
|
attempts = []
|
|
109
98
|
|
|
110
99
|
cfg = ActiveHarness.config
|
|
@@ -118,7 +107,6 @@ module ActiveHarness
|
|
|
118
107
|
response = retry_policy.run { attempt_model(entry, @system_prompt) }
|
|
119
108
|
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0).round(3)
|
|
120
109
|
result = build_result(response, entry, attempts, elapsed)
|
|
121
|
-
save_to_memory(result)
|
|
122
110
|
fire(:after_call, result)
|
|
123
111
|
@result = result
|
|
124
112
|
return self
|
|
@@ -171,21 +159,6 @@ module ActiveHarness
|
|
|
171
159
|
)
|
|
172
160
|
end
|
|
173
161
|
|
|
174
|
-
# Auto-save to memory if no manual record was done in after_call hook.
|
|
175
|
-
# Hooks fire after this method — if a hook calls memory.record manually,
|
|
176
|
-
# the automatic save here is still the first save (hook overrides are additive).
|
|
177
|
-
# To suppress auto-save, set @memory_auto_saved in the hook.
|
|
178
|
-
def save_to_memory(result)
|
|
179
|
-
return unless @memory
|
|
180
|
-
|
|
181
|
-
@memory.record(
|
|
182
|
-
request: @input,
|
|
183
|
-
response: result.output,
|
|
184
|
-
agent: self.class.name,
|
|
185
|
-
model: result.model
|
|
186
|
-
)
|
|
187
|
-
end
|
|
188
|
-
|
|
189
162
|
def normalize_input!
|
|
190
163
|
return if @config.fetch(:normalize_input, true) == false
|
|
191
164
|
@input = @input&.strip&.gsub(/\s+/, " ")
|
|
@@ -51,16 +51,46 @@ module ActiveHarness
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def pipeline_config
|
|
54
|
-
@pipeline_config ||= { steps: [], hooks: {}, step_hooks: {} }
|
|
54
|
+
@pipeline_config ||= { steps: [], hooks: {}, step_hooks: {}, streams: {} }
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# Each subclass gets its own isolated config.
|
|
58
58
|
def inherited(subclass)
|
|
59
59
|
subclass.instance_variable_set(
|
|
60
60
|
:@pipeline_config,
|
|
61
|
-
{ steps: [], hooks: {}, step_hooks: {} }
|
|
61
|
+
{ steps: [], hooks: {}, step_hooks: {}, streams: {} }
|
|
62
62
|
)
|
|
63
63
|
end
|
|
64
|
+
|
|
65
|
+
# Class-level event stream handlers — fired for every matching event from
|
|
66
|
+
# any agent or tribunal executed within this pipeline (including agents
|
|
67
|
+
# running inside tribunals). Multiple blocks can be registered; all fire.
|
|
68
|
+
#
|
|
69
|
+
# The handler receives the same (event, *args) signature that the runtime
|
|
70
|
+
# streams: { agent: lambda } would receive.
|
|
71
|
+
#
|
|
72
|
+
# on_agent_event do |event, result|
|
|
73
|
+
# Rails.logger.info "[Agent #{event}] #{result.model}" if event == :after_call
|
|
74
|
+
# end
|
|
75
|
+
#
|
|
76
|
+
# on_tribunal_event do |event, verdict|
|
|
77
|
+
# Rails.logger.info "[Tribunal #{event}] verdict=#{verdict}" if event == :after_verdict
|
|
78
|
+
# end
|
|
79
|
+
#
|
|
80
|
+
# on_pipeline_event do |event, step_name, _data|
|
|
81
|
+
# Rails.logger.info "[Pipeline #{event}] step=#{step_name}"
|
|
82
|
+
# end
|
|
83
|
+
def on_agent_event(&block)
|
|
84
|
+
(pipeline_config[:streams][:agent] ||= []) << block
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def on_tribunal_event(&block)
|
|
88
|
+
(pipeline_config[:streams][:tribunal] ||= []) << block
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def on_pipeline_event(&block)
|
|
92
|
+
(pipeline_config[:streams][:pipeline] ||= []) << block
|
|
93
|
+
end
|
|
64
94
|
end
|
|
65
95
|
|
|
66
96
|
# -------------------------------------------------------------------------
|
|
@@ -94,9 +124,10 @@ module ActiveHarness
|
|
|
94
124
|
@params = params
|
|
95
125
|
@memory = memory
|
|
96
126
|
@token_stream = streams[:token]
|
|
97
|
-
|
|
98
|
-
@
|
|
99
|
-
@
|
|
127
|
+
class_streams = self.class.pipeline_config[:streams] || {}
|
|
128
|
+
@agent_event_stream = merge_stream(streams[:agent], class_streams[:agent])
|
|
129
|
+
@tribunal_event_stream = merge_stream(streams[:tribunal], class_streams[:tribunal])
|
|
130
|
+
@pipeline_event_stream = merge_stream(streams[:pipeline], class_streams[:pipeline])
|
|
100
131
|
@step_results = {}
|
|
101
132
|
@stopped = false
|
|
102
133
|
@stopped_at = nil
|
|
@@ -159,6 +190,25 @@ module ActiveHarness
|
|
|
159
190
|
|
|
160
191
|
private
|
|
161
192
|
|
|
193
|
+
# Combines a runtime-passed stream lambda with zero or more class-level handler
|
|
194
|
+
# blocks registered via on_agent_event / on_tribunal_event / on_pipeline_event.
|
|
195
|
+
# Returns nil when there are no handlers at all, preserving the existing
|
|
196
|
+
# "no stream" fast path in agents and tribunals.
|
|
197
|
+
#
|
|
198
|
+
# Each class-level handler is evaluated via instance_exec so that blocks
|
|
199
|
+
# written in the pipeline class body can access pipeline instance variables
|
|
200
|
+
# (e.g. @otel_pipeline_span, @params) and call pipeline instance methods.
|
|
201
|
+
def merge_stream(passed_in, class_handlers)
|
|
202
|
+
class_handlers = Array(class_handlers).compact
|
|
203
|
+
return passed_in if class_handlers.empty?
|
|
204
|
+
|
|
205
|
+
pipeline_instance = self
|
|
206
|
+
->(event, *args) {
|
|
207
|
+
class_handlers.each { |h| pipeline_instance.instance_exec(event, *args, &h) }
|
|
208
|
+
passed_in&.call(event, *args)
|
|
209
|
+
}
|
|
210
|
+
end
|
|
211
|
+
|
|
162
212
|
def execute_step(step)
|
|
163
213
|
if step.tribunal?
|
|
164
214
|
agent_streams = { token: @token_stream, agent: @agent_event_stream, tribunal: @tribunal_event_stream }.compact
|
data/lib/active_harness.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.23
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- the-teacher
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|