Module: RubyLLM::Agents::Tenant::Budgetable

Extended by:
ActiveSupport::Concern
Included in:
RubyLLM::Agents::Tenant
Defined in:
app/models/ruby_llm/agents/tenant/budgetable.rb

Overview

Handles budget limits and enforcement for tenants.

Supports three types of limits:

  • Cost limits (USD): daily_limit, monthly_limit

  • Token limits: daily_token_limit, monthly_token_limit

  • Execution limits: daily_execution_limit, monthly_execution_limit

Enforcement modes:

  • :none - No enforcement, tracking only

  • :soft - Log warnings when limits exceeded

  • :hard - Block execution when limits exceeded

Examples:

Setting limits

tenant.daily_limit = 100.0
tenant.monthly_limit = 1000.0
tenant.enforcement = "hard"

Checking budget

tenant.within_budget?                    # => true
tenant.within_budget?(type: :monthly_cost)
tenant.remaining_budget(type: :daily_tokens)

See Also:

Constant Summary collapse

ENFORCEMENT_MODES =

Valid enforcement modes

%w[none soft hard].freeze

Instance Method Summary collapse

Instance Method Details

#budget_statusHash

Get full budget status using counter columns

Returns:

  • (Hash)

    Budget status with usage information



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 283

def budget_status
  ensure_daily_reset!
  ensure_monthly_reset!

  {
    enabled: budgets_enabled?,
    enforcement: effective_enforcement,
    global_daily: budget_status_for(effective_daily_limit, daily_cost_spent),
    global_monthly: budget_status_for(effective_monthly_limit, monthly_cost_spent),
    global_daily_tokens: budget_status_for(effective_daily_token_limit, daily_tokens_used),
    global_monthly_tokens: budget_status_for(effective_monthly_token_limit, monthly_tokens_used),
    global_daily_executions: budget_status_for(effective_daily_execution_limit, daily_executions_count),
    global_monthly_executions: budget_status_for(effective_monthly_execution_limit, monthly_executions_count)
  }
end

#budgets_enabled?Boolean

Checks if budget enforcement is enabled

Returns:

  • (Boolean)

    true if enforcement is :soft or :hard



161
162
163
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 161

def budgets_enabled?
  effective_enforcement != :none
end

#check_budget!(agent_type = nil) ⇒ Object

Check budget and raise if exceeded (for hard enforcement)

Parameters:

  • agent_type (String) (defaults to: nil)

    The agent class name

Raises:

  • (BudgetExceededError)

    If hard enforcement and over budget



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 236

def check_budget!(agent_type = nil)
  return unless budgets_enabled?
  return unless hard_enforcement?

  ensure_daily_reset!
  ensure_monthly_reset!

  if effective_daily_limit && daily_cost_spent >= effective_daily_limit
    raise Reliability::BudgetExceededError.new(
      :global_daily, effective_daily_limit, daily_cost_spent, tenant_id: tenant_id
    )
  end

  if effective_monthly_limit && monthly_cost_spent >= effective_monthly_limit
    raise Reliability::BudgetExceededError.new(
      :global_monthly, effective_monthly_limit, monthly_cost_spent, tenant_id: tenant_id
    )
  end

  if effective_daily_token_limit && daily_tokens_used >= effective_daily_token_limit
    raise Reliability::BudgetExceededError.new(
      :global_daily_tokens, effective_daily_token_limit, daily_tokens_used, tenant_id: tenant_id
    )
  end

  if effective_monthly_token_limit && monthly_tokens_used >= effective_monthly_token_limit
    raise Reliability::BudgetExceededError.new(
      :global_monthly_tokens, effective_monthly_token_limit, monthly_tokens_used, tenant_id: tenant_id
    )
  end

  if effective_daily_execution_limit && daily_executions_count >= effective_daily_execution_limit
    raise Reliability::BudgetExceededError.new(
      :global_daily_executions, effective_daily_execution_limit, daily_executions_count, tenant_id: tenant_id
    )
  end

  if effective_monthly_execution_limit && monthly_executions_count >= effective_monthly_execution_limit
    raise Reliability::BudgetExceededError.new(
      :global_monthly_executions, effective_monthly_execution_limit, monthly_executions_count, tenant_id: tenant_id
    )
  end
end

#effective_daily_execution_limitInteger?

Returns the effective daily execution limit

