Class: Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::CounterfactualEngine

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb

Constant Summary

Constants included from Constants

Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::COUNTERFACTUAL_TYPES, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::DEFAULT_REGRET, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::DOWNWARD_WEIGHT, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::EMOTIONAL_RESPONSES, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::MAX_ALTERNATIVES, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::MAX_HISTORY, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::MAX_SCENARIOS, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::MUTATION_TYPES, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::PLAUSIBILITY_THRESHOLD, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::REGRET_CEILING, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::REGRET_DECAY, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::REGRET_FLOOR, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::RELEVANCE_THRESHOLD, Legion::Extensions::Agentic::Inference::Counterfactual::Helpers::Constants::UPWARD_WEIGHT

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCounterfactualEngine

Returns a new instance of CounterfactualEngine.



16
17
18
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 16

def initialize
  @scenarios = {}
end

Instance Attribute Details

#scenariosObject (readonly)

Returns the value of attribute scenarios.



14
15
16
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 14

def scenarios
  @scenarios
end

Instance Method Details

#by_domain(domain:) ⇒ Object



107
108
109
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 107

def by_domain(domain:)
  @scenarios.values.select { |s| s.domain == domain }
end

#by_type(type:) ⇒ Object



103
104
105
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 103

def by_type(type:)
  @scenarios.values.select { |s| s.scenario_type == type }
end

#compute_regret(scenario_id:) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 70

def compute_regret(scenario_id:)
  scenario = @scenarios[scenario_id]
  return 0.0 unless scenario

  if scenario.upward?
    scenario.regret_magnitude * UPWARD_WEIGHT * scenario.plausibility
  elsif scenario.downward?
    -(scenario.regret_magnitude * DOWNWARD_WEIGHT * scenario.plausibility)
  else
    0.0
  end
end

#domain_regret(domain:) ⇒ Object



87
88
89
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 87

def domain_regret(domain:)
  by_domain(domain: domain).reject(&:resolved).sum(0.0) { |s| compute_regret(scenario_id: s.id) }
end

#generate(actual_outcome:, counterfactual_outcome:, antecedent:, scenario_type:, mutation_type:, domain:, plausibility:, regret_magnitude: 0.5) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 20

def generate(actual_outcome:, counterfactual_outcome:, antecedent:,
             scenario_type:, mutation_type:, domain:, plausibility:,
             regret_magnitude: 0.5)
  prune_if_needed
  scenario = Scenario.new(
    scenario_type:          scenario_type,
    mutation_type:          mutation_type,
    actual_outcome:         actual_outcome,
    counterfactual_outcome: counterfactual_outcome,
    antecedent:             antecedent,
    domain:                 domain,
    plausibility:           plausibility,
    regret_magnitude:       regret_magnitude
  )
  @scenarios[scenario.id] = scenario
  scenario
end

#generate_alternatives(actual_outcome:, domain:) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 38

def generate_alternatives(actual_outcome:, domain:)
  alternatives = []
  mutation_samples = MUTATION_TYPES.first(MAX_ALTERNATIVES)

  mutation_samples.each_with_index do |mutation, idx|
    type = idx.even? ? :upward : :downward
    plausibility = (0.8 - (idx * 0.1)).clamp(PLAUSIBILITY_THRESHOLD, 1.0)

    scenario = generate(
      actual_outcome:         actual_outcome,
      counterfactual_outcome: "alternative_#{mutation}_#{idx}",
      antecedent:             "#{mutation}_variation_#{idx}",
      scenario_type:          type,
      mutation_type:          mutation,
      domain:                 domain,
      plausibility:           plausibility,
      regret_magnitude:       0.5
    )
    alternatives << scenario
  end

  alternatives
end

#lessons_learnedObject



91
92
93
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 91

def lessons_learned
  @scenarios.values.select { |s| s.resolved && s.lesson }
end

#net_regretObject



83
84
85
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 83

def net_regret
  unresolved.sum(0.0) { |s| compute_regret(scenario_id: s.id) }
end

#recent(count:) ⇒ Object



115
116
117
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 115

def recent(count:)
  @scenarios.values.sort_by(&:created_at).last(count)
end

#regret_decayObject



95
96
97
98
99
100
101
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 95

def regret_decay
  unresolved.each do |scenario|
    new_magnitude = (scenario.regret_magnitude - REGRET_DECAY).clamp(REGRET_FLOOR, REGRET_CEILING)
    scenario.instance_variable_set(:@regret_magnitude, new_magnitude)
    scenario.instance_variable_set(:@emotional_valence, recompute_valence(scenario))
  end
end

#resolve(scenario_id:, lesson:) ⇒ Object



62
63
64
65
66
67
68
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 62

def resolve(scenario_id:, lesson:)
  scenario = @scenarios[scenario_id]
  return nil unless scenario

  scenario.resolve(lesson: lesson)
  scenario
end

#to_hObject



119
120
121
122
123
124
125
126
127
128
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 119

def to_h
  {
    total:         @scenarios.size,
    unresolved:    unresolved.size,
    resolved:      lessons_learned.size,
    net_regret:    net_regret.round(4),
    by_type:       type_summary,
    lessons_count: lessons_learned.size
  }
end

#unresolvedObject



111
112
113
# File 'lib/legion/extensions/agentic/inference/counterfactual/helpers/counterfactual_engine.rb', line 111

def unresolved
  @scenarios.values.reject(&:resolved)
end