metergraph-core 0.7.0__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.
- {metergraph_core-0.7.0/src/metergraph_core.egg-info → metergraph_core-0.7.4}/PKG-INFO +17 -3
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/README.md +16 -2
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/pyproject.toml +1 -1
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/__init__.py +3 -1
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/catalog.py +24 -2
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/catalog_delta.py +1 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/data/prices.yaml +29 -6
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/loader.py +12 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/responses.py +50 -18
- {metergraph_core-0.7.0 → metergraph_core-0.7.4/src/metergraph_core.egg-info}/PKG-INFO +17 -3
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/MANIFEST.in +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/setup.cfg +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/billing.py +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/data/models.yaml +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/data/sources.yaml +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/extractors.py +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/models.py +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/refresh.py +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/retrieval.py +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core/sources.py +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core.egg-info/SOURCES.txt +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core.egg-info/dependency_links.txt +0 -0
- {metergraph_core-0.7.0 → metergraph_core-0.7.4}/src/metergraph_core.egg-info/requires.txt +0 -0
- {metergraph_core-0.7.0 → 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.
|
|
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`.
|
|
@@ -225,7 +233,9 @@ It accepts the object a provider SDK returned or the same response as JSON:
|
|
|
225
233
|
| Bedrock Converse (pass `model=`, the response does not name one) | `bedrock` |
|
|
226
234
|
|
|
227
235
|
`provider` names who served the call and selects its direct channel unless
|
|
228
|
-
`channel` is given. The response
|
|
236
|
+
`channel` is given. The response format is detected from its fields; pass
|
|
237
|
+
`format=`, one of `RESPONSE_FORMATS`, for a response too sparse to detect.
|
|
238
|
+
`response_format(response)` returns the detected format, or `None`. Only the
|
|
229
239
|
model, usage counts, service tier and creation time are read; prompt and
|
|
230
240
|
output content never are.
|
|
231
241
|
|
|
@@ -240,7 +250,9 @@ The price in effect when the response was created applies, from `created`,
|
|
|
240
250
|
creation time is priced now. `catalog=` costs against a catalog you loaded;
|
|
241
251
|
the default is the bundled one.
|
|
242
252
|
|
|
243
|
-
A batch service tier bills at batch rates
|
|
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
|
|
255
|
+
response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
|
|
244
256
|
such as `priority` or `flex`, returns `partial` with `service_tier` in
|
|
245
257
|
`missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
|
|
246
258
|
call from its final response or final usage chunk. Fees outside token usage,
|
|
@@ -508,6 +520,7 @@ from metergraph_core import (
|
|
|
508
520
|
PriceResult,
|
|
509
521
|
PriceRowKey,
|
|
510
522
|
PriceSource,
|
|
523
|
+
RESPONSE_FORMATS,
|
|
511
524
|
RefreshResult,
|
|
512
525
|
ResolvedPrice,
|
|
513
526
|
SourceRegistry,
|
|
@@ -524,6 +537,7 @@ from metergraph_core import (
|
|
|
524
537
|
parse_catalog_delta,
|
|
525
538
|
plan_low_risk_refresh,
|
|
526
539
|
resolve_billing,
|
|
540
|
+
response_format,
|
|
527
541
|
structural_fingerprint,
|
|
528
542
|
validate_model_registry,
|
|
529
543
|
validate_source_registry,
|
|
@@ -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`.
|
|
@@ -195,7 +203,9 @@ It accepts the object a provider SDK returned or the same response as JSON:
|
|
|
195
203
|
| Bedrock Converse (pass `model=`, the response does not name one) | `bedrock` |
|
|
196
204
|
|
|
197
205
|
`provider` names who served the call and selects its direct channel unless
|
|
198
|
-
`channel` is given. The response
|
|
206
|
+
`channel` is given. The response format is detected from its fields; pass
|
|
207
|
+
`format=`, one of `RESPONSE_FORMATS`, for a response too sparse to detect.
|
|
208
|
+
`response_format(response)` returns the detected format, or `None`. Only the
|
|
199
209
|
model, usage counts, service tier and creation time are read; prompt and
|
|
200
210
|
output content never are.
|
|
201
211
|
|
|
@@ -210,7 +220,9 @@ The price in effect when the response was created applies, from `created`,
|
|
|
210
220
|
creation time is priced now. `catalog=` costs against a catalog you loaded;
|
|
211
221
|
the default is the bundled one.
|
|
212
222
|
|
|
213
|
-
A batch service tier bills at batch rates
|
|
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
|
|
225
|
+
response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
|
|
214
226
|
such as `priority` or `flex`, returns `partial` with `service_tier` in
|
|
215
227
|
`missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
|
|
216
228
|
call from its final response or final usage chunk. Fees outside token usage,
|
|
@@ -478,6 +490,7 @@ from metergraph_core import (
|
|
|
478
490
|
PriceResult,
|
|
479
491
|
PriceRowKey,
|
|
480
492
|
PriceSource,
|
|
493
|
+
RESPONSE_FORMATS,
|
|
481
494
|
RefreshResult,
|
|
482
495
|
ResolvedPrice,
|
|
483
496
|
SourceRegistry,
|
|
@@ -494,6 +507,7 @@ from metergraph_core import (
|
|
|
494
507
|
parse_catalog_delta,
|
|
495
508
|
plan_low_risk_refresh,
|
|
496
509
|
resolve_billing,
|
|
510
|
+
response_format,
|
|
497
511
|
structural_fingerprint,
|
|
498
512
|
validate_model_registry,
|
|
499
513
|
validate_source_registry,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "metergraph-core"
|
|
3
|
-
version = "0.7.
|
|
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"
|
|
@@ -59,7 +59,7 @@ from .catalog_delta import (
|
|
|
59
59
|
apply_catalog_delta,
|
|
60
60
|
parse_catalog_delta,
|
|
61
61
|
)
|
|
62
|
-
from .responses import cost_from_response
|
|
62
|
+
from .responses import RESPONSE_FORMATS, cost_from_response, response_format
|
|
63
63
|
from .extractors import (
|
|
64
64
|
ExtractedRate,
|
|
65
65
|
Extraction,
|
|
@@ -135,6 +135,8 @@ __all__ = [
|
|
|
135
135
|
"count_fetch_units",
|
|
136
136
|
"count_search_units",
|
|
137
137
|
"cost_from_response",
|
|
138
|
+
"response_format",
|
|
139
|
+
"RESPONSE_FORMATS",
|
|
138
140
|
"counts_cache_read_in_input",
|
|
139
141
|
"direct_channel_for_provider",
|
|
140
142
|
"normalize_provider",
|
|
@@ -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
|
|
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
|
-
*
|
|
577
|
+
* cache_read_rate
|
|
556
578
|
* input_multiplier
|
|
557
579
|
/ _MILLION
|
|
558
580
|
)
|
|
@@ -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-
|
|
9
|
+
version: "2026-10-25"
|
|
10
10
|
currency: USD
|
|
11
|
-
pricing_verified_at: "2026-10-
|
|
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-
|
|
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
|
-
|
|
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)
|
|
@@ -12,7 +12,7 @@ from datetime import datetime, timezone
|
|
|
12
12
|
from functools import lru_cache
|
|
13
13
|
from typing import Any, Callable, Mapping
|
|
14
14
|
|
|
15
|
-
from .catalog import CostResult, ModelUsage
|
|
15
|
+
from .catalog import CostResult, ModelUsage, _coerce_datetime
|
|
16
16
|
from .loader import LoadedCatalog, load_catalog
|
|
17
17
|
|
|
18
18
|
_STANDARD_TIERS = frozenset({"default", "standard", "standard_only", "auto", "on_demand"})
|
|
@@ -188,23 +188,41 @@ def _bedrock_converse(response: Mapping[str, Any]) -> _Facts:
|
|
|
188
188
|
)
|
|
189
189
|
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
""
|
|
193
|
-
|
|
191
|
+
_READERS: dict[str, Callable[[Mapping[str, Any]], _Facts]] = {
|
|
192
|
+
"openai.chat_completions": _openai_chat_completions,
|
|
193
|
+
"openai.responses": _openai_responses,
|
|
194
|
+
"anthropic.messages": _anthropic_messages,
|
|
195
|
+
"gemini.generate_content": _gemini,
|
|
196
|
+
"bedrock.converse": _bedrock_converse,
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# The response formats :func:`cost_from_response` reads.
|
|
200
|
+
RESPONSE_FORMATS = tuple(_READERS)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _detect(response: Mapping[str, Any]) -> str | None:
|
|
204
|
+
"""The format of this response, told apart by the fields each API
|
|
205
|
+
always returns."""
|
|
194
206
|
if _first(response, "usage_metadata", "usageMetadata") is not None:
|
|
195
|
-
return
|
|
207
|
+
return "gemini.generate_content"
|
|
196
208
|
usage = _object(response.get("usage"))
|
|
197
209
|
if "prompt_tokens" in usage:
|
|
198
|
-
return
|
|
210
|
+
return "openai.chat_completions"
|
|
199
211
|
if "inputTokens" in usage:
|
|
200
|
-
return
|
|
212
|
+
return "bedrock.converse"
|
|
201
213
|
if response.get("type") == "message" or "cache_read_input_tokens" in usage:
|
|
202
|
-
return
|
|
214
|
+
return "anthropic.messages"
|
|
203
215
|
if response.get("object") == "response" or "input_tokens_details" in usage:
|
|
204
|
-
return
|
|
216
|
+
return "openai.responses"
|
|
205
217
|
return None
|
|
206
218
|
|
|
207
219
|
|
|
220
|
+
def response_format(response: Any) -> str | None:
|
|
221
|
+
"""The :data:`RESPONSE_FORMATS` entry ``response`` is in, or None when its
|
|
222
|
+
shape is not recognized."""
|
|
223
|
+
return _detect(_as_mapping(response))
|
|
224
|
+
|
|
225
|
+
|
|
208
226
|
def _as_mapping(response: Any) -> Mapping[str, Any]:
|
|
209
227
|
if isinstance(response, Mapping):
|
|
210
228
|
return response
|
|
@@ -270,6 +288,8 @@ def cost_from_response(
|
|
|
270
288
|
at: Any = None,
|
|
271
289
|
channel: str | None = None,
|
|
272
290
|
region: str | None = None,
|
|
291
|
+
service_tier: str | None = None,
|
|
292
|
+
format: str | None = None,
|
|
273
293
|
) -> CostResult:
|
|
274
294
|
"""The cost of one provider response, from the usage it reports.
|
|
275
295
|
|
|
@@ -280,24 +300,36 @@ def cost_from_response(
|
|
|
280
300
|
is given. ``model`` overrides the response's model id and is required for
|
|
281
301
|
Bedrock Converse, whose responses do not name one. ``at`` defaults to the
|
|
282
302
|
response's creation time when it reports one, else now. ``catalog``
|
|
283
|
-
defaults to the catalog bundled with this package.
|
|
303
|
+
defaults to the catalog bundled with this package. ``format``, one of
|
|
304
|
+
:data:`RESPONSE_FORMATS`, names the response's format; without it the
|
|
305
|
+
format is detected from the response's fields.
|
|
284
306
|
|
|
285
307
|
A streamed call is costed from its final response or final usage chunk.
|
|
286
|
-
Fees outside token usage, such as web search, are not included.
|
|
287
|
-
|
|
288
|
-
|
|
308
|
+
Fees outside token usage, such as web search, are not included.
|
|
309
|
+
``service_tier`` overrides the tier the response reports, for responses
|
|
310
|
+
that do not state it, such as an OpenAI batch output line. A tier other
|
|
311
|
+
than standard or batch turns the result ``partial`` with ``service_tier``
|
|
312
|
+
missing, since the catalog does not carry tier rates.
|
|
289
313
|
"""
|
|
290
314
|
data = _as_mapping(response)
|
|
291
|
-
|
|
292
|
-
|
|
315
|
+
if format is not None and format not in _READERS:
|
|
316
|
+
raise ValueError(f"format must be one of {', '.join(RESPONSE_FORMATS)}")
|
|
317
|
+
detected = format or _detect(data)
|
|
318
|
+
if detected is None:
|
|
293
319
|
raise ValueError("response carries no usage this function can read")
|
|
294
|
-
facts =
|
|
320
|
+
facts = _READERS[detected](data)
|
|
295
321
|
when = at if at is not None else facts.at or datetime.now(timezone.utc)
|
|
296
322
|
model_id = _text(model) or facts.model
|
|
297
|
-
tier = (facts.service_tier or "").lower() or None
|
|
323
|
+
tier = (_text(service_tier) or facts.service_tier or "").lower() or None
|
|
298
324
|
if model_id is None:
|
|
299
325
|
return CostResult(
|
|
300
|
-
None,
|
|
326
|
+
None,
|
|
327
|
+
None,
|
|
328
|
+
None,
|
|
329
|
+
"unpriced",
|
|
330
|
+
("missing_model",),
|
|
331
|
+
effective_at=_coerce_datetime(when),
|
|
332
|
+
missing_dimensions=("model",),
|
|
301
333
|
)
|
|
302
334
|
loaded = catalog if catalog is not None else _bundled_catalog()
|
|
303
335
|
batch = tier == _BATCH_TIER
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metergraph-core
|
|
3
|
-
Version: 0.7.
|
|
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`.
|
|
@@ -225,7 +233,9 @@ It accepts the object a provider SDK returned or the same response as JSON:
|
|
|
225
233
|
| Bedrock Converse (pass `model=`, the response does not name one) | `bedrock` |
|
|
226
234
|
|
|
227
235
|
`provider` names who served the call and selects its direct channel unless
|
|
228
|
-
`channel` is given. The response
|
|
236
|
+
`channel` is given. The response format is detected from its fields; pass
|
|
237
|
+
`format=`, one of `RESPONSE_FORMATS`, for a response too sparse to detect.
|
|
238
|
+
`response_format(response)` returns the detected format, or `None`. Only the
|
|
229
239
|
model, usage counts, service tier and creation time are read; prompt and
|
|
230
240
|
output content never are.
|
|
231
241
|
|
|
@@ -240,7 +250,9 @@ The price in effect when the response was created applies, from `created`,
|
|
|
240
250
|
creation time is priced now. `catalog=` costs against a catalog you loaded;
|
|
241
251
|
the default is the bundled one.
|
|
242
252
|
|
|
243
|
-
A batch service tier bills at batch rates
|
|
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
|
|
255
|
+
response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
|
|
244
256
|
such as `priority` or `flex`, returns `partial` with `service_tier` in
|
|
245
257
|
`missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
|
|
246
258
|
call from its final response or final usage chunk. Fees outside token usage,
|
|
@@ -508,6 +520,7 @@ from metergraph_core import (
|
|
|
508
520
|
PriceResult,
|
|
509
521
|
PriceRowKey,
|
|
510
522
|
PriceSource,
|
|
523
|
+
RESPONSE_FORMATS,
|
|
511
524
|
RefreshResult,
|
|
512
525
|
ResolvedPrice,
|
|
513
526
|
SourceRegistry,
|
|
@@ -524,6 +537,7 @@ from metergraph_core import (
|
|
|
524
537
|
parse_catalog_delta,
|
|
525
538
|
plan_low_risk_refresh,
|
|
526
539
|
resolve_billing,
|
|
540
|
+
response_format,
|
|
527
541
|
structural_fingerprint,
|
|
528
542
|
validate_model_registry,
|
|
529
543
|
validate_source_registry,
|
|
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.7.0 → metergraph_core-0.7.4}/src/metergraph_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|