metergraph-core 0.7.1__tar.gz → 0.7.4__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.4}/PKG-INFO +11 -2
  2. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/README.md +10 -1
  3. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/pyproject.toml +1 -1
  4. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/catalog.py +24 -2
  5. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/catalog_delta.py +1 -0
  6. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/data/prices.yaml +29 -6
  7. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/loader.py +12 -0
  8. {metergraph_core-0.7.1 → metergraph_core-0.7.4/src/metergraph_core.egg-info}/PKG-INFO +11 -2
  9. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/MANIFEST.in +0 -0
  10. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/setup.cfg +0 -0
  11. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/__init__.py +0 -0
  12. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/billing.py +0 -0
  13. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/data/models.yaml +0 -0
  14. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/data/sources.yaml +0 -0
  15. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/extractors.py +0 -0
  16. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/models.py +0 -0
  17. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/refresh.py +0 -0
  18. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/responses.py +0 -0
  19. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/retrieval.py +0 -0
  20. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core/sources.py +0 -0
  21. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core.egg-info/SOURCES.txt +0 -0
  22. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core.egg-info/dependency_links.txt +0 -0
  23. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/src/metergraph_core.egg-info/requires.txt +0 -0
  24. {metergraph_core-0.7.1 → metergraph_core-0.7.4}/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.4
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,14 @@ 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
+
182
190
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
183
191
  output count keeps the subtotal that is known, marks the result `partial`, and
184
192
  lists `input_tokens` or `output_tokens` in `missing_dimensions`.
@@ -242,7 +250,8 @@ The price in effect when the response was created applies, from `created`,
242
250
  creation time is priced now. `catalog=` costs against a catalog you loaded;
243
251
  the default is the bundled one.
244
252
 
245
- A batch service tier bills at batch rates. Pass `service_tier=` when the
253
+ A batch service tier bills at batch rates, with cache reads priced as for
254
+ `ModelUsage(batch=True)` above. Pass `service_tier=` when the
246
255
  response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
247
256
  such as `priority` or `flex`, returns `partial` with `service_tier` in
248
257
  `missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
@@ -149,6 +149,14 @@ 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
+
152
160
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
153
161
  output count keeps the subtotal that is known, marks the result `partial`, and
154
162
  lists `input_tokens` or `output_tokens` in `missing_dimensions`.
@@ -212,7 +220,8 @@ The price in effect when the response was created applies, from `created`,
212
220
  creation time is priced now. `catalog=` costs against a catalog you loaded;
213
221
  the default is the bundled one.
214
222
 
215
- A batch service tier bills at batch rates. Pass `service_tier=` when the
223
+ A batch service tier bills at batch rates, with cache reads priced as for
224
+ `ModelUsage(batch=True)` above. Pass `service_tier=` when the
216
225
  response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
217
226
  such as `priority` or `flex`, returns `partial` with `service_tier` in
218
227
  `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.4"
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,22 @@ 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
+
233
+ def __post_init__(self) -> None:
234
+ # The loader checks the rate it reads, but a row built or replaced
235
+ # directly must not bill a negative or undefined cache component.
236
+ rate = self.batch_cache_read_per_mtok
237
+ if rate is not None and not (
238
+ isinstance(rate, Decimal) and rate.is_finite() and rate >= 0
239
+ ):
240
+ raise ValueError(
241
+ "batch_cache_read_per_mtok must be a finite non-negative "
242
+ f"Decimal or None, not {rate!r}"
243
+ )
228
244
 
229
245
 
230
246
  @dataclass(frozen=True, slots=True)
@@ -353,6 +369,7 @@ def _billed_terms(resolved: ResolvedPrice) -> tuple[Any, ...]:
353
369
  price.cache_write_1h_per_mtok,
354
370
  price.batch_input_per_mtok,
355
371
  price.batch_output_per_mtok,
372
+ price.batch_cache_read_per_mtok,
356
373
  price.request_fee_per_1k_usd,
357
374
  resolved.rules,
358
375
  )
