llm_cost_tracker 0.12.0 → 0.13.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/app/controllers/llm_cost_tracker/data_quality_controller.rb +1 -0
- data/app/helpers/llm_cost_tracker/application_helper.rb +6 -4
- data/app/models/llm_cost_tracker/ingestion/inbox_entry.rb +1 -0
- data/app/services/llm_cost_tracker/dashboard/data_quality.rb +12 -0
- data/app/views/llm_cost_tracker/calls/show.html.erb +2 -2
- data/app/views/llm_cost_tracker/data_quality/index.html.erb +11 -0
- data/app/views/llm_cost_tracker/pricing/index.html.erb +1 -1
- data/lib/llm_cost_tracker/capture/stream_collector.rb +5 -4
- data/lib/llm_cost_tracker/charges/line_item.rb +3 -2
- data/lib/llm_cost_tracker/event.rb +3 -1
- data/lib/llm_cost_tracker/generators/llm_cost_tracker/templates/initializer.rb.erb +1 -1
- data/lib/llm_cost_tracker/ingestion/batch.rb +25 -7
- data/lib/llm_cost_tracker/integrations/anthropic.rb +8 -7
- data/lib/llm_cost_tracker/integrations/base.rb +1 -1
- data/lib/llm_cost_tracker/ledger/store.rb +8 -1
- data/lib/llm_cost_tracker/prices.json +173 -71
- data/lib/llm_cost_tracker/pricing/calculation.rb +40 -21
- data/lib/llm_cost_tracker/pricing/matcher.rb +17 -3
- data/lib/llm_cost_tracker/pricing/mode.rb +7 -7
- data/lib/llm_cost_tracker/tracker.rb +2 -3
- data/lib/llm_cost_tracker/usage/catalog.rb +11 -0
- data/lib/llm_cost_tracker/usage/dimensions.yml +0 -24
- data/lib/llm_cost_tracker/usage/token_usage.rb +20 -75
- data/lib/llm_cost_tracker/version.rb +1 -1
- data/lib/llm_cost_tracker.rb +1 -1
- metadata +1 -1
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
"min_gem_version": "0.4.0",
|
|
5
5
|
"schema_version": 1,
|
|
6
6
|
"source_urls": [
|
|
7
|
-
"https://developers.openai.com/api/docs/pricing",
|
|
8
7
|
"https://platform.claude.com/docs/en/about-claude/pricing",
|
|
9
8
|
"https://ai.google.dev/gemini-api/docs/pricing",
|
|
10
|
-
"https://
|
|
9
|
+
"https://groq.com/pricing",
|
|
11
10
|
"https://console.groq.com/docs/prompt-caching",
|
|
12
11
|
"https://console.groq.com/docs/flex-processing",
|
|
13
|
-
"https://
|
|
12
|
+
"https://developers.openai.com/api/docs/pricing",
|
|
13
|
+
"https://openrouter.ai/api/v1/models"
|
|
14
14
|
],
|
|
15
15
|
"unit": "1M tokens",
|
|
16
|
-
"updated_at": "2026-06-
|
|
16
|
+
"updated_at": "2026-06-26"
|
|
17
17
|
},
|
|
18
18
|
"models": {
|
|
19
19
|
"anthropic/claude-haiku-3": {
|
|
@@ -34,15 +34,6 @@
|
|
|
34
34
|
"input": 1.0,
|
|
35
35
|
"output": 5.0
|
|
36
36
|
},
|
|
37
|
-
"anthropic/claude-opus-4-1": {
|
|
38
|
-
"batch_input": 7.5,
|
|
39
|
-
"batch_output": 37.5,
|
|
40
|
-
"cache_read_input": 1.5,
|
|
41
|
-
"cache_write_extended_input": 30.0,
|
|
42
|
-
"cache_write_input": 18.75,
|
|
43
|
-
"input": 15.0,
|
|
44
|
-
"output": 75.0
|
|
45
|
-
},
|
|
46
37
|
"anthropic/claude-opus-4-5": {
|
|
47
38
|
"batch_input": 2.5,
|
|
48
39
|
"batch_output": 12.5,
|
|
@@ -91,6 +82,16 @@
|
|
|
91
82
|
"data_residency_cache_write_input": 6.875,
|
|
92
83
|
"data_residency_input": 5.5,
|
|
93
84
|
"data_residency_output": 27.5,
|
|
85
|
+
"fast_cache_read_input": 3.0,
|
|
86
|
+
"fast_cache_write_extended_input": 60.0,
|
|
87
|
+
"fast_cache_write_input": 37.5,
|
|
88
|
+
"fast_data_residency_cache_read_input": 3.3,
|
|
89
|
+
"fast_data_residency_cache_write_extended_input": 66.0,
|
|
90
|
+
"fast_data_residency_cache_write_input": 41.25,
|
|
91
|
+
"fast_data_residency_input": 33.0,
|
|
92
|
+
"fast_data_residency_output": 165.0,
|
|
93
|
+
"fast_input": 30.0,
|
|
94
|
+
"fast_output": 150.0,
|
|
94
95
|
"input": 5.0,
|
|
95
96
|
"output": 25.0
|
|
96
97
|
},
|
|
@@ -107,6 +108,16 @@
|
|
|
107
108
|
"data_residency_cache_write_input": 6.875,
|
|
108
109
|
"data_residency_input": 5.5,
|
|
109
110
|
"data_residency_output": 27.5,
|
|
111
|
+
"fast_cache_read_input": 1.0,
|
|
112
|
+
"fast_cache_write_extended_input": 20.0,
|
|
113
|
+
"fast_cache_write_input": 12.5,
|
|
114
|
+
"fast_data_residency_cache_read_input": 1.1,
|
|
115
|
+
"fast_data_residency_cache_write_extended_input": 22.0,
|
|
116
|
+
"fast_data_residency_cache_write_input": 13.75,
|
|
117
|
+
"fast_data_residency_input": 11.0,
|
|
118
|
+
"fast_data_residency_output": 55.0,
|
|
119
|
+
"fast_input": 10.0,
|
|
120
|
+
"fast_output": 50.0,
|
|
110
121
|
"input": 5.0,
|
|
111
122
|
"output": 25.0
|
|
112
123
|
},
|
|
@@ -377,6 +388,14 @@
|
|
|
377
388
|
"on_demand_output": 0.79,
|
|
378
389
|
"output": 0.79
|
|
379
390
|
},
|
|
391
|
+
"groq/meta-llama/llama-4-scout-17b-16e-instruct": {
|
|
392
|
+
"flex_input": 0.11,
|
|
393
|
+
"flex_output": 0.34,
|
|
394
|
+
"input": 0.11,
|
|
395
|
+
"on_demand_input": 0.11,
|
|
396
|
+
"on_demand_output": 0.34,
|
|
397
|
+
"output": 0.34
|
|
398
|
+
},
|
|
380
399
|
"groq/openai/gpt-oss-120b": {
|
|
381
400
|
"cache_read_input": 0.075,
|
|
382
401
|
"flex_cache_read_input": 0.075,
|
|
@@ -399,6 +418,22 @@
|
|
|
399
418
|
"on_demand_output": 0.3,
|
|
400
419
|
"output": 0.3
|
|
401
420
|
},
|
|
421
|
+
"groq/qwen/qwen3-32b": {
|
|
422
|
+
"flex_input": 0.29,
|
|
423
|
+
"flex_output": 0.59,
|
|
424
|
+
"input": 0.29,
|
|
425
|
+
"on_demand_input": 0.29,
|
|
426
|
+
"on_demand_output": 0.59,
|
|
427
|
+
"output": 0.59
|
|
428
|
+
},
|
|
429
|
+
"groq/qwen/qwen3.6-27b": {
|
|
430
|
+
"flex_input": 0.6,
|
|
431
|
+
"flex_output": 3.0,
|
|
432
|
+
"input": 0.6,
|
|
433
|
+
"on_demand_input": 0.6,
|
|
434
|
+
"on_demand_output": 3.0,
|
|
435
|
+
"output": 3.0
|
|
436
|
+
},
|
|
402
437
|
"openai/chatgpt-4o-latest": {
|
|
403
438
|
"input": 5.0,
|
|
404
439
|
"output": 15.0
|
|
@@ -1083,6 +1118,12 @@
|
|
|
1083
1118
|
"input": 0.8,
|
|
1084
1119
|
"output": 4.0
|
|
1085
1120
|
},
|
|
1121
|
+
"openrouter/anthropic/claude-fable-5": {
|
|
1122
|
+
"cache_read_input": 1.0,
|
|
1123
|
+
"cache_write_input": 12.5,
|
|
1124
|
+
"input": 10.0,
|
|
1125
|
+
"output": 50.0
|
|
1126
|
+
},
|
|
1086
1127
|
"openrouter/anthropic/claude-haiku-4.5": {
|
|
1087
1128
|
"cache_read_input": 0.1,
|
|
1088
1129
|
"cache_write_input": 1.25,
|
|
@@ -1175,8 +1216,8 @@
|
|
|
1175
1216
|
},
|
|
1176
1217
|
"openrouter/arcee-ai/trinity-large-thinking": {
|
|
1177
1218
|
"cache_read_input": 0.06,
|
|
1178
|
-
"input": 0.
|
|
1179
|
-
"output": 0.
|
|
1219
|
+
"input": 0.25,
|
|
1220
|
+
"output": 0.8
|
|
1180
1221
|
},
|
|
1181
1222
|
"openrouter/arcee-ai/trinity-mini": {
|
|
1182
1223
|
"input": 0.045,
|
|
@@ -1275,7 +1316,7 @@
|
|
|
1275
1316
|
"output": 2.15
|
|
1276
1317
|
},
|
|
1277
1318
|
"openrouter/deepseek/deepseek-r1-distill-llama-70b": {
|
|
1278
|
-
"input": 0.
|
|
1319
|
+
"input": 0.8,
|
|
1279
1320
|
"output": 0.8
|
|
1280
1321
|
},
|
|
1281
1322
|
"openrouter/deepseek/deepseek-r1-distill-qwen-32b": {
|
|
@@ -1288,6 +1329,7 @@
|
|
|
1288
1329
|
"output": 0.95
|
|
1289
1330
|
},
|
|
1290
1331
|
"openrouter/deepseek/deepseek-v3.2": {
|
|
1332
|
+
"cache_read_input": 0.02288,
|
|
1291
1333
|
"input": 0.2288,
|
|
1292
1334
|
"output": 0.3432
|
|
1293
1335
|
},
|
|
@@ -1301,9 +1343,9 @@
|
|
|
1301
1343
|
"output": 0.431
|
|
1302
1344
|
},
|
|
1303
1345
|
"openrouter/deepseek/deepseek-v4-flash": {
|
|
1304
|
-
"cache_read_input": 0.
|
|
1305
|
-
"input": 0.
|
|
1306
|
-
"output": 0.
|
|
1346
|
+
"cache_read_input": 0.02,
|
|
1347
|
+
"input": 0.09,
|
|
1348
|
+
"output": 0.18
|
|
1307
1349
|
},
|
|
1308
1350
|
"openrouter/deepseek/deepseek-v4-pro": {
|
|
1309
1351
|
"cache_read_input": 0.003625,
|
|
@@ -1392,6 +1434,14 @@
|
|
|
1392
1434
|
"input": 0.5,
|
|
1393
1435
|
"output": 3.0
|
|
1394
1436
|
},
|
|
1437
|
+
"openrouter/google/gemini-3-pro-image": {
|
|
1438
|
+
"audio_input": 2.0,
|
|
1439
|
+
"cache_read_input": 0.2,
|
|
1440
|
+
"cache_write_input": 0.375,
|
|
1441
|
+
"image_input": 2.0,
|
|
1442
|
+
"input": 2.0,
|
|
1443
|
+
"output": 12.0
|
|
1444
|
+
},
|
|
1395
1445
|
"openrouter/google/gemini-3-pro-image-preview": {
|
|
1396
1446
|
"audio_input": 2.0,
|
|
1397
1447
|
"cache_read_input": 0.2,
|
|
@@ -1400,6 +1450,10 @@
|
|
|
1400
1450
|
"input": 2.0,
|
|
1401
1451
|
"output": 12.0
|
|
1402
1452
|
},
|
|
1453
|
+
"openrouter/google/gemini-3.1-flash-image": {
|
|
1454
|
+
"input": 0.5,
|
|
1455
|
+
"output": 3.0
|
|
1456
|
+
},
|
|
1403
1457
|
"openrouter/google/gemini-3.1-flash-image-preview": {
|
|
1404
1458
|
"input": 0.5,
|
|
1405
1459
|
"output": 3.0
|
|
@@ -1449,16 +1503,16 @@
|
|
|
1449
1503
|
"output": 0.65
|
|
1450
1504
|
},
|
|
1451
1505
|
"openrouter/google/gemma-3-12b-it": {
|
|
1452
|
-
"input": 0.
|
|
1453
|
-
"output": 0.
|
|
1506
|
+
"input": 0.05,
|
|
1507
|
+
"output": 0.15
|
|
1454
1508
|
},
|
|
1455
1509
|
"openrouter/google/gemma-3-27b-it": {
|
|
1456
1510
|
"input": 0.08,
|
|
1457
1511
|
"output": 0.16
|
|
1458
1512
|
},
|
|
1459
1513
|
"openrouter/google/gemma-3-4b-it": {
|
|
1460
|
-
"input": 0.
|
|
1461
|
-
"output": 0.
|
|
1514
|
+
"input": 0.05,
|
|
1515
|
+
"output": 0.1
|
|
1462
1516
|
},
|
|
1463
1517
|
"openrouter/google/gemma-3n-e4b-it": {
|
|
1464
1518
|
"input": 0.06,
|
|
@@ -1469,8 +1523,9 @@
|
|
|
1469
1523
|
"output": 0.33
|
|
1470
1524
|
},
|
|
1471
1525
|
"openrouter/google/gemma-4-31b-it": {
|
|
1526
|
+
"cache_read_input": 0.09,
|
|
1472
1527
|
"input": 0.12,
|
|
1473
|
-
"output": 0.
|
|
1528
|
+
"output": 0.35
|
|
1474
1529
|
},
|
|
1475
1530
|
"openrouter/gryphe/mythomax-l2-13b": {
|
|
1476
1531
|
"input": 0.06,
|
|
@@ -1531,8 +1586,8 @@
|
|
|
1531
1586
|
"output": 0.74
|
|
1532
1587
|
},
|
|
1533
1588
|
"openrouter/meta-llama/llama-3-8b-instruct": {
|
|
1534
|
-
"input": 0.
|
|
1535
|
-
"output": 0.
|
|
1589
|
+
"input": 0.14,
|
|
1590
|
+
"output": 0.14
|
|
1536
1591
|
},
|
|
1537
1592
|
"openrouter/meta-llama/llama-3.1-70b-instruct": {
|
|
1538
1593
|
"input": 0.4,
|
|
@@ -1540,11 +1595,11 @@
|
|
|
1540
1595
|
},
|
|
1541
1596
|
"openrouter/meta-llama/llama-3.1-8b-instruct": {
|
|
1542
1597
|
"input": 0.02,
|
|
1543
|
-
"output": 0.
|
|
1598
|
+
"output": 0.03
|
|
1544
1599
|
},
|
|
1545
1600
|
"openrouter/meta-llama/llama-3.2-11b-vision-instruct": {
|
|
1546
|
-
"input": 0.
|
|
1547
|
-
"output": 0.
|
|
1601
|
+
"input": 0.345,
|
|
1602
|
+
"output": 0.345
|
|
1548
1603
|
},
|
|
1549
1604
|
"openrouter/meta-llama/llama-3.2-1b-instruct": {
|
|
1550
1605
|
"input": 0.027,
|
|
@@ -1563,7 +1618,7 @@
|
|
|
1563
1618
|
"output": 0.6
|
|
1564
1619
|
},
|
|
1565
1620
|
"openrouter/meta-llama/llama-4-scout": {
|
|
1566
|
-
"input": 0.
|
|
1621
|
+
"input": 0.1,
|
|
1567
1622
|
"output": 0.3
|
|
1568
1623
|
},
|
|
1569
1624
|
"openrouter/meta-llama/llama-guard-3-8b": {
|
|
@@ -1575,7 +1630,7 @@
|
|
|
1575
1630
|
"output": 0.18
|
|
1576
1631
|
},
|
|
1577
1632
|
"openrouter/microsoft/phi-4": {
|
|
1578
|
-
"input": 0.
|
|
1633
|
+
"input": 0.07,
|
|
1579
1634
|
"output": 0.14
|
|
1580
1635
|
},
|
|
1581
1636
|
"openrouter/microsoft/phi-4-mini-instruct": {
|
|
@@ -1611,12 +1666,12 @@
|
|
|
1611
1666
|
"output": 0.95
|
|
1612
1667
|
},
|
|
1613
1668
|
"openrouter/minimax/minimax-m2.5": {
|
|
1614
|
-
"input": 0.
|
|
1615
|
-
"output":
|
|
1669
|
+
"input": 0.12,
|
|
1670
|
+
"output": 0.48
|
|
1616
1671
|
},
|
|
1617
1672
|
"openrouter/minimax/minimax-m2.7": {
|
|
1618
|
-
"input": 0.
|
|
1619
|
-
"output":
|
|
1673
|
+
"input": 0.18,
|
|
1674
|
+
"output": 0.72
|
|
1620
1675
|
},
|
|
1621
1676
|
"openrouter/minimax/minimax-m3": {
|
|
1622
1677
|
"cache_read_input": 0.06,
|
|
@@ -1747,18 +1802,23 @@
|
|
|
1747
1802
|
"output": 2.5
|
|
1748
1803
|
},
|
|
1749
1804
|
"openrouter/moonshotai/kimi-k2-thinking": {
|
|
1805
|
+
"cache_read_input": 0.6,
|
|
1750
1806
|
"input": 0.6,
|
|
1751
1807
|
"output": 2.5
|
|
1752
1808
|
},
|
|
1753
1809
|
"openrouter/moonshotai/kimi-k2.5": {
|
|
1754
|
-
"
|
|
1755
|
-
"
|
|
1756
|
-
"output": 1.9
|
|
1810
|
+
"input": 0.375,
|
|
1811
|
+
"output": 2.025
|
|
1757
1812
|
},
|
|
1758
1813
|
"openrouter/moonshotai/kimi-k2.6": {
|
|
1759
1814
|
"cache_read_input": 0.144,
|
|
1760
|
-
"input": 0.
|
|
1761
|
-
"output": 3.
|
|
1815
|
+
"input": 0.66,
|
|
1816
|
+
"output": 3.41
|
|
1817
|
+
},
|
|
1818
|
+
"openrouter/moonshotai/kimi-k2.7-code": {
|
|
1819
|
+
"cache_read_input": 0.15,
|
|
1820
|
+
"input": 0.74,
|
|
1821
|
+
"output": 3.5
|
|
1762
1822
|
},
|
|
1763
1823
|
"openrouter/morph/morph-v3-fast": {
|
|
1764
1824
|
"input": 0.8,
|
|
@@ -1772,6 +1832,11 @@
|
|
|
1772
1832
|
"input": 0.135,
|
|
1773
1833
|
"output": 0.5
|
|
1774
1834
|
},
|
|
1835
|
+
"openrouter/nex-agi/nex-n2-pro": {
|
|
1836
|
+
"cache_read_input": 0.025,
|
|
1837
|
+
"input": 0.25,
|
|
1838
|
+
"output": 1.0
|
|
1839
|
+
},
|
|
1775
1840
|
"openrouter/nousresearch/hermes-2-pro-llama-3-8b": {
|
|
1776
1841
|
"input": 0.14,
|
|
1777
1842
|
"output": 0.14
|
|
@@ -1781,8 +1846,8 @@
|
|
|
1781
1846
|
"output": 1.0
|
|
1782
1847
|
},
|
|
1783
1848
|
"openrouter/nousresearch/hermes-3-llama-3.1-70b": {
|
|
1784
|
-
"input": 0.
|
|
1785
|
-
"output": 0.
|
|
1849
|
+
"input": 0.7,
|
|
1850
|
+
"output": 0.7
|
|
1786
1851
|
},
|
|
1787
1852
|
"openrouter/nousresearch/hermes-4-405b": {
|
|
1788
1853
|
"input": 1.0,
|
|
@@ -1793,7 +1858,7 @@
|
|
|
1793
1858
|
"output": 0.4
|
|
1794
1859
|
},
|
|
1795
1860
|
"openrouter/nvidia/llama-3.3-nemotron-super-49b-v1.5": {
|
|
1796
|
-
"input": 0.
|
|
1861
|
+
"input": 0.4,
|
|
1797
1862
|
"output": 0.4
|
|
1798
1863
|
},
|
|
1799
1864
|
"openrouter/nvidia/nemotron-3-nano-30b-a3b": {
|
|
@@ -1801,8 +1866,13 @@
|
|
|
1801
1866
|
"output": 0.2
|
|
1802
1867
|
},
|
|
1803
1868
|
"openrouter/nvidia/nemotron-3-super-120b-a12b": {
|
|
1804
|
-
"input": 0.
|
|
1805
|
-
"output": 0.
|
|
1869
|
+
"input": 0.085,
|
|
1870
|
+
"output": 0.4
|
|
1871
|
+
},
|
|
1872
|
+
"openrouter/nvidia/nemotron-3-ultra-550b-a55b": {
|
|
1873
|
+
"cache_read_input": 0.1,
|
|
1874
|
+
"input": 0.5,
|
|
1875
|
+
"output": 2.2
|
|
1806
1876
|
},
|
|
1807
1877
|
"openrouter/nvidia/nemotron-nano-9b-v2": {
|
|
1808
1878
|
"input": 0.04,
|
|
@@ -2042,15 +2112,15 @@
|
|
|
2042
2112
|
"output": 30.0
|
|
2043
2113
|
},
|
|
2044
2114
|
"openrouter/openai/gpt-oss-120b": {
|
|
2045
|
-
"input": 0.
|
|
2046
|
-
"output": 0.
|
|
2115
|
+
"input": 0.03,
|
|
2116
|
+
"output": 0.15
|
|
2047
2117
|
},
|
|
2048
2118
|
"openrouter/openai/gpt-oss-20b": {
|
|
2049
2119
|
"input": 0.029,
|
|
2050
2120
|
"output": 0.14
|
|
2051
2121
|
},
|
|
2052
2122
|
"openrouter/openai/gpt-oss-safeguard-20b": {
|
|
2053
|
-
"cache_read_input": 0.
|
|
2123
|
+
"cache_read_input": 0.0375,
|
|
2054
2124
|
"input": 0.075,
|
|
2055
2125
|
"output": 0.3
|
|
2056
2126
|
},
|
|
@@ -2126,6 +2196,16 @@
|
|
|
2126
2196
|
"input": 2.0,
|
|
2127
2197
|
"output": 8.0
|
|
2128
2198
|
},
|
|
2199
|
+
"openrouter/poolside/laguna-m.1": {
|
|
2200
|
+
"cache_read_input": 0.1,
|
|
2201
|
+
"input": 0.2,
|
|
2202
|
+
"output": 0.4
|
|
2203
|
+
},
|
|
2204
|
+
"openrouter/poolside/laguna-xs.2": {
|
|
2205
|
+
"cache_read_input": 0.05,
|
|
2206
|
+
"input": 0.1,
|
|
2207
|
+
"output": 0.2
|
|
2208
|
+
},
|
|
2129
2209
|
"openrouter/prime-intellect/intellect-3": {
|
|
2130
2210
|
"input": 0.2,
|
|
2131
2211
|
"output": 1.1
|
|
@@ -2158,8 +2238,9 @@
|
|
|
2158
2238
|
"output": 0.78
|
|
2159
2239
|
},
|
|
2160
2240
|
"openrouter/qwen/qwen2.5-vl-72b-instruct": {
|
|
2161
|
-
"
|
|
2162
|
-
"
|
|
2241
|
+
"cache_read_input": 0.4,
|
|
2242
|
+
"input": 0.8,
|
|
2243
|
+
"output": 1.0
|
|
2163
2244
|
},
|
|
2164
2245
|
"openrouter/qwen/qwen3-14b": {
|
|
2165
2246
|
"input": 0.1,
|
|
@@ -2170,7 +2251,7 @@
|
|
|
2170
2251
|
"output": 1.82
|
|
2171
2252
|
},
|
|
2172
2253
|
"openrouter/qwen/qwen3-235b-a22b-2507": {
|
|
2173
|
-
"input": 0.
|
|
2254
|
+
"input": 0.09,
|
|
2174
2255
|
"output": 0.1
|
|
2175
2256
|
},
|
|
2176
2257
|
"openrouter/qwen/qwen3-235b-a22b-thinking-2507": {
|
|
@@ -2179,12 +2260,12 @@
|
|
|
2179
2260
|
"output": 0.1
|
|
2180
2261
|
},
|
|
2181
2262
|
"openrouter/qwen/qwen3-30b-a3b": {
|
|
2182
|
-
"input": 0.
|
|
2183
|
-
"output": 0.
|
|
2263
|
+
"input": 0.12,
|
|
2264
|
+
"output": 0.5
|
|
2184
2265
|
},
|
|
2185
2266
|
"openrouter/qwen/qwen3-30b-a3b-instruct-2507": {
|
|
2186
|
-
"input": 0.
|
|
2187
|
-
"output": 0.
|
|
2267
|
+
"input": 0.04815,
|
|
2268
|
+
"output": 0.19305
|
|
2188
2269
|
},
|
|
2189
2270
|
"openrouter/qwen/qwen3-30b-a3b-thinking-2507": {
|
|
2190
2271
|
"cache_read_input": 0.08,
|
|
@@ -2286,11 +2367,11 @@
|
|
|
2286
2367
|
"output": 1.0
|
|
2287
2368
|
},
|
|
2288
2369
|
"openrouter/qwen/qwen3.5-397b-a17b": {
|
|
2289
|
-
"input": 0.
|
|
2290
|
-
"output": 2.
|
|
2370
|
+
"input": 0.385,
|
|
2371
|
+
"output": 2.45
|
|
2291
2372
|
},
|
|
2292
2373
|
"openrouter/qwen/qwen3.5-9b": {
|
|
2293
|
-
"input": 0.
|
|
2374
|
+
"input": 0.1,
|
|
2294
2375
|
"output": 0.15
|
|
2295
2376
|
},
|
|
2296
2377
|
"openrouter/qwen/qwen3.5-flash-02-23": {
|
|
@@ -2307,8 +2388,8 @@
|
|
|
2307
2388
|
"output": 1.8
|
|
2308
2389
|
},
|
|
2309
2390
|
"openrouter/qwen/qwen3.6-27b": {
|
|
2310
|
-
"input": 0.
|
|
2311
|
-
"output": 3.
|
|
2391
|
+
"input": 0.2885,
|
|
2392
|
+
"output": 3.17
|
|
2312
2393
|
},
|
|
2313
2394
|
"openrouter/qwen/qwen3.6-35b-a3b": {
|
|
2314
2395
|
"input": 0.14,
|
|
@@ -2335,6 +2416,12 @@
|
|
|
2335
2416
|
"input": 1.25,
|
|
2336
2417
|
"output": 3.75
|
|
2337
2418
|
},
|
|
2419
|
+
"openrouter/qwen/qwen3.7-plus": {
|
|
2420
|
+
"cache_read_input": 0.064,
|
|
2421
|
+
"cache_write_input": 0.4,
|
|
2422
|
+
"input": 0.32,
|
|
2423
|
+
"output": 1.28
|
|
2424
|
+
},
|
|
2338
2425
|
"openrouter/rekaai/reka-edge": {
|
|
2339
2426
|
"input": 0.1,
|
|
2340
2427
|
"output": 0.1
|
|
@@ -2351,6 +2438,11 @@
|
|
|
2351
2438
|
"input": 1.0,
|
|
2352
2439
|
"output": 3.0
|
|
2353
2440
|
},
|
|
2441
|
+
"openrouter/sakana/fugu-ultra": {
|
|
2442
|
+
"cache_read_input": 0.5,
|
|
2443
|
+
"input": 5.0,
|
|
2444
|
+
"output": 30.0
|
|
2445
|
+
},
|
|
2354
2446
|
"openrouter/sao10k/l3-euryale-70b": {
|
|
2355
2447
|
"input": 1.48,
|
|
2356
2448
|
"output": 1.48
|
|
@@ -2400,8 +2492,8 @@
|
|
|
2400
2492
|
"output": 0.5
|
|
2401
2493
|
},
|
|
2402
2494
|
"openrouter/thedrummer/rocinante-12b": {
|
|
2403
|
-
"input": 0.
|
|
2404
|
-
"output": 0.
|
|
2495
|
+
"input": 0.25,
|
|
2496
|
+
"output": 0.5
|
|
2405
2497
|
},
|
|
2406
2498
|
"openrouter/thedrummer/skyfall-36b-v2": {
|
|
2407
2499
|
"cache_read_input": 0.25,
|
|
@@ -2432,8 +2524,8 @@
|
|
|
2432
2524
|
},
|
|
2433
2525
|
"openrouter/x-ai/grok-4.20-multi-agent": {
|
|
2434
2526
|
"cache_read_input": 0.2,
|
|
2435
|
-
"input":
|
|
2436
|
-
"output":
|
|
2527
|
+
"input": 1.25,
|
|
2528
|
+
"output": 2.5
|
|
2437
2529
|
},
|
|
2438
2530
|
"openrouter/x-ai/grok-4.3": {
|
|
2439
2531
|
"cache_read_input": 0.2,
|
|
@@ -2461,8 +2553,7 @@
|
|
|
2461
2553
|
"output": 3.0
|
|
2462
2554
|
},
|
|
2463
2555
|
"openrouter/xiaomi/mimo-v2.5": {
|
|
2464
|
-
"
|
|
2465
|
-
"input": 0.14,
|
|
2556
|
+
"input": 0.105,
|
|
2466
2557
|
"output": 0.28
|
|
2467
2558
|
},
|
|
2468
2559
|
"openrouter/xiaomi/mimo-v2.5-pro": {
|
|
@@ -2480,8 +2571,8 @@
|
|
|
2480
2571
|
"output": 2.2
|
|
2481
2572
|
},
|
|
2482
2573
|
"openrouter/z-ai/glm-4.5-air": {
|
|
2483
|
-
"cache_read_input": 0.
|
|
2484
|
-
"input": 0.
|
|
2574
|
+
"cache_read_input": 0.025,
|
|
2575
|
+
"input": 0.13,
|
|
2485
2576
|
"output": 0.85
|
|
2486
2577
|
},
|
|
2487
2578
|
"openrouter/z-ai/glm-4.5v": {
|
|
@@ -2495,7 +2586,7 @@
|
|
|
2495
2586
|
"output": 1.74
|
|
2496
2587
|
},
|
|
2497
2588
|
"openrouter/z-ai/glm-4.6v": {
|
|
2498
|
-
"cache_read_input": 0.
|
|
2589
|
+
"cache_read_input": 0.055,
|
|
2499
2590
|
"input": 0.3,
|
|
2500
2591
|
"output": 0.9
|
|
2501
2592
|
},
|
|
@@ -2524,11 +2615,22 @@
|
|
|
2524
2615
|
"input": 0.98,
|
|
2525
2616
|
"output": 3.08
|
|
2526
2617
|
},
|
|
2618
|
+
"openrouter/z-ai/glm-5.2": {
|
|
2619
|
+
"cache_read_input": 0.18,
|
|
2620
|
+
"input": 0.95,
|
|
2621
|
+
"output": 3.0
|
|
2622
|
+
},
|
|
2527
2623
|
"openrouter/z-ai/glm-5v-turbo": {
|
|
2528
2624
|
"cache_read_input": 0.24,
|
|
2529
2625
|
"input": 1.2,
|
|
2530
2626
|
"output": 4.0
|
|
2531
2627
|
},
|
|
2628
|
+
"openrouter/~anthropic/claude-fable-latest": {
|
|
2629
|
+
"cache_read_input": 1.0,
|
|
2630
|
+
"cache_write_input": 12.5,
|
|
2631
|
+
"input": 10.0,
|
|
2632
|
+
"output": 50.0
|
|
2633
|
+
},
|
|
2532
2634
|
"openrouter/~anthropic/claude-haiku-latest": {
|
|
2533
2635
|
"cache_read_input": 0.1,
|
|
2534
2636
|
"cache_write_input": 1.25,
|
|
@@ -2565,8 +2667,8 @@
|
|
|
2565
2667
|
},
|
|
2566
2668
|
"openrouter/~moonshotai/kimi-latest": {
|
|
2567
2669
|
"cache_read_input": 0.144,
|
|
2568
|
-
"input": 0.
|
|
2569
|
-
"output": 3.
|
|
2670
|
+
"input": 0.66,
|
|
2671
|
+
"output": 3.41
|
|
2570
2672
|
},
|
|
2571
2673
|
"openrouter/~openai/gpt-latest": {
|
|
2572
2674
|
"cache_read_input": 0.5,
|
|
@@ -10,7 +10,8 @@ module LlmCostTracker
|
|
|
10
10
|
module Pricing
|
|
11
11
|
class Calculation
|
|
12
12
|
RATE_DENOMINATOR_TOKENS = Pricing::RATE_BASIS_QUANTITIES.fetch("per_million_tokens")
|
|
13
|
-
|
|
13
|
+
SNAPSHOT_SCHEMA_VERSION = 1
|
|
14
|
+
private_constant :RATE_DENOMINATOR_TOKENS, :SNAPSHOT_SCHEMA_VERSION
|
|
14
15
|
|
|
15
16
|
def self.for(provider:, model:, tokens:, pricing_mode:, line_items: [], usage_source: nil)
|
|
16
17
|
new(provider: provider,
|
|
@@ -61,7 +62,12 @@ module LlmCostTracker
|
|
|
61
62
|
def snapshot
|
|
62
63
|
return @snapshot if defined?(@snapshot)
|
|
63
64
|
|
|
64
|
-
@snapshot =
|
|
65
|
+
@snapshot =
|
|
66
|
+
if priceable?
|
|
67
|
+
build_snapshot
|
|
68
|
+
elsif kept_service_lines.any?
|
|
69
|
+
build_service_snapshot
|
|
70
|
+
end
|
|
65
71
|
end
|
|
66
72
|
|
|
67
73
|
def cost
|
|
@@ -131,7 +137,7 @@ module LlmCostTracker
|
|
|
131
137
|
|
|
132
138
|
def build_snapshot
|
|
133
139
|
{
|
|
134
|
-
"schema_version" =>
|
|
140
|
+
"schema_version" => SNAPSHOT_SCHEMA_VERSION,
|
|
135
141
|
"source" => match.source.name,
|
|
136
142
|
"source_key" => match.key,
|
|
137
143
|
"source_version" => match.source.version,
|
|
@@ -141,6 +147,18 @@ module LlmCostTracker
|
|
|
141
147
|
}
|
|
142
148
|
end
|
|
143
149
|
|
|
150
|
+
def build_service_snapshot
|
|
151
|
+
primary = kept_service_lines.first
|
|
152
|
+
{
|
|
153
|
+
"schema_version" => SNAPSHOT_SCHEMA_VERSION,
|
|
154
|
+
"source" => primary.price_source,
|
|
155
|
+
"source_version" => primary.price_source_version,
|
|
156
|
+
"matched_by" => "service_charges",
|
|
157
|
+
"currency" => cost.currency,
|
|
158
|
+
"rates" => service_charge_rates
|
|
159
|
+
}
|
|
160
|
+
end
|
|
161
|
+
|
|
144
162
|
def token_charge_rates
|
|
145
163
|
priced_token_line_items.each_with_object({}) do |line_item, rates|
|
|
146
164
|
next if line_item.price_key.nil? || line_item.rate_amount.nil?
|
|
@@ -150,8 +168,8 @@ module LlmCostTracker
|
|
|
150
168
|
end
|
|
151
169
|
|
|
152
170
|
def service_charge_rates
|
|
153
|
-
|
|
154
|
-
next if line_item.
|
|
171
|
+
kept_service_lines.each_with_object({}) do |line_item, rates|
|
|
172
|
+
next if line_item.price_key.nil? || line_item.rate_amount.nil?
|
|
155
173
|
|
|
156
174
|
rates[line_item.price_key] ||= rate_entry(line_item.rate_amount, line_item.rate_quantity)
|
|
157
175
|
end
|
|
@@ -162,7 +180,7 @@ module LlmCostTracker
|
|
|
162
180
|
end
|
|
163
181
|
|
|
164
182
|
def price_token(line_item)
|
|
165
|
-
dimension =
|
|
183
|
+
dimension = line_item.dimension
|
|
166
184
|
return line_item unless dimension
|
|
167
185
|
return line_item.with(cost_status: Charges::CostStatus::UNKNOWN) unless priceable?
|
|
168
186
|
|
|
@@ -210,30 +228,31 @@ module LlmCostTracker
|
|
|
210
228
|
)
|
|
211
229
|
end
|
|
212
230
|
|
|
213
|
-
def
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
231
|
+
def kept_service_lines
|
|
232
|
+
return @kept_service_lines if defined?(@kept_service_lines)
|
|
233
|
+
|
|
234
|
+
@kept_service_lines = begin
|
|
235
|
+
priced_services = priced_line_items.reject(&:token?).select(&:priced?)
|
|
236
|
+
if priced_services.empty?
|
|
237
|
+
[]
|
|
238
|
+
else
|
|
239
|
+
base_currency = base_currency_for(token_cost, priced_services)
|
|
240
|
+
matching, mismatched = priced_services.partition { |line| line.currency.to_s == base_currency.to_s }
|
|
241
|
+
warn_currency_mismatch(mismatched, base_currency) if mismatched.any?
|
|
242
|
+
matching
|
|
243
|
+
end
|
|
220
244
|
end
|
|
221
245
|
end
|
|
222
246
|
|
|
223
247
|
def combine_service_lines
|
|
224
248
|
cost = token_cost
|
|
225
|
-
|
|
226
|
-
return cost if priced_services.empty?
|
|
227
|
-
|
|
228
|
-
base_currency = base_currency_for(cost, priced_services)
|
|
229
|
-
matching, mismatched = priced_services.partition { |line| line.currency.to_s == base_currency.to_s }
|
|
230
|
-
warn_currency_mismatch(mismatched, base_currency) if mismatched.any?
|
|
249
|
+
return cost if kept_service_lines.empty?
|
|
231
250
|
|
|
232
|
-
service_total =
|
|
251
|
+
service_total = kept_service_lines.sum(BigDecimal("0")) { |line| line.cost_value.round(8) }
|
|
233
252
|
Charges::Cost.new(
|
|
234
253
|
components: cost ? cost.components : {}.freeze,
|
|
235
254
|
total: (cost&.total || BigDecimal("0")) + service_total,
|
|
236
|
-
currency: (cost&.currency ||
|
|
255
|
+
currency: (cost&.currency || kept_service_lines.first.currency).to_s
|
|
237
256
|
)
|
|
238
257
|
end
|
|
239
258
|
|