ai-sdk-cost-calculator 0.1.21 → 0.1.22
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/core/calculator.d.ts.map +1 -1
- package/dist/core/calculator.js +7 -3
- package/dist/index.js +18 -12
- package/dist/index.mjs +18 -12
- package/dist/shared/usage.d.ts.map +1 -1
- package/dist/shared/usage.js +20 -8
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calculator.d.ts","sourceRoot":"","sources":["../../src/core/calculator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,IAAI,CAAC;AACxC,OAAO,EAA4B,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,SAAS,CAAC;AAMjB,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,kBAAkB,CAAC;IAC1B,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kDAAkD;IAClD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qDAAqD;IACrD,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,iCAAiC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AA0DD,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"calculator.d.ts","sourceRoot":"","sources":["../../src/core/calculator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,IAAI,CAAC;AACxC,OAAO,EAA4B,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,SAAS,CAAC;AAMjB,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,kBAAkB,CAAC;IAC1B,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kDAAkD;IAClD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qDAAqD;IACrD,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,iCAAiC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AA0DD,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAsL1E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,MAAM,CAErE;AAwCD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,QAAQ,GAAE,MAAU,GACnB,MAAM,CAaR"}
|
package/dist/core/calculator.js
CHANGED
|
@@ -134,6 +134,10 @@ export function calculateCost(options) {
|
|
|
134
134
|
imageGenerationCost,
|
|
135
135
|
additionalCost: 0,
|
|
136
136
|
};
|
|
137
|
+
// Defensive: clamp every cost component to ≥0. Negative costs are never
|
|
138
|
+
// valid — they only arise from upstream data anomalies (e.g., AI SDK
|
|
139
|
+
// providers occasionally emit negative noCacheTokens when cache size
|
|
140
|
+
// exceeds prompt size). Clamping here guarantees the breakdown contract.
|
|
137
141
|
let totalCost = 0;
|
|
138
142
|
const breakdown = {
|
|
139
143
|
currency: "USD",
|
|
@@ -141,9 +145,9 @@ export function calculateCost(options) {
|
|
|
141
145
|
totalCost: 0,
|
|
142
146
|
};
|
|
143
147
|
for (const field of COST_COMPONENT_FIELDS) {
|
|
144
|
-
const
|
|
145
|
-
breakdown[field] =
|
|
146
|
-
totalCost +=
|
|
148
|
+
const value = components[field] > 0 ? components[field] : 0;
|
|
149
|
+
breakdown[field] = roundToMicroDollars(value);
|
|
150
|
+
totalCost += value;
|
|
147
151
|
}
|
|
148
152
|
breakdown.totalCost = roundToMicroDollars(totalCost);
|
|
149
153
|
return breakdown;
|
package/dist/index.js
CHANGED
|
@@ -1536,32 +1536,38 @@ function totalsToUsage(totals) {
|
|
|
1536
1536
|
}
|
|
1537
1537
|
};
|
|
1538
1538
|
}
|
|
1539
|
+
var nonNegative = (n) => n > 0 ? n : 0;
|
|
1539
1540
|
function getUsageTokenDetails(usage) {
|
|
1540
1541
|
const inputDetails = usage.inputTokenDetails;
|
|
1541
1542
|
const outputDetails = usage.outputTokenDetails;
|
|
1542
|
-
const cacheReadTokens =
|
|
1543
|
-
|
|
1543
|
+
const cacheReadTokens = nonNegative(
|
|
1544
|
+
inputDetails?.cacheReadTokens ?? usage.cachedInputTokens ?? 0
|
|
1545
|
+
);
|
|
1546
|
+
const cacheWriteTokens = nonNegative(inputDetails?.cacheWriteTokens ?? 0);
|
|
1544
1547
|
let noCacheTokens;
|
|
1545
1548
|
if (inputDetails?.noCacheTokens !== void 0) {
|
|
1546
|
-
noCacheTokens = inputDetails.noCacheTokens;
|
|
1549
|
+
noCacheTokens = nonNegative(inputDetails.noCacheTokens);
|
|
1547
1550
|
} else if (usage.inputTokens !== void 0) {
|
|
1548
|
-
noCacheTokens =
|
|
1549
|
-
0,
|
|
1551
|
+
noCacheTokens = nonNegative(
|
|
1550
1552
|
usage.inputTokens - cacheReadTokens - cacheWriteTokens
|
|
1551
1553
|
);
|
|
1552
1554
|
} else {
|
|
1553
1555
|
noCacheTokens = 0;
|
|
1554
1556
|
}
|
|
1555
|
-
const reasoningTokens =
|
|
1557
|
+
const reasoningTokens = nonNegative(
|
|
1558
|
+
outputDetails?.reasoningTokens ?? usage.reasoningTokens ?? 0
|
|
1559
|
+
);
|
|
1556
1560
|
let textTokens;
|
|
1557
1561
|
if (outputDetails?.textTokens !== void 0) {
|
|
1558
|
-
textTokens = outputDetails.textTokens;
|
|
1562
|
+
textTokens = nonNegative(outputDetails.textTokens);
|
|
1559
1563
|
} else if (usage.outputTokens !== void 0) {
|
|
1560
|
-
textTokens =
|
|
1564
|
+
textTokens = nonNegative(usage.outputTokens - reasoningTokens);
|
|
1561
1565
|
} else {
|
|
1562
1566
|
textTokens = 0;
|
|
1563
1567
|
}
|
|
1564
|
-
const
|
|
1568
|
+
const componentSum = noCacheTokens + cacheReadTokens + cacheWriteTokens;
|
|
1569
|
+
const reportedInput = usage.inputTokens ?? 0;
|
|
1570
|
+
const totalInputTokens = Math.max(reportedInput, componentSum);
|
|
1565
1571
|
return {
|
|
1566
1572
|
noCacheTokens,
|
|
1567
1573
|
cacheReadTokens,
|
|
@@ -1724,9 +1730,9 @@ function calculateCost(options) {
|
|
|
1724
1730
|
totalCost: 0
|
|
1725
1731
|
};
|
|
1726
1732
|
for (const field of COST_COMPONENT_FIELDS) {
|
|
1727
|
-
const
|
|
1728
|
-
breakdown[field] =
|
|
1729
|
-
totalCost +=
|
|
1733
|
+
const value = components[field] > 0 ? components[field] : 0;
|
|
1734
|
+
breakdown[field] = roundToMicroDollars(value);
|
|
1735
|
+
totalCost += value;
|
|
1730
1736
|
}
|
|
1731
1737
|
breakdown.totalCost = roundToMicroDollars(totalCost);
|
|
1732
1738
|
return breakdown;
|
package/dist/index.mjs
CHANGED
|
@@ -1484,32 +1484,38 @@ function totalsToUsage(totals) {
|
|
|
1484
1484
|
}
|
|
1485
1485
|
};
|
|
1486
1486
|
}
|
|
1487
|
+
var nonNegative = (n) => n > 0 ? n : 0;
|
|
1487
1488
|
function getUsageTokenDetails(usage) {
|
|
1488
1489
|
const inputDetails = usage.inputTokenDetails;
|
|
1489
1490
|
const outputDetails = usage.outputTokenDetails;
|
|
1490
|
-
const cacheReadTokens =
|
|
1491
|
-
|
|
1491
|
+
const cacheReadTokens = nonNegative(
|
|
1492
|
+
inputDetails?.cacheReadTokens ?? usage.cachedInputTokens ?? 0
|
|
1493
|
+
);
|
|
1494
|
+
const cacheWriteTokens = nonNegative(inputDetails?.cacheWriteTokens ?? 0);
|
|
1492
1495
|
let noCacheTokens;
|
|
1493
1496
|
if (inputDetails?.noCacheTokens !== void 0) {
|
|
1494
|
-
noCacheTokens = inputDetails.noCacheTokens;
|
|
1497
|
+
noCacheTokens = nonNegative(inputDetails.noCacheTokens);
|
|
1495
1498
|
} else if (usage.inputTokens !== void 0) {
|
|
1496
|
-
noCacheTokens =
|
|
1497
|
-
0,
|
|
1499
|
+
noCacheTokens = nonNegative(
|
|
1498
1500
|
usage.inputTokens - cacheReadTokens - cacheWriteTokens
|
|
1499
1501
|
);
|
|
1500
1502
|
} else {
|
|
1501
1503
|
noCacheTokens = 0;
|
|
1502
1504
|
}
|
|
1503
|
-
const reasoningTokens =
|
|
1505
|
+
const reasoningTokens = nonNegative(
|
|
1506
|
+
outputDetails?.reasoningTokens ?? usage.reasoningTokens ?? 0
|
|
1507
|
+
);
|
|
1504
1508
|
let textTokens;
|
|
1505
1509
|
if (outputDetails?.textTokens !== void 0) {
|
|
1506
|
-
textTokens = outputDetails.textTokens;
|
|
1510
|
+
textTokens = nonNegative(outputDetails.textTokens);
|
|
1507
1511
|
} else if (usage.outputTokens !== void 0) {
|
|
1508
|
-
textTokens =
|
|
1512
|
+
textTokens = nonNegative(usage.outputTokens - reasoningTokens);
|
|
1509
1513
|
} else {
|
|
1510
1514
|
textTokens = 0;
|
|
1511
1515
|
}
|
|
1512
|
-
const
|
|
1516
|
+
const componentSum = noCacheTokens + cacheReadTokens + cacheWriteTokens;
|
|
1517
|
+
const reportedInput = usage.inputTokens ?? 0;
|
|
1518
|
+
const totalInputTokens = Math.max(reportedInput, componentSum);
|
|
1513
1519
|
return {
|
|
1514
1520
|
noCacheTokens,
|
|
1515
1521
|
cacheReadTokens,
|
|
@@ -1672,9 +1678,9 @@ function calculateCost(options) {
|
|
|
1672
1678
|
totalCost: 0
|
|
1673
1679
|
};
|
|
1674
1680
|
for (const field of COST_COMPONENT_FIELDS) {
|
|
1675
|
-
const
|
|
1676
|
-
breakdown[field] =
|
|
1677
|
-
totalCost +=
|
|
1681
|
+
const value = components[field] > 0 ? components[field] : 0;
|
|
1682
|
+
breakdown[field] = roundToMicroDollars(value);
|
|
1683
|
+
totalCost += value;
|
|
1678
1684
|
}
|
|
1679
1685
|
breakdown.totalCost = roundToMicroDollars(totalCost);
|
|
1680
1686
|
return breakdown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/shared/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,IAAI,CAAC;AAE7C,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAQzD;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,kBAAkB,GACxB,IAAI,CAON;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,kBAAkB,CAmB1E;
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/shared/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,IAAI,CAAC;AAE7C,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAQzD;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,kBAAkB,GACxB,IAAI,CAON;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,kBAAkB,CAmB1E;AASD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,kBAAkB,GACxB,iBAAiB,CAwDnB"}
|
package/dist/shared/usage.js
CHANGED
|
@@ -33,40 +33,52 @@ export function totalsToUsage(totals) {
|
|
|
33
33
|
},
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
/** Clamp negative token counts to 0. Some AI SDK providers (notably Google
|
|
37
|
+
* with context caching) can report `noCacheTokens` as negative when
|
|
38
|
+
* cachedContentTokenCount exceeds promptTokenCount (the cache blob is
|
|
39
|
+
* larger than what the prompt actually consumed). Negative tokens produce
|
|
40
|
+
* negative costs, which is never correct. */
|
|
41
|
+
const nonNegative = (n) => (n > 0 ? n : 0);
|
|
36
42
|
export function getUsageTokenDetails(usage) {
|
|
37
43
|
const inputDetails = usage.inputTokenDetails;
|
|
38
44
|
const outputDetails = usage.outputTokenDetails;
|
|
39
45
|
// Cache read: prefer details, fall back to deprecated top-level cachedInputTokens
|
|
40
|
-
const cacheReadTokens = inputDetails?.cacheReadTokens ?? usage.cachedInputTokens ?? 0;
|
|
41
|
-
const cacheWriteTokens = inputDetails?.cacheWriteTokens ?? 0;
|
|
46
|
+
const cacheReadTokens = nonNegative(inputDetails?.cacheReadTokens ?? usage.cachedInputTokens ?? 0);
|
|
47
|
+
const cacheWriteTokens = nonNegative(inputDetails?.cacheWriteTokens ?? 0);
|
|
42
48
|
// noCacheTokens: prefer details. If details exist but noCacheTokens is
|
|
43
49
|
// undefined (some providers populate only cacheRead), derive from
|
|
44
50
|
// inputTokens - cacheRead - cacheWrite. If no details at all, use inputTokens.
|
|
51
|
+
// Always clamp ≥0: providers can report negatives when cache > prompt.
|
|
45
52
|
let noCacheTokens;
|
|
46
53
|
if (inputDetails?.noCacheTokens !== undefined) {
|
|
47
|
-
noCacheTokens = inputDetails.noCacheTokens;
|
|
54
|
+
noCacheTokens = nonNegative(inputDetails.noCacheTokens);
|
|
48
55
|
}
|
|
49
56
|
else if (usage.inputTokens !== undefined) {
|
|
50
|
-
noCacheTokens =
|
|
57
|
+
noCacheTokens = nonNegative(usage.inputTokens - cacheReadTokens - cacheWriteTokens);
|
|
51
58
|
}
|
|
52
59
|
else {
|
|
53
60
|
noCacheTokens = 0;
|
|
54
61
|
}
|
|
55
62
|
// reasoningTokens: prefer details, fall back to deprecated top-level
|
|
56
|
-
const reasoningTokens = outputDetails?.reasoningTokens ?? usage.reasoningTokens ?? 0;
|
|
63
|
+
const reasoningTokens = nonNegative(outputDetails?.reasoningTokens ?? usage.reasoningTokens ?? 0);
|
|
57
64
|
// textTokens: prefer details. If details exist but textTokens is undefined,
|
|
58
65
|
// derive from outputTokens - reasoningTokens. If no details, use outputTokens.
|
|
59
66
|
let textTokens;
|
|
60
67
|
if (outputDetails?.textTokens !== undefined) {
|
|
61
|
-
textTokens = outputDetails.textTokens;
|
|
68
|
+
textTokens = nonNegative(outputDetails.textTokens);
|
|
62
69
|
}
|
|
63
70
|
else if (usage.outputTokens !== undefined) {
|
|
64
|
-
textTokens =
|
|
71
|
+
textTokens = nonNegative(usage.outputTokens - reasoningTokens);
|
|
65
72
|
}
|
|
66
73
|
else {
|
|
67
74
|
textTokens = 0;
|
|
68
75
|
}
|
|
69
|
-
|
|
76
|
+
// Total input for long-context threshold check. Use the larger of:
|
|
77
|
+
// - reported inputTokens
|
|
78
|
+
// - sum of components (in case inputTokens is stale or undercounts)
|
|
79
|
+
const componentSum = noCacheTokens + cacheReadTokens + cacheWriteTokens;
|
|
80
|
+
const reportedInput = usage.inputTokens ?? 0;
|
|
81
|
+
const totalInputTokens = Math.max(reportedInput, componentSum);
|
|
70
82
|
return {
|
|
71
83
|
noCacheTokens,
|
|
72
84
|
cacheReadTokens,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-sdk-cost-calculator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"description": "Cost calculator for Vercel AI SDK token usage. Supports OpenAI, Anthropic, Google, xAI (Grok), and DeepSeek with long context pricing, prompt caching, and reasoning tokens.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|