Module: LlmCostTracker::Pricing

Extended by:
ServiceCharges
Defined in:
lib/llm_cost_tracker/pricing.rb,
lib/llm_cost_tracker.rb,
lib/llm_cost_tracker/pricing/mode.rb,
lib/llm_cost_tracker/pricing/sync.rb,
lib/llm_cost_tracker/pricing/lookup.rb,
lib/llm_cost_tracker/pricing/unknown.rb,
lib/llm_cost_tracker/pricing/backfill.rb,
lib/llm_cost_tracker/pricing/registry.rb,
lib/llm_cost_tracker/pricing/estimator.rb,
lib/llm_cost_tracker/pricing/explainer.rb,
lib/llm_cost_tracker/pricing/sync/fetcher.rb,
lib/llm_cost_tracker/pricing/service_charges.rb,
lib/llm_cost_tracker/pricing/effective_prices.rb,
lib/llm_cost_tracker/pricing/sync/registry_diff.rb,
lib/llm_cost_tracker/pricing/sync/change_printer.rb,
lib/llm_cost_tracker/pricing/sync/registry_writer.rb

Overview

rubocop:disable Metrics/ModuleLength

Defined Under Namespace

Modules: EffectivePrices, Estimator, Explainer, Lookup, Registry, ServiceCharges, Sync Classes: Backfill, Explanation, Mode, Unknown

Constant Summary collapse

STANDARD_MODE_VALUES =
%i[auto default standard standard_only].freeze

Constants included from ServiceCharges

ServiceCharges::DEFAULT_CURRENCY, ServiceCharges::EMPTY_RATES, ServiceCharges::MUTEX

Class Method Summary collapse

Methods included from ServiceCharges

builtin_rates, charge_rate, file_rates, rates_from_registry, reset!

Class Method Details

.calculate(provider:, model:, tokens:, line_items:, pricing_mode: nil) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/llm_cost_tracker/pricing.rb', line 50

def calculate(provider:, model:, tokens:, line_items:, pricing_mode: nil)
  calculation = calculation_for(
    provider: provider,
    model: model,
    tokens: tokens,
    pricing_mode: pricing_mode
  )
  cost_data = calculation && cost_from(calculation)
  snapshot = calculation && snapshot_from(calculation)
  priced = apply_calculation_to_line_items(line_items, calculation,
                                           provider: provider, pricing_mode: pricing_mode)
  [cost_data, snapshot, priced]
end

.combine_with_service_lines(cost_data, line_items) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/llm_cost_tracker/pricing.rb', line 99

def combine_with_service_lines(cost_data, line_items)
  priced_services = line_items.reject(&:token?).select(&:priced?)
  return cost_data if priced_services.empty?

  base_currency = base_currency_for(cost_data, priced_services)
  matching, mismatched = priced_services.partition { |line| line.currency.to_s == base_currency.to_s }
  warn_currency_mismatch(mismatched, base_currency) if mismatched.any?

  cost = cost_data ? cost_data.dup : {}
  cost[:currency] ||= base_currency.to_s
  return cost if matching.empty?

  service_total = matching.sum(BigDecimal("0"), &:cost_value)
  base_total = BigDecimal(cost.fetch(:total_cost, 0).to_s)
  cost[:total_cost] = (base_total + service_total).round(8)
  cost
end

.cost_for(provider:, model:, tokens:, pricing_mode: nil) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/llm_cost_tracker/pricing.rb', line 38

def cost_for(provider:, model:, tokens:, pricing_mode: nil)
  calculation = calculation_for(
    provider: provider,
    model: model,
    tokens: tokens,
    pricing_mode: pricing_mode
  )
  return nil unless calculation

  cost_from(calculation)
end

.explain(provider:, model:, tokens:, pricing_mode: nil) ⇒ Object



85
86
87
88
89
90
91
92
# File 'lib/llm_cost_tracker/pricing.rb', line 85

def explain(provider:, model:, tokens:, pricing_mode: nil)
  Explainer.call(
    provider: provider,
    model: model,
    tokens: tokens,
    pricing_mode: pricing_mode
  )
end

.normalize_mode(value) ⇒ Object



29
30
31
32
33
34
35
36
# File 'lib/llm_cost_tracker/pricing.rb', line 29

def normalize_mode(value)
  return nil if value.nil?

  mode = normalize_string_mode(value.to_s)
  return nil unless mode

  STANDARD_MODE_VALUES.include?(mode) ? nil : mode
end

.price_line_items(provider:, model:, line_items:, pricing_mode: nil) ⇒ Object



64
65
66
67
68
69
70
71
# File 'lib/llm_cost_tracker/pricing.rb', line 64

def price_line_items(provider:, model:, line_items:, pricing_mode: nil)
  token_usage = TokenUsage.build_from_tokens(token_attributes_from(line_items))
  calculation = calculation_for(provider: provider, model: model, tokens: token_usage, pricing_mode: pricing_mode)
  snapshot = calculation && snapshot_from(calculation)
  priced = apply_calculation_to_line_items(line_items, calculation,
                                           provider: provider, pricing_mode: pricing_mode)
  [priced, snapshot]
end

.snapshot_for(provider:, model:, tokens:, pricing_mode: nil) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/llm_cost_tracker/pricing.rb', line 73

def snapshot_for(provider:, model:, tokens:, pricing_mode: nil)
  calculation = calculation_for(
    provider: provider,
    model: model,
    tokens: tokens,
    pricing_mode: pricing_mode
  )
  return nil unless calculation

  snapshot_from(calculation)
end

.stored_cost_attributes(attributes) ⇒ Object



94
95
96
97
# File 'lib/llm_cost_tracker/pricing.rb', line 94

def stored_cost_attributes(attributes)
  value = attributes.to_h[:total_cost]
  value ? { total_cost: value } : {}
end

.token_pricing_partial?(token_usage, cost_data) ⇒ Boolean

Returns:

  • (Boolean)


117
118
119
120
121
122
123
124
125
# File 'lib/llm_cost_tracker/pricing.rb', line 117

def token_pricing_partial?(token_usage, cost_data)
  return false unless cost_data

  token_usage.priced_quantities.any? do |key, quantity|
    next false unless quantity.positive?

    cost_data[Billing::Components::BY_KEY.fetch(key).cost_key].nil?
  end
end