@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/{chunk-QUOZWQ7G.js → chunk-B74RSBT5.js} +186 -19
- package/dist/chunk-B74RSBT5.js.map +1 -0
- package/dist/index.d.ts +41 -5
- package/dist/index.js +61 -85
- package/dist/index.js.map +1 -1
- package/dist/pricing.d.ts +127 -11
- package/dist/pricing.js +9 -1
- package/package.json +1 -1
- package/dist/chunk-QUOZWQ7G.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export { AvailabilitySource, AvailabilityStatus, ModelStatus, ModelUnavailableError, ResolveOptions, ResolveResult, listModels, resolveModel } from './registry.js';
|
|
3
|
-
export { PRICING_STALE_AFTER_DAYS, PricingFreshness, pricingFreshness, pricingGeneratedAt } from './pricing.js';
|
|
3
|
+
export { DEFAULT_CLIP_SEC, MEDIA_PRICING_CHECKED_AT, MediaPrice, MediaUnit, PRICING_STALE_AFTER_DAYS, PricingFreshness, UnitFreshness, getMediaPrice, listMediaPrices, pricingFreshness, pricingGeneratedAt } from './pricing.js';
|
|
4
4
|
|
|
5
5
|
/** Coarse data-residency of the endpoint a call went to.
|
|
6
6
|
*
|
|
@@ -39,13 +39,31 @@ declare function classifyRegionName(name: string | undefined): Region;
|
|
|
39
39
|
* is it?" — a narrower question than "where will my call go?". */
|
|
40
40
|
declare function regionOfProvider(provider: string): Region;
|
|
41
41
|
|
|
42
|
-
/** One dictionary entry. `alias` says it differently; `ipa` says it
|
|
42
|
+
/** One dictionary entry. `alias` says it differently; `ipa` says it in phonemes.
|
|
43
|
+
*
|
|
44
|
+
* **`ipa` is NOT the "more precise" option for a FOREIGN word — measured 2026-09-04.**
|
|
45
|
+
* Christian listened to `workflow` as `ˈwɜːkfloʊ` through the da-DK voices jeppe and
|
|
46
|
+
* christel and reported: *"ikke korrekt engelsk med et sjovt mix"*. A Danish neural
|
|
47
|
+
* voice renders English phonemes through Danish phonology, so foreign IPA comes out a
|
|
48
|
+
* hybrid rather than English. The symbols are honoured; the accent is not.
|
|
49
|
+
*
|
|
50
|
+
* So pick by what the word IS, not by which field sounds more exact:
|
|
51
|
+
* - a DANISH word the voice mispronounces → `ipa` does what you want.
|
|
52
|
+
* - an ENGLISH word inside Danish text → neither field is right yet. `alias` (spell it
|
|
53
|
+
* the way a Dane would say it) is the honest workaround today; a real engine switch
|
|
54
|
+
* is F051.2, and it is blocked on a listening test, not on code.
|
|
55
|
+
*
|
|
56
|
+
* Dated on purpose. This is a measurement of two specific voices on one date, not a
|
|
57
|
+
* permanent property of IPA — Azure ships new voices, and the next reader deserves to
|
|
58
|
+
* see that it may have moved. */
|
|
43
59
|
interface Pronunciation {
|
|
44
60
|
/** The word as it appears in the text. Matched whole-word, case-insensitively. */
|
|
45
61
|
word: string;
|
|
46
62
|
/** Say this instead. Azure `<sub alias>`; ElevenLabs plain substitution. */
|
|
47
63
|
alias?: string;
|
|
48
|
-
/** IPA phonemes. Azure `<phoneme alphabet="ipa" ph>`. ElevenLabs cannot do this
|
|
64
|
+
/** IPA phonemes. Azure `<phoneme alphabet="ipa" ph>`. ElevenLabs cannot do this
|
|
65
|
+
* (it THROWS rather than silently skipping). See the type doc above before reaching
|
|
66
|
+
* for this on a foreign word. */
|
|
49
67
|
ipa?: string;
|
|
50
68
|
/** Reserved for a future per-entry language switch; carried but not yet emitted. */
|
|
51
69
|
lang?: string;
|
|
@@ -159,6 +177,24 @@ interface Usage {
|
|
|
159
177
|
cacheReadTokens: number;
|
|
160
178
|
cacheCreationTokens: number;
|
|
161
179
|
costUsd: number;
|
|
180
|
+
/** How `costUsd` was arrived at (F050). Absent means the same as `"computed"`;
|
|
181
|
+
* it is optional only so that adding it could not break a consumer building a
|
|
182
|
+
* Usage by hand.
|
|
183
|
+
*
|
|
184
|
+
* - `"reported"` — the PROVIDER told us what the call cost (BFL returns credits).
|
|
185
|
+
* - `"computed"` — a price table rate × a quantity we actually measured (tokens).
|
|
186
|
+
* - `"estimated"` — a quantity or a rate was ASSUMED. A video call with no
|
|
187
|
+
* `durationSec` is billed at {@link DEFAULT_CLIP_SEC} seconds regardless of the
|
|
188
|
+
* clip that came back, and fal's per-image rates are fal's own estimates.
|
|
189
|
+
* - `"unpriced"` — we have NO price for this model, so `costUsd` is 0 because we
|
|
190
|
+
* could not answer, not because the call was free. Those two were the same
|
|
191
|
+
* number, and the free one is much rarer than the unknown one.
|
|
192
|
+
*
|
|
193
|
+
* This is a DATA field on purpose. upmetrics distinguishes reported / computed /
|
|
194
|
+
* unpriced / untokened, and it reads fields — a JSDoc note saying "this one is a
|
|
195
|
+
* guess" never reaches it, so an assumed number arrived there indistinguishable
|
|
196
|
+
* from a measured one. super did exactly that and passed it on as measured. */
|
|
197
|
+
costBasis?: "reported" | "computed" | "estimated" | "unpriced";
|
|
162
198
|
toolCalls?: {
|
|
163
199
|
name: string;
|
|
164
200
|
count: number;
|
|
@@ -2302,8 +2338,8 @@ declare const falStubAdapter: ProviderAdapter;
|
|
|
2302
2338
|
* wires the live adapters. */
|
|
2303
2339
|
declare const stubProviders: Record<string, ProviderAdapter>;
|
|
2304
2340
|
|
|
2305
|
-
declare const VERSION: "0.
|
|
2306
|
-
declare const SDK_TAG: "@broberg/ai-sdk@0.
|
|
2341
|
+
declare const VERSION: "0.41.0";
|
|
2342
|
+
declare const SDK_TAG: "@broberg/ai-sdk@0.41.0";
|
|
2307
2343
|
|
|
2308
2344
|
/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
|
|
2309
2345
|
* per-call override.
|
package/dist/index.js
CHANGED
|
@@ -10,11 +10,15 @@ import {
|
|
|
10
10
|
setAvailability
|
|
11
11
|
} from "./chunk-ZFWSLSE7.js";
|
|
12
12
|
import {
|
|
13
|
+
DEFAULT_CLIP_SEC,
|
|
14
|
+
MEDIA_PRICING_CHECKED_AT,
|
|
13
15
|
PRICING_STALE_AFTER_DAYS,
|
|
16
|
+
getMediaPrice,
|
|
14
17
|
getPrice,
|
|
18
|
+
listMediaPrices,
|
|
15
19
|
pricingFreshness,
|
|
16
20
|
pricingGeneratedAt
|
|
17
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-B74RSBT5.js";
|
|
18
22
|
|
|
19
23
|
// src/transport/http.ts
|
|
20
24
|
async function httpTransport(req) {
|
|
@@ -355,6 +359,11 @@ function freshUsage(args) {
|
|
|
355
359
|
cacheReadTokens,
|
|
356
360
|
cacheCreationTokens,
|
|
357
361
|
costUsd,
|
|
362
|
+
// F050 — say HOW we got the number. A $0 because the model is not in the price
|
|
363
|
+
// table and a $0 because the call was genuinely free were the same value, and the
|
|
364
|
+
// free case is much rarer than the unknown one. Adapters that bill a non-token
|
|
365
|
+
// unit overwrite this with "estimated"/"reported" right after.
|
|
366
|
+
costBasis: args.subprocess ? "computed" : getPrice(args.provider, args.model) ? "computed" : "unpriced",
|
|
358
367
|
latencyMs: 0,
|
|
359
368
|
capability: args.capability,
|
|
360
369
|
ts: ""
|
|
@@ -882,9 +891,6 @@ function mapFinishReason(reason) {
|
|
|
882
891
|
}
|
|
883
892
|
|
|
884
893
|
// src/providers/openai.ts
|
|
885
|
-
var WHISPER_PRICE_PER_MIN = {
|
|
886
|
-
"whisper-1": 6e-3
|
|
887
|
-
};
|
|
888
894
|
function openaiAdapter(config = {}) {
|
|
889
895
|
const baseUrl = config.baseUrl ?? "https://api.openai.com/v1";
|
|
890
896
|
const base = makeOpenAICompatibleAdapter({ name: "openai", baseUrl, apiKey: config.apiKey });
|
|
@@ -945,7 +951,7 @@ function openaiAdapter(config = {}) {
|
|
|
945
951
|
// Whisper is per-minute, not token-priced.
|
|
946
952
|
});
|
|
947
953
|
if (req.durationSec !== void 0) {
|
|
948
|
-
const perMinute =
|
|
954
|
+
const perMinute = getMediaPrice("openai", req.spec.model)?.usd ?? 0;
|
|
949
955
|
usage.costUsd = req.durationSec / 60 * perMinute;
|
|
950
956
|
}
|
|
951
957
|
const result = { text: data.text ?? "", usage };
|
|
@@ -985,17 +991,6 @@ function sniffMime(b) {
|
|
|
985
991
|
}
|
|
986
992
|
|
|
987
993
|
// src/providers/gemini.ts
|
|
988
|
-
var GEMINI_IMAGE_PRICE_PER_IMAGE = {
|
|
989
|
-
"gemini-2.5-flash-image": 0.039,
|
|
990
|
-
// "nano-banana" — 1024px = 1290 tok
|
|
991
|
-
"gemini-3.1-flash-image": 0.067,
|
|
992
|
-
// 1K=$0.067, 2K=$0.101, 4K=$0.151
|
|
993
|
-
"gemini-3.1-flash-image-preview": 0.067,
|
|
994
|
-
"gemini-3-pro-image": 0.134,
|
|
995
|
-
// premium — 1K/2K=$0.134, 4K=$0.24
|
|
996
|
-
"gemini-3-pro-image-preview": 0.134
|
|
997
|
-
// was $0.039 — wrong (that's the flash price); pro is $0.134
|
|
998
|
-
};
|
|
999
994
|
function splitCached(meta) {
|
|
1000
995
|
const prompt = meta?.promptTokenCount ?? 0;
|
|
1001
996
|
const cached = meta?.cachedContentTokenCount;
|
|
@@ -1014,16 +1009,6 @@ function partsFrom(content) {
|
|
|
1014
1009
|
return { inlineData: { mimeType: p.mimeType ?? "image/png", data } };
|
|
1015
1010
|
});
|
|
1016
1011
|
}
|
|
1017
|
-
var VEO_PRICE_PER_SEC = {
|
|
1018
|
-
"veo-3.1-generate-preview": 0.4,
|
|
1019
|
-
// standard; 4K = 0.60
|
|
1020
|
-
"veo-3.1-fast-generate-preview": 0.1,
|
|
1021
|
-
// 720p; 1080p = 0.12, 4K = 0.30
|
|
1022
|
-
"veo-3.1-lite-generate-preview": 0.05,
|
|
1023
|
-
// 720p; 1080p = 0.08
|
|
1024
|
-
"veo-3.0-generate-001": 0.4,
|
|
1025
|
-
"veo-3.0-fast-generate-001": 0.1
|
|
1026
|
-
};
|
|
1027
1012
|
function geminiAdapter(config = {}) {
|
|
1028
1013
|
const baseUrl = config.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta";
|
|
1029
1014
|
function resolveKey() {
|
|
@@ -1180,7 +1165,7 @@ function geminiAdapter(config = {}) {
|
|
|
1180
1165
|
...splitCached(data.usageMetadata),
|
|
1181
1166
|
outputTokens: data.usageMetadata?.candidatesTokenCount ?? 0
|
|
1182
1167
|
});
|
|
1183
|
-
usage.costUsd = config.pricePerImage ??
|
|
1168
|
+
usage.costUsd = config.pricePerImage ?? getMediaPrice("gemini", req.spec.model)?.usd ?? 0;
|
|
1184
1169
|
return { url, usage };
|
|
1185
1170
|
}
|
|
1186
1171
|
async function animate(req) {
|
|
@@ -1232,8 +1217,9 @@ function geminiAdapter(config = {}) {
|
|
|
1232
1217
|
inputTokens: 0,
|
|
1233
1218
|
outputTokens: 0
|
|
1234
1219
|
});
|
|
1235
|
-
const perSec = config.pricePerSecond ??
|
|
1236
|
-
usage.costUsd = perSec * (req.durationSec ??
|
|
1220
|
+
const perSec = config.pricePerSecond ?? getMediaPrice("gemini", req.spec.model)?.usd ?? 0;
|
|
1221
|
+
usage.costUsd = perSec * (req.durationSec ?? DEFAULT_CLIP_SEC);
|
|
1222
|
+
usage.costBasis = req.durationSec === void 0 ? "estimated" : "computed";
|
|
1237
1223
|
return { url: videoUri, bytes, mimeType: "video/mp4", usage };
|
|
1238
1224
|
}
|
|
1239
1225
|
return { name: "gemini", chat, chatStream, image, animate, vision: chat };
|
|
@@ -1259,10 +1245,6 @@ function deepinfraAdapter(config = {}) {
|
|
|
1259
1245
|
}
|
|
1260
1246
|
|
|
1261
1247
|
// src/providers/openrouter.ts
|
|
1262
|
-
var OPENROUTER_IMAGE_PRICE_ESTIMATE = {
|
|
1263
|
-
"recraft/recraft-v4.1": 0.035,
|
|
1264
|
-
"recraft/recraft-v4.1-vector": 0.08
|
|
1265
|
-
};
|
|
1266
1248
|
function openrouterAdapter(config = {}) {
|
|
1267
1249
|
const baseUrl = config.baseUrl ?? "https://openrouter.ai/api/v1";
|
|
1268
1250
|
const headers = {
|
|
@@ -1317,7 +1299,7 @@ function openrouterAdapter(config = {}) {
|
|
|
1317
1299
|
inputTokens: 0,
|
|
1318
1300
|
outputTokens: 0
|
|
1319
1301
|
});
|
|
1320
|
-
usage.costUsd = data.usage?.cost ?? config.pricePerImage ??
|
|
1302
|
+
usage.costUsd = data.usage?.cost ?? config.pricePerImage ?? getMediaPrice("openrouter", req.spec.model)?.usd ?? 0;
|
|
1321
1303
|
return { url: `data:${first.media_type ?? "image/png"};base64,${first.b64_json}`, usage };
|
|
1322
1304
|
}
|
|
1323
1305
|
return { ...base, image };
|
|
@@ -1354,12 +1336,6 @@ function deepseekAdapter(config = {}) {
|
|
|
1354
1336
|
}
|
|
1355
1337
|
|
|
1356
1338
|
// src/providers/mistral.ts
|
|
1357
|
-
var MISTRAL_OCR_PRICE_PER_PAGE = 2e-3;
|
|
1358
|
-
var VOXTRAL_PRICE_PER_MIN = {
|
|
1359
|
-
"voxtral-mini-latest": 2e-3,
|
|
1360
|
-
"voxtral-mini-2507": 2e-3,
|
|
1361
|
-
"voxtral-mini-2602": 2e-3
|
|
1362
|
-
};
|
|
1363
1339
|
function mistralAdapter(config = {}) {
|
|
1364
1340
|
const baseUrl = config.baseUrl ?? "https://api.mistral.ai/v1";
|
|
1365
1341
|
const base = makeOpenAICompatibleAdapter({ name: "mistral", baseUrl, apiKey: config.apiKey, supportsPromptCacheKey: true, supportsPrefix: true });
|
|
@@ -1401,7 +1377,7 @@ function mistralAdapter(config = {}) {
|
|
|
1401
1377
|
inputTokens: 0,
|
|
1402
1378
|
outputTokens: 0
|
|
1403
1379
|
});
|
|
1404
|
-
usage.costUsd = pagesProcessed * (config.pricePerPage ??
|
|
1380
|
+
usage.costUsd = pagesProcessed * (config.pricePerPage ?? getMediaPrice("mistral", "ocr")?.usd ?? 0);
|
|
1405
1381
|
return { pages, usage };
|
|
1406
1382
|
}
|
|
1407
1383
|
async function moderate(req) {
|
|
@@ -1495,7 +1471,7 @@ function mistralAdapter(config = {}) {
|
|
|
1495
1471
|
outputTokens: 0
|
|
1496
1472
|
});
|
|
1497
1473
|
if (req.durationSec !== void 0) {
|
|
1498
|
-
usage.costUsd = req.durationSec / 60 * (
|
|
1474
|
+
usage.costUsd = req.durationSec / 60 * (getMediaPrice("mistral", req.spec.model)?.usd ?? 0);
|
|
1499
1475
|
}
|
|
1500
1476
|
return { text: data.text ?? "", usage };
|
|
1501
1477
|
}
|
|
@@ -1561,7 +1537,7 @@ function assertPronunciations(list, provider) {
|
|
|
1561
1537
|
}
|
|
1562
1538
|
if (p.alias !== void 0 && p.ipa !== void 0) {
|
|
1563
1539
|
throw new Error(
|
|
1564
|
-
`${provider} adapter: pronunciation "${p.word}" sets BOTH alias and ipa. They are different instructions \u2014 alias says it differently, ipa says it
|
|
1540
|
+
`${provider} adapter: pronunciation "${p.word}" sets BOTH alias and ipa. They are different instructions \u2014 alias says it differently, ipa says it in phonemes. Pick one.`
|
|
1565
1541
|
);
|
|
1566
1542
|
}
|
|
1567
1543
|
if (p.alias === void 0 && p.ipa === void 0) {
|
|
@@ -1589,7 +1565,6 @@ function applyPronunciations(haystack, list, render, escape = (s) => s) {
|
|
|
1589
1565
|
}
|
|
1590
1566
|
|
|
1591
1567
|
// src/providers/elevenlabs.ts
|
|
1592
|
-
var ELEVENLABS_PRICE_PER_1K_CHARS = 0.15;
|
|
1593
1568
|
var ELEVENLABS_DANISH_VOICES = {
|
|
1594
1569
|
soren: "xj6X4BCUsv9oxohm1E8o",
|
|
1595
1570
|
jesper: "Bl1YwS3uJac5zEOSNESn",
|
|
@@ -1628,7 +1603,8 @@ function elevenlabsAdapter(config = {}) {
|
|
|
1628
1603
|
inputTokens: 0,
|
|
1629
1604
|
outputTokens: 0
|
|
1630
1605
|
});
|
|
1631
|
-
usage.costUsd = chars / 1e3 * (config.pricePer1kChars ??
|
|
1606
|
+
usage.costUsd = chars / 1e3 * (config.pricePer1kChars ?? getMediaPrice("elevenlabs", "tts")?.usd ?? 0);
|
|
1607
|
+
usage.costBasis = config.pricePer1kChars !== void 0 ? "computed" : "estimated";
|
|
1632
1608
|
return usage;
|
|
1633
1609
|
}
|
|
1634
1610
|
async function dialogue(req) {
|
|
@@ -1673,8 +1649,6 @@ function elevenlabsAdapter(config = {}) {
|
|
|
1673
1649
|
}
|
|
1674
1650
|
|
|
1675
1651
|
// src/providers/azure.ts
|
|
1676
|
-
var AZURE_TTS_PRICE_PER_1K_CHARS = 0.016;
|
|
1677
|
-
var AZURE_STT_PRICE_PER_MIN = 0.0167;
|
|
1678
1652
|
var DEFAULT_STT_API_VERSION = "2025-10-15";
|
|
1679
1653
|
var DEFAULT_REGION = "westeurope";
|
|
1680
1654
|
var DEFAULT_FORMAT = "audio-24khz-48kbitrate-mono-mp3";
|
|
@@ -1752,7 +1726,7 @@ function azureAdapter(config = {}) {
|
|
|
1752
1726
|
inputTokens: 0,
|
|
1753
1727
|
outputTokens: 0
|
|
1754
1728
|
});
|
|
1755
|
-
usage.costUsd = chars / 1e3 * (config.pricePer1kChars ??
|
|
1729
|
+
usage.costUsd = chars / 1e3 * (config.pricePer1kChars ?? getMediaPrice("azure", "tts")?.usd ?? 0);
|
|
1756
1730
|
return usage;
|
|
1757
1731
|
}
|
|
1758
1732
|
async function tts(req) {
|
|
@@ -1819,7 +1793,7 @@ function azureAdapter(config = {}) {
|
|
|
1819
1793
|
inputTokens: 0,
|
|
1820
1794
|
outputTokens: 0
|
|
1821
1795
|
});
|
|
1822
|
-
usage.costUsd = minutes * (config.sttPricePerMin ??
|
|
1796
|
+
usage.costUsd = minutes * (config.sttPricePerMin ?? getMediaPrice("azure", "stt")?.usd ?? 0);
|
|
1823
1797
|
const result = { text, usage };
|
|
1824
1798
|
if (req.timestamps && req.timestamps.length > 0 && data.phrases) {
|
|
1825
1799
|
const toSec = (ms) => (ms ?? 0) / 1e3;
|
|
@@ -1850,13 +1824,6 @@ import { readFileSync } from "fs";
|
|
|
1850
1824
|
var TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
|
|
1851
1825
|
var DEFAULT_REGION2 = "europe-west1";
|
|
1852
1826
|
var CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
1853
|
-
var VERTEX_VEO_PRICE_PER_SEC = {
|
|
1854
|
-
"veo-3.1-generate-preview": 0.4,
|
|
1855
|
-
"veo-3.1-fast-generate-preview": 0.1,
|
|
1856
|
-
"veo-3.1-lite-generate-preview": 0.05,
|
|
1857
|
-
"veo-3.0-generate-001": 0.4,
|
|
1858
|
-
"veo-3.0-fast-generate-001": 0.1
|
|
1859
|
-
};
|
|
1860
1827
|
function base64url(input) {
|
|
1861
1828
|
const buf = typeof input === "string" ? Buffer.from(input) : input;
|
|
1862
1829
|
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
@@ -1996,8 +1963,9 @@ function vertexAdapter(config = {}) {
|
|
|
1996
1963
|
inputTokens: 0,
|
|
1997
1964
|
outputTokens: 0
|
|
1998
1965
|
});
|
|
1999
|
-
const perSec = config.pricePerSecond ??
|
|
2000
|
-
usage.costUsd = perSec * (req.durationSec ??
|
|
1966
|
+
const perSec = config.pricePerSecond ?? getMediaPrice("vertex", req.spec.model)?.usd ?? 0;
|
|
1967
|
+
usage.costUsd = perSec * (req.durationSec ?? DEFAULT_CLIP_SEC);
|
|
1968
|
+
usage.costBasis = req.durationSec === void 0 ? "estimated" : "computed";
|
|
2001
1969
|
return { url: `vertex://${op.name}`, bytes: Buffer.from(videoB64, "base64"), mimeType: videoMime, usage };
|
|
2002
1970
|
}
|
|
2003
1971
|
async function vision(req) {
|
|
@@ -2049,7 +2017,6 @@ function vertexAdapter(config = {}) {
|
|
|
2049
2017
|
}
|
|
2050
2018
|
|
|
2051
2019
|
// src/providers/deepl.ts
|
|
2052
|
-
var DEEPL_PRICE_PER_1K_CHARS_ESTIMATE = 0.0217;
|
|
2053
2020
|
function deeplAdapter(config = {}) {
|
|
2054
2021
|
const fetchImpl = config.fetch ?? fetch;
|
|
2055
2022
|
function key() {
|
|
@@ -2085,7 +2052,8 @@ function deeplAdapter(config = {}) {
|
|
|
2085
2052
|
inputTokens: 0,
|
|
2086
2053
|
outputTokens: 0
|
|
2087
2054
|
});
|
|
2088
|
-
usage.costUsd = req.text.length / 1e3 * (config.pricePer1kChars ??
|
|
2055
|
+
usage.costUsd = req.text.length / 1e3 * (config.pricePer1kChars ?? getMediaPrice("deepl", "translate")?.usd ?? 0);
|
|
2056
|
+
usage.costBasis = config.pricePer1kChars !== void 0 ? "computed" : "estimated";
|
|
2089
2057
|
return { text, usage };
|
|
2090
2058
|
}
|
|
2091
2059
|
return { name: "deepl", translate };
|
|
@@ -2093,21 +2061,6 @@ function deeplAdapter(config = {}) {
|
|
|
2093
2061
|
|
|
2094
2062
|
// src/providers/fal.ts
|
|
2095
2063
|
import { deflateRawSync, crc32 } from "zlib";
|
|
2096
|
-
var FAL_IMAGE_PRICE_ESTIMATE = {
|
|
2097
|
-
"fal-ai/flux/schnell": 3e-3,
|
|
2098
|
-
"fal-ai/flux/dev": 0.025,
|
|
2099
|
-
"fal-ai/flux-lora": 0.025,
|
|
2100
|
-
"fal-ai/flux-pro": 0.05,
|
|
2101
|
-
"fal-ai/flux-pro/v1.1": 0.04
|
|
2102
|
-
};
|
|
2103
|
-
var FAL_TRAIN_PRICE_ESTIMATE = 2;
|
|
2104
|
-
var FAL_VIDEO_PRICE_PER_SEC = {
|
|
2105
|
-
// Kling 2.5 Turbo Pro i2v — the blessed FAL_KEY-only image→video route (F014
|
|
2106
|
-
// contentpush spike). $0.35 for the first 5 s + $0.07/additional s = a flat
|
|
2107
|
-
// $0.07/s (1080p); per-second-billed, commercial-use, not deprecated.
|
|
2108
|
-
"fal-ai/kling-video/v2.5-turbo/pro/image-to-video": 0.07
|
|
2109
|
-
};
|
|
2110
|
-
var FAL_VIDEO_DEFAULT_SEC = 8;
|
|
2111
2064
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2112
2065
|
function falAdapter(config = {}) {
|
|
2113
2066
|
const doFetch = config.fetch ?? fetch;
|
|
@@ -2147,7 +2100,9 @@ function falAdapter(config = {}) {
|
|
|
2147
2100
|
inputTokens: 0,
|
|
2148
2101
|
outputTokens: 0
|
|
2149
2102
|
});
|
|
2150
|
-
|
|
2103
|
+
const perImage = config.pricePerImage ?? getMediaPrice("fal", req.spec.model)?.usd;
|
|
2104
|
+
usage.costUsd = (perImage ?? 0) * calls;
|
|
2105
|
+
usage.costBasis = config.pricePerImage !== void 0 ? "computed" : "estimated";
|
|
2151
2106
|
return { url, usage };
|
|
2152
2107
|
}
|
|
2153
2108
|
async function animate(req) {
|
|
@@ -2176,8 +2131,9 @@ function falAdapter(config = {}) {
|
|
|
2176
2131
|
inputTokens: 0,
|
|
2177
2132
|
outputTokens: 0
|
|
2178
2133
|
});
|
|
2179
|
-
const perSec = config.pricePerSecond ??
|
|
2180
|
-
usage.costUsd = perSec * (req.durationSec ??
|
|
2134
|
+
const perSec = config.pricePerSecond ?? getMediaPrice("fal", req.spec.model)?.usd ?? 0;
|
|
2135
|
+
usage.costUsd = perSec * (req.durationSec ?? DEFAULT_CLIP_SEC);
|
|
2136
|
+
usage.costBasis = req.durationSec === void 0 ? "estimated" : "computed";
|
|
2181
2137
|
return { url, usage };
|
|
2182
2138
|
}
|
|
2183
2139
|
async function trainStyle(req) {
|
|
@@ -2213,7 +2169,8 @@ function falAdapter(config = {}) {
|
|
|
2213
2169
|
inputTokens: 0,
|
|
2214
2170
|
outputTokens: 0
|
|
2215
2171
|
});
|
|
2216
|
-
usage.costUsd = config.pricePerTraining ??
|
|
2172
|
+
usage.costUsd = config.pricePerTraining ?? getMediaPrice("fal", "train")?.usd ?? 0;
|
|
2173
|
+
usage.costBasis = config.pricePerTraining !== void 0 ? "computed" : "estimated";
|
|
2217
2174
|
return { loraUrl, configUrl: configUrl ?? "", usage };
|
|
2218
2175
|
}
|
|
2219
2176
|
async function resolveImagesUrl(images, apiKey) {
|
|
@@ -2411,7 +2368,6 @@ function buildZip(files) {
|
|
|
2411
2368
|
// src/providers/bfl.ts
|
|
2412
2369
|
var EU_BASE2 = "https://api.eu.bfl.ai";
|
|
2413
2370
|
var BFL_CREDIT_USD = 0.01;
|
|
2414
|
-
var BFL_IMAGE_PRICE = 0.06;
|
|
2415
2371
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2416
2372
|
function gcd(a, b) {
|
|
2417
2373
|
return b === 0 ? a : gcd(b, a % b);
|
|
@@ -2487,7 +2443,13 @@ function bflAdapter(config = {}) {
|
|
|
2487
2443
|
inputTokens: 0,
|
|
2488
2444
|
outputTokens: 0
|
|
2489
2445
|
});
|
|
2490
|
-
|
|
2446
|
+
if (typeof submit.cost === "number") {
|
|
2447
|
+
usage.costUsd = submit.cost * BFL_CREDIT_USD;
|
|
2448
|
+
usage.costBasis = "reported";
|
|
2449
|
+
} else {
|
|
2450
|
+
usage.costUsd = config.pricePerImage ?? getMediaPrice("bfl", "flux-pro-1.1-ultra-finetuned")?.usd ?? 0;
|
|
2451
|
+
usage.costBasis = config.pricePerImage !== void 0 ? "computed" : "estimated";
|
|
2452
|
+
}
|
|
2491
2453
|
return { url: sample, usage };
|
|
2492
2454
|
}
|
|
2493
2455
|
function toBflImage(img) {
|
|
@@ -3131,8 +3093,8 @@ var aiConfigSchema = z.object({
|
|
|
3131
3093
|
});
|
|
3132
3094
|
|
|
3133
3095
|
// src/version.ts
|
|
3134
|
-
var VERSION = "0.
|
|
3135
|
-
var SDK_TAG = "@broberg/ai-sdk@0.
|
|
3096
|
+
var VERSION = "0.41.0";
|
|
3097
|
+
var SDK_TAG = "@broberg/ai-sdk@0.41.0";
|
|
3136
3098
|
|
|
3137
3099
|
// src/cost/sinks/upmetrics.ts
|
|
3138
3100
|
function upmetricsSink(config) {
|
|
@@ -3172,6 +3134,11 @@ function upmetricsSink(config) {
|
|
|
3172
3134
|
// capability/transport — no ingest-schema change, and without it the one
|
|
3173
3135
|
// field built for auditability existed only in memory.
|
|
3174
3136
|
region: usage.region,
|
|
3137
|
+
// F050: HOW cost_usd was arrived at. upmetrics already distinguishes
|
|
3138
|
+
// reported / computed / unpriced — we were sending an assumed number in
|
|
3139
|
+
// the same field as a measured one, so their labels could not be right
|
|
3140
|
+
// about our rows however carefully they were applied.
|
|
3141
|
+
cost_basis: usage.costBasis ?? "computed",
|
|
3175
3142
|
sdk: SDK_TAG
|
|
3176
3143
|
}
|
|
3177
3144
|
};
|
|
@@ -3998,6 +3965,7 @@ CREATE TABLE IF NOT EXISTS ai_usage (
|
|
|
3998
3965
|
cache_read_tokens INTEGER NOT NULL,
|
|
3999
3966
|
cache_creation_tokens INTEGER NOT NULL,
|
|
4000
3967
|
cost_usd REAL NOT NULL,
|
|
3968
|
+
cost_basis TEXT NOT NULL DEFAULT 'unknown',
|
|
4001
3969
|
latency_ms INTEGER NOT NULL,
|
|
4002
3970
|
subprocess INTEGER NOT NULL DEFAULT 0
|
|
4003
3971
|
)`;
|
|
@@ -4014,13 +3982,16 @@ function sqliteSink(config) {
|
|
|
4014
3982
|
if (!cols.some((c) => c.name === "region")) {
|
|
4015
3983
|
db.run(`ALTER TABLE ai_usage ADD COLUMN region TEXT NOT NULL DEFAULT 'unknown'`);
|
|
4016
3984
|
}
|
|
3985
|
+
if (!cols.some((c) => c.name === "cost_basis")) {
|
|
3986
|
+
db.run(`ALTER TABLE ai_usage ADD COLUMN cost_basis TEXT NOT NULL DEFAULT 'unknown'`);
|
|
3987
|
+
}
|
|
4017
3988
|
const insert = db.prepare(
|
|
4018
3989
|
`INSERT INTO ai_usage
|
|
4019
3990
|
(ts, provider, model, tier, transport, capability, region, purpose,
|
|
4020
3991
|
input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens,
|
|
4021
|
-
cost_usd, latency_ms, subprocess)
|
|
3992
|
+
cost_usd, cost_basis, latency_ms, subprocess)
|
|
4022
3993
|
VALUES ($ts, $provider, $model, $tier, $transport, $capability, $region, $purpose,
|
|
4023
|
-
$input, $output, $cacheRead, $cacheCreation, $cost, $latency, $subprocess)`
|
|
3994
|
+
$input, $output, $cacheRead, $cacheCreation, $cost, $costBasis, $latency, $subprocess)`
|
|
4024
3995
|
);
|
|
4025
3996
|
return insert;
|
|
4026
3997
|
};
|
|
@@ -4041,6 +4012,7 @@ function sqliteSink(config) {
|
|
|
4041
4012
|
$cacheRead: usage.cacheReadTokens,
|
|
4042
4013
|
$cacheCreation: usage.cacheCreationTokens,
|
|
4043
4014
|
$cost: usage.costUsd,
|
|
4015
|
+
$costBasis: usage.costBasis ?? "computed",
|
|
4044
4016
|
$latency: usage.latencyMs,
|
|
4045
4017
|
$subprocess: usage.subprocess ? 1 : 0
|
|
4046
4018
|
});
|
|
@@ -4133,8 +4105,10 @@ export {
|
|
|
4133
4105
|
AZURE_DANISH_VOICE_LIST,
|
|
4134
4106
|
BudgetExceededError,
|
|
4135
4107
|
BudgetGuard,
|
|
4108
|
+
DEFAULT_CLIP_SEC,
|
|
4136
4109
|
DEFAULT_TIER_MAP,
|
|
4137
4110
|
ELEVENLABS_DANISH_VOICES,
|
|
4111
|
+
MEDIA_PRICING_CHECKED_AT,
|
|
4138
4112
|
ModelUnavailableError,
|
|
4139
4113
|
PRICING_STALE_AFTER_DAYS,
|
|
4140
4114
|
SDK_TAG,
|
|
@@ -4167,10 +4141,12 @@ export {
|
|
|
4167
4141
|
fromProviderToolCall,
|
|
4168
4142
|
geminiAdapter,
|
|
4169
4143
|
getCostSummary,
|
|
4144
|
+
getMediaPrice,
|
|
4170
4145
|
getPrice,
|
|
4171
4146
|
httpTransport,
|
|
4172
4147
|
imageInputSchema,
|
|
4173
4148
|
listAzureDanishVoices,
|
|
4149
|
+
listMediaPrices,
|
|
4174
4150
|
listModels,
|
|
4175
4151
|
listVoices,
|
|
4176
4152
|
makeContracts,
|