Module: Legion::Extensions::Agentic::Affect::Emotion::Helpers::Valence

Defined in:
lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb

Constant Summary collapse

DIMENSIONS =
%i[urgency importance novelty familiarity].freeze
MAX_MAGNITUDE =

all 4 dimensions at 1.0

Math.sqrt(4.0)
URGENCY_ATTENTION_WEIGHT =

Attention modulation weights (from spec Section 6.1)

0.4
IMPORTANCE_ATTENTION_WEIGHT =
0.35
NOVELTY_ATTENTION_WEIGHT =
0.25
ATTENTION_MULTIPLIER =
0.3
MOMENTUM_ALPHA =

Momentum

0.3
SOURCE_URGENCY =

Source urgency map (spec Section 3.2)

{
  firmware_violation: 1.0,
  human_direct:       0.9,
  direct_address:     0.8,
  mesh_priority:      0.7,
  scheduled:          0.4,
  partner_absence:    0.2,
  ambient:            0.1
}.freeze
ABSENCE_BASE_IMPORTANCE =

Partner absence importance scaling

0.4
ABSENCE_IMPORTANCE_SCALE =
0.1
ABSENCE_MAX_IMPORTANCE =
0.7
FAMILIARITY_SATURATION =

Familiarity saturation (spec Section 3.5)

100

Class Method Summary collapse

Class Method Details

.absence_importance(consecutive_misses) ⇒ Object



83
84
85
86
# File 'lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb', line 83

def absence_importance(consecutive_misses)
  raw = ABSENCE_BASE_IMPORTANCE + (ABSENCE_IMPORTANCE_SCALE * Math.log(consecutive_misses + 1))
  clamp(raw, 0.0, ABSENCE_MAX_IMPORTANCE)
end

.aggregate(valences) ⇒ Object



65
66
67
68
69
70
71
72
73
74
# File 'lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb', line 65

def aggregate(valences)
  return new_valence if valences.empty?

  sums = Hash.new(0.0)
  valences.each do |v|
    DIMENSIONS.each { |d| sums[d] += v[d] }
  end
  n = valences.size.to_f
  new_valence(**DIMENSIONS.to_h { |d| [d, sums[d] / n] })
end

.clamp(value, min = 0.0, max = 1.0) ⇒ Object



95
96
97
# File 'lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb', line 95

def clamp(value, min = 0.0, max = 1.0)
  value.clamp(min, max)
end

.compute_arousal(valences) ⇒ Object



76
77
78
79
80
81
# File 'lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb', line 76

def compute_arousal(valences)
  return 0.0 if valences.empty?

  total = valences.sum { |v| magnitude(v) }
  clamp(total / (valences.size * MAX_MAGNITUDE))
end

.dominant_dimension(valence) ⇒ Object



61
62
63
# File 'lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb', line 61

def dominant_dimension(valence)
  DIMENSIONS.max_by { |d| valence[d] }
end

.magnitude(valence) ⇒ Object



52
53
54
55
56
57
58
59
# File 'lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb', line 52

def magnitude(valence)
  Math.sqrt(
    (valence[:urgency]**2) +
    (valence[:importance]**2) +
    (valence[:novelty]**2) +
    (valence[:familiarity]**2)
  )
end

.modulate_salience(base_salience, valence) ⇒ Object



88
89
90
91
92
93
# File 'lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb', line 88

def modulate_salience(base_salience, valence)
  boost = ((valence[:urgency] * URGENCY_ATTENTION_WEIGHT) +
           (valence[:importance] * IMPORTANCE_ATTENTION_WEIGHT) +
           (valence[:novelty] * NOVELTY_ATTENTION_WEIGHT)) * ATTENTION_MULTIPLIER
  clamp(base_salience + boost)
end

.new_valence(urgency: 0.0, importance: 0.0, novelty: 0.0, familiarity: 0.0) ⇒ Object



43
44
45
46
47
48
49
50
# File 'lib/legion/extensions/agentic/affect/emotion/helpers/valence.rb', line 43

def new_valence(urgency: 0.0, importance: 0.0, novelty: 0.0, familiarity: 0.0)
  {
    urgency:     clamp(urgency),
    importance:  clamp(importance),
    novelty:     clamp(novelty),
    familiarity: clamp(familiarity)
  }
end