legion-gaia 0.9.59 → 0.9.60
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/CHANGELOG.md +7 -0
- data/lib/legion/gaia/behavioral_synapse.rb +335 -0
- data/lib/legion/gaia/version.rb +1 -1
- data/lib/legion/gaia.rb +61 -0
- 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: 2fc298cf66a402057db523e07a53e4bde4a30a7b3e8090b819b8b1f64569c589
|
|
4
|
+
data.tar.gz: 6226b64c0fc7ace2122f014784b21f074fae19ebfc214360b5288cfc7d9e80d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d74b4bc1634a51f8759771f8c77711a58c92627487ca3c98e2af22c2244ab1c3efc63de5364656179d3232aa8d9ee9a0faa48f7a35d28dfa34aef21e493351bb
|
|
7
|
+
data.tar.gz: 1d4f2872dc1b9e332160f34676dc1f5950dd6a13b2d72a050c4c65b693546e8743309ba34148cdc3d18cb103127279c5dd8d055832ad2db41e182dcb0adf17d5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.60] - 2026-07-16
|
|
4
|
+
### Added
|
|
5
|
+
- H2: BehavioralSynapse store with vendored Confidence math (lifecycle: crystallize, lazy decay with intensity resistance, grade via record_outcome, pain at 3 consecutive failures → dampened + event emission)
|
|
6
|
+
- `record_response_applied` public method — attribution facade for legion-llm's final-delivery hook; persists to Apollo Local with self-knowledge/attribution tags
|
|
7
|
+
- Grading path in `evaluate_calibration`: reaction_score >= 0.6 → :success, <= 0.4 → :failure; maps each applied synapse_id via BehavioralSynapse.record_outcome scaled by imprint multiplier
|
|
8
|
+
- TrackerPersistence integration for BehavioralSynapse: Tracker wrapper, hydrate at boot alongside bonds, flush_dirty/flush_all on heartbeat/shutdown
|
|
9
|
+
|
|
3
10
|
## [0.9.59] - 2026-07-16
|
|
4
11
|
### Added
|
|
5
12
|
- H0: Partner identity as first-class store dimension — `record_interaction_trace` adds `partner:<identity>` domain tag; `SessionStore#erase_partner!(identity:)`; `AuditObserver` tool patterns keyed per-identity with `tool_patterns_for(identity)` and `erase_partner!(identity:)`; `TrackerPersistence.register_tracker` is idempotent (re-registration is a no-op, safe for partial-boot resume); fail-closed: no partner state written to shared Postgres when Apollo local is unavailable
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'concurrent/hash'
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
require 'legion/json'
|
|
6
|
+
require 'legion/logging/helper'
|
|
7
|
+
|
|
8
|
+
module Legion
|
|
9
|
+
module Gaia
|
|
10
|
+
module BehavioralSynapse # rubocop:disable Metrics/ModuleLength
|
|
11
|
+
extend Legion::Logging::Helper
|
|
12
|
+
|
|
13
|
+
TO_APOLLO_TAGS = %w[self-knowledge behavior].freeze
|
|
14
|
+
|
|
15
|
+
# Vendored confidence math — identical constants to Legion::Extensions::Synapse::Helpers::Confidence.
|
|
16
|
+
# At runtime we delegate to the real module if it is loaded.
|
|
17
|
+
module Math
|
|
18
|
+
STARTING_SCORES = { explicit: 0.7, emergent: 0.3, seeded: 0.5 }.freeze
|
|
19
|
+
ADJUSTMENTS = {
|
|
20
|
+
success: 0.02,
|
|
21
|
+
failure: -0.05,
|
|
22
|
+
validation_failure: -0.03,
|
|
23
|
+
consecutive_bonus: 0.05
|
|
24
|
+
}.freeze
|
|
25
|
+
DECAY_RATE = 0.998
|
|
26
|
+
CONSECUTIVE_BONUS_THRESHOLD = 50
|
|
27
|
+
AUTONOMY_RANGES = {
|
|
28
|
+
observe: 0.0..0.3,
|
|
29
|
+
filter: 0.3..0.6,
|
|
30
|
+
transform: 0.6..0.8,
|
|
31
|
+
autonomous: 0.8..1.0
|
|
32
|
+
}.freeze
|
|
33
|
+
E_WEIGHT = 3.0
|
|
34
|
+
|
|
35
|
+
module_function
|
|
36
|
+
|
|
37
|
+
def starting_score(origin)
|
|
38
|
+
STARTING_SCORES.fetch(origin.to_sym, 0.3)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def adjust(confidence, event, consecutive_successes: 0)
|
|
42
|
+
delta = ADJUSTMENTS.fetch(event.to_sym, 0.0)
|
|
43
|
+
delta += ADJUSTMENTS[:consecutive_bonus] if consecutive_successes >= CONSECUTIVE_BONUS_THRESHOLD
|
|
44
|
+
(confidence + delta).clamp(0.0, 1.0)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def decay(confidence, hours: 1)
|
|
48
|
+
(confidence * (DECAY_RATE**hours)).clamp(0.0, 1.0)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def autonomy_mode(confidence)
|
|
52
|
+
AUTONOMY_RANGES.find { |_mode, range| range.cover?(confidence) }&.first || :observe
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@store = Concurrent::Hash.new
|
|
57
|
+
@dirty = false
|
|
58
|
+
@mutex = Mutex.new
|
|
59
|
+
|
|
60
|
+
module_function
|
|
61
|
+
|
|
62
|
+
# --- Confidence math delegation ---
|
|
63
|
+
|
|
64
|
+
def starting_score(origin)
|
|
65
|
+
if defined?(Legion::Extensions::Synapse::Helpers::Confidence)
|
|
66
|
+
Legion::Extensions::Synapse::Helpers::Confidence.starting_score(origin)
|
|
67
|
+
else
|
|
68
|
+
Math.starting_score(origin)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def adjust(confidence, event, consecutive_successes: 0)
|
|
73
|
+
if defined?(Legion::Extensions::Synapse::Helpers::Confidence)
|
|
74
|
+
Legion::Extensions::Synapse::Helpers::Confidence.adjust(confidence, event,
|
|
75
|
+
consecutive_successes: consecutive_successes)
|
|
76
|
+
else
|
|
77
|
+
Math.adjust(confidence, event, consecutive_successes: consecutive_successes)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def decay_confidence(confidence, hours: 1)
|
|
82
|
+
if defined?(Legion::Extensions::Synapse::Helpers::Confidence)
|
|
83
|
+
Legion::Extensions::Synapse::Helpers::Confidence.decay(confidence, hours: hours)
|
|
84
|
+
else
|
|
85
|
+
Math.decay(confidence, hours: hours)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def e_weight
|
|
90
|
+
if defined?(Legion::Extensions::Synapse::Helpers::Confidence)
|
|
91
|
+
Legion::Extensions::Synapse::Helpers::Confidence::E_WEIGHT
|
|
92
|
+
else
|
|
93
|
+
Math::E_WEIGHT
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# --- Public API ---
|
|
98
|
+
|
|
99
|
+
def for(identity:, domain:)
|
|
100
|
+
key = store_key(identity, domain)
|
|
101
|
+
entry = @store[key]
|
|
102
|
+
return nil unless entry
|
|
103
|
+
|
|
104
|
+
apply_lazy_decay(entry)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def crystallize(identity:, domain:, directive:, evidence_trace_ids: [], origin: 'emergent')
|
|
108
|
+
key = store_key(identity, domain)
|
|
109
|
+
@mutex.synchronize do
|
|
110
|
+
return @store[key] if @store[key]
|
|
111
|
+
|
|
112
|
+
conf = starting_score(origin.to_sym)
|
|
113
|
+
now = Time.now.utc
|
|
114
|
+
entry = {
|
|
115
|
+
id: SecureRandom.uuid,
|
|
116
|
+
identity: identity.to_s,
|
|
117
|
+
domain: domain.to_s,
|
|
118
|
+
origin: origin.to_s,
|
|
119
|
+
confidence: conf,
|
|
120
|
+
emotional_valence: 0.0,
|
|
121
|
+
emotional_intensity: 0.0,
|
|
122
|
+
consecutive_failures: 0,
|
|
123
|
+
consecutive_successes: 0,
|
|
124
|
+
directive: directive,
|
|
125
|
+
evidence_trace_ids: Array(evidence_trace_ids),
|
|
126
|
+
status: 'active',
|
|
127
|
+
last_applied_at: nil,
|
|
128
|
+
last_reinforced_at: now,
|
|
129
|
+
created_at: now
|
|
130
|
+
}
|
|
131
|
+
@store[key] = entry
|
|
132
|
+
@dirty = true
|
|
133
|
+
log.info("[gaia] synapse crystallized identity=#{identity} domain=#{domain} origin=#{origin} conf=#{conf}")
|
|
134
|
+
entry
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def record_outcome(id:, outcome:, multiplier: 1.0)
|
|
139
|
+
@mutex.synchronize do
|
|
140
|
+
entry = find_by_id(id)
|
|
141
|
+
return { found: false } unless entry
|
|
142
|
+
|
|
143
|
+
conf = entry[:confidence]
|
|
144
|
+
base_event = outcome.to_sym
|
|
145
|
+
new_conf = adjust(conf, base_event, consecutive_successes: entry[:consecutive_successes])
|
|
146
|
+
|
|
147
|
+
# Apply multiplier to delta (scale the delta, not the raw confidence)
|
|
148
|
+
raw_delta = new_conf - conf
|
|
149
|
+
scaled_conf = (conf + (raw_delta * multiplier)).clamp(0.0, 1.0)
|
|
150
|
+
|
|
151
|
+
if base_event == :success
|
|
152
|
+
entry[:consecutive_successes] = entry[:consecutive_successes].to_i + 1
|
|
153
|
+
entry[:consecutive_failures] = 0
|
|
154
|
+
elsif base_event == :failure
|
|
155
|
+
entry[:consecutive_failures] = entry[:consecutive_failures].to_i + 1
|
|
156
|
+
entry[:consecutive_successes] = 0
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
entry[:confidence] = scaled_conf
|
|
160
|
+
entry[:last_reinforced_at] = Time.now.utc
|
|
161
|
+
@dirty = true
|
|
162
|
+
|
|
163
|
+
apply_pain(entry) if entry[:consecutive_failures].to_i >= 3
|
|
164
|
+
|
|
165
|
+
entry
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def all_for(identity:)
|
|
170
|
+
prefix = "#{identity}:"
|
|
171
|
+
@store.select { |key, _| key.start_with?(prefix) }.values
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def erase_partner!(identity:)
|
|
175
|
+
prefix = "#{identity}:"
|
|
176
|
+
keys = @store.keys.select { |key| key.start_with?(prefix) }
|
|
177
|
+
keys.each { |key| @store.delete(key) }
|
|
178
|
+
@mutex.synchronize { @dirty = true } unless keys.empty?
|
|
179
|
+
log.info("[gaia] BehavioralSynapse erased identity=#{identity} count=#{keys.size}")
|
|
180
|
+
keys.size
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def store
|
|
184
|
+
@store
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def reset!
|
|
188
|
+
@mutex.synchronize do
|
|
189
|
+
@store = Concurrent::Hash.new
|
|
190
|
+
@dirty = false
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# --- TrackerPersistence support ---
|
|
195
|
+
|
|
196
|
+
def dirty?
|
|
197
|
+
@dirty == true
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def mark_clean!
|
|
201
|
+
@dirty = false
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def to_apollo_entries
|
|
205
|
+
@store.values.map do |entry|
|
|
206
|
+
identity = entry[:identity]
|
|
207
|
+
{
|
|
208
|
+
content: Legion::JSON.dump(entry),
|
|
209
|
+
tags: TO_APOLLO_TAGS + ["partner:#{identity}"],
|
|
210
|
+
confidence: entry[:confidence] || 0.3,
|
|
211
|
+
access_scope: 'local'
|
|
212
|
+
}
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def from_apollo(store: nil)
|
|
217
|
+
return unless store
|
|
218
|
+
|
|
219
|
+
result = store.query(text: 'behavior', tags: TO_APOLLO_TAGS)
|
|
220
|
+
return unless result.is_a?(Hash) && result[:success]
|
|
221
|
+
|
|
222
|
+
count = 0
|
|
223
|
+
result[:results]&.each do |entry|
|
|
224
|
+
hydrate_apollo_entry(entry)
|
|
225
|
+
count += 1
|
|
226
|
+
rescue StandardError => e
|
|
227
|
+
log.debug("[gaia] BehavioralSynapse skipped unparseable Apollo entry: #{e.message}")
|
|
228
|
+
end
|
|
229
|
+
log.info("[gaia] BehavioralSynapse hydrated from Apollo count=#{count}")
|
|
230
|
+
rescue StandardError => e
|
|
231
|
+
handle_exception(e, level: :warn, operation: 'gaia.behavioral_synapse.from_apollo')
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
private
|
|
235
|
+
|
|
236
|
+
def store_key(identity, domain)
|
|
237
|
+
"#{identity}:#{domain}"
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def find_by_id(id)
|
|
241
|
+
@store.values.find { |entry| entry[:id] == id }
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def apply_lazy_decay(entry)
|
|
245
|
+
return entry unless entry[:last_reinforced_at]
|
|
246
|
+
|
|
247
|
+
hours = (Time.now.utc - entry[:last_reinforced_at]) / 3600.0
|
|
248
|
+
intensity = entry[:emotional_intensity].to_f
|
|
249
|
+
effective_hours = hours / (1 + (intensity * e_weight))
|
|
250
|
+
return entry if effective_hours < 0.001
|
|
251
|
+
|
|
252
|
+
new_conf = decay_confidence(entry[:confidence], hours: effective_hours)
|
|
253
|
+
@mutex.synchronize do
|
|
254
|
+
entry[:confidence] = new_conf
|
|
255
|
+
entry[:last_reinforced_at] = Time.now.utc
|
|
256
|
+
@dirty = true
|
|
257
|
+
end
|
|
258
|
+
entry
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def apply_pain(entry)
|
|
262
|
+
entry[:status] = 'dampened'
|
|
263
|
+
entry[:confidence] = 0.29
|
|
264
|
+
entry[:consecutive_failures] = 0
|
|
265
|
+
entry[:consecutive_successes] = 0
|
|
266
|
+
log.warn("[gaia] synapse pain id=#{entry[:id]} domain=#{entry[:domain]} identity=#{entry[:identity]}")
|
|
267
|
+
|
|
268
|
+
if defined?(Legion::Events) && Legion::Events.respond_to?(:emit)
|
|
269
|
+
Legion::Events.emit('gaia.behavior.reverted',
|
|
270
|
+
id: entry[:id], identity: entry[:identity], domain: entry[:domain])
|
|
271
|
+
end
|
|
272
|
+
rescue StandardError => e
|
|
273
|
+
handle_exception(e, level: :warn, operation: 'gaia.behavioral_synapse.apply_pain', id: entry[:id])
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def hydrate_apollo_entry(raw_entry)
|
|
277
|
+
return unless raw_entry[:content].to_s.start_with?('{')
|
|
278
|
+
|
|
279
|
+
parsed = Legion::JSON.load(raw_entry[:content])
|
|
280
|
+
return unless parsed[:id] && parsed[:identity] && parsed[:domain]
|
|
281
|
+
|
|
282
|
+
key = store_key(parsed[:identity], parsed[:domain])
|
|
283
|
+
@mutex.synchronize do
|
|
284
|
+
@store[key] = {
|
|
285
|
+
id: parsed[:id],
|
|
286
|
+
identity: parsed[:identity].to_s,
|
|
287
|
+
domain: parsed[:domain].to_s,
|
|
288
|
+
origin: parsed[:origin].to_s,
|
|
289
|
+
confidence: parsed[:confidence].to_f,
|
|
290
|
+
emotional_valence: parsed[:emotional_valence].to_f,
|
|
291
|
+
emotional_intensity: parsed[:emotional_intensity].to_f,
|
|
292
|
+
consecutive_failures: parsed[:consecutive_failures].to_i,
|
|
293
|
+
consecutive_successes: parsed[:consecutive_successes].to_i,
|
|
294
|
+
directive: parsed[:directive],
|
|
295
|
+
evidence_trace_ids: Array(parsed[:evidence_trace_ids]),
|
|
296
|
+
status: parsed[:status].to_s,
|
|
297
|
+
last_applied_at: parsed[:last_applied_at],
|
|
298
|
+
last_reinforced_at: parse_time(parsed[:last_reinforced_at]),
|
|
299
|
+
created_at: parse_time(parsed[:created_at])
|
|
300
|
+
}
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def parse_time(value)
|
|
305
|
+
return value if value.is_a?(Time)
|
|
306
|
+
return nil if value.nil?
|
|
307
|
+
|
|
308
|
+
Time.parse(value.to_s)
|
|
309
|
+
rescue ArgumentError, TypeError
|
|
310
|
+
nil
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
module_function :store_key, :find_by_id, :apply_lazy_decay, :apply_pain, :hydrate_apollo_entry, :parse_time
|
|
314
|
+
|
|
315
|
+
# Wrapper class that satisfies TrackerPersistence interface
|
|
316
|
+
class Tracker
|
|
317
|
+
def dirty?
|
|
318
|
+
BehavioralSynapse.dirty?
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def mark_clean!
|
|
322
|
+
BehavioralSynapse.mark_clean!
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def to_apollo_entries
|
|
326
|
+
BehavioralSynapse.to_apollo_entries
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def from_apollo(store: nil)
|
|
330
|
+
BehavioralSynapse.from_apollo(store: store)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
data/lib/legion/gaia/version.rb
CHANGED
data/lib/legion/gaia.rb
CHANGED
|
@@ -32,6 +32,7 @@ require 'legion/gaia/proactive'
|
|
|
32
32
|
require 'legion/gaia/offline_handler'
|
|
33
33
|
require 'legion/gaia/proactive_dispatcher'
|
|
34
34
|
require 'legion/gaia/bond_registry'
|
|
35
|
+
require 'legion/gaia/behavioral_synapse'
|
|
35
36
|
require 'legion/gaia/tracker_persistence'
|
|
36
37
|
require 'legion/gaia/router'
|
|
37
38
|
|
|
@@ -118,6 +119,7 @@ module Legion
|
|
|
118
119
|
@absence_pattern_checked_at = nil
|
|
119
120
|
@last_valences = nil
|
|
120
121
|
@last_response_at = nil
|
|
122
|
+
@last_applied = nil
|
|
121
123
|
@tick_history = nil
|
|
122
124
|
@tick_count = nil
|
|
123
125
|
@started_at = nil
|
|
@@ -258,6 +260,28 @@ module Legion
|
|
|
258
260
|
handle_exception(e, level: :warn, operation: 'gaia.record_advisory_meta', advisory_id: advisory_id)
|
|
259
261
|
end
|
|
260
262
|
|
|
263
|
+
def record_response_applied(advisory_id:, identity:, applied:)
|
|
264
|
+
return unless started?
|
|
265
|
+
|
|
266
|
+
synapse_count = Array(applied[:behavioral_synapse_ids]).size
|
|
267
|
+
log.info("[gaia] attribution advisory_id=#{advisory_id} identity=#{identity} synapses=#{synapse_count}")
|
|
268
|
+
|
|
269
|
+
store = apollo_local_store
|
|
270
|
+
store&.upsert(
|
|
271
|
+
content: Legion::JSON.dump(applied.merge(advisory_id: advisory_id, identity: identity)),
|
|
272
|
+
tags: %w[self-knowledge attribution] + ["partner:#{identity}"],
|
|
273
|
+
source_channel: 'gaia',
|
|
274
|
+
confidence: 0.9,
|
|
275
|
+
access_scope: 'local'
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
@last_applied = applied.merge(advisory_id: advisory_id, identity: identity)
|
|
279
|
+
{ recorded: true, advisory_id: advisory_id }
|
|
280
|
+
rescue StandardError => e
|
|
281
|
+
handle_exception(e, level: :warn, operation: 'gaia.record_response_applied', advisory_id: advisory_id)
|
|
282
|
+
{ recorded: false }
|
|
283
|
+
end
|
|
284
|
+
|
|
261
285
|
def status
|
|
262
286
|
return { started: false } unless started?
|
|
263
287
|
|
|
@@ -377,6 +401,7 @@ module Legion
|
|
|
377
401
|
@registry.discover
|
|
378
402
|
boot_channels
|
|
379
403
|
boot_agent_bridge
|
|
404
|
+
register_behavioral_synapse_tracker
|
|
380
405
|
hydrate_from_apollo_local
|
|
381
406
|
register_provisional_partner_prior
|
|
382
407
|
log.info('Legion::Gaia agent mode boot complete')
|
|
@@ -638,6 +663,8 @@ module Legion
|
|
|
638
663
|
log.info("[gaia] calibration identity=#{observation[:identity]} " \
|
|
639
664
|
"deltas=#{result[:deltas].keys.join(',')}")
|
|
640
665
|
end
|
|
666
|
+
|
|
667
|
+
grade_behavioral_synapses(result) if result.is_a?(Hash) && result[:reaction_score]
|
|
641
668
|
rescue StandardError => e
|
|
642
669
|
handle_exception(e, level: :warn, operation: 'gaia.evaluate_calibration', identity: observation[:identity])
|
|
643
670
|
end
|
|
@@ -655,6 +682,31 @@ module Legion
|
|
|
655
682
|
@calibration_runner = runner
|
|
656
683
|
end
|
|
657
684
|
|
|
685
|
+
def grade_behavioral_synapses(calibration_result)
|
|
686
|
+
return unless @last_applied.is_a?(Hash)
|
|
687
|
+
|
|
688
|
+
synapse_ids = Array(@last_applied[:behavioral_synapse_ids])
|
|
689
|
+
return if synapse_ids.empty?
|
|
690
|
+
|
|
691
|
+
reaction_score = calibration_result[:reaction_score].to_f
|
|
692
|
+
outcome = if reaction_score >= 0.6
|
|
693
|
+
:success
|
|
694
|
+
elsif reaction_score <= 0.4
|
|
695
|
+
:failure
|
|
696
|
+
end
|
|
697
|
+
return unless outcome
|
|
698
|
+
|
|
699
|
+
multiplier = fetch_imprint_multiplier
|
|
700
|
+
synapse_ids.each do |sid|
|
|
701
|
+
BehavioralSynapse.record_outcome(id: sid, outcome: outcome, multiplier: multiplier)
|
|
702
|
+
end
|
|
703
|
+
log.info("[gaia] graded synapses count=#{synapse_ids.size} outcome=#{outcome} " \
|
|
704
|
+
"reaction_score=#{reaction_score.round(3)}")
|
|
705
|
+
@last_applied = nil
|
|
706
|
+
rescue StandardError => e
|
|
707
|
+
handle_exception(e, level: :warn, operation: 'gaia.grade_behavioral_synapses')
|
|
708
|
+
end
|
|
709
|
+
|
|
658
710
|
def fetch_imprint_multiplier
|
|
659
711
|
# Soft-dep on lex-coldstart; returns settings default if unavailable
|
|
660
712
|
return 1.0 unless defined?(Legion::Extensions::Coldstart) && Legion::Extensions::Coldstart.connected?
|
|
@@ -835,12 +887,21 @@ module Legion
|
|
|
835
887
|
handle_exception(e, level: :debug, operation: 'gaia.flush_trackers_on_shutdown')
|
|
836
888
|
end
|
|
837
889
|
|
|
890
|
+
def register_behavioral_synapse_tracker
|
|
891
|
+
TrackerPersistence.register_tracker(
|
|
892
|
+
:behavioral_synapse,
|
|
893
|
+
tracker: BehavioralSynapse::Tracker.new,
|
|
894
|
+
tags: %w[self-knowledge behavior]
|
|
895
|
+
)
|
|
896
|
+
end
|
|
897
|
+
|
|
838
898
|
def hydrate_from_apollo_local
|
|
839
899
|
store = apollo_local_store
|
|
840
900
|
return unless store
|
|
841
901
|
|
|
842
902
|
TrackerPersistence.hydrate_all(store: store)
|
|
843
903
|
BondRegistry.hydrate_from_apollo(store: store)
|
|
904
|
+
BehavioralSynapse.from_apollo(store: store)
|
|
844
905
|
log.info('Legion::Gaia hydrated trackers and bond registry from Apollo Local')
|
|
845
906
|
rescue StandardError => e
|
|
846
907
|
handle_exception(e, level: :warn, operation: 'gaia.hydrate_from_apollo_local')
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legion-gaia
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.60
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -417,6 +417,7 @@ files:
|
|
|
417
417
|
- lib/legion/gaia/actors/heartbeat.rb
|
|
418
418
|
- lib/legion/gaia/advisory.rb
|
|
419
419
|
- lib/legion/gaia/audit_observer.rb
|
|
420
|
+
- lib/legion/gaia/behavioral_synapse.rb
|
|
420
421
|
- lib/legion/gaia/bond_registry.rb
|
|
421
422
|
- lib/legion/gaia/bond_store.rb
|
|
422
423
|
- lib/legion/gaia/bond_tracker.rb
|