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
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
- .from_record(record) ⇒ Object
- .from_records(records) ⇒ Object
- .from_ruby_llm(usage, model) ⇒ Object
- .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.
125 126 127 128 129 130 131 132 133 |
# File 'lib/turnkit/cost.rb', line 125 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
118 119 120 121 122 123 |
# File 'lib/turnkit/cost.rb', line 118 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
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/turnkit/cost.rb', line 102 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
90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/turnkit/cost.rb', line 90 def self.from_hash(hash) hash = hash.transform_keys(&:to_sym) new( input: hash[:input], output: hash[:output], cache_read: hash[:cache_read] || hash[:cached_input], cache_write: hash[:cache_write] || hash[:cache_creation], thinking: hash[:thinking] || hash[:reasoning] || hash[:thinking_output] || hash[:reasoning_output], total: hash[:total] ) end |
.from_rates(usage, rates) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/turnkit/cost.rb', line 51 def self.from_rates(usage, rates) rates = rates.transform_keys(&:to_sym) new( input: amount(usage.input_tokens, rates[:input] || rates[:input_per_million]), output: amount(usage.output_tokens, rates[:output] || rates[:output_per_million]), cache_read: amount(usage.cached_tokens, rates[:cache_read] || rates[:cached_input] || rates[:cache_read_input_per_million] || rates[:cached_input_per_million]), cache_write: amount(usage.cache_write_tokens, rates[:cache_write] || rates[:cache_creation] || rates[:cache_write_input_per_million] || rates[:cache_creation_input_per_million]), thinking: amount(usage.thinking_tokens, rates[:thinking] || rates[:reasoning] || rates[:thinking_output] || rates[:reasoning_output] || rates[:thinking_output_per_million] || rates[:reasoning_output_per_million]), 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
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/turnkit/cost.rb', line 63 def self.from_ruby_llm(usage, model) require "ruby_llm" model_info = ::RubyLLM.models.find(model) if model return new unless model_info if defined?(::RubyLLM::Cost) 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) else from_rates( usage, input: model_info.input_price_per_million, output: model_info.output_price_per_million, cached_input: model_info.pricing&.text_tokens&.cached_input ) end rescue LoadError, StandardError 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
143 144 145 146 147 148 149 150 151 152 |
# File 'lib/turnkit/cost.rb', line 143 def to_h { "input" => input, "output" => output, "cache_read" => cache_read, "cache_write" => cache_write, "thinking" => thinking, "total" => total }.compact end |
#total ⇒ Object
135 136 137 138 139 140 141 |
# File 'lib/turnkit/cost.rb', line 135 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 |