Returns:

  • (Integer, nil)

    The daily execution limit or nil if not set



99
100
101
102
103
104
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 99

def effective_daily_execution_limit
  return daily_execution_limit if daily_execution_limit.present?
  return nil unless inherit_global_defaults

  global_config&.dig(:global_daily_executions)
end

#effective_daily_limitFloat?

Returns the effective daily cost limit

Returns:

  • (Float, nil)

    The daily limit or nil if not set



59
60
61
62
63
64
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 59

def effective_daily_limit
  return daily_limit if daily_limit.present?
  return nil unless inherit_global_defaults

  global_config&.dig(:global_daily)
end

#effective_daily_token_limitInteger?

Returns the effective daily token limit

Returns:

  • (Integer, nil)

    The daily token limit or nil if not set



79
80
81
82
83
84
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 79

def effective_daily_token_limit
  return daily_token_limit if daily_token_limit.present?
  return nil unless inherit_global_defaults

  global_config&.dig(:global_daily_tokens)
end

#effective_enforcementSymbol

Returns the effective enforcement mode

Returns:

  • (Symbol)

    :none, :soft, or :hard



119
120
121
122
123
124
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 119

def effective_enforcement
  return enforcement.to_sym if enforcement.present?
  return :soft unless inherit_global_defaults

  RubyLLM::Agents.configuration.budget_enforcement
end

#effective_monthly_execution_limitInteger?

Returns the effective monthly execution limit

Returns:

  • (Integer, nil)

    The monthly execution limit or nil if not set



109
110
111
112
113
114
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 109

def effective_monthly_execution_limit
  return monthly_execution_limit if monthly_execution_limit.present?
  return nil unless inherit_global_defaults

  global_config&.dig(:global_monthly_executions)
end

#effective_monthly_limitFloat?

Returns the effective monthly cost limit

Returns:

  • (Float, nil)

    The monthly limit or nil if not set



69
70
71
72
73
74
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 69

def effective_monthly_limit
  return monthly_limit if monthly_limit.present?
  return nil unless inherit_global_defaults

  global_config&.dig(:global_monthly)
end

#effective_monthly_token_limitInteger?

Returns the effective monthly token limit

Returns:

  • (Integer, nil)

    The monthly token limit or nil if not set



89
90
91
92
93
94
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 89

def effective_monthly_token_limit
  return monthly_token_limit if monthly_token_limit.present?
  return nil unless inherit_global_defaults

  global_config&.dig(:global_monthly_tokens)
end

#effective_per_agent_daily(agent_type) ⇒ Float?

Returns the effective per-agent daily limit

Checks the current name and all aliases for matching limits.

Parameters:

  • agent_type (String)

    The agent class name

Returns:

  • (Float, nil)

    The limit or nil if not set



132
133
134
135
136
137
138
139
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 132

def effective_per_agent_daily(agent_type)
  names = resolve_agent_names(agent_type)
  limit = names.lazy.filter_map { |n| per_agent_daily&.dig(n) }.first
  return limit if limit.present?
  return nil unless inherit_global_defaults

  names.lazy.filter_map { |n| global_config&.dig(:per_agent_daily, n) }.first
end

#effective_per_agent_monthly(agent_type) ⇒ Float?

Returns the effective per-agent monthly limit

Checks the current name and all aliases for matching limits.

Parameters:

  • agent_type (String)

    The agent class name

Returns:

  • (Float, nil)

    The limit or nil if not set



147
148
149
150
151
152
153
154
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 147

def effective_per_agent_monthly(agent_type)
  names = resolve_agent_names(agent_type)
  limit = names.lazy.filter_map { |n| per_agent_monthly&.dig(n) }.first
  return limit if limit.present?
  return nil unless inherit_global_defaults

  names.lazy.filter_map { |n| global_config&.dig(:per_agent_monthly, n) }.first
end

#hard_enforcement?Boolean

Checks if hard enforcement is enabled

Returns:

  • (Boolean)


168
169
170
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 168

def hard_enforcement?
  effective_enforcement == :hard
end

#remaining_budget(type: :daily_cost) ⇒ Numeric?

Get remaining budget for a specific type

