lex-salience 0.1.0
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/lib/legion/extensions/salience/client.rb +17 -0
- data/lib/legion/extensions/salience/helpers/constants.rb +40 -0
- data/lib/legion/extensions/salience/helpers/salience_map.rb +118 -0
- data/lib/legion/extensions/salience/helpers/signal_integrator.rb +93 -0
- data/lib/legion/extensions/salience/runners/salience.rb +66 -0
- data/lib/legion/extensions/salience/version.rb +9 -0
- data/lib/legion/extensions/salience.rb +16 -0
- metadata +68 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2c9ffd1698dd5b992b73679b28677974b343656d2131df48d5f893eb08ef1312
|
|
4
|
+
data.tar.gz: a792f5c3144f2fd7af78423326620246b8a89ee48bbc17762c2b5b83abb218da
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9566aac35355ff3119937b074d1afead3440df0625ae4128b0de8030b5e0820b23b6feb8734900ce23db8f18d0aa27b4194a64f54001a14f845aa2a46c71550f
|
|
7
|
+
data.tar.gz: 04aa1f807506a0514d19ca87b1edbc6fd9a3c2a8ddbb7b54ca32ab5f47cba61283658c89fa4aab123eb2b8a9a72f17444abf1634462bb23f943528873512f2a5
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Salience
|
|
6
|
+
class Client
|
|
7
|
+
include Runners::Salience
|
|
8
|
+
|
|
9
|
+
attr_reader :salience_map
|
|
10
|
+
|
|
11
|
+
def initialize(salience_map: nil, **)
|
|
12
|
+
@salience_map = salience_map || Helpers::SalienceMap.new
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Salience
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
SALIENCE_SOURCES = %i[emotion mood attention curiosity homeostasis trust empathy volition].freeze
|
|
9
|
+
|
|
10
|
+
SOURCE_WEIGHTS = {
|
|
11
|
+
emotion: 0.20,
|
|
12
|
+
mood: 0.10,
|
|
13
|
+
attention: 0.15,
|
|
14
|
+
curiosity: 0.15,
|
|
15
|
+
homeostasis: 0.15,
|
|
16
|
+
trust: 0.10,
|
|
17
|
+
empathy: 0.05,
|
|
18
|
+
volition: 0.10
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
URGENCY_LEVELS = %i[critical high moderate low background].freeze
|
|
22
|
+
|
|
23
|
+
URGENCY_THRESHOLDS = {
|
|
24
|
+
critical: 0.9,
|
|
25
|
+
high: 0.7,
|
|
26
|
+
moderate: 0.4,
|
|
27
|
+
low: 0.2,
|
|
28
|
+
background: 0.0
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
MAX_SALIENCE_ITEMS = 10
|
|
32
|
+
SALIENCE_DECAY_RATE = 0.05
|
|
33
|
+
INTEGRATION_ALPHA = 0.3
|
|
34
|
+
NOVELTY_BOOST = 0.2
|
|
35
|
+
CONFLICT_AMPLIFIER = 1.5
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Salience
|
|
6
|
+
module Helpers
|
|
7
|
+
class SalienceMap
|
|
8
|
+
attr_reader :current_map, :baseline, :history
|
|
9
|
+
|
|
10
|
+
MAX_HISTORY = 50
|
|
11
|
+
TREND_WINDOW = 5
|
|
12
|
+
TREND_DELTA = 0.02
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@current_map = {}
|
|
16
|
+
@baseline = 0.0
|
|
17
|
+
@history = []
|
|
18
|
+
@novel_sources = {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def update(integrated_signals)
|
|
22
|
+
@current_map = integrated_signals.transform_values { |v| v[:weighted] }
|
|
23
|
+
apply_novelty_boost(integrated_signals)
|
|
24
|
+
overall = overall_salience
|
|
25
|
+
@baseline = (Constants::INTEGRATION_ALPHA * overall) + ((1.0 - Constants::INTEGRATION_ALPHA) * @baseline)
|
|
26
|
+
snapshot = {
|
|
27
|
+
timestamp: Time.now.utc.to_f,
|
|
28
|
+
overall: overall,
|
|
29
|
+
urgency: urgency_level,
|
|
30
|
+
dominant: dominant_source,
|
|
31
|
+
map_snapshot: @current_map.dup
|
|
32
|
+
}
|
|
33
|
+
@history << snapshot
|
|
34
|
+
@history.shift while @history.size > MAX_HISTORY
|
|
35
|
+
snapshot
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def overall_salience
|
|
39
|
+
@current_map.values.sum.round(6)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def urgency_level
|
|
43
|
+
score = overall_salience
|
|
44
|
+
Constants::URGENCY_LEVELS.find do |level|
|
|
45
|
+
score >= Constants::URGENCY_THRESHOLDS[level]
|
|
46
|
+
end || :background
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def dominant_source
|
|
50
|
+
return nil if @current_map.empty?
|
|
51
|
+
|
|
52
|
+
@current_map.max_by { |_k, v| v }&.first
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def above_baseline?
|
|
56
|
+
overall_salience > @baseline
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def conflict_signals
|
|
60
|
+
values = @current_map.values
|
|
61
|
+
return [] if values.size < 2
|
|
62
|
+
|
|
63
|
+
pairs = []
|
|
64
|
+
keys = @current_map.keys
|
|
65
|
+
keys.each_with_index do |key_a, i|
|
|
66
|
+
keys[(i + 1)..].each do |key_b|
|
|
67
|
+
diff = (@current_map[key_a] - @current_map[key_b]).abs
|
|
68
|
+
pairs << [key_a, key_b] if diff > 0.3
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
pairs
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def salience_trend
|
|
75
|
+
return :stable if @history.size < 2
|
|
76
|
+
|
|
77
|
+
window = @history.last([TREND_WINDOW, @history.size].min).map { |h| h[:overall] }
|
|
78
|
+
delta = window.last - window.first
|
|
79
|
+
if delta > TREND_DELTA
|
|
80
|
+
:rising
|
|
81
|
+
elsif delta < -TREND_DELTA
|
|
82
|
+
:falling
|
|
83
|
+
else
|
|
84
|
+
:stable
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def to_h
|
|
89
|
+
{
|
|
90
|
+
current_map: @current_map,
|
|
91
|
+
overall: overall_salience,
|
|
92
|
+
baseline: @baseline,
|
|
93
|
+
urgency: urgency_level,
|
|
94
|
+
dominant: dominant_source,
|
|
95
|
+
above_baseline: above_baseline?,
|
|
96
|
+
conflicts: conflict_signals,
|
|
97
|
+
trend: salience_trend,
|
|
98
|
+
history_size: @history.size
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def apply_novelty_boost(integrated_signals)
|
|
105
|
+
integrated_signals.each_key do |source|
|
|
106
|
+
next if @novel_sources.key?(source)
|
|
107
|
+
|
|
108
|
+
@novel_sources[source] = true
|
|
109
|
+
current = @current_map[source] || 0.0
|
|
110
|
+
boosted = [current + Constants::NOVELTY_BOOST, 1.0].min
|
|
111
|
+
@current_map[source] = boosted
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Salience
|
|
6
|
+
module Helpers
|
|
7
|
+
module SignalIntegrator
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def integrate(tick_results: {})
|
|
11
|
+
Constants::SALIENCE_SOURCES.each_with_object({}) do |source, result|
|
|
12
|
+
data = tick_results.fetch(source, {}) || {}
|
|
13
|
+
raw = extract_raw(source, data)
|
|
14
|
+
weight = Constants::SOURCE_WEIGHTS.fetch(source, 0.0)
|
|
15
|
+
|
|
16
|
+
result[source] = {
|
|
17
|
+
raw_salience: raw,
|
|
18
|
+
weighted: (raw * weight).round(6),
|
|
19
|
+
signal_data: data
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def extract_raw(source, data)
|
|
25
|
+
case source
|
|
26
|
+
when :emotion then extract_emotion(data)
|
|
27
|
+
when :mood then extract_mood(data)
|
|
28
|
+
when :attention then extract_attention(data)
|
|
29
|
+
when :curiosity then extract_curiosity(data)
|
|
30
|
+
when :homeostasis then extract_homeostasis(data)
|
|
31
|
+
when :trust then extract_trust(data)
|
|
32
|
+
when :empathy then extract_empathy(data)
|
|
33
|
+
when :volition then extract_volition(data)
|
|
34
|
+
else 0.0
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def extract_emotion(data)
|
|
39
|
+
arousal = (data[:arousal] || 0.0).to_f
|
|
40
|
+
gut_strength = (data[:gut_strength] || 0.0).to_f
|
|
41
|
+
clamp([arousal, gut_strength].max)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def extract_mood(data)
|
|
45
|
+
stability = (data[:stability] || 1.0).to_f
|
|
46
|
+
clamp(1.0 - stability)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def extract_attention(data)
|
|
50
|
+
spotlight_count = (data[:spotlight_count] || 0).to_f
|
|
51
|
+
total = [(data[:total] || 0).to_f, 1.0].max
|
|
52
|
+
clamp(spotlight_count / total)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def extract_curiosity(data)
|
|
56
|
+
top_salience = (data[:top_salience] || 0.0).to_f
|
|
57
|
+
clamp(top_salience)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def extract_homeostasis(data)
|
|
61
|
+
worst_deviation = (data[:worst_deviation] || 0.0).to_f
|
|
62
|
+
clamp(worst_deviation)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def extract_trust(data)
|
|
66
|
+
violation_count = (data[:violation_count] || 0).to_f
|
|
67
|
+
clamp(violation_count * 0.3)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
EMPATHY_CLIMATE_SCORES = {
|
|
71
|
+
adversarial: 1.0,
|
|
72
|
+
tense: 0.5,
|
|
73
|
+
neutral: 0.1,
|
|
74
|
+
harmonious: 0.0
|
|
75
|
+
}.freeze
|
|
76
|
+
|
|
77
|
+
def extract_empathy(data)
|
|
78
|
+
EMPATHY_CLIMATE_SCORES.fetch(data[:climate], 0.0)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def extract_volition(data)
|
|
82
|
+
intention_salience = (data[:intention_salience] || 0.0).to_f
|
|
83
|
+
clamp(intention_salience)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def clamp(value, min = 0.0, max = 1.0)
|
|
87
|
+
value.clamp(min, max)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Salience
|
|
6
|
+
module Runners
|
|
7
|
+
module Salience
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def compute_salience(tick_results: {}, **)
|
|
12
|
+
integrated = Helpers::SignalIntegrator.integrate(tick_results: tick_results)
|
|
13
|
+
salience_map.update(integrated)
|
|
14
|
+
|
|
15
|
+
Legion::Logging.debug "[salience] compute: overall=#{salience_map.overall_salience.round(3)} " \
|
|
16
|
+
"urgency=#{salience_map.urgency_level} dominant=#{salience_map.dominant_source}"
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
overall: salience_map.overall_salience,
|
|
20
|
+
urgency: salience_map.urgency_level,
|
|
21
|
+
dominant_source: salience_map.dominant_source,
|
|
22
|
+
above_baseline: salience_map.above_baseline?,
|
|
23
|
+
conflicts: salience_map.conflict_signals,
|
|
24
|
+
sources: integrated,
|
|
25
|
+
trend: salience_map.salience_trend
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def salience_status(**)
|
|
30
|
+
Legion::Logging.debug "[salience] status: urgency=#{salience_map.urgency_level} trend=#{salience_map.salience_trend}"
|
|
31
|
+
{
|
|
32
|
+
overall: salience_map.overall_salience,
|
|
33
|
+
urgency: salience_map.urgency_level,
|
|
34
|
+
dominant: salience_map.dominant_source,
|
|
35
|
+
baseline: salience_map.baseline,
|
|
36
|
+
trend: salience_map.salience_trend,
|
|
37
|
+
above_baseline: salience_map.above_baseline?
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def salience_for(source:, **)
|
|
42
|
+
data = salience_map.current_map[source]
|
|
43
|
+
Legion::Logging.debug "[salience] salience_for: source=#{source} salience=#{data || 0.0}"
|
|
44
|
+
{ source: source, salience: data || 0.0, urgency: salience_map.urgency_level }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def salience_history(limit: 10, **)
|
|
48
|
+
{ entries: salience_map.history.last(limit) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def salience_stats(**)
|
|
52
|
+
{
|
|
53
|
+
overall: salience_map.overall_salience,
|
|
54
|
+
baseline: salience_map.baseline,
|
|
55
|
+
urgency: salience_map.urgency_level,
|
|
56
|
+
dominant: salience_map.dominant_source,
|
|
57
|
+
history_size: salience_map.history.size,
|
|
58
|
+
conflict_count: salience_map.conflict_signals.size,
|
|
59
|
+
trend: salience_map.salience_trend
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/salience/version'
|
|
4
|
+
require 'legion/extensions/salience/helpers/constants'
|
|
5
|
+
require 'legion/extensions/salience/helpers/signal_integrator'
|
|
6
|
+
require 'legion/extensions/salience/helpers/salience_map'
|
|
7
|
+
require 'legion/extensions/salience/runners/salience'
|
|
8
|
+
require 'legion/extensions/salience/client'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module Salience
|
|
13
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-salience
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Esity
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-gaia
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: Unified salience network for brain-modeled agentic AI — integrates signals
|
|
27
|
+
from all cognitive subsystems
|
|
28
|
+
email:
|
|
29
|
+
- matthewdiverson@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- lib/legion/extensions/salience.rb
|
|
35
|
+
- lib/legion/extensions/salience/client.rb
|
|
36
|
+
- lib/legion/extensions/salience/helpers/constants.rb
|
|
37
|
+
- lib/legion/extensions/salience/helpers/salience_map.rb
|
|
38
|
+
- lib/legion/extensions/salience/helpers/signal_integrator.rb
|
|
39
|
+
- lib/legion/extensions/salience/runners/salience.rb
|
|
40
|
+
- lib/legion/extensions/salience/version.rb
|
|
41
|
+
homepage: https://github.com/LegionIO/lex-salience
|
|
42
|
+
licenses:
|
|
43
|
+
- MIT
|
|
44
|
+
metadata:
|
|
45
|
+
homepage_uri: https://github.com/LegionIO/lex-salience
|
|
46
|
+
source_code_uri: https://github.com/LegionIO/lex-salience
|
|
47
|
+
documentation_uri: https://github.com/LegionIO/lex-salience
|
|
48
|
+
changelog_uri: https://github.com/LegionIO/lex-salience
|
|
49
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-salience/issues
|
|
50
|
+
rubygems_mfa_required: 'true'
|
|
51
|
+
rdoc_options: []
|
|
52
|
+
require_paths:
|
|
53
|
+
- lib
|
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '3.4'
|
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '0'
|
|
64
|
+
requirements: []
|
|
65
|
+
rubygems_version: 3.6.9
|
|
66
|
+
specification_version: 4
|
|
67
|
+
summary: LEX Salience
|
|
68
|
+
test_files: []
|