@broberg/ai-sdk 0.39.0 → 0.41.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.
package/dist/pricing.d.ts CHANGED
@@ -1,22 +1,108 @@
1
+ /** A price that is not per-token.
2
+ *
3
+ * Six units, not the two F050 was carded for. The form-forbidding test found ten
4
+ * MORE hand-written price constants than super's report named — speech billed per
5
+ * 1000 characters, transcription per audio-minute, OCR per page, a flat LoRA
6
+ * training fee, and two more per-image tables. Moving only the four that had been
7
+ * noticed would have left `caveats` reporting "covered" while four other units sat
8
+ * in four adapters' private constants: the same blindness, one unit along. */
9
+ type MediaUnit = "per_sec" | "per_image" | "per_1k_chars" | "per_min" | "per_page" | "per_training";
10
+ interface MediaPrice {
11
+ unit: MediaUnit;
12
+ /** USD per second (video) or per generated image. */
13
+ usd: number;
14
+ /** ISO date a HUMAN last checked this against the vendor's published price.
15
+ * Not derived from the monthly token job — that job never sees these. */
16
+ checkedAt: string;
17
+ /** Where the number comes from, so the next person can re-check it. */
18
+ source: string;
19
+ }
20
+ /** When a human last walked the whole media table against vendor pricing pages.
21
+ *
22
+ * ONE date for the table rather than trusting every row's own: a row added later
23
+ * with a fresh date would otherwise pull the table's average forward while the
24
+ * older rows sat unrevised. Freshness reads the OLDEST answer, and this is it. */
25
+ declare const MEDIA_PRICING_CHECKED_AT = "2026-09-05";
26
+ /** Default clip length billed when the caller passes no `durationSec`.
27
+ *
28
+ * It is an ASSUMPTION, not a measurement — the provider does not tell us how long
29
+ * the clip it returned actually is. Every cost derived from it is stamped
30
+ * `costBasis: "estimated"` so a consumer (and upmetrics) can tell it apart from a
31
+ * cost computed off a real duration. super reported using such a number as if it
32
+ * were measured and passing it on to Christian; that is the damage this stamp
33
+ * removes. */
34
+ declare const DEFAULT_CLIP_SEC = 8;
35
+ /** Look up a non-token price. Unknown → undefined (never a fabricated 0). */
36
+ declare function getMediaPrice(provider: string, model: string): MediaPrice | undefined;
37
+
1
38
  type PriceRegion = "eu" | "us" | "cn" | "other";