Parameters:

  • type (Symbol) (defaults to: :daily_cost)

    Budget type (see #within_budget?)

Returns:

  • (Numeric, nil)


209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 209

def remaining_budget(type: :daily_cost)
  case type
  when :daily_cost
    effective_daily_limit && (ensure_daily_reset!
                              effective_daily_limit - daily_cost_spent)
  when :monthly_cost
    effective_monthly_limit && (ensure_monthly_reset!
                                effective_monthly_limit - monthly_cost_spent)
  when :daily_tokens
    effective_daily_token_limit && (ensure_daily_reset!
                                    effective_daily_token_limit - daily_tokens_used)
  when :monthly_tokens
    effective_monthly_token_limit && (ensure_monthly_reset!
                                      effective_monthly_token_limit - monthly_tokens_used)
  when :daily_executions
    effective_daily_execution_limit && (ensure_daily_reset!
                                        effective_daily_execution_limit - daily_executions_count)
  when :monthly_executions
    effective_monthly_execution_limit && (ensure_monthly_reset!
                                          effective_monthly_execution_limit - monthly_executions_count)
  end
end

#soft_enforcement?Boolean

Checks if soft enforcement is enabled

Returns:

  • (Boolean)


175
176
177
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 175

def soft_enforcement?
  effective_enforcement == :soft
end

#to_budget_configHash

Convert to config hash for BudgetTracker

Returns:

  • (Hash)

    Budget configuration hash



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 334

def to_budget_config
  {
    enabled: budgets_enabled?,
    enforcement: effective_enforcement,
    # Cost limits
    global_daily: effective_daily_limit,
    global_monthly: effective_monthly_limit,
    per_agent_daily: merged_per_agent_daily,
    per_agent_monthly: merged_per_agent_monthly,
    # Token limits
    global_daily_tokens: effective_daily_token_limit,
    global_monthly_tokens: effective_monthly_token_limit,
    # Execution limits
    global_daily_executions: effective_daily_execution_limit,
    global_monthly_executions: effective_monthly_execution_limit
  }
end

#within_budget?(type: :daily_cost) ⇒ Boolean

Check if within budget for a specific type using counter columns

Parameters:

  • type (Symbol) (defaults to: :daily_cost)

    :daily_cost, :monthly_cost, :daily_tokens, :monthly_tokens, :daily_executions, :monthly_executions

Returns:

  • (Boolean)


184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 184

def within_budget?(type: :daily_cost)
  return true unless budgets_enabled?

  case type
  when :daily_cost
    within_daily_cost_budget?
  when :monthly_cost
    within_monthly_cost_budget?
  when :daily_tokens
    within_daily_token_budget?
  when :monthly_tokens
    within_monthly_token_budget?
  when :daily_executions
    within_daily_execution_budget?
  when :monthly_executions
    within_monthly_execution_budget?
  else
    true
  end
end

#within_daily_cost_budget?Boolean

Individual budget check methods

Returns:

  • (Boolean)


301
302
303
304
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 301

def within_daily_cost_budget?
  ensure_daily_reset!
  effective_daily_limit.nil? || daily_cost_spent < effective_daily_limit
end

#within_daily_execution_budget?Boolean

Returns:

  • (Boolean)


321
322
323
324
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 321

def within_daily_execution_budget?
  ensure_daily_reset!
  effective_daily_execution_limit.nil? || daily_executions_count < effective_daily_execution_limit
end

#within_daily_token_budget?Boolean

Returns:

  • (Boolean)


311
312
313
314
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 311

def within_daily_token_budget?
  ensure_daily_reset!
  effective_daily_token_limit.nil? || daily_tokens_used < effective_daily_token_limit
end

#within_monthly_cost_budget?Boolean

Returns:

  • (Boolean)


306
307
308
309
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 306

def within_monthly_cost_budget?
  ensure_monthly_reset!
  effective_monthly_limit.nil? || monthly_cost_spent < effective_monthly_limit
end

#within_monthly_execution_budget?Boolean

Returns:

  • (Boolean)


326
327
328
329
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 326

def within_monthly_execution_budget?
  ensure_monthly_reset!
  effective_monthly_execution_limit.nil? || monthly_executions_count < effective_monthly_execution_limit
end

#within_monthly_token_budget?Boolean

Returns:

  • (Boolean)


316
317
318
319
# File 'app/models/ruby_llm/agents/tenant/budgetable.rb', line 316

def within_monthly_token_budget?
  ensure_monthly_reset!
  effective_monthly_token_limit.nil? || monthly_tokens_used < effective_monthly_token_limit
end