Class: Insika::Telemetry::Pricing
- Inherits:
-
Object
- Object
- Insika::Telemetry::Pricing
- Defined in:
- lib/insika/telemetry/pricing.rb
Overview
Estimated cost of a turn, from a rates table given as DATA (item 16 / P4).
Insika never ships prices: they change weekly, differ per contract and per region, and a stale table in the engine would be worse than no number at all. The operator declares the rates, Insika only multiplies — which is why the attribute is documented as an ESTIMATE, not a bill.
Rates are USD per million tokens (the unit every provider publishes):
{ "deepseek-chat" => { "input" => 0.27, "output" => 1.10,
"cached_input" => 0.07, "cache_write" => 0.34 } }
A key matches the model id the provider reports, with or without the
provider/ prefix (deepseek/deepseek-chat and deepseek-chat both hit the
entry above). An UNKNOWN model -> nil: a missing price is not a zero cost, so
nothing is emitted and the dashboard shows a gap instead of a lie.
Token accounting (matches Executor#usage_of, which mirrors the providers):
cached_tokens is a SUBSET of input_tokens, cache_creation_tokens is not.
cached_inputgiven -> cached tokens are billed at that rate and subtracted from the fresh input; absent -> they stay at the input rate.cache_writegiven -> cache-creation tokens billed at that rate; absent -> at the input rate (they are input tokens the provider did write).
Constant Summary collapse
- PRECISION =
12 decimals: a single cheap turn can cost ~1e-6 USD, and rounding is only here to keep float noise out of the exported attribute.
12
Class Method Summary collapse
-
.parse(json) ⇒ Object
Parses the operator's table.
Instance Method Summary collapse
-
#cost(usage) ⇒ Object
-> Float (USD) | nil when the model is absent/unpriced.
- #empty? ⇒ Boolean
-
#initialize(rates) ⇒ Pricing
constructor
rates: Hash of model id -> Hash of rate name -> USD per million tokens.
Constructor Details
#initialize(rates) ⇒ Pricing
rates: Hash of model id -> Hash of rate name -> USD per million tokens. Anything not shaped like that is ignored (a bad table degrades to "no cost", never to a wrong number).
38 39 40 |
# File 'lib/insika/telemetry/pricing.rb', line 38 def initialize(rates) @rates = normalize(rates) end |
Class Method Details
.parse(json) ⇒ Object
Parses the operator's table. Accepts a JSON object; anything else (blank, malformed, not an object) -> an EMPTY Pricing, never an exception: telemetry config must not be able to stop a boot.
68 69 70 71 72 73 74 75 |
# File 'lib/insika/telemetry/pricing.rb', line 68 def self.parse(json) return new({}) if json.nil? || json.to_s.strip.empty? parsed = JSON.parse(json.to_s) new(parsed.is_a?(Hash) ? parsed : {}) rescue JSON::ParserError new({}) end |
Instance Method Details
#cost(usage) ⇒ Object
-> Float (USD) | nil when the model is absent/unpriced.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/insika/telemetry/pricing.rb', line 45 def cost(usage) return nil if usage.nil? rate = rate_for(usage[:model]) or return nil input = usage[:input_tokens].to_i cached = usage[:cached_tokens].to_i written = usage[:cache_creation_tokens].to_i output = usage[:output_tokens].to_i cached_rate = rate["cached_input"] fresh = cached_rate ? [input - cached, 0].max : input millionths = fresh * rate["input"].to_f + (cached_rate ? cached * cached_rate.to_f : 0.0) + written * (rate["cache_write"] || rate["input"]).to_f + output * rate["output"].to_f (millionths / 1_000_000.0).round(PRECISION) end |
#empty? ⇒ Boolean
42 |
# File 'lib/insika/telemetry/pricing.rb', line 42 def empty? = @rates.empty? |