Module: Ask::LLM::CostCalculator

Defined in:
lib/ask/llm/cost_calculator.rb

Overview

Calculate LLM API costs from model pricing data.

Constant Summary collapse

MILLION =
1_000_000

Class Method Summary collapse

Class Method Details

.breakdown(model, input_tokens: 0, output_tokens: 0, cache_read_tokens: 0, cache_write_tokens: 0, reasoning_tokens: 0, audio_input_tokens: 0, audio_output_tokens: 0, tier: :standard) ⇒ Hash?

Per-component cost breakdown.

Returns:

  • (Hash, nil)


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ask/llm/cost_calculator.rb', line 74

def breakdown(model, input_tokens: 0, output_tokens: 0,
              cache_read_tokens: 0, cache_write_tokens: 0,
              reasoning_tokens: 0,
              audio_input_tokens: 0, audio_output_tokens: 0,
              tier: :standard)
  pricing = extract_pricing(model)
  return nil unless pricing

  rates = pricing.dig(:text_tokens, tier) or return nil

  result = {
    input: cost(input_tokens, rates[:input_per_million]),
    output: cost(output_tokens, rates[:output_per_million]),
    cache_read: cost(cache_read_tokens, rates[:cache_read_input_per_million]),
    cache_write: cost(cache_write_tokens, rates[:cache_write_input_per_million]),
    reasoning: cost(reasoning_tokens, rates[:reasoning_output_per_million] || rates[:output_per_million])
  }.compact

  if audio_input_tokens > 0 || audio_output_tokens > 0
    audio = pricing.dig(:audio_tokens, tier)
    result[:audio_input] = cost(audio_input_tokens, audio[:input_per_million]) if audio
    result[:audio_output] = cost(audio_output_tokens, audio[:output_per_million]) if audio
  end

  result
end

.calculate(model, input_tokens: 0, output_tokens: 0, cache_read_tokens: 0, cache_write_tokens: 0, reasoning_tokens: 0, audio_input_tokens: 0, audio_output_tokens: 0, tier: :standard) ⇒ Float?

Calculate total cost in USD for a model invocation.

Parameters:

  • model (Ask::ModelInfo, #pricing)

    the model or a pricing hash

  • input_tokens (Integer) (defaults to: 0)
  • output_tokens (Integer) (defaults to: 0)
  • cache_read_tokens (Integer) (defaults to: 0)
  • cache_write_tokens (Integer) (defaults to: 0)
  • reasoning_tokens (Integer) (defaults to: 0)
  • audio_input_tokens (Integer) (defaults to: 0)
  • audio_output_tokens (Integer) (defaults to: 0)
  • tier (Symbol) (defaults to: :standard)

    pricing tier (:standard or :batch)

Returns:

  • (Float, nil)

    cost in USD, or nil if no pricing data



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ask/llm/cost_calculator.rb', line 22

def calculate(model, input_tokens: 0, output_tokens: 0,
              cache_read_tokens: 0, cache_write_tokens: 0,
              reasoning_tokens: 0,
              audio_input_tokens: 0, audio_output_tokens: 0,
              tier: :standard)
  pricing = extract_pricing(model)
  return nil unless pricing

  rates = pricing.dig(:text_tokens, tier) or return nil

  sum = cost(input_tokens, rates[:input_per_million])
  sum += cost(output_tokens, rates[:output_per_million])
  sum += cost(cache_read_tokens, rates[:cache_read_input_per_million])
  sum += cost(cache_write_tokens, rates[:cache_write_input_per_million])

  if reasoning_tokens > 0
    rate = rates[:reasoning_output_per_million] || rates[:output_per_million]
    sum += cost(reasoning_tokens, rate)
  end

  if audio_input_tokens > 0 || audio_output_tokens > 0
    audio = pricing.dig(:audio_tokens, tier)
    sum += cost(audio_input_tokens, audio[:input_per_million]) if audio
    sum += cost(audio_output_tokens, audio[:output_per_million]) if audio
  end

  sum
end

.per_million(model, tier: :standard) ⇒ Hash?

Per-million rates for quick display.

Parameters:

  • model (Ask::ModelInfo, #pricing)
  • tier (Symbol) (defaults to: :standard)

    (:standard or :batch)

Returns:

  • (Hash, nil)


56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ask/llm/cost_calculator.rb', line 56

def per_million(model, tier: :standard)
  pricing = extract_pricing(model)
  return nil unless pricing

  rates = pricing.dig(:text_tokens, tier) or return nil

  {
    input: rates[:input_per_million],
    output: rates[:output_per_million],
    cache_read: rates[:cache_read_input_per_million],
    cache_write: rates[:cache_write_input_per_million],
    reasoning: rates[:reasoning_output_per_million]
  }.compact
end