metergraph-core 0.2.56__tar.gz → 0.2.58__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 (18) hide show
  1. {metergraph_core-0.2.56/src/metergraph_core.egg-info → metergraph_core-0.2.58}/PKG-INFO +8 -1
  2. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/README.md +7 -0
  3. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/pyproject.toml +1 -1
  4. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core/__init__.py +2 -0
  5. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core/catalog.py +16 -6
  6. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core/data/models.yaml +78 -1
  7. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core/data/prices.yaml +11 -0
  8. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core/loader.py +1 -0
  9. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core/models.py +68 -0
  10. {metergraph_core-0.2.56 → metergraph_core-0.2.58/src/metergraph_core.egg-info}/PKG-INFO +8 -1
  11. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/MANIFEST.in +0 -0
  12. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/setup.cfg +0 -0
  13. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core/billing.py +0 -0
  14. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core/retrieval.py +0 -0
  15. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core.egg-info/SOURCES.txt +0 -0
  16. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core.egg-info/dependency_links.txt +0 -0
  17. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/src/metergraph_core.egg-info/requires.txt +0 -0
  18. {metergraph_core-0.2.56 → metergraph_core-0.2.58}/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.2.56
3
+ Version: 0.2.58
4
4
  Summary: Reusable MeterGraph catalog and deterministic billing engine
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -109,6 +109,13 @@ IDs may intentionally repeat across execution profiles when gateway and
109
109
  Bedrock use different physical routes; each route therefore also has a unique
110
110
  internal `key`.
111
111
 
112
+ A model entry may record `released`, the publisher's general-availability
113
+ date as an ISO date. `models.release_date(canonical_id, load_catalog())`
114
+ returns that date with basis `released`, or, when none is recorded, the
115
+ earliest `effective_from` of the model's catalog prices with basis
116
+ `first_price`, so a consumer can tell a launch date from a pricing date
117
+ without computing the fallback itself.
118
+
112
119
  Use `validate_model_registry(models, load_catalog())` to verify that every
113
120
  route's provider-facing model ID is declared on its exact pricing channel,
114
121
  resolves to its canonical model, and has an active price on the registry
@@ -97,6 +97,13 @@ IDs may intentionally repeat across execution profiles when gateway and
97
97
  Bedrock use different physical routes; each route therefore also has a unique
98
98
  internal `key`.
99
99
 
100
+ A model entry may record `released`, the publisher's general-availability
101
+ date as an ISO date. `models.release_date(canonical_id, load_catalog())`
102
+ returns that date with basis `released`, or, when none is recorded, the
103
+ earliest `effective_from` of the model's catalog prices with basis
104
+ `first_price`, so a consumer can tell a launch date from a pricing date
105
+ without computing the fallback itself.
106
+
100
107
  Use `validate_model_registry(models, load_catalog())` to verify that every
101
108
  route's provider-facing model ID is declared on its exact pricing channel,
102
109
  resolves to its canonical model, and has an active price on the registry
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "metergraph-core"
3
- version = "0.2.56"
3
+ version = "0.2.58"
4
4
  description = "Reusable MeterGraph catalog and deterministic billing engine"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -32,6 +32,7 @@ from .models import (
32
32
  ModelRegistryError,
33
33
  ModelRoute,
34
34
  OfferGroup,
35
+ ReleaseDate,
35
36
  load_model_registry,
36
37
  parse_model_registry,
37
38
  validate_model_registry,
@@ -60,6 +61,7 @@ __all__ = [
60
61
  "ModelRegistryError",
61
62
  "ModelRoute",
62
63
  "OfferGroup",
64
+ "ReleaseDate",
63
65
  "GatewayBillingEvidence",
64
66
  "Price",
65
67
  "ResolvedPrice",
@@ -209,19 +209,24 @@ class Price:
209
209
  # imply one: google-vertex-ai serves Anthropic's models beside Google's,
210
210
  # and the two report cache reads differently.
211
211
  publisher: str | None = None
212
+ # A fixed fee charged once per call on top of tokens, in USD per 1,000
213
+ # calls: a gateway billing a model's built-in search per request.
214
+ request_fee_per_1k_usd: Decimal | None = None
212
215
 
213
216
 
214
217
  @dataclass(frozen=True, slots=True)
215
218
  class CostComponents:
216
- """A priced call's cost by what it paid for. The four parts sum to the
217
- call's ``cost_usd`` exactly: each is quantized and the rounding residue
218
- lands in the input part, so a consumer that stores them can rebuild the
219
- total without a second rounding."""
219
+ """A priced call's cost by what it paid for. The parts sum to the call's
220
+ ``cost_usd`` exactly: each is quantized and the rounding residue lands in
221
+ the input part, so a consumer that stores them can rebuild the total
222
+ without a second rounding. ``request_usd`` is a per-call fee, zero on
223
+ every row that has none."""
220
224
 
221
225
  input_usd: Decimal
222
226
  output_usd: Decimal
223
227
  cache_read_usd: Decimal
224
228
  cache_write_usd: Decimal
229
+ request_usd: Decimal = Decimal("0")
225
230
 
226
231
 
227
232
  @dataclass(frozen=True, slots=True)
@@ -456,15 +461,20 @@ def _price_tokens(
456
461
  )
457
462
  if rules.get("uncaptured_fees"):
458
463
  reasons.append("uncaptured_fees")
464
+ request_usd = (
465
+ Decimal("0")
466
+ if price.request_fee_per_1k_usd is None
467
+ else price.request_fee_per_1k_usd / Decimal(1000)
468
+ )
459
469
 
460
470
  # The total is rounded once, as it always was; the parts are rounded
461
471
  # each and the residue goes to input, so they sum to the total exactly.
462
- cost = (input_usd + output_usd + cache_read_usd + cache_write_usd).quantize(
472
+ cost = (input_usd + output_usd + cache_read_usd + cache_write_usd + request_usd).quantize(
463
473
  _COST_QUANTUM, rounding=ROUND_HALF_UP
464
474
  )
465
475
  parts = [
466
476
  part.quantize(_COST_QUANTUM, rounding=ROUND_HALF_UP)
467
- for part in (input_usd, output_usd, cache_read_usd, cache_write_usd)
477
+ for part in (input_usd, output_usd, cache_read_usd, cache_write_usd, request_usd)
468
478
  ]
469
479
  parts[0] += cost - sum(parts)
470
480
  return cost, reasons, CostComponents(*parts)
@@ -1,8 +1,9 @@
1
- version: '2026-10-07'
1
+ version: '2026-10-09'
2
2
  models:
3
3
  - canonical_id: openai/gpt-5.6-sol
4
4
  display_name: GPT-5.6 Sol
5
5
  publisher: openai
6
+ released: '2026-07-09' # https://x.com/OpenAI/status/2074704958419792299
6
7
  routes:
7
8
  - key: default:openai/gpt-5.6-sol
8
9
  id: openai/gpt-5.6-sol
@@ -29,6 +30,7 @@ models:
29
30
  - canonical_id: openai/gpt-5.6-luna
30
31
  display_name: GPT-5.6 Luna
31
32
  publisher: openai
33
+ released: '2026-07-09' # https://x.com/OpenAI/status/2074704958419792299
32
34
  routes:
33
35
  - key: default:openai/gpt-5.6-luna
34
36
  id: openai/gpt-5.6-luna
@@ -55,6 +57,7 @@ models:
55
57
  - canonical_id: openai/gpt-6-luna
56
58
  display_name: GPT-6 Luna
57
59
  publisher: openai
60
+ released: '2026-09-22' # https://community.openai.com/t/announcing-gpt-6-sol-and-gpt-6-luna-in-the-api-codex-and-chatgpt/1399925
58
61
  routes:
59
62
  - key: default:openai/gpt-6-luna
60
63
  id: openai/gpt-6-luna
@@ -66,6 +69,7 @@ models:
66
69
  - canonical_id: openai/gpt-6-sol
67
70
  display_name: GPT-6 Sol
68
71
  publisher: openai
72
+ released: '2026-09-22' # https://community.openai.com/t/announcing-gpt-6-sol-and-gpt-6-luna-in-the-api-codex-and-chatgpt/1399925
69
73
  routes:
70
74
  - key: default:openai/gpt-6-sol
71
75
  id: openai/gpt-6-sol
@@ -77,6 +81,7 @@ models:
77
81
  - canonical_id: openai/gpt-6-astra
78
82
  display_name: GPT-6 Astra
79
83
  publisher: openai
84
+ released: '2026-09-04' # https://en.wikipedia.org/wiki/GPT-6_Astra
80
85
  routes:
81
86
  - key: default:openai/gpt-6-astra
82
87
  id: openai/gpt-6-astra
@@ -88,6 +93,7 @@ models:
88
93
  - canonical_id: openai/gpt-5-mini
89
94
  display_name: GPT-5 Mini
90
95
  publisher: openai
96
+ released: '2025-08-07' # https://openai.com/index/introducing-gpt-5/
91
97
  routes:
92
98
  - key: default:openai/gpt-5-mini
93
99
  id: openai/gpt-5-mini
@@ -99,6 +105,7 @@ models:
99
105
  - canonical_id: openai/gpt-5.6-terra
100
106
  display_name: GPT-5.6 Terra
101
107
  publisher: openai
108
+ released: '2026-07-09' # https://x.com/OpenAI/status/2074704958419792299
102
109
  routes:
103
110
  - key: default:openai/gpt-5.6-terra
104
111
  id: openai/gpt-5.6-terra
@@ -125,6 +132,7 @@ models:
125
132
  - canonical_id: anthropic/claude-opus-5
126
133
  display_name: Claude Opus 5
127
134
  publisher: anthropic
135
+ released: '2026-07-24' # https://github.com/jqueryscript/anthropic-claude-timeline
128
136
  routes:
129
137
  - key: default:anthropic/claude-opus-5
130
138
  id: anthropic/claude-opus-5
@@ -144,6 +152,7 @@ models:
144
152
  - canonical_id: anthropic/claude-sonnet-5
145
153
  display_name: Claude Sonnet 5
146
154
  publisher: anthropic
155
+ released: '2026-06-30' # https://github.com/jqueryscript/anthropic-claude-timeline
147
156
  routes:
148
157
  - key: default:anthropic/claude-sonnet-5
149
158
  id: anthropic/claude-sonnet-5
@@ -170,6 +179,7 @@ models:
170
179
  - canonical_id: anthropic/claude-fable-5
171
180
  display_name: Claude Fable 5
172
181
  publisher: anthropic
182
+ released: '2026-06-09' # https://github.com/jqueryscript/anthropic-claude-timeline
173
183
  routes:
174
184
  - key: default:anthropic/claude-fable-5
175
185
  id: anthropic/claude-fable-5
@@ -181,6 +191,7 @@ models:
181
191
  - canonical_id: anthropic/claude-fable-5.1
182
192
  display_name: Claude Fable 5.1
183
193
  publisher: anthropic
194
+ released: '2026-09-01' # https://www.anthropic.com/claude/fable
184
195
  routes:
185
196
  - key: default:anthropic/claude-fable-5.1
186
197
  id: anthropic/claude-fable-5.1
@@ -192,6 +203,7 @@ models:
192
203
  - canonical_id: google/gemini-3.1-pro-preview
193
204
  display_name: Gemini 3.1 Pro Preview
194
205
  publisher: google
206
+ released: '2026-02-19' # https://en.wikipedia.org/wiki/Gemini_(language_model)
195
207
  routes:
196
208
  - key: default:google/gemini-3.1-pro-preview
197
209
  id: google/gemini-3.1-pro-preview
@@ -203,6 +215,7 @@ models:
203
215
  - canonical_id: google/gemini-3.6-flash
204
216
  display_name: Gemini 3.6 Flash
205
217
  publisher: google
218
+ released: '2026-07-21' # https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/gemini/3-6-flash
206
219
  routes:
207
220
  - key: default:google/gemini-3.6-flash
208
221
  id: google/gemini-3.6-flash
@@ -214,6 +227,7 @@ models:
214
227
  - canonical_id: google/gemini-3.8-flash
215
228
  display_name: Gemini 3.8 Flash
216
229
  publisher: google
230
+ released: '2026-09-02' # https://emergent.sh/news/google-launches-gemini-3-8-flash-third-flash
217
231
  routes:
218
232
  - key: default:google/gemini-3.8-flash
219
233
  id: google/gemini-3.8-flash
@@ -225,6 +239,7 @@ models:
225
239
  - canonical_id: google/gemini-3.7-flash
226
240
  display_name: Gemini 3.7 Flash
227
241
  publisher: google
242
+ released: '2026-08-13' # https://ai.google.dev/gemini-api/docs/models/gemini-3.7-flash
228
243
  routes:
229
244
  - key: default:google/gemini-3.7-flash
230
245
  id: google/gemini-3.7-flash
@@ -236,6 +251,7 @@ models:
236
251
  - canonical_id: google/gemini-3.5-flash
237
252
  display_name: Gemini 3.5 Flash
238
253
  publisher: google
254
+ released: '2026-05-19' # https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/gemini/3-5-flash
239
255
  routes:
240
256
  - key: default:google/gemini-3.5-flash
241
257
  id: google/gemini-3.5-flash
@@ -247,6 +263,7 @@ models:
247
263
  - canonical_id: google/gemini-3.5-flash-lite
248
264
  display_name: Gemini 3.5 Flash-Lite
249
265
  publisher: google
266
+ released: '2026-07-21' # https://datanorth.ai/news/google-releases-gemini-3-5-flash
250
267
  routes:
251
268
  - key: default:google/gemini-3.5-flash-lite
252
269
  id: google/gemini-3.5-flash-lite
@@ -258,6 +275,7 @@ models:
258
275
  - canonical_id: google/gemini-3.1-flash-lite
259
276
  display_name: Gemini 3.1 Flash-Lite
260
277
  publisher: google
278
+ released: '2026-05-07' # https://cloud.google.com/blog/products/ai-machine-learning/gemini-3-1-flash-lite-is-now-generally-available
261
279
  routes:
262
280
  - key: default:google/gemini-3.1-flash-lite
263
281
  id: google/gemini-3.1-flash-lite
@@ -269,6 +287,7 @@ models:
269
287
  - canonical_id: google/gemma-4-31b-it
270
288
  display_name: Gemma 4 31B
271
289
  publisher: google
290
+ released: '2026-04-02' # https://ai.google.dev/gemma/docs/releases
272
291
  routes:
273
292
  - key: default:google/gemma-4-31b-it
274
293
  id: google/gemma-4-31b-it
@@ -280,6 +299,7 @@ models:
280
299
  - canonical_id: google/gemma-4-26b-a4b-it
281
300
  display_name: Gemma 4 26B
282
301
  publisher: google
302
+ released: '2026-04-02' # https://ai.google.dev/gemma/docs/releases
283
303
  routes:
284
304
  - key: default:google/gemma-4-26b-a4b-it
285
305
  id: google/gemma-4-26b-a4b-it
@@ -291,6 +311,7 @@ models:
291
311
  - canonical_id: moonshotai/kimi-k3
292
312
  display_name: Kimi K3
293
313
  publisher: moonshotai
314
+ released: '2026-07-16' # https://en.wikipedia.org/wiki/Kimi_(AI)
294
315
  routes:
295
316
  - key: default:moonshotai/kimi-k3
296
317
  id: moonshotai/kimi-k3
@@ -302,6 +323,7 @@ models:
302
323
  - canonical_id: moonshotai/kimi-k2.5
303
324
  display_name: Kimi K2.5
304
325
  publisher: moonshotai
326
+ released: '2026-01-27' # https://en.wikipedia.org/wiki/Kimi_(chatbot)
305
327
  routes:
306
328
  - key: default:moonshotai/kimi-k2.5
307
329
  id: moonshotai/kimi-k2.5
@@ -320,6 +342,7 @@ models:
320
342
  - canonical_id: minimax/minimax-m3
321
343
  display_name: MiniMax M3
322
344
  publisher: minimax
345
+ released: '2026-06-01' # https://www.minimax.io/blog/minimax-m3
323
346
  routes:
324
347
  - key: default:minimax/minimax-m3
325
348
  id: minimax/minimax-m3
@@ -331,6 +354,7 @@ models:
331
354
  - canonical_id: deepseek/v4-pro
332
355
  display_name: DeepSeek V4 Pro
333
356
  publisher: deepseek
357
+ released: '2026-08-13' # https://www.yottalabs.ai/post/deepseek-v4-release-date-specs-how-to-access-2026
334
358
  routes:
335
359
  - key: default:deepseek/deepseek-v4-pro
336
360
  id: deepseek/deepseek-v4-pro
@@ -342,6 +366,7 @@ models:
342
366
  - canonical_id: deepseek/v4-flash
343
367
  display_name: DeepSeek V4 Flash
344
368
  publisher: deepseek
369
+ released: '2026-07-31' # https://www.lindy.ai/blog/deepseek-v4-flash
345
370
  routes:
346
371
  - key: default:deepseek/deepseek-v4-flash
347
372
  id: deepseek/deepseek-v4-flash
@@ -353,6 +378,7 @@ models:
353
378
  - canonical_id: xai/grok-4.1-fast-reasoning
354
379
  display_name: Grok 4.1 Fast Reasoning
355
380
  publisher: xai
381
+ released: '2025-11-19' # https://x.ai/news/grok-4-1-fast
356
382
  routes:
357
383
  - key: default:xai/grok-4.1-fast-reasoning
358
384
  id: xai/grok-4.1-fast-reasoning
@@ -364,6 +390,7 @@ models:
364
390
  - canonical_id: xai/grok-4.1-fast-non-reasoning
365
391
  display_name: Grok 4.1 Fast
366
392
  publisher: xai
393
+ released: '2025-11-19' # https://x.ai/news/grok-4-1-fast
367
394
  routes:
368
395
  - key: default:xai/grok-4.1-fast-non-reasoning
369
396
  id: xai/grok-4.1-fast-non-reasoning
@@ -375,6 +402,7 @@ models:
375
402
  - canonical_id: mistral/ministral-14b
376
403
  display_name: Ministral 3 14B
377
404
  publisher: mistral
405
+ released: '2025-12-02' # https://mistral.ai/news/mistral-3
378
406
  routes:
379
407
  - key: default:mistral/ministral-14b
380
408
  id: mistral/ministral-14b
@@ -386,6 +414,7 @@ models:
386
414
  - canonical_id: nvidia/nemotron-3-super-120b-a12b
387
415
  display_name: Nemotron 3 Super
388
416
  publisher: nvidia
417
+ released: '2026-03-11' # https://huggingface.co/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16
389
418
  routes:
390
419
  - key: default:nvidia/nemotron-3-super-120b-a12b
391
420
  id: nvidia/nemotron-3-super-120b-a12b
@@ -397,6 +426,7 @@ models:
397
426
  - canonical_id: zai/glm-5.3-flash
398
427
  display_name: GLM 5.3 Flash
399
428
  publisher: zai
429
+ released: '2026-08-26' # https://emergent.sh/news/glm-5-3-flash-officially-launched
400
430
  routes:
401
431
  - key: default:zai/glm-5.3-flash
402
432
  id: zai/glm-5.3-flash
@@ -408,6 +438,7 @@ models:
408
438
  - canonical_id: deepseek/deepseek-v4.1-flash
409
439
  display_name: DeepSeek V4.1 Flash
410
440
  publisher: deepseek
441
+ released: '2026-09-10' # https://llm-stats.com/models/deepseek-v4.1-flash
411
442
  routes:
412
443
  - key: default:deepseek/deepseek-v4.1-flash
413
444
  id: deepseek/deepseek-v4.1-flash
@@ -419,6 +450,7 @@ models:
419
450
  - canonical_id: alibaba/qwen3.8-flash
420
451
  display_name: Qwen 3.8 Flash
421
452
  publisher: alibaba
453
+ released: '2026-08-26' # https://thenewstack.io/qwen38-flash-previews-qwen4/
422
454
  routes:
423
455
  - key: default:alibaba/qwen3.8-flash
424
456
  id: alibaba/qwen3.8-flash
@@ -430,6 +462,7 @@ models:
430
462
  - canonical_id: inception/mercury-2.5
431
463
  display_name: Mercury 2.5
432
464
  publisher: inception
465
+ released: '2026-09-08' # https://runtimewire.com/article/inception-mercury-2-5-diffusion-language-model-launch
433
466
  routes:
434
467
  - key: default:inception/mercury-2.5
435
468
  id: inception/mercury-2.5
@@ -441,6 +474,7 @@ models:
441
474
  - canonical_id: openai/gpt-5.4-mini
442
475
  display_name: GPT-5.4 Mini
443
476
  publisher: openai
477
+ released: '2026-03-17' # https://community.openai.com/t/introducing-gpt-5-4-mini-and-nano-our-most-capable-small-models-yet/1377015
444
478
  routes:
445
479
  - key: default:openai/gpt-5.4-mini
446
480
  id: openai/gpt-5.4-mini
@@ -460,6 +494,7 @@ models:
460
494
  - canonical_id: openai/gpt-5.4-nano
461
495
  display_name: GPT-5.4 Nano
462
496
  publisher: openai
497
+ released: '2026-03-17' # https://community.openai.com/t/introducing-gpt-5-4-mini-and-nano-our-most-capable-small-models-yet/1377015
463
498
  routes:
464
499
  - key: default:openai/gpt-5.4-nano
465
500
  id: openai/gpt-5.4-nano
@@ -479,6 +514,7 @@ models:
479
514
  - canonical_id: anthropic/claude-opus-4.8
480
515
  display_name: Claude Opus 4.8
481
516
  publisher: anthropic
517
+ released: '2026-05-28' # https://hidekazu-konishi.com/entry/anthropic_claude_model_release_timeline.html
482
518
  routes:
483
519
  - key: default:anthropic/claude-opus-4.8
484
520
  id: anthropic/claude-opus-4.8
@@ -498,6 +534,7 @@ models:
498
534
  - canonical_id: anthropic/claude-haiku-4.5
499
535
  display_name: Claude Haiku 4.5
500
536
  publisher: anthropic
537
+ released: '2025-10-15' # https://www.anthropic.com/news/claude-haiku-4-5
501
538
  routes:
502
539
  - key: default:anthropic/claude-haiku-4.5
503
540
  id: anthropic/claude-haiku-4.5
@@ -517,6 +554,7 @@ models:
517
554
  - canonical_id: fireworks/glm-5p2
518
555
  display_name: GLM 5.2
519
556
  publisher: fireworks
557
+ released: '2026-06-13' # https://pandaily.com/zhipu-ai-glm-5-dot-2-open-source-mit-jun2026
520
558
  routes:
521
559
  - key: default:fireworks:accounts/fireworks/models/glm-5p2
522
560
  id: fireworks:accounts/fireworks/models/glm-5p2
@@ -528,6 +566,7 @@ models:
528
566
  - canonical_id: fireworks/qwen3p7-plus
529
567
  display_name: Qwen 3.7 Plus
530
568
  publisher: fireworks
569
+ released: '2026-06-01' # https://www.vals.ai/models/alibaba_qwen3.7-plus
531
570
  routes:
532
571
  - key: default:fireworks:accounts/fireworks/models/qwen3p7-plus
533
572
  id: fireworks:accounts/fireworks/models/qwen3p7-plus
@@ -539,6 +578,7 @@ models:
539
578
  - canonical_id: openai/gpt-5.4
540
579
  display_name: GPT-5.4
541
580
  publisher: openai
581
+ released: '2026-03-05' # https://en.wikipedia.org/wiki/GPT-5.4
542
582
  routes:
543
583
  - key: default:openai:gpt-5.4
544
584
  id: openai:gpt-5.4
@@ -551,6 +591,7 @@ models:
551
591
  - canonical_id: google/gemma-3-27b-it
552
592
  display_name: Gemma 3 27B
553
593
  publisher: google
594
+ released: '2025-03-12' # https://blog.google/technology/developers/gemma-3/
554
595
  routes:
555
596
  - key: bedrock:google/gemma-3-27b-it
556
597
  id: google/gemma-3-27b-it
@@ -562,6 +603,7 @@ models:
562
603
  - canonical_id: deepseek/v3.2
563
604
  display_name: DeepSeek V3.2
564
605
  publisher: deepseek
606
+ released: '2025-12-01' # https://en.wikipedia.org/wiki/DeepSeek_(chatbot)
565
607
  routes:
566
608
  - key: bedrock:deepseek/deepseek-v3.2
567
609
  id: deepseek/deepseek-v3.2
@@ -573,6 +615,7 @@ models:
573
615
  - canonical_id: deepseek/v3.1
574
616
  display_name: DeepSeek V3.1
575
617
  publisher: deepseek
618
+ released: '2025-08-21' # https://en.wikipedia.org/wiki/DeepSeek_(chatbot)
576
619
  routes:
577
620
  - key: bedrock:deepseek/deepseek-v3.1
578
621
  id: deepseek/deepseek-v3.1
@@ -584,6 +627,7 @@ models:
584
627
  - canonical_id: anthropic/claude-opus-5.5
585
628
  display_name: Claude Opus 5.5
586
629
  publisher: anthropic
630
+ released: '2026-09-22' # https://www.anthropic.com/claude-opus-5-5
587
631
  routes:
588
632
  - key: default:anthropic/claude-opus-5.5
589
633
  id: anthropic/claude-opus-5.5
@@ -595,6 +639,7 @@ models:
595
639
  - canonical_id: anthropic/claude-sonnet-5.5
596
640
  display_name: Claude Sonnet 5.5
597
641
  publisher: anthropic
642
+ released: '2026-09-28' # https://www.anthropic.com/claude/sonnet
598
643
  routes:
599
644
  - key: default:anthropic/claude-sonnet-5.5
600
645
  id: anthropic/claude-sonnet-5.5
@@ -606,6 +651,7 @@ models:
606
651
  - canonical_id: openai/gpt-6.1-sol
607
652
  display_name: GPT-6.1 Sol
608
653
  publisher: openai
654
+ released: '2026-09-29' # https://www.sitepoint.com/gpt-6-1-sol-developers-pricing-quickstart/
609
655
  routes:
610
656
  - key: default:openai/gpt-6.1-sol
611
657
  id: openai/gpt-6.1-sol
@@ -617,6 +663,7 @@ models:
617
663
  - canonical_id: xai/grok-4.7
618
664
  display_name: Grok 4.7
619
665
  publisher: xai
666
+ released: '2026-09-21' # https://x.ai/news
620
667
  routes:
621
668
  - key: default:xai/grok-4.7
622
669
  id: xai/grok-4.7
@@ -628,6 +675,7 @@ models:
628
675
  - canonical_id: moonshotai/kimi-k2.7-code
629
676
  display_name: Kimi K2.7 Code
630
677
  publisher: moonshotai
678
+ released: '2026-06-12' # https://www.marktechpost.com/2026/06/12/moonshot-ai-releases-kimi-k2-7-code-a-coding-model-reporting-21-8-on-kimi-code-bench-v2-over-k2-6/
631
679
  routes:
632
680
  - key: default:moonshotai/kimi-k2.7-code
633
681
  id: moonshotai/kimi-k2.7-code
@@ -639,6 +687,7 @@ models:
639
687
  - canonical_id: zai/glm-5.3
640
688
  display_name: GLM 5.3
641
689
  publisher: zai
690
+ released: '2026-08-14' # https://emergent.sh/news/glm-53-officially-launched-1e1dc
642
691
  routes:
643
692
  - key: default:zai/glm-5.3
644
693
  id: zai/glm-5.3
@@ -647,6 +696,30 @@ models:
647
696
  pricing_channel: vercel-ai-gateway
648
697
  execution_profiles:
649
698
  - default
699
+ - canonical_id: anthropic/claude-sonnet-4.6
700
+ display_name: Claude Sonnet 4.6
701
+ publisher: anthropic
702
+ released: '2026-02-17' # https://alternativeto.net/news/2026/2/anthropic-s-claude-sonnet-4-6-launches-with-faster-speeds-and-1m-token-context
703
+ routes:
704
+ - key: default:anthropic/claude-sonnet-4.6
705
+ id: anthropic/claude-sonnet-4.6
706
+ provider: vercel
707
+ model_id: anthropic/claude-sonnet-4.6
708
+ pricing_channel: vercel-ai-gateway
709
+ execution_profiles:
710
+ - default
711
+ - canonical_id: xai/grok-4.3
712
+ display_name: Grok 4.3
713
+ publisher: xai
714
+ released: '2026-04-30' # https://artificialanalysis.ai/articles/xai-launches-grok-4-3-with-improved-agentic-performance-and-lower-pricing
715
+ routes:
716
+ - key: default:xai/grok-4.3
717
+ id: xai/grok-4.3
718
+ provider: vercel
719
+ model_id: xai/grok-4.3
720
+ pricing_channel: vercel-ai-gateway
721
+ execution_profiles:
722
+ - default
650
723
  execution_profiles:
651
724
  - id: default
652
725
  routes:
@@ -705,6 +778,8 @@ execution_profiles:
705
778
  - default:anthropic:claude-opus-4-8
706
779
  - default:anthropic:claude-sonnet-5
707
780
  - default:anthropic:claude-haiku-4-5
781
+ - default:anthropic/claude-sonnet-4.6
782
+ - default:xai/grok-4.3
708
783
  - id: bedrock
709
784
  routes:
710
785
  - bedrock:anthropic/claude-sonnet-5
@@ -762,6 +837,8 @@ offer_groups:
762
837
  - default:xai/grok-4.7
763
838
  - default:moonshotai/kimi-k2.7-code
764
839
  - default:zai/glm-5.3
840
+ - default:anthropic/claude-sonnet-4.6
841
+ - default:xai/grok-4.3
765
842
  - id: fireworks
766
843
  credential: FIREWORKS_API_KEY
767
844
  routes:
@@ -2029,6 +2029,7 @@ models:
2029
2029
  - {provider: x-ai, alias: grok-latest, channel: xai-api}
2030
2030
  - {provider: bedrock, alias: xai.grok-4.3, channel: aws-bedrock}
2031
2031
  - {provider: vercel, alias: spacexai/grok-4.3, channel: vercel-ai-gateway}
2032
+ - {provider: xai, alias: xai/grok-4.3, channel: vercel-ai-gateway}
2032
2033
  - {provider: litellm, alias: grok-4.3, channel: xai-api}
2033
2034
  - {provider: litellm, alias: grok-4.3-latest, channel: xai-api}
2034
2035
  - {provider: unknown, alias: grok-4.3, channel: xai-api}
@@ -2077,6 +2078,7 @@ models:
2077
2078
  - {provider: litellm, alias: sonar, channel: perplexity-api}
2078
2079
  - {provider: perplexity-ai, alias: perplexity/sonar, channel: perplexity-api}
2079
2080
  - {provider: unknown, alias: sonar, channel: perplexity-api}
2081
+ - {provider: vercel, alias: perplexity/sonar, channel: vercel-ai-gateway}
2080
2082
  prices:
2081
2083
  - channel: perplexity-api
2082
2084
  region: global
@@ -2086,6 +2088,15 @@ models:
2086
2088
  rules:
2087
2089
  uncaptured_fees: true
2088
2090
  source_url: https://docs.perplexity.ai/docs/getting-started/pricing
2091
+ # The gateway bills Sonar's built-in search per call, so the row carries
2092
+ # it as a request fee and the call is priced in full.
2093
+ - channel: vercel-ai-gateway
2094
+ region: global
2095
+ effective_from: "2026-10-11"
2096
+ input_per_mtok: 0.25
2097
+ output_per_mtok: 2.50
2098
+ request_fee_per_1k_usd: 2.50
2099
+ source_url: https://vercel.com/ai-gateway/models/sonar
2089
2100
 
2090
2101
  - canonical_id: openai/gpt-5-mini
2091
2102
  publisher: openai
@@ -305,6 +305,7 @@ def parse_catalog(
305
305
  ),
306
306
  batch_input_per_mtok=_decimal(price.get("batch_input_per_mtok")),
307
307
  batch_output_per_mtok=_decimal(price.get("batch_output_per_mtok")),
308
+ request_fee_per_1k_usd=_decimal(price.get("request_fee_per_1k_usd")),
308
309
  rules=_freeze(price.get("rules") or {}),
309
310
  effective_from=effective_from,
310
311
  effective_to=effective_to,
@@ -76,6 +76,19 @@ class ModelDefinition:
76
76
  display_name: str
77
77
  publisher: str
78
78
  routes: tuple[ModelRoute, ...]
79
+ # The publisher's general-availability date, when recorded.
80
+ released: date | None = None
81
+
82
+
83
+ @dataclass(frozen=True, slots=True)
84
+ class ReleaseDate:
85
+ """When a model became available, and what that date is based on:
86
+ ``released`` is the registry's recorded date; ``first_price`` is the
87
+ earliest price window the catalog has for the model, used when no date
88
+ is recorded."""
89
+
90
+ date: date
91
+ basis: str
79
92
 
80
93
 
81
94
  @dataclass(frozen=True, slots=True)
@@ -96,6 +109,36 @@ class ModelRegistry:
96
109
  def model(self, canonical_id: str) -> ModelDefinition:
97
110
  return self.models[canonical_id]
98
111
 
112
+ def release_date(
113
+ self, canonical_id: str, catalog: LoadedCatalog
114
+ ) -> ReleaseDate | None:
115
+ """The model's release date: recorded when the registry has one,
116
+ otherwise the earliest ``effective_from`` of its catalog prices, or
117
+ None when neither exists. Callers never compute the fallback
118
+ themselves, so every product reads the same answer."""
119
+ model = self.models.get(canonical_id)
120
+ if model is not None and model.released is not None:
121
+ return ReleaseDate(model.released, "released")
122
+ entry = next(
123
+ (
124
+ entry
125
+ for entry in catalog.document.get("models", [])
126
+ if entry.get("canonical_id") == canonical_id
127
+ ),
128
+ None,
129
+ )
130
+ # A price window starts on a date or a timestamp; the catalog accepts
131
+ # both, so both are read here.
132
+ first_price = min(
133
+ (
134
+ _window_day(price["effective_from"])
135
+ for price in (entry or {}).get("prices", [])
136
+ if price.get("effective_from") is not None
137
+ ),
138
+ default=None,
139
+ )
140
+ return None if first_price is None else ReleaseDate(first_price, "first_price")
141
+
99
142
  def route(self, route_key: str) -> ModelRoute:
100
143
  return self.routes[route_key]
101
144
 
@@ -152,6 +195,29 @@ def _unique_text_list(value: Any, field: str) -> tuple[str, ...]:
152
195
  return items
153
196
 
154
197
 
198
+ def _window_day(value: Any) -> date:
199
+ """The calendar day a catalog price window starts, from a date or a
200
+ timestamp as the catalog loader accepts them."""
201
+ if isinstance(value, datetime):
202
+ return value.date()
203
+ if isinstance(value, date):
204
+ return value
205
+ return datetime.fromisoformat(str(value)).date()
206
+
207
+
208
+ def _optional_date(value: Any, field: str) -> date | None:
209
+ """An ISO calendar date, or None when absent. A datetime is not accepted:
210
+ a release is a day, and the registry version is already a date."""
211
+ if value is None:
212
+ return None
213
+ if isinstance(value, date) and not isinstance(value, datetime):
214
+ return value
215
+ try:
216
+ return date.fromisoformat(str(value))
217
+ except ValueError as exc:
218
+ raise ModelRegistryError(f"{field} must be an ISO date") from exc
219
+
220
+
155
221
  def _registry_version_date(version: str) -> date:
156
222
  date_value, separator, revision = version.partition(".")
157
223
  try:
@@ -194,6 +260,7 @@ def parse_model_registry(document: Any) -> ModelRegistry:
194
260
  raise ModelRegistryError(
195
261
  f"{canonical_id}: publisher {publisher!r} does not match canonical id"
196
262
  )
263
+ released = _optional_date(model.get("released"), f"{canonical_id}: released")
197
264
  model_routes: list[ModelRoute] = []
198
265
  route_values = _list(model.get("routes"), f"{canonical_id}: routes")
199
266
  if not route_values:
@@ -263,6 +330,7 @@ def parse_model_registry(document: Any) -> ModelRegistry:
263
330
  display_name=display_name,
264
331
  publisher=publisher,
265
332
  routes=tuple(model_routes),
333
+ released=released,
266
334
  )
267
335
 
268
336
  execution_profiles: dict[str, tuple[str, ...]] = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metergraph-core
3
- Version: 0.2.56
3
+ Version: 0.2.58
4
4
  Summary: Reusable MeterGraph catalog and deterministic billing engine
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -109,6 +109,13 @@ IDs may intentionally repeat across execution profiles when gateway and
109
109
  Bedrock use different physical routes; each route therefore also has a unique
110
110
  internal `key`.
111
111
 
112
+ A model entry may record `released`, the publisher's general-availability
113
+ date as an ISO date. `models.release_date(canonical_id, load_catalog())`
114
+ returns that date with basis `released`, or, when none is recorded, the
115
+ earliest `effective_from` of the model's catalog prices with basis
116
+ `first_price`, so a consumer can tell a launch date from a pricing date
117
+ without computing the fallback itself.
118
+
112
119
  Use `validate_model_registry(models, load_catalog())` to verify that every
113
120
  route's provider-facing model ID is declared on its exact pricing channel,
114
121
  resolves to its canonical model, and has an active price on the registry