Class: TurnKit::Cost
- Inherits:
-
Object
- Object
- TurnKit::Cost
- Defined in:
- lib/turnkit/cost.rb
Constant Summary collapse
- COMPONENTS =
%i[input output cache_read cache_write thinking].freeze
- PER_MILLION =
1_000_000.0- RATE_KEYS =
%i[input output cache_read cache_write thinking].freeze
Instance Attribute Summary collapse
-
#cache_read ⇒ Object
readonly
Returns the value of attribute cache_read.
-
#cache_write ⇒ Object
readonly
Returns the value of attribute cache_write.
-
#input ⇒ Object
readonly
Returns the value of attribute input.
-
#output ⇒ Object
readonly
Returns the value of attribute output.
-
#thinking ⇒ Object
readonly
Returns the value of attribute thinking.
Class Method Summary collapse
- .aggregate(costs) ⇒ Object
- .amount(tokens, price) ⇒ Object
- .custom_cost(usage, model) ⇒ Object
- .from_hash(hash) ⇒ Object
-
.from_rates(usage, rates) ⇒ Object
Rates are USD per million tokens, keyed by component.
- .from_record(record) ⇒ Object
- .from_records(records) ⇒ Object
-
.from_ruby_llm(usage, model) ⇒ Object
Estimates cost from RubyLLM's model pricing registry (ruby_llm >= 1.16).
- .from_usage(usage, model: nil) ⇒ Object
Instance Method Summary collapse
-
#initialize(input: nil, output: nil, cache_read: nil, cache_write: nil, thinking: nil, total: nil, strict: false) ⇒ Cost
constructor
A new instance of Cost.
- #to_h ⇒ Object
- #total ⇒ Object
Constructor Details
#initialize(input: nil, output: nil, cache_read: nil, cache_write: nil, thinking: nil, total: nil, strict: false) ⇒ Cost
Returns a new instance of Cost.
123 124 125 126 127 128 129 130 131 |
# File 'lib/turnkit/cost.rb', line 123 def initialize(input: nil, output: nil, cache_read: nil, cache_write: nil, thinking: nil, total: nil, strict: false) @input = number(input) @output = number(output) @cache_read = number(cache_read) @cache_write = number(cache_write) @thinking = number(thinking) @total = number(total) @strict = strict end |
Instance Attribute Details
#cache_read ⇒ Object (readonly)
Returns the value of attribute cache_read.
8 9 10 |
# File 'lib/turnkit/cost.rb', line 8 def cache_read @cache_read end |
#cache_write ⇒ Object (readonly)
Returns the value of attribute cache_write.
8 9 10 |
# File 'lib/turnkit/cost.rb', line 8 def cache_write @cache_write end |
#input ⇒ Object (readonly)
Returns the value of attribute input.
8 9 10 |
# File 'lib/turnkit/cost.rb', line 8 def input @input end |
#output ⇒ Object (readonly)
Returns the value of attribute output.
8 9 10 |
# File 'lib/turnkit/cost.rb', line 8 def output @output end |
#thinking ⇒ Object (readonly)
Returns the value of attribute thinking.
8 9 10 |
# File 'lib/turnkit/cost.rb', line 8 def thinking @thinking end |
Class Method Details
.aggregate(costs) ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/turnkit/cost.rb', line 10 def self.aggregate(costs) costs = costs.compact return new unless costs.any? if costs.any? { |cost| COMPONENTS.any? { |component| !cost.public_send(component).nil? } } values = COMPONENTS.to_h do |component| amounts = costs.filter_map { |cost| cost.public_send(component) } [ component, amounts.any? ? amounts.sum : nil ] end return new(**values) end totals = costs.map(&:total) return new(total: totals.sum) if totals.none?(&:nil?) new end |
.amount(tokens, price) ⇒ Object
116 117 118 119 120 121 |
# File 'lib/turnkit/cost.rb', line 116 def self.amount(tokens, price) return nil if tokens.to_i.positive? && price.nil? return 0.0 if tokens.to_i.zero? tokens.to_i * price.to_f / PER_MILLION end |
.custom_cost(usage, model) ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/turnkit/cost.rb', line 100 def self.custom_cost(usage, model) return unless TurnKit.cost_calculator value = TurnKit.cost_calculator.call(usage, model) case value when nil nil when Cost value when Hash from_hash(value) else new(total: value) end end |
.from_hash(hash) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/turnkit/cost.rb', line 88 def self.from_hash(hash) hash = hash.transform_keys(&:to_sym) new( input: hash[:input], output: hash[:output], cache_read: hash[:cache_read], cache_write: hash[:cache_write], thinking: hash[:thinking], total: hash[:total] ) end |
.from_rates(usage, rates) ⇒ Object
Rates are USD per million tokens, keyed by component.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/turnkit/cost.rb', line 54 def self.from_rates(usage, rates) rates = rates.transform_keys(&:to_sym) unknown = rates.keys - RATE_KEYS raise ConfigError, "unknown cost rate keys: #{unknown.join(", ")} (use: #{RATE_KEYS.join(", ")})" if unknown.any? new( input: amount(usage.input_tokens, rates[:input]), output: amount(usage.output_tokens, rates[:output]), cache_read: amount(usage.cached_tokens, rates[:cache_read]), cache_write: amount(usage.cache_write_tokens, rates[:cache_write]), thinking: amount(usage.thinking_tokens, rates[:thinking]), strict: true ) end |
.from_record(record) ⇒ Object
42 43 44 45 46 47 48 49 |
# File 'lib/turnkit/cost.rb', line 42 def self.from_record(record) attrs = record.transform_keys(&:to_s) usage = attrs["usage"] || {} return from_hash(usage["cost_details"] || usage[:cost_details]) if usage["cost_details"] || usage[:cost_details] return new(total: attrs["cost"]) if attrs["cost"] from_usage(Usage.from_h(usage), model: attrs["model"]) end |
.from_records(records) ⇒ Object
38 39 40 |
# File 'lib/turnkit/cost.rb', line 38 def self.from_records(records) aggregate(records.map { |record| from_record(record) }) end |
.from_ruby_llm(usage, model) ⇒ Object
Estimates cost from RubyLLM's model pricing registry (ruby_llm >= 1.16). Returns an empty Cost when ruby_llm is not loaded or the model is not in the registry; any other failure raises.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/turnkit/cost.rb', line 72 def self.from_ruby_llm(usage, model) return new unless defined?(::RubyLLM) && model model_info = ::RubyLLM.models.find(model) tokens = ::RubyLLM::Tokens.new( input: usage.input_tokens, output: usage.output_tokens, cached: usage.cached_tokens, cache_creation: usage.cache_write_tokens, thinking: usage.thinking_tokens ) from_hash(::RubyLLM::Cost.new(tokens: tokens, model: model_info).to_h) rescue ::RubyLLM::ModelNotFoundError new end |
.from_usage(usage, model: nil) ⇒ Object
28 29 30 31 32 33 34 35 36 |
# File 'lib/turnkit/cost.rb', line 28 def self.from_usage(usage, model: nil) return new(total: usage.cost) if usage.cost custom = custom_cost(usage, model) return custom if custom rates = TurnKit.cost_rates[model.to_s] || TurnKit.cost_rates[model&.to_sym] rates ? from_rates(usage, rates) : from_ruby_llm(usage, model) end |
Instance Method Details
#to_h ⇒ Object
141 142 143 144 145 146 147 148 149 150 |
# File 'lib/turnkit/cost.rb', line 141 def to_h { "input" => input, "output" => output, "cache_read" => cache_read, "cache_write" => cache_write, "thinking" => thinking, "total" => total }.compact end |
#total ⇒ Object
133 134 135 136 137 138 139 |
# File 'lib/turnkit/cost.rb', line 133 def total return @total if @total return nil if @strict && COMPONENTS.any? { |component| public_send(component).nil? } values = COMPONENTS.filter_map { |component| public_send(component) } values.empty? ? nil : values.sum end |