Class: Legion::Extensions::Agentic::Inference::CausalAttribution::Helpers::AttributionEngine

Inherits:
Object
  • Object
show all
Defined in:
lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb

Instance Method Summary collapse

Constructor Details

#initializeAttributionEngine

Returns a new instance of AttributionEngine.



10
11
12
13
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 10

def initialize
  @attributions = {}
  @history      = []
end

Instance Method Details

#attribution_biasObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 69

def attribution_bias
  return { bias: :none, detail: 'no attributions' } if @attributions.empty?

  all     = @attributions.values
  total   = all.size.to_f
  locus   = bias_ratio(all, :locus, :external, total)
  stab    = bias_ratio(all, :stability, :stable, total)
  control = bias_ratio(all, :controllability, :uncontrollable, total)

  failures = all.select { |a| a.outcome == :failure }
  external_failure_ratio = failures.empty? ? 0.0 : failures.count(&:external?).to_f / failures.size

  {
    external_locus_ratio:       locus.round(3),
    stable_ratio:               stab.round(3),
    uncontrollable_ratio:       control.round(3),
    external_failure_ratio:     external_failure_ratio.round(3),
    self_serving_bias_detected: external_failure_ratio > 0.6,
    total_attributions:         @attributions.size
  }
end

#by_domain(domain:) ⇒ Object



61
62
63
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 61

def by_domain(domain:)
  @attributions.values.select { |a| a.domain == domain }
end

#by_outcome(outcome:) ⇒ Object



65
66
67
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 65

def by_outcome(outcome:)
  @attributions.values.select { |a| a.outcome == outcome }
end

#by_pattern(locus: nil, stability: nil, controllability: nil) ⇒ Object



53
54
55
56
57
58
59
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 53

def by_pattern(locus: nil, stability: nil, controllability: nil)
  @attributions.values.select do |a|
    (locus.nil? || a.locus == locus) &&
      (stability.nil?       || a.stability == stability) &&
      (controllability.nil? || a.controllability == controllability)
  end
end

#countObject



120
121
122
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 120

def count
  @attributions.size
end

#create_attribution(event:, outcome:, domain:, locus:, stability:, controllability:, confidence: Attribution::DEFAULT_CONFIDENCE) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 15

def create_attribution(event:, outcome:, domain:, locus:, stability:, controllability:,
                       confidence: Attribution::DEFAULT_CONFIDENCE)
  trim_if_needed
  attribution = Attribution.new(
    event:           event,
    outcome:         outcome,
    domain:          domain,
    locus:           locus,
    stability:       stability,
    controllability: controllability,
    confidence:      confidence
  )
  @attributions[attribution.id] = attribution
  attribution
end

#decay_allObject



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 108

def decay_all
  decayed = 0
  @attributions.each_value do |a|
    new_conf = (a.confidence - Attribution::DECAY_RATE).clamp(
      Attribution::CONFIDENCE_FLOOR, Attribution::CONFIDENCE_CEILING
    )
    a.instance_variable_set(:@confidence, new_conf)
    decayed += 1
  end
  decayed
end

#emotional_profileObject



91
92
93
94
95
96
97
98
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 91

def emotional_profile
  counts = Hash.new(0)
  @attributions.each_value { |a| counts[a.emotional_response] += 1 if a.emotional_response }
  total = counts.values.sum.to_f
  profile = counts.transform_values { |v| (v / total).round(3) }
  dominant = counts.max_by { |_, v| v }&.first
  { distribution: profile, dominant: dominant, total: counts.values.sum }
end

#most_common_patternObject



100
101
102
103
104
105
106
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 100

def most_common_pattern
  return { pattern: nil, count: 0 } if @attributions.empty?

  grouped = @attributions.values.group_by(&:pattern)
  best    = grouped.max_by { |_, attrs| attrs.size }
  { pattern: best[0], count: best[1].size }
end

#reattribute(attribution_id:, locus: nil, stability: nil, controllability: nil) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 31

def reattribute(attribution_id:, locus: nil, stability: nil, controllability: nil)
  attr = @attributions[attribution_id]
  return { found: false, attribution_id: attribution_id } unless attr

  new_locus           = locus           || attr.locus
  new_stability       = stability       || attr.stability
  new_controllability = controllability || attr.controllability

  updated = Attribution.new(
    event:           attr.event,
    outcome:         attr.outcome,
    domain:          attr.domain,
    locus:           new_locus,
    stability:       new_stability,
    controllability: new_controllability,
    confidence:      attr.confidence
  )
  @history << attr.to_h if @history.size < Attribution::MAX_HISTORY
  @attributions[attribution_id] = updated
  updated
end

#to_hObject



124
125
126
127
128
129
130
131
# File 'lib/legion/extensions/agentic/inference/causal_attribution/helpers/attribution_engine.rb', line 124

def to_h
  {
    total_attributions: @attributions.size,
    history_size:       @history.size,
    outcome_counts:     outcome_counts,
    locus_counts:       locus_counts
  }
end