cctally 1.102.0 → 1.103.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 +1218 -1389
- package/README.md +3 -3
- package/bin/_cctally_cache.py +31 -0
- package/bin/_lib_jsonl.py +102 -5
- package/bin/_lib_pricing.py +97 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,9 +34,9 @@ Your Claude Code plan meters you with a percentage that creeps up all week. ccta
|
|
|
34
34
|
|
|
35
35
|
Highlights from the `v1.95.5` to `v1.101.0` stable upgrade:
|
|
36
36
|
|
|
37
|
-
- Dashboard refreshes
|
|
38
|
-
- Every dashboard warning
|
|
39
|
-
- The dashboard's Projects table
|
|
37
|
+
- Dashboard refreshes release their read lock on `cache.db` sooner, cutting the measured median hold from 3.40 seconds to 1.04. The published data is unchanged.
|
|
38
|
+
- Every dashboard warning carries a button that opens the surface explaining it, the week, the five-hour block, the month, the project, or the forecast. A warning whose window has closed says so and opens nothing.
|
|
39
|
+
- The dashboard's Projects table writes out what its two percentages mean under the week selector, where a phone can read it. `Used pp` is relabelled `Used pp (sum)`, and the caption names the denominator of `Cost share`.
|
|
40
40
|
|
|
41
41
|
[See every change in this stable upgrade](https://github.com/omrikais/cctally/releases/tag/v1.101.0)
|
|
42
42
|
<!-- cctally:latest-stable:end -->
|
package/bin/_cctally_cache.py
CHANGED
|
@@ -7327,6 +7327,20 @@ def sync_codex_cache(
|
|
|
7327
7327
|
model=initial_model,
|
|
7328
7328
|
total_tokens=initial_total_tokens,
|
|
7329
7329
|
)
|
|
7330
|
+
if start_offset > 0 and not truncated and not requalified:
|
|
7331
|
+
prior_accounting = conn.execute(
|
|
7332
|
+
"SELECT MAX(timestamp_utc),MAX(line_offset) "
|
|
7333
|
+
"FROM codex_session_entries "
|
|
7334
|
+
"WHERE source_path=? AND line_offset<? ",
|
|
7335
|
+
(path_str, start_offset),
|
|
7336
|
+
).fetchone()
|
|
7337
|
+
if prior_accounting is not None and prior_accounting[0] is not None:
|
|
7338
|
+
iter_state.last_accounting_timestamp = _parse_anchor_iso(
|
|
7339
|
+
prior_accounting[0])
|
|
7340
|
+
if prior_accounting is not None and prior_accounting[1] is not None:
|
|
7341
|
+
iter_state.generation_transition_pending = (
|
|
7342
|
+
_lib_jsonl._codex_generation_transition_between(
|
|
7343
|
+
path_str, int(prior_accounting[1]), start_offset))
|
|
7330
7344
|
if (
|
|
7331
7345
|
prev is not None and not truncated and not requalified
|
|
7332
7346
|
and prev_native_thread_id is not None
|
|
@@ -11223,6 +11237,23 @@ def sync_codex_conversations(
|
|
|
11223
11237
|
model=initial_model,
|
|
11224
11238
|
total_tokens=initial_total_tokens,
|
|
11225
11239
|
)
|
|
11240
|
+
if start_offset > 0 and not reset_file:
|
|
11241
|
+
try:
|
|
11242
|
+
prior_accounting = conn.execute(
|
|
11243
|
+
"SELECT MAX(timestamp_utc),MAX(line_offset) FROM "
|
|
11244
|
+
"cache_db.codex_session_entries "
|
|
11245
|
+
"WHERE source_path=? AND line_offset<? ",
|
|
11246
|
+
(path_str, start_offset),
|
|
11247
|
+
).fetchone()
|
|
11248
|
+
except sqlite3.OperationalError:
|
|
11249
|
+
prior_accounting = None
|
|
11250
|
+
if prior_accounting is not None and prior_accounting[0] is not None:
|
|
11251
|
+
state.last_accounting_timestamp = _parse_anchor_iso(
|
|
11252
|
+
prior_accounting[0])
|
|
11253
|
+
if prior_accounting is not None and prior_accounting[1] is not None:
|
|
11254
|
+
state.generation_transition_pending = (
|
|
11255
|
+
_lib_jsonl._codex_generation_transition_between(
|
|
11256
|
+
path_str, int(prior_accounting[1]), start_offset))
|
|
11226
11257
|
if initial_native and initial_root:
|
|
11227
11258
|
state.thread = _lib_jsonl.CodexThreadMetadata(
|
|
11228
11259
|
source_root_key=discovered.source_root_key,
|
package/bin/_lib_jsonl.py
CHANGED
|
@@ -381,6 +381,19 @@ class _CodexIterState:
|
|
|
381
381
|
# watermark by construction, and the caller persists exactly it (replacing
|
|
382
382
|
# the old reconstructed initial+Σ(per-turn) sum, which could diverge).
|
|
383
383
|
total_tokens: int = 0
|
|
384
|
+
# #647: provider time of the last accepted accounting event. The caller
|
|
385
|
+
# seeds this from the retained row immediately before a delta cursor, and
|
|
386
|
+
# the iterator advances it on every accepted accounting record. A lower
|
|
387
|
+
# cumulative can start a new producer generation only when its timestamp
|
|
388
|
+
# advances this independent chronological watermark; copied or
|
|
389
|
+
# out-of-order history at a new byte offset therefore cannot lower the
|
|
390
|
+
# cumulative watermark and make the copied suffix count twice.
|
|
391
|
+
last_accounting_timestamp: dt.datetime | None = None
|
|
392
|
+
# A provider lifecycle record (turn_context/task_started) observed since
|
|
393
|
+
# the last accepted accounting row. Token-count re-emissions cannot set
|
|
394
|
+
# this bit themselves, so it is the producer-transition evidence required
|
|
395
|
+
# before a restart-shaped lower cumulative may replace the watermark.
|
|
396
|
+
generation_transition_pending: bool = False
|
|
384
397
|
# #279 S2 F1 parse-health counters — per-iterator-call; sync_codex_cache
|
|
385
398
|
# folds them into CodexIngestStats after each file drains. Reason
|
|
386
399
|
# vocabulary: info-non-dict / no-last-token-usage / bad-timestamp /
|
|
@@ -869,6 +882,57 @@ def _reject_nonfinite_json_constant(value: str) -> None:
|
|
|
869
882
|
raise ValueError(f"non-finite JSON constant: {value}")
|
|
870
883
|
|
|
871
884
|
|
|
885
|
+
def _codex_generation_transition_between(
|
|
886
|
+
path_str: str, after_offset: int, before_offset: int,
|
|
887
|
+
) -> bool:
|
|
888
|
+
"""Return whether a producer lifecycle boundary exists in this byte gap.
|
|
889
|
+
|
|
890
|
+
``after_offset`` is the physical row of the last retained accounting
|
|
891
|
+
event, so that row is skipped. ``before_offset`` is a durable resume cursor
|
|
892
|
+
and is never consumed. This reconstructs the one bit of parser state a
|
|
893
|
+
metadata-only or budgeted pass can leave between the last accounting row
|
|
894
|
+
and the next token event without adding another cache schema column.
|
|
895
|
+
Malformed or unreadable evidence fails closed.
|
|
896
|
+
"""
|
|
897
|
+
if after_offset < 0 or before_offset <= after_offset:
|
|
898
|
+
return False
|
|
899
|
+
try:
|
|
900
|
+
with open(path_str, "rb") as fh:
|
|
901
|
+
fh.seek(after_offset)
|
|
902
|
+
fh.readline() # skip the retained accounting record itself
|
|
903
|
+
while fh.tell() < before_offset:
|
|
904
|
+
line_offset = fh.tell()
|
|
905
|
+
line = fh.readline()
|
|
906
|
+
if not line or not line.endswith(b"\n"):
|
|
907
|
+
return False
|
|
908
|
+
if fh.tell() > before_offset:
|
|
909
|
+
return False
|
|
910
|
+
try:
|
|
911
|
+
obj = json.loads(
|
|
912
|
+
line.decode("utf-8"),
|
|
913
|
+
parse_constant=_reject_nonfinite_json_constant,
|
|
914
|
+
)
|
|
915
|
+
except (UnicodeDecodeError, json.JSONDecodeError, ValueError, TypeError):
|
|
916
|
+
continue
|
|
917
|
+
if not isinstance(obj, dict) or not _json_value_is_finite(obj):
|
|
918
|
+
continue
|
|
919
|
+
payload = (
|
|
920
|
+
obj.get("payload")
|
|
921
|
+
if isinstance(obj.get("payload"), dict)
|
|
922
|
+
else {}
|
|
923
|
+
)
|
|
924
|
+
if obj.get("type") == "turn_context" or (
|
|
925
|
+
obj.get("type") == "event_msg"
|
|
926
|
+
and payload.get("type") == "task_started"
|
|
927
|
+
):
|
|
928
|
+
return True
|
|
929
|
+
if fh.tell() <= line_offset: # defensive progress invariant
|
|
930
|
+
return False
|
|
931
|
+
except OSError:
|
|
932
|
+
return False
|
|
933
|
+
return False
|
|
934
|
+
|
|
935
|
+
|
|
872
936
|
def _seed_codex_iter_state(
|
|
873
937
|
state: _CodexIterState, initial_session_id: str | None,
|
|
874
938
|
initial_model: str | None, initial_total_tokens: int,
|
|
@@ -934,6 +998,11 @@ def _accounting_from_record(
|
|
|
934
998
|
and state.thread.parent_thread_id is not None):
|
|
935
999
|
return None, last_total_tokens, filename_session_id_warned
|
|
936
1000
|
|
|
1001
|
+
timestamp = _parse_codex_timestamp(obj.get("timestamp"))
|
|
1002
|
+
if timestamp is None:
|
|
1003
|
+
_codex_skip(state, "bad-timestamp")
|
|
1004
|
+
return None, last_total_tokens, filename_session_id_warned
|
|
1005
|
+
|
|
937
1006
|
total_token_usage = info.get("total_token_usage")
|
|
938
1007
|
if isinstance(total_token_usage, dict):
|
|
939
1008
|
try:
|
|
@@ -941,14 +1010,33 @@ def _accounting_from_record(
|
|
|
941
1010
|
except (TypeError, ValueError):
|
|
942
1011
|
cumulative = 0
|
|
943
1012
|
if cumulative <= last_total_tokens:
|
|
944
|
-
|
|
1013
|
+
# A continued Codex producer can append to the same rollout while
|
|
1014
|
+
# restarting its cumulative counter at zero. The first record of
|
|
1015
|
+
# that new generation is self-originating: its cumulative total is
|
|
1016
|
+
# exactly the complete provider-native last-turn total. A distinct
|
|
1017
|
+
# producer lifecycle record must have occurred since the last
|
|
1018
|
+
# accepted accounting row, and this event must also advance the
|
|
1019
|
+
# retained maximum timestamp. Token-count re-emissions cannot
|
|
1020
|
+
# manufacture the lifecycle evidence, even when they carry fresh
|
|
1021
|
+
# timestamps. Equal totals remain duplicates, and arbitrary or
|
|
1022
|
+
# non-monotonic lower totals remain noise.
|
|
1023
|
+
try:
|
|
1024
|
+
last_usage_total = int(last_token_usage.get("total_tokens") or 0)
|
|
1025
|
+
except (TypeError, ValueError):
|
|
1026
|
+
last_usage_total = 0
|
|
1027
|
+
starts_generation = (
|
|
1028
|
+
state.generation_transition_pending
|
|
1029
|
+
and cumulative < last_total_tokens
|
|
1030
|
+
and cumulative > 0
|
|
1031
|
+
and cumulative == last_usage_total
|
|
1032
|
+
and state.last_accounting_timestamp is not None
|
|
1033
|
+
and timestamp > state.last_accounting_timestamp
|
|
1034
|
+
)
|
|
1035
|
+
if not starts_generation:
|
|
1036
|
+
return None, last_total_tokens, filename_session_id_warned
|
|
945
1037
|
else:
|
|
946
1038
|
cumulative = None
|
|
947
1039
|
|
|
948
|
-
timestamp = _parse_codex_timestamp(obj.get("timestamp"))
|
|
949
|
-
if timestamp is None:
|
|
950
|
-
_codex_skip(state, "bad-timestamp")
|
|
951
|
-
return None, last_total_tokens, filename_session_id_warned
|
|
952
1040
|
session_id = state.session_id
|
|
953
1041
|
if session_id is None:
|
|
954
1042
|
session_id = filename_uuid
|
|
@@ -982,6 +1070,12 @@ def _accounting_from_record(
|
|
|
982
1070
|
if cumulative is not None:
|
|
983
1071
|
state.total_tokens = cumulative
|
|
984
1072
|
last_total_tokens = cumulative
|
|
1073
|
+
if (
|
|
1074
|
+
state.last_accounting_timestamp is None
|
|
1075
|
+
or timestamp > state.last_accounting_timestamp
|
|
1076
|
+
):
|
|
1077
|
+
state.last_accounting_timestamp = timestamp
|
|
1078
|
+
state.generation_transition_pending = False
|
|
985
1079
|
return entry, last_total_tokens, filename_session_id_warned
|
|
986
1080
|
|
|
987
1081
|
|
|
@@ -1076,6 +1170,9 @@ def _iter_codex_fused_records_with_offsets(
|
|
|
1076
1170
|
model = _codex_string(payload.get("model"))
|
|
1077
1171
|
if model is not None:
|
|
1078
1172
|
state.model = model.strip()
|
|
1173
|
+
state.generation_transition_pending = True
|
|
1174
|
+
elif obj.get("type") == "event_msg" and payload.get("type") == "task_started":
|
|
1175
|
+
state.generation_transition_pending = True
|
|
1079
1176
|
|
|
1080
1177
|
event = _event_from_record(
|
|
1081
1178
|
obj, path_str, line_offset, source_root_key, state.thread
|
package/bin/_lib_pricing.py
CHANGED
|
@@ -53,7 +53,7 @@ def _chip_for_model(name: str) -> str:
|
|
|
53
53
|
# Date the embedded pricing snapshots below were last verified against
|
|
54
54
|
# vendor sources. Bump whenever CLAUDE_MODEL_PRICING / CODEX_MODEL_PRICING
|
|
55
55
|
# is synced. Read by `pricing-check` + the release pre-flight staleness nudge.
|
|
56
|
-
PRICING_SNAPSHOT_DATE = "2026-08-
|
|
56
|
+
PRICING_SNAPSHOT_DATE = "2026-08-25"
|
|
57
57
|
PRICING_STALENESS_DAYS = 60 # release pre-flight WARNs past this age
|
|
58
58
|
|
|
59
59
|
# Canonical machine-readable pricing source (Claude values + Codex values).
|
|
@@ -73,6 +73,22 @@ LITELLM_PRICES_URL = (
|
|
|
73
73
|
# currently mirrors successor Mythos 5's lower $10/$50 rate onto the Preview
|
|
74
74
|
# identifier. Retained Preview rows therefore keep the explicit historical
|
|
75
75
|
# rate rather than being rewritten to the successor's rate.
|
|
76
|
+
#
|
|
77
|
+
# gpt-5.6-sol (#643): OpenAI's pricing page states that "GPT-5.6 Sol's
|
|
78
|
+
# promotional pricing is available at least through November 21, 2026" and
|
|
79
|
+
# publishes no post-promotional price, so LiteLLM's $4/$20 per MTok rate is
|
|
80
|
+
# time-boxed. This table is date-blind and roughly half the retained Sol rows
|
|
81
|
+
# predate the promotion, so the pre-promotional $5/$30 card is kept and the
|
|
82
|
+
# promotion is suppressed here. `expires` is what stops the divergence
|
|
83
|
+
# ossifying: `stale_allowlist_entries` only fires if LiteLLM reverts, so a
|
|
84
|
+
# promotion made permanent would otherwise never surface.
|
|
85
|
+
#
|
|
86
|
+
# gpt-5.6 (#643): a model-only entry, which suppresses `missing_from_us` rather
|
|
87
|
+
# than a value drift. OpenAI lists `gpt-5.6` as an alias of `gpt-5.6-sol`, so
|
|
88
|
+
# CODEX_MODEL_ALIASES resolves it instead of duplicating the rate card.
|
|
89
|
+
# `diff_pricing` keys on raw table membership and is deliberately NOT
|
|
90
|
+
# alias-aware, so the omission needs this entry. It is self-policing: it goes
|
|
91
|
+
# stale automatically if upstream drops `gpt-5.6` or we restore a direct card.
|
|
76
92
|
PRICING_DRIFT_ALLOWLIST: list[dict] = [
|
|
77
93
|
{
|
|
78
94
|
"model": "claude-mythos-preview",
|
|
@@ -90,6 +106,39 @@ PRICING_DRIFT_ALLOWLIST: list[dict] = [
|
|
|
90
106
|
"cache_creation_input_token_cost",
|
|
91
107
|
"cache_read_input_token_cost",
|
|
92
108
|
)
|
|
109
|
+
] + [
|
|
110
|
+
{
|
|
111
|
+
"model": "gpt-5.6-sol",
|
|
112
|
+
"field": field,
|
|
113
|
+
"expires": "2026-11-21",
|
|
114
|
+
"reason": (
|
|
115
|
+
"OpenAI's pricing page guarantees GPT-5.6 Sol's promotional "
|
|
116
|
+
"$4/$20 per MTok rate only 'at least through November 21, 2026' "
|
|
117
|
+
"and publishes no post-promotional price. This table is "
|
|
118
|
+
"date-blind, so the pre-promotional $5/$30 card is kept rather "
|
|
119
|
+
"than adopting a rate that would have to be reverted by hand "
|
|
120
|
+
"when the promotion ends (#643)."
|
|
121
|
+
),
|
|
122
|
+
}
|
|
123
|
+
for field in (
|
|
124
|
+
"input_cost_per_token",
|
|
125
|
+
"cache_read_input_token_cost",
|
|
126
|
+
"output_cost_per_token",
|
|
127
|
+
"input_cost_per_token_above_272k_tokens",
|
|
128
|
+
"cache_read_input_token_cost_above_272k_tokens",
|
|
129
|
+
"output_cost_per_token_above_272k_tokens",
|
|
130
|
+
)
|
|
131
|
+
] + [
|
|
132
|
+
{
|
|
133
|
+
"model": "gpt-5.6",
|
|
134
|
+
"reason": (
|
|
135
|
+
"OpenAI lists gpt-5.6 as an alias of gpt-5.6-sol, so "
|
|
136
|
+
"CODEX_MODEL_ALIASES resolves it to Sol's card and this table "
|
|
137
|
+
"carries no duplicate entry. `diff_pricing` is deliberately not "
|
|
138
|
+
"alias-aware, so the intentional omission is suppressed here "
|
|
139
|
+
"(#643)."
|
|
140
|
+
),
|
|
141
|
+
},
|
|
93
142
|
]
|
|
94
143
|
|
|
95
144
|
# Anthropic API pricing snapshot:
|
|
@@ -398,10 +447,11 @@ _unknown_model_warnings: set[str] = set()
|
|
|
398
447
|
# Codex (OpenAI) API pricing snapshot:
|
|
399
448
|
# - Source: https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json
|
|
400
449
|
# - Captured: 2026-07-19 — the last FULL Codex sync. PRICING_SNAPSHOT_DATE has
|
|
401
|
-
# since moved for
|
|
402
|
-
# sync; 2026-07-31, the gpt-5.6-terra/-luna correction logged below;
|
|
403
|
-
# 2026-08-13, the Claude-side Sonnet/Mythos sync above
|
|
404
|
-
#
|
|
450
|
+
# since moved for four targeted syncs (2026-07-24, the Claude-side opus-5
|
|
451
|
+
# sync; 2026-07-31, the gpt-5.6-terra/-luna correction logged below;
|
|
452
|
+
# 2026-08-13, the Claude-side Sonnet/Mythos sync above; and 2026-08-25, the
|
|
453
|
+
# gpt-5.6-cyber addition logged below). Codex values outside those two Codex
|
|
454
|
+
# corrections were NOT re-verified on those days.
|
|
405
455
|
# - As of the 2026-07-19 sync this carries every openai-provider
|
|
406
456
|
# gpt-5* model the LiteLLM snapshot lists, so `pricing-check`'s scope finds
|
|
407
457
|
# nothing missing. Models absent from this table still fall back to `gpt-5`
|
|
@@ -425,6 +475,21 @@ _unknown_model_warnings: set[str] = set()
|
|
|
425
475
|
# the vendor lists these as standard ongoing prices with no promotional or
|
|
426
476
|
# expiring annotation, so the durable rate is the cut rate. gpt-5.6 and
|
|
427
477
|
# gpt-5.6-sol were not repriced and are unchanged.
|
|
478
|
+
# 2026-08-25 (#643): added gpt-5.6-cyber at OpenAI's published $12.50 input /
|
|
479
|
+
# $1.25 cached input / $75.00 output per MTok, with LiteLLM's above-272k tier
|
|
480
|
+
# ($25.00 / $2.50 / $112.50 per MTok). The vendor page shows dashes in the
|
|
481
|
+
# long-context columns, but max_input_tokens is 400,000, so a turn above the
|
|
482
|
+
# 272,000 threshold is reachable and pricing it at the base rate would be
|
|
483
|
+
# knowingly wrong; LiteLLM's tier ratios match every other gpt-5.6 member.
|
|
484
|
+
# Removed the duplicated gpt-5.6 card and aliased that identifier to
|
|
485
|
+
# gpt-5.6-sol, which OpenAI lists it as an alias of. gpt-5.6-sol's own values
|
|
486
|
+
# are UNCHANGED: LiteLLM now carries OpenAI's promotional $4/$20 per MTok
|
|
487
|
+
# rate, which the vendor guarantees only "at least through November 21, 2026"
|
|
488
|
+
# and publishes no successor for. That promotion is suppressed in
|
|
489
|
+
# PRICING_DRIFT_ALLOWLIST with expires 2026-11-21 instead of being written
|
|
490
|
+
# into this date-blind table, because roughly half the retained Sol rows
|
|
491
|
+
# predate it. The accepted cost is that while the promotion runs, Sol
|
|
492
|
+
# reporting is high by 25% on input and 50% on output.
|
|
428
493
|
#
|
|
429
494
|
# Billing rules:
|
|
430
495
|
# - reasoning_output_tokens is billed at the *output* rate (matches
|
|
@@ -518,19 +583,15 @@ CODEX_MODEL_PRICING: dict[str, dict[str, Any]] = {
|
|
|
518
583
|
"output_cost_per_token_above_272k_tokens": 4.5e-05,
|
|
519
584
|
},
|
|
520
585
|
# ── gpt-5.6 family (LiteLLM openai-provider entries) ──
|
|
521
|
-
#
|
|
522
|
-
#
|
|
523
|
-
# gpt-5.5's rate card from the 2026-07-10 sync
|
|
524
|
-
#
|
|
525
|
-
#
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
"input_cost_per_token_above_272k_tokens": 1e-05,
|
|
531
|
-
"cache_read_input_token_cost_above_272k_tokens": 1e-06,
|
|
532
|
-
"output_cost_per_token_above_272k_tokens": 4.5e-05,
|
|
533
|
-
},
|
|
586
|
+
# Every member carries the above-272k tier, but the context windows differ:
|
|
587
|
+
# -sol, -terra and -luna are max_input_tokens 922,000 and -cyber is 400,000.
|
|
588
|
+
# gpt-5.6-sol keeps gpt-5.5's rate card from the 2026-07-10 sync and is
|
|
589
|
+
# deliberately HELD there while OpenAI's promotion runs — see the
|
|
590
|
+
# gpt-5.6-sol block in PRICING_DRIFT_ALLOWLIST (#643). -terra and -luna
|
|
591
|
+
# carry OpenAI's 2026-07-30 post-cut rates (#441) and no longer track
|
|
592
|
+
# gpt-5.4's card or any other model's. -cyber carries its own launch rates.
|
|
593
|
+
# The bare `gpt-5.6` identifier is OpenAI's alias of -sol and has NO card
|
|
594
|
+
# here; CODEX_MODEL_ALIASES resolves it.
|
|
534
595
|
"gpt-5.6-sol": {
|
|
535
596
|
"input_cost_per_token": 5e-06,
|
|
536
597
|
"cache_read_input_token_cost": 5e-07,
|
|
@@ -555,6 +616,16 @@ CODEX_MODEL_PRICING: dict[str, dict[str, Any]] = {
|
|
|
555
616
|
"cache_read_input_token_cost_above_272k_tokens": 4e-08,
|
|
556
617
|
"output_cost_per_token_above_272k_tokens": 1.8e-06,
|
|
557
618
|
},
|
|
619
|
+
# No cache_creation field: the Codex cost kernel never reads one, so
|
|
620
|
+
# carrying LiteLLM's would only give `diff_pricing` a value to compare.
|
|
621
|
+
"gpt-5.6-cyber": {
|
|
622
|
+
"input_cost_per_token": 1.25e-05,
|
|
623
|
+
"cache_read_input_token_cost": 1.25e-06,
|
|
624
|
+
"output_cost_per_token": 7.5e-05,
|
|
625
|
+
"input_cost_per_token_above_272k_tokens": 2.5e-05,
|
|
626
|
+
"cache_read_input_token_cost_above_272k_tokens": 2.5e-06,
|
|
627
|
+
"output_cost_per_token_above_272k_tokens": 1.125e-04,
|
|
628
|
+
},
|
|
558
629
|
# ── Issue #123: full gpt-5.x LiteLLM sync (2026-05-30 snapshot) ──
|
|
559
630
|
# Exact model_prices_and_context_window.json values for every
|
|
560
631
|
# openai-provider gpt-5* model `pricing-check`'s scope flags but the
|
|
@@ -740,11 +811,16 @@ CODEX_LEGACY_FALLBACK_MODEL = "gpt-5"
|
|
|
740
811
|
# drift retains one source of truth per card. OpenAI's live pricing page states
|
|
741
812
|
# that ``daybreak-blue-latest`` currently points to ``gpt-5.6-sol`` and inherits
|
|
742
813
|
# the underlying model's pricing; Codex emits the prefixed runtime identifier
|
|
743
|
-
# retained below.
|
|
744
|
-
#
|
|
745
|
-
#
|
|
814
|
+
# retained below. OpenAI's models page lists the bare ``gpt-5.6`` identifier as
|
|
815
|
+
# an alias of ``gpt-5.6-sol`` too, so it resolves here rather than duplicating
|
|
816
|
+
# Sol's rates (#643) — its intentional absence from CODEX_MODEL_PRICING is
|
|
817
|
+
# covered by a model-only PRICING_DRIFT_ALLOWLIST entry.
|
|
818
|
+
# ``codex-auto-review`` is the hidden Guardian model and maps to the model
|
|
819
|
+
# current when it appeared, covering every retained event observed for issue
|
|
820
|
+
# #535.
|
|
746
821
|
CODEX_MODEL_ALIASES: dict[str, str] = {
|
|
747
822
|
"codex-auto-review": "gpt-5.5",
|
|
823
|
+
"gpt-5.6": "gpt-5.6-sol",
|
|
748
824
|
"gpt-daybreak-blue-latest": "gpt-5.6-sol",
|
|
749
825
|
}
|
|
750
826
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cctally",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.103.0",
|
|
4
4
|
"description": "Claude Code usage tracker and local dashboard for Pro/Max subscription limits - weekly cost-per-percent trend, quota forecasts, threshold alerts. ccusage-compatible.",
|
|
5
5
|
"homepage": "https://github.com/omrikais/cctally",
|
|
6
6
|
"repository": {
|