Class: LanguageOperator::Agent::Safety::RateLimiter

Inherits:
Object
  • Object
show all
Includes:
Loggable
Defined in:
lib/language_operator/agent/safety/rate_limiter.rb

Overview

Rate Limiter for enforcing request limits

Enforces per-agent rate limiting to prevent runaway requests.

Examples:

limiter = RateLimiter.new(requests_per_minute: 10, requests_per_hour: 100)
limiter.check_rate_limit!

Defined Under Namespace

Classes: RateLimitExceededError

Instance Method Summary collapse

Methods included from Loggable

#logger

Constructor Details

#initialize(requests_per_minute: nil, requests_per_hour: nil, requests_per_day: nil) ⇒ RateLimiter

Returns a new instance of RateLimiter.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/language_operator/agent/safety/rate_limiter.rb', line 21

def initialize(requests_per_minute: nil, requests_per_hour: nil, requests_per_day: nil)
  @requests_per_minute = requests_per_minute&.to_i
  @requests_per_hour = requests_per_hour&.to_i
  @requests_per_day = requests_per_day&.to_i

  @minute_requests = []
  @hour_requests = []
  @day_requests = []

  logger.info('Rate limiter initialized',
              per_minute: @requests_per_minute,
              per_hour: @requests_per_hour,
              per_day: @requests_per_day)
end

Instance Method Details

#check_rate_limit!Object

Check if a request would exceed rate limits

Raises:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/language_operator/agent/safety/rate_limiter.rb', line 39

def check_rate_limit!
  now = Time.now

  # Clean up old requests
  cleanup_old_requests(now)

  # Check per-minute limit
  if @requests_per_minute && @minute_requests.length >= @requests_per_minute
    oldest = @minute_requests.first
    wait_time = 60 - (now - oldest)
    logger.error('Per-minute rate limit exceeded',
                 current: @minute_requests.length,
                 limit: @requests_per_minute,
                 wait_time: wait_time.round(1))
    raise RateLimitExceededError,
          "Per-minute rate limit exceeded: #{@minute_requests.length}/#{@requests_per_minute} " \
          "(retry in #{wait_time.round(1)}s)"
  end

  # Check per-hour limit
  if @requests_per_hour && @hour_requests.length >= @requests_per_hour
    oldest = @hour_requests.first
    wait_time = 3600 - (now - oldest)
    logger.error('Per-hour rate limit exceeded',
                 current: @hour_requests.length,
                 limit: @requests_per_hour,
                 wait_time: (wait_time / 60).round(1))
    raise RateLimitExceededError,
          "Per-hour rate limit exceeded: #{@hour_requests.length}/#{@requests_per_hour} " \
          "(retry in #{(wait_time / 60).round(1)}m)"
  end

  # Check per-day limit
  if @requests_per_day && @day_requests.length >= @requests_per_day
    oldest = @day_requests.first
    wait_time = 86_400 - (now - oldest)
    logger.error('Per-day rate limit exceeded',
                 current: @day_requests.length,
                 limit: @requests_per_day,
                 wait_time: (wait_time / 3600).round(1))
    raise RateLimitExceededError,
          "Per-day rate limit exceeded: #{@day_requests.length}/#{@requests_per_day} " \
          "(retry in #{(wait_time / 3600).round(1)}h)"
  end

  logger.debug('Rate limit check passed',
               minute_requests: @minute_requests.length,
               hour_requests: @hour_requests.length,
               day_requests: @day_requests.length)
end

#record_requestObject

Record a request



91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/language_operator/agent/safety/rate_limiter.rb', line 91

def record_request
  now = Time.now
  cleanup_old_requests(now)

  @minute_requests << now
  @hour_requests << now
  @day_requests << now

  logger.debug('Request recorded',
               minute_count: @minute_requests.length,
               hour_count: @hour_requests.length,
               day_count: @day_requests.length)
end

#statusHash

Get current rate limit status

Returns:

  • (Hash)

    Rate limit status information



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/language_operator/agent/safety/rate_limiter.rb', line 108

def status
  now = Time.now
  cleanup_old_requests(now)

  {
    per_minute: {
      requests: @minute_requests.length,
      limit: @requests_per_minute,
      remaining: @requests_per_minute ? (@requests_per_minute - @minute_requests.length) : nil
    },
    per_hour: {
      requests: @hour_requests.length,
      limit: @requests_per_hour,
      remaining: @requests_per_hour ? (@requests_per_hour - @hour_requests.length) : nil
    },
    per_day: {
      requests: @day_requests.length,
      limit: @requests_per_day,
      remaining: @requests_per_day ? (@requests_per_day - @day_requests.length) : nil
    }
  }
end