Nous.Usage.Pricing (nous v0.17.1)

Copy Markdown View Source

Per-model token prices for turning a %Nous.Usage{} into a dollar figure.

Prices are quoted per 1M tokens in USD, the way every vendor publishes them. Each entry carries an :input and :output rate plus optional :cache_read (a cache hit) and :cache_write (creating a cache entry) rates, because cached input is billed at a different rate than fresh input — which is the whole reason %Nous.Usage{} tracks cache_read_input_tokens and cache_creation_input_tokens separately.

Lookup

lookup/2 is keyed by {provider, model} where provider is one of Nous.Model.provider/0 and model is the API model id string.

Resolution order:

  1. Exact match on {provider, model}.

  2. Longest-prefix match — model ids carry dated or variant suffixes ("gpt-4o-2026-05-13", "claude-opus-4-5-20251101"), so the entry whose model string is the longest prefix of the requested id wins. The prefix must end on a - boundary. Longest wins is what keeps "gemini-2.5-flash-lite-preview-09-2025" on the Flash-Lite price instead of falling into the pricier "gemini-2.5-flash" entry.

    The trade-off: an unlisted sibling variant inherits its family's price rather than reporting :unknown (ask for "o1-mini" and you get the "o1" rate). Add the sibling through the override config below when that matters.

  3. Local providers (:ollama, :lmstudio, :llamacpp, :vllm, :sglang) resolve to all-zero rates: you run the weights on your own hardware, so there is no per-token invoice. Zero is the honest answer here, not :unknown.

  4. Otherwise :unknown. A wrong price is worse than no price, so an unrecognised model never gets a guessed rate and never raises.

Operator overrides

Fine-tuned, self-hosted, brokered and enterprise-contract models cannot be priced from a public table. Supply your own rates, which are merged over the built-in table (so they also win on exact keys):

config :nous, :model_prices, %{
  {:openai, "ft:gpt-4.1-2026-01-01:acme:support:abc123"} =>
    %{input: 3.0, output: 12.0, cache_read: 0.75},
  {:groq, "llama-3.3-70b-versatile"} => %{input: 0.59, output: 0.79}
}

Omitted optional keys default conservatively: a missing :cache_read bills cache hits at the full :input rate (no published discount to apply), and a missing :cache_write bills cache creation at zero (no published surcharge, which is how OpenAI prompt caching works).

Price snapshot

Prices below were recorded on 2026-08-14 from the vendors' official pricing pages:

This is a snapshot and it will go stale — vendors reprice, and this table is only refreshed when someone remembers to. Treat the numbers as an estimate, and when they are wrong for you, correct them with config :nous, :model_prices (above) rather than waiting for a release.

Only models whose published rates were confirmed at that date are listed. Deliberate omissions:

  • Audio, image, realtime and embedding models — those are billed per modality (and sometimes per character or per second), which %Nous.Usage{} cannot distinguish from text tokens.
  • :vertex_ai — Gemini on Vertex is invoiced by Google Cloud on its own rate card, which is not the Developer API rate card.
  • :groq, :openrouter, :together, :mistral, :custom — broker and per-account pricing that varies by model and contract.

Anything not listed resolves to :unknown, which is the honest answer.

Summary

Types

A price table entry as written in the table or in operator config.

A resolved price, per 1M tokens in USD. All four rates are always present.

Functions

Look up the price for provider and model.

The effective price table: the built-in snapshot with config :nous, :model_prices merged over it.

Types

entry()

@type entry() :: %{
  :input => number(),
  :output => number(),
  optional(:cache_read) => number(),
  optional(:cache_write) => number()
}

A price table entry as written in the table or in operator config.

price()

@type price() :: %{
  input: float(),
  output: float(),
  cache_read: float(),
  cache_write: float()
}

A resolved price, per 1M tokens in USD. All four rates are always present.

Functions

lookup(provider, model)

@spec lookup(Nous.Model.provider(), String.t()) :: price() | :unknown

Look up the price for provider and model.

Returns a price/0 with all four rates filled in, or :unknown when no entry matches. See the moduledoc for the exact resolution order (exact match, then longest prefix, then zero for local providers).

Examples

iex> price = Pricing.lookup(:ollama, "llama3.3:70b")
iex> {price.input, price.output}
{0.0, 0.0}

iex> Pricing.lookup(:openai, "no-such-model")
:unknown

table()

@spec table() :: %{required({Nous.Model.provider(), String.t()}) => entry()}

The effective price table: the built-in snapshot with config :nous, :model_prices merged over it.

Entries are returned as written (optional cache rates may be absent); lookup/2 is what fills in the defaults.