cctally 1.82.0 → 1.83.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 +70 -0
- package/README.md +52 -74
- package/bin/_cctally_alerts.py +8 -1
- package/bin/_cctally_cache.py +963 -149
- package/bin/_cctally_config.py +43 -4
- package/bin/_cctally_core.py +933 -759
- package/bin/_cctally_dashboard.py +157 -47
- package/bin/_cctally_dashboard_cache_report.py +13 -6
- package/bin/_cctally_dashboard_conversation.py +1 -0
- package/bin/_cctally_dashboard_envelope.py +186 -8
- package/bin/_cctally_dashboard_share.py +60 -20
- package/bin/_cctally_dashboard_sources.py +427 -128
- package/bin/_cctally_db.py +605 -128
- package/bin/_cctally_doctor.py +413 -28
- package/bin/_cctally_five_hour.py +12 -5
- package/bin/_cctally_journal.py +2050 -156
- package/bin/_cctally_journal_repair.py +519 -0
- package/bin/_cctally_milestone_history.py +142 -56
- package/bin/_cctally_milestones.py +179 -111
- package/bin/_cctally_parser.py +42 -0
- package/bin/_cctally_project.py +24 -18
- package/bin/_cctally_quota.py +139 -25
- package/bin/_cctally_record.py +279 -108
- package/bin/_cctally_rederive.py +1052 -0
- package/bin/_cctally_reporting.py +58 -53
- package/bin/_cctally_setup.py +1 -0
- package/bin/_cctally_source_analytics.py +4 -1
- package/bin/_cctally_statusline.py +11 -11
- package/bin/_cctally_store.py +1039 -31
- package/bin/_cctally_sync_week.py +17 -8
- package/bin/_cctally_tui.py +421 -54
- package/bin/_cctally_update.py +133 -8
- package/bin/_cctally_weekrefs.py +14 -0
- package/bin/_lib_aggregators.py +10 -6
- package/bin/_lib_cache_report.py +101 -9
- package/bin/_lib_codex_pools.py +82 -0
- package/bin/_lib_conversation_query.py +126 -33
- package/bin/_lib_dashboard_sources.py +126 -1
- package/bin/_lib_diff_kernel.py +28 -15
- package/bin/_lib_doctor.py +342 -4
- package/bin/_lib_journal.py +924 -2
- package/bin/_lib_jsonl.py +43 -14
- package/bin/_lib_pricing.py +140 -21
- package/bin/_lib_readme_refresh.py +401 -0
- package/bin/_lib_rederive.py +395 -0
- package/bin/_lib_share.py +58 -2
- package/bin/cctally +56 -8
- package/dashboard/static/assets/{index-DJP4gEB7.js → index-3bgCMVHb.js} +52 -52
- package/dashboard/static/assets/index-D27EIHEI.css +1 -0
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +6 -1
- package/dashboard/static/assets/index-Dk1nplOz.css +0 -1
|
@@ -16,6 +16,7 @@ reached via the call-time _cctally() accessor (ns-patchable). No _lib_ kernel.
|
|
|
16
16
|
"""
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
+
import contextlib
|
|
19
20
|
import datetime as dt
|
|
20
21
|
import sqlite3
|
|
21
22
|
import sys
|
|
@@ -40,6 +41,34 @@ def _cctally():
|
|
|
40
41
|
return sys.modules["cctally"]
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
def _own_conn_stats_guard():
|
|
45
|
+
"""The sanctioned regime for the three ``own_conn`` reconcile branches (#386).
|
|
46
|
+
|
|
47
|
+
``_reconcile_budget_on_config_write``, its Codex twin, and
|
|
48
|
+
``_reconcile_project_budget_milestones_on_write`` each accept ``conn=None``
|
|
49
|
+
and then ``open_db()`` → write → ``commit()`` → ``close()``. That branch
|
|
50
|
+
writes stats.db OUTSIDE the ingest cycle and outside every lock — the
|
|
51
|
+
R1-RESIDUAL the #386 mutation inventory recorded. No production caller
|
|
52
|
+
reaches it today (all nine route through ``reconcile_budget_config`` with
|
|
53
|
+
``conn=ctx.conn``; only ``bin/cctally-reconcile-test`` calls it directly),
|
|
54
|
+
but it is one careless caller away from being real, and the whole body sits
|
|
55
|
+
inside a best-effort ``except Exception`` that would swallow the authorizer's
|
|
56
|
+
denial into a log line — a silently-dropped milestone, not a loud failure.
|
|
57
|
+
|
|
58
|
+
Rather than delete a branch a live harness depends on, the branch is ROUTED:
|
|
59
|
+
it becomes a genuine spec §3.1 administrative writer, holding
|
|
60
|
+
``stats.db.maintenance.lock`` exclusive (re-entrant — a caller that already
|
|
61
|
+
owns it is not made to wait on itself) for the duration and declaring the
|
|
62
|
+
sanctioned scope the authorizer checks. Self-sanctioning WITHOUT the lock
|
|
63
|
+
was the option rejected: it would have made the guard agree with a writer
|
|
64
|
+
that is still unserialized.
|
|
65
|
+
"""
|
|
66
|
+
import _cctally_store
|
|
67
|
+
|
|
68
|
+
return _cctally_store.stats_open_time_guard()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
43
72
|
@dataclass
|
|
44
73
|
class WeekCostResult:
|
|
45
74
|
week_start: dt.date
|
|
@@ -59,9 +88,17 @@ def compute_week_cost(
|
|
|
59
88
|
end_iso_override: str | None = None,
|
|
60
89
|
*,
|
|
61
90
|
account_key: "str | None" = None,
|
|
91
|
+
as_of: "str | None" = None,
|
|
62
92
|
) -> WeekCostResult:
|
|
93
|
+
# `as_of` (#372 Task B) makes historical rederivation range-bounded by the
|
|
94
|
+
# retained triggering record instead of leaking later cache rows through the
|
|
95
|
+
# host wall clock. Normal callers omit it and retain the existing behavior.
|
|
63
96
|
# internal fallback: host-local intentional
|
|
64
|
-
now_local =
|
|
97
|
+
now_local = (
|
|
98
|
+
parse_iso_datetime(as_of, "asOf").astimezone()
|
|
99
|
+
if as_of is not None
|
|
100
|
+
else dt.datetime.now().astimezone()
|
|
101
|
+
)
|
|
65
102
|
start_dt_override = (
|
|
66
103
|
parse_iso_datetime(start_iso_override, "weekStartAt")
|
|
67
104
|
if start_iso_override
|
|
@@ -82,22 +119,35 @@ def compute_week_cost(
|
|
|
82
119
|
start_iso = format_local_iso(week_start, end_of_day=False)
|
|
83
120
|
|
|
84
121
|
if end_dt_override is not None:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
122
|
+
if as_of is not None:
|
|
123
|
+
# A historical custom sync may name a window that starts after the
|
|
124
|
+
# op. The retained clock is an unconditional upper bound.
|
|
125
|
+
end_iso = min(now_local, end_dt_override).isoformat(
|
|
126
|
+
timespec="seconds"
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
in_current_window = (
|
|
130
|
+
start_dt_override is not None
|
|
131
|
+
and start_dt_override <= now_local < end_dt_override
|
|
132
|
+
)
|
|
133
|
+
end_iso = (
|
|
134
|
+
now_local.isoformat(timespec="seconds")
|
|
135
|
+
if in_current_window
|
|
136
|
+
else end_dt_override.isoformat(timespec="seconds")
|
|
137
|
+
)
|
|
94
138
|
else:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
now_local.isoformat(timespec="seconds")
|
|
98
|
-
if is_current_week
|
|
99
|
-
else format_local_iso(week_end, end_of_day=True)
|
|
139
|
+
declared_end = parse_iso_datetime(
|
|
140
|
+
format_local_iso(week_end, end_of_day=True), "weekEnd"
|
|
100
141
|
)
|
|
142
|
+
if as_of is not None:
|
|
143
|
+
end_iso = min(now_local, declared_end).isoformat(timespec="seconds")
|
|
144
|
+
else:
|
|
145
|
+
is_current_week = week_start <= now_local.date() <= week_end
|
|
146
|
+
end_iso = (
|
|
147
|
+
now_local.isoformat(timespec="seconds")
|
|
148
|
+
if is_current_week
|
|
149
|
+
else declared_end.isoformat(timespec="seconds")
|
|
150
|
+
)
|
|
101
151
|
|
|
102
152
|
start_dt = parse_iso_datetime(start_iso, "start")
|
|
103
153
|
end_dt = parse_iso_datetime(end_iso, "end")
|
|
@@ -857,32 +907,38 @@ def _reconcile_codex_budget_on_config_write(validated_budget, *, conn=None, as_o
|
|
|
857
907
|
config = c.load_config()
|
|
858
908
|
tz = c.resolve_display_tz(argparse.Namespace(tz=None), config)
|
|
859
909
|
now_utc = _as_of_or_command(as_of)
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
ladders.extend((k, v) for k, v in accounts.items())
|
|
867
|
-
for acct_key, acct_usd in ladders:
|
|
868
|
-
_reconcile_budget_milestones_on_set(
|
|
869
|
-
conn,
|
|
870
|
-
vendor="codex",
|
|
871
|
-
target=acct_usd,
|
|
872
|
-
thresholds=thresholds,
|
|
873
|
-
now_utc=now_utc,
|
|
874
|
-
period=codex["period"],
|
|
875
|
-
config=config,
|
|
876
|
-
tz=tz,
|
|
877
|
-
as_of=as_of,
|
|
878
|
-
commit=False,
|
|
879
|
-
account_key=acct_key,
|
|
880
|
-
)
|
|
881
|
-
if own_conn:
|
|
882
|
-
conn.commit()
|
|
883
|
-
finally:
|
|
910
|
+
# #386 R1-RESIDUAL: an own_conn write is an ADMINISTRATIVE stats
|
|
911
|
+
# writer — outside the ingest cycle and, before this, outside every
|
|
912
|
+
# lock. It now takes the maintenance lock + the sanctioned scope.
|
|
913
|
+
# See _own_conn_stats_guard.
|
|
914
|
+
with (_own_conn_stats_guard() if own_conn
|
|
915
|
+
else contextlib.nullcontext()):
|
|
884
916
|
if own_conn:
|
|
885
|
-
conn
|
|
917
|
+
conn = open_db()
|
|
918
|
+
try:
|
|
919
|
+
ladders = []
|
|
920
|
+
if amount_usd is not None:
|
|
921
|
+
ladders.append(("*", amount_usd))
|
|
922
|
+
ladders.extend((k, v) for k, v in accounts.items())
|
|
923
|
+
for acct_key, acct_usd in ladders:
|
|
924
|
+
_reconcile_budget_milestones_on_set(
|
|
925
|
+
conn,
|
|
926
|
+
vendor="codex",
|
|
927
|
+
target=acct_usd,
|
|
928
|
+
thresholds=thresholds,
|
|
929
|
+
now_utc=now_utc,
|
|
930
|
+
period=codex["period"],
|
|
931
|
+
config=config,
|
|
932
|
+
tz=tz,
|
|
933
|
+
as_of=as_of,
|
|
934
|
+
commit=False,
|
|
935
|
+
account_key=acct_key,
|
|
936
|
+
)
|
|
937
|
+
if own_conn:
|
|
938
|
+
conn.commit()
|
|
939
|
+
finally:
|
|
940
|
+
if own_conn:
|
|
941
|
+
conn.close()
|
|
886
942
|
except Exception as exc: # best-effort; never fail the write
|
|
887
943
|
eprint(f"[codex-budget-milestone] reconcile on set failed: {exc}")
|
|
888
944
|
|
|
@@ -915,32 +971,38 @@ def _reconcile_budget_on_config_write(validated_budget, *, conn=None, as_of=None
|
|
|
915
971
|
config = c.load_config()
|
|
916
972
|
tz = c.resolve_display_tz(argparse.Namespace(tz=None), config)
|
|
917
973
|
now_utc = _as_of_or_command(as_of)
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
ladders.extend((k, v) for k, v in accounts.items())
|
|
925
|
-
for acct_key, acct_usd in ladders:
|
|
926
|
-
_reconcile_budget_milestones_on_set(
|
|
927
|
-
conn,
|
|
928
|
-
vendor="claude",
|
|
929
|
-
target=acct_usd,
|
|
930
|
-
thresholds=thresholds,
|
|
931
|
-
now_utc=now_utc,
|
|
932
|
-
period=period,
|
|
933
|
-
config=config,
|
|
934
|
-
tz=tz,
|
|
935
|
-
as_of=as_of,
|
|
936
|
-
commit=False,
|
|
937
|
-
account_key=acct_key,
|
|
938
|
-
)
|
|
939
|
-
if own_conn:
|
|
940
|
-
conn.commit()
|
|
941
|
-
finally:
|
|
974
|
+
# #386 R1-RESIDUAL: an own_conn write is an ADMINISTRATIVE stats
|
|
975
|
+
# writer — outside the ingest cycle and, before this, outside every
|
|
976
|
+
# lock. It now takes the maintenance lock + the sanctioned scope.
|
|
977
|
+
# See _own_conn_stats_guard.
|
|
978
|
+
with (_own_conn_stats_guard() if own_conn
|
|
979
|
+
else contextlib.nullcontext()):
|
|
942
980
|
if own_conn:
|
|
943
|
-
conn
|
|
981
|
+
conn = open_db()
|
|
982
|
+
try:
|
|
983
|
+
ladders = []
|
|
984
|
+
if weekly_usd is not None:
|
|
985
|
+
ladders.append(("*", weekly_usd))
|
|
986
|
+
ladders.extend((k, v) for k, v in accounts.items())
|
|
987
|
+
for acct_key, acct_usd in ladders:
|
|
988
|
+
_reconcile_budget_milestones_on_set(
|
|
989
|
+
conn,
|
|
990
|
+
vendor="claude",
|
|
991
|
+
target=acct_usd,
|
|
992
|
+
thresholds=thresholds,
|
|
993
|
+
now_utc=now_utc,
|
|
994
|
+
period=period,
|
|
995
|
+
config=config,
|
|
996
|
+
tz=tz,
|
|
997
|
+
as_of=as_of,
|
|
998
|
+
commit=False,
|
|
999
|
+
account_key=acct_key,
|
|
1000
|
+
)
|
|
1001
|
+
if own_conn:
|
|
1002
|
+
conn.commit()
|
|
1003
|
+
finally:
|
|
1004
|
+
if own_conn:
|
|
1005
|
+
conn.close()
|
|
944
1006
|
except Exception as exc: # best-effort; never fail the write
|
|
945
1007
|
eprint(f"[budget-milestone] reconcile on set failed: {exc}")
|
|
946
1008
|
|
|
@@ -1018,53 +1080,59 @@ def _reconcile_project_budget_milestones_on_write(
|
|
|
1018
1080
|
c = _cctally()
|
|
1019
1081
|
own_conn = conn is None
|
|
1020
1082
|
try:
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
return
|
|
1028
|
-
week_start_at, _week_end_at = window
|
|
1029
|
-
week_key = week_start_at.isoformat(timespec="seconds")
|
|
1030
|
-
by_proj = c._sum_cost_by_project(week_start_at, now_utc, mode="auto")
|
|
1031
|
-
items = (
|
|
1032
|
-
projects.items()
|
|
1033
|
-
if touched_projects is None
|
|
1034
|
-
else [
|
|
1035
|
-
(k, v) for k, v in projects.items()
|
|
1036
|
-
if k in touched_projects
|
|
1037
|
-
]
|
|
1038
|
-
)
|
|
1039
|
-
# Same crossing arithmetic as firing, via the shared generator
|
|
1040
|
-
# (#130). Reconcile differs ONLY in the tail: UPDATE alerted_at with
|
|
1041
|
-
# NO dispatch (retroactive-storm suppression). `items` already
|
|
1042
|
-
# honors touched_projects filtering above.
|
|
1043
|
-
for project_key, t, spent, target, consumption_pct in c._project_crossings(
|
|
1044
|
-
items, thresholds, by_proj
|
|
1045
|
-
):
|
|
1046
|
-
insert_project_budget_milestone(
|
|
1047
|
-
conn,
|
|
1048
|
-
week_start_at=week_key,
|
|
1049
|
-
project_key=project_key,
|
|
1050
|
-
threshold=t,
|
|
1051
|
-
budget_usd=target,
|
|
1052
|
-
spent_usd=spent,
|
|
1053
|
-
consumption_pct=consumption_pct,
|
|
1054
|
-
commit=False,
|
|
1055
|
-
as_of=as_of,
|
|
1056
|
-
)
|
|
1057
|
-
conn.execute(
|
|
1058
|
-
"UPDATE project_budget_milestones SET alerted_at = ? "
|
|
1059
|
-
"WHERE week_start_at = ? AND project_key = ? "
|
|
1060
|
-
" AND threshold = ? AND alerted_at IS NULL",
|
|
1061
|
-
(as_of or now_utc_iso(), week_key, project_key, t),
|
|
1062
|
-
)
|
|
1083
|
+
# #386 R1-RESIDUAL: an own_conn write is an ADMINISTRATIVE stats
|
|
1084
|
+
# writer — outside the ingest cycle and, before this, outside every
|
|
1085
|
+
# lock. It now takes the maintenance lock + the sanctioned scope.
|
|
1086
|
+
# See _own_conn_stats_guard.
|
|
1087
|
+
with (_own_conn_stats_guard() if own_conn
|
|
1088
|
+
else contextlib.nullcontext()):
|
|
1063
1089
|
if own_conn:
|
|
1064
|
-
conn
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1090
|
+
conn = open_db()
|
|
1091
|
+
try:
|
|
1092
|
+
now_utc = _as_of_or_command(as_of)
|
|
1093
|
+
window = c._resolve_current_budget_window(conn, now_utc)
|
|
1094
|
+
if window is None:
|
|
1095
|
+
return
|
|
1096
|
+
week_start_at, _week_end_at = window
|
|
1097
|
+
week_key = week_start_at.isoformat(timespec="seconds")
|
|
1098
|
+
by_proj = c._sum_cost_by_project(week_start_at, now_utc, mode="auto")
|
|
1099
|
+
items = (
|
|
1100
|
+
projects.items()
|
|
1101
|
+
if touched_projects is None
|
|
1102
|
+
else [
|
|
1103
|
+
(k, v) for k, v in projects.items()
|
|
1104
|
+
if k in touched_projects
|
|
1105
|
+
]
|
|
1106
|
+
)
|
|
1107
|
+
# Same crossing arithmetic as firing, via the shared generator
|
|
1108
|
+
# (#130). Reconcile differs ONLY in the tail: UPDATE alerted_at with
|
|
1109
|
+
# NO dispatch (retroactive-storm suppression). `items` already
|
|
1110
|
+
# honors touched_projects filtering above.
|
|
1111
|
+
for project_key, t, spent, target, consumption_pct in c._project_crossings(
|
|
1112
|
+
items, thresholds, by_proj
|
|
1113
|
+
):
|
|
1114
|
+
insert_project_budget_milestone(
|
|
1115
|
+
conn,
|
|
1116
|
+
week_start_at=week_key,
|
|
1117
|
+
project_key=project_key,
|
|
1118
|
+
threshold=t,
|
|
1119
|
+
budget_usd=target,
|
|
1120
|
+
spent_usd=spent,
|
|
1121
|
+
consumption_pct=consumption_pct,
|
|
1122
|
+
commit=False,
|
|
1123
|
+
as_of=as_of,
|
|
1124
|
+
)
|
|
1125
|
+
conn.execute(
|
|
1126
|
+
"UPDATE project_budget_milestones SET alerted_at = ? "
|
|
1127
|
+
"WHERE week_start_at = ? AND project_key = ? "
|
|
1128
|
+
" AND threshold = ? AND alerted_at IS NULL",
|
|
1129
|
+
(as_of or now_utc_iso(), week_key, project_key, t),
|
|
1130
|
+
)
|
|
1131
|
+
if own_conn:
|
|
1132
|
+
conn.commit()
|
|
1133
|
+
finally:
|
|
1134
|
+
if own_conn:
|
|
1135
|
+
conn.close()
|
|
1068
1136
|
except Exception as exc: # best-effort; never fail the write
|
|
1069
1137
|
eprint(
|
|
1070
1138
|
f"[project-budget-milestone] reconcile on write failed: {exc}"
|
package/bin/_cctally_parser.py
CHANGED
|
@@ -2951,6 +2951,48 @@ def _build_db_parser(subparsers, name, *, help_text, xref=None):
|
|
|
2951
2951
|
help="Emit JSON to stdout",
|
|
2952
2952
|
)
|
|
2953
2953
|
db_rebuild.set_defaults(func=c.cmd_db_rebuild)
|
|
2954
|
+
db_rederive = db_sub.add_parser(
|
|
2955
|
+
"rederive",
|
|
2956
|
+
help="Preview or apply an append-only correction plan from retained truth",
|
|
2957
|
+
)
|
|
2958
|
+
db_rederive.add_argument(
|
|
2959
|
+
"--family",
|
|
2960
|
+
required=True,
|
|
2961
|
+
help="Closed derivation family to re-run (initially: claude-usage)",
|
|
2962
|
+
)
|
|
2963
|
+
db_rederive.add_argument(
|
|
2964
|
+
"--yes",
|
|
2965
|
+
action="store_true",
|
|
2966
|
+
help="Append the correction batch and rebuild stats.db (default: preview)",
|
|
2967
|
+
)
|
|
2968
|
+
db_rederive.add_argument(
|
|
2969
|
+
"--json",
|
|
2970
|
+
action="store_true",
|
|
2971
|
+
help="Emit schemaVersion 1 JSON to stdout",
|
|
2972
|
+
)
|
|
2973
|
+
db_rederive.set_defaults(func=c.cmd_db_rederive)
|
|
2974
|
+
db_journal_repair = db_sub.add_parser(
|
|
2975
|
+
"journal-repair",
|
|
2976
|
+
help="Preview or acknowledge exact structural journal violations",
|
|
2977
|
+
)
|
|
2978
|
+
db_journal_repair.add_argument(
|
|
2979
|
+
"--violation",
|
|
2980
|
+
action="append",
|
|
2981
|
+
default=[],
|
|
2982
|
+
metavar="FINGERPRINT",
|
|
2983
|
+
help="Exact violation fingerprint to acknowledge (repeatable)",
|
|
2984
|
+
)
|
|
2985
|
+
db_journal_repair.add_argument(
|
|
2986
|
+
"--yes",
|
|
2987
|
+
action="store_true",
|
|
2988
|
+
help="Append the audit decision and rebuild stats.db (default: preview)",
|
|
2989
|
+
)
|
|
2990
|
+
db_journal_repair.add_argument(
|
|
2991
|
+
"--json",
|
|
2992
|
+
action="store_true",
|
|
2993
|
+
help="Emit schemaVersion 1 JSON to stdout",
|
|
2994
|
+
)
|
|
2995
|
+
db_journal_repair.set_defaults(func=c.cmd_db_journal_repair)
|
|
2954
2996
|
db_repair = db_sub.add_parser(
|
|
2955
2997
|
"repair",
|
|
2956
2998
|
help="Recover a malformed pre-cutover stats.db through a verified fresh "
|
package/bin/_cctally_project.py
CHANGED
|
@@ -135,12 +135,14 @@ def _sum_cost_by_project(
|
|
|
135
135
|
continue
|
|
136
136
|
cost = c._calculate_entry_cost(
|
|
137
137
|
entry.model,
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
c.claude_usage_dict( # #195 chokepoint
|
|
139
|
+
input_tokens=entry.input_tokens,
|
|
140
|
+
output_tokens=entry.output_tokens,
|
|
141
|
+
cache_creation_tokens=entry.cache_creation_tokens,
|
|
142
|
+
cache_read_tokens=entry.cache_read_tokens,
|
|
143
|
+
cache_1h_tokens=getattr(entry, "cache_1h_tokens", None),
|
|
144
|
+
speed=getattr(entry, "speed", None),
|
|
145
|
+
),
|
|
144
146
|
mode=mode,
|
|
145
147
|
cost_usd=entry.cost_usd,
|
|
146
148
|
)
|
|
@@ -208,12 +210,14 @@ def _accumulate_entry_into_bucket(
|
|
|
208
210
|
else:
|
|
209
211
|
cost = c._calculate_entry_cost(
|
|
210
212
|
entry.model,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
213
|
+
c.claude_usage_dict( # #195 chokepoint
|
|
214
|
+
input_tokens=entry.input_tokens,
|
|
215
|
+
output_tokens=entry.output_tokens,
|
|
216
|
+
cache_creation_tokens=entry.cache_creation_tokens,
|
|
217
|
+
cache_read_tokens=entry.cache_read_tokens,
|
|
218
|
+
cache_1h_tokens=getattr(entry, "cache_1h_tokens", None),
|
|
219
|
+
speed=getattr(entry, "speed", None),
|
|
220
|
+
),
|
|
217
221
|
mode="auto",
|
|
218
222
|
cost_usd=entry.cost_usd,
|
|
219
223
|
)
|
|
@@ -555,12 +559,14 @@ def cmd_project(args: argparse.Namespace) -> int:
|
|
|
555
559
|
|
|
556
560
|
entry_cost = c._calculate_entry_cost(
|
|
557
561
|
entry.model,
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
562
|
+
c.claude_usage_dict( # #195 chokepoint
|
|
563
|
+
input_tokens=entry.input_tokens,
|
|
564
|
+
output_tokens=entry.output_tokens,
|
|
565
|
+
cache_creation_tokens=entry.cache_creation_tokens,
|
|
566
|
+
cache_read_tokens=entry.cache_read_tokens,
|
|
567
|
+
cache_1h_tokens=getattr(entry, "cache_1h_tokens", None),
|
|
568
|
+
speed=getattr(entry, "speed", None),
|
|
569
|
+
),
|
|
564
570
|
mode="auto",
|
|
565
571
|
cost_usd=entry.cost_usd,
|
|
566
572
|
)
|