cctally 1.46.0 → 1.47.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 +5 -0
- package/bin/_lib_conversation_query.py +34 -3
- package/dashboard/static/assets/{index-DHfwfOHz.js → index-BOR4FBvt.js} +34 -34
- package/dashboard/static/assets/index-CuAtCrys.css +1 -0
- package/dashboard/static/dashboard.html +2 -2
- package/package.json +1 -1
- package/dashboard/static/assets/index-Dgq5Vvml.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.47.0] - 2026-06-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Dashboard:** the Recent Sessions modal now shows cache-rebuild events for the selected conversation — rebuild count, wasted $, tokens re-created, a session cache-value-saved figure, and a worst-first list of jump-links that open the conversation viewer at the message that triggered each rebuild (respects `dashboard.cache_failure_markers`).
|
|
12
|
+
|
|
8
13
|
## [1.46.0] - 2026-06-15
|
|
9
14
|
|
|
10
15
|
### Added
|
|
@@ -313,6 +313,18 @@ def _cache_failure_wasted_usd(model, lost):
|
|
|
313
313
|
return write - read
|
|
314
314
|
|
|
315
315
|
|
|
316
|
+
def _cache_read_saved_usd(model, cache_read):
|
|
317
|
+
"""Marginal USD the cache SAVED this turn: the `cache_read` prefix priced at
|
|
318
|
+
the full input rate minus its actual cache-READ rate. Display-only (same
|
|
319
|
+
caveat as `_cache_failure_wasted_usd`): NEVER summed into a cost-snapshot /
|
|
320
|
+
budget / reconciled figure. `input_tokens` and `cache_read_input_tokens` are
|
|
321
|
+
independent keys in the Claude `_calculate_entry_cost` (no subset
|
|
322
|
+
subtraction), so passing each alone yields the two rates cleanly."""
|
|
323
|
+
full = _calculate_entry_cost(model or "", {"input_tokens": cache_read})
|
|
324
|
+
read = _calculate_entry_cost(model or "", {"cache_read_input_tokens": cache_read})
|
|
325
|
+
return full - read
|
|
326
|
+
|
|
327
|
+
|
|
316
328
|
def _stamp_cache_failures(items):
|
|
317
329
|
"""Stamp ``item["cache_failure"]`` on each assistant turn that re-creates the
|
|
318
330
|
bulk of its cached prefix instead of reading it (spec §1). Mutates `items` in
|
|
@@ -1120,6 +1132,8 @@ def get_conversation_outline(conn, session_id):
|
|
|
1120
1132
|
cf_count = 0
|
|
1121
1133
|
cf_tokens = 0
|
|
1122
1134
|
cf_wasted = 0.0
|
|
1135
|
+
cf_rebuilds = [] # per-rebuild list (worst-first), spec §1
|
|
1136
|
+
cache_saved = 0.0 # session cache-value-saved, spec §1
|
|
1123
1137
|
for it in items:
|
|
1124
1138
|
kind = it["kind"]
|
|
1125
1139
|
turn_counts["total"] += 1
|
|
@@ -1161,6 +1175,9 @@ def get_conversation_outline(conn, session_id):
|
|
|
1161
1175
|
t["tokens"] = tok
|
|
1162
1176
|
for k in tokens:
|
|
1163
1177
|
tokens[k] += tok.get(k, 0)
|
|
1178
|
+
cr_tokens = tok.get("cache_read", 0) or 0
|
|
1179
|
+
if cr_tokens > 0:
|
|
1180
|
+
cache_saved += _cache_read_saved_usd(it.get("model"), cr_tokens)
|
|
1164
1181
|
# Copy the cache-failure marker onto the OutlineTurn exactly where
|
|
1165
1182
|
# tokens is copied (assistant-only, rides the same source row) and
|
|
1166
1183
|
# accumulate the session-level aggregate (spec §2).
|
|
@@ -1170,6 +1187,13 @@ def get_conversation_outline(conn, session_id):
|
|
|
1170
1187
|
cf_count += 1
|
|
1171
1188
|
cf_tokens += cf.get("tokens_recreated", 0)
|
|
1172
1189
|
cf_wasted += cf.get("est_wasted_usd", 0.0)
|
|
1190
|
+
cf_rebuilds.append({
|
|
1191
|
+
"uuid": t["uuid"],
|
|
1192
|
+
"subagent_key": t["subagent_key"],
|
|
1193
|
+
"ts": t["ts"],
|
|
1194
|
+
"tokens_recreated": cf.get("tokens_recreated", 0),
|
|
1195
|
+
"est_wasted_usd": cf.get("est_wasted_usd", 0.0),
|
|
1196
|
+
})
|
|
1173
1197
|
if kind == "meta":
|
|
1174
1198
|
t["meta_kind"] = it.get("meta_kind")
|
|
1175
1199
|
t["skill_name"] = it.get("skill_name")
|
|
@@ -1185,9 +1209,16 @@ def get_conversation_outline(conn, session_id):
|
|
|
1185
1209
|
"duration_seconds": duration, "tokens": tokens,
|
|
1186
1210
|
"cost_usd": asm["header_cost"]}
|
|
1187
1211
|
if cf_count:
|
|
1188
|
-
stats["cache_failures"] = {
|
|
1189
|
-
|
|
1190
|
-
|
|
1212
|
+
stats["cache_failures"] = {
|
|
1213
|
+
"count": cf_count,
|
|
1214
|
+
"tokens_recreated": cf_tokens,
|
|
1215
|
+
"est_wasted_usd": cf_wasted,
|
|
1216
|
+
"rebuilds": sorted(
|
|
1217
|
+
cf_rebuilds,
|
|
1218
|
+
key=lambda r: (-r["est_wasted_usd"], r["ts"] is None, r["ts"] or ""),
|
|
1219
|
+
),
|
|
1220
|
+
}
|
|
1221
|
+
stats["cache_saved_usd"] = cache_saved
|
|
1191
1222
|
return {"session_id": session_id,
|
|
1192
1223
|
"subagent_meta": asm["subagent_meta"],
|
|
1193
1224
|
"stats": stats,
|