Class: Legion::Extensions::Agentic::Social::Consent::Helpers::ConsentMap

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

Constant Summary collapse

APPROVAL_TIMEOUT =

72 hours

259_200

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConsentMap

Returns a new instance of ConsentMap.



16
17
18
19
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 16

def initialize
  @domains = {}
  load_from_local
end

Instance Attribute Details

#domainsObject (readonly)

Returns the value of attribute domains.



14
15
16
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 14

def domains
  @domains
end

Instance Method Details

#clear_pending(domain) ⇒ Object



113
114
115
116
117
118
119
120
121
122
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 113

def clear_pending(domain)
  entry = @domains[domain]
  return unless entry

  entry[:pending_tier] = nil
  entry[:pending_since] = nil
  entry[:pending_requested_by] = nil
  persist!
  entry
end

#domain_countObject



92
93
94
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 92

def domain_count
  @domains.size
end

#eligible_for_change?(domain) ⇒ Boolean

Returns:

  • (Boolean)


60
61
62
63
64
65
66
67
68
69
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 60

def eligible_for_change?(domain)
  entry = @domains[domain]
  return false unless entry && entry[:total_actions] >= Tiers::MIN_ACTIONS_TO_PROMOTE

  if entry[:last_changed_at]
    (Time.now.utc - entry[:last_changed_at]) >= Tiers::PROMOTION_COOLDOWN
  else
    true
  end
end

#evaluate_promotion(domain) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 71

def evaluate_promotion(domain)
  return :ineligible unless eligible_for_change?(domain)

  rate = success_rate(domain)
  current = get_tier(domain)

  if rate >= Tiers::PROMOTION_THRESHOLD
    promoted = Tiers.promote(current)
    return :already_max if promoted == current

    :promote
  elsif rate < Tiers::DEMOTION_THRESHOLD
    demoted = Tiers.demote(current)
    return :already_min if demoted == current

    :demote
  else
    :maintain
  end
end

#get_tier(domain) ⇒ Object



21
22
23
24
25
26
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 21

def get_tier(domain)
  entry = @domains[domain]
  return Tiers::DEFAULT_TIER unless entry

  entry[:tier]
end

#load_from_localObject



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 163

def load_from_local
  return unless defined?(Legion::Data::Local) && Legion::Data::Local.connected?

  Legion::Data::Local.connection[:consent_domains].each do |row|
    key = row[:domain_key]
    history = begin
      ::JSON.parse(row[:history] || '[]', symbolize_names: false).map do |h|
        { from: h['from'].to_sym, to: h['to'].to_sym, at: h['at'] }
      end
    rescue StandardError => _e
      []
    end

    @domains[key] = new_entry
    @domains[key].merge!(
      tier:            row[:tier].to_sym,
      success_count:   row[:success_count].to_i,
      failure_count:   row[:failure_count].to_i,
      total_actions:   row[:total_actions].to_i,
      last_changed_at: row[:last_changed_at],
      history:         history
    )
  end
rescue StandardError => e
  Legion::Logging.warn "[consent] load_from_local failed: #{e.message}"
end

#pending?(domain) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
127
128
129
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 124

def pending?(domain)
  entry = @domains[domain]
  return false unless entry

  !entry[:pending_tier].nil?
end

#pending_expired?(domain, timeout: APPROVAL_TIMEOUT) ⇒ Boolean

Returns:

  • (Boolean)


131
132
133
134
135
136
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 131

def pending_expired?(domain, timeout: APPROVAL_TIMEOUT)
  entry = @domains[domain]
  return false unless entry && entry[:pending_since]

  Time.now - entry[:pending_since] > timeout
end

#record_outcome(domain, success:) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 41

def record_outcome(domain, success:)
  ensure_domain(domain)
  entry = @domains[domain]
  entry[:total_actions] += 1
  if success
    entry[:success_count] += 1
  else
    entry[:failure_count] += 1
  end
  persist!
end

#save_to_localObject



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 138

def save_to_local
  return unless defined?(Legion::Data::Local) && Legion::Data::Local.connected?

  dataset = Legion::Data::Local.connection[:consent_domains]
  @domains.each do |domain_key, entry|
    row = {
      domain_key:      domain_key,
      tier:            entry[:tier].to_s,
      success_count:   entry[:success_count],
      failure_count:   entry[:failure_count],
      total_actions:   entry[:total_actions],
      last_changed_at: entry[:last_changed_at],
      history:         ::JSON.generate(entry[:history].map { |h| h.transform_values(&:to_s) })
    }
    existing = dataset.where(domain_key: domain_key).first
    if existing
      dataset.where(domain_key: domain_key).update(row.except(:domain_key))
    else
      dataset.insert(row)
    end
  end
rescue StandardError => e
  Legion::Logging.warn "[consent] save_to_local failed: #{e.message}"
end

#set_pending(domain, proposed_tier:, requested_by: 'system') ⇒ Object



103
104
105
106
107
108
109
110
111
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 103

def set_pending(domain, proposed_tier:, requested_by: 'system')
  ensure_domain(domain)
  entry = @domains[domain]
  entry[:pending_tier] = proposed_tier
  entry[:pending_since] = Time.now
  entry[:pending_requested_by] = requested_by
  persist!
  entry
end

#set_tier(domain, tier) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 28

def set_tier(domain, tier)
  return unless Tiers.valid_tier?(tier)

  ensure_domain(domain)
  entry = @domains[domain]
  old_tier = entry[:tier]
  entry[:tier] = tier
  entry[:last_changed_at] = Time.now.utc
  entry[:history] << { from: old_tier, to: tier, at: Time.now.utc }
  entry[:history].shift while entry[:history].size > 50
  persist!
end

#success_rate(domain) ⇒ Object



53
54
55
56
57
58
# File 'lib/legion/extensions/agentic/social/consent/helpers/consent_map.rb', line 53

def success_rate(domain)
  entry = @domains[domain]
  return 0.0 unless entry && entry[:total_actions].positive?

  entry[:success_count].to_f / entry[:total_actions]
end

#to_hObject



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

def to_h
  @domains.transform_values do |v|
    { tier: v[:tier], success_rate: success_rate_from(v), total_actions: v[:total_actions],
      pending_tier: v[:pending_tier], pending_since: v[:pending_since] }
  end
end