AbstractOptionalpriceUnitType: MJAIModelPriceUnitTypeEntityProtected Optional ReadonlyPriceThe MJ: AI Model Price Unit Types row this driver was resolved for, when the caller supplied
it — AIEngineBase.GetPriceCalculator always does.
Optional so any subclass outside this repo — which predates the parameter entirely — keeps working: extra constructor arguments are inert in JS. When present it is authoritative for UnitsPerBillingUnit, which is what makes that column mean something rather than decorate a form.
Protected AbstractDeclaredSeconds per unit of the price row's period — 60 for a per-minute rate, 3600 for per-hour.
Declared through the inherited BasePriceUnitType.DeclaredUnitsPerBillingUnit rather
than a separate SecondsPerUnit hook, so a time driver's divisor is readable by the same
generic accessor as every other driver's. Two names for one number is what let the exported
divisor table drift from the drivers in the first place.
The base measure this driver prices. Callers must hand it quantities in this measure — a driver that prices audio never receives token counts, and vice versa.
Defaults to Tokens, which is what every driver that predates continuous-media pricing
measures, so existing subclasses need not declare it.
How many quantities in this driver's UnitKind make up ONE billed unit — 1,000,000 for a per-million-tokens rate, 3,600 for a per-hour rate, 1 for a per-image rate.
The catalog row wins when one was supplied, and GetPriceCalculator always supplies it.
The compiled-in literal is the fallback for a driver instantiated directly (tests, and any
consumer that wants a scale without loading the engine).
Reading the column rather than the literal is what makes UnitsPerBillingUnit mean something.
With the literal authoritative, the column was decoration: an admin could edit Per Hour to
7200 through the generated form, save successfully, and change nothing about how anything
priced — the mirror image of B60, where the data was present and the code ignored it. A
non-positive or non-finite column value falls through to the literal rather than poisoning a
cost with Infinity/NaN; the database's CK_AIModelPriceUnitType_UnitsPerBillingUnit CHECK (> 0) is the real guard, and reaching this fallback means something bypassed it.
Prices a run from its quantities, whatever measure those are in. This is the entry point callers should use: it is the only one that works uniformly across token and continuous-media pricing, so a caller need not branch on which kind of model it just ran.
The default delegates to CalculateNormalizedCostWithCache, so every existing driver — including subclasses outside this repo — gets it for free with unchanged behavior.
Callers are responsible for handing over quantities in this driver's UnitKind;
AIEngineBase.CalculateModelCost verifies that before calling.
Calculates normalized cost based on each sub-classes specific algorithm.
The two quantities are expressed in this driver's UnitKind: token counts for the token drivers, seconds of audio for the time drivers, a count of images for the per-image driver. The parameter names are historical — they predate any non-token unit type.
The active cost configuration
Seconds of input audio (e.g. audio submitted for transcription)
Seconds of output audio (e.g. synthesized speech)
The calculated cost
Cache-aware cost calculation: prices the three input buckets (uncached/net-new, cache reads, cache writes) at their own per-unit rates, plus output. Cache reads/writes use CacheReadPricePerUnit / CacheWritePricePerUnit when recorded on the cost row; when those are NULL they fall back to InputPricePerUnit, which makes the result identical to the legacy single-bucket pricing. This is the entry point cost calculators should prefer.
The default implementation here preserves the legacy behavior (all input at the input rate) so any external BasePriceUnitType subclass that only overrides CalculateNormalizedCost keeps working unchanged. The built-in per-unit types below override it to apply per-bucket rates.
ProtectedInternalProtectedInternalPer-bucket cost math shared by the built-in unit types. Cache rates fall back to the input rate when not populated, so cost is unchanged until a model/vendor records a distinct cache rate. All buckets are normalized by the same divisor (e.g. 1,000,000 for per-1M-tokens).
Base for the drivers that price audio by elapsed time.
Quantities arrive in SECONDS regardless of the rate's period, and the subclass supplies the divisor that converts. Recording seconds rather than the vendor's billing period is what lets one measured duration be priced against a per-minute row from one vendor and a per-hour row from another without the run having to know which it will be billed by.
The cache buckets play no part: no transcription or speech vendor caches audio, so the inherited BasePriceUnitType.CalculateNormalizedCostWithCache default — which folds them into input at the input rate — is both correct and inert here.