Class: RubyLLM::Cost

Inherits:
Object
  • Object
show all
Includes:
Support::Inspectable
Defined in:
lib/ruby_llm/cost.rb

Overview

A Cost prices token usage in US dollars using pricing from the model registry. Usage entries own accounting events; Message and Chat aggregate calls, and Model#cost_for prices any token usage against a specific model.

response = chat.ask "Summarize Ruby's object model."
response.cost.total

cost = model.cost_for(response.tokens)
cost.input
cost.output

The components are RubyLLM's normalized token buckets: #input, #output, #cache_read, #cache_write, and #thinking. When the registry lacks pricing for tokens that were used, the affected component and #total return nil instead of a false zero.

When the provider reports the exact cost of a call, #total returns the reported amount instead of a registry-price estimate, even when registry pricing is missing.

Costs are computed when the object is built. Readers do not recalculate them when registry prices change. ::aggregate and ::from_h return the same class, so a single call, a whole chat, and a stored breakdown all read the same way.

Constant Summary collapse

COMPONENTS =

:nodoc:

%i[input output cache_read cache_write thinking].freeze
PER_MILLION =

:nodoc:

1_000_000.0

Constants included from Support::Inspectable

Support::Inspectable::TRUNCATE_AT

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Support::Inspectable

#full_inspect, #inspect, #pretty_print

Constructor Details

#initialize(tokens: nil, model: nil, category: :text_tokens, input_details: nil, tier: :standard, amounts: nil, missing: nil, reported: nil, complete: true, total: nil) ⇒ Cost

:nodoc:



99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ruby_llm/cost.rb', line 99

def initialize(tokens: nil, model: nil, category: :text_tokens, input_details: nil, tier: :standard, # :nodoc:
               amounts: nil, missing: nil, reported: nil, complete: true, total: nil)
  if amounts
    @amounts = amounts
    @missing = missing || []
    @reported = reported
  else
    price_tokens(tokens, model, category, input_details, tier)
    total = reported_total if total.nil?
    @reported ||= !total.nil?
  end
  @complete = complete
  @total = total
end

Class Method Details

.aggregate(costs, complete: true) ⇒ Object

Combines several costs into one Cost that sums each component. Ignores nil entries. A component returns nil when pricing was missing for one of the calls, or when no call has a cost for that component. Pass complete: false when some requests are still running or their costs are unknown; #total then remains nil.

cost = RubyLLM::Cost.aggregate(messages.map(&:cost))
cost.total


44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/ruby_llm/cost.rb', line 44

def aggregate(costs, complete: true)
  costs = costs.compact.select(&:tokens?)

  missing = COMPONENTS.select do |component|
    costs.any? { |cost| cost.missing?(component) }
  end

  amounts = COMPONENTS.to_h do |component|
    values = costs.filter_map { |cost| cost.public_send(component) }
    [component, missing.include?(component) || values.empty? ? nil : values.sum]
  end

  new(amounts:, missing:, reported: costs.any?, complete:, total: aggregate_total(costs))
end

.from_h(hash, tokens: nil) ⇒ Object

Rebuilds a cost from a stored breakdown Hash, as produced by #to_h and persisted alongside a usage entry. Keys may be Strings or Symbols. The component readers return the recorded amounts and #total equals the recorded :total.

RubyLLM::Cost.from_h({ input: 0.0001, total: 0.0003 }).total


66
67
68
69
70
71
72
73
# File 'lib/ruby_llm/cost.rb', line 66

def from_h(hash, tokens: nil)
  amounts = COMPONENTS.to_h { |component| [component, hash[component] || hash[component.to_s]] }
  total_recorded = hash.key?(:total) || hash.key?('total')
  total = hash[:total] || hash['total'] if total_recorded
  missing = missing_recorded_components(amounts, tokens, total_recorded)

  new(amounts:, missing:, reported: recorded_tokens?(amounts, tokens, total_recorded), total:)
end

Instance Method Details

#cache_readObject

Returns the cost of cache-read input tokens in US dollars, or nil when the token count or its pricing is unavailable.



128
129
130
# File 'lib/ruby_llm/cost.rb', line 128

def cache_read
  @amounts[:cache_read]
end

#cache_writeObject

Returns the cost of cache-write input tokens in US dollars, or nil when the token count or its pricing is unavailable.



134
135
136
# File 'lib/ruby_llm/cost.rb', line 134

def cache_write
  @amounts[:cache_write]
end

#inputObject

Returns the cost of input tokens in US dollars, or nil when the token count or its pricing is unavailable.



116
117
118
# File 'lib/ruby_llm/cost.rb', line 116

def input
  @amounts[:input]
end

#missing?(component) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


179
180
181
# File 'lib/ruby_llm/cost.rb', line 179

def missing?(component) # :nodoc:
  @missing.include?(component)
end

#outputObject

Returns the cost of billable output tokens in US dollars, or nil when the token count or its pricing is unavailable.



122
123
124
# File 'lib/ruby_llm/cost.rb', line 122

def output
  @amounts[:output]
end

#thinkingObject

Returns the cost of thinking tokens in US dollars, or nil when the model does not price reasoning output separately from regular output or the token count is unavailable. When not priced separately, thinking tokens are part of #output.



142
143
144
# File 'lib/ruby_llm/cost.rb', line 142

def thinking
  @amounts[:thinking]
end

#to_hObject

Returns a hash of component costs in US dollars, plus :total, omitting nil values.



164
165
166
167
168
169
170
171
172
173
# File 'lib/ruby_llm/cost.rb', line 164

def to_h
  {
    input: input,
    output: output,
    cache_read: cache_read,
    cache_write: cache_write,
    thinking: thinking,
    total: total
  }.compact
end

#tokens?Boolean

:nodoc:

Returns:

  • (Boolean)


175
176
177
# File 'lib/ruby_llm/cost.rb', line 175

def tokens? # :nodoc:
  @reported
end

#totalObject

Returns the sum of all components in US dollars, or the exact amount the provider reported when it reported one. Returns nil when there is no token usage, or when pricing is missing for tokens that were used and the provider reported no cost.



150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ruby_llm/cost.rb', line 150

def total
  return nil unless @complete
  return nil unless tokens?
  return @total unless @total.nil?
  return nil if @missing.any?

  amounts = @amounts.values.compact
  return nil if amounts.empty?

  amounts.sum
end