metergraph-core 0.7.0__tar.gz → 0.7.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.
Files changed (24) hide show
  1. {metergraph_core-0.7.0/src/metergraph_core.egg-info → metergraph_core-0.7.1}/PKG-INFO +8 -3
  2. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/README.md +7 -2
  3. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/pyproject.toml +1 -1
  4. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/__init__.py +3 -1
  5. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/responses.py +50 -18
  6. {metergraph_core-0.7.0 → metergraph_core-0.7.1/src/metergraph_core.egg-info}/PKG-INFO +8 -3
  7. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/MANIFEST.in +0 -0
  8. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/setup.cfg +0 -0
  9. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/billing.py +0 -0
  10. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/catalog.py +0 -0
  11. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/catalog_delta.py +0 -0
  12. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/data/models.yaml +0 -0
  13. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/data/prices.yaml +0 -0
  14. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/data/sources.yaml +0 -0
  15. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/extractors.py +0 -0
  16. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/loader.py +0 -0
  17. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/models.py +0 -0
  18. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/refresh.py +0 -0
  19. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/retrieval.py +0 -0
  20. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core/sources.py +0 -0
  21. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core.egg-info/SOURCES.txt +0 -0
  22. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core.egg-info/dependency_links.txt +0 -0
  23. {metergraph_core-0.7.0 → metergraph_core-0.7.1}/src/metergraph_core.egg-info/requires.txt +0 -0
  24. {metergraph_core-0.7.0 → metergraph_core-0.7.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.7.0
3
+ Version: 0.7.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
@@ -225,7 +225,9 @@ It accepts the object a provider SDK returned or the same response as JSON:
225
225
  | Bedrock Converse (pass `model=`, the response does not name one) | `bedrock` |
226
226
 
227
227
  `provider` names who served the call and selects its direct channel unless
228
- `channel` is given. The response shape is detected from its fields. Only the
228
+ `channel` is given. The response format is detected from its fields; pass
229
+ `format=`, one of `RESPONSE_FORMATS`, for a response too sparse to detect.
230
+ `response_format(response)` returns the detected format, or `None`. Only the
229
231
  model, usage counts, service tier and creation time are read; prompt and
230
232
  output content never are.
231
233
 
@@ -240,7 +242,8 @@ The price in effect when the response was created applies, from `created`,
240
242
  creation time is priced now. `catalog=` costs against a catalog you loaded;
241
243
  the default is the bundled one.
242
244
 
243
- A batch service tier bills at batch rates. Any other tier besides standard,
245
+ A batch service tier bills at batch rates. Pass `service_tier=` when the
246
+ response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
244
247
  such as `priority` or `flex`, returns `partial` with `service_tier` in
245
248
  `missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
246
249
  call from its final response or final usage chunk. Fees outside token usage,
@@ -508,6 +511,7 @@ from metergraph_core import (
508
511
  PriceResult,
509
512
  PriceRowKey,
510
513
  PriceSource,
514
+ RESPONSE_FORMATS,
511
515
  RefreshResult,
512
516
  ResolvedPrice,
513
517
  SourceRegistry,
@@ -524,6 +528,7 @@ from metergraph_core import (
524
528
  parse_catalog_delta,
525
529
  plan_low_risk_refresh,
526
530
  resolve_billing,
531
+ response_format,
527
532
  structural_fingerprint,
528
533
  validate_model_registry,
529
534
  validate_source_registry,
@@ -195,7 +195,9 @@ It accepts the object a provider SDK returned or the same response as JSON:
195
195
  | Bedrock Converse (pass `model=`, the response does not name one) | `bedrock` |
196
196
 
197
197
  `provider` names who served the call and selects its direct channel unless
198
- `channel` is given. The response shape is detected from its fields. Only the
198
+ `channel` is given. The response format is detected from its fields; pass
199
+ `format=`, one of `RESPONSE_FORMATS`, for a response too sparse to detect.
200
+ `response_format(response)` returns the detected format, or `None`. Only the
199
201
  model, usage counts, service tier and creation time are read; prompt and
200
202
  output content never are.
201
203
 
@@ -210,7 +212,8 @@ The price in effect when the response was created applies, from `created`,
210
212
  creation time is priced now. `catalog=` costs against a catalog you loaded;
211
213
  the default is the bundled one.
212
214
 
213
- A batch service tier bills at batch rates. Any other tier besides standard,
215
+ A batch service tier bills at batch rates. Pass `service_tier=` when the
216
+ response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
214
217
  such as `priority` or `flex`, returns `partial` with `service_tier` in
215
218
  `missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
216
219
  call from its final response or final usage chunk. Fees outside token usage,
@@ -478,6 +481,7 @@ from metergraph_core import (
478
481
  PriceResult,
479
482
  PriceRowKey,
480
483
  PriceSource,
484
+ RESPONSE_FORMATS,
481
485
  RefreshResult,
482
486
  ResolvedPrice,
483
487
  SourceRegistry,
@@ -494,6 +498,7 @@ from metergraph_core import (
494
498
  parse_catalog_delta,
495
499
  plan_low_risk_refresh,
496
500
  resolve_billing,
501
+ response_format,
497
502
  structural_fingerprint,
498
503
  validate_model_registry,
499
504
  validate_source_registry,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "metergraph-core"
3
- version = "0.7.0"
3
+ version = "0.7.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"
@@ -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",
@@ -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
- def _reader(response: Mapping[str, Any]) -> Callable[[Mapping[str, Any]], _Facts] | None:
192
- """The reader for this response's shape, told apart by the fields each
193
- API always returns."""
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 _gemini
207
+ return "gemini.generate_content"
196
208
  usage = _object(response.get("usage"))
197
209
  if "prompt_tokens" in usage:
198
- return _openai_chat_completions
210
+ return "openai.chat_completions"
199
211
  if "inputTokens" in usage:
200
- return _bedrock_converse
212
+ return "bedrock.converse"
201
213
  if response.get("type") == "message" or "cache_read_input_tokens" in usage:
202
- return _anthropic_messages
214
+ return "anthropic.messages"
203
215
  if response.get("object") == "response" or "input_tokens_details" in usage:
204
- return _openai_responses
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. A service
287
- tier other than standard or batch turns the result ``partial`` with
288
- ``service_tier`` missing, since the catalog does not carry tier rates.
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
- reader = _reader(data)
292
- if reader is None:
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 = reader(data)
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, None, None, "unpriced", ("missing_model",), missing_dimensions=("model",)
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.0
3
+ Version: 0.7.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
@@ -225,7 +225,9 @@ It accepts the object a provider SDK returned or the same response as JSON:
225
225
  | Bedrock Converse (pass `model=`, the response does not name one) | `bedrock` |
226
226
 
227
227
  `provider` names who served the call and selects its direct channel unless
228
- `channel` is given. The response shape is detected from its fields. Only the
228
+ `channel` is given. The response format is detected from its fields; pass
229
+ `format=`, one of `RESPONSE_FORMATS`, for a response too sparse to detect.
230
+ `response_format(response)` returns the detected format, or `None`. Only the
229
231
  model, usage counts, service tier and creation time are read; prompt and
230
232
  output content never are.
231
233
 
@@ -240,7 +242,8 @@ The price in effect when the response was created applies, from `created`,
240
242
  creation time is priced now. `catalog=` costs against a catalog you loaded;
241
243
  the default is the bundled one.
242
244
 
243
- A batch service tier bills at batch rates. Any other tier besides standard,
245
+ A batch service tier bills at batch rates. Pass `service_tier=` when the
246
+ response does not state its tier, such as an OpenAI batch output line. Any other tier besides standard,
244
247
  such as `priority` or `flex`, returns `partial` with `service_tier` in
245
248
  `missing_dimensions`, since the catalog carries no tier rates. Cost a streamed
246
249
  call from its final response or final usage chunk. Fees outside token usage,
@@ -508,6 +511,7 @@ from metergraph_core import (
508
511
  PriceResult,
509
512
  PriceRowKey,
510
513
  PriceSource,
514
+ RESPONSE_FORMATS,
511
515
  RefreshResult,
512
516
  ResolvedPrice,
513
517
  SourceRegistry,
@@ -524,6 +528,7 @@ from metergraph_core import (
524
528
  parse_catalog_delta,
525
529
  plan_low_risk_refresh,
526
530
  resolve_billing,
531
+ response_format,
527
532
  structural_fingerprint,
528
533
  validate_model_registry,
529
534
  validate_source_registry,