Class: Legion::Extensions::Agentic::Social::PerspectiveShifting::Helpers::ShiftingEngine

Inherits:
Object
  • Object
show all
Defined in:
lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeShiftingEngine

Returns a new instance of ShiftingEngine.



14
15
16
17
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 14

def initialize
  @perspectives = {}
  @situations   = {}
end

Instance Attribute Details

#perspectivesObject (readonly)

Returns the value of attribute perspectives.



12
13
14
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 12

def perspectives
  @perspectives
end

#situationsObject (readonly)

Returns the value of attribute situations.



12
13
14
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 12

def situations
  @situations
end

Instance Method Details

#add_perspective(name:, type: :stakeholder, priorities: [], empathy: Constants::DEFAULT_EMPATHY, bias: nil, expertise_domains: []) ⇒ Object



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

def add_perspective(name:, type: :stakeholder, priorities: [], empathy: Constants::DEFAULT_EMPATHY,
                    bias: nil, expertise_domains: [])
  return { error: :too_many_perspectives } if @perspectives.size >= Constants::MAX_PERSPECTIVES
  return { error: :invalid_type } unless Constants::PERSPECTIVE_TYPES.include?(type)

  sym_priorities = Array(priorities).map(&:to_sym)
  invalid = sym_priorities - Constants::PRIORITY_TYPES
  return { error: :invalid_priorities, invalid: invalid } unless invalid.empty?

  p = Perspective.new(
    name:              name,
    perspective_type:  type,
    priorities:        priorities,
    expertise_domains: expertise_domains,
    empathy_level:     empathy,
    bias_toward:       bias
  )
  @perspectives[p.id] = p
  p
end

#add_situation(content:) ⇒ Object



40
41
42
43
44
45
46
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 40

def add_situation(content:)
  return { error: :too_many_situations } if @situations.size >= Constants::MAX_SITUATIONS

  id = SecureRandom.uuid
  @situations[id] = { id: id, content: content, views: [], created_at: Time.now.utc }
  @situations[id]
end

#blind_spots(situation_id:) ⇒ Object



89
90
91
92
93
94
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 89

def blind_spots(situation_id:)
  views = views_for_situation(situation_id: situation_id)
  applied_ids = views.map(&:perspective_id).uniq
  applied_types = applied_ids.filter_map { |pid| @perspectives[pid]&.perspective_type }.uniq
  Constants::PERSPECTIVE_TYPES - applied_types
end

#coverage_score(situation_id:) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 96

def coverage_score(situation_id:)
  views = views_for_situation(situation_id: situation_id)
  return 0.0 if @perspectives.empty?

  applied_count = views.map(&:perspective_id).uniq.size
  (applied_count.to_f / @perspectives.size).clamp(0.0, 1.0).round(10)
end

#dominant_view(situation_id:) ⇒ Object



104
105
106
107
108
109
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 104

def dominant_view(situation_id:)
  views = views_for_situation(situation_id: situation_id)
  return nil if views.empty?

  views.max_by(&:confidence)
end

#generate_view(situation_id:, perspective_id:, valence: 0.0, concerns: [], opportunities: [], confidence: 0.5) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 48

def generate_view(situation_id:, perspective_id:, valence: 0.0,
                  concerns: [], opportunities: [], confidence: 0.5)
  situation = @situations[situation_id]
  perspective = @perspectives[perspective_id]

  return { error: :situation_not_found } unless situation
  return { error: :perspective_not_found } unless perspective
  return { error: :too_many_views } if situation[:views].size >= Constants::MAX_VIEWS_PER_SITUATION

  view = PerspectiveView.new(
    situation_id:   situation_id,
    perspective_id: perspective_id,
    valence:        valence,
    concerns:       concerns,
    opportunities:  opportunities,
    confidence:     confidence
  )
  situation[:views] << view
  view
end

#most_divergent_pair(situation_id:) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 136

def most_divergent_pair(situation_id:)
  views = views_for_situation(situation_id: situation_id)
  return nil if views.size < 2

  max_diff = -1.0
  pair     = nil

  views.combination(2) do |a, b|
    diff = (a.valence - b.valence).abs
    if diff > max_diff
      max_diff = diff
      pair     = [a, b]
    end
  end

  pair
end

#perspective_agreement(situation_id:) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 76

def perspective_agreement(situation_id:)
  views = views_for_situation(situation_id: situation_id)
  return 0.0 if views.size < Constants::MIN_PERSPECTIVES_FOR_SYNTHESIS

  valences = views.map(&:valence)
  mean     = valences.sum / valences.size.to_f
  variance = valences.sum { |v| (v - mean)**2 } / valences.size.to_f
  std_dev  = Math.sqrt(variance)

  # Agreement is inverse of normalized std_dev (max std_dev is 1.0 for symmetric ±1 distribution)
  (1.0 - std_dev).clamp(0.0, 1.0).round(10)
end

#synthesize(situation_id:) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 111

def synthesize(situation_id:)
  views = views_for_situation(situation_id: situation_id)
  return { error: :insufficient_views } if views.size < Constants::MIN_PERSPECTIVES_FOR_SYNTHESIS

  total_confidence = views.sum(&:confidence)
  return { error: :zero_confidence } if total_confidence.zero?

  weighted_valence = views.sum { |v| v.valence * v.confidence } / total_confidence
  all_concerns     = views.flat_map(&:concerns).uniq
  all_opportunities = views.flat_map(&:opportunities).uniq
  agreement = perspective_agreement(situation_id: situation_id)

  {
    situation_id:     situation_id,
    weighted_valence: weighted_valence.round(10),
    concerns:         all_concerns,
    opportunities:    all_opportunities,
    agreement:        agreement.round(10),
    agreement_label:  Constants.agreement_label(agreement),
    view_count:       views.size,
    coverage_score:   coverage_score(situation_id: situation_id),
    coverage_label:   Constants.coverage_label(coverage_score(situation_id: situation_id))
  }
end

#to_hObject



154
155
156
157
158
159
160
161
162
163
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 154

def to_h
  {
    perspective_count: @perspectives.size,
    situation_count:   @situations.size,
    perspectives:      @perspectives.values.map(&:to_h),
    situations:        @situations.values.map do |s|
      s.merge(views: s[:views].map(&:to_h))
    end
  }
end

#views_for_situation(situation_id:) ⇒ Object



69
70
71
72
73
74
# File 'lib/legion/extensions/agentic/social/perspective_shifting/helpers/shifting_engine.rb', line 69

def views_for_situation(situation_id:)
  situation = @situations[situation_id]
  return [] unless situation

  situation[:views]
end