Module: RubyLLM::Contract::CostCalculator

Defined in:
lib/ruby_llm/contract/cost_calculator.rb

Overview

Pricing lookup for ‘max_cost` budget gating + retry usage aggregation.

**What this module does (public surface):**

  1. **Fine-tune / custom-model pricing registry** — ‘register_model` fills the gap left by RubyLLM 1.14’s models.json: there is no upstream ‘RubyLLM::Models.register` API, so fine-tuned models (e.g. `ft:gpt-4o-custom`) need their pricing supplied locally.

  2. **Lookup with fallback chain** — ‘calculate(model_name:, usage:)` checks the custom registry first, falls back to `RubyLLM.models.find(model_name)`, returns `nil` on miss.

**What this module is NOT:**

  • Not a “cost calculator” feature — the math itself (‘tokens × price_per_million / 1_000_000`) is trivial and lives in `private_class_method :compute_cost` for internal use only.

  • Not a substitute for RubyLLM’s pricing data — for any model in ‘RubyLLM.models`, this module simply queries it.

The reason this module exists at all is the registry + retry usage aggregation across attempts (the latter sits in ‘Step::RetryExecutor`, which calls `calculate` per attempt and sums; not in this module).

Defined Under Namespace

Classes: RegisteredModel

Class Method Summary collapse

Class Method Details

.calculate(model_name:, usage:) ⇒ Object

Look up cost for a single model + usage hash. Returns nil if model is unknown (custom registry miss + RubyLLM miss), so callers can decide whether to refuse the call or proceed (see ‘on_unknown_pricing:` step option for the budget-gating policy).

CostCalculator.calculate(
  model_name: "gpt-4o-mini",
  usage: { input_tokens: 1_500, output_tokens: 800 }
)
# => 0.00069 (or nil if model not registered)

Math is intentionally simple and private — this method is the primary public entry point. Aggregating across retry attempts is done in ‘Step::RetryExecutor`, not here.



75
76
77
78
79
80
81
82
83
84
# File 'lib/ruby_llm/contract/cost_calculator.rb', line 75

def self.calculate(model_name:, usage:)
  return nil unless model_name && usage.is_a?(Hash)

  model_info = find_model(model_name)
  return nil unless model_info

  compute_cost(model_info, usage)
rescue StandardError
  nil
end

.register_model(model_name, input_per_1m:, output_per_1m:) ⇒ Object

Register pricing for custom or fine-tuned models not in the RubyLLM registry. This is the gem’s primary value-add for cost computation; everything else falls back to RubyLLM’s own model registry.

CostCalculator.register_model("ft:gpt-4o-custom",
  input_per_1m: 3.0, output_per_1m: 6.0)


41
42
43
44
45
46
47
48
49
# File 'lib/ruby_llm/contract/cost_calculator.rb', line 41

def self.register_model(model_name, input_per_1m:, output_per_1m:)
  validate_price!(:input_per_1m, input_per_1m)
  validate_price!(:output_per_1m, output_per_1m)

  @custom_models[model_name] = RegisteredModel.new(
    input_price_per_million: input_per_1m,
    output_price_per_million: output_per_1m
  )
end

.reset_custom_models!Object

Reset all custom model registrations. Mainly useful in tests.



57
58
59
# File 'lib/ruby_llm/contract/cost_calculator.rb', line 57

def self.reset_custom_models!
  @custom_models.clear
end

.unregister_model(model_name) ⇒ Object

Remove a previously registered custom model. Mainly useful in tests.



52
53
54
# File 'lib/ruby_llm/contract/cost_calculator.rb', line 52

def self.unregister_model(model_name)
  @custom_models.delete(model_name)
end