metergraph-core 0.7.1__tar.gz → 0.7.5__tar.gz

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.
Files changed (24) hide show
  1. {metergraph_core-0.7.1/src/metergraph_core.egg-info → metergraph_core-0.7.5}/PKG-INFO +13 -2
  2. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/README.md +12 -1
  3. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/pyproject.toml +1 -1
  4. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/catalog.py +32 -4
  5. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/catalog_delta.py +2 -0
  6. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/data/prices.yaml +53 -6
  7. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/loader.py +22 -0
  8. {metergraph_core-0.7.1 → metergraph_core-0.7.5/src/metergraph_core.egg-info}/PKG-INFO +13 -2
  9. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/MANIFEST.in +0 -0
  10. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/setup.cfg +0 -0
  11. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/__init__.py +0 -0
  12. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/billing.py +0 -0
  13. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/data/models.yaml +0 -0
  14. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/data/sources.yaml +0 -0
  15. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/extractors.py +0 -0
  16. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/models.py +0 -0
  17. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/refresh.py +0 -0
  18. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/responses.py +0 -0
  19. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/retrieval.py +0 -0
  20. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core/sources.py +0 -0
  21. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core.egg-info/SOURCES.txt +0 -0
  22. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core.egg-info/dependency_links.txt +0 -0
  23. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core.egg-info/requires.txt +0 -0
  24. {metergraph_core-0.7.1 → metergraph_core-0.7.5}/src/metergraph_core.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metergraph-core
3
- Version: 0.7.1
3
+ Version: 0.7.5
4
4
  Summary: Open, effective-dated LLM pricing catalog and deterministic cost calculator for popular models across major providers and gateways, including OpenAI, Anthropic, Google Gemini, AWS Bedrock, OpenRouter, Vercel AI Gateway, and more.
5
5
  Author: Metergraph
6
6
  License-Expression: Apache-2.0
@@ -179,6 +179,16 @@ of the applied price. The bundled catalog is USD. `cost` and
179
179
  `components.input`, `output`, `cache_read`, `cache_write` and `request` are in
180
180
  that currency; the older `cost_usd` and `*_usd` names return the same values.
181
181
 
182
+ `ModelUsage(batch=True)`, or `batch=True` on `price` and `cost`, bills at the
183
+ row's batch input and output rates. Its cache reads bill at
184
+ `Price.batch_cache_read_per_mtok` when the row has one, and otherwise at the
185
+ standard `cache_read_per_mtok`, as before. A standard call always uses the
186
+ standard cache-read rate. A row with only a batch cache rate fully prices a
187
+ cached batch call, while a cached standard call on it is `partial` with
188
+ `cache_read_rate_unavailable`.
189
+ Batch cache writes use `Price.batch_cache_write_5m_per_mtok` when stated;
190
+ otherwise they retain the row's standard cache-write rate.
191
+
182
192
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
183
193
  output count keeps the subtotal that is known, marks the result `partial`, and
184
194
  lists `input_tokens` or `output_tokens` in `missing_dimensions`.
@@ -242,7 +252,8 @@ The price in effect when the response was created applies, from `created`,
242
252
  creation time is priced now. `catalog=` costs against a catalog you loaded;
243
253
  the default is the bundled one.
244
254
 
245
- A batch service tier bills at batch rates. Pass `service_tier=` when the
255
+ A batch service tier bills at batch rates, with cache reads priced as for
256
+ `ModelUsage(batch=True)` above. Pass `service_tier=` when the
246
257
  response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
247
258
  such as `priority` or `flex`, returns `partial` with `service_tier` in
