Class: Legion::Gaia::ProactiveDispatcher

Inherits:
Object
  • Object
show all
Includes:
Logging::Helper
Defined in:
lib/legion/gaia/proactive_dispatcher.rb

Constant Summary collapse

MAX_PER_DAY =
3
MIN_INTERVAL =

2 hours

7200
IGNORE_COOLDOWN =

24 hours

86_400
MAX_PENDING =
5
DAY_WINDOW =

24 hours

86_400

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_per_day: MAX_PER_DAY, min_interval: MIN_INTERVAL, ignore_cooldown: IGNORE_COOLDOWN) ⇒ ProactiveDispatcher

Returns a new instance of ProactiveDispatcher.



18
19
20
21
22
23
24
25
26
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 18

def initialize(max_per_day: MAX_PER_DAY, min_interval: MIN_INTERVAL,
               ignore_cooldown: IGNORE_COOLDOWN)
  @max_per_day     = max_per_day
  @min_interval    = min_interval
  @ignore_cooldown = ignore_cooldown
  @dispatch_log    = []
  @pending_buffer  = []
  @last_ignored_at = nil
end

Instance Attribute Details

#ignore_cooldownObject (readonly)

Returns the value of attribute ignore_cooldown.



16
17
18
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 16

def ignore_cooldown
  @ignore_cooldown
end

#max_per_dayObject (readonly)

Returns the value of attribute max_per_day.



16
17
18
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 16

def max_per_day
  @max_per_day
end

#min_intervalObject (readonly)

Returns the value of attribute min_interval.



16
17
18
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 16

def min_interval
  @min_interval
end

#pending_bufferObject (readonly)

Returns the value of attribute pending_buffer.



16
17
18
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 16

def pending_buffer
  @pending_buffer
end

Instance Method Details

#can_dispatch?Boolean

Returns:

  • (Boolean)


28
29
30
31
32
33
34
35
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 28

def can_dispatch?
  prune_old_dispatches!
  return false if @dispatch_log.size >= @max_per_day
  return false if @dispatch_log.any? && (Time.now.utc - @dispatch_log.last[:at]) < @min_interval
  return false if @last_ignored_at && (Time.now.utc - @last_ignored_at) < @ignore_cooldown

  true
end

#dispatch_with_gate(intent) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 67

def dispatch_with_gate(intent)
  unless can_dispatch?
    log.info("ProactiveDispatcher skipped intent reason=#{intent.dig(:trigger, :reason)} status=rate_limited")
    return { dispatched: false, reason: :rate_limited }
  end

  content = generate_content(intent)
  unless content
    log.info("ProactiveDispatcher skipped intent reason=#{intent.dig(:trigger, :reason)} status=no_content")
    return { dispatched: false, reason: :no_content }
  end

  partner_id = resolve_partner_id
  channel_id = resolve_partner_channel
  target_failure = validate_dispatch_target(intent, partner_id: partner_id, channel_id: channel_id)
  return target_failure if target_failure

  result = proactive_module.send_notification(
    content: content,
    priority: intent.dig(:trigger, :priority) || :low,
    channel_id: channel_id,
    user_id: partner_id
  )

  delivery_failure = failed_dispatch_response(
    intent, result, partner_id: partner_id, channel_id: channel_id, content: content
  )
  return delivery_failure if delivery_failure

  record_dispatch!
  log.info(
    'ProactiveDispatcher dispatched intent ' \
    "reason=#{intent.dig(:trigger, :reason)} user_id=#{partner_id} channel_id=#{channel_id}"
  )
  { dispatched: true, content: content, result: result, user_id: partner_id, channel_id: channel_id }
rescue StandardError => e
  handle_exception(e, level: :warn, operation: 'gaia.proactive_dispatcher.dispatch_with_gate')
  { dispatched: false, reason: :error, error: e.message }
end

#dispatches_todayObject



48
49
50
51
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 48

def dispatches_today
  prune_old_dispatches!
  @dispatch_log.size
end

#drain_pendingObject



60
61
62
63
64
65
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 60

def drain_pending
  drained = @pending_buffer.dup
  @pending_buffer.clear
  log.info("ProactiveDispatcher drained pending count=#{drained.size}") if drained.any?
  drained
end

#queue_intent(intent) ⇒ Object



53
54
55
56
57
58
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 53

def queue_intent(intent)
  @pending_buffer << intent
  @pending_buffer.shift while @pending_buffer.size > MAX_PENDING
  log.info("ProactiveDispatcher queued intent reason=#{intent.dig(:trigger,
                                                                  :reason)} pending=#{@pending_buffer.size}")
end

#record_dispatch!Object



37
38
39
40
41
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 37

def record_dispatch!
  prune_old_dispatches!
  @dispatch_log << { at: Time.now.utc }
  log.info("ProactiveDispatcher recorded dispatch count=#{@dispatch_log.size}")
end

#record_ignored!Object



43
44
45
46
# File 'lib/legion/gaia/proactive_dispatcher.rb', line 43

def record_ignored!
  @last_ignored_at = Time.now.utc
  log.info('ProactiveDispatcher recorded ignored interaction')
end