Module: Smith::Pricing
- Defined in:
- lib/smith/pricing.rb
Class Method Summary collapse
-
.compute_cost(model:, input_tokens:, output_tokens:, provider: nil) ⇒ Object
Compute provider cost for a single agent call.
-
.validate_catalog!(catalog = Smith.config.pricing) ⇒ Object
Admission-time catalog validation.
Class Method Details
.compute_cost(model:, input_tokens:, output_tokens:, provider: nil) ⇒ Object
Compute provider cost for a single agent call. Two pricing shapes are supported:
Flat (existing): the catalog entry is a Hash with
`:input_cost_per_token` / `:output_cost_per_token` keys. Used
for models with a single rate across all input sizes
(Gemini 2.5 Flash, Claude Opus 4.6/4.7).
Tiered (new): the catalog entry has a `:tiers` array of bracket
hashes, each with `:max_input_tokens` (nil = unbounded ceiling),
`:input_cost_per_token`, `:output_cost_per_token`. Tiers are
walked in order; the first whose `max_input_tokens` covers the
call's input_tokens picks the rate. Used for models with
long-context premium pricing (Gemini 2.5 Pro: $1.25/$10 below
200K input tokens, $2.50/$15 above).
Catalog keys are normalized once per assigned catalog object:
["provider", "model"] / [:provider, :model] -> provider-qualified
"provider/model" (first slash splits) -> provider-qualified
"model" / :model (no slash) -> legacy model-only
Lookup policy: a provider-qualified lookup reads only qualified
entries and NEVER prices from a legacy model-only rate; unpriced
usage stays visibly unpriced (nil cost). This method never raises,
so accounting paths cannot mask a provider error with a pricing
configuration error. The legacy-only-key policy is enforced at
catalog admission time via validate_catalog!.
32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/smith/pricing.rb', line 32 def self.compute_cost(model:, input_tokens:, output_tokens:, provider: nil) catalog = Smith.config.pricing return nil unless catalog.is_a?(Hash) entry = lookup_entry(normalized_index(catalog), model, provider) return nil unless entry input_rate, output_rate = resolve_rates(entry, input_tokens) return nil unless input_rate (input_tokens * input_rate) + (output_tokens * output_rate) end |
.validate_catalog!(catalog = Smith.config.pricing) ⇒ Object
Admission-time catalog validation. Call when the pricing catalog
is assigned (config.pricing = Smith::Pricing.validate_catalog!(catalog))
so misconfiguration fails at configuration time instead of inside
an in-flight accounting path. Rejects legacy model-only keys,
unrecognized key shapes, non-Hash entries, and keys that collide
after normalization. Returns the catalog for assignment chaining.
57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/smith/pricing.rb', line 57 def self.validate_catalog!(catalog = Smith.config.pricing) return catalog if catalog.nil? raise Smith::PricingConfigurationError, "pricing catalog must be a Hash" unless catalog.is_a?(Hash) catalog.each_with_object({}) do |(key, entry), seen| provider, model = validated_key!(key) validate_entry!(key, entry) reject_collision!(seen, key, provider, model) end catalog end |