2
- interface ModelPrice {
39
+ /** Fields every price row carries, whatever it is billed in. */
40
+ interface BasePrice {
3
41
  /** Vendor/provider prefix (e.g. "deepseek", "anthropic"). */
4
42
  provider: string;
5
43
  /** Model id (OpenRouter-style "vendor/model", or the bare model for curated entries). */
6
44
  model: string;
7
45
  /** Human label, when known. */
8
46
  name?: string;
47
+ /** GDPR region derived from the PROVIDER NAME — a rough grouping, not a residency
48
+ * claim. It is `"other"` for vertex/bfl/fal/azure precisely because those take a
49
+ * configurable endpoint, so their region is a property of the CALL and not of the
50
+ * model. For an actual residency answer use `regionOfHost()` before the call, or
51
+ * `usage.region` after it. */
52
+ region: PriceRegion;
53
+ /** "curated" = authoritative hand-maintained number; "inventory" = from inventory.json. */
54
+ source: "curated" | "inventory";
55
+ /** ISO date this row was last verified. Media rows carry a HUMAN-set date; token
56
+ * rows inherit the inventory snapshot's. */
57
+ checkedAt?: string;
58
+ }
59
+ /** A model billed per token. */
60
+ interface TokenModelPrice extends BasePrice {
61
+ unit: "per_1m_tokens";
9
62
  /** USD per 1M input tokens. */
10
63
  inputPer1M: number;
11
64
  /** USD per 1M output tokens. */
12
65
  outputPer1M: number;
13
- /** Pricing unit (almost always "per_1m_tokens"). */
14
- unit: string;
15
- /** GDPR region of the host. */
16
- region: PriceRegion;
17
- /** "curated" = authoritative hand-maintained number; "inventory" = from inventory.json. */
18
- source: "curated" | "inventory";
66
+ /** Set when this model ALSO carries a non-token price — and that is the one the SDK
67
+ * bills with (F050.2).
68
+ *
69
+ * Gemini's image models are the real case: they have honest per-token rates for the
70
+ * prompt AND a per-image price for the output, and `ai.image` charges the per-image
71
+ * one. Returning only the token row is not WRONG the way whisper's fabricated $0 was
72
+ * — the rates are real — it just answers a question nobody asked, and hides the
73
+ * number that decides the bill. Both are true, so both are here. */
74
+ alsoBilled?: {
75
+ unit: MediaUnit;
76
+ usd: number;
77
+ checkedAt: string;
78
+ };
79
+ }
80
+ /** A model billed in anything else — per second, image, 1000 chars, minute, page,
81
+ * or training run (F050.2).
82
+ *
83
+ * **It deliberately has NO `inputPer1M`.** Until 0.40.0 a media row carried
84
+ * `inputPer1M: 0`, and super's report named the fault exactly: "a field that does not
85
+ * apply and a price that is free are the same number again" — the very distinction
86
+ * `costBasis: "unpriced"` exists for, one storey down. A 0 there is a placeholder that
87
+ * reads as a price. Splitting the type turns reading it into a COMPILE error instead. */
88
+ interface MediaModelPrice extends BasePrice {
89
+ unit: MediaUnit;
90
+ /** The price, in USD, for one of whatever `unit` names. ALWAYS set on a media row —
91
+ * read this rather than the unit-specific aliases below. */
92
+ usd: number;
93
+ /** USD per second. Set ONLY when `unit === "per_sec"`.
94
+ *
95
+ * 0.40.0 set this-or-`perImage` from a TWO-armed ternary over SIX units, so every
96
+ * non-per-second price was labelled `perImage`: `azure:tts` reported
97
+ * `perImage: 0.016` for a price that is per 1000 CHARACTERS. A confident wrong number
98
+ * under a name that lies about its own unit — worse than the 0 that was reported. */
99
+ perSec?: number;
100
+ /** USD per generated image. Set ONLY when `unit === "per_image"`. */
101
+ perImage?: number;
19
102
  }
103
+ /** A price row. Narrow on `unit` before reading rates:
104
+ * `if (p.unit === "per_1m_tokens") … else …`. */
105
+ type ModelPrice = TokenModelPrice | MediaModelPrice;
20
106
  interface PriceFilter {
21
107
  provider?: string;
22
108
  region?: PriceRegion;
@@ -25,13 +111,17 @@ interface PriceFilter {
25
111
  /** Only $0/$0 models when true; only paid when false. */
26
112
  free?: boolean;
27
113
  }
114
+ /** Every non-token price the SDK bills from (F050). Separate from
115
+ * {@link listModelPrices} because the two answer different questions and mixing
116
+ * them silently changed what an existing caller's list meant. */
117
+ declare function listMediaPrices(): MediaModelPrice[];
28
118
  /** Exact price for a model. `modelId` accepts "vendor/model", "provider:model", or a
29
119
  * bare model/basename. Returns undefined if unknown. */
30
120
  declare function getModelPrice(modelId: string): ModelPrice | undefined;
31
121
  /** Every known model price (inventory, with the curated overlay applied). */
32
- declare function listModelPrices(): ModelPrice[];
122
+ declare function listModelPrices(): TokenModelPrice[];
33
123
  /** Filter the price list (provider / region / max input rate / free-only). */
34
- declare function findModelPrices(filter?: PriceFilter): ModelPrice[];
124
+ declare function findModelPrices(filter?: PriceFilter): TokenModelPrice[];
35
125
  /** Convenience USD compute for a token-priced model; undefined if unknown / not token-priced. */
36
126
  declare function priceCall(modelId: string, inputTokens: number, outputTokens: number): number | undefined;
37
127
  /** ISO timestamp of the inventory snapshot these prices came from.
@@ -45,6 +135,17 @@ declare function pricingGeneratedAt(): string;
45
135
  * ONE constant: the `model-advisor` skill quotes the same number, and two copies of a
46
136
  * threshold drift apart until the doc and the code disagree about what "stale" means. */
47
137
  declare const PRICING_STALE_AFTER_DAYS = 35;
138
+ /** Freshness of ONE pricing unit. */
139
+ interface UnitFreshness {
140
+ unit: string;
141
+ /** How many rows the table holds in this unit. Zero means NOT COVERED — which is
142
+ * a different answer from "covered and fresh", and used to be indistinguishable. */
143
+ count: number;
144
+ /** When a check last happened for this unit. Empty = never. */
145
+ checkedAt: string;
146
+ ageDays: number | null;
147
+ stale: boolean;
148
+ }
48
149
  interface PricingFreshness {
49
150
  /** When the numbers last CHANGED. */
50
151
  generatedAt: string;
@@ -54,9 +155,24 @@ interface PricingFreshness {
54
155
  /** Days since `checkedAt`; `null` when there is no check date to measure from. */
55
156
  ageDays: number | null;
56
157
  /** True when the check is older than {@link PRICING_STALE_AFTER_DAYS} — or when
57
- * there is no check date at all. An unanswerable question is not a pass. */
158
+ * there is no check date at all. An unanswerable question is not a pass.
159
+ *
160
+ * **Scope: the TOKEN table only.** Unchanged from F046 on purpose — consumers and
161
+ * the release guard already read it. For "is anything I might bill for covered?",
162
+ * read {@link PricingFreshness.caveats}. */
58
163
  stale: boolean;
59
164
  thresholdDays: number;
165
+ /** Per-unit breakdown (F050). Every unit the SDK can bill in, whether or not the
166
+ * table has rows for it — an absent unit must show up as `count: 0`, not as an
167
+ * absent key that a caller iterating the object would never notice. */
168
+ units: UnitFreshness[];
169
+ /** Human-readable reservations, one per unit that is uncovered or stale.
170
+ *
171
+ * This is the field that makes `stale: false` honest. super measured the state it
172
+ * exists to end: `{ageDays: 0, stale: false}` on a table where per-second prices
173
+ * could not exist, so the API built to catch price drift reported "fresh" about
174
+ * numbers it structurally could not see. Empty array = no reservations. */
175
+ caveats: string[];
60
176
  }
61
177
  declare function pricingFreshness(nowMs?: number): PricingFreshness;
62
178
  /** Warn ONCE per process that these prices are old. Called from every price lookup.
@@ -69,4 +185,4 @@ declare function warnIfPricingStale(nowMs?: number): void;
69
185
  /** Test-only: forget that we already warned. */
70
186
  declare function resetPricingWarningForTests(): void;
71
187
 
72
- export { type ModelPrice, PRICING_STALE_AFTER_DAYS, type PriceFilter, type PriceRegion, type PricingFreshness, findModelPrices, getModelPrice, listModelPrices, priceCall, pricingFreshness, pricingGeneratedAt, resetPricingWarningForTests, warnIfPricingStale };
188
+ export { DEFAULT_CLIP_SEC, MEDIA_PRICING_CHECKED_AT, type MediaPrice, type MediaUnit, type ModelPrice, PRICING_STALE_AFTER_DAYS, type PriceFilter, type PriceRegion, type PricingFreshness, type UnitFreshness, findModelPrices, getMediaPrice, getModelPrice, listMediaPrices, listModelPrices, priceCall, pricingFreshness, pricingGeneratedAt, resetPricingWarningForTests, warnIfPricingStale };
package/dist/pricing.js CHANGED
@@ -1,18 +1,26 @@
1
1
  import {
2
+ DEFAULT_CLIP_SEC,
3
+ MEDIA_PRICING_CHECKED_AT,
2
4
  PRICING_STALE_AFTER_DAYS,
3
5
  findModelPrices,
6
+ getMediaPrice,
4
7
  getModelPrice,
8
+ listMediaPrices,
5
9
  listModelPrices,
6
10
  priceCall,
7
11
  pricingFreshness,
8
12
  pricingGeneratedAt,
9
13
  resetPricingWarningForTests,
10
14
  warnIfPricingStale
11
- } from "./chunk-QUOZWQ7G.js";
15
+ } from "./chunk-B74RSBT5.js";
12
16
  export {
17
+ DEFAULT_CLIP_SEC,
18
+ MEDIA_PRICING_CHECKED_AT,
13
19
  PRICING_STALE_AFTER_DAYS,
14
20
  findModelPrices,
21
+ getMediaPrice,
15
22
  getModelPrice,
23
+ listMediaPrices,
16
24
  listModelPrices,
17
25
  priceCall,
18
26
  pricingFreshness,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@broberg/ai-sdk",
3
- "version": "0.39.0",
3
+ "version": "0.41.0",
4
4
  "description": "Unified AI/LLM SDK \u2014 one facade, all providers, all capabilities, first-class cost control on every call.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",