Pricing lookup + cost estimation, layered on llm.sdk.registry.
Data flow: (sdk/estimate-cost ...) ─► estimate-cost-for-model │ ▼ registry/lookup ─► merged ModelEntry - override tier (pricing from cost map) - live /models tier - models.dev tier (including bundled snapshot)
The hardcoded pricing snapshot that previously lived here folded into the bundled models.dev snapshot at resources/models-dev-snapshot.json (every former entry verified present with current pricing). Callers who need to inject custom pricing should use llm.sdk.registry/register-entry!, which bypasses the public registries.
The PricingEntry record shape is preserved for callers who already consume it — internally we convert ModelEntry's :model/cost map back to this shape at lookup time.
Pricing lookup + cost estimation, layered on llm.sdk.registry.
Data flow:
(sdk/estimate-cost ...) ─► estimate-cost-for-model
│
▼
registry/lookup ─► merged ModelEntry
- override tier (pricing from cost map)
- live /models tier
- models.dev tier (including bundled snapshot)
The hardcoded pricing snapshot that previously lived here folded into
the bundled models.dev snapshot at resources/models-dev-snapshot.json
(every former entry verified present with current pricing). Callers
who need to inject custom pricing should use
llm.sdk.registry/register-entry!, which bypasses the public registries.
The PricingEntry record shape is preserved for callers who already
consume it — internally we convert ModelEntry's :model/cost map back
to this shape at lookup time.(canonical-cache usage)Build a canonical :response/cache map from a usage map.
Status is :hit when the provider reported a positive cached-input-tokens count, :miss when it explicitly reported 0, and :unknown when the provider did not report cache stats at all.
The usage normalizers omit :usage/cached-input-tokens when the raw provider payload had no cache field — that absence is how :unknown propagates here.
Build a canonical :response/cache map from a usage map. Status is :hit when the provider reported a positive cached-input-tokens count, :miss when it explicitly reported 0, and :unknown when the provider did not report cache stats at all. The usage normalizers omit :usage/cached-input-tokens when the raw provider payload had no cache field — that absence is how :unknown propagates here.
(canonical-cost provider model usage)Build a canonical :response/cost map for (provider, model, usage). Returns nil when usage is nil — caller should not stamp cost on a response that has no usage to attribute it to.
Build a canonical :response/cost map for (provider, model, usage). Returns nil when usage is nil — caller should not stamp cost on a response that has no usage to attribute it to.
(cost-result->canonical result pricing breakdown)Convert a modality-specific cost-result into the public :response/cost shape. Used by non-chat drivers whose cost is based on image count, audio duration, or synthesized characters instead of chat token usage.
Convert a modality-specific cost-result into the public :response/cost shape. Used by non-chat drivers whose cost is based on image count, audio duration, or synthesized characters instead of chat token usage.
(embedding-cost usage pricing)Cost for an embedding call. Embeddings only meter input tokens.
Cost for an embedding call. Embeddings only meter input tokens.
(estimate-cost usage pricing)Compute cost from canonical Usage and a pricing-entry. Returns a cost-result map. Pure — no registry lookup.
Compute cost from canonical Usage and a pricing-entry. Returns a cost-result map. Pure — no registry lookup.
(estimate-cost-for-model provider model usage)Look up pricing through the registry and estimate cost for the given usage. Returns a cost-result. Works for any provider+model the merged registry knows — no manual registration required.
When the provider profile carries a :profile/cost-calculator fn, it is called with {:provider :model :usage :pricing} and must return a cost-result map. This is the escape hatch for providers whose pricing doesn't fit the default per-million-token formula (Perplexity's citation-token surcharge is the canonical example).
Look up pricing through the registry and estimate cost for the given
usage. Returns a cost-result. Works for any provider+model the
merged registry knows — no manual registration required.
When the provider profile carries a :profile/cost-calculator fn, it
is called with {:provider :model :usage :pricing} and must return a
cost-result map. This is the escape hatch for providers whose pricing
doesn't fit the default per-million-token formula (Perplexity's
citation-token surcharge is the canonical example).(estimate-openrouter-cost provider model usage)Estimate cost using OpenRouter billing-route pricing. Does not refresh live data by itself; call fetch-openrouter-pricing! when freshness matters.
Estimate cost using OpenRouter billing-route pricing. Does not refresh live data by itself; call fetch-openrouter-pricing! when freshness matters.
(fetch-openrouter-pricing!)Refresh OpenRouter's live /models catalog. This populates registry live entries under provider :openrouter, including pricing from https://openrouter.ai/api/v1/models. Returns the number of entries fetched, or 0 on failure.
Refresh OpenRouter's live /models catalog. This populates registry live entries under provider :openrouter, including pricing from https://openrouter.ai/api/v1/models. Returns the number of entries fetched, or 0 on failure.
(fetch-pricing! billing-route & {:keys [_api-key]})Refresh pricing for a billing-route by hitting the provider's live /models endpoint via registry/refresh!. Returns the number of entries fetched, or 0 when the provider lacks /models support.
Refresh pricing for a billing-route by hitting the provider's live /models endpoint via registry/refresh!. Returns the number of entries fetched, or 0 when the provider lacks /models support.
(get-openrouter-pricing provider model)Get OpenRouter billing-route pricing for provider/model.
Unlike get-pricing, this deliberately looks under provider :openrouter. Use it when the actual call is routed through OpenRouter or when comparing OpenRouter pricing against direct-provider pricing. Call fetch-openrouter-pricing! first when fresh live data is required.
Get OpenRouter billing-route pricing for provider/model. Unlike get-pricing, this deliberately looks under provider :openrouter. Use it when the actual call is routed through OpenRouter or when comparing OpenRouter pricing against direct-provider pricing. Call fetch-openrouter-pricing! first when fresh live data is required.
(get-pricing provider model)Get the pricing-entry for (provider, model), or nil when no tier of the registry has cost data for the pair.
Get the pricing-entry for (provider, model), or nil when no tier of the registry has cost data for the pair.
(image-cost {:keys [n-images width height]} pricing)Cost for an image-generation call. Pricing may be per-image (DALL-E, ElevenLabs voice clone) or per-megapixel (Imagen, Stability). Args: :n-images number of images produced :width pixels (optional, used for per-megapixel pricing) :height pixels (optional, used for per-megapixel pricing)
Cost for an image-generation call. Pricing may be per-image (DALL-E, ElevenLabs voice clone) or per-megapixel (Imagen, Stability). Args: :n-images number of images produced :width pixels (optional, used for per-megapixel pricing) :height pixels (optional, used for per-megapixel pricing)
(openrouter-model-id provider model)Return the OpenRouter model id for a provider/model pair. If model already contains a slash, it is treated as an OpenRouter id.
Return the OpenRouter model id for a provider/model pair. If model already contains a slash, it is treated as an OpenRouter id.
Best-effort mapping from SDK provider ids to OpenRouter model id prefixes.
This is intentionally conservative. OpenRouter pricing is billing-route pricing for OpenRouter, not proof of the direct provider's current direct API price. Callers can always pass the full OpenRouter model id (for example, "openai/gpt-4o") to avoid inference.
Best-effort mapping from SDK provider ids to OpenRouter model id prefixes. This is intentionally conservative. OpenRouter pricing is billing-route pricing for OpenRouter, not proof of the direct provider's current direct API price. Callers can always pass the full OpenRouter model id (for example, "openai/gpt-4o") to avoid inference.
(pricing-entry &
{:keys [input output cache-read cache-write request-cost
image-per-image image-per-megapixel
transcription-per-minute tts-per-million-chars
search-per-call source source-url pricing-version]})Construct a pricing entry. Token costs are per-million-tokens in USD. Per-modality knobs: :image-cost-per-image dollars per image returned :image-cost-per-megapixel dollars per megapixel produced :transcription-cost-per-minute dollars per minute of audio in :tts-cost-per-million-chars dollars per 1M output characters :search-cost-per-call dollars per search query (Perplexity)
Construct a pricing entry. Token costs are per-million-tokens in USD. Per-modality knobs: :image-cost-per-image dollars per image returned :image-cost-per-megapixel dollars per megapixel produced :transcription-cost-per-minute dollars per minute of audio in :tts-cost-per-million-chars dollars per 1M output characters :search-cost-per-call dollars per search query (Perplexity)
(register-pricing provider model entry)Register a caller-provided pricing entry as a registry override. Internally converts to ModelEntry shape and pushes into the override tier. The entry argument can be a legacy pricing-entry map, OR a map with :input/:output/:cache-read/:cache-write/:request-cost shorthand.
Register a caller-provided pricing entry as a registry override. Internally converts to ModelEntry shape and pushes into the override tier. The entry argument can be a legacy pricing-entry map, OR a map with :input/:output/:cache-read/:cache-write/:request-cost shorthand.
(resolve-billing-route model & {:keys [provider base-url]})Determine billing route metadata from model name, provider, and an optional base-url. Pure data — not a pricing lookup.
Determine billing route metadata from model name, provider, and an optional base-url. Pure data — not a pricing lookup.
(stamp-response-cost-and-cache response provider-id model)Augment a parsed canonical response with :response/cost and :response/cache, derived from its usage. Pure transform — does not touch the wire. When the response carries no :response/usage, stamps unknown cost and unknown cache (callers want consistent shape; honest unknowns are still surfaced).
Augment a parsed canonical response with :response/cost and :response/cache, derived from its usage. Pure transform — does not touch the wire. When the response carries no :response/usage, stamps unknown cost and unknown cache (callers want consistent shape; honest unknowns are still surfaced).
(transcription-cost {:keys [duration-seconds]} pricing)Cost for a speech-to-text call. Most providers price per-minute of audio input. duration-seconds may come from response :transcription/ duration-seconds (verbose_json) or the caller's own metering.
Cost for a speech-to-text call. Most providers price per-minute of audio input. duration-seconds may come from response :transcription/ duration-seconds (verbose_json) or the caller's own metering.
(tts-cost {:keys [characters]} pricing)Cost for a text-to-speech call. Most TTS providers (OpenAI, ElevenLabs) price per million output characters.
Cost for a text-to-speech call. Most TTS providers (OpenAI, ElevenLabs) price per million output characters.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |