metergraph-core 0.7.4__tar.gz → 0.7.6__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.4/src/metergraph_core.egg-info → metergraph_core-0.7.6}/PKG-INFO +3 -1
  2. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/README.md +2 -0
  3. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/pyproject.toml +1 -1
  4. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/catalog.py +16 -10
  5. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/catalog_delta.py +1 -0
  6. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/data/prices.yaml +60 -2
  7. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/data/sources.yaml +10 -0
  8. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/extractors.py +26 -2
  9. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/loader.py +10 -0
  10. {metergraph_core-0.7.4 → metergraph_core-0.7.6/src/metergraph_core.egg-info}/PKG-INFO +3 -1
  11. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/MANIFEST.in +0 -0
  12. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/setup.cfg +0 -0
  13. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/__init__.py +0 -0
  14. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/billing.py +0 -0
  15. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/data/models.yaml +0 -0
  16. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/models.py +0 -0
  17. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/refresh.py +0 -0
  18. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/responses.py +0 -0
  19. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/retrieval.py +0 -0
  20. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core/sources.py +0 -0
  21. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core.egg-info/SOURCES.txt +0 -0
  22. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core.egg-info/dependency_links.txt +0 -0
  23. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/src/metergraph_core.egg-info/requires.txt +0 -0
  24. {metergraph_core-0.7.4 → metergraph_core-0.7.6}/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.4
3
+ Version: 0.7.6
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
@@ -186,6 +186,8 @@ standard `cache_read_per_mtok`, as before. A standard call always uses the
186
186
  standard cache-read rate. A row with only a batch cache rate fully prices a
187
187
  cached batch call, while a cached standard call on it is `partial` with
188
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.
189
191
 
190
192
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
191
193
  output count keeps the subtotal that is known, marks the result `partial`, and
@@ -156,6 +156,8 @@ standard `cache_read_per_mtok`, as before. A standard call always uses the
156
156
  standard cache-read rate. A row with only a batch cache rate fully prices a
157
157
  cached batch call, while a cached standard call on it is `partial` with
158
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.
159
161
 
160
162
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
161
163
  output count keeps the subtotal that is known, marks the result `partial`, and
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "metergraph-core"
3
- version = "0.7.4"
3
+ version = "0.7.6"
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"
@@ -229,18 +229,20 @@ class Price:
229
229
  # one. Absent, batch cache reads keep the standard cache-read rate; it is
230
230
  # never inferred from another rate.
231
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
232
234
 
233
235
  def __post_init__(self) -> None:
234
236
  # The loader checks the rate it reads, but a row built or replaced
235
237
  # 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
- )
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
+ )
244
246
 
245
247
 
246
248
  @dataclass(frozen=True, slots=True)
@@ -370,6 +372,7 @@ def _billed_terms(resolved: ResolvedPrice) -> tuple[Any, ...]:
370
372
  price.batch_input_per_mtok,
371
373
  price.batch_output_per_mtok,
372
374
  price.batch_cache_read_per_mtok,
375
+ price.batch_cache_write_5m_per_mtok,
373
376
  price.request_fee_per_1k_usd,
374
377
  resolved.rules,
375
378
  )
