metergraph-core 0.5.2__tar.gz → 0.6.1__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.
- {metergraph_core-0.5.2/src/metergraph_core.egg-info → metergraph_core-0.6.1}/PKG-INFO +7 -1
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/README.md +6 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/pyproject.toml +1 -1
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/catalog.py +39 -8
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/data/prices.yaml +41 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/loader.py +2 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1/src/metergraph_core.egg-info}/PKG-INFO +7 -1
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/MANIFEST.in +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/setup.cfg +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/__init__.py +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/billing.py +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/catalog_delta.py +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/data/models.yaml +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/data/sources.yaml +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/extractors.py +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/models.py +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/refresh.py +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/retrieval.py +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core/sources.py +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core.egg-info/SOURCES.txt +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core.egg-info/dependency_links.txt +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core.egg-info/requires.txt +0 -0
- {metergraph_core-0.5.2 → metergraph_core-0.6.1}/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.
|
|
3
|
+
Version: 0.6.1
|
|
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
|
|
@@ -194,6 +194,12 @@ lists `input_tokens` or `output_tokens` in `missing_dimensions`.
|
|
|
194
194
|
|
|
195
195
|
A region the model is not sold in is `unpriced` with nothing missing.
|
|
196
196
|
|
|
197
|
+
A price closed with `effective_to` and not replaced is the last known price.
|
|
198
|
+
A call after it ended resolves to that price, stays `priced`, and carries
|
|
199
|
+
`price_ended` in `reasons` (on both `PriceResult` and `CostResult`). The
|
|
200
|
+
applied `price.effective_to` says when it ended. A closed price followed by a
|
|
201
|
+
later one never extends, so a gap before a scheduled price stays `unpriced`.
|
|
202
|
+
|
|
197
203
|
## Model registry
|
|
198
204
|
|
|
199
205
|
```python
|
|
@@ -164,6 +164,12 @@ lists `input_tokens` or `output_tokens` in `missing_dimensions`.
|
|
|
164
164
|
|
|
165
165
|
A region the model is not sold in is `unpriced` with nothing missing.
|
|
166
166
|
|
|
167
|
+
A price closed with `effective_to` and not replaced is the last known price.
|
|
168
|
+
A call after it ended resolves to that price, stays `priced`, and carries
|
|
169
|
+
`price_ended` in `reasons` (on both `PriceResult` and `CostResult`). The
|
|
170
|
+
applied `price.effective_to` says when it ended. A closed price followed by a
|
|
171
|
+
later one never extends, so a gap before a scheduled price stays `unpriced`.
|
|
172
|
+
|
|
167
173
|
## Model registry
|
|
168
174
|
|
|
169
175
|
```python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "metergraph-core"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.1"
|
|
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"
|
|
@@ -83,6 +83,8 @@ _BEDROCK_CHANNELS = frozenset({_BEDROCK_CHANNEL, "aws-bedrock-geo"})
|
|
|
83
83
|
_BEDROCK_VERSION_SUFFIX = re.compile(r"-v\d+(?::\d+)?$")
|
|
84
84
|
_BEDROCK_GLOBAL_PREFIX = "global."
|
|
85
85
|
_GLOBAL_REGION = "global"
|
|
86
|
+
# Reason for a result priced at a price that ended and was not replaced.
|
|
87
|
+
PRICE_ENDED = "price_ended"
|
|
86
88
|
|
|
87
89
|
|
|
88
90
|
def _bedrock_fallback_ids(model_key: str) -> tuple[tuple[str, bool], ...]:
|
|
@@ -321,6 +323,8 @@ class PriceResult:
|
|
|
321
323
|
price: Price | None
|
|
322
324
|
effective_at: datetime
|
|
323
325
|
missing_dimensions: tuple[str, ...] = ()
|
|
326
|
+
# Notes that do not make the result partial, such as ``price_ended``.
|
|
327
|
+
reasons: tuple[str, ...] = ()
|
|
324
328
|
|
|
325
329
|
|
|
326
330
|
@dataclass(frozen=True, slots=True)
|
|
@@ -330,6 +334,8 @@ class ResolvedPrice:
|
|
|
330
334
|
canonical_model: str
|
|
331
335
|
price: Price
|
|
332
336
|
rules: Mapping[str, Any]
|
|
337
|
+
# The last known price: it ended before the resolved time, unreplaced.
|
|
338
|
+
ended: bool = False
|
|
333
339
|
|
|
334
340
|
|
|
335
341
|
def _billed_terms(resolved: ResolvedPrice) -> tuple[Any, ...]:
|
|
@@ -611,6 +617,9 @@ def _cost_result(
|
|
|
611
617
|
batch=usage.batch,
|
|
612
618
|
)
|
|
613
619
|
reasons = [f"missing_{dimension}" for dimension in missing] + reasons
|
|
620
|
+
partial = bool(reasons)
|
|
621
|
+
if resolved.ended:
|
|
622
|
+
reasons = [PRICE_ENDED] + reasons
|
|
614
623
|
missing += tuple(
|
|
615
624
|
dimension
|
|
616
625
|
for dimension in ("input_tokens", "output_tokens")
|
|
@@ -620,7 +629,7 @@ def _cost_result(
|
|
|
620
629
|
resolved.canonical_model,
|
|
621
630
|
resolved.price.id,
|
|
622
631
|
cost,
|
|
623
|
-
"partial" if
|
|
632
|
+
"partial" if partial else "priced",
|
|
624
633
|
tuple(dict.fromkeys(reasons)),
|
|
625
634
|
components=components,
|
|
626
635
|
publisher=resolved.price.publisher,
|
|
@@ -715,18 +724,37 @@ class CatalogSnapshot:
|
|
|
715
724
|
|
|
716
725
|
def _price_for(
|
|
717
726
|
self, alias: Alias, at: datetime, region: str | None = None
|
|
718
|
-
) -> Price | None:
|
|
727
|
+
) -> tuple[Price | None, bool]:
|
|
728
|
+
"""The price in effect at ``at``, and whether it is the last known
|
|
729
|
+
price: when no row covers ``at``, the row that applied most recently,
|
|
730
|
+
provided nothing in these scopes starts at or after its end. A
|
|
731
|
+
superseded price, or a gap before a scheduled one, never extends."""
|
|
719
732
|
at = at if at.tzinfo else at.replace(tzinfo=timezone.utc)
|
|
720
733
|
candidates = self._prices.get((alias.model_id, alias.pricing_channel), [])
|
|
721
|
-
|
|
734
|
+
scopes = self._price_regions(alias, region or self._region)
|
|
735
|
+
for scope in scopes:
|
|
722
736
|
for price in candidates:
|
|
723
737
|
if price.region.lower() != scope:
|
|
724
738
|
continue
|
|
725
739
|
if price.effective_from <= at and (
|
|
726
740
|
price.effective_to is None or at < price.effective_to
|
|
727
741
|
):
|
|
728
|
-
return price
|
|
729
|
-
|
|
742
|
+
return price, False
|
|
743
|
+
in_scopes = [price for price in candidates if price.region.lower() in scopes]
|
|
744
|
+
ended = [
|
|
745
|
+
price for price in in_scopes
|
|
746
|
+
if price.effective_to is not None and price.effective_to <= at
|
|
747
|
+
]
|
|
748
|
+
if not ended:
|
|
749
|
+
return None, False
|
|
750
|
+
# The latest end wins; on a tie, the earlier scope, as above.
|
|
751
|
+
last = max(
|
|
752
|
+
ended,
|
|
753
|
+
key=lambda price: (price.effective_to, -scopes.index(price.region.lower())),
|
|
754
|
+
)
|
|
755
|
+
if any(price.effective_from >= last.effective_to for price in in_scopes):
|
|
756
|
+
return None, False
|
|
757
|
+
return last, True
|
|
730
758
|
|
|
731
759
|
def infer_direct_channel(self, model: Any) -> str | None:
|
|
732
760
|
"""Return the sole direct billing channel known for ``model``.
|
|
@@ -791,13 +819,14 @@ class CatalogSnapshot:
|
|
|
791
819
|
break
|
|
792
820
|
if alias is None:
|
|
793
821
|
return None
|
|
794
|
-
price = self._price_for(alias, when, region)
|
|
822
|
+
price, ended = self._price_for(alias, when, region)
|
|
795
823
|
if price is None:
|
|
796
824
|
return None
|
|
797
825
|
return ResolvedPrice(
|
|
798
826
|
canonical_model=alias.canonical_id,
|
|
799
827
|
price=price,
|
|
800
828
|
rules=MappingProxyType({**price.rules, **alias.rules}),
|
|
829
|
+
ended=ended,
|
|
801
830
|
)
|
|
802
831
|
|
|
803
832
|
def _lookup(
|
|
@@ -876,7 +905,7 @@ class CatalogSnapshot:
|
|
|
876
905
|
None, None, None, "unpriced", ("no_effective_alias",)
|
|
877
906
|
)
|
|
878
907
|
return CostResult(None, None, None, "unpriced", ("unknown_model",))
|
|
879
|
-
price = self._price_for(alias, when)
|
|
908
|
+
price, ended = self._price_for(alias, when)
|
|
880
909
|
if price is None:
|
|
881
910
|
return CostResult(
|
|
882
911
|
alias.canonical_id,
|
|
@@ -886,7 +915,9 @@ class CatalogSnapshot:
|
|
|
886
915
|
("no_effective_price",),
|
|
887
916
|
)
|
|
888
917
|
return _cost_result(
|
|
889
|
-
ResolvedPrice(
|
|
918
|
+
ResolvedPrice(
|
|
919
|
+
alias.canonical_id, price, {**price.rules, **alias.rules}, ended=ended
|
|
920
|
+
),
|
|
890
921
|
at=when,
|
|
891
922
|
usage=ModelUsage(
|
|
892
923
|
input_tokens=input_tokens,
|
|
@@ -1523,6 +1523,7 @@ models:
|
|
|
1523
1523
|
- channel: google-api
|
|
1524
1524
|
region: global
|
|
1525
1525
|
effective_from: "2026-03-09"
|
|
1526
|
+
effective_to: "2026-10-21"
|
|
1526
1527
|
input_per_mtok: 2.00
|
|
1527
1528
|
output_per_mtok: 12.00
|
|
1528
1529
|
cache_read_per_mtok: 0.20
|
|
@@ -1653,12 +1654,33 @@ models:
|
|
|
1653
1654
|
- channel: google-api
|
|
1654
1655
|
region: global
|
|
1655
1656
|
effective_from: "2026-05-19"
|
|
1657
|
+
effective_to: "2026-10-08"
|
|
1656
1658
|
input_per_mtok: 1.50
|
|
1657
1659
|
output_per_mtok: 9.00
|
|
1658
1660
|
cache_read_per_mtok: 0.15
|
|
1659
1661
|
batch_input_per_mtok: 0.75
|
|
1660
1662
|
batch_output_per_mtok: 4.50
|
|
1661
1663
|
source_url: https://ai.google.dev/gemini-api/docs/pricing
|
|
1664
|
+
# Routed to gemini-3.6-flash; mirrors its google-api rows.
|
|
1665
|
+
- channel: google-api
|
|
1666
|
+
region: global
|
|
1667
|
+
effective_from: "2026-10-08"
|
|
1668
|
+
effective_to: "2027-01-01"
|
|
1669
|
+
input_per_mtok: 0.75
|
|
1670
|
+
output_per_mtok: 3.75
|
|
1671
|
+
cache_read_per_mtok: 0.075
|
|
1672
|
+
batch_input_per_mtok: 0.375
|
|
1673
|
+
batch_output_per_mtok: 1.875
|
|
1674
|
+
source_url: https://ai.google.dev/gemini-api/docs/changelog
|
|
1675
|
+
- channel: google-api
|
|
1676
|
+
region: global
|
|
1677
|
+
effective_from: "2027-01-01"
|
|
1678
|
+
input_per_mtok: 1.50
|
|
1679
|
+
output_per_mtok: 7.50
|
|
1680
|
+
cache_read_per_mtok: 0.15
|
|
1681
|
+
batch_input_per_mtok: 0.75
|
|
1682
|
+
batch_output_per_mtok: 3.75
|
|
1683
|
+
source_url: https://ai.google.dev/gemini-api/docs/changelog
|
|
1662
1684
|
- channel: google-vertex-ai
|
|
1663
1685
|
region: global
|
|
1664
1686
|
effective_from: "2026-05-19"
|
|
@@ -1863,6 +1885,7 @@ models:
|
|
|
1863
1885
|
- channel: google-api
|
|
1864
1886
|
region: global
|
|
1865
1887
|
effective_from: "2026-04-02"
|
|
1888
|
+
effective_to: "2026-10-21"
|
|
1866
1889
|
input_per_mtok: 0.00
|
|
1867
1890
|
output_per_mtok: 0.00
|
|
1868
1891
|
source_url: https://ai.google.dev/gemini-api/docs/pricing
|
|
@@ -2531,6 +2554,7 @@ models:
|
|
|
2531
2554
|
- channel: anthropic-api
|
|
2532
2555
|
region: global
|
|
2533
2556
|
effective_from: "2024-03-13"
|
|
2557
|
+
effective_to: "2026-10-21"
|
|
2534
2558
|
input_per_mtok: 0.25
|
|
2535
2559
|
output_per_mtok: 1.25
|
|
2536
2560
|
cache_read_per_mtok: 0.03
|
|
@@ -2816,6 +2840,7 @@ models:
|
|
|
2816
2840
|
- channel: google-api
|
|
2817
2841
|
region: global
|
|
2818
2842
|
effective_from: "2026-04-02"
|
|
2843
|
+
effective_to: "2026-10-21"
|
|
2819
2844
|
input_per_mtok: 0.00
|
|
2820
2845
|
output_per_mtok: 0.00
|
|
2821
2846
|
source_url: https://ai.google.dev/gemini-api/docs/pricing
|
|
@@ -2997,6 +3022,22 @@ models:
|
|
|
2997
3022
|
cache_read_per_mtok: 0.15
|
|
2998
3023
|
source_url: https://vercel.com/ai-gateway/models/muse-spark-1.3
|
|
2999
3024
|
|
|
3025
|
+
- canonical_id: typesafe-ai/jev-1.13.0
|
|
3026
|
+
publisher: typesafe-ai
|
|
3027
|
+
aliases:
|
|
3028
|
+
- {provider: typesafe-ai, alias: typesafe-ai/jev-1.13.0, channel: vercel-ai-gateway}
|
|
3029
|
+
- {provider: vercel, alias: typesafe-ai/jev-1.13.0, channel: vercel-ai-gateway}
|
|
3030
|
+
- {provider: unknown, alias: typesafe/jev-1.13.0, channel: vercel-ai-gateway}
|
|
3031
|
+
- {provider: unknown, alias: jev-1.13.0, channel: vercel-ai-gateway}
|
|
3032
|
+
prices:
|
|
3033
|
+
# Vercel lists an input rate and no output fee for this decision model.
|
|
3034
|
+
- channel: vercel-ai-gateway
|
|
3035
|
+
region: global
|
|
3036
|
+
effective_from: "2026-09-15"
|
|
3037
|
+
input_per_mtok: 0.04
|
|
3038
|
+
output_per_mtok: 0
|
|
3039
|
+
source_url: https://vercel.com/ai-gateway/models/jev-1.13.0
|
|
3040
|
+
|
|
3000
3041
|
- canonical_id: thinkingmachines/inkling
|
|
3001
3042
|
publisher: thinkingmachines
|
|
3002
3043
|
aliases:
|
|
@@ -15,6 +15,7 @@ import yaml
|
|
|
15
15
|
|
|
16
16
|
from .catalog import (
|
|
17
17
|
Alias,
|
|
18
|
+
PRICE_ENDED,
|
|
18
19
|
CatalogSnapshot,
|
|
19
20
|
CostResult,
|
|
20
21
|
ModelUsage,
|
|
@@ -159,6 +160,7 @@ class LoadedCatalog:
|
|
|
159
160
|
resolved.price,
|
|
160
161
|
when,
|
|
161
162
|
missing,
|
|
163
|
+
(PRICE_ENDED,) if resolved.ended else (),
|
|
162
164
|
)
|
|
163
165
|
|
|
164
166
|
def calculate_cost(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metergraph-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
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
|
|
@@ -194,6 +194,12 @@ lists `input_tokens` or `output_tokens` in `missing_dimensions`.
|
|
|
194
194
|
|
|
195
195
|
A region the model is not sold in is `unpriced` with nothing missing.
|
|
196
196
|
|
|
197
|
+
A price closed with `effective_to` and not replaced is the last known price.
|
|
198
|
+
A call after it ended resolves to that price, stays `priced`, and carries
|
|
199
|
+
`price_ended` in `reasons` (on both `PriceResult` and `CostResult`). The
|
|
200
|
+
applied `price.effective_to` says when it ended. A closed price followed by a
|
|
201
|
+
later one never extends, so a gap before a scheduled price stays `unpriced`.
|
|
202
|
+
|
|
197
203
|
## Model registry
|
|
198
204
|
|
|
199
205
|
```python
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{metergraph_core-0.5.2 → metergraph_core-0.6.1}/src/metergraph_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|