lex-synapse 0.2.2
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 +7 -0
- data/.github/workflows/ci.yml +16 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +53 -0
- data/CHANGELOG.md +36 -0
- data/CLAUDE.md +98 -0
- data/Gemfile +16 -0
- data/README.md +156 -0
- data/Rakefile +7 -0
- data/lex-synapse.gemspec +31 -0
- data/lib/legion/extensions/synapse/actors/crystallize.rb +31 -0
- data/lib/legion/extensions/synapse/actors/decay.rb +31 -0
- data/lib/legion/extensions/synapse/actors/evaluate.rb +23 -0
- data/lib/legion/extensions/synapse/actors/homeostasis.rb +31 -0
- data/lib/legion/extensions/synapse/actors/pain.rb +23 -0
- data/lib/legion/extensions/synapse/client.rb +62 -0
- data/lib/legion/extensions/synapse/data/migrations/001_create_synapses.rb +26 -0
- data/lib/legion/extensions/synapse/data/migrations/002_create_synapse_mutations.rb +21 -0
- data/lib/legion/extensions/synapse/data/migrations/003_create_synapse_signals.rb +20 -0
- data/lib/legion/extensions/synapse/data/models/synapse.rb +16 -0
- data/lib/legion/extensions/synapse/data/models/synapse_mutation.rb +15 -0
- data/lib/legion/extensions/synapse/data/models/synapse_signal.rb +15 -0
- data/lib/legion/extensions/synapse/helpers/confidence.rb +75 -0
- data/lib/legion/extensions/synapse/helpers/homeostasis.rb +43 -0
- data/lib/legion/extensions/synapse/helpers/relationship_wrapper.rb +42 -0
- data/lib/legion/extensions/synapse/runners/crystallize.rb +45 -0
- data/lib/legion/extensions/synapse/runners/dream.rb +106 -0
- data/lib/legion/extensions/synapse/runners/evaluate.rb +101 -0
- data/lib/legion/extensions/synapse/runners/gaia_report.rb +68 -0
- data/lib/legion/extensions/synapse/runners/mutate.rb +67 -0
- data/lib/legion/extensions/synapse/runners/pain.rb +78 -0
- data/lib/legion/extensions/synapse/runners/promote.rb +79 -0
- data/lib/legion/extensions/synapse/runners/report.rb +50 -0
- data/lib/legion/extensions/synapse/runners/retrieve.rb +61 -0
- data/lib/legion/extensions/synapse/runners/revert.rb +58 -0
- data/lib/legion/extensions/synapse/transport/exchanges/synapse.rb +16 -0
- data/lib/legion/extensions/synapse/transport/messages/pain.rb +23 -0
- data/lib/legion/extensions/synapse/transport/messages/signal.rb +23 -0
- data/lib/legion/extensions/synapse/transport/queues/evaluate.rb +19 -0
- data/lib/legion/extensions/synapse/transport/queues/pain.rb +19 -0
- data/lib/legion/extensions/synapse/transport.rb +18 -0
- data/lib/legion/extensions/synapse/version.rb +9 -0
- data/lib/legion/extensions/synapse.rb +20 -0
- metadata +118 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
create_table(:synapse_mutations) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
foreign_key :synapse_id, :synapses, null: false, index: true
|
|
8
|
+
Integer :version, null: false
|
|
9
|
+
String :mutation_type, null: false, size: 50
|
|
10
|
+
String :before_state, text: true
|
|
11
|
+
String :after_state, text: true
|
|
12
|
+
String :trigger, null: false, size: 50
|
|
13
|
+
String :outcome, size: 50
|
|
14
|
+
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
down do
|
|
19
|
+
drop_table :synapse_mutations
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
create_table(:synapse_signals) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
foreign_key :synapse_id, :synapses, null: false, index: true
|
|
8
|
+
Integer :task_id
|
|
9
|
+
TrueClass :passed_attention, default: false
|
|
10
|
+
TrueClass :transform_success, default: false
|
|
11
|
+
String :downstream_outcome, size: 50
|
|
12
|
+
Integer :latency_ms
|
|
13
|
+
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
down do
|
|
18
|
+
drop_table :synapse_signals
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Synapse
|
|
6
|
+
module Data
|
|
7
|
+
module Model
|
|
8
|
+
class Synapse < Sequel::Model(:synapses)
|
|
9
|
+
one_to_many :mutations, class: 'Legion::Extensions::Synapse::Data::Model::SynapseMutation'
|
|
10
|
+
one_to_many :signals, class: 'Legion::Extensions::Synapse::Data::Model::SynapseSignal'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Synapse
|
|
6
|
+
module Data
|
|
7
|
+
module Model
|
|
8
|
+
class SynapseMutation < Sequel::Model(:synapse_mutations)
|
|
9
|
+
many_to_one :synapse, class: 'Legion::Extensions::Synapse::Data::Model::Synapse'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Synapse
|
|
6
|
+
module Data
|
|
7
|
+
module Model
|
|
8
|
+
class SynapseSignal < Sequel::Model(:synapse_signals)
|
|
9
|
+
many_to_one :synapse, class: 'Legion::Extensions::Synapse::Data::Model::Synapse'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Synapse
|
|
6
|
+
module Helpers
|
|
7
|
+
module Confidence
|
|
8
|
+
VALID_STATUSES = %w[active observing dampened].freeze
|
|
9
|
+
EVALUABLE_STATUSES = %w[active observing].freeze
|
|
10
|
+
VALID_ORIGINS = %w[explicit emergent seeded].freeze
|
|
11
|
+
VALID_OUTCOMES = %w[success failed].freeze
|
|
12
|
+
|
|
13
|
+
STARTING_SCORES = { explicit: 0.7, emergent: 0.3, seeded: 0.5 }.freeze
|
|
14
|
+
ADJUSTMENTS = {
|
|
15
|
+
success: 0.02,
|
|
16
|
+
failure: -0.05,
|
|
17
|
+
validation_failure: -0.03,
|
|
18
|
+
consecutive_bonus: 0.05
|
|
19
|
+
}.freeze
|
|
20
|
+
DECAY_RATE = 0.998
|
|
21
|
+
CONSECUTIVE_BONUS_THRESHOLD = 50
|
|
22
|
+
|
|
23
|
+
AUTONOMY_RANGES = {
|
|
24
|
+
observe: 0.0..0.3,
|
|
25
|
+
filter: 0.3..0.6,
|
|
26
|
+
transform: 0.6..0.8,
|
|
27
|
+
autonomous: 0.8..1.0
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
def starting_score(origin)
|
|
32
|
+
STARTING_SCORES.fetch(origin.to_sym, 0.5)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def adjust(confidence, event, consecutive_successes: 0)
|
|
36
|
+
delta = ADJUSTMENTS.fetch(event, 0)
|
|
37
|
+
result = confidence + delta
|
|
38
|
+
result += ADJUSTMENTS[:consecutive_bonus] if event == :success && consecutive_successes > CONSECUTIVE_BONUS_THRESHOLD
|
|
39
|
+
clamp(result)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def decay(confidence, hours: 1)
|
|
43
|
+
clamp(confidence * (DECAY_RATE**hours))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def autonomy_mode(confidence)
|
|
47
|
+
AUTONOMY_RANGES.each do |mode, range|
|
|
48
|
+
return mode if range.include?(confidence)
|
|
49
|
+
end
|
|
50
|
+
:observe
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def can_filter?(confidence)
|
|
54
|
+
confidence >= 0.3
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def can_transform?(confidence)
|
|
58
|
+
confidence >= 0.6
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def can_self_modify?(confidence)
|
|
62
|
+
confidence >= 0.8
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def clamp(value)
|
|
68
|
+
value.clamp(0.0, 1.0)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Synapse
|
|
6
|
+
module Helpers
|
|
7
|
+
module Homeostasis
|
|
8
|
+
SPIKE_MULTIPLIER = 3.0
|
|
9
|
+
SPIKE_DURATION_SECONDS = 60
|
|
10
|
+
DROUGHT_MULTIPLIER = 10.0
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def spike?(current_throughput, baseline_throughput, duration_seconds: 0)
|
|
14
|
+
return false if baseline_throughput <= 0
|
|
15
|
+
|
|
16
|
+
current_throughput > (baseline_throughput * SPIKE_MULTIPLIER) &&
|
|
17
|
+
duration_seconds >= SPIKE_DURATION_SECONDS
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def drought?(current_throughput, baseline_throughput, silent_seconds: 0)
|
|
21
|
+
return false if baseline_throughput <= 0
|
|
22
|
+
|
|
23
|
+
avg_interval = 60.0 / baseline_throughput
|
|
24
|
+
current_throughput.zero? && silent_seconds >= (avg_interval * DROUGHT_MULTIPLIER)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update_baseline(current_baseline, new_sample, alpha: 0.1)
|
|
28
|
+
((1 - alpha) * current_baseline) + (alpha * new_sample)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def should_dampen?(current_throughput, baseline_throughput, duration_seconds: 0)
|
|
32
|
+
spike?(current_throughput, baseline_throughput, duration_seconds: duration_seconds)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def should_flag_for_review?(current_throughput, baseline_throughput, silent_seconds: 0)
|
|
36
|
+
drought?(current_throughput, baseline_throughput, silent_seconds: silent_seconds)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../data/models/synapse'
|
|
4
|
+
require_relative 'confidence'
|
|
5
|
+
|
|
6
|
+
module Legion
|
|
7
|
+
module Extensions
|
|
8
|
+
module Synapse
|
|
9
|
+
module Helpers
|
|
10
|
+
module RelationshipWrapper
|
|
11
|
+
class << self
|
|
12
|
+
def wrap(relationship)
|
|
13
|
+
existing = Data::Model::Synapse.where(relationship_id: relationship[:id]).first
|
|
14
|
+
return existing if existing
|
|
15
|
+
|
|
16
|
+
Data::Model::Synapse.create(
|
|
17
|
+
source_function_id: relationship[:trigger_function_id],
|
|
18
|
+
target_function_id: relationship[:function_id],
|
|
19
|
+
relationship_id: relationship[:id],
|
|
20
|
+
attention: relationship[:conditions],
|
|
21
|
+
transform: relationship[:transformation],
|
|
22
|
+
routing_strategy: 'direct',
|
|
23
|
+
origin: 'explicit',
|
|
24
|
+
confidence: Confidence.starting_score(:explicit),
|
|
25
|
+
status: 'active'
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def unwrap(synapse_id)
|
|
30
|
+
synapse = Data::Model::Synapse[synapse_id]
|
|
31
|
+
return { success: false, error: 'synapse not found' } unless synapse
|
|
32
|
+
return { success: false, error: 'not a wrapped relationship' } unless synapse.relationship_id
|
|
33
|
+
|
|
34
|
+
synapse.destroy
|
|
35
|
+
{ success: true, relationship_id: synapse.relationship_id }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/confidence'
|
|
4
|
+
require_relative '../data/models/synapse'
|
|
5
|
+
|
|
6
|
+
module Legion
|
|
7
|
+
module Extensions
|
|
8
|
+
module Synapse
|
|
9
|
+
module Runners
|
|
10
|
+
module Crystallize
|
|
11
|
+
EMERGENCE_THRESHOLD = 20
|
|
12
|
+
|
|
13
|
+
def crystallize(signal_pairs: [], threshold: EMERGENCE_THRESHOLD)
|
|
14
|
+
created = []
|
|
15
|
+
|
|
16
|
+
signal_pairs.each do |pair|
|
|
17
|
+
next if pair[:count] < threshold
|
|
18
|
+
next if synapse_exists?(pair[:source_function_id], pair[:target_function_id])
|
|
19
|
+
|
|
20
|
+
synapse = Data::Model::Synapse.create(
|
|
21
|
+
source_function_id: pair[:source_function_id],
|
|
22
|
+
target_function_id: pair[:target_function_id],
|
|
23
|
+
origin: 'emergent',
|
|
24
|
+
confidence: Helpers::Confidence.starting_score(:emergent),
|
|
25
|
+
status: 'observing'
|
|
26
|
+
)
|
|
27
|
+
created << { id: synapse.id, source: pair[:source_function_id], target: pair[:target_function_id] }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
{ success: true, created: created, count: created.size }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def synapse_exists?(source_id, target_id)
|
|
36
|
+
Data::Model::Synapse.where(
|
|
37
|
+
source_function_id: source_id,
|
|
38
|
+
target_function_id: target_id
|
|
39
|
+
).any?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../data/models/synapse'
|
|
4
|
+
require_relative '../data/models/synapse_mutation'
|
|
5
|
+
require_relative '../helpers/confidence'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Extensions
|
|
9
|
+
module Synapse
|
|
10
|
+
module Runners
|
|
11
|
+
module Dream
|
|
12
|
+
def dream_replay(synapse_id: nil, **)
|
|
13
|
+
synapses = if synapse_id
|
|
14
|
+
s = Data::Model::Synapse[synapse_id]
|
|
15
|
+
s ? [s] : []
|
|
16
|
+
else
|
|
17
|
+
Data::Model::Synapse.where { version > 1 }.all
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
replays = synapses.map { |s| replay_mutations(s) }
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
success: true,
|
|
24
|
+
replays: replays,
|
|
25
|
+
count: replays.size
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def dream_simulate(synapse_id:, mutation_type:, changes:, **)
|
|
30
|
+
synapse = Data::Model::Synapse[synapse_id]
|
|
31
|
+
return { success: false, error: 'synapse not found' } unless synapse
|
|
32
|
+
|
|
33
|
+
before = snapshot_state(synapse)
|
|
34
|
+
simulated = apply_simulated_changes(before, mutation_type, changes)
|
|
35
|
+
simulated_confidence = Helpers::Confidence.adjust(
|
|
36
|
+
simulated[:confidence] || synapse.confidence,
|
|
37
|
+
:success
|
|
38
|
+
)
|
|
39
|
+
simulated_mode = Helpers::Confidence.autonomy_mode(simulated_confidence)
|
|
40
|
+
|
|
41
|
+
{
|
|
42
|
+
success: true,
|
|
43
|
+
synapse_id: synapse.id,
|
|
44
|
+
before: before,
|
|
45
|
+
simulated: simulated,
|
|
46
|
+
simulated_confidence: simulated_confidence,
|
|
47
|
+
simulated_mode: simulated_mode,
|
|
48
|
+
recommendation: simulated_confidence > synapse.confidence ? :apply : :skip
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def replay_mutations(synapse)
|
|
55
|
+
mutations = synapse.mutations_dataset.order(:version).all
|
|
56
|
+
timeline = mutations.map do |m|
|
|
57
|
+
{
|
|
58
|
+
version: m.version,
|
|
59
|
+
mutation_type: m.mutation_type,
|
|
60
|
+
trigger: m.trigger,
|
|
61
|
+
outcome: m.outcome,
|
|
62
|
+
created_at: m.created_at
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
{
|
|
67
|
+
synapse_id: synapse.id,
|
|
68
|
+
current_version: synapse.version,
|
|
69
|
+
origin: synapse.origin,
|
|
70
|
+
confidence: synapse.confidence,
|
|
71
|
+
mutation_count: mutations.size,
|
|
72
|
+
timeline: timeline,
|
|
73
|
+
reverts: mutations.count { |m| m.outcome == 'reverted' },
|
|
74
|
+
net_trend: synapse.confidence >= 0.5 ? :improving : :declining
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def snapshot_state(synapse)
|
|
79
|
+
{
|
|
80
|
+
attention: synapse.attention,
|
|
81
|
+
transform: synapse.transform,
|
|
82
|
+
routing_strategy: synapse.routing_strategy,
|
|
83
|
+
confidence: synapse.confidence,
|
|
84
|
+
status: synapse.status
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def apply_simulated_changes(state, mutation_type, changes)
|
|
89
|
+
simulated = state.dup
|
|
90
|
+
case mutation_type
|
|
91
|
+
when 'attention_adjusted'
|
|
92
|
+
simulated[:attention] = changes[:attention] if changes[:attention]
|
|
93
|
+
when 'transform_adjusted'
|
|
94
|
+
simulated[:transform] = changes[:transform] if changes[:transform]
|
|
95
|
+
when 'route_changed'
|
|
96
|
+
simulated[:routing_strategy] = changes[:routing_strategy] if changes[:routing_strategy]
|
|
97
|
+
when 'confidence_changed'
|
|
98
|
+
simulated[:confidence] = changes[:confidence] if changes[:confidence]
|
|
99
|
+
end
|
|
100
|
+
simulated
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/confidence'
|
|
4
|
+
require_relative '../data/models/synapse'
|
|
5
|
+
require_relative '../data/models/synapse_signal'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Extensions
|
|
9
|
+
module Synapse
|
|
10
|
+
module Runners
|
|
11
|
+
module Evaluate
|
|
12
|
+
def evaluate(synapse_id:, payload: {}, conditioner_client: nil, transformer_client: nil)
|
|
13
|
+
synapse = Data::Model::Synapse[synapse_id]
|
|
14
|
+
return { success: false, error: 'synapse not found' } unless synapse
|
|
15
|
+
return { success: false, error: 'synapse not active' } unless Helpers::Confidence::EVALUABLE_STATUSES.include?(synapse.status)
|
|
16
|
+
|
|
17
|
+
mode = Helpers::Confidence.autonomy_mode(synapse.confidence)
|
|
18
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
|
19
|
+
|
|
20
|
+
# Step 1: Attention check
|
|
21
|
+
attention_result = check_attention(synapse, payload, mode, conditioner_client)
|
|
22
|
+
|
|
23
|
+
# Step 2: Transform (if attention passed and confidence allows)
|
|
24
|
+
transform_result = if attention_result[:passed] && Helpers::Confidence.can_transform?(synapse.confidence)
|
|
25
|
+
run_transform(synapse, payload, transformer_client)
|
|
26
|
+
else
|
|
27
|
+
{ success: attention_result[:passed], result: payload }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - start_time
|
|
31
|
+
|
|
32
|
+
# Step 3: Record signal
|
|
33
|
+
record_signal(synapse, attention_result[:passed], transform_result[:success], elapsed)
|
|
34
|
+
|
|
35
|
+
# Step 4: Adjust confidence
|
|
36
|
+
event = transform_result[:success] ? :success : :failure
|
|
37
|
+
new_confidence = Helpers::Confidence.adjust(synapse.confidence, event)
|
|
38
|
+
synapse.update(confidence: new_confidence)
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
success: transform_result[:success],
|
|
42
|
+
mode: mode,
|
|
43
|
+
passed: attention_result[:passed],
|
|
44
|
+
transformed: transform_result[:success],
|
|
45
|
+
result: transform_result[:result],
|
|
46
|
+
latency_ms: elapsed
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def check_attention(synapse, payload, mode, conditioner_client)
|
|
53
|
+
return { passed: true } if synapse.attention.nil? || synapse.attention.empty?
|
|
54
|
+
|
|
55
|
+
passed = if conditioner_client
|
|
56
|
+
result = conditioner_client.evaluate(conditions: Legion::JSON.load(synapse.attention), values: payload)
|
|
57
|
+
result[:valid]
|
|
58
|
+
else
|
|
59
|
+
true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# In OBSERVE mode, always pass through regardless of result
|
|
63
|
+
passed = true if mode == :observe
|
|
64
|
+
|
|
65
|
+
{ passed: passed }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def run_transform(synapse, payload, transformer_client)
|
|
69
|
+
return { success: true, result: payload } if synapse.transform.nil? || synapse.transform.empty?
|
|
70
|
+
return { success: true, result: payload } unless transformer_client
|
|
71
|
+
|
|
72
|
+
transform_def = Legion::JSON.load(synapse.transform)
|
|
73
|
+
result = transformer_client.transform(
|
|
74
|
+
transformation: transform_def[:template] || transform_def[:transformation],
|
|
75
|
+
payload: payload,
|
|
76
|
+
engine: transform_def[:engine]&.to_sym,
|
|
77
|
+
schema: transform_def[:schema]
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if result[:success]
|
|
81
|
+
{ success: true, result: result[:result] }
|
|
82
|
+
else
|
|
83
|
+
new_conf = Helpers::Confidence.adjust(synapse.confidence, :validation_failure)
|
|
84
|
+
synapse.update(confidence: new_conf)
|
|
85
|
+
{ success: false, result: payload, error: result[:errors] }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def record_signal(synapse, passed_attention, transform_success, latency_ms)
|
|
90
|
+
Data::Model::SynapseSignal.create(
|
|
91
|
+
synapse_id: synapse.id,
|
|
92
|
+
passed_attention: passed_attention,
|
|
93
|
+
transform_success: transform_success,
|
|
94
|
+
latency_ms: latency_ms.to_i
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../data/models/synapse'
|
|
4
|
+
require_relative '../data/models/synapse_signal'
|
|
5
|
+
require_relative '../helpers/confidence'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Extensions
|
|
9
|
+
module Synapse
|
|
10
|
+
module Runners
|
|
11
|
+
module GaiaReport
|
|
12
|
+
def gaia_summary(**)
|
|
13
|
+
synapses = Data::Model::Synapse.all
|
|
14
|
+
active = synapses.select { |s| s.status == 'active' }
|
|
15
|
+
dampened = synapses.select { |s| s.status == 'dampened' }
|
|
16
|
+
observing = synapses.select { |s| s.status == 'observing' }
|
|
17
|
+
|
|
18
|
+
pain_threshold = 0.3
|
|
19
|
+
elevated_pain = active.select { |s| s.confidence < pain_threshold }
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
success: true,
|
|
23
|
+
total_synapses: synapses.size,
|
|
24
|
+
active_count: active.size,
|
|
25
|
+
dampened_count: dampened.size,
|
|
26
|
+
observing_count: observing.size,
|
|
27
|
+
elevated_pain_count: elevated_pain.size,
|
|
28
|
+
avg_confidence: avg_confidence(active),
|
|
29
|
+
emergent_candidates: observing.size,
|
|
30
|
+
health_score: compute_health_score(active, dampened, elevated_pain)
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def gaia_reflection(**)
|
|
35
|
+
summary = gaia_summary
|
|
36
|
+
recent_mutations = Data::Model::SynapseMutation
|
|
37
|
+
.where { created_at >= Time.now - 3600 }
|
|
38
|
+
.all
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
success: true,
|
|
42
|
+
summary: summary,
|
|
43
|
+
mutations_1h: recent_mutations.size,
|
|
44
|
+
mutation_types: recent_mutations.map(&:mutation_type).tally,
|
|
45
|
+
mutation_triggers: recent_mutations.map(&:trigger).tally
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def avg_confidence(synapses)
|
|
52
|
+
return 0.0 if synapses.empty?
|
|
53
|
+
|
|
54
|
+
(synapses.sum(&:confidence) / synapses.size).round(4)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def compute_health_score(active, dampened, elevated_pain)
|
|
58
|
+
return 1.0 if active.empty? && dampened.empty?
|
|
59
|
+
|
|
60
|
+
total = active.size + dampened.size
|
|
61
|
+
healthy = active.size - elevated_pain.size
|
|
62
|
+
(healthy.to_f / total).round(4).clamp(0.0, 1.0)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../data/models/synapse'
|
|
4
|
+
require_relative '../data/models/synapse_mutation'
|
|
5
|
+
|
|
6
|
+
module Legion
|
|
7
|
+
module Extensions
|
|
8
|
+
module Synapse
|
|
9
|
+
module Runners
|
|
10
|
+
module Mutate
|
|
11
|
+
VALID_MUTATION_TYPES = %w[attention_adjusted transform_adjusted route_changed confidence_changed].freeze
|
|
12
|
+
VALID_TRIGGERS = %w[hebbian pain dream gaia manual].freeze
|
|
13
|
+
|
|
14
|
+
def mutate(synapse_id:, mutation_type:, changes:, trigger:)
|
|
15
|
+
synapse = Data::Model::Synapse[synapse_id]
|
|
16
|
+
return { success: false, error: 'synapse not found' } unless synapse
|
|
17
|
+
return { success: false, error: "invalid mutation_type: #{mutation_type}" } unless VALID_MUTATION_TYPES.include?(mutation_type)
|
|
18
|
+
return { success: false, error: "invalid trigger: #{trigger}" } unless VALID_TRIGGERS.include?(trigger)
|
|
19
|
+
|
|
20
|
+
before_state = snapshot(synapse)
|
|
21
|
+
apply_changes(synapse, mutation_type, changes)
|
|
22
|
+
after_state = snapshot(synapse)
|
|
23
|
+
|
|
24
|
+
new_version = synapse.version + 1
|
|
25
|
+
synapse.update(version: new_version)
|
|
26
|
+
|
|
27
|
+
Data::Model::SynapseMutation.create(
|
|
28
|
+
synapse_id: synapse.id,
|
|
29
|
+
version: new_version,
|
|
30
|
+
mutation_type: mutation_type,
|
|
31
|
+
before_state: Legion::JSON.dump(before_state),
|
|
32
|
+
after_state: Legion::JSON.dump(after_state),
|
|
33
|
+
trigger: trigger
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
{ success: true, version: new_version, synapse_id: synapse.id }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def snapshot(synapse)
|
|
42
|
+
{
|
|
43
|
+
attention: synapse.attention,
|
|
44
|
+
transform: synapse.transform,
|
|
45
|
+
routing_strategy: synapse.routing_strategy,
|
|
46
|
+
confidence: synapse.confidence,
|
|
47
|
+
status: synapse.status
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def apply_changes(synapse, mutation_type, changes)
|
|
52
|
+
case mutation_type
|
|
53
|
+
when 'attention_adjusted'
|
|
54
|
+
synapse.update(attention: changes[:attention]) if changes[:attention]
|
|
55
|
+
when 'transform_adjusted'
|
|
56
|
+
synapse.update(transform: changes[:transform]) if changes[:transform]
|
|
57
|
+
when 'route_changed'
|
|
58
|
+
synapse.update(routing_strategy: changes[:routing_strategy]) if changes[:routing_strategy]
|
|
59
|
+
when 'confidence_changed'
|
|
60
|
+
synapse.update(confidence: changes[:confidence]) if changes[:confidence]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|