Class: Legion::Extensions::Agentic::Affect::Mood::Helpers::MoodState

Inherits:
Object
  • Object
show all
Defined in:
lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb

Constant Summary collapse

DIRTY_THRESHOLD =
0.02

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeMoodState

Returns a new instance of MoodState.



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 14

def initialize
  @valence = 0.5
  @arousal = 0.3
  @energy = 0.5
  @stability = 0.8
  @current_mood = :neutral
  @history = []
  @tick_counter = 0
  @dirty = false
  @last_persisted_valence = @valence
end

Instance Attribute Details

#arousalObject (readonly)

Returns the value of attribute arousal.



10
11
12
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 10

def arousal
  @arousal
end

#current_moodObject (readonly)

Returns the value of attribute current_mood.



10
11
12
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 10

def current_mood
  @current_mood
end

#energyObject (readonly)

Returns the value of attribute energy.



10
11
12
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 10

def energy
  @energy
end

#historyObject (readonly)

Returns the value of attribute history.



10
11
12
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 10

def history
  @history
end

#stabilityObject (readonly)

Returns the value of attribute stability.



10
11
12
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 10

def stability
  @stability
end

#tick_counterObject (readonly)

Returns the value of attribute tick_counter.



10
11
12
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 10

def tick_counter
  @tick_counter
end

#valenceObject (readonly)

Returns the value of attribute valence.



10
11
12
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 10

def valence
  @valence
end

Instance Method Details

#dirty?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 43

def dirty?
  @dirty
end

#duration_in_current_moodObject



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 82

def duration_in_current_mood
  return 0 if @history.empty?

  consecutive = 0
  @history.reverse_each do |entry|
    break unless entry[:mood] == @current_mood

    consecutive += 1
  end
  consecutive * Constants::UPDATE_INTERVAL
end

#from_apollo(store:) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 59

def from_apollo(store:)
  entries = store.query(tags: %w[affect state global])
  return if entries.empty?

  data = ::JSON.parse(entries.first[:content])
  @valence = data['valence'].to_f if data['valence']
  @arousal = data['arousal'].to_f if data['arousal']
  @energy  = data['energy'].to_f  if data['energy']
  @current_mood = data['current_mood']&.to_sym || @current_mood
  @last_persisted_valence = @valence
  @dirty = false
rescue ::JSON::ParserError => e
  warn "[mood_state] from_apollo: invalid entry: #{e.message}"
end

#inertiaObject



78
79
80
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 78

def inertia
  Constants::MOOD_INERTIA.fetch(@current_mood, 0.5)
end

#mark_clean!Object



47
48
49
50
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 47

def mark_clean!
  @dirty = false
  @last_persisted_valence = @valence
end

#modulationsObject



74
75
76
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 74

def modulations
  Constants::MOOD_MODULATIONS.fetch(@current_mood, Constants::MOOD_MODULATIONS[:neutral])
end

#mood_trend(window: 20) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 94

def mood_trend(window: 20)
  recent = @history.last(window)
  return :insufficient_data if recent.size < 3

  valences = recent.map { |h| h[:valence] }
  avg_first = valences[0...(valences.size / 2)].sum / (valences.size / 2).to_f
  avg_second = valences[(valences.size / 2)..].sum / (valences.size - (valences.size / 2)).to_f

  delta = avg_second - avg_first
  if delta > 0.05
    :improving
  elsif delta < -0.05
    :declining
  else
    :stable
  end
end

#to_apollo_entriesObject



52
53
54
55
56
57
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 52

def to_apollo_entries
  [{
    content: ::JSON.generate(to_h.transform_keys(&:to_s).except('modulations')),
    tags:    %w[affect state global]
  }]
end

#to_hObject



112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 112

def to_h
  {
    current_mood: @current_mood,
    valence:      @valence.round(3),
    arousal:      @arousal.round(3),
    energy:       @energy.round(3),
    stability:    @stability.round(3),
    modulations:  modulations,
    inertia:      inertia,
    duration:     duration_in_current_mood,
    trend:        mood_trend,
    history_size: @history.size
  }
end

#update(inputs) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/legion/extensions/agentic/affect/mood/helpers/mood_state.rb', line 26

def update(inputs)
  @tick_counter += 1
  return @current_mood unless (@tick_counter % Constants::UPDATE_INTERVAL).zero?

  alpha = effective_alpha
  @valence = ema(@valence, inputs[:valence] || @valence, alpha)
  @arousal = ema(@arousal, inputs[:arousal] || @arousal, alpha)
  @energy = ema(@energy, inputs[:energy] || @energy, alpha)

  compute_stability
  classify_mood
  record_history
  check_dirty

  @current_mood
end