248
259
  `missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
@@ -149,6 +149,16 @@ of the applied price. The bundled catalog is USD. `cost` and
149
149
  `components.input`, `output`, `cache_read`, `cache_write` and `request` are in
150
150
  that currency; the older `cost_usd` and `*_usd` names return the same values.
151
151
 
152
+ `ModelUsage(batch=True)`, or `batch=True` on `price` and `cost`, bills at the
153
+ row's batch input and output rates. Its cache reads bill at
154
+ `Price.batch_cache_read_per_mtok` when the row has one, and otherwise at the
155
+ standard `cache_read_per_mtok`, as before. A standard call always uses the
156
+ standard cache-read rate. A row with only a batch cache rate fully prices a
157
+ cached batch call, while a cached standard call on it is `partial` with
158
+ `cache_read_rate_unavailable`.
159
+ Batch cache writes use `Price.batch_cache_write_5m_per_mtok` when stated;
160
+ otherwise they retain the row's standard cache-write rate.
161
+
152
162
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
153
163
  output count keeps the subtotal that is known, marks the result `partial`, and
154
164
  lists `input_tokens` or `output_tokens` in `missing_dimensions`.
@@ -212,7 +222,8 @@ The price in effect when the response was created applies, from `created`,
212
222
  creation time is priced now. `catalog=` costs against a catalog you loaded;
213
223
  the default is the bundled one.
214
224
 
215
- A batch service tier bills at batch rates. Pass `service_tier=` when the
225
+ A batch service tier bills at batch rates, with cache reads priced as for
226
+ `ModelUsage(batch=True)` above. Pass `service_tier=` when the
216
227
  response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
217
228
  such as `priority` or `flex`, returns `partial` with `service_tier` in
218
229
  `missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "metergraph-core"
3
- version = "0.7.1"
3
+ version = "0.7.5"
4
4
  description = "Open, effective-dated LLM pricing catalog and deterministic cost calculator for popular models across major providers and gateways, including OpenAI, Anthropic, Google Gemini, AWS Bedrock, OpenRouter, Vercel AI Gateway, and more."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -225,6 +225,24 @@ class Price:
225
225
  # The currency every rate on this row is stated in. The `_usd` names on
226
226
  # rates and results predate it and are kept for existing callers.
227
227
  currency: str = "USD"
228
+ # The rate a batch call's cache reads bill at, when the provider publishes
229
+ # one. Absent, batch cache reads keep the standard cache-read rate; it is
230
+ # never inferred from another rate.
231
+ batch_cache_read_per_mtok: Decimal | None = None
232
+ # OpenAI publishes a separate Batch/Flex cache-write rate.
233
+ batch_cache_write_5m_per_mtok: Decimal | None = None
234
+
235
+ def __post_init__(self) -> None:
236
+ # The loader checks the rate it reads, but a row built or replaced
237
+ # directly must not bill a negative or undefined cache component.
238
+ for name in ("batch_cache_read_per_mtok", "batch_cache_write_5m_per_mtok"):
239
+ rate = getattr(self, name)
240
+ if rate is not None and not (
241
+ isinstance(rate, Decimal) and rate.is_finite() and rate >= 0
242
+ ):
243
+ raise ValueError(
244
+ f"{name} must be a finite non-negative Decimal or None, not {rate!r}"
245
+ )
228
246
 
229
247
 
230
248
  @dataclass(frozen=True, slots=True)
@@ -353,6 +371,8 @@ def _billed_terms(resolved: ResolvedPrice) -> tuple[Any, ...]:
353
371
  price.cache_write_1h_per_mtok,
354
372
  price.batch_input_per_mtok,
355
373
  price.batch_output_per_mtok,
374
+ price.batch_cache_read_per_mtok,
375
+ price.batch_cache_write_5m_per_mtok,
356
376
  price.request_fee_per_1k_usd,
357
377
  resolved.rules,
358
378
  )
