Class: Aidp::Harness::UsageLimitTracker
- Inherits:
-
Object
- Object
- Aidp::Harness::UsageLimitTracker
- Includes:
- RescueLogging
- Defined in:
- lib/aidp/harness/usage_limit_tracker.rb
Overview
Domain service for tracking usage metrics per provider and period Handles recording usage, querying current usage, and period resets
Constant Summary collapse
- MAX_HISTORY_PERIODS =
Maximum number of historical periods to retain for trend analysis
12
Instance Attribute Summary collapse
-
#project_dir ⇒ Object
readonly
Returns the value of attribute project_dir.
-
#provider_name ⇒ Object
readonly
Returns the value of attribute provider_name.
Instance Method Summary collapse
-
#clear_all_usage ⇒ Object
Clear all usage data (for testing or user request).
-
#current_usage(period_type: "monthly", reset_day: 1) ⇒ Hash
Get current usage for a period.
-
#initialize(provider_name:, project_dir:, metrics_repo: nil) ⇒ UsageLimitTracker
constructor
A new instance of UsageLimitTracker.
-
#record_usage(tokens:, cost:, tier: "standard", period_type: "monthly", reset_day: 1) ⇒ Object
Record usage for a request.
-
#reset_current_period(period_type: "monthly", reset_day: 1) ⇒ Object
Reset usage for the current period (for testing or manual reset).
-
#tier_usage(tier:, period_type: "monthly", reset_day: 1) ⇒ Hash
Get usage for a specific tier in the current period.
-
#usage_history(limit: MAX_HISTORY_PERIODS) ⇒ Array<Hash>
Get usage history across multiple periods.
Methods included from RescueLogging
__log_rescue_impl, #log_rescue, log_rescue
Constructor Details
#initialize(provider_name:, project_dir:, metrics_repo: nil) ⇒ UsageLimitTracker
Returns a new instance of UsageLimitTracker.
20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 20 def initialize(provider_name:, project_dir:, metrics_repo: nil) @provider_name = provider_name.to_s @project_dir = project_dir @metrics_repo = metrics_repo @usage_file = File.join(project_dir, ".aidp", "usage_tracking", "#{@provider_name}.yml") @usage_data = nil @lock = Mutex.new if defined?(Mutex) Aidp.log_debug("usage_limit_tracker", "initialized", provider: @provider_name, project_dir: @project_dir) end |
Instance Attribute Details
#project_dir ⇒ Object (readonly)
Returns the value of attribute project_dir.
18 19 20 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 18 def project_dir @project_dir end |
#provider_name ⇒ Object (readonly)
Returns the value of attribute provider_name.
18 19 20 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 18 def provider_name @provider_name end |
Instance Method Details
#clear_all_usage ⇒ Object
Clear all usage data (for testing or user request)
201 202 203 204 205 206 207 208 209 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 201 def clear_all_usage Aidp.log_warn("usage_limit_tracker", "clearing_all_usage", provider: @provider_name) with_lock do File.delete(@usage_file) if File.exist?(@usage_file) @usage_data = nil end end |
#current_usage(period_type: "monthly", reset_day: 1) ⇒ Hash
Get current usage for a period
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 97 def current_usage(period_type: "monthly", reset_day: 1) period = UsagePeriod.current(period_type: period_type, reset_day: reset_day) period_key = period.period_key data = load_usage_data period_data = data[:periods]&.[](period_key.to_sym) if period_data { period_key: period_key, period_description: period.description, total_tokens: period_data[:total_tokens] || 0, total_cost: period_data[:total_cost] || 0.0, request_count: period_data[:request_count] || 0, tier_usage: period_data[:tier_usage] || {}, start_time: period.start_time, end_time: period.end_time, remaining_seconds: period.remaining_seconds } else { period_key: period_key, period_description: period.description, total_tokens: 0, total_cost: 0.0, request_count: 0, tier_usage: {}, start_time: period.start_time, end_time: period.end_time, remaining_seconds: period.remaining_seconds } end end |
#record_usage(tokens:, cost:, tier: "standard", period_type: "monthly", reset_day: 1) ⇒ Object
Record usage for a request
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 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 40 def record_usage(tokens:, cost:, tier: "standard", period_type: "monthly", reset_day: 1) Aidp.log_debug("usage_limit_tracker", "recording_usage", provider: @provider_name, tokens: tokens, cost: cost, tier: tier) return unless tokens.positive? || cost.positive? with_lock do data = load_usage_data period = UsagePeriod.current(period_type: period_type, reset_day: reset_day) period_key = period.period_key # Initialize period data if needed data[:periods] ||= {} period_sym = period_key.to_sym data[:periods][period_sym] ||= new_period_data(period) # Update usage metrics period_data = data[:periods][period_sym] period_data[:total_tokens] = (period_data[:total_tokens] || 0) + tokens period_data[:total_cost] = (period_data[:total_cost] || 0.0) + cost period_data[:request_count] = (period_data[:request_count] || 0) + 1 period_data[:last_updated] = Time.now.iso8601 # Update tier-specific usage tier_key = tier.to_s.downcase period_data[:tier_usage] ||= {} period_data[:tier_usage][tier_key] ||= {tokens: 0, cost: 0.0, requests: 0} period_data[:tier_usage][tier_key][:tokens] += tokens period_data[:tier_usage][tier_key][:cost] += cost period_data[:tier_usage][tier_key][:requests] += 1 # Prune old periods prune_old_periods(data) # Save updated data save_usage_data(data) @usage_data = data end end |
#reset_current_period(period_type: "monthly", reset_day: 1) ⇒ Object
Reset usage for the current period (for testing or manual reset)
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 183 def reset_current_period(period_type: "monthly", reset_day: 1) Aidp.log_warn("usage_limit_tracker", "resetting_current_period", provider: @provider_name, period_type: period_type) with_lock do period = UsagePeriod.current(period_type: period_type, reset_day: reset_day) period_key = period.period_key data = load_usage_data data[:periods]&.delete(period_key.to_sym) save_usage_data(data) @usage_data = data end end |
#tier_usage(tier:, period_type: "monthly", reset_day: 1) ⇒ Hash
Get usage for a specific tier in the current period
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 137 def tier_usage(tier:, period_type: "monthly", reset_day: 1) usage = current_usage(period_type: period_type, reset_day: reset_day) tier_key = tier.to_s.downcase tier_data = usage[:tier_usage][tier_key] || usage[:tier_usage][tier_key.to_sym] if tier_data { tokens: tier_data[:tokens] || tier_data["tokens"] || 0, cost: tier_data[:cost] || tier_data["cost"] || 0.0, requests: tier_data[:requests] || tier_data["requests"] || 0 } else {tokens: 0, cost: 0.0, requests: 0} end end |
#usage_history(limit: MAX_HISTORY_PERIODS) ⇒ Array<Hash>
Sorting relies on lexicographic ordering of period keys (e.g., "2024-01", "2024-W01") which produces correct chronological order for ISO-formatted date strings
Get usage history across multiple periods
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/aidp/harness/usage_limit_tracker.rb', line 160 def usage_history(limit: MAX_HISTORY_PERIODS) data = load_usage_data return [] unless data[:periods].is_a?(Hash) data[:periods] .sort_by { |key, _| key } .reverse .take(limit) .map do |key, period_data| { period_key: key, total_tokens: period_data[:total_tokens] || 0, total_cost: period_data[:total_cost] || 0.0, request_count: period_data[:request_count] || 0, tier_usage: period_data[:tier_usage] || {} } end end |