cctally 1.110.0 → 1.112.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.
- package/CHANGELOG.md +23 -0
- package/README.md +5 -6
- package/bin/_cctally_cache.py +14 -7
- package/bin/_cctally_core.py +12 -1
- package/bin/_cctally_dashboard.py +60 -154
- package/bin/_cctally_dashboard_envelope.py +7 -6
- package/bin/_cctally_dashboard_sources.py +90 -55
- package/bin/_cctally_doctor.py +73 -27
- package/bin/_cctally_forecast.py +2 -1
- package/bin/_cctally_journal.py +147 -6
- package/bin/_cctally_milestones.py +29 -3
- package/bin/_cctally_quota.py +10 -1
- package/bin/_cctally_record.py +136 -4
- package/bin/_cctally_rederive.py +311 -8
- package/bin/_cctally_source_analytics.py +20 -8
- package/bin/_cctally_tui.py +546 -45
- package/bin/_cctally_update.py +420 -14
- package/bin/_lib_cache_report.py +7 -10
- package/bin/_lib_codex_conversation_query.py +40 -4
- package/bin/_lib_codex_metadata.py +57 -11
- package/bin/_lib_cost_provenance.py +303 -0
- package/bin/_lib_dashboard_sources.py +263 -28
- package/bin/_lib_doctor.py +2 -2
- package/bin/_lib_ingest_frontier.py +137 -38
- package/bin/_lib_pricing.py +24 -263
- package/bin/_lib_rederive.py +704 -36
- package/bin/_lib_snapshot_cache.py +332 -9
- package/bin/cctally +18 -5
- package/dashboard/static/assets/{ConversationsView-BB9vUiL6.js → ConversationsView-CEZ-BUyi.js} +1 -1
- package/dashboard/static/assets/{DoctorModal-DFY2iPDf.js → DoctorModal-CE1oH_Zy.js} +1 -1
- package/dashboard/static/assets/ModalRoot-D5Y8IFUG.js +1 -0
- package/dashboard/static/assets/{ProjectsDrillPanel-DAv39uSo.js → ProjectsDrillPanel-B3DZoCeD.js} +1 -1
- package/dashboard/static/assets/{SourceDetailModal-DSAWo2Fp.js → SourceDetailModal-CHp9EgLP.js} +1 -1
- package/dashboard/static/assets/{UpdateModal-R_9dOVnh.js → UpdateModal-5iKST6jo.js} +1 -1
- package/dashboard/static/assets/{index-DdU62sov.js → index-Cm0VTxJC.js} +2 -2
- package/dashboard/static/assets/index-Dt4tqf43.css +1 -0
- package/dashboard/static/assets/{outlineNavigation-BOCLcRa6.js → outlineNavigation-DkXGL8J6.js} +1 -1
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +2 -1
- package/dashboard/static/assets/ModalRoot-DvRKHbQE.js +0 -1
- package/dashboard/static/assets/index-COhxe3vU.css +0 -1
package/bin/_lib_pricing.py
CHANGED
|
@@ -17,17 +17,12 @@ Spec: docs/superpowers/specs/2026-05-13-bin-cctally-split-design.md
|
|
|
17
17
|
"""
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
import contextlib as _contextlib
|
|
21
|
-
import contextvars as _contextvars
|
|
22
20
|
import dataclasses
|
|
23
21
|
import datetime as dt
|
|
24
22
|
import re
|
|
25
23
|
import sys
|
|
26
|
-
import threading as _threading
|
|
27
24
|
from typing import Any
|
|
28
25
|
|
|
29
|
-
_sys = sys
|
|
30
|
-
|
|
31
26
|
|
|
32
27
|
def _eprint(*args: Any) -> None:
|
|
33
28
|
print(*args, file=sys.stderr)
|
|
@@ -68,7 +63,7 @@ def _chip_for_model(name: str) -> str:
|
|
|
68
63
|
# fingerprint a store recorded and refuses a write from an older process. That
|
|
69
64
|
# comparison is day-granular by construction, so two revisions sharing a date
|
|
70
65
|
# compare equal and the older process is authorized to write.
|
|
71
|
-
PRICING_SNAPSHOT_DATE = "2026-09-
|
|
66
|
+
PRICING_SNAPSHOT_DATE = "2026-09-29"
|
|
72
67
|
PRICING_STALENESS_DAYS = 60 # release pre-flight WARNs past this age
|
|
73
68
|
|
|
74
69
|
|
|
@@ -405,6 +400,11 @@ PRICING_DRIFT_ALLOWLIST: list[dict] = [
|
|
|
405
400
|
# MTok, $5 five-minute cache write, $0.20 cache read, and 2x fast rate.
|
|
406
401
|
# Its 1M context uses standard rates throughout. The pricing fingerprint
|
|
407
402
|
# advances to 2026-09-23 because another revision already used 2026-09-22.
|
|
403
|
+
# 2026-09-29: added claude-sonnet-5-5 at Anthropic's published $2/$10 per
|
|
404
|
+
# MTok, $2.50 five-minute cache write and $0.20 cache read, the same card
|
|
405
|
+
# as Sonnet 5 and the standard 0.1x read multiplier. 1M context at standard
|
|
406
|
+
# pricing, so NO above-200k tier, and no fast mode, so no fast multiplier.
|
|
407
|
+
# Verified against LiteLLM's anthropic-provider row on 2026-09-29.
|
|
408
408
|
# Anthropic prices a cache WRITE by TTL: 1.25x base input for a 5-minute write,
|
|
409
409
|
# 2x for a 1-hour write. Both WRITE multipliers are documented as applying
|
|
410
410
|
# consistently across all supported models, so the 1h rate is DERIVED from
|
|
@@ -651,6 +651,13 @@ CLAUDE_MODEL_PRICING: dict[str, dict[str, Any]] = {
|
|
|
651
651
|
"cache_creation_input_token_cost": 2.5e-06,
|
|
652
652
|
"cache_read_input_token_cost": 2e-07,
|
|
653
653
|
},
|
|
654
|
+
"claude-sonnet-5-5": {
|
|
655
|
+
# Source: https://platform.claude.com/docs/en/models/sonnet-5-5/overview
|
|
656
|
+
"input_cost_per_token": 2e-06,
|
|
657
|
+
"output_cost_per_token": 1e-05,
|
|
658
|
+
"cache_creation_input_token_cost": 2.5e-06,
|
|
659
|
+
"cache_read_input_token_cost": 2e-07,
|
|
660
|
+
},
|
|
654
661
|
}
|
|
655
662
|
|
|
656
663
|
# Anthropic fast-mode pricing is genuinely model-specific. Unsupported models
|
|
@@ -1448,36 +1455,20 @@ def _short_model_name(model: str) -> str:
|
|
|
1448
1455
|
|
|
1449
1456
|
|
|
1450
1457
|
# ---------------------------------------------------------------------------
|
|
1451
|
-
#
|
|
1458
|
+
# The pricing snapshot every cost kernel reads.
|
|
1452
1459
|
#
|
|
1453
|
-
#
|
|
1454
|
-
#
|
|
1455
|
-
#
|
|
1456
|
-
#
|
|
1457
|
-
# A PARTIAL SWAP IS WORSE THAN NO SWAP. `PRICING_SNAPSHOT_DATE` is what gates
|
|
1458
|
-
# the materialized-cost write refusal in `_cctally_cache`, so replacing the
|
|
1459
|
-
# date alone clears the refusal and resumes writing cost computed from the old
|
|
1460
|
-
# tables. A candidate is therefore validated whole and adopted whole, or it is
|
|
1461
|
-
# rejected and the live snapshot is retained unchanged.
|
|
1462
|
-
#
|
|
1463
|
-
# NEVER `importlib.reload`. It executes arbitrary module code from a file an
|
|
1464
|
-
# installer has just written; it mutates a live module dict incrementally, so a
|
|
1465
|
-
# request in flight can see half a table; and it would not even fix the bug,
|
|
1466
|
-
# because reloading this module rebinds none of the import-time copies listed
|
|
1467
|
-
# in `PRICING_EXPORT_BINDINGS`. `ast.literal_eval` cannot execute code.
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
class PricingCandidateRejected(Exception):
|
|
1471
|
-
"""A candidate pricing file was not adopted. Carries the reason."""
|
|
1460
|
+
# Built once at import from the module globals above and never replaced. A
|
|
1461
|
+
# long-lived dashboard does not reload pricing in process: after a proven
|
|
1462
|
+
# install of a newer version it restarts itself (#868), and after a manual
|
|
1463
|
+
# install it keeps these tables until the user restarts it.
|
|
1472
1464
|
|
|
1473
1465
|
|
|
1474
1466
|
class PricingSnapshot:
|
|
1475
1467
|
"""One complete, immutable pricing revision.
|
|
1476
1468
|
|
|
1477
|
-
Every pricing-affecting value lives here — not only the two tables
|
|
1478
|
-
tier
|
|
1479
|
-
|
|
1480
|
-
revision, which is the same defect as a stale table and harder to see.
|
|
1469
|
+
Every pricing-affecting value lives here — not only the two tables: both
|
|
1470
|
+
tier thresholds, the Codex fallback model, the aliases, the cache-write
|
|
1471
|
+
multiplier and the fast multipliers.
|
|
1481
1472
|
|
|
1482
1473
|
Hand-written frozen rather than ``@dataclasses.dataclass(frozen=True)``,
|
|
1483
1474
|
for the reason `PricingFingerprintObservation` above states at length:
|
|
@@ -1522,81 +1513,6 @@ class PricingSnapshot:
|
|
|
1522
1513
|
f"codex_models={len(self.codex_pricing)})")
|
|
1523
1514
|
|
|
1524
1515
|
|
|
1525
|
-
#: The complete inventory of import-time copies, made explicit so it can be
|
|
1526
|
-
#: tested rather than asserted. `(module name, attribute, field accessor)`.
|
|
1527
|
-
#: `bin/cctally` copies the tables at import and both dashboard source modules
|
|
1528
|
-
#: read those copies, so a swap that missed one would price from a stale table
|
|
1529
|
-
#: with the refusal latch already cleared.
|
|
1530
|
-
PRICING_EXPORT_BINDINGS = (
|
|
1531
|
-
("_lib_pricing", "PRICING_SNAPSHOT_DATE", lambda s: s.snapshot_date),
|
|
1532
|
-
("_lib_pricing", "CLAUDE_MODEL_PRICING", lambda s: s.claude_pricing),
|
|
1533
|
-
("_lib_pricing", "CODEX_MODEL_PRICING", lambda s: s.codex_pricing),
|
|
1534
|
-
("_lib_pricing", "CODEX_MODEL_ALIASES", lambda s: s.aliases),
|
|
1535
|
-
("_lib_pricing", "TIERED_THRESHOLD",
|
|
1536
|
-
lambda s: s.tier_thresholds["claude"]),
|
|
1537
|
-
("_lib_pricing", "CODEX_TIERED_THRESHOLD",
|
|
1538
|
-
lambda s: s.tier_thresholds["codex"]),
|
|
1539
|
-
("_lib_pricing", "CODEX_LEGACY_FALLBACK_MODEL", lambda s: s.fallback_model),
|
|
1540
|
-
("_lib_pricing", "CACHE_WRITE_1H_MULTIPLIER",
|
|
1541
|
-
lambda s: s.cache_write_1h_multiplier),
|
|
1542
|
-
("_lib_pricing", "CLAUDE_FAST_MULTIPLIER_OVERRIDES",
|
|
1543
|
-
lambda s: s.fast_multipliers["claude"]),
|
|
1544
|
-
("_lib_pricing", "CODEX_FAST_MULTIPLIER_OVERRIDES",
|
|
1545
|
-
lambda s: s.fast_multipliers["codex"]),
|
|
1546
|
-
("_lib_pricing", "CODEX_FAST_MULTIPLIER_FALLBACK",
|
|
1547
|
-
lambda s: s.fast_multipliers["codex_fallback"]),
|
|
1548
|
-
("cctally", "PRICING_SNAPSHOT_DATE", lambda s: s.snapshot_date),
|
|
1549
|
-
("cctally", "CLAUDE_MODEL_PRICING", lambda s: s.claude_pricing),
|
|
1550
|
-
("cctally", "CODEX_MODEL_PRICING", lambda s: s.codex_pricing),
|
|
1551
|
-
("cctally", "TIERED_THRESHOLD", lambda s: s.tier_thresholds["claude"]),
|
|
1552
|
-
("cctally", "CODEX_TIERED_THRESHOLD",
|
|
1553
|
-
lambda s: s.tier_thresholds["codex"]),
|
|
1554
|
-
("cctally", "CODEX_LEGACY_FALLBACK_MODEL", lambda s: s.fallback_model),
|
|
1555
|
-
("cctally", "CACHE_WRITE_1H_MULTIPLIER",
|
|
1556
|
-
lambda s: s.cache_write_1h_multiplier),
|
|
1557
|
-
("cctally", "CLAUDE_FAST_MULTIPLIER_OVERRIDES",
|
|
1558
|
-
lambda s: s.fast_multipliers["claude"]),
|
|
1559
|
-
("cctally", "CODEX_FAST_MULTIPLIER_OVERRIDES",
|
|
1560
|
-
lambda s: s.fast_multipliers["codex"]),
|
|
1561
|
-
("cctally", "CODEX_FAST_MULTIPLIER_FALLBACK",
|
|
1562
|
-
lambda s: s.fast_multipliers["codex_fallback"]),
|
|
1563
|
-
# `_cctally_cache` binds the date as a module global on purpose, so a test
|
|
1564
|
-
# can monkeypatch it; that affordance is preserved and the binding is
|
|
1565
|
-
# simply refreshed here too.
|
|
1566
|
-
("_cctally_cache", "PRICING_SNAPSHOT_DATE", lambda s: s.snapshot_date),
|
|
1567
|
-
# `_lib_cache_report` unpacks the multiplier and the Claude tier threshold
|
|
1568
|
-
# at import. The threshold used to be a literal `200_000` in that file with
|
|
1569
|
-
# nothing keeping it equal to `TIERED_THRESHOLD` here, which made it a
|
|
1570
|
-
# pricing value no reload could replace; it is read from this module now
|
|
1571
|
-
# and refreshed through this entry.
|
|
1572
|
-
("_lib_cache_report", "CACHE_WRITE_1H_MULTIPLIER",
|
|
1573
|
-
lambda s: s.cache_write_1h_multiplier),
|
|
1574
|
-
("_lib_cache_report", "DEFAULT_TIERED_THRESHOLD",
|
|
1575
|
-
lambda s: s.tier_thresholds["claude"]),
|
|
1576
|
-
)
|
|
1577
|
-
|
|
1578
|
-
#: The names `read_pricing_snapshot_from_source` requires, each mapped to the
|
|
1579
|
-
#: `PricingSnapshot` field it fills. A missing OR non-literal name rejects the
|
|
1580
|
-
#: whole candidate.
|
|
1581
|
-
_CANDIDATE_ASSIGNMENTS = (
|
|
1582
|
-
"PRICING_SNAPSHOT_DATE",
|
|
1583
|
-
"CLAUDE_MODEL_PRICING",
|
|
1584
|
-
"CODEX_MODEL_PRICING",
|
|
1585
|
-
"CODEX_MODEL_ALIASES",
|
|
1586
|
-
"TIERED_THRESHOLD",
|
|
1587
|
-
"CODEX_TIERED_THRESHOLD",
|
|
1588
|
-
"CODEX_LEGACY_FALLBACK_MODEL",
|
|
1589
|
-
"CACHE_WRITE_1H_MULTIPLIER",
|
|
1590
|
-
"CLAUDE_FAST_MULTIPLIER_OVERRIDES",
|
|
1591
|
-
"CODEX_FAST_MULTIPLIER_OVERRIDES",
|
|
1592
|
-
"CODEX_FAST_MULTIPLIER_FALLBACK",
|
|
1593
|
-
)
|
|
1594
|
-
|
|
1595
|
-
_PRICING_PUBLISH_LOCK = _threading.Lock()
|
|
1596
|
-
_PRICING_CONTEXT = _contextvars.ContextVar("cctally_pricing_snapshot",
|
|
1597
|
-
default=None)
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
1516
|
def _snapshot_from_module_globals() -> PricingSnapshot:
|
|
1601
1517
|
return PricingSnapshot(
|
|
1602
1518
|
snapshot_date=PRICING_SNAPSHOT_DATE,
|
|
@@ -1619,161 +1535,6 @@ _LIVE_PRICING_SNAPSHOT = _snapshot_from_module_globals()
|
|
|
1619
1535
|
|
|
1620
1536
|
|
|
1621
1537
|
def current_pricing_snapshot() -> PricingSnapshot:
|
|
1622
|
-
"""The snapshot
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
with, so a request, a dashboard snapshot build or a sync pass already
|
|
1626
|
-
under way is never assembled from two revisions. Everyone else reads the
|
|
1627
|
-
live pointer, which is one immutable object replaced by one assignment.
|
|
1628
|
-
"""
|
|
1629
|
-
captured = _PRICING_CONTEXT.get()
|
|
1630
|
-
return _LIVE_PRICING_SNAPSHOT if captured is None else captured
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
@_contextlib.contextmanager
|
|
1634
|
-
def pricing_snapshot_context(snapshot: "PricingSnapshot | None" = None):
|
|
1635
|
-
"""Pin one revision for the duration of a request, build or sync pass."""
|
|
1636
|
-
pinned = current_pricing_snapshot() if snapshot is None else snapshot
|
|
1637
|
-
token = _PRICING_CONTEXT.set(pinned)
|
|
1638
|
-
try:
|
|
1639
|
-
yield pinned
|
|
1640
|
-
finally:
|
|
1641
|
-
_PRICING_CONTEXT.reset(token)
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
def publish_pricing_snapshot(snapshot: PricingSnapshot) -> None:
|
|
1645
|
-
"""Swap the live pointer and refresh every import-time copy.
|
|
1646
|
-
|
|
1647
|
-
The pointer swap is one assignment of one frozen object, so a concurrent
|
|
1648
|
-
reader observes the old revision whole or the new one whole. The binding
|
|
1649
|
-
refresh that follows exists for call sites that still read a copied name;
|
|
1650
|
-
every cost kernel in this module reads `current_pricing_snapshot()`
|
|
1651
|
-
instead, so correctness does not depend on the refresh reaching a module
|
|
1652
|
-
that has not been imported.
|
|
1653
|
-
"""
|
|
1654
|
-
global _LIVE_PRICING_SNAPSHOT
|
|
1655
|
-
with _PRICING_PUBLISH_LOCK:
|
|
1656
|
-
_LIVE_PRICING_SNAPSHOT = snapshot
|
|
1657
|
-
for module_name, attr, accessor in PRICING_EXPORT_BINDINGS:
|
|
1658
|
-
module = _sys.modules.get(module_name)
|
|
1659
|
-
if module is None:
|
|
1660
|
-
continue
|
|
1661
|
-
try:
|
|
1662
|
-
setattr(module, attr, accessor(snapshot))
|
|
1663
|
-
except Exception: # noqa: BLE001
|
|
1664
|
-
# A module that refuses an attribute set must not stop the
|
|
1665
|
-
# rest of the inventory being refreshed.
|
|
1666
|
-
_eprint(f"[pricing] could not refresh {module_name}.{attr}")
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
def adopt_pricing_candidate(candidate: PricingSnapshot, *,
|
|
1670
|
-
force: bool = False) -> bool:
|
|
1671
|
-
"""Publish `candidate` iff it is strictly newer than the live snapshot.
|
|
1672
|
-
|
|
1673
|
-
`_lib_pricing` documents that a pricing revision always ADVANCES
|
|
1674
|
-
`PRICING_SNAPSHOT_DATE`, so a candidate that does not is a rollback, a
|
|
1675
|
-
corrupt read or the same revision read twice; each of those is a reason to
|
|
1676
|
-
keep what is running. `force` exists for tests restoring a captured
|
|
1677
|
-
snapshot and for nothing else.
|
|
1678
|
-
"""
|
|
1679
|
-
if force:
|
|
1680
|
-
publish_pricing_snapshot(candidate)
|
|
1681
|
-
return True
|
|
1682
|
-
live = _LIVE_PRICING_SNAPSHOT
|
|
1683
|
-
new_date = parse_pricing_fingerprint(candidate.snapshot_date)
|
|
1684
|
-
live_date = parse_pricing_fingerprint(live.snapshot_date)
|
|
1685
|
-
if new_date is None:
|
|
1686
|
-
return False
|
|
1687
|
-
if live_date is not None and new_date <= live_date:
|
|
1688
|
-
return False
|
|
1689
|
-
publish_pricing_snapshot(candidate)
|
|
1690
|
-
return True
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
def _file_signature(path) -> tuple:
|
|
1694
|
-
st = path.stat()
|
|
1695
|
-
return (st.st_dev, st.st_ino, st.st_size, st.st_mtime_ns)
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
def read_pricing_snapshot_from_source(path) -> PricingSnapshot:
|
|
1699
|
-
"""Build a candidate from the literal assignments in a pricing file.
|
|
1700
|
-
|
|
1701
|
-
`ast.parse` plus `ast.literal_eval`, never `importlib.reload` and never
|
|
1702
|
-
`exec`: the file is one an installer has just written and this process
|
|
1703
|
-
must not run it.
|
|
1704
|
-
|
|
1705
|
-
Raises `PricingCandidateRejected` — never returns a partial snapshot — on
|
|
1706
|
-
a missing name, a name whose value is not a literal, a date that does not
|
|
1707
|
-
parse, or a file whose inode/size/mtime signature changes while it is
|
|
1708
|
-
being read, which is an installer replacing it mid-parse.
|
|
1709
|
-
"""
|
|
1710
|
-
import ast
|
|
1711
|
-
import pathlib as _pathlib
|
|
1712
|
-
|
|
1713
|
-
path = _pathlib.Path(path)
|
|
1714
|
-
try:
|
|
1715
|
-
before = _file_signature(path)
|
|
1716
|
-
source = path.read_bytes()
|
|
1717
|
-
after = _file_signature(path)
|
|
1718
|
-
except OSError as exc:
|
|
1719
|
-
raise PricingCandidateRejected(
|
|
1720
|
-
f"pricing candidate could not be read: {exc}") from exc
|
|
1721
|
-
if before != after:
|
|
1722
|
-
raise PricingCandidateRejected(
|
|
1723
|
-
"pricing candidate signature changed while it was being read")
|
|
1724
|
-
|
|
1725
|
-
try:
|
|
1726
|
-
tree = ast.parse(source.decode("utf-8"), filename=str(path))
|
|
1727
|
-
except (SyntaxError, UnicodeDecodeError) as exc:
|
|
1728
|
-
raise PricingCandidateRejected(
|
|
1729
|
-
f"pricing candidate does not parse: {exc}") from exc
|
|
1730
|
-
|
|
1731
|
-
found: "dict[str, Any]" = {}
|
|
1732
|
-
for node in tree.body:
|
|
1733
|
-
targets = []
|
|
1734
|
-
if isinstance(node, ast.Assign):
|
|
1735
|
-
targets = [t for t in node.targets if isinstance(t, ast.Name)]
|
|
1736
|
-
value = node.value
|
|
1737
|
-
elif isinstance(node, ast.AnnAssign) and isinstance(node.target,
|
|
1738
|
-
ast.Name):
|
|
1739
|
-
targets = [node.target]
|
|
1740
|
-
value = node.value
|
|
1741
|
-
else:
|
|
1742
|
-
continue
|
|
1743
|
-
for target in targets:
|
|
1744
|
-
if target.id not in _CANDIDATE_ASSIGNMENTS or value is None:
|
|
1745
|
-
continue
|
|
1746
|
-
try:
|
|
1747
|
-
found[target.id] = ast.literal_eval(value)
|
|
1748
|
-
except (ValueError, SyntaxError, TypeError) as exc:
|
|
1749
|
-
raise PricingCandidateRejected(
|
|
1750
|
-
f"{target.id} is no longer a literal, so the in-process "
|
|
1751
|
-
f"pricing reload cannot read it: {exc}") from exc
|
|
1752
|
-
|
|
1753
|
-
missing = [name for name in _CANDIDATE_ASSIGNMENTS if name not in found]
|
|
1754
|
-
if missing:
|
|
1755
|
-
raise PricingCandidateRejected(
|
|
1756
|
-
"pricing candidate is incomplete; adopting a partial revision "
|
|
1757
|
-
"would clear the write refusal and price from the old tables. "
|
|
1758
|
-
f"Missing: {', '.join(missing)}")
|
|
1759
|
-
|
|
1760
|
-
if parse_pricing_fingerprint(found["PRICING_SNAPSHOT_DATE"]) is None:
|
|
1761
|
-
raise PricingCandidateRejected(
|
|
1762
|
-
"pricing candidate has no orderable PRICING_SNAPSHOT_DATE: "
|
|
1763
|
-
f"{found['PRICING_SNAPSHOT_DATE']!r}")
|
|
1764
|
-
|
|
1765
|
-
return PricingSnapshot(
|
|
1766
|
-
snapshot_date=found["PRICING_SNAPSHOT_DATE"],
|
|
1767
|
-
claude_pricing=found["CLAUDE_MODEL_PRICING"],
|
|
1768
|
-
codex_pricing=found["CODEX_MODEL_PRICING"],
|
|
1769
|
-
aliases=found["CODEX_MODEL_ALIASES"],
|
|
1770
|
-
tier_thresholds={"claude": found["TIERED_THRESHOLD"],
|
|
1771
|
-
"codex": found["CODEX_TIERED_THRESHOLD"]},
|
|
1772
|
-
fallback_model=found["CODEX_LEGACY_FALLBACK_MODEL"],
|
|
1773
|
-
cache_write_1h_multiplier=found["CACHE_WRITE_1H_MULTIPLIER"],
|
|
1774
|
-
fast_multipliers={
|
|
1775
|
-
"claude": found["CLAUDE_FAST_MULTIPLIER_OVERRIDES"],
|
|
1776
|
-
"codex": found["CODEX_FAST_MULTIPLIER_OVERRIDES"],
|
|
1777
|
-
"codex_fallback": found["CODEX_FAST_MULTIPLIER_FALLBACK"],
|
|
1778
|
-
},
|
|
1779
|
-
)
|
|
1538
|
+
"""The snapshot every cost kernel prices from; it never changes after
|
|
1539
|
+
import."""
|
|
1540
|
+
return _LIVE_PRICING_SNAPSHOT
|