@@ -505,12 +522,17 @@ def _price_tokens(
505
522
 
506
523
  input_rate = price.input_per_mtok
507
524
  output_rate = price.output_per_mtok
525
+ cache_read_rate = price.cache_read_per_mtok
508
526
  if batch:
509
527
  if price.batch_input_per_mtok is None or price.batch_output_per_mtok is None:
510
528
  reasons.append("batch_rate_unavailable")
511
529
  else:
512
530
  input_rate = price.batch_input_per_mtok
513
531
  output_rate = price.batch_output_per_mtok
532
+ # A row without a batch cache rate keeps billing batch cache reads at
533
+ # the standard rate, as it always has.
534
+ if price.batch_cache_read_per_mtok is not None:
535
+ cache_read_rate = price.batch_cache_read_per_mtok
514
536
 
515
537
  input_multiplier = Decimal("1")
516
538
  output_multiplier = Decimal("1")
@@ -547,12 +569,12 @@ def _price_tokens(
547
569
  else:
548
570
  output_usd = Decimal(output_count) * output_rate * output_multiplier / _MILLION
549
571
  if cache_read_count:
550
- if price.cache_read_per_mtok is None:
572
+ if cache_read_rate is None:
551
573
  reasons.append("cache_read_rate_unavailable")
552
574
  else:
553
575
  cache_read_usd = (
554
576
  Decimal(cache_read_count)
555
- * price.cache_read_per_mtok
577
+ * cache_read_rate
556
578
  * input_multiplier
557
579
  / _MILLION
558
580
  )
@@ -45,6 +45,7 @@ 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",
48
49
  "request_fee_per_1k_usd",
49
50
  )
50
51
  _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-25"
10
10
  currency: USD
11
- pricing_verified_at: "2026-10-23"
11
+ pricing_verified_at: "2026-10-25"
12
12
  models:
13
13
  - canonical_id: openai/gpt-6-sol
14
14
  publisher: openai
@@ -1599,18 +1599,32 @@ models:
1599
1599
  - channel: google-api
1600
1600
  region: global
1601
1601
  effective_from: "2026-10-13"
1602
- effective_to: "2026-12-01"
1602
+ effective_to: "2026-10-25"
1603
1603
  input_per_mtok: 2.00
1604
1604
  output_per_mtok: 10.00
1605
1605
  cache_read_per_mtok: 0.10
1606
1606
  source_url: https://ai.google.dev/gemini-api/docs/generate-content/latest-model
1607
+ - channel: google-api
1608
+ region: global
1609
+ effective_from: "2026-10-25"
1610
+ effective_to: "2026-12-01"
1611
+ input_per_mtok: 2.00
1612
+ output_per_mtok: 10.00
1613
+ cache_read_per_mtok: 0.10
1614
+ batch_input_per_mtok: 1.00
1615
+ batch_output_per_mtok: 5.00
1616
+ batch_cache_read_per_mtok: 0.05
1617
+ source_url: https://ai.google.dev/gemini-api/docs/pricing
1607
1618
  - channel: google-api
1608
1619
  region: global
1609
1620
  effective_from: "2026-12-01"
1610
1621
  input_per_mtok: 4.00
1611
1622
  output_per_mtok: 20.00
1612
1623
  cache_read_per_mtok: 0.20
1613
- source_url: https://ai.google.dev/gemini-api/docs/generate-content/latest-model
1624
+ batch_input_per_mtok: 2.00
1625
+ batch_output_per_mtok: 10.00
1626
+ batch_cache_read_per_mtok: 0.10
1627
+ source_url: https://ai.google.dev/gemini-api/docs/pricing
1614
1628
  - channel: vercel-ai-gateway
1615
1629
  region: global
1616
1630
  effective_from: "2026-10-13"
@@ -2506,15 +2520,24 @@ models:
2506
2520
  rules:
2507
2521
  uncaptured_fees: true
2508
2522
  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
2523
  - channel: vercel-ai-gateway
2512
2524
  region: global
2513
2525
  effective_from: "2026-10-11"
2526
+ effective_to: "2026-10-24"
2514
2527
  input_per_mtok: 0.25
2515
2528
  output_per_mtok: 2.50
2516
2529
  request_fee_per_1k_usd: 2.50
2517
2530
  source_url: https://vercel.com/ai-gateway/models/sonar
2531
+ # The gateway lists Sonar's web searches as a charge of their own, billed
2532
+ # per search and not per request, so no request fee applies.
2533
+ - channel: vercel-ai-gateway
2534
+ region: global
2535
+ effective_from: "2026-10-24"
2536
+ input_per_mtok: 0.25
2537
+ output_per_mtok: 2.50
2538
+ rules:
2539
+ uncaptured_fees: true
2540
+ source_url: https://vercel.com/ai-gateway/models/sonar
2518
2541
 
2519
2542
  - canonical_id: openai/gpt-5-mini
2520
2543
  publisher: openai
@@ -388,6 +388,17 @@ 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
+ )
391
402
  prices.append(
392
403
  Price(
393
404
  id=f"{canonical}:{channel}:{region}:{_id_date(effective_from)}",
@@ -412,6 +423,7 @@ def parse_catalog(
412
423
  source_url=str(price["source_url"]).strip(),
413
424
  publisher=publisher,
414
425
  currency=currency,
426
+ batch_cache_read_per_mtok=batch_cache_read_per_mtok,
415
427
  )
416
428
  )
417
429
  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.4
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,14 @@ 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
+
182
190
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
183
191
  output count keeps the subtotal that is known, marks the result `partial`, and
184
192
  lists `input_tokens` or `output_tokens` in `missing_dimensions`.
@@ -242,7 +250,8 @@ The price in effect when the response was created applies, from `created`,
242
250
  creation time is priced now. `catalog=` costs against a catalog you loaded;
243
251
  the default is the bundled one.
244
252
 
245
- A batch service tier bills at batch rates. Pass `service_tier=` when the
253
+ A batch service tier bills at batch rates, with cache reads priced as for
254
+ `ModelUsage(batch=True)` above. Pass `service_tier=` when the
246
255
  response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
247
256
  such as `priority` or `flex`, returns `partial` with `service_tier` in
248
257
  `missing_dimensions`, since the catalog carries no tier rates. Cost a streamed