@pydantic/genai-prices 0.0.6 → 0.0.8
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/README.md +26 -6
- package/dist/__tests__/comprehensive.test.d.ts +1 -0
- package/dist/__tests__/matcher.test.d.ts +1 -0
- package/dist/async/calcPriceAsync.js +4 -2
- package/dist/cli.js +3 -1
- package/dist/data.js +13 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.umd.cjs +57 -18
- package/dist/matcher.js +8 -2
- package/dist/priceCalc.d.ts +2 -2
- package/dist/priceCalc.js +35 -13
- package/dist/sync/calcPriceSync.js +4 -2
- package/dist/types.d.ts +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,12 @@ Library and CLI for calculating LLM API prices, supporting browser, Node.js and
|
|
|
13
13
|
|
|
14
14
|
## API Usage
|
|
15
15
|
|
|
16
|
+
The library provides separated input and output pricing, giving you detailed breakdown of costs:
|
|
17
|
+
|
|
18
|
+
- `result.total_price` - Total cost for the request
|
|
19
|
+
- `result.input_price` - Cost for input/prompt tokens
|
|
20
|
+
- `result.output_price` - Cost for output/completion tokens
|
|
21
|
+
|
|
16
22
|
### Node.js & Browser (Library)
|
|
17
23
|
|
|
18
24
|
```js
|
|
@@ -23,7 +29,11 @@ const usage = { input_tokens: 1000, output_tokens: 100 }
|
|
|
23
29
|
// Sync (works everywhere, including browser)
|
|
24
30
|
const result = calcPriceSync(usage, 'gpt-3.5-turbo', { providerId: 'openai' })
|
|
25
31
|
if (result) {
|
|
26
|
-
console.log(
|
|
32
|
+
console.log(
|
|
33
|
+
`$${result.total_price} (input: $${result.input_price}, output: $${result.output_price})`,
|
|
34
|
+
result.provider.name,
|
|
35
|
+
result.model.name,
|
|
36
|
+
)
|
|
27
37
|
} else {
|
|
28
38
|
console.log('No price found for this model/provider combination')
|
|
29
39
|
}
|
|
@@ -31,7 +41,11 @@ if (result) {
|
|
|
31
41
|
// Async (works everywhere)
|
|
32
42
|
const asyncResult = await calcPriceAsync(usage, 'gpt-3.5-turbo', { providerId: 'openai' })
|
|
33
43
|
if (asyncResult) {
|
|
34
|
-
console.log(
|
|
44
|
+
console.log(
|
|
45
|
+
`$${asyncResult.total_price} (input: $${asyncResult.input_price}, output: $${asyncResult.output_price})`,
|
|
46
|
+
asyncResult.provider.name,
|
|
47
|
+
asyncResult.model.name,
|
|
48
|
+
)
|
|
35
49
|
} else {
|
|
36
50
|
console.log('No price found for this model/provider combination')
|
|
37
51
|
}
|
|
@@ -44,7 +58,11 @@ import { calcPriceSync, calcPriceAsync } from './dist/index.js'
|
|
|
44
58
|
const usage = { input_tokens: 1000, output_tokens: 100 }
|
|
45
59
|
const result = calcPriceSync(usage, 'gpt-3.5-turbo', { providerId: 'openai' })
|
|
46
60
|
if (result) {
|
|
47
|
-
console.log(
|
|
61
|
+
console.log(
|
|
62
|
+
`$${result.total_price} (input: $${result.input_price}, output: $${result.output_price})`,
|
|
63
|
+
result.provider.name,
|
|
64
|
+
result.model.name,
|
|
65
|
+
)
|
|
48
66
|
}
|
|
49
67
|
```
|
|
50
68
|
|
|
@@ -110,7 +128,7 @@ const result = calcPriceSync(usage, 'non-existent-model')
|
|
|
110
128
|
if (result === null) {
|
|
111
129
|
console.log('No pricing information available for this model')
|
|
112
130
|
} else {
|
|
113
|
-
console.log(`Price: $${result.
|
|
131
|
+
console.log(`Total Price: $${result.total_price} (input: $${result.input_price}, output: $${result.output_price})`)
|
|
114
132
|
}
|
|
115
133
|
|
|
116
134
|
// Async version also returns null
|
|
@@ -118,7 +136,9 @@ const asyncResult = await calcPriceAsync(usage, 'non-existent-model', { provider
|
|
|
118
136
|
if (asyncResult === null) {
|
|
119
137
|
console.log('No pricing information available for this model/provider combination')
|
|
120
138
|
} else {
|
|
121
|
-
console.log(
|
|
139
|
+
console.log(
|
|
140
|
+
`Total Price: $${asyncResult.total_price} (input: $${asyncResult.input_price}, output: $${asyncResult.output_price})`,
|
|
141
|
+
)
|
|
122
142
|
}
|
|
123
143
|
```
|
|
124
144
|
|
|
@@ -174,7 +194,7 @@ src/
|
|
|
174
194
|
### Common Issues
|
|
175
195
|
|
|
176
196
|
- **No price found (returns null)**:
|
|
177
|
-
- Make sure you specify the correct `providerId` (e.g., `openai`)
|
|
197
|
+
- Make sure you specify the correct `providerId` (e.g., `openai`, `google`, `anthropic`)
|
|
178
198
|
- Try using `provider:model` format in CLI
|
|
179
199
|
- Use `--auto-update` flag to fetch latest data
|
|
180
200
|
- Check that the model name is correct and supported by the provider
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -11,9 +11,11 @@ export async function calcPriceAsync(usage, modelRef, options = {}) {
|
|
|
11
11
|
return null;
|
|
12
12
|
const timestamp = options.timestamp || new Date();
|
|
13
13
|
const model_price = getActiveModelPrice(model, timestamp);
|
|
14
|
-
const
|
|
14
|
+
const priceResult = calcModelPrice(usage, model_price);
|
|
15
15
|
return {
|
|
16
|
-
|
|
16
|
+
input_price: priceResult.input_price,
|
|
17
|
+
output_price: priceResult.output_price,
|
|
18
|
+
total_price: priceResult.total_price,
|
|
17
19
|
provider,
|
|
18
20
|
model,
|
|
19
21
|
model_price,
|
package/dist/cli.js
CHANGED
|
@@ -126,7 +126,9 @@ async function main() {
|
|
|
126
126
|
['Model', result.model.name || result.model.id],
|
|
127
127
|
['Model Prices', JSON.stringify(result.model_price)],
|
|
128
128
|
['Context Window', w !== undefined ? w.toLocaleString() : undefined],
|
|
129
|
-
['Price', `$${result.
|
|
129
|
+
['Total Price', `$${result.total_price}`],
|
|
130
|
+
['Input Price', `$${result.input_price}`],
|
|
131
|
+
['Output Price', `$${result.output_price}`],
|
|
130
132
|
];
|
|
131
133
|
for (const [key, value] of output) {
|
|
132
134
|
if (value !== undefined) {
|
package/dist/data.js
CHANGED
|
@@ -1292,6 +1292,16 @@ export const data = [
|
|
|
1292
1292
|
model_match: {
|
|
1293
1293
|
contains: 'gemini',
|
|
1294
1294
|
},
|
|
1295
|
+
provider_match: {
|
|
1296
|
+
or: [
|
|
1297
|
+
{
|
|
1298
|
+
contains: 'google',
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
equals: 'gemini',
|
|
1302
|
+
},
|
|
1303
|
+
],
|
|
1304
|
+
},
|
|
1295
1305
|
models: [
|
|
1296
1306
|
{
|
|
1297
1307
|
id: 'claude-3-5-haiku',
|
|
@@ -1973,6 +1983,9 @@ export const data = [
|
|
|
1973
1983
|
model_match: {
|
|
1974
1984
|
regex: '(?:mi|code|dev|magi|mini)stral',
|
|
1975
1985
|
},
|
|
1986
|
+
provider_match: {
|
|
1987
|
+
starts_with: 'mistral',
|
|
1988
|
+
},
|
|
1976
1989
|
models: [
|
|
1977
1990
|
{
|
|
1978
1991
|
id: 'codestral',
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ export { calcPriceSync } from './sync/calcPriceSync.js';
|
|
|
2
2
|
export { calcPriceAsync } from './async/calcPriceAsync.js';
|
|
3
3
|
export { getProvidersSync, getProvidersAsync, enableAutoUpdate, isLocalDataOutdated, prefetchAsync, getEnvironmentInfo, } from './dataLoader.js';
|
|
4
4
|
export { matchProvider, matchModel } from './matcher.js';
|
|
5
|
-
export type { Usage, PriceCalculation, PriceCalculationResult, Provider, ModelInfo } from './types.js';
|
|
5
|
+
export type { Usage, PriceCalculation, PriceCalculationResult, Provider, ModelInfo, CalcPrice } from './types.js';
|
package/dist/index.umd.cjs
CHANGED
|
@@ -1298,6 +1298,16 @@
|
|
|
1298
1298
|
model_match: {
|
|
1299
1299
|
contains: "gemini"
|
|
1300
1300
|
},
|
|
1301
|
+
provider_match: {
|
|
1302
|
+
or: [
|
|
1303
|
+
{
|
|
1304
|
+
contains: "google"
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
equals: "gemini"
|
|
1308
|
+
}
|
|
1309
|
+
]
|
|
1310
|
+
},
|
|
1301
1311
|
models: [
|
|
1302
1312
|
{
|
|
1303
1313
|
id: "claude-3-5-haiku",
|
|
@@ -1979,6 +1989,9 @@
|
|
|
1979
1989
|
model_match: {
|
|
1980
1990
|
regex: "(?:mi|code|dev|magi|mini)stral"
|
|
1981
1991
|
},
|
|
1992
|
+
provider_match: {
|
|
1993
|
+
starts_with: "mistral"
|
|
1994
|
+
},
|
|
1982
1995
|
models: [
|
|
1983
1996
|
{
|
|
1984
1997
|
id: "codestral",
|
|
@@ -10573,9 +10586,15 @@
|
|
|
10573
10586
|
}
|
|
10574
10587
|
return false;
|
|
10575
10588
|
}
|
|
10589
|
+
function findProviderById(providers, providerId) {
|
|
10590
|
+
const normalizedProviderId = providerId.toLowerCase().trim();
|
|
10591
|
+
const exactMatch = providers.find((p) => p.id === normalizedProviderId);
|
|
10592
|
+
if (exactMatch) return exactMatch;
|
|
10593
|
+
return providers.find((p) => p.provider_match && matchLogic(p.provider_match, normalizedProviderId));
|
|
10594
|
+
}
|
|
10576
10595
|
function matchProvider(providers, modelRef, providerId, providerApiUrl) {
|
|
10577
10596
|
if (providerId) {
|
|
10578
|
-
return providers
|
|
10597
|
+
return findProviderById(providers, providerId);
|
|
10579
10598
|
}
|
|
10580
10599
|
if (providerApiUrl) {
|
|
10581
10600
|
return providers.find((p) => new RegExp(p.api_pattern).test(providerApiUrl));
|
|
@@ -10611,18 +10630,26 @@
|
|
|
10611
10630
|
return calcTieredPrice(price, tokens);
|
|
10612
10631
|
}
|
|
10613
10632
|
function calcPrice(usage, modelPrice) {
|
|
10614
|
-
let
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10633
|
+
let input_price = 0;
|
|
10634
|
+
let output_price = 0;
|
|
10635
|
+
input_price += calcMtokPrice(modelPrice.input_mtok, usage.input_tokens);
|
|
10636
|
+
input_price += calcMtokPrice(modelPrice.cache_write_mtok, usage.cache_write_tokens);
|
|
10637
|
+
input_price += calcMtokPrice(modelPrice.cache_read_mtok, usage.cache_read_tokens);
|
|
10638
|
+
input_price += calcMtokPrice(modelPrice.input_audio_mtok, usage.input_audio_tokens);
|
|
10639
|
+
input_price += calcMtokPrice(
|
|
10640
|
+
modelPrice.cache_audio_read_mtok,
|
|
10641
|
+
usage.cache_audio_read_tokens);
|
|
10642
|
+
output_price += calcMtokPrice(modelPrice.output_mtok, usage.output_tokens);
|
|
10643
|
+
output_price += calcMtokPrice(modelPrice.output_audio_mtok, usage.output_audio_tokens);
|
|
10622
10644
|
if (modelPrice.requests_kcount !== void 0) {
|
|
10623
|
-
|
|
10645
|
+
input_price += modelPrice.requests_kcount / 1e3;
|
|
10624
10646
|
}
|
|
10625
|
-
|
|
10647
|
+
const total_price = input_price + output_price;
|
|
10648
|
+
return {
|
|
10649
|
+
input_price,
|
|
10650
|
+
output_price,
|
|
10651
|
+
total_price
|
|
10652
|
+
};
|
|
10626
10653
|
}
|
|
10627
10654
|
function getActiveModelPrice(model, timestamp) {
|
|
10628
10655
|
if (!Array.isArray(model.prices)) {
|
|
@@ -10636,9 +10663,17 @@
|
|
|
10636
10663
|
return cond.prices;
|
|
10637
10664
|
}
|
|
10638
10665
|
} else if (cond.constraint.type === "time_of_date") {
|
|
10639
|
-
const t = timestamp.
|
|
10640
|
-
|
|
10641
|
-
|
|
10666
|
+
const t = timestamp.toISOString().slice(11, 19);
|
|
10667
|
+
const startTime = cond.constraint.start_time;
|
|
10668
|
+
const endTime = cond.constraint.end_time;
|
|
10669
|
+
if (endTime < startTime) {
|
|
10670
|
+
if (t >= startTime || t < endTime) {
|
|
10671
|
+
return cond.prices;
|
|
10672
|
+
}
|
|
10673
|
+
} else {
|
|
10674
|
+
if (t >= startTime && t < endTime) {
|
|
10675
|
+
return cond.prices;
|
|
10676
|
+
}
|
|
10642
10677
|
}
|
|
10643
10678
|
}
|
|
10644
10679
|
}
|
|
@@ -10653,9 +10688,11 @@
|
|
|
10653
10688
|
if (!model) return null;
|
|
10654
10689
|
const timestamp = options.timestamp || /* @__PURE__ */ new Date();
|
|
10655
10690
|
const model_price = getActiveModelPrice(model, timestamp);
|
|
10656
|
-
const
|
|
10691
|
+
const priceResult = calcPrice(usage, model_price);
|
|
10657
10692
|
return {
|
|
10658
|
-
|
|
10693
|
+
input_price: priceResult.input_price,
|
|
10694
|
+
output_price: priceResult.output_price,
|
|
10695
|
+
total_price: priceResult.total_price,
|
|
10659
10696
|
provider,
|
|
10660
10697
|
model,
|
|
10661
10698
|
model_price,
|
|
@@ -10671,9 +10708,11 @@
|
|
|
10671
10708
|
if (!model) return null;
|
|
10672
10709
|
const timestamp = options.timestamp || /* @__PURE__ */ new Date();
|
|
10673
10710
|
const model_price = getActiveModelPrice(model, timestamp);
|
|
10674
|
-
const
|
|
10711
|
+
const priceResult = calcPrice(usage, model_price);
|
|
10675
10712
|
return {
|
|
10676
|
-
|
|
10713
|
+
input_price: priceResult.input_price,
|
|
10714
|
+
output_price: priceResult.output_price,
|
|
10715
|
+
total_price: priceResult.total_price,
|
|
10677
10716
|
provider,
|
|
10678
10717
|
model,
|
|
10679
10718
|
model_price,
|
package/dist/matcher.js
CHANGED
|
@@ -22,14 +22,20 @@ function matchLogic(logic, text) {
|
|
|
22
22
|
}
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
|
+
function findProviderById(providers, providerId) {
|
|
26
|
+
const normalizedProviderId = providerId.toLowerCase().trim();
|
|
27
|
+
const exactMatch = providers.find((p) => p.id === normalizedProviderId);
|
|
28
|
+
if (exactMatch)
|
|
29
|
+
return exactMatch;
|
|
30
|
+
return providers.find((p) => p.provider_match && matchLogic(p.provider_match, normalizedProviderId));
|
|
31
|
+
}
|
|
25
32
|
export function matchProvider(providers, modelRef, providerId, providerApiUrl) {
|
|
26
33
|
if (providerId) {
|
|
27
|
-
return providers
|
|
34
|
+
return findProviderById(providers, providerId);
|
|
28
35
|
}
|
|
29
36
|
if (providerApiUrl) {
|
|
30
37
|
return providers.find((p) => new RegExp(p.api_pattern).test(providerApiUrl));
|
|
31
38
|
}
|
|
32
|
-
// Try model_match logic
|
|
33
39
|
return providers.find((p) => p.model_match && matchLogic(p.model_match, modelRef));
|
|
34
40
|
}
|
|
35
41
|
export function matchModel(models, modelRef) {
|
package/dist/priceCalc.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { Usage, ModelPrice, ModelInfo } from './types.js';
|
|
2
|
-
export declare function calcPrice(usage: Usage, modelPrice: ModelPrice):
|
|
1
|
+
import { Usage, ModelPrice, ModelInfo, CalcPrice } from './types.js';
|
|
2
|
+
export declare function calcPrice(usage: Usage, modelPrice: ModelPrice): CalcPrice;
|
|
3
3
|
export declare function getActiveModelPrice(model: ModelInfo, timestamp: Date): ModelPrice;
|
package/dist/priceCalc.js
CHANGED
|
@@ -29,18 +29,27 @@ function calcMtokPrice(price, tokens, _field) {
|
|
|
29
29
|
return calcTieredPrice(price, tokens);
|
|
30
30
|
}
|
|
31
31
|
export function calcPrice(usage, modelPrice) {
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
let input_price = 0;
|
|
33
|
+
let output_price = 0;
|
|
34
|
+
// Input-related prices
|
|
35
|
+
input_price += calcMtokPrice(modelPrice.input_mtok, usage.input_tokens, 'input_mtok');
|
|
36
|
+
input_price += calcMtokPrice(modelPrice.cache_write_mtok, usage.cache_write_tokens, 'cache_write_mtok');
|
|
37
|
+
input_price += calcMtokPrice(modelPrice.cache_read_mtok, usage.cache_read_tokens, 'cache_read_mtok');
|
|
38
|
+
input_price += calcMtokPrice(modelPrice.input_audio_mtok, usage.input_audio_tokens, 'input_audio_mtok');
|
|
39
|
+
input_price += calcMtokPrice(modelPrice.cache_audio_read_mtok, usage.cache_audio_read_tokens, 'cache_audio_read_mtok');
|
|
40
|
+
// Output-related prices
|
|
41
|
+
output_price += calcMtokPrice(modelPrice.output_mtok, usage.output_tokens, 'output_mtok');
|
|
42
|
+
output_price += calcMtokPrice(modelPrice.output_audio_mtok, usage.output_audio_tokens, 'output_audio_mtok');
|
|
43
|
+
// Requests price (counted as input cost)
|
|
40
44
|
if (modelPrice.requests_kcount !== undefined) {
|
|
41
|
-
|
|
45
|
+
input_price += modelPrice.requests_kcount / 1000;
|
|
42
46
|
}
|
|
43
|
-
|
|
47
|
+
const total_price = input_price + output_price;
|
|
48
|
+
return {
|
|
49
|
+
input_price,
|
|
50
|
+
output_price,
|
|
51
|
+
total_price,
|
|
52
|
+
};
|
|
44
53
|
}
|
|
45
54
|
export function getActiveModelPrice(model, timestamp) {
|
|
46
55
|
if (!Array.isArray(model.prices)) {
|
|
@@ -57,9 +66,22 @@ export function getActiveModelPrice(model, timestamp) {
|
|
|
57
66
|
}
|
|
58
67
|
}
|
|
59
68
|
else if (cond.constraint.type === 'time_of_date') {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
// Extract UTC time to match constraint times which are in UTC (with 'Z' suffix)
|
|
70
|
+
const t = timestamp.toISOString().slice(11, 19); // Get "HH:MM:SS" from ISO string
|
|
71
|
+
const startTime = cond.constraint.start_time;
|
|
72
|
+
const endTime = cond.constraint.end_time;
|
|
73
|
+
// Handle time ranges that span midnight (end time < start time)
|
|
74
|
+
if (endTime < startTime) {
|
|
75
|
+
// Time is in range if it's >= start OR < end
|
|
76
|
+
if (t >= startTime || t < endTime) {
|
|
77
|
+
return cond.prices;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Normal time range (start <= time < end)
|
|
82
|
+
if (t >= startTime && t < endTime) {
|
|
83
|
+
return cond.prices;
|
|
84
|
+
}
|
|
63
85
|
}
|
|
64
86
|
}
|
|
65
87
|
}
|
|
@@ -11,9 +11,11 @@ export function calcPriceSync(usage, modelRef, options = {}) {
|
|
|
11
11
|
return null;
|
|
12
12
|
const timestamp = options.timestamp || new Date();
|
|
13
13
|
const model_price = getActiveModelPrice(model, timestamp);
|
|
14
|
-
const
|
|
14
|
+
const priceResult = calcModelPrice(usage, model_price);
|
|
15
15
|
return {
|
|
16
|
-
|
|
16
|
+
input_price: priceResult.input_price,
|
|
17
|
+
output_price: priceResult.output_price,
|
|
18
|
+
total_price: priceResult.total_price,
|
|
17
19
|
provider,
|
|
18
20
|
model,
|
|
19
21
|
model_price,
|
package/dist/types.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ export interface Usage {
|
|
|
6
6
|
input_audio_tokens?: number;
|
|
7
7
|
cache_audio_read_tokens?: number;
|
|
8
8
|
output_audio_tokens?: number;
|
|
9
|
-
requests?: number;
|
|
10
9
|
}
|
|
11
10
|
export interface Tier {
|
|
12
11
|
start: number;
|
|
@@ -71,10 +70,18 @@ export interface Provider {
|
|
|
71
70
|
description?: string;
|
|
72
71
|
price_comments?: string;
|
|
73
72
|
model_match?: MatchLogic;
|
|
73
|
+
provider_match?: MatchLogic;
|
|
74
74
|
models: ModelInfo[];
|
|
75
75
|
}
|
|
76
|
+
export interface CalcPrice {
|
|
77
|
+
input_price: number;
|
|
78
|
+
output_price: number;
|
|
79
|
+
total_price: number;
|
|
80
|
+
}
|
|
76
81
|
export interface PriceCalculation {
|
|
77
|
-
|
|
82
|
+
input_price: number;
|
|
83
|
+
output_price: number;
|
|
84
|
+
total_price: number;
|
|
78
85
|
provider: Provider;
|
|
79
86
|
model: ModelInfo;
|
|
80
87
|
model_price: ModelPrice;
|
package/package.json
CHANGED