Class: Legion::Extensions::Agentic::Homeostasis::Rhythm::Helpers::RhythmEngine

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb

Constant Summary

Constants included from Constants

Constants::AMPLITUDE_LABELS, Constants::CIRCADIAN_PERIOD, Constants::COGNITIVE_DIMENSIONS, Constants::DEFAULT_PHASE_OFFSET, Constants::MAX_AMPLITUDE, Constants::MAX_HISTORY, Constants::MAX_RHYTHMS, Constants::MIN_AMPLITUDE, Constants::PHASE_LABELS, Constants::RHYTHM_TYPES, Constants::ULTRADIAN_PERIOD

Instance Method Summary collapse

Constructor Details

#initializeRhythmEngine

Returns a new instance of RhythmEngine.



12
13
14
15
16
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 12

def initialize
  @rhythms  = {}
  @counter  = 0
  @history  = []
end

Instance Method Details

#add_rhythm(name:, rhythm_type:, dimension:, period: nil, amplitude: 0.5, phase_offset: 0.0) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 18

def add_rhythm(name:, rhythm_type:, dimension:, period: nil, amplitude: 0.5, phase_offset: 0.0)
  return nil unless RHYTHM_TYPES.include?(rhythm_type.to_sym)
  return nil unless COGNITIVE_DIMENSIONS.include?(dimension.to_sym)
  return nil if @rhythms.size >= MAX_RHYTHMS

  resolved_period = resolve_period(rhythm_type, period)
  return nil if resolved_period.nil?

  @counter += 1
  id = :"rhythm_#{@counter}"
  rhythm = Rhythm.new(
    id:           id,
    name:         name,
    rhythm_type:  rhythm_type,
    dimension:    dimension,
    period:       resolved_period,
    amplitude:    amplitude,
    phase_offset: phase_offset
  )
  @rhythms[id] = rhythm
  rhythm
end

#best_time_for(dimension:, within: 3600) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 66

def best_time_for(dimension:, within: 3600)
  matching = @rhythms.values.select { |r| r.dimension == dimension }
  return nil if matching.empty?

  now = Time.now.utc.to_f
  step = 60
  steps = within / step

  best_time  = nil
  best_value = -1.0

  (0..steps).each do |i|
    t = now + (i * step)
    value = matching.sum { |r| r.value_at(t) } / matching.size
    if value > best_value
      best_value = value
      best_time  = t
    end
  end

  best_time ? Time.at(best_time).utc : nil
end

#cognitive_profileObject



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 103

def cognitive_profile
  current = current_state
  current.each_with_object({}) do |(dim, value), hash|
    phase_label = dominant_phase_label(dim)
    amplitude_label = dominant_amplitude_label(dim)
    hash[dim] = {
      value:           value,
      phase_label:     phase_label,
      amplitude_label: amplitude_label,
      optimal:         optimal_for(dimension: dim)
    }
  end
end

#current_stateObject



53
54
55
56
57
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 53

def current_state
  COGNITIVE_DIMENSIONS.to_h do |dim|
    [dim, dimension_value(dimension: dim).round(4)]
  end
end

#dimension_value(dimension:) ⇒ Object



46
47
48
49
50
51
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 46

def dimension_value(dimension:)
  matching = @rhythms.values.select { |r| r.dimension == dimension }
  return 0.0 if matching.empty?

  matching.sum(&:current_value) / matching.size
end

#optimal_for(dimension:) ⇒ Object



59
60
61
62
63
64
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 59

def optimal_for(dimension:)
  matching = @rhythms.values.select { |r| r.dimension == dimension }
  return false if matching.empty?

  matching.any?(&:peak?)
end

#peak_dimensionsObject



117
118
119
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 117

def peak_dimensions
  COGNITIVE_DIMENSIONS.select { |dim| optimal_for(dimension: dim) }
end

#remove_rhythm(rhythm_id:) ⇒ Object



41
42
43
44
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 41

def remove_rhythm(rhythm_id:)
  removed = @rhythms.delete(rhythm_id)
  removed ? { success: true, rhythm_id: rhythm_id } : { success: false, reason: :not_found }
end

#synchronize(rhythm_ids:) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 89

def synchronize(rhythm_ids:)
  rhythms = rhythm_ids.filter_map { |id| @rhythms[id] }
  return { success: false, reason: :not_found } if rhythms.empty?

  reference_phase = rhythms.first.current_phase
  rhythms.each do |r|
    current = r.current_phase
    offset_adjustment = (reference_phase - current) * r.period
    instance_variable_set_phase_offset(r, r.phase_offset + offset_adjustment)
  end

  { success: true, synchronized: rhythms.map(&:id) }
end

#to_hObject



128
129
130
131
132
133
134
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 128

def to_h
  {
    rhythm_count:  @rhythms.size,
    rhythms:       @rhythms.values.map(&:to_h),
    current_state: current_state
  }
end

#trough_dimensionsObject



121
122
123
124
125
126
# File 'lib/legion/extensions/agentic/homeostasis/rhythm/helpers/rhythm_engine.rb', line 121

def trough_dimensions
  COGNITIVE_DIMENSIONS.select do |dim|
    matching = @rhythms.values.select { |r| r.dimension == dim }
    matching.any?(&:trough?)
  end
end