@broberg/ai-sdk 0.40.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,26 +1,49 @@
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;
9
- /** USD per 1M input tokens. */
10
- inputPer1M: number;
11
- /** USD per 1M output tokens. */
12
- outputPer1M: number;
13
- /** Pricing unit: "per_1m_tokens", or "per_sec" / "per_image" for a media model.
14
- * Check it before reading the token rates — they are 0 on a media row, which is a
15
- * placeholder and NOT a claim that the model is free. */
16
- unit: string;
17
- /** USD per second of generated video. Set only when `unit` is "per_sec". */
18
- perSec?: number;
19
- /** USD per generated image. Set only when `unit` is "per_image". */
20
- perImage?: number;
21
- /** ISO date this row was last verified. Media rows carry a HUMAN-set date; token
22
- * rows inherit the inventory snapshot's. */
23
- checkedAt?: string;
24
47
  /** GDPR region derived from the PROVIDER NAME — a rough grouping, not a residency
25
48
  * claim. It is `"other"` for vertex/bfl/fal/azure precisely because those take a
26
49
  * configurable endpoint, so their region is a property of the CALL and not of the
@@ -29,7 +52,57 @@ interface ModelPrice {
29
52
  region: PriceRegion;
30
53
  /** "curated" = authoritative hand-maintained number; "inventory" = from inventory.json. */
31
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;
32
58
  }
59
+ /** A model billed per token. */
60
+ interface TokenModelPrice extends BasePrice {
61
+ unit: "per_1m_tokens";
62
+ /** USD per 1M input tokens. */
63
+ inputPer1M: number;
64
+ /** USD per 1M output tokens. */
65
+ outputPer1M: number;
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;
102
+ }
103
+ /** A price row. Narrow on `unit` before reading rates:
104
+ * `if (p.unit === "per_1m_tokens") … else …`. */
105
+ type ModelPrice = TokenModelPrice | MediaModelPrice;
33
106
  interface PriceFilter {
34
107
  provider?: string;
35
108
  region?: PriceRegion;
@@ -41,14 +114,14 @@ interface PriceFilter {
41
114
  /** Every non-token price the SDK bills from (F050). Separate from
42
115
  * {@link listModelPrices} because the two answer different questions and mixing
43
116
  * them silently changed what an existing caller's list meant. */
44
- declare function listMediaPrices(): ModelPrice[];
117
+ declare function listMediaPrices(): MediaModelPrice[];
45
118
  /** Exact price for a model. `modelId` accepts "vendor/model", "provider:model", or a
46
119
  * bare model/basename. Returns undefined if unknown. */
47
120
  declare function getModelPrice(modelId: string): ModelPrice | undefined;
48
121
  /** Every known model price (inventory, with the curated overlay applied). */
49
- declare function listModelPrices(): ModelPrice[];
122
+ declare function listModelPrices(): TokenModelPrice[];
50
123
  /** Filter the price list (provider / region / max input rate / free-only). */
51
- declare function findModelPrices(filter?: PriceFilter): ModelPrice[];
124
+ declare function findModelPrices(filter?: PriceFilter): TokenModelPrice[];
52
125
  /** Convenience USD compute for a token-priced model; undefined if unknown / not token-priced. */
53
126
  declare function priceCall(modelId: string, inputTokens: number, outputTokens: number): number | undefined;
54
127
  /** ISO timestamp of the inventory snapshot these prices came from.
@@ -112,41 +185,4 @@ declare function warnIfPricingStale(nowMs?: number): void;
112
185
  /** Test-only: forget that we already warned. */
113
186
  declare function resetPricingWarningForTests(): void;
114
187
 
115
- /** A price that is not per-token.
116
- *
117
- * Six units, not the two F050 was carded for. The form-forbidding test found ten
118
- * MORE hand-written price constants than super's report named — speech billed per
119
- * 1000 characters, transcription per audio-minute, OCR per page, a flat LoRA
120
- * training fee, and two more per-image tables. Moving only the four that had been
121
- * noticed would have left `caveats` reporting "covered" while four other units sat
122
- * in four adapters' private constants: the same blindness, one unit along. */
123
- type MediaUnit = "per_sec" | "per_image" | "per_1k_chars" | "per_min" | "per_page" | "per_training";
124
- interface MediaPrice {
125
- unit: MediaUnit;
126
- /** USD per second (video) or per generated image. */
127
- usd: number;
128
- /** ISO date a HUMAN last checked this against the vendor's published price.
129
- * Not derived from the monthly token job — that job never sees these. */
130
- checkedAt: string;
131
- /** Where the number comes from, so the next person can re-check it. */
132
- source: string;
133
- }
134
- /** When a human last walked the whole media table against vendor pricing pages.
135
- *
136
- * ONE date for the table rather than trusting every row's own: a row added later
137
- * with a fresh date would otherwise pull the table's average forward while the
138
- * older rows sat unrevised. Freshness reads the OLDEST answer, and this is it. */
139
- declare const MEDIA_PRICING_CHECKED_AT = "2026-09-05";
140
- /** Default clip length billed when the caller passes no `durationSec`.
141
- *
142
- * It is an ASSUMPTION, not a measurement — the provider does not tell us how long
143
- * the clip it returned actually is. Every cost derived from it is stamped
144
- * `costBasis: "estimated"` so a consumer (and upmetrics) can tell it apart from a
145
- * cost computed off a real duration. super reported using such a number as if it
146
- * were measured and passing it on to Christian; that is the damage this stamp
147
- * removes. */
148
- declare const DEFAULT_CLIP_SEC = 8;
149
- /** Look up a non-token price. Unknown → undefined (never a fabricated 0). */
150
- declare function getMediaPrice(provider: string, model: string): MediaPrice | undefined;
151
-
152
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
@@ -12,7 +12,7 @@ import {
12
12
  pricingGeneratedAt,
13
13
  resetPricingWarningForTests,
14
14
  warnIfPricingStale
15
- } from "./chunk-KXTCLLVF.js";
15
+ } from "./chunk-B74RSBT5.js";
16
16
  export {
17
17
  DEFAULT_CLIP_SEC,
18
18
  MEDIA_PRICING_CHECKED_AT,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@broberg/ai-sdk",
3
- "version": "0.40.0",
4
- "description": "Unified AI/LLM SDK one facade, all providers, all capabilities, first-class cost control on every call.",
3
+ "version": "0.41.0",
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",
7
7
  "author": "Christian Broberg <cb@webhouse.dk>",