Class: SolidAgent::ModelPricing
- Inherits:
-
Object
- Object
- SolidAgent::ModelPricing
- Defined in:
- lib/solid_agent/model_pricing.rb
Overview
Estimates LLM spend from token counts (USD). Generation records store tokens only; pricing is layered on top for cost reporting, so figures are always estimates.
Rates come from RubyLLM's model registry (USD per million tokens, maintained upstream per model) when that gem is available and knows the model; the static pattern table below is the fallback for aliases and self-hosted models, and a conservative blended rate covers everything else so totals stay meaningful.
Constant Summary collapse
- PRICES =
[ # [pattern, input $/1M, output $/1M] — first match wins, so mock # models ("mock-gpt-4o-mini") price free before real-model patterns. [ /mock/i, 0.0, 0.0 ], [ /gpt-4o-mini/i, 0.15, 0.60 ], [ /gpt-4o/i, 2.50, 10.00 ], [ /gpt-4\.1-nano/i, 0.10, 0.40 ], [ /gpt-4\.1-mini/i, 0.40, 1.60 ], [ /gpt-4\.1/i, 2.00, 8.00 ], [ /o3-mini|o4-mini/i, 1.10, 4.40 ], [ /claude.*(fable|mythos)/i, 10.00, 50.00 ], [ /claude.*haiku-?4/i, 1.00, 5.00 ], [ /claude.*(haiku)/i, 0.80, 4.00 ], [ /claude.*(sonnet)/i, 3.00, 15.00 ], [ /claude.*opus-(5|4-[5-9])/i, 5.00, 25.00 ], [ /claude.*(opus)/i, 15.00, 75.00 ], [ /gemini.*flash/i, 0.10, 0.40 ], [ /gemini.*pro/i, 1.25, 10.00 ], [ /llama|mistral|mixtral|qwen|deepseek/i, 0.20, 0.60 ] ].freeze
- DEFAULT_RATE =
Fallback blended rate for unknown models ($/1M input, $/1M output)
[ 1.00, 4.00 ].freeze
Class Method Summary collapse
-
.estimate(model:, input_tokens:, output_tokens:) ⇒ Float?
Estimated USD cost, nil when there is nothing to price.
-
.rate_for(model) ⇒ Array(Float, Float)
$/1M input and output token rates.
-
.registry_rate(model) ⇒ Object
Exact per-model rates from RubyLLM's registry when that gem is loaded.
- .static_rate(model) ⇒ Object
Class Method Details
.estimate(model:, input_tokens:, output_tokens:) ⇒ Float?
Returns estimated USD cost, nil when there is nothing to price.
49 50 51 52 53 54 55 56 |
# File 'lib/solid_agent/model_pricing.rb', line 49 def estimate(model:, input_tokens:, output_tokens:) input = input_tokens.to_i output = output_tokens.to_i return nil if input.zero? && output.zero? input_rate, output_rate = rate_for(model) ((input * input_rate) + (output * output_rate)) / 1_000_000.0 end |
.rate_for(model) ⇒ Array(Float, Float)
Returns $/1M input and output token rates.
59 60 61 62 63 |
# File 'lib/solid_agent/model_pricing.rb', line 59 def rate_for(model) return DEFAULT_RATE if model.blank? registry_rate(model) || static_rate(model) end |
.registry_rate(model) ⇒ Object
Exact per-model rates from RubyLLM's registry when that gem is loaded. Lookups are memoized — the registry scan is not free and cost reporting calls this per row.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/solid_agent/model_pricing.rb', line 68 def registry_rate(model) return nil unless defined?(::RubyLLM) @registry_rates ||= {} return @registry_rates[model] if @registry_rates.key?(model) @registry_rates[model] = begin info = ::RubyLLM.models.find(model.to_s) tokens = info&.pricing&.text_tokens if tokens&.input && tokens&.output [ tokens.input, tokens.output ] end rescue ::StandardError nil end end |
.static_rate(model) ⇒ Object
85 86 87 88 89 90 |
# File 'lib/solid_agent/model_pricing.rb', line 85 def static_rate(model) PRICES.each do |pattern, input_rate, output_rate| return [ input_rate, output_rate ] if model.to_s.match?(pattern) end DEFAULT_RATE end |