@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 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(result.price, result.provider.name, result.model.name)
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(asyncResult.price, asyncResult.provider.name, asyncResult.model.name)
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(result.price, result.provider.name, result.model.name)
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.price}`)
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(`Price: $${asyncResult.price}`)
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 price = calcModelPrice(usage, model_price);
14
+ const priceResult = calcModelPrice(usage, model_price);
15
15
  return {
16
- price,
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.price}`],
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';
@@ -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.find((p) => p.id === providerId);
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 price = 0;
10615
- price += calcMtokPrice(modelPrice.input_mtok, usage.input_tokens);
10616
- price += calcMtokPrice(modelPrice.cache_write_mtok, usage.cache_write_tokens);
10617
- price += calcMtokPrice(modelPrice.cache_read_mtok, usage.cache_read_tokens);
10618
- price += calcMtokPrice(modelPrice.output_mtok, usage.output_tokens);
10619
- price += calcMtokPrice(modelPrice.input_audio_mtok, usage.input_audio_tokens);
10620
- price += calcMtokPrice(modelPrice.cache_audio_read_mtok, usage.cache_audio_read_tokens);
10621
- price += calcMtokPrice(modelPrice.output_audio_mtok, usage.output_audio_tokens);
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
- price += modelPrice.requests_kcount * ((usage.requests ?? 1) / 1e3);
10645
+ input_price += modelPrice.requests_kcount / 1e3;
10624
10646
  }
10625
- return price;
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.toTimeString().slice(0, 8);
10640
- if (t >= cond.constraint.start_time && t < cond.constraint.end_time) {
10641
- return cond.prices;
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 price = calcPrice(usage, model_price);
10691
+ const priceResult = calcPrice(usage, model_price);
10657
10692
  return {
10658
- price,
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 price = calcPrice(usage, model_price);
10711
+ const priceResult = calcPrice(usage, model_price);
10675
10712
  return {
10676
- price,
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.find((p) => p.id === providerId);
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) {
@@ -1,3 +1,3 @@
1
- import { Usage, ModelPrice, ModelInfo } from './types.js';
2
- export declare function calcPrice(usage: Usage, modelPrice: ModelPrice): number;
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 price = 0;
33
- price += calcMtokPrice(modelPrice.input_mtok, usage.input_tokens, 'input_mtok');
34
- price += calcMtokPrice(modelPrice.cache_write_mtok, usage.cache_write_tokens, 'cache_write_mtok');
35
- price += calcMtokPrice(modelPrice.cache_read_mtok, usage.cache_read_tokens, 'cache_read_mtok');
36
- price += calcMtokPrice(modelPrice.output_mtok, usage.output_tokens, 'output_mtok');
37
- price += calcMtokPrice(modelPrice.input_audio_mtok, usage.input_audio_tokens, 'input_audio_mtok');
38
- price += calcMtokPrice(modelPrice.cache_audio_read_mtok, usage.cache_audio_read_tokens, 'cache_audio_read_mtok');
39
- price += calcMtokPrice(modelPrice.output_audio_mtok, usage.output_audio_tokens, 'output_audio_mtok');
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
- price += modelPrice.requests_kcount * ((usage.requests ?? 1) / 1000);
45
+ input_price += modelPrice.requests_kcount / 1000;
42
46
  }
43
- return price;
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
- const t = timestamp.toTimeString().slice(0, 8);
61
- if (t >= cond.constraint.start_time && t < cond.constraint.end_time) {
62
- return cond.prices;
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 price = calcModelPrice(usage, model_price);
14
+ const priceResult = calcModelPrice(usage, model_price);
15
15
  return {
16
- price,
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
- price: number;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pydantic/genai-prices",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Calculate prices for calling LLM inference APIs (JS/TS version of genai-prices)",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",