@@ -505,12 +525,20 @@ def _price_tokens(
505
525
 
506
526
  input_rate = price.input_per_mtok
507
527
  output_rate = price.output_per_mtok
528
+ cache_read_rate = price.cache_read_per_mtok
529
+ cache_write_5m_rate = price.cache_write_5m_per_mtok
508
530
  if batch:
509
531
  if price.batch_input_per_mtok is None or price.batch_output_per_mtok is None:
510
532
  reasons.append("batch_rate_unavailable")
511
533
  else:
512
534
  input_rate = price.batch_input_per_mtok
513
535
  output_rate = price.batch_output_per_mtok
536
+ # A row without a batch cache rate keeps billing batch cache reads at
537
+ # the standard rate, as it always has.
538
+ if price.batch_cache_read_per_mtok is not None:
539
+ cache_read_rate = price.batch_cache_read_per_mtok
540
+ if price.batch_cache_write_5m_per_mtok is not None:
541
+ cache_write_5m_rate = price.batch_cache_write_5m_per_mtok
514
542
 
515
543
  input_multiplier = Decimal("1")
516
544
  output_multiplier = Decimal("1")
@@ -547,22 +575,22 @@ def _price_tokens(
547
575
  else:
548
576
  output_usd = Decimal(output_count) * output_rate * output_multiplier / _MILLION
549
577
  if cache_read_count:
550
- if price.cache_read_per_mtok is None:
578
+ if cache_read_rate is None:
551
579
  reasons.append("cache_read_rate_unavailable")
552
580
  else:
553
581
  cache_read_usd = (
554
582
  Decimal(cache_read_count)
555
- * price.cache_read_per_mtok
583
+ * cache_read_rate
556
584
  * input_multiplier
557
585
  / _MILLION
558
586
  )
559
587
  if cache_write_5m_count:
560
- if price.cache_write_5m_per_mtok is None:
588
+ if cache_write_5m_rate is None:
561
589
  reasons.append("cache_write_5m_rate_unavailable")
562
590
  else:
563
591
  cache_write_usd += (
564
592
  Decimal(cache_write_5m_count)
565
- * price.cache_write_5m_per_mtok
593
+ * cache_write_5m_rate
566
594
  * input_multiplier
567
595
  / _MILLION
568
596
  )
@@ -45,6 +45,8 @@ RATE_FIELDS = (
45
45
  "cache_write_1h_per_mtok",
46
46
  "batch_input_per_mtok",
47
47
  "batch_output_per_mtok",
48
+ "batch_cache_read_per_mtok",
49
+ "batch_cache_write_5m_per_mtok",
48
50
  "request_fee_per_1k_usd",
49
51
  )
50
52
  _PRICE_FIELDS = frozenset(
@@ -6,9 +6,9 @@
6
6
  #
7
7
  # Community updates welcome: add an alias or price entry with its provider
8
8
  # source_url and open a PR. CI validates structure and date overlaps.
9
- version: "2026-10-23"
9
+ version: "2026-10-27"
10
10
  currency: USD
11
- pricing_verified_at: "2026-10-23"
11
+ pricing_verified_at: "2026-10-27"
12
12
  models:
13
13
  - canonical_id: openai/gpt-6-sol
14
14
  publisher: openai
@@ -290,6 +290,30 @@ models:
290
290
  long_context: {threshold: 272000, input_multiplier: 2, output_multiplier: 1.5}
291
291
  source_url: https://ai-gateway.vercel.sh/v1/models
292
292
 
293
+ - canonical_id: openai/gpt-6.1-terra
294
+ publisher: openai
295
+ aliases:
296
+ - {provider: openai, alias: gpt-6.1-terra, channel: openai-api}
297
+ - {provider: litellm, alias: gpt-6.1-terra, channel: openai-api}
298
+ - {provider: unknown, alias: gpt-6.1-terra, channel: openai-api}
299
+ prices:
300
+ - channel: openai-api
301
+ region: global
302
+ effective_from: "2026-10-26"
303
+ input_per_mtok: 1.00
304
+ output_per_mtok: 5.00
305
+ cache_read_per_mtok: 0.05
306
+ cache_write_5m_per_mtok: 1.25
307
+ batch_input_per_mtok: 0.50
308
+ batch_output_per_mtok: 2.50
309
+ batch_cache_read_per_mtok: 0.025
310
+ batch_cache_write_5m_per_mtok: 0.625
311
+ rules:
312
+ input_includes_cache_read: true
313
+ input_includes_cache_write: true
314
+ long_context: {threshold: 272000, input_multiplier: 2, output_multiplier: 1.5}
315
+ source_url: https://developers.openai.com/api/docs/models/gpt-6.1-terra
316
+
293
317
  - canonical_id: openai/gpt-5.6-sol
294
318
  publisher: openai
295
319
  aliases:
@@ -1599,18 +1623,32 @@ models:
1599
1623
  - channel: google-api
1600
1624
  region: global
1601
1625
  effective_from: "2026-10-13"
1602
- effective_to: "2026-12-01"
1626
+ effective_to: "2026-10-25"
1603
1627
  input_per_mtok: 2.00
1604
1628
  output_per_mtok: 10.00
1605
1629
  cache_read_per_mtok: 0.10
1606
1630
  source_url: https://ai.google.dev/gemini-api/docs/generate-content/latest-model
1631
+ - channel: google-api
1632
+ region: global
1633
+ effective_from: "2026-10-25"
1634
+ effective_to: "2026-12-01"
1635
+ input_per_mtok: 2.00
1636
+ output_per_mtok: 10.00
1637
+ cache_read_per_mtok: 0.10
1638
+ batch_input_per_mtok: 1.00
1639
+ batch_output_per_mtok: 5.00
1640
+ batch_cache_read_per_mtok: 0.05
1641
+ source_url: https://ai.google.dev/gemini-api/docs/pricing
1607
1642
  - channel: google-api
1608
1643
  region: global
1609
1644
  effective_from: "2026-12-01"
1610
1645
  input_per_mtok: 4.00
1611
1646
  output_per_mtok: 20.00
1612
1647
  cache_read_per_mtok: 0.20
1613
- source_url: https://ai.google.dev/gemini-api/docs/generate-content/latest-model
1648
+ batch_input_per_mtok: 2.00
1649
+ batch_output_per_mtok: 10.00
1650
+ batch_cache_read_per_mtok: 0.10
1651
+ source_url: https://ai.google.dev/gemini-api/docs/pricing
1614
1652
  - channel: vercel-ai-gateway
1615
1653
  region: global
1616
1654
  effective_from: "2026-10-13"
@@ -2506,15 +2544,24 @@ models:
2506
2544
  rules:
2507
2545
  uncaptured_fees: true
2508
2546
  source_url: https://docs.perplexity.ai/docs/getting-started/pricing
2509
- # The gateway bills Sonar's built-in search per call, so the row carries
2510
- # it as a request fee and the call is priced in full.
2511
2547
  - channel: vercel-ai-gateway
2512
2548
  region: global
2513
2549
  effective_from: "2026-10-11"
2550
+ effective_to: "2026-10-24"
2514
2551
  input_per_mtok: 0.25
2515
2552
  output_per_mtok: 2.50
2516
2553
  request_fee_per_1k_usd: 2.50
2517
2554
  source_url: https://vercel.com/ai-gateway/models/sonar
2555
+ # The gateway lists Sonar's web searches as a charge of their own, billed
2556
+ # per search and not per request, so no request fee applies.
2557
+ - channel: vercel-ai-gateway
2558
+ region: global
2559
+ effective_from: "2026-10-24"
2560
+ input_per_mtok: 0.25
2561
+ output_per_mtok: 2.50
2562
+ rules:
2563
+ uncaptured_fees: true
2564
+ source_url: https://vercel.com/ai-gateway/models/sonar
2518
2565
 
2519
2566
  - canonical_id: openai/gpt-5-mini
2520
2567
  publisher: openai
@@ -388,6 +388,26 @@ def parse_catalog(
388
388
  f"{canonical}: overlapping {channel}/{region} price windows"
389
389
  )
390
390
  seen_windows.append((channel, region, effective_from, effective_to))
391
+ batch_cache_read = price.get("batch_cache_read_per_mtok")
392
+ batch_cache_read_per_mtok = _decimal(batch_cache_read)
393
+ # A stated batch cache rate replaces the standard one on batch
394
+ # calls, so one that cannot be read must not quietly fall back.
395
+ if batch_cache_read is not None and (
396
+ batch_cache_read_per_mtok is None or batch_cache_read_per_mtok < 0
397
+ ):
398
+ raise CatalogError(
399
+ f"{canonical}: invalid batch_cache_read_per_mtok "
400
+ f"{batch_cache_read!r}"
401
+ )
402
+ batch_cache_write = price.get("batch_cache_write_5m_per_mtok")
403
+ batch_cache_write_5m_per_mtok = _decimal(batch_cache_write)
404
+ if batch_cache_write is not None and (
405
+ batch_cache_write_5m_per_mtok is None or batch_cache_write_5m_per_mtok < 0
406
+ ):
407
+ raise CatalogError(
408
+ f"{canonical}: invalid batch_cache_write_5m_per_mtok "
409
+ f"{batch_cache_write!r}"
410
+ )
391
411
  prices.append(
392
412
  Price(
393
413
  id=f"{canonical}:{channel}:{region}:{_id_date(effective_from)}",
@@ -412,6 +432,8 @@ def parse_catalog(
412
432
  source_url=str(price["source_url"]).strip(),
413
433
  publisher=publisher,
414
434
  currency=currency,
435
+ batch_cache_read_per_mtok=batch_cache_read_per_mtok,
436
+ batch_cache_write_5m_per_mtok=batch_cache_write_5m_per_mtok,
415
437
  )
416
438
  )
417
439
  return version, {key: tuple(value) for key, value in aliases.items()}, _unique_ids(prices)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metergraph-core
3
- Version: 0.7.1
3
+ Version: 0.7.5
4
4
  Summary: Open, effective-dated LLM pricing catalog and deterministic cost calculator for popular models across major providers and gateways, including OpenAI, Anthropic, Google Gemini, AWS Bedrock, OpenRouter, Vercel AI Gateway, and more.
5
5
  Author: Metergraph
6
6
  License-Expression: Apache-2.0
@@ -179,6 +179,16 @@ of the applied price. The bundled catalog is USD. `cost` and
179
179
  `components.input`, `output`, `cache_read`, `cache_write` and `request` are in
180
180
  that currency; the older `cost_usd` and `*_usd` names return the same values.
181
181
 
182
+ `ModelUsage(batch=True)`, or `batch=True` on `price` and `cost`, bills at the
183
+ row's batch input and output rates. Its cache reads bill at
184
+ `Price.batch_cache_read_per_mtok` when the row has one, and otherwise at the
185
+ standard `cache_read_per_mtok`, as before. A standard call always uses the
186
+ standard cache-read rate. A row with only a batch cache rate fully prices a
187
+ cached batch call, while a cached standard call on it is `partial` with
188
+ `cache_read_rate_unavailable`.
189
+ Batch cache writes use `Price.batch_cache_write_5m_per_mtok` when stated;
190
+ otherwise they retain the row's standard cache-write rate.
191
+
182
192
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
183
193
  output count keeps the subtotal that is known, marks the result `partial`, and
184
194
  lists `input_tokens` or `output_tokens` in `missing_dimensions`.
@@ -242,7 +252,8 @@ The price in effect when the response was created applies, from `created`,
242
252
  creation time is priced now. `catalog=` costs against a catalog you loaded;
243
253
  the default is the bundled one.
244
254
 
245
- A batch service tier bills at batch rates. Pass `service_tier=` when the
255
+ A batch service tier bills at batch rates, with cache reads priced as for
256
+ `ModelUsage(batch=True)` above. Pass `service_tier=` when the
246
257
  response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
247
258
  such as `priority` or `flex`, returns `partial` with `service_tier` in
248
259
  `missing_dimensions`, since the catalog carries no tier rates. Cost a streamed