@@ -523,6 +526,7 @@ def _price_tokens(
523
526
  input_rate = price.input_per_mtok
524
527
  output_rate = price.output_per_mtok
525
528
  cache_read_rate = price.cache_read_per_mtok
529
+ cache_write_5m_rate = price.cache_write_5m_per_mtok
526
530
  if batch:
527
531
  if price.batch_input_per_mtok is None or price.batch_output_per_mtok is None:
528
532
  reasons.append("batch_rate_unavailable")
@@ -533,6 +537,8 @@ def _price_tokens(
533
537
  # the standard rate, as it always has.
534
538
  if price.batch_cache_read_per_mtok is not None:
535
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
536
542
 
537
543
  input_multiplier = Decimal("1")
538
544
  output_multiplier = Decimal("1")
@@ -579,12 +585,12 @@ def _price_tokens(
579
585
  / _MILLION
580
586
  )
581
587
  if cache_write_5m_count:
582
- if price.cache_write_5m_per_mtok is None:
588
+ if cache_write_5m_rate is None:
583
589
  reasons.append("cache_write_5m_rate_unavailable")
584
590
  else:
585
591
  cache_write_usd += (
586
592
  Decimal(cache_write_5m_count)
587
- * price.cache_write_5m_per_mtok
593
+ * cache_write_5m_rate
588
594
  * input_multiplier
589
595
  / _MILLION
590
596
  )
@@ -46,6 +46,7 @@ RATE_FIELDS = (
46
46
  "batch_input_per_mtok",
47
47
  "batch_output_per_mtok",
48
48
  "batch_cache_read_per_mtok",
49
+ "batch_cache_write_5m_per_mtok",
49
50
  "request_fee_per_1k_usd",
50
51
  )
51
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-25"
9
+ version: "2026-10-27"
10
10
  currency: USD
11
- pricing_verified_at: "2026-10-25"
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:
@@ -3164,6 +3188,40 @@ models:
3164
3188
  rules:
3165
3189
  varies_by_provider: true
3166
3190
  source_url: https://openrouter.ai/api/v1/models
3191
+ - canonical_id: moonshotai/kimi-k3.1-pro
3192
+ publisher: moonshotai
3193
+ aliases:
3194
+ - {provider: moonshotai, alias: kimi-k3.1-pro, channel: moonshot-api}
3195
+ - {provider: unknown, alias: kimi-k3.1-pro, channel: moonshot-api}
3196
+ - {provider: litellm, alias: kimi-k3.1-pro, channel: moonshot-api}
3197
+ prices:
3198
+ - channel: moonshot-api
3199
+ region: global
3200
+ effective_from: "2026-10-24"
3201
+ input_per_mtok: 2.00
3202
+ output_per_mtok: 10.00
3203
+ cache_read_per_mtok: 0.20
3204
+ cache_write_5m_per_mtok: 2.00
3205
+ rules: {input_includes_cache_read: true, input_includes_cache_write: true}
3206
+ source_url: https://platform.kimi.ai/
3207
+
3208
+ - canonical_id: moonshotai/kimi-k3.1-pro-highspeed
3209
+ publisher: moonshotai
3210
+ aliases:
3211
+ - {provider: moonshotai, alias: kimi-k3.1-pro-highspeed, channel: moonshot-api}
3212
+ - {provider: unknown, alias: kimi-k3.1-pro-highspeed, channel: moonshot-api}
3213
+ - {provider: litellm, alias: kimi-k3.1-pro-highspeed, channel: moonshot-api}
3214
+ prices:
3215
+ - channel: moonshot-api
3216
+ region: global
3217
+ effective_from: "2026-10-24"
3218
+ input_per_mtok: 4.00
3219
+ output_per_mtok: 20.00
3220
+ cache_read_per_mtok: 0.20
3221
+ cache_write_5m_per_mtok: 4.00
3222
+ rules: {input_includes_cache_read: true, input_includes_cache_write: true}
3223
+ source_url: https://platform.kimi.ai/
3224
+
3167
3225
  - canonical_id: moonshotai/kimi-k3
3168
3226
  publisher: moonshotai
3169
3227
  aliases:
@@ -152,6 +152,16 @@ sources:
152
152
  fingerprint: null
153
153
  low_risk: {eligible: false, dimensions: []}
154
154
 
155
+ - id: moonshot-api-pricing
156
+ provider: moonshotai
157
+ channel: moonshot-api
158
+ catalog_channel: true
159
+ url: https://platform.kimi.ai/
160
+ parser: html-price-prose
161
+ dimensions: [input_per_mtok, output_per_mtok, cache_read_per_mtok, cache_write_5m_per_mtok]
162
+ fingerprint: null
163
+ low_risk: {eligible: false, dimensions: []}
164
+
155
165
  # Gemini and partner models on Vertex AI. Prices vary by model, context
156
166
  # length, tier and processing mode; reviewed discovery only. The URL is the
157
167
  # current destination of Google's Vertex AI generative pricing page
@@ -7,8 +7,8 @@ the parser id a source registry entry names.
7
7
  Official parsers (OpenAI, Anthropic, Google, DeepSeek, Mistral, xAI and
8
8
  Perplexity) turn a page into ``{model name: {field: Decimal}}`` tables keyed
9
9
  by the page's own model spelling; :func:`extract` maps those to catalog
10
- price columns. The two generic discovery parsers, ``html-tables`` and
11
- ``gpu-license-prose``, record normalized evidence from any page for a
10
+ price columns. The generic discovery parsers, ``html-tables``,
11
+ ``html-price-prose``, and ``gpu-license-prose`` record normalized evidence for a
12
12
  reviewer and never produce applicable price rows; the license parser's
13
13
  evidence is GPU or license pricing, never token rates.
14
14
 
@@ -886,6 +886,23 @@ def _table_features(content: str) -> Any:
886
886
  return {"headers": sorted(list(row) for row in headers)}
887
887
 
888
888
 
889
+ def _price_prose_evidence(content: str) -> dict[str, Any]:
890
+ text = page_text(content)
891
+ mentions = []
892
+ amounts = []
893
+ for match in _AMOUNT.finditer(text):
894
+ amounts.append(match.group()[1:].replace(",", ""))
895
+ excerpt = text[max(0, match.start() - 300):match.end() + 100].strip(" |")
896
+ if excerpt not in mentions:
897
+ mentions.append(excerpt)
898
+ return {"kind": "price_mentions", "mentions": mentions[:100], "amounts": amounts[:100]}
899
+
900
+
901
+ def _price_prose_features(content: str) -> Any:
902
+ text = _AMOUNT.sub("$#", page_text(content))
903
+ return {"has_prices": "$#" in text, "has_model": "model" in text.lower()}
904
+
905
+
889
906
  _LICENSE_CANDIDATES = {
890
907
  "license_per_gpu_year_usd": re.compile(r"\$([0-9][0-9,]*(?:\.[0-9]+)?) per GPU per year"),
891
908
  "gpu_hour_usd": re.compile(r"\$([0-9][0-9,]*(?:\.[0-9]+)?) per GPU per hour"),
@@ -964,6 +981,12 @@ def redact_evidence(evidence: Mapping[str, Any]) -> dict[str, Any]:
964
981
  "statement_count": len(evidence.get("statements") or []),
965
982
  "candidates": dict(evidence.get("candidates") or {}),
966
983
  }
984
+ if kind == "price_mentions":
985
+ return {
986
+ "kind": kind,
987
+ "redacted": True,
988
+ "amounts": list(evidence.get("amounts") or []),
989
+ }
967
990
  return {"kind": kind, "redacted": True}
968
991
 
969
992
 
@@ -1006,6 +1029,7 @@ PARSERS: Mapping[str, ParserSpec] = MappingProxyType(
1006
1029
  # Generic discovery parsers: any page's tables, or GPU and license
1007
1030
  # pricing stated in prose. They describe no provider's layout.
1008
1031
  "html-tables": ParserSpec(False, _table_features, evidence=_table_evidence),
1032
+ "html-price-prose": ParserSpec(False, _price_prose_features, evidence=_price_prose_evidence),
1009
1033
  "gpu-license-prose": ParserSpec(False, _license_features, evidence=_license_evidence),
1010
1034
  }
1011
1035
  )
@@ -399,6 +399,15 @@ def parse_catalog(
399
399
  f"{canonical}: invalid batch_cache_read_per_mtok "
400
400
  f"{batch_cache_read!r}"
401
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
+ )
402
411
  prices.append(
403
412
  Price(
404
413
  id=f"{canonical}:{channel}:{region}:{_id_date(effective_from)}",
@@ -424,6 +433,7 @@ def parse_catalog(
424
433
  publisher=publisher,
425
434
  currency=currency,
426
435
  batch_cache_read_per_mtok=batch_cache_read_per_mtok,
436
+ batch_cache_write_5m_per_mtok=batch_cache_write_5m_per_mtok,
427
437
  )
428
438
  )
429
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.4
3
+ Version: 0.7.6
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
@@ -186,6 +186,8 @@ standard `cache_read_per_mtok`, as before. A standard call always uses the
186
186
  standard cache-read rate. A row with only a batch cache rate fully prices a
187
187
  cached batch call, while a cached standard call on it is `partial` with
188
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.
189
191
 
190
192
  A `ModelUsage` count left `None` is unknown, not zero. A missing input or
191
193
  output count keeps the subtotal that is known, marks the result `partial`, and