usage-tab 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Registry schema — model identity and pricing periods.
3
+ *
4
+ * This is the contract `usage-tab` consumes directly, and that `chat-fit`
5
+ * optionally consumes for context-window helpers. Changing a field here is
6
+ * a breaking change for both packages.
7
+ */
8
+ type ProviderId = 'openai' | 'anthropic' | 'google' | 'azure-openai' | 'aws-bedrock' | 'groq' | 'mistral' | 'cohere' | 'together' | 'openrouter';
9
+ /**
10
+ * Provenance for a registry entry: where the data came from and when it was
11
+ * last reviewed against that source. Distinct from a pricing period's own
12
+ * `sourceUrl`/`observedAt`, which track the price specifically — a model's
13
+ * identity metadata (context window, capabilities) and its price can be
14
+ * confirmed on different dates.
15
+ */
16
+ interface RegistrySource {
17
+ readonly url: string;
18
+ /** ISO date (YYYY-MM-DD) the data was observed/reviewed. */
19
+ readonly observedAt: string;
20
+ readonly notes?: readonly string[];
21
+ }
22
+ /**
23
+ * A priced interval for one model. Rates are decimal STRINGS end to end — a
24
+ * price must never pass through a JavaScript `number` on its authoritative
25
+ * path: money must never be calculated with binary floating point.
26
+ * Consumers (e.g. `usage-tab`) parse them into fixed-point integers
27
+ * themselves; this package never does arithmetic on them.
28
+ *
29
+ * A period is active for `effectiveFrom <= at < effectiveTo` — `effectiveTo`
30
+ * is exclusive so a restated price takes over cleanly at midnight of its own
31
+ * effective date, with no shared instant between two periods. Omit
32
+ * `effectiveTo` for the current, open-ended period. See `pricing-period.ts`.
33
+ */
34
+ interface PricingPeriod {
35
+ /** ISO date (YYYY-MM-DD) the period starts being active (inclusive). */
36
+ readonly effectiveFrom: string;
37
+ /** ISO date (YYYY-MM-DD) the period stops being active (exclusive). */
38
+ readonly effectiveTo?: string;
39
+ readonly currency: 'USD';
40
+ readonly unit: 'per-million-tokens';
41
+ readonly input: string;
42
+ readonly output: string;
43
+ readonly cachedInput?: string;
44
+ readonly cacheWrite?: string;
45
+ readonly reasoning?: string;
46
+ /** Multiplier applied to all token usage under the provider's batch API, e.g. `"0.5"` for a 50% discount. */
47
+ readonly batchMultiplier?: string;
48
+ /**
49
+ * `true` when this period records the cheapest of several published
50
+ * pricing tiers for the same model — e.g. Google's <=200k-token prompt-size
51
+ * tier (a higher rate applies above it), or Azure OpenAI's Global
52
+ * deployment + short-context + Standard service tier (Data Zone/Regional
53
+ * deployment, long-context, and Priority Processing each cost more). This
54
+ * schema has no dimension for the tier itself — only a flag that one
55
+ * exists — because the extra dimensions differ per provider (prompt size,
56
+ * deployment region, context length, service tier) and are not uniform
57
+ * enough to model generically. Consumers (`usage-tab`) surface a warning
58
+ * whenever a calculation uses a period with this flag set, since real usage
59
+ * under a different tier costs more than the calculation reports — never
60
+ * omit this on a period whose provider file documents a higher unrecorded
61
+ * tier: accuracy matters more than breadth here, and under-reporting cost
62
+ * is the worst failure a pricing library can make.
63
+ * Omit entirely (never `false`) when the recorded rate is the only
64
+ * published tier.
65
+ */
66
+ readonly cheapestTier?: true;
67
+ /**
68
+ * URL of the authoritative documentation this rate was read from.
69
+ * Mandatory: every pricing period carries a source URL, an observed date,
70
+ * and an effective date.
71
+ */
72
+ readonly sourceUrl: string;
73
+ /** ISO date (YYYY-MM-DD) the rate was observed/reviewed against `sourceUrl`. */
74
+ readonly observedAt: string;
75
+ readonly notes?: readonly string[];
76
+ }
77
+ interface ModelDescriptor {
78
+ readonly canonicalId: string;
79
+ readonly provider: ProviderId;
80
+ readonly aliases: readonly string[];
81
+ readonly family?: string;
82
+ readonly tokenizerFamily?: string;
83
+ readonly contextWindow?: number;
84
+ readonly capabilities?: readonly string[];
85
+ readonly pricing?: readonly PricingPeriod[];
86
+ readonly source?: RegistrySource;
87
+ }
88
+
89
+ /**
90
+ * Stable error types for `@llm-kit/model-registry`.
91
+ *
92
+ * Every error extends `Error`, sets a stable string `code`, and carries an
93
+ * actionable message. Callers branch on `code`, never on `message` text —
94
+ * messages may be reworded.
95
+ */
96
+ interface ModelCandidate {
97
+ readonly provider: string;
98
+ readonly canonicalId: string;
99
+ }
100
+ /**
101
+ * Raised by `resolveModel` (resolution step 6) when a requested model id
102
+ * matches nothing — not a custom override, not a provider-qualified
103
+ * canonical id or alias, not a globally unambiguous alias (only reachable
104
+ * when no provider was supplied — a provider qualifier is a constraint, not
105
+ * a hint), and no fallback was configured or matched.
106
+ */
107
+ declare class UnknownModelError extends Error {
108
+ readonly code = "UNKNOWN_MODEL";
109
+ readonly requestedId: string;
110
+ readonly provider?: string;
111
+ /**
112
+ * Set only when the lookup was provider-qualified and `requestedId` exists
113
+ * under one or more *other* providers — the single most actionable fact
114
+ * for this failure. Sorted, deduplicated, and never the full registry:
115
+ * just the providers that actually carry this id.
116
+ */
117
+ readonly otherProviders?: readonly string[];
118
+ constructor(requestedId: string, provider?: string, otherProviders?: readonly string[]);
119
+ }
120
+ /**
121
+ * Raised by `resolveModel` whenever a requested model id would resolve to
122
+ * more than one distinct model. Ambiguity is always an error, never a guess —
123
+ * this is the single most important invariant this package enforces, since
124
+ * silently picking one candidate is how a caller gets billed against the
125
+ * wrong model.
126
+ */
127
+ declare class AmbiguousAliasError extends Error {
128
+ readonly code = "AMBIGUOUS_ALIAS";
129
+ readonly requestedId: string;
130
+ readonly candidates: readonly ModelCandidate[];
131
+ constructor(requestedId: string, candidates: readonly ModelCandidate[]);
132
+ }
133
+ /**
134
+ * Raised by `selectPricingPeriod` when the `at` lookup value cannot be
135
+ * parsed as a date. Distinct from "no period matches" (a normal result,
136
+ * represented by `undefined` — see `pricing-period.ts`), which is not an
137
+ * error.
138
+ */
139
+ declare class InvalidLookupDateError extends Error {
140
+ readonly code = "INVALID_LOOKUP_DATE";
141
+ readonly value: string;
142
+ constructor(value: string);
143
+ }
144
+
145
+ type ModelMatchKind = 'override' | 'canonical-qualified' | 'alias-scoped' | 'alias-global' | 'fallback';
146
+
147
+ /**
148
+ * GENERATED FILE — DO NOT EDIT BY HAND.
149
+ *
150
+ * Produced by `scripts/generate-model-registry.ts` from the reviewed source
151
+ * files under `docs/provider-data/`. Editing this file directly is forbidden.
152
+ *
153
+ * Regenerate: pnpm exec tsx scripts/generate-model-registry.ts
154
+ * Verify: pnpm exec tsx scripts/generate-model-registry.ts --check
155
+ */
156
+
157
+ declare const REGISTRY_VERSION = "registry-5af85ce1a47be918";
158
+ declare const MODEL_REGISTRY: readonly ModelDescriptor[];
159
+
160
+ /**
161
+ * Public types.
162
+ */
163
+
164
+ /**
165
+ * Normalized token usage for one request. `inputTokens` and `outputTokens`
166
+ * are the *total* reported counts; `cachedInputTokens` and `cacheWriteTokens`
167
+ * are subsets of `inputTokens`, and `reasoningTokens` is a subset of
168
+ * `outputTokens`, unless a specific provider adapter documents otherwise
169
+ * — see `normalize/anthropic.ts` and `normalize/google.ts`
170
+ * for the two adapters that must add rather than assume a subset relationship
171
+ * in the provider's own raw shape.
172
+ */
173
+ interface LlmUsage {
174
+ readonly inputTokens: number;
175
+ readonly outputTokens: number;
176
+ readonly cachedInputTokens?: number;
177
+ readonly cacheWriteTokens?: number;
178
+ readonly reasoningTokens?: number;
179
+ }
180
+ type PriceMode = 'standard' | 'batch';
181
+ interface PriceRequest {
182
+ /** The id you have — a canonical id or an alias, from any provider. */
183
+ readonly model: string;
184
+ /**
185
+ * Qualifies resolution to one provider (resolution steps 2–3). Omit to
186
+ * resolve globally (step 4). `calculateCost` also accepts a provider
187
+ * qualifier on its `options` bag (`PriceOptions.provider`, useful when a
188
+ * `PriceCalculator` was built with a default) — when both are supplied,
189
+ * `request.provider` takes precedence. Either channel populates
190
+ * `CostBreakdown.requestedProvider`, so it is never a way to tell which
191
+ * one was used.
192
+ */
193
+ readonly provider?: ProviderId | string;
194
+ /**
195
+ * Already-normalized usage, or a raw value structurally close enough to
196
+ * `LlmUsage` to be used directly (numeric `inputTokens`/`outputTokens`).
197
+ * Anything else — a raw OpenAI/Anthropic/Google response object — must go
198
+ * through the matching `normalize*Usage` adapter first; `calculateCost`
199
+ * never guesses a provider's field names for you.
200
+ */
201
+ readonly usage: LlmUsage | unknown;
202
+ /** `'batch'` applies the resolved period's `batchMultiplier` when one is published; falls back to standard pricing (with a warning) otherwise. */
203
+ readonly mode?: PriceMode;
204
+ /** Effective date for pricing-period selection. Defaults to `new Date()`. */
205
+ readonly at?: Date | string;
206
+ }
207
+ interface CostLine {
208
+ /** Token count this line was billed against. */
209
+ readonly tokens: number;
210
+ /** The decimal USD-per-million-tokens rate actually applied, after any batch multiplier. */
211
+ readonly rate: string;
212
+ readonly costUsd: number;
213
+ readonly costUsdExact: string;
214
+ }
215
+ type PriceWarningCode = 'UNSUPPORTED_USAGE_FIELD' | 'CACHED_EXCEEDS_INPUT' | 'REASONING_EXCEEDS_OUTPUT' | 'BATCH_PRICING_UNAVAILABLE' | 'PARTIAL_TIER_PRICING' | 'REASONING_PRICED_AS_OUTPUT' | 'CACHED_INPUT_PRICED_AS_INPUT' | 'CACHE_WRITE_PRICED_AS_INPUT';
216
+ /**
217
+ * A non-fatal condition surfaced alongside a `CostBreakdown` — the same rule
218
+ * that keeps unsupported usage fields visible as warnings, never silently
219
+ * discarded, extended to every other place this package must not
220
+ * fail silently. Always JSON-serializable. Never a substitute for an error:
221
+ * anything that would make the reported cost meaningless (an unresolvable
222
+ * model, no priced period for the lookup date) throws instead of warning —
223
+ * see `errors.ts`.
224
+ */
225
+ interface PriceWarning {
226
+ readonly code: PriceWarningCode;
227
+ readonly message: string;
228
+ /** Usage or pricing field the warning concerns, when applicable. */
229
+ readonly field?: string;
230
+ }
231
+ interface CostBreakdown {
232
+ /** The id exactly as requested. */
233
+ readonly model: string;
234
+ /** The resolved registry entry's canonical id — may differ from `model` when `model` was an alias. */
235
+ readonly canonicalModel: string;
236
+ readonly provider: string;
237
+ /**
238
+ * How `model` was matched (registry resolution steps 1–5 — see
239
+ * `ResolveModelOptions`/`resolveModel`). Surfaced so a caller can tell a
240
+ * plain provider-qualified match (`'canonical-qualified'`/`'alias-scoped'`)
241
+ * apart from one that only succeeded via an explicitly configured
242
+ * `fallback` — the one remaining path (by design, not an oversight: see
243
+ * README "Edge cases and limitations") where `provider` can legitimately
244
+ * differ from the `provider` you requested.
245
+ */
246
+ readonly matchedBy: ModelMatchKind;
247
+ /** The provider qualifier exactly as requested, when one was supplied on either `request.provider` or `options.provider` (see `PriceRequest.provider`). Compare against `provider` to notice a `fallback` match that landed on a different provider. */
248
+ readonly requestedProvider?: string;
249
+ readonly currency: 'USD';
250
+ readonly input: CostLine;
251
+ readonly output: CostLine;
252
+ readonly cachedInput?: CostLine;
253
+ readonly cacheWrite?: CostLine;
254
+ readonly reasoning?: CostLine;
255
+ /** Ergonomic numeric total. Not the authoritative value — see `totalUsdExact`. */
256
+ readonly totalUsd: number;
257
+ /** Exact decimal string total, computed with integer/BigInt fixed-point arithmetic — the authoritative value; never derived from binary floating point. */
258
+ readonly totalUsdExact: string;
259
+ /** Content hash of the pricing data used (`@llm-kit/model-registry`'s `REGISTRY_VERSION`), independent of this package's own npm version. */
260
+ readonly registryVersion: string;
261
+ /** `effectiveFrom` of the pricing period actually used. */
262
+ readonly pricingEffectiveFrom: string;
263
+ readonly warnings: readonly PriceWarning[];
264
+ }
265
+ /** Return shape of every `normalize*Usage` adapter (and the internal generic fallback). */
266
+ interface NormalizedUsageResult {
267
+ readonly usage: LlmUsage;
268
+ readonly warnings: readonly PriceWarning[];
269
+ }
270
+ interface ResolveModelOptions {
271
+ readonly provider?: ProviderId | string;
272
+ readonly overrides?: readonly ModelDescriptor[];
273
+ readonly fallback?: string;
274
+ /** Registry to resolve against. Defaults to the bundled `MODEL_REGISTRY`; overriding is mainly for tests. */
275
+ readonly registry?: readonly ModelDescriptor[];
276
+ }
277
+ interface ResolvedModel {
278
+ readonly descriptor: ModelDescriptor;
279
+ readonly matchedBy: ModelMatchKind;
280
+ readonly requestedId: string;
281
+ readonly requestedProvider?: string;
282
+ }
283
+ /**
284
+ * `PriceOptions.provider` and `PriceRequest.provider` are the same
285
+ * qualifier on two channels — see `PriceRequest.provider`'s doc comment for
286
+ * the precedence rule `calculateCost` applies between them.
287
+ */
288
+ type PriceOptions = ResolveModelOptions;
289
+ interface PriceCalculatorOptions {
290
+ readonly overrides?: readonly ModelDescriptor[];
291
+ readonly fallback?: string;
292
+ readonly registry?: readonly ModelDescriptor[];
293
+ }
294
+ interface PriceCalculator {
295
+ calculateCost(request: PriceRequest, options?: PriceOptions): CostBreakdown;
296
+ resolveModel(model: string, options?: ResolveModelOptions): ResolvedModel;
297
+ }
298
+ /** Input to `createPriceOverride` — a simplified way to build one custom `ModelDescriptor` without hand-writing the full registry shape. */
299
+ interface CustomPriceInput {
300
+ readonly canonicalId: string;
301
+ /**
302
+ * Defaults to `'custom'`. Typed as a plain `string`, not `ProviderId` —
303
+ * unlike registry source data (schema-validated against the baseline
304
+ * provider list), a custom override may represent a negotiated deal with a
305
+ * vendor outside that list, and `resolveModel` never enforces `ProviderId`
306
+ * at runtime, only at the registry-generation boundary.
307
+ */
308
+ readonly provider?: string;
309
+ readonly aliases?: readonly string[];
310
+ readonly family?: string;
311
+ readonly contextWindow?: number;
312
+ readonly input: string;
313
+ readonly output: string;
314
+ readonly cachedInput?: string;
315
+ readonly cacheWrite?: string;
316
+ readonly reasoning?: string;
317
+ readonly batchMultiplier?: string;
318
+ /** ISO date. Defaults to `'1970-01-01'` (always active). */
319
+ readonly effectiveFrom?: string;
320
+ readonly effectiveTo?: string;
321
+ readonly sourceUrl?: string;
322
+ /** ISO date. Defaults to `effectiveFrom`. */
323
+ readonly observedAt?: string;
324
+ readonly notes?: readonly string[];
325
+ }
326
+
327
+ declare function calculateCost(request: PriceRequest, options?: PriceOptions): CostBreakdown;
328
+
329
+ declare function resolveModel(model: string, options?: ResolveModelOptions): ResolvedModel;
330
+
331
+ declare function createPriceCalculator(defaults?: PriceCalculatorOptions): PriceCalculator;
332
+
333
+ /**
334
+ * Custom/negotiated pricing overrides — an exact custom override is the
335
+ * highest-precedence resolution step.
336
+ */
337
+
338
+ /**
339
+ * Builds a single-period `ModelDescriptor` from a simplified rate shape, for
340
+ * passing as `options.overrides` to `calculateCost`/`resolveModel`/
341
+ * `createPriceCalculator` — without hand-writing the full registry schema
342
+ * (`sourceUrl`, `observedAt`, etc. are given sensible, deterministic
343
+ * defaults rather than reading the system clock, so the result is a pure
344
+ * function of its input).
345
+ */
346
+ declare function createPriceOverride(input: CustomPriceInput): ModelDescriptor;
347
+
348
+ /**
349
+ * Stable error types this package throws directly. `resolveModel` and
350
+ * `calculateCost` also propagate `AmbiguousAliasError`, `UnknownModelError`,
351
+ * and `InvalidLookupDateError` unchanged from `@llm-kit/model-registry` (see
352
+ * `index.ts`) — they are not redefined here, so a caller catching by `code`
353
+ * only ever deals with one class per code, regardless of which package threw
354
+ * it.
355
+ *
356
+ * Every error extends `Error`, sets a stable string `code`, and carries an
357
+ * actionable message. Normal conditions —
358
+ * an unsupported usage field, a period recording only the cheapest of
359
+ * several tiers — are `PriceWarning`s (see `types.ts`), not exceptions;
360
+ * these classes are reserved for conditions where the reported cost would
361
+ * otherwise be meaningless or silently wrong.
362
+ */
363
+ /**
364
+ * Raised by `calculateCost` when `selectPricingPeriod` finds no period
365
+ * covering the lookup date — including a lookup date that precedes the
366
+ * first known period for a model. Returning a zero or default cost here
367
+ * would be exactly the silent under-reporting this package exists to
368
+ * prevent, so this is a thrown error, never a warning.
369
+ */
370
+ declare class NoPricingPeriodError extends Error {
371
+ readonly code = "NO_PRICING_PERIOD";
372
+ readonly canonicalModel: string;
373
+ readonly provider: string;
374
+ readonly at: string;
375
+ constructor(canonicalModel: string, provider: string, at: string);
376
+ }
377
+ /**
378
+ * Raised when `PriceRequest.usage` is neither an `LlmUsage`-shaped object
379
+ * (numeric `inputTokens`/`outputTokens`) nor something a `normalize*Usage`
380
+ * adapter already turned into one. `calculateCost` never guesses a raw
381
+ * provider response's field names — call the matching adapter first.
382
+ */
383
+ declare class InvalidUsageError extends Error {
384
+ readonly code = "INVALID_USAGE";
385
+ constructor(reason: string);
386
+ }
387
+ /**
388
+ * Raised when a token count is negative, non-integer, or not a JS safe
389
+ * integer. Very large aggregate token counts are expected to work up to
390
+ * `Number.MAX_SAFE_INTEGER`; beyond that, or below zero, or fractional, the
391
+ * count is rejected rather than silently coerced.
392
+ */
393
+ declare class InvalidTokenCountError extends Error {
394
+ readonly code = "INVALID_TOKEN_COUNT";
395
+ readonly field: string;
396
+ readonly value: number;
397
+ constructor(field: string, value: number);
398
+ }
399
+ /**
400
+ * Raised when a pricing-period rate string does not parse as an exact
401
+ * non-negative decimal. Registry data is validated at generation time
402
+ * (`@llm-kit/model-registry`'s schema), so this only fires for a malformed
403
+ * `createPriceOverride`/custom `ModelDescriptor` supplied at the call site.
404
+ */
405
+ declare class InvalidRateError extends Error {
406
+ readonly code = "INVALID_RATE";
407
+ readonly field: string;
408
+ readonly value: string;
409
+ constructor(field: string, value: string);
410
+ }
411
+
412
+ declare function normalizeOpenAIUsage(value: unknown): NormalizedUsageResult;
413
+
414
+ declare function normalizeAnthropicUsage(value: unknown): NormalizedUsageResult;
415
+
416
+ declare function normalizeGoogleUsage(value: unknown): NormalizedUsageResult;
417
+
418
+ declare function normalizeOpenAICompatibleUsage(value: unknown): NormalizedUsageResult;
419
+
420
+ export { AmbiguousAliasError, type CostBreakdown, type CostLine, type CustomPriceInput, InvalidLookupDateError, InvalidRateError, InvalidTokenCountError, InvalidUsageError, type LlmUsage, MODEL_REGISTRY, type ModelCandidate, type ModelDescriptor, NoPricingPeriodError, type NormalizedUsageResult, type PriceCalculator, type PriceCalculatorOptions, type PriceMode, type PriceOptions, type PriceRequest, type PriceWarning, type PriceWarningCode, type PricingPeriod, type ProviderId, REGISTRY_VERSION, type RegistrySource, type ResolveModelOptions, type ResolvedModel, UnknownModelError, calculateCost, createPriceCalculator, createPriceOverride, normalizeAnthropicUsage, normalizeGoogleUsage, normalizeOpenAICompatibleUsage, normalizeOpenAIUsage, resolveModel };