Module: RubyLLM::Agents::LLMTenant

Extended by:
ActiveSupport::Concern
Defined in:
lib/ruby_llm/agents/core/llm_tenant.rb

Overview

DSL for declaring Rails models as LLM tenants

Provides automatic budget management and usage tracking when included in ActiveRecord models. Models using this concern can be passed as the ‘tenant:` parameter to agents.

Examples:

Basic usage

class Organization < ApplicationRecord
  include RubyLLM::Agents::LLMTenant
  llm_tenant
end

With custom ID method

class Organization < ApplicationRecord
  include RubyLLM::Agents::LLMTenant
  llm_tenant id: :slug
end

With auto-created budget

class Organization < ApplicationRecord
  include RubyLLM::Agents::LLMTenant
  llm_tenant id: :slug, budget: true
end

With limits (auto-creates budget)

class Organization < ApplicationRecord
  include RubyLLM::Agents::LLMTenant
  llm_tenant(
    id: :slug,
    name: :company_name,
    limits: {
      daily_cost: 100,
      monthly_cost: 1000,
      daily_executions: 500
    },
    enforcement: :hard
  )
end

With API keys from model columns/methods

class Organization < ApplicationRecord
  include RubyLLM::Agents::LLMTenant
  encrypts :openai_api_key, :anthropic_api_key  # Rails 7+ encryption

  llm_tenant(
    id: :slug,
    api_keys: {
      openai: :openai_api_key,        # column name
      anthropic: :anthropic_api_key,  # column name
      gemini: :fetch_gemini_key       # custom method
    }
  )

  def fetch_gemini_key
    Vault.read("secret/#{slug}/gemini")
  end
end

See Also:

Instance Method Summary collapse

Instance Method Details

#llm_api_keysHash

Returns API keys resolved from the DSL configuration

Maps provider names (e.g., :openai, :anthropic) to their resolved values by calling the configured method/column on this model instance.

Examples:

org.llm_api_keys
# => { openai: "sk-abc123", anthropic: "sk-ant-xyz789" }

Returns:

  • (Hash)

    Provider to API key mapping (e.g., { openai: “sk-…” })



171
172
173
174
175
176
177
178
179
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 171

def llm_api_keys
  api_keys_config = self.class.llm_tenant_options[:api_keys]
  return {} if api_keys_config.blank?

  api_keys_config.transform_values do |method_name|
    value = send(method_name)
    value.presence
  end.compact
end

#llm_budget_statusHash

Returns the budget status from BudgetTracker

Returns:

  • (Hash)

    Budget status



300
301
302
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 300

def llm_budget_status
  llm_tenant.budget_status
end

#llm_check_budget!void

This method returns an undefined value.

Raises an error if over budget

Raises:

  • (BudgetExceededError)

    if budget is exceeded



324
325
326
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 324

def llm_check_budget!
  llm_tenant.check_budget!(self.class.name)
end

#llm_configure {|tenant| ... } ⇒ Tenant Also known as: llm_configure_budget

Configure tenant with a block

Yields:

  • (tenant)

    The tenant to configure

Returns:

  • (Tenant)

    The saved tenant



196
197
198
199
200
201
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 196

def llm_configure(&block)
  tenant = llm_tenant
  yield(tenant) if block_given?
  tenant.save!
  tenant
end

#llm_cost(period: nil) ⇒ BigDecimal

Returns cost for a given period

Parameters:

  • period (Symbol, Range, nil) (defaults to: nil)

    Time period (:today, :this_month, etc.)

Returns:

  • (BigDecimal)

    Total cost



214
215
216
217
218
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 214

def llm_cost(period: nil)
  scope = llm_executions
  scope = apply_llm_period_scope(scope, period) if period
  scope.sum(:total_cost) || 0
end

#llm_cost_this_monthBigDecimal

Returns cost for this month

Returns:

  • (BigDecimal)

    This month’s cost



230
231
232
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 230

def llm_cost_this_month
  llm_cost(period: :this_month)
end

#llm_cost_todayBigDecimal

Returns cost for today

Returns:

  • (BigDecimal)

    Today’s cost



223
224
225
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 223

def llm_cost_today
  llm_cost(period: :today)
end

#llm_execution_count(period: nil) ⇒ Integer

Returns execution count for a given period

Parameters:

  • period (Symbol, Range, nil) (defaults to: nil)

    Time period

Returns:

  • (Integer)

    Execution count



262
263
264
265
266
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 262

def llm_execution_count(period: nil)
  scope = llm_executions
  scope = apply_llm_period_scope(scope, period) if period
  scope.count
end

#llm_executions_this_monthInteger

Returns executions for this month

Returns:

  • (Integer)

    This month’s execution count



278
279
280
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 278

def llm_executions_this_month
  llm_execution_count(period: :this_month)
end

#llm_executions_todayInteger

Returns executions for today

Returns:

  • (Integer)

    Today’s execution count



271
272
273
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 271

def llm_executions_today
  llm_execution_count(period: :today)
end

#llm_remaining_budget(type: :daily_cost) ⇒ Numeric?

Returns remaining budget for a given limit type

Parameters:

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

    Limit type

Returns:

  • (Numeric, nil)

    Remaining amount



316
317
318
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 316

def llm_remaining_budget(type: :daily_cost)
  llm_tenant.remaining_budget(type: type)
end

#llm_tenantTenant Also known as: llm_budget

Returns or builds the associated Tenant record

Returns:

  • (Tenant)

    The tenant record



184
185
186
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 184

def llm_tenant
  llm_tenant_record || build_llm_tenant_record(tenant_id: llm_tenant_id)
end

#llm_tenant_idString

Returns the tenant_id string for this model

Returns:

  • (String)

    The tenant identifier



146
147
148
149
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 146

def llm_tenant_id
  id_method = self.class.llm_tenant_options[:id] || :id
  send(id_method).to_s
end

#llm_tenant_nameString

Returns this model’s tenant display name, resolved live from the configured name method (‘llm_tenant name: :company_name`). Resolving on read means the tenant always reflects the current value instead of the snapshot taken when its Tenant record was first created.

Returns:

  • (String)

    The current display name



157
158
159
160
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 157

def llm_tenant_name
  name_method = self.class.llm_tenant_options[:name] || :to_s
  send(name_method).to_s
end

#llm_tokens(period: nil) ⇒ Integer

Returns token count for a given period

Parameters:

  • period (Symbol, Range, nil) (defaults to: nil)

    Time period

Returns:

  • (Integer)

    Total tokens



238
239
240
241
242
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 238

def llm_tokens(period: nil)
  scope = llm_executions
  scope = apply_llm_period_scope(scope, period) if period
  scope.sum(:total_tokens) || 0
end

#llm_tokens_this_monthInteger

Returns tokens for this month

Returns:

  • (Integer)

    This month’s tokens



254
255
256
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 254

def llm_tokens_this_month
  llm_tokens(period: :this_month)
end

#llm_tokens_todayInteger

Returns tokens for today

Returns:

  • (Integer)

    Today’s tokens



247
248
249
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 247

def llm_tokens_today
  llm_tokens(period: :today)
end

#llm_usage_summary(period: :this_month) ⇒ Hash

Returns a usage summary for a given period

Parameters:

  • period (Symbol) (defaults to: :this_month)

    Time period (default: :this_month)

Returns:

  • (Hash)

    Usage summary with cost, tokens, and executions



286
287
288
289
290
291
292
293
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 286

def llm_usage_summary(period: :this_month)
  {
    cost: llm_cost(period: period),
    tokens: llm_tokens(period: period),
    executions: llm_execution_count(period: period),
    period: period
  }
end

#llm_within_budget?(type: :daily_cost) ⇒ Boolean

Checks if within budget for a given limit type

Parameters:

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

    Limit type (:daily_cost, :monthly_cost, :daily_tokens, etc.)

Returns:

  • (Boolean)

    true if within budget



308
309
310
# File 'lib/ruby_llm/agents/core/llm_tenant.rb', line 308

def llm_within_budget?(type: :daily_cost)
  llm_tenant.within_budget?(type: type)
end