active_harness 0.2.26 → 0.2.28
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/prompt.rb +8 -3
- data/lib/active_harness/agent.rb +23 -7
- data/lib/active_harness/costs.rb +12 -5
- data/lib/active_harness/memory.rb +16 -3
- data/lib/active_harness/providers/anthropic.rb +8 -1
- data/lib/active_harness/result.rb +2 -1
- data/lib/active_harness/tribunal.rb +4 -1
- 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: ce3118f24bd7e48d024a4b5c14d0e01531e453be2f56479918759dc425bc932d
|
|
4
|
+
data.tar.gz: 561d4d3f46b98a224d53ed964c319fbb1a73bffc5c6a3dd189d01e24d1af2427
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 29f2a581e10290eb176a80e142a120ba5e9c9f0bc9e5decfacb7237fd11f35621bd35a64ebca2999399edd5f5865aec343edb8a11c8040e539329857b51c6d23
|
|
7
|
+
data.tar.gz: 86ef4129befa8b92af78f5666a4f6346f9d9eba46f39e880cf9968d03b7339f6de9620a0b558d67571bd4650425da0ea3455b8251c356e366717c87233dc3516
|
|
@@ -49,10 +49,15 @@ module ActiveHarness
|
|
|
49
49
|
prompt
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# Injects agent state into a prompt class instance before #call.
|
|
53
|
+
# Available in prompt classes: @input, @context, @params, @memory, @context_window, @config
|
|
52
54
|
def inject_agent_state(obj)
|
|
53
|
-
obj.instance_variable_set(:@input,
|
|
54
|
-
obj.instance_variable_set(:@context,
|
|
55
|
-
obj.instance_variable_set(:@
|
|
55
|
+
obj.instance_variable_set(:@input, @input)
|
|
56
|
+
obj.instance_variable_set(:@context, @context)
|
|
57
|
+
obj.instance_variable_set(:@params, @params)
|
|
58
|
+
obj.instance_variable_set(:@config, @config)
|
|
59
|
+
obj.instance_variable_set(:@memory, @memory)
|
|
60
|
+
obj.instance_variable_set(:@context_window, @context_window)
|
|
56
61
|
end
|
|
57
62
|
end
|
|
58
63
|
end
|
data/lib/active_harness/agent.rb
CHANGED
|
@@ -14,6 +14,7 @@ module ActiveHarness
|
|
|
14
14
|
input: nil,
|
|
15
15
|
context: {},
|
|
16
16
|
params: {},
|
|
17
|
+
memory: nil,
|
|
17
18
|
models: nil,
|
|
18
19
|
streams: {}
|
|
19
20
|
)
|
|
@@ -21,6 +22,7 @@ module ActiveHarness
|
|
|
21
22
|
input: input,
|
|
22
23
|
context: context,
|
|
23
24
|
params: params,
|
|
25
|
+
memory: memory,
|
|
24
26
|
models: models,
|
|
25
27
|
streams: streams
|
|
26
28
|
).call
|
|
@@ -49,8 +51,10 @@ module ActiveHarness
|
|
|
49
51
|
# -------------------------------------------------------------------------
|
|
50
52
|
attr_accessor :input,
|
|
51
53
|
:context,
|
|
52
|
-
:params
|
|
54
|
+
:params,
|
|
55
|
+
:memory
|
|
53
56
|
attr_reader :result,
|
|
57
|
+
:context_window,
|
|
54
58
|
:token_stream,
|
|
55
59
|
:event_stream
|
|
56
60
|
|
|
@@ -63,6 +67,7 @@ module ActiveHarness
|
|
|
63
67
|
input: nil,
|
|
64
68
|
context: {},
|
|
65
69
|
params: {},
|
|
70
|
+
memory: nil,
|
|
66
71
|
models: nil,
|
|
67
72
|
streams: {}
|
|
68
73
|
)
|
|
@@ -71,9 +76,11 @@ module ActiveHarness
|
|
|
71
76
|
normalize_input!
|
|
72
77
|
@context = context
|
|
73
78
|
@params = params
|
|
74
|
-
@
|
|
75
|
-
@
|
|
76
|
-
@
|
|
79
|
+
@memory = memory
|
|
80
|
+
@models_override = Array(models) if models
|
|
81
|
+
@context_window = lookup_context_window(self.models.to_a.first)
|
|
82
|
+
@token_stream = streams[:token]
|
|
83
|
+
@event_stream = streams[:agent]
|
|
77
84
|
fire(:setup)
|
|
78
85
|
end
|
|
79
86
|
|
|
@@ -139,9 +146,10 @@ module ActiveHarness
|
|
|
139
146
|
end
|
|
140
147
|
|
|
141
148
|
def build_result(response, entry, attempts, elapsed)
|
|
142
|
-
raw
|
|
149
|
+
raw = response[:content]
|
|
143
150
|
processed = parse_output(raw)
|
|
144
|
-
usage
|
|
151
|
+
usage = response[:usage]
|
|
152
|
+
cw = lookup_context_window(entry)
|
|
145
153
|
|
|
146
154
|
Result.new(
|
|
147
155
|
input: @input,
|
|
@@ -155,10 +163,18 @@ module ActiveHarness
|
|
|
155
163
|
attempts: attempts,
|
|
156
164
|
execution_time: elapsed,
|
|
157
165
|
usage: usage,
|
|
158
|
-
cost: calculate_cost(entry[:model], usage)
|
|
166
|
+
cost: calculate_cost(entry[:model], usage),
|
|
167
|
+
context_window: cw
|
|
159
168
|
)
|
|
160
169
|
end
|
|
161
170
|
|
|
171
|
+
def lookup_context_window(entry)
|
|
172
|
+
return nil unless entry
|
|
173
|
+
Costs.find(entry[:model])&.context_window
|
|
174
|
+
rescue StandardError
|
|
175
|
+
nil
|
|
176
|
+
end
|
|
177
|
+
|
|
162
178
|
def normalize_input!
|
|
163
179
|
return if @config.fetch(:normalize_input, true) == false
|
|
164
180
|
@input = @input&.strip&.gsub(/\s+/, " ")
|
data/lib/active_harness/costs.rb
CHANGED
|
@@ -62,12 +62,15 @@ module ActiveHarness
|
|
|
62
62
|
:output_per_million,
|
|
63
63
|
:cache_read_input_per_million,
|
|
64
64
|
:cache_write_input_per_million,
|
|
65
|
+
:context_window,
|
|
66
|
+
:max_output_tokens,
|
|
65
67
|
keyword_init: true
|
|
66
68
|
) do
|
|
67
69
|
def inspect
|
|
68
70
|
parts = ["id=#{id.inspect}", "provider=#{provider.inspect}"]
|
|
69
71
|
parts << "input=$#{input_per_million}/M" if input_per_million
|
|
70
72
|
parts << "output=$#{output_per_million}/M" if output_per_million
|
|
73
|
+
parts << "ctx=#{context_window}" if context_window
|
|
71
74
|
"#<ModelCost #{parts.join(' ')}>"
|
|
72
75
|
end
|
|
73
76
|
end
|
|
@@ -230,10 +233,12 @@ module ActiveHarness
|
|
|
230
233
|
}.compact
|
|
231
234
|
|
|
232
235
|
{
|
|
233
|
-
id:
|
|
234
|
-
name:
|
|
235
|
-
provider:
|
|
236
|
-
|
|
236
|
+
id: m[:id],
|
|
237
|
+
name: m[:name] || m[:id],
|
|
238
|
+
provider: ah_provider,
|
|
239
|
+
context_window: m[:context_window] || m.dig(:limit, :context),
|
|
240
|
+
max_output_tokens: m[:max_output_tokens] || m.dig(:limit, :output),
|
|
241
|
+
pricing: standard.any? ? { text_tokens: { standard: standard } } : {}
|
|
237
242
|
}
|
|
238
243
|
end
|
|
239
244
|
end
|
|
@@ -248,7 +253,9 @@ module ActiveHarness
|
|
|
248
253
|
input_per_million: standard[:input_per_million],
|
|
249
254
|
output_per_million: standard[:output_per_million],
|
|
250
255
|
cache_read_input_per_million: standard[:cache_read_input_per_million],
|
|
251
|
-
cache_write_input_per_million: standard[:cache_write_input_per_million]
|
|
256
|
+
cache_write_input_per_million: standard[:cache_write_input_per_million],
|
|
257
|
+
context_window: raw[:context_window],
|
|
258
|
+
max_output_tokens: raw[:max_output_tokens]
|
|
252
259
|
)
|
|
253
260
|
end
|
|
254
261
|
|
|
@@ -138,13 +138,15 @@ module ActiveHarness
|
|
|
138
138
|
|
|
139
139
|
# Returns messages array for LLM consumption, respecting depth.
|
|
140
140
|
# Optional filters:
|
|
141
|
-
# filter:
|
|
142
|
-
# since:
|
|
143
|
-
|
|
141
|
+
# filter: ->(turn) { turn[:agent] == "SupportAgent" }
|
|
142
|
+
# since: Time.now - 3600
|
|
143
|
+
# token_budget: 4000 # rough limit (chars / 4 estimate); trims oldest turns first
|
|
144
|
+
def to_messages(filter: nil, since: nil, token_budget: nil)
|
|
144
145
|
turns = @turns.dup
|
|
145
146
|
turns.select! { |t| filter.call(t) } if filter
|
|
146
147
|
turns.select! { |t| after?(t, since) } if since
|
|
147
148
|
turns = turns.last(@depth) if @depth
|
|
149
|
+
turns = trim_to_token_budget(turns, token_budget) if token_budget
|
|
148
150
|
|
|
149
151
|
turns.flat_map do |t|
|
|
150
152
|
[
|
|
@@ -210,5 +212,16 @@ module ActiveHarness
|
|
|
210
212
|
turn_time = Time.parse(turn[:at].to_s) rescue nil
|
|
211
213
|
turn_time ? turn_time >= time : true
|
|
212
214
|
end
|
|
215
|
+
|
|
216
|
+
def trim_to_token_budget(turns, budget)
|
|
217
|
+
return turns if turns.empty?
|
|
218
|
+
|
|
219
|
+
total = 0
|
|
220
|
+
turns.reverse.take_while do |t|
|
|
221
|
+
tokens = ((t[:request].to_s.length + t[:response].to_s.length) / 4.0).ceil
|
|
222
|
+
total += tokens
|
|
223
|
+
total <= budget
|
|
224
|
+
end.reverse
|
|
225
|
+
end
|
|
213
226
|
end
|
|
214
227
|
end
|
|
@@ -25,7 +25,14 @@ module ActiveHarness
|
|
|
25
25
|
"anthropic-version" => ANTHROPIC_VERSION
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
return call_streaming(
|
|
28
|
+
return call_streaming(
|
|
29
|
+
url: config.anthropic_api_url,
|
|
30
|
+
headers: headers,
|
|
31
|
+
body: body,
|
|
32
|
+
stream: stream,
|
|
33
|
+
provider: :anthropic,
|
|
34
|
+
model: model
|
|
35
|
+
) if stream
|
|
29
36
|
|
|
30
37
|
raw = post_json(URI(config.anthropic_api_url), headers: headers, body: body)
|
|
31
38
|
data = parse!(raw)
|
|
@@ -11,7 +11,7 @@ module ActiveHarness
|
|
|
11
11
|
:input,
|
|
12
12
|
:output,
|
|
13
13
|
:processed,
|
|
14
|
-
:system_prompt,
|
|
14
|
+
:system_prompt,
|
|
15
15
|
:provider, :model,
|
|
16
16
|
:temperature,
|
|
17
17
|
:model_list,
|
|
@@ -19,5 +19,6 @@ module ActiveHarness
|
|
|
19
19
|
:execution_time,
|
|
20
20
|
:usage,
|
|
21
21
|
:cost,
|
|
22
|
+
:context_window,
|
|
22
23
|
keyword_init: true)
|
|
23
24
|
end
|
|
@@ -48,7 +48,8 @@ module ActiveHarness
|
|
|
48
48
|
# -------------------------------------------------------------------------
|
|
49
49
|
attr_accessor :input,
|
|
50
50
|
:context,
|
|
51
|
-
:params
|
|
51
|
+
:params,
|
|
52
|
+
:memory
|
|
52
53
|
attr_reader :results,
|
|
53
54
|
:errors,
|
|
54
55
|
:verdict,
|
|
@@ -62,6 +63,7 @@ module ActiveHarness
|
|
|
62
63
|
input: nil,
|
|
63
64
|
context: {},
|
|
64
65
|
params: {},
|
|
66
|
+
memory: nil,
|
|
65
67
|
agents: nil,
|
|
66
68
|
timeout: 7,
|
|
67
69
|
streams: {},
|
|
@@ -72,6 +74,7 @@ module ActiveHarness
|
|
|
72
74
|
@input = input
|
|
73
75
|
@context = context
|
|
74
76
|
@params = params
|
|
77
|
+
@memory = memory
|
|
75
78
|
@agents = agents || config[:agents]
|
|
76
79
|
@timeout = timeout
|
|
77
80
|
@process_block = config[:process]
|
data/lib/active_harness.rb
CHANGED