Tarife ve modeller
getRate o anki tarifeyi döner: tek bir birim fiyat değil, kademelerin tamamı. Hangi kademede olunduğunu ve bir gözlemin kademeler arasında nasıl bölüneceğini rate() belirler. Sürüm, scope, geçerlilik tarihi ve tarihçe uygulamanın tablosunda yaşar; lib onları görmez.
export interface Tier { /** First unit position (0-based) this tier applies to. The first tier starts at 0. */ from: number; /** Price per `per` units (per block for `package`). */ unitPriceMicroUsd: number; /** Price denominator; `per: 1_000_000` prices per million. Default 1. */ per?: number; /** Added once, the first time usage enters this tier. */ flatMicroUsd?: number;}
export interface RatePolicy { /** `per_event_up` rounds each observation up; `cumulative` rounds the running total so the period sum never drifts. */ rounding?: "per_event_up" | "cumulative"; /** Volume only: `on_crossing` emits a (usually negative) adjustment for earlier units when a tier is crossed. */ adjustmentTiming?: "on_crossing" | "none"; /** Clamp for a single observation's charge (adjustments are not clamped). */ perObservation?: { minMicroUsd?: number; maxMicroUsd?: number }; /** Which period key `usedSoFar` belongs to. A hint for `getRate`; the library does not read it. */ tierPeriod?: string;}
export interface Rate { model: RateModel; tiers: Tier[]; /** Package only: units per block. */ packageSize?: number; policy?: RatePolicy; /** Written to usage rows so each row can be traced to the tariff that priced it. */ id?: string;}Rate para birimi taşımaz. Tutarlar tamsayı mikro birimdir (MicroUsd, 1 = 1/1.000.000); adı tarihseldir, EUR tarifesinde aynı sayı mikro-EUR’dur. number kullanılır: 2^53 mikro birim ≈ 9 milyar ana birim. Hesaplar içeride kesirli sayılarla (BigInt rasyonel) yapılır, yuvarlama yalnızca sonda olur.
Üç model
Bölüm başlığı “Üç model”import { defineRate, rate, type Tier } from "pricemeter";
const tiers: Tier[] = [ { from: 0, unitPriceMicroUsd: 50_000 }, { from: 10_000, unitPriceMicroUsd: 45_000 },];const graduated = defineRate({ model: "graduated", tiers });const volume = defineRate({ model: "volume", tiers, policy: { adjustmentTiming: "on_crossing" } });const pkg = defineRate({ model: "package", packageSize: 1_000, tiers }); // price per block
rate({ rate: graduated, usedSoFar: 9_990, quantity: 20 }).totalMicroUsd; // 950_000rate({ rate: volume, usedSoFar: 9_990, quantity: 20 }).totalMicroUsd; // 900_000rate({ rate: volume, usedSoFar: 9_990, quantity: 20 }).adjustmentMicroUsd; // −49_950_000rate({ rate: pkg, usedSoFar: 9_990, quantity: 20 }).totalMicroUsd; // 45_000 (1 new block)| Model | Hesap | Örnek: usedSoFar 9990, quantity 20, kademeler [0: 50000, 10000: 45000] |
|---|---|---|
graduated |
Her dilim kendi kademesinin fiyatından; kademeye ilk girişte flatMicroUsd |
10×50.000 + 10×45.000 = 950.000 |
volume |
Gözlemin tamamı, son birimin düştüğü kademenin fiyatından; on_crossing ise önceki birimler için ayrı düzeltme kalemi |
20×45.000 = 900.000; düzeltme −9.990×5.000 = −49.950.000 |
package |
ceil((u+q)/size) − ceil(u/size) yeni blok × blok fiyatı (kademe fiyatı blok başınadır) |
size 1000 → 1 yeni blok, 10. blok 2. kademede → 45.000 |
Üç model yeter:
- tek birim fiyat = tek kademeli
graduated; - sabit ücret = tek kademe,
unitPriceMicroUsd: 0,flatMicroUsd(B11); - yüzde =
quantityişlem tutarıdır,per: 1_000_000(29.000 / 1.000.000 = %2,9; B22); - oranlama =
perile ölçek: koltuk-saniye / ay-saniye (B9).
Bir tarife tek model taşır. Bileşiklik kademe alanlarından ve çok satırlı observe’dan gelir; iç içe tarife yoktur.
per ve sabit ücret
Bölüm başlığı “per ve sabit ücret”perfiyatın paydasıdır:{ unitPriceMicroUsd: 150_000, per: 1_000_000 }milyon token başına $0,15’tir. Varsayılan 1.flatMicroUsd, kullanım o kademeye ilk girdiğinde bir kez eklenir; yaniusedSoFar ≤ from < usedSoFar + quantityolan gözlemde. İlk kademedeki sabit ücret dönemin ilk gözleminde düşer. Her gözlemde tekrar etmesi gereken bir sabit ücret için her gözlem kendi dönemidir:getRateusedSoFar: 0döner (tierPeriod: "observation", A97).
Yuvarlama
Bölüm başlığı “Yuvarlama”// $0.15 per 1M tokens = 0.15 micro-USD per token. 1,000 calls of 3 tokens each:const perEvent = defineRate({ model: "graduated", tiers: [{ from: 0, unitPriceMicroUsd: 150_000, per: 1_000_000 }] });const cumulative = defineRate({ ...perEvent, policy: { rounding: "cumulative" } });
let a = 0;let b = 0;for (let used = 0; used < 3_000; used += 3) { a += rate({ rate: perEvent, usedSoFar: used, quantity: 3 }).totalMicroUsd; // each call rounds up to 1 b += rate({ rate: cumulative, usedSoFar: used, quantity: 3 }).totalMicroUsd; // floor(T(u+q)) − floor(T(u))}// a === 1_000, b === 450 (exactly 3,000 × 0.15)rounding |
Hesap | Ne zaman |
|---|---|---|
per_event_up (varsayılan) |
Her gözlem yukarı yuvarlanır | Gözlem başına tutarın anlamlı olduğu her yer |
cumulative |
floor(T(u+q)) − floor(T(u)); T dönemin kesin toplamı |
Çok küçük birim fiyatlar (LLM token): dönem toplamı sapmasız |
cumulative tam teleskopiktir: dönemin gözlemleri toplamı, dönemin tek seferde fiyatlanmasıyla aynıdır (A83). Kesin toplam için usedSoFar gerekir. T’nin tanımsız olduğu durumda — volume ile adjustmentTiming: "none" — kütüphane bir önceki gözlemden kalan küsuratı (carry, [0, 1)) ister: getRate onu { rate, usedSoFar, carry } içinde döner. Her usage satırı detail.carry’yi taşır.
Gözlem başı taban ve tavan
Bölüm başlığı “Gözlem başı taban ve tavan”// 2.9% + $0.30, at least $0.50, at most $5 per transaction (quantity = amount in micro-USD)const card = defineRate({ model: "graduated", tiers: [{ from: 0, unitPriceMicroUsd: 29_000, per: 1_000_000, flatMicroUsd: 300_000 }], policy: { perObservation: { minMicroUsd: 500_000, maxMicroUsd: 5_000_000 }, tierPeriod: "observation" },});rate({ rate: card, usedSoFar: 0, quantity: 1_000_000 }); // { totalMicroUsd: 500_000, clamped: "min" }rate({ rate: card, usedSoFar: 0, quantity: 100_000_000 }); // { totalMicroUsd: 3_200_000 }rate({ rate: card, usedSoFar: 0, quantity: 500_000_000 }); // { totalMicroUsd: 5_000_000, clamped: "max" }policy.perObservation tek bir gözlemin tutarını [minMicroUsd, maxMicroUsd] aralığına kırpar ve satırı clamped: "min" | "max" ile işaretler. Hacim düzeltmesi kırpılmaz; miktarı 0 olan gözlem kırpılmaz.
Hold üst sınırı
Bölüm başlığı “Hold üst sınırı”Bir hold’un ayırdığı tutar, gözlemin nereye düşeceği bilinmeden geçerli en kötü durumdur (A80):
- en pahalı kademenin birim fiyatı (
unitPriceMicroUsd / per) × adet (package:ceil(q/size)blok), - + tarifedeki tüm
flatMicroUsd’ler, - + (
volume+on_crossing)usedSoFar × (pmax − pmin), perObservationile kırpılır.
rate() bunu her gözlem için holdUpperBound alanında da döner; ayrıca holdUpperBound(rate, quantity, usedSoFar) olarak dışa açıktır.
usedSoFar
Bölüm başlığı “usedSoFar”Bu hesabın bu sayaçta bu dönemde şimdiye kadarki adedi. Kademe bölme için gerekir ve sessizce 0 varsayılmaz (A58, A82). Şunlardan biri varsa zorunludur:
| Tarife | usedSoFar |
|---|---|
| Birden çok kademe | zorunlu |
volume ya da package |
zorunlu |
Herhangi bir kademede flatMicroUsd (tek kademe bile: ilk girişi bilmek gerekir) |
zorunlu |
cumulative yuvarlama, carry yoksa |
zorunlu |
Tek kademeli, sabit ücretsiz graduated |
gerekmez |
Eksikse sonuç { ok: false, reason: "usage_required", meter } olur. Kaynağı getRate’tir; observe(..., { usedSoFar }) onu ezer (ör. Durable Object içinde sayaç zaten elde). Aynı çağrıda aynı (meter, dims) satırları birbirinin usedSoFar’ını ilerletir (A84). Hangi dönemin sayacının kullanılacağı uygulamanındır; policy.tierPeriod bu seçimi tarifeye not eden bir ipucudur, lib onu okumaz (bkz. /calendar).
validateRate
Bölüm başlığı “validateRate”import { validateRate } from "pricemeter";
validateRate({ model: "graduated", tiers: [{ from: 10, unitPriceMicroUsd: 1.5 }] });// → { ok: false, errors: [// { code: "first_tier_from", path: ["tiers", 0, "from"], message: "the first tier must start at 0" },// { code: "invalid_price", path: ["tiers", 0, "unitPriceMicroUsd"], message: "unitPriceMicroUsd must be a non-negative integer" } ] }Kurallar: ilk kademe from = 0; from kesin artan; fiyatlar ve sabit ücretler ≥ 0 tamsayı; per ≥ 1 tamsayı; packageSize ≥ 1 ve yalnızca package’da; adjustmentTiming yalnızca volume’da; min ≤ max. graduated için “artmayan fiyat” kuralı yoktur: B3’ün [0: 0, 500: p] tarifesi artan fiyatlıdır (A79).
Hata kodları: invalid_shape, invalid_model, empty_tiers, first_tier_from, invalid_from, tiers_not_increasing, invalid_price, invalid_per, invalid_flat, invalid_package_size, package_size_not_allowed, adjustment_timing_not_allowed, invalid_policy, min_gt_max. Her hata { code, path, message }.
Başarılı doğrulama dondurulmuş, markalı bir kopya döner: ValidRate. getRate ham obje dönebilir; doğrudan price() ya da rate() çağıran ValidRate vermek zorundadır. defineRate(rate) aynı işi yapar ama hata durumunda RateError fırlatır; koddaki sabit tarifeler ve testler için.