cctally 1.65.0 → 1.67.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 +63 -0
- package/README.md +6 -4
- package/bin/_cctally_alerts.py +1 -1
- package/bin/_cctally_cache.py +203 -22
- package/bin/_cctally_cache_report.py +7 -5
- package/bin/_cctally_core.py +125 -14
- package/bin/_cctally_dashboard.py +495 -4893
- package/bin/_cctally_dashboard_cache_report.py +714 -0
- package/bin/_cctally_dashboard_conversation.py +830 -0
- package/bin/_cctally_dashboard_envelope.py +1520 -0
- package/bin/_cctally_dashboard_share.py +2012 -0
- package/bin/_cctally_db.py +158 -5
- package/bin/_cctally_doctor.py +104 -3
- package/bin/_cctally_five_hour.py +19 -3
- package/bin/_cctally_forecast.py +118 -161
- package/bin/_cctally_parser.py +1003 -777
- package/bin/_cctally_percent_breakdown.py +1 -1
- package/bin/_cctally_pricing_check.py +39 -0
- package/bin/_cctally_project.py +18 -42
- package/bin/_cctally_record.py +279 -274
- package/bin/_cctally_reporting.py +1 -1
- package/bin/_cctally_setup.py +150 -43
- package/bin/_cctally_sync_week.py +1 -1
- package/bin/_cctally_telemetry.py +3 -5
- package/bin/_cctally_transcript.py +191 -0
- package/bin/_cctally_tui.py +42 -18
- package/bin/_lib_alert_dispatch.py +1 -1
- package/bin/_lib_blocks.py +6 -1
- package/bin/_lib_conversation_anon.py +254 -0
- package/bin/_lib_conversation_query.py +66 -0
- package/bin/_lib_credit.py +133 -0
- package/bin/_lib_diff_kernel.py +19 -7
- package/bin/_lib_doctor.py +150 -1
- package/bin/_lib_five_hour.py +61 -0
- package/bin/_lib_forecast.py +144 -0
- package/bin/_lib_json_envelope.py +38 -0
- package/bin/_lib_jsonl.py +115 -73
- package/bin/_lib_log.py +96 -0
- package/bin/_lib_pricing.py +47 -1
- package/bin/_lib_pricing_check.py +25 -3
- package/bin/_lib_record.py +179 -0
- package/bin/_lib_render.py +26 -11
- package/bin/_lib_snapshot_cache.py +61 -0
- package/bin/_lib_view_models.py +7 -1
- package/bin/cctally +59 -7
- package/bin/cctally-dashboard +2 -2
- package/bin/cctally-statusline +1 -0
- package/bin/cctally-transcript +5 -0
- package/bin/cctally-tui +2 -2
- package/dashboard/static/assets/index-BybNp_Di.js +80 -0
- package/dashboard/static/assets/{index-DQWNrIqu.css → index-DgAmLK65.css} +1 -1
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +13 -1
- package/dashboard/static/assets/index-CJTUCpKt.js +0 -80
|
@@ -156,7 +156,7 @@ def cmd_percent_breakdown(args: argparse.Namespace) -> int:
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
if args.json:
|
|
159
|
-
print(json.dumps(output, indent=2))
|
|
159
|
+
print(json.dumps(c.stamp_schema_version(output), indent=2))
|
|
160
160
|
return 0
|
|
161
161
|
|
|
162
162
|
# Prefer the reset-adjusted ISO timestamps in the terminal header
|
|
@@ -283,6 +283,14 @@ def cmd_pricing_check(args: argparse.Namespace) -> int:
|
|
|
283
283
|
|
|
284
284
|
drift = {"value_drift": [], "missing_from_us": [], "ahead_of_litellm": []}
|
|
285
285
|
existence = {"status": "skipped", "unpriced_vendor_models": []}
|
|
286
|
+
# staleSuppressions is network-derived (needs the LiteLLM scoped table), so
|
|
287
|
+
# under --offline it is SKIPPED, not degraded — matching the offline branch,
|
|
288
|
+
# which leaves degraded_components empty (#279 S7 W7). expiredSuppressions is
|
|
289
|
+
# date-derived and runs OFFLINE too, off the _command_as_of() clock seam.
|
|
290
|
+
stale_suppressions: list = []
|
|
291
|
+
expired_suppressions = _lib_pricing_check.expired_allowlist_entries(
|
|
292
|
+
c.PRICING_DRIFT_ALLOWLIST, now_utc,
|
|
293
|
+
)
|
|
286
294
|
|
|
287
295
|
if not args.offline:
|
|
288
296
|
litellm, ok = _fetch_litellm_prices()
|
|
@@ -297,6 +305,10 @@ def cmd_pricing_check(args: argparse.Namespace) -> int:
|
|
|
297
305
|
"missing_from_us": list(res.missing_from_us),
|
|
298
306
|
"ahead_of_litellm": list(res.ahead_of_litellm),
|
|
299
307
|
}
|
|
308
|
+
stale_suppressions = _lib_pricing_check.stale_allowlist_entries(
|
|
309
|
+
c.PRICING_DRIFT_ALLOWLIST, c.CLAUDE_MODEL_PRICING,
|
|
310
|
+
c.CODEX_MODEL_PRICING, scoped,
|
|
311
|
+
)
|
|
300
312
|
else:
|
|
301
313
|
status = "degraded"
|
|
302
314
|
degraded.append("litellm")
|
|
@@ -307,11 +319,15 @@ def cmd_pricing_check(args: argparse.Namespace) -> int:
|
|
|
307
319
|
|
|
308
320
|
# Actionable = any finding on a leg that ran. `ahead_of_litellm` is
|
|
309
321
|
# NEVER actionable (invariant #2). A degraded leg contributes no finding.
|
|
322
|
+
# A stale OR expired suppression is actionable (remove the allowlist entry /
|
|
323
|
+
# re-sync the snapshot) — both wired in explicitly (#279 S7 W7).
|
|
310
324
|
actionable = (
|
|
311
325
|
bool(coverage)
|
|
312
326
|
or bool(drift["value_drift"])
|
|
313
327
|
or bool(drift["missing_from_us"])
|
|
314
328
|
or bool(existence["unpriced_vendor_models"])
|
|
329
|
+
or bool(stale_suppressions)
|
|
330
|
+
or bool(expired_suppressions)
|
|
315
331
|
)
|
|
316
332
|
|
|
317
333
|
payload = {
|
|
@@ -322,6 +338,12 @@ def cmd_pricing_check(args: argparse.Namespace) -> int:
|
|
|
322
338
|
"coverage": [dataclasses.asdict(g) for g in coverage],
|
|
323
339
|
"drift": drift,
|
|
324
340
|
"existence": existence,
|
|
341
|
+
# Additive keys on the existing schemaVersion 1 envelope (no bump — CLI
|
|
342
|
+
# contract additive rule). staleSuppressions: allowlist entries whose
|
|
343
|
+
# divergence no longer exists (network). expiredSuppressions: allowlist
|
|
344
|
+
# entries past their `expires` cutover (date).
|
|
345
|
+
"staleSuppressions": list(stale_suppressions),
|
|
346
|
+
"expiredSuppressions": list(expired_suppressions),
|
|
325
347
|
"litellmSource": c.LITELLM_PRICES_URL,
|
|
326
348
|
}
|
|
327
349
|
|
|
@@ -385,6 +407,23 @@ def _render_pricing_check_text(payload: dict, *, offline: bool, actionable: bool
|
|
|
385
407
|
out("\n Existence: all vendor models priced.\n")
|
|
386
408
|
elif ex["status"] == "degraded":
|
|
387
409
|
out("\n Existence: /v1/models unavailable (skipped).\n")
|
|
410
|
+
stale = payload.get("staleSuppressions") or []
|
|
411
|
+
if stale:
|
|
412
|
+
out(f"\n Stale suppressions ({len(stale)}) — the divergence no "
|
|
413
|
+
f"longer exists, remove the allowlist entry:\n")
|
|
414
|
+
for e in stale:
|
|
415
|
+
fld = f".{e['field']}" if e.get("field") else ""
|
|
416
|
+
out(f" • {e['model']}{fld}\n")
|
|
417
|
+
|
|
418
|
+
# Expired suppressions are date-derived, so they render in BOTH offline and
|
|
419
|
+
# online modes.
|
|
420
|
+
expired = payload.get("expiredSuppressions") or []
|
|
421
|
+
if expired:
|
|
422
|
+
out(f"\n Expired suppressions ({len(expired)}) — past their `expires` "
|
|
423
|
+
f"cutover, remove the allowlist entry / re-sync the snapshot:\n")
|
|
424
|
+
for e in expired:
|
|
425
|
+
fld = f".{e['field']}" if e.get("field") else ""
|
|
426
|
+
out(f" • {e['model']}{fld} (expired {e.get('expires')})\n")
|
|
388
427
|
|
|
389
428
|
# Single-sourced from cmd_pricing_check's exit-code predicate (don't
|
|
390
429
|
# recompute the four-clause boolean here — it would drift).
|
package/bin/_cctally_project.py
CHANGED
|
@@ -28,7 +28,7 @@ import sys
|
|
|
28
28
|
|
|
29
29
|
from _cctally_core import (
|
|
30
30
|
_command_as_of,
|
|
31
|
-
|
|
31
|
+
_floored_week_max,
|
|
32
32
|
eprint,
|
|
33
33
|
open_db,
|
|
34
34
|
parse_iso_datetime,
|
|
@@ -82,45 +82,18 @@ def _load_week_snapshots(
|
|
|
82
82
|
"AND datetime(week_end_at) > datetime(?)",
|
|
83
83
|
(until.isoformat(), since.isoformat()),
|
|
84
84
|
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
# mid-week credit doesn't re-inflate the per-project Used %. None means
|
|
88
|
-
# "no floor" — every captured row counts.
|
|
89
|
-
floor_epoch: dict[str, int | None] = {}
|
|
90
|
-
|
|
91
|
-
def _floor_for(wsd, ws_iso, we_iso):
|
|
92
|
-
if wsd not in floor_epoch:
|
|
93
|
-
f_iso = _reset_aware_floor(conn, wsd, ws_iso, we_iso)
|
|
94
|
-
floor_epoch[wsd] = (
|
|
95
|
-
int(parse_iso_datetime(f_iso, "project.floor").timestamp())
|
|
96
|
-
if f_iso else None
|
|
97
|
-
)
|
|
98
|
-
return floor_epoch[wsd]
|
|
99
|
-
|
|
100
|
-
result: dict[dt.datetime, float] = {}
|
|
101
|
-
for wsd, ws_iso, we_iso, cap_iso, pct in rows:
|
|
85
|
+
rows_in = []
|
|
86
|
+
for wsd, ws_iso, we_iso, cap_iso, pct in cur.fetchall():
|
|
102
87
|
if ws_iso is None or pct is None:
|
|
103
88
|
continue
|
|
104
|
-
|
|
105
|
-
if floor is not None and cap_iso is not None:
|
|
106
|
-
try:
|
|
107
|
-
cap_epoch = int(
|
|
108
|
-
parse_iso_datetime(str(cap_iso), "project.cap").timestamp()
|
|
109
|
-
)
|
|
110
|
-
except ValueError:
|
|
111
|
-
cap_epoch = None
|
|
112
|
-
# Drop pre-floor (stale pre-credit) snapshots from the MAX.
|
|
113
|
-
if cap_epoch is not None and cap_epoch < floor:
|
|
114
|
-
continue
|
|
115
|
-
ws = dt.datetime.fromisoformat(
|
|
116
|
-
str(ws_iso).replace("Z", "+00:00")
|
|
117
|
-
)
|
|
89
|
+
ws = dt.datetime.fromisoformat(str(ws_iso).replace("Z", "+00:00"))
|
|
118
90
|
key = ws.astimezone(dt.timezone.utc)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
91
|
+
rows_in.append((key, wsd, ws_iso, we_iso, cap_iso, pct))
|
|
92
|
+
# Reset-aware per-week max (#290): the shared reducer resolves each
|
|
93
|
+
# week's clamp floor once (keyed on week_start_date) and drops stale
|
|
94
|
+
# pre-credit captures before taking the per-week maximum. The query
|
|
95
|
+
# above already filters NULL bounds, so canonical bounds are present.
|
|
96
|
+
return _floored_week_max(conn, rows_in)
|
|
124
97
|
finally:
|
|
125
98
|
conn.close()
|
|
126
99
|
|
|
@@ -352,7 +325,7 @@ def _project_json_output(
|
|
|
352
325
|
"projects": projects_json,
|
|
353
326
|
"warnings": warnings,
|
|
354
327
|
}
|
|
355
|
-
return json.dumps(payload, indent=2)
|
|
328
|
+
return json.dumps(_cctally().stamp_schema_version(payload), indent=2)
|
|
356
329
|
|
|
357
330
|
|
|
358
331
|
def _project_sort_key(row: dict, sort_by: str, order: str):
|
|
@@ -390,10 +363,10 @@ def cmd_project(args: argparse.Namespace) -> int:
|
|
|
390
363
|
# Flag-combination validation (must run before any expensive work).
|
|
391
364
|
if args.weeks is not None and args.weeks < 1:
|
|
392
365
|
eprint("Error: --weeks must be >= 1")
|
|
393
|
-
return
|
|
366
|
+
return 2
|
|
394
367
|
if args.weeks is not None and (args.since or args.until):
|
|
395
368
|
eprint("Error: --weeks cannot be combined with --since/--until")
|
|
396
|
-
return
|
|
369
|
+
return 2
|
|
397
370
|
if args.since and args.until:
|
|
398
371
|
# Parse both as dates using the same multi-format helper shape used
|
|
399
372
|
# elsewhere in the codebase so YYYY-MM-DD and YYYYMMDD both compare
|
|
@@ -415,7 +388,7 @@ def cmd_project(args: argparse.Namespace) -> int:
|
|
|
415
388
|
# complaint triggered by garbage input).
|
|
416
389
|
if since_parsed is not None and until_parsed is not None and since_parsed > until_parsed:
|
|
417
390
|
eprint("Error: --since must be <= --until")
|
|
418
|
-
return
|
|
391
|
+
return 2
|
|
419
392
|
|
|
420
393
|
now = _command_as_of()
|
|
421
394
|
conn = open_db()
|
|
@@ -424,7 +397,10 @@ def cmd_project(args: argparse.Namespace) -> int:
|
|
|
424
397
|
if args.since or args.until:
|
|
425
398
|
parsed = c._parse_cli_date_range(args, now_utc=now)
|
|
426
399
|
if isinstance(parsed, int):
|
|
427
|
-
|
|
400
|
+
# Translate the ccusage-parity helper's exit 1 into project's own
|
|
401
|
+
# native usage code 2 — project is cctally-native, not a ccusage
|
|
402
|
+
# drop-in (docs/cli-contract.md; #279 S6 W2).
|
|
403
|
+
return 2
|
|
428
404
|
since_dt, until_dt = parsed
|
|
429
405
|
since_dt = since_dt.astimezone(dt.timezone.utc)
|
|
430
406
|
until_dt = until_dt.astimezone(dt.timezone.utc)
|