cctally 1.74.0 → 1.75.1

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.
@@ -9,12 +9,14 @@ import sqlite3
9
9
  import sys
10
10
  from collections.abc import Mapping
11
11
  from dataclasses import dataclass, replace
12
- from types import MappingProxyType
12
+ from types import MappingProxyType, SimpleNamespace
13
13
  from typing import Any, Iterable
14
14
  from zoneinfo import ZoneInfo
15
15
 
16
16
  from _cctally_core import get_week_start_name
17
17
  from _cctally_quota import (
18
+ codex_five_hour_percent_at_crossing,
19
+ codex_quota_breakdown,
18
20
  codex_physical_mutation_seq,
19
21
  load_codex_quota_observations,
20
22
  load_codex_quota_projection_certificate,
@@ -88,6 +90,7 @@ class CodexCycleBoundary:
88
90
  resets_at: dt.datetime
89
91
  # Root provenance is server-only accounting input, never public wire data.
90
92
  source_root_keys: tuple[str, ...]
93
+ used_percent: float | None = None
91
94
 
92
95
 
93
96
  @dataclass(frozen=True)
@@ -97,6 +100,7 @@ class CodexWeeklyPeriod:
97
100
  start_at: dt.datetime
98
101
  end_at: dt.datetime
99
102
  source_root_keys: tuple[str, ...]
103
+ used_percent: float | None = None
100
104
 
101
105
 
102
106
  def _resolve_codex_weekly_cycle(
@@ -104,7 +108,7 @@ def _resolve_codex_weekly_cycle(
104
108
  now_utc: dt.datetime,
105
109
  ) -> CodexCycleBoundary:
106
110
  """Select exactly one active account-level 10,080-minute native cycle."""
107
- boundaries: dict[tuple[int, dt.datetime], set[str]] = {}
111
+ boundaries: dict[tuple[int, dt.datetime], dict[str, object]] = {}
108
112
  stale_weekly_evidence = False
109
113
  for history in build_history(tuple(observations)):
110
114
  if history.identity.window_minutes != 10_080:
@@ -116,17 +120,23 @@ def _resolve_codex_weekly_cycle(
116
120
  stale_weekly_evidence = True
117
121
  continue
118
122
  boundary = (history.identity.window_minutes, baseline.resets_at)
119
- boundaries.setdefault(boundary, set()).add(history.identity.source_root_key)
123
+ item = boundaries.setdefault(boundary, {"roots": set(), "used": []})
124
+ item["roots"].add(history.identity.source_root_key)
125
+ if baseline.used_percent is not None:
126
+ item["used"].append(float(baseline.used_percent))
120
127
  if len(boundaries) != 1:
121
128
  if not boundaries:
122
129
  raise CodexCycleUnavailable("stale" if stale_weekly_evidence else "missing")
123
130
  raise CodexCycleUnavailable("conflicting")
124
- (window_minutes, resets_at), root_keys = next(iter(boundaries.items()))
131
+ (window_minutes, resets_at), boundary_data = next(iter(boundaries.items()))
132
+ root_keys = boundary_data["roots"]
133
+ used_values = boundary_data["used"]
125
134
  return CodexCycleBoundary(
126
135
  window_minutes=window_minutes,
127
136
  start_at=resets_at - dt.timedelta(minutes=window_minutes),
128
137
  resets_at=resets_at,
129
138
  source_root_keys=tuple(sorted(root_keys)),
139
+ used_percent=max(used_values) if used_values else None,
130
140
  )
131
141
 
132
142
 
@@ -151,7 +161,7 @@ def _codex_weekly_periods(
151
161
  placeholders = ",".join("?" for _ in roots)
152
162
  try:
153
163
  rows = stats_conn.execute(
154
- "SELECT source_root_key, resets_at_utc, nominal_start_at_utc "
164
+ "SELECT source_root_key, resets_at_utc, nominal_start_at_utc, current_percent "
155
165
  "FROM quota_window_blocks "
156
166
  "WHERE source='codex' AND window_minutes=10080 "
157
167
  f"AND source_root_key IN ({placeholders}) AND orphaned_at IS NULL "
@@ -162,9 +172,9 @@ def _codex_weekly_periods(
162
172
  except sqlite3.Error:
163
173
  rows = ()
164
174
 
165
- raw_boundaries: list[tuple[dt.datetime, dt.datetime, set[str]]] = []
175
+ raw_boundaries: list[tuple[dt.datetime, dt.datetime, set[str], list[float]]] = []
166
176
 
167
- for root_key, resets_at_raw, start_at_raw in rows:
177
+ for root_key, resets_at_raw, start_at_raw, current_percent in rows:
168
178
  try:
169
179
  start_at = dt.datetime.fromisoformat(str(start_at_raw).replace("Z", "+00:00"))
170
180
  resets_at = dt.datetime.fromisoformat(str(resets_at_raw).replace("Z", "+00:00"))
@@ -176,17 +186,21 @@ def _codex_weekly_periods(
176
186
  resets_at = resets_at.astimezone(UTC)
177
187
  if resets_at <= start_at:
178
188
  continue
179
- raw_boundaries.append((start_at, resets_at, {str(root_key)}))
189
+ used_values = []
190
+ if isinstance(current_percent, (int, float)) and not isinstance(current_percent, bool):
191
+ used_values.append(float(current_percent))
192
+ raw_boundaries.append((start_at, resets_at, {str(root_key)}, used_values))
180
193
 
181
194
  if active_cycle is not None:
182
195
  raw_boundaries.append((
183
196
  active_cycle.start_at.astimezone(UTC),
184
197
  active_cycle.resets_at.astimezone(UTC),
185
198
  set(active_cycle.source_root_keys),
199
+ [active_cycle.used_percent] if active_cycle.used_percent is not None else [],
186
200
  ))
187
201
 
188
- ordered: list[tuple[dt.datetime, dt.datetime, set[str]]] = []
189
- for start_at, resets_at, period_roots in sorted(
202
+ ordered: list[tuple[dt.datetime, dt.datetime, set[str], list[float]]] = []
203
+ for start_at, resets_at, period_roots, used_values in sorted(
190
204
  raw_boundaries, key=lambda item: (item[0], item[1]),
191
205
  ):
192
206
  if (
@@ -194,13 +208,16 @@ def _codex_weekly_periods(
194
208
  and (start_at - ordered[-1][0]).total_seconds()
195
209
  < _FIVE_HOUR_JITTER_FLOOR_SECONDS
196
210
  ):
197
- first_start, latest_reset, existing_roots = ordered[-1]
211
+ first_start, latest_reset, existing_roots, existing_used = ordered[-1]
198
212
  existing_roots.update(period_roots)
199
- ordered[-1] = (first_start, max(latest_reset, resets_at), existing_roots)
213
+ existing_used.extend(used_values)
214
+ ordered[-1] = (
215
+ first_start, max(latest_reset, resets_at), existing_roots, existing_used,
216
+ )
200
217
  else:
201
- ordered.append((start_at, resets_at, set(period_roots)))
218
+ ordered.append((start_at, resets_at, set(period_roots), list(used_values)))
202
219
  periods: list[CodexWeeklyPeriod] = []
203
- for index, (start_at, resets_at, period_roots) in enumerate(ordered):
220
+ for index, (start_at, resets_at, period_roots, used_values) in enumerate(ordered):
204
221
  next_start = ordered[index + 1][0] if index + 1 < len(ordered) else None
205
222
  end_at = min(resets_at, next_start) if next_start is not None else resets_at
206
223
  if end_at <= start_at:
@@ -209,6 +226,7 @@ def _codex_weekly_periods(
209
226
  start_at=start_at,
210
227
  end_at=end_at,
211
228
  source_root_keys=tuple(sorted(period_roots)),
229
+ used_percent=max(used_values) if used_values else None,
212
230
  ))
213
231
  return tuple(periods)
214
232
 
@@ -245,6 +263,9 @@ class DashboardSourceSemantics:
245
263
  week_start_idx: int
246
264
  speed: str
247
265
  codex_budget: Mapping[str, object] | None
266
+ codex_quota_actual_thresholds: tuple[int, ...]
267
+ codex_quota_projected_thresholds: tuple[int, ...]
268
+ cache_report_anomaly_threshold_pp: int
248
269
  claude_identity: str
249
270
  codex_identity: str
250
271
 
@@ -267,6 +288,17 @@ def resolve_dashboard_source_semantics(
267
288
  week_start_idx = c.WEEKDAY_MAP[week_start_name]
268
289
  speed = c._resolve_codex_speed("auto")
269
290
  budget_config = c._get_budget_config(raw_config)
291
+ quota_alerts = c._get_quota_alerts_config(raw_config)
292
+ raw_cache_report = raw_config.get("cache_report")
293
+ raw_cache_threshold = (
294
+ raw_cache_report.get("anomaly_threshold_pp", 15)
295
+ if isinstance(raw_cache_report, Mapping) else 15
296
+ )
297
+ cache_threshold = (
298
+ int(raw_cache_threshold)
299
+ if isinstance(raw_cache_threshold, int) and not isinstance(raw_cache_threshold, bool)
300
+ and 1 <= raw_cache_threshold <= 100 else 15
301
+ )
270
302
  raw_codex_budget = budget_config.get("codex")
271
303
  codex_budget = (
272
304
  MappingProxyType(dict(raw_codex_budget))
@@ -290,6 +322,8 @@ def resolve_dashboard_source_semantics(
290
322
  }
291
323
  codex_identity_payload = {
292
324
  "codex_budget": dict(codex_budget) if codex_budget is not None else None,
325
+ "codex_quota_alerts": quota_alerts,
326
+ "cache_report_anomaly_threshold_pp": cache_threshold,
293
327
  "display_tz_name": display_tz_name,
294
328
  "speed": speed,
295
329
  "week_start_name": week_start_name,
@@ -306,6 +340,9 @@ def resolve_dashboard_source_semantics(
306
340
  week_start_idx=week_start_idx,
307
341
  speed=speed,
308
342
  codex_budget=codex_budget,
343
+ codex_quota_actual_thresholds=tuple(quota_alerts["actual_thresholds"]),
344
+ codex_quota_projected_thresholds=tuple(quota_alerts["projected_thresholds"]),
345
+ cache_report_anomaly_threshold_pp=cache_threshold,
309
346
  claude_identity=claude_identity,
310
347
  codex_identity=codex_identity,
311
348
  )
@@ -324,6 +361,9 @@ class DashboardReadContext:
324
361
  week_start_name: str = "monday"
325
362
  speed: str = "standard"
326
363
  codex_budget: Mapping[str, object] | None = None
364
+ codex_quota_actual_thresholds: tuple[int, ...] = ()
365
+ codex_quota_projected_thresholds: tuple[int, ...] = ()
366
+ cache_report_anomaly_threshold_pp: int = 15
327
367
 
328
368
  def __post_init__(self) -> None:
329
369
  for name in ("range_start", "now_utc"):
@@ -453,7 +493,7 @@ def _codex_budget_cost_events(
453
493
 
454
494
 
455
495
  def _bucket_wire(bucket: Any) -> dict[str, object]:
456
- return {
496
+ result = {
457
497
  "label": bucket.bucket,
458
498
  "cost_usd": bucket.cost_usd,
459
499
  "input_tokens": bucket.input_tokens,
@@ -464,6 +504,15 @@ def _bucket_wire(bucket: Any) -> dict[str, object]:
464
504
  "models": tuple(bucket.models),
465
505
  "model_breakdowns": tuple(dict(row) for row in bucket.model_breakdowns),
466
506
  }
507
+ for name in ("period_start_at", "period_end_at"):
508
+ value = getattr(bucket, name, None)
509
+ if isinstance(value, dt.datetime):
510
+ result[name.replace("period_", "")] = value.astimezone(UTC).isoformat()
511
+ for name in ("used_pct", "dollar_per_pct"):
512
+ value = getattr(bucket, name, None)
513
+ if value is not None:
514
+ result[name] = value
515
+ return result
467
516
 
468
517
 
469
518
  def _period_wire(view: Any) -> dict[str, object]:
@@ -475,6 +524,188 @@ def _period_wire(view: Any) -> dict[str, object]:
475
524
  }
476
525
 
477
526
 
527
+ def _codex_cache_report_wire(
528
+ entries: Iterable[object],
529
+ *,
530
+ metadata: Mapping[tuple[str, str], Mapping[str, object]],
531
+ now_utc: dt.datetime,
532
+ display_tz_name: str | None,
533
+ speed: str,
534
+ anomaly_threshold_pp: int = 15,
535
+ window_days: int = 14,
536
+ ) -> dict[str, object]:
537
+ """Compute the canonical cache report from Codex's inclusive counters.
538
+
539
+ Codex input is cache-inclusive, so the shared cache-report kernel receives
540
+ uncached input plus cached input as two disjoint counters. OpenAI does not
541
+ charge a cache-write premium; the counterfactual is therefore the exact
542
+ uncached-vs-cached input price difference for each token-count event.
543
+ """
544
+ c = sys.modules["cctally"]
545
+ crk = c._load_sibling("_lib_cache_report")
546
+ display_tz = ZoneInfo(display_tz_name) if display_tz_name else None
547
+ cutoff = now_utc - dt.timedelta(days=window_days)
548
+
549
+ def _tiered_cost(tokens: int, pricing: Mapping[str, object], base: str, above: str) -> float:
550
+ if tokens <= 0:
551
+ return 0.0
552
+ base_rate = float(pricing.get(base, 0.0) or 0.0)
553
+ above_rate = pricing.get(above)
554
+ threshold = int(c.CODEX_TIERED_THRESHOLD)
555
+ if tokens > threshold and above_rate is not None:
556
+ return threshold * base_rate + (tokens - threshold) * float(above_rate)
557
+ return tokens * base_rate
558
+
559
+ wrapped = []
560
+ for entry in entries:
561
+ timestamp = getattr(entry, "timestamp", None)
562
+ if not isinstance(timestamp, dt.datetime) or timestamp < cutoff:
563
+ continue
564
+ model = str(getattr(entry, "model", "") or "unknown")
565
+ input_tokens = int(getattr(entry, "input_tokens", 0))
566
+ cached_tokens = min(input_tokens, int(getattr(entry, "cached_input_tokens", 0)))
567
+ uncached_tokens = max(0, input_tokens - cached_tokens)
568
+ pricing, _is_fallback = c._resolve_codex_pricing(model)
569
+ pricing = pricing or {}
570
+ uncached_counterfactual = _tiered_cost(
571
+ cached_tokens, pricing,
572
+ "input_cost_per_token", "input_cost_per_token_above_272k_tokens",
573
+ )
574
+ cached_actual = _tiered_cost(
575
+ cached_tokens, pricing,
576
+ "cache_read_input_token_cost", "cache_read_input_token_cost_above_272k_tokens",
577
+ )
578
+ multiplier = c._codex_fast_multiplier(model) if speed == "fast" else 1.0
579
+ saved = max(0.0, uncached_counterfactual - cached_actual) * multiplier
580
+ identity = (
581
+ str(getattr(entry, "source_root_key", "") or ""),
582
+ str(getattr(entry, "source_path", "") or ""),
583
+ )
584
+ item_metadata = metadata.get(identity) or {}
585
+ project = (
586
+ str(getattr(entry, "project_label", "") or "").strip()
587
+ or str(item_metadata.get("project_label") or "").strip()
588
+ or "(unknown)"
589
+ )
590
+ wrapped.append(SimpleNamespace(
591
+ timestamp=timestamp,
592
+ model=model,
593
+ cost_usd=float(getattr(entry, "cost_usd", 0.0)),
594
+ project_path=project,
595
+ input_tokens=uncached_tokens,
596
+ output_tokens=int(getattr(entry, "output_tokens", 0)),
597
+ cache_creation_tokens=0,
598
+ cache_read_tokens=cached_tokens,
599
+ cache_saved_usd=saved,
600
+ cache_wasted_usd=0.0,
601
+ cache_net_usd=saved,
602
+ usage={
603
+ "input_tokens": uncached_tokens,
604
+ "output_tokens": int(getattr(entry, "output_tokens", 0)),
605
+ "cache_creation_input_tokens": 0,
606
+ "cache_read_input_tokens": cached_tokens,
607
+ },
608
+ ))
609
+
610
+ today_iso = now_utc.astimezone(display_tz or UTC).strftime("%Y-%m-%d")
611
+ if not wrapped:
612
+ return {
613
+ "window_days": window_days,
614
+ "anomaly_threshold_pp": anomaly_threshold_pp,
615
+ "anomaly_window_days": window_days,
616
+ "today": {
617
+ "date": today_iso, "cache_hit_percent": 0.0,
618
+ "baseline_median_percent": None, "delta_pp": None,
619
+ "net_usd": 0.0, "saved_usd": 0.0, "wasted_usd": 0.0,
620
+ "anomaly_triggered": False, "anomaly_reasons": (),
621
+ "baseline_daily_row_count": 0,
622
+ },
623
+ "days": (), "by_project": (), "by_model": (),
624
+ "seven_day_net_usd": 0.0, "seven_day_anomaly_count": 0,
625
+ "fourteen_day_counterfactual_usd": 0.0,
626
+ "fourteen_day_efficiency_ratio": 0.0, "is_empty": True,
627
+ }
628
+
629
+ result = crk._build_cache_report(
630
+ wrapped,
631
+ now_utc=now_utc,
632
+ window_days=window_days,
633
+ anomaly_threshold_pp=anomaly_threshold_pp,
634
+ anomaly_window_days=window_days,
635
+ display_tz=display_tz,
636
+ pricing=c.CODEX_MODEL_PRICING,
637
+ cost_calculator=lambda _model, _usage, _mode, cost: float(cost or 0.0),
638
+ )
639
+ raw_rows = sorted(result.rows, key=lambda row: row.date or "", reverse=True)
640
+ days = tuple({
641
+ "date": row.date or "",
642
+ "cache_hit_percent": row.cache_hit_percent,
643
+ "input_tokens": row.input_tokens,
644
+ "output_tokens": row.output_tokens,
645
+ "cache_creation_tokens": row.cache_creation_tokens,
646
+ "cache_read_tokens": row.cache_read_tokens,
647
+ "saved_usd": row.saved_usd,
648
+ "wasted_usd": row.wasted_usd,
649
+ "net_usd": row.net_usd,
650
+ "anomaly_triggered": row.anomaly_triggered,
651
+ "anomaly_reasons": tuple(row.anomaly_reasons),
652
+ } for row in raw_rows[:window_days])
653
+ today_row = next((row for row in raw_rows if row.date == today_iso), None)
654
+ baseline_count = sum(1 for row in raw_rows if row.date != today_iso)
655
+ baseline = result.today_baseline_median
656
+ today_hit = today_row.cache_hit_percent if today_row else 0.0
657
+ kept_dates = {row["date"] for row in days}
658
+ kept_entries = [
659
+ entry for entry in wrapped
660
+ if entry.timestamp.astimezone(display_tz or UTC).strftime("%Y-%m-%d") in kept_dates
661
+ ]
662
+ by_project = crk._aggregate_cache_breakdown(
663
+ kept_entries, key_fn=lambda entry: entry.project_path,
664
+ pricing=c.CODEX_MODEL_PRICING,
665
+ )
666
+ by_model = crk._aggregate_cache_breakdown(
667
+ kept_entries, key_fn=lambda entry: entry.model,
668
+ pricing=c.CODEX_MODEL_PRICING,
669
+ )
670
+ seven = days[:7]
671
+ saved_total = stable_sum(float(row["saved_usd"]) for row in days)
672
+ wasted_total = stable_sum(float(row["wasted_usd"]) for row in days)
673
+ efficiency_denom = saved_total + abs(wasted_total)
674
+ return {
675
+ "window_days": window_days,
676
+ "anomaly_threshold_pp": anomaly_threshold_pp,
677
+ "anomaly_window_days": window_days,
678
+ "today": {
679
+ "date": today_iso,
680
+ "cache_hit_percent": today_hit,
681
+ "baseline_median_percent": baseline,
682
+ "delta_pp": today_hit - baseline if baseline is not None else None,
683
+ "net_usd": today_row.net_usd if today_row else 0.0,
684
+ "saved_usd": today_row.saved_usd if today_row else 0.0,
685
+ "wasted_usd": today_row.wasted_usd if today_row else 0.0,
686
+ "anomaly_triggered": today_row.anomaly_triggered if today_row else False,
687
+ "anomaly_reasons": tuple(today_row.anomaly_reasons) if today_row else (),
688
+ "baseline_daily_row_count": baseline_count,
689
+ },
690
+ "days": days,
691
+ "by_project": tuple({
692
+ "key": row.key, "cache_hit_percent": row.cache_hit_percent,
693
+ "net_usd": row.net_usd,
694
+ } for row in by_project),
695
+ "by_model": tuple({
696
+ "key": row.key, "cache_hit_percent": row.cache_hit_percent,
697
+ "net_usd": row.net_usd,
698
+ } for row in by_model),
699
+ "seven_day_net_usd": stable_sum(float(row["net_usd"]) for row in seven),
700
+ "seven_day_anomaly_count": sum(bool(row["anomaly_triggered"]) for row in seven),
701
+ "fourteen_day_counterfactual_usd": saved_total,
702
+ "fourteen_day_efficiency_ratio": (
703
+ saved_total / efficiency_denom if efficiency_denom > 1e-9 else 0.0
704
+ ),
705
+ "is_empty": False,
706
+ }
707
+
708
+
478
709
  def _codex_conversation_metadata(
479
710
  cache_conn: sqlite3.Connection,
480
711
  ) -> dict[tuple[str, str], dict[str, object]]:
@@ -656,6 +887,9 @@ def _session_wire(
656
887
  "reasoning_output_tokens": row.reasoning_output_tokens,
657
888
  "total_tokens": row.total_tokens,
658
889
  "models": tuple(row.models),
890
+ "model_breakdowns": tuple(
891
+ dict(item) for item in getattr(row, "model_breakdowns", ())
892
+ ),
659
893
  })
660
894
  return {
661
895
  "rows": tuple(rows),
@@ -893,9 +1127,12 @@ def _configured_codex_budget_window(
893
1127
  def _quota_read_model(
894
1128
  context: DashboardReadContext,
895
1129
  observations: Iterable[object],
1130
+ *,
1131
+ accounting_entries: Iterable[object] = (),
896
1132
  ) -> dict[str, object]:
897
1133
  """Use S2's pure history/block/forecast kernels over cache evidence."""
898
1134
  quota_observations = tuple(observations)
1135
+ cost_entries = tuple(accounting_entries)
899
1136
  histories = build_history(quota_observations)
900
1137
  blocks = build_blocks(quota_observations)
901
1138
  history_rows: list[dict[str, object]] = []
@@ -955,7 +1192,82 @@ def _quota_read_model(
955
1192
  identity.window_minutes,
956
1193
  block.resets_at.astimezone(UTC).isoformat(),
957
1194
  )
1195
+ quota_key = dashboard_resource_key(
1196
+ "quota", "codex", identity.source_root_key,
1197
+ identity.logical_limit_key, identity.observed_slot,
1198
+ identity.window_minutes,
1199
+ )
1200
+ block_cost_entries = tuple(
1201
+ entry for entry in cost_entries
1202
+ if str(getattr(entry, "source_root_key", "")) == identity.source_root_key
1203
+ and block.nominal_start_at
1204
+ <= getattr(entry, "timestamp").astimezone(UTC)
1205
+ < block.resets_at
1206
+ )
1207
+ canonical_rows = ()
1208
+ if identity.window_minutes == 10_080 and block.resets_at > context.now_utc:
1209
+ try:
1210
+ canonical_rows = codex_quota_breakdown(
1211
+ identity,
1212
+ block.resets_at,
1213
+ speed=context.speed,
1214
+ cache_conn=context.cache_conn,
1215
+ stats_conn=context.stats_conn,
1216
+ )
1217
+ except sqlite3.Error:
1218
+ # Older or partially migrated stores retain the bounded
1219
+ # observation-derived fallback below. A coherent current
1220
+ # store always has the durable projection used by the CLI.
1221
+ canonical_rows = ()
1222
+ if canonical_rows:
1223
+ try:
1224
+ correlated_five_hour = tuple(
1225
+ observation
1226
+ for observation in load_codex_quota_observations(
1227
+ source_root_keys={identity.source_root_key},
1228
+ cache_conn=context.cache_conn,
1229
+ captured_at_or_after=block.nominal_start_at,
1230
+ )
1231
+ if observation.identity.window_minutes == 300
1232
+ and observation.identity.observed_slot == identity.observed_slot
1233
+ and observation.identity.limit_id == identity.limit_id
1234
+ )
1235
+ except sqlite3.Error:
1236
+ correlated_five_hour = ()
1237
+
1238
+ for row in canonical_rows:
1239
+ milestone_rows.append({
1240
+ "key": dashboard_resource_key(
1241
+ "quota_milestone", "codex", *block_parts,
1242
+ row.percent, row.captured_at.astimezone(UTC).isoformat(),
1243
+ ),
1244
+ "source": "codex",
1245
+ "block_key": dashboard_resource_key("block", "codex", *block_parts),
1246
+ "quota_key": quota_key,
1247
+ "window_minutes": identity.window_minutes,
1248
+ "resets_at": block.resets_at.astimezone(UTC).isoformat(),
1249
+ "percent": row.percent,
1250
+ "captured_at": row.captured_at.astimezone(UTC).isoformat(),
1251
+ "cumulative_usd": row.cost_usd,
1252
+ "marginal_usd": row.marginal_cost_usd,
1253
+ "input_tokens": row.input_tokens,
1254
+ "cached_input_tokens": row.cached_input_tokens,
1255
+ "output_tokens": row.output_tokens,
1256
+ "reasoning_output_tokens": row.reasoning_output_tokens,
1257
+ "total_tokens": row.total_tokens,
1258
+ "five_hour_percent": codex_five_hour_percent_at_crossing(
1259
+ identity, row.captured_at, correlated_five_hour,
1260
+ ),
1261
+ })
1262
+ continue
1263
+
1264
+ previous_cumulative = 0.0
958
1265
  for milestone in percent_milestones(block):
1266
+ cumulative_usd = stable_sum(
1267
+ float(getattr(entry, "cost_usd", 0.0))
1268
+ for entry in block_cost_entries
1269
+ if getattr(entry, "timestamp").astimezone(UTC) <= milestone.captured_at
1270
+ )
959
1271
  milestone_rows.append({
960
1272
  "key": dashboard_resource_key(
961
1273
  "quota_milestone", "codex", *block_parts,
@@ -963,9 +1275,15 @@ def _quota_read_model(
963
1275
  ),
964
1276
  "source": "codex",
965
1277
  "block_key": dashboard_resource_key("block", "codex", *block_parts),
1278
+ "quota_key": quota_key,
1279
+ "window_minutes": identity.window_minutes,
1280
+ "resets_at": block.resets_at.astimezone(UTC).isoformat(),
966
1281
  "percent": milestone.percent,
967
1282
  "captured_at": milestone.captured_at.astimezone(UTC).isoformat(),
1283
+ "cumulative_usd": cumulative_usd,
1284
+ "marginal_usd": max(0.0, cumulative_usd - previous_cumulative),
968
1285
  })
1286
+ previous_cumulative = cumulative_usd
969
1287
  latest_percent = max(
970
1288
  (float(row["current_percent"]) for row in active_rows), default=None,
971
1289
  )
@@ -1400,6 +1718,8 @@ def _partial_projects_wire(
1400
1718
  "output_tokens": 0,
1401
1719
  "reasoning_output_tokens": 0,
1402
1720
  "total_tokens": 0,
1721
+ "models": {},
1722
+ "session_rows": {},
1403
1723
  })
1404
1724
  timestamp = getattr(entry, "timestamp")
1405
1725
  group["first_seen"] = min(group["first_seen"], timestamp)
@@ -1410,6 +1730,27 @@ def _partial_projects_wire(
1410
1730
  "reasoning_output_tokens", "total_tokens",
1411
1731
  ):
1412
1732
  group[field] += getattr(entry, field)
1733
+ model = str(getattr(entry, "model", "") or "unknown")
1734
+ model_totals = group["models"].setdefault(model, {
1735
+ "model": model, "cost_usd": 0.0, "input_tokens": 0,
1736
+ "cached_input_tokens": 0, "output_tokens": 0,
1737
+ "reasoning_output_tokens": 0, "total_tokens": 0,
1738
+ })
1739
+ session_totals = group["session_rows"].setdefault(identity, {
1740
+ "label": str(row_metadata.get("title") or "Session"),
1741
+ "last_activity": timestamp.astimezone(UTC).isoformat(),
1742
+ "cost_usd": 0.0, "input_tokens": 0, "cached_input_tokens": 0,
1743
+ "output_tokens": 0, "reasoning_output_tokens": 0, "total_tokens": 0,
1744
+ })
1745
+ if timestamp.astimezone(UTC).isoformat() > session_totals["last_activity"]:
1746
+ session_totals["last_activity"] = timestamp.astimezone(UTC).isoformat()
1747
+ for field in (
1748
+ "cost_usd", "input_tokens", "cached_input_tokens", "output_tokens",
1749
+ "reasoning_output_tokens", "total_tokens",
1750
+ ):
1751
+ value = getattr(entry, field)
1752
+ model_totals[field] += value
1753
+ session_totals[field] += value
1413
1754
 
1414
1755
  rows = []
1415
1756
  for (root_key, _project_key), group in groups.items():
@@ -1426,6 +1767,14 @@ def _partial_projects_wire(
1426
1767
  "output_tokens": group["output_tokens"],
1427
1768
  "reasoning_output_tokens": group["reasoning_output_tokens"],
1428
1769
  "total_tokens": group["total_tokens"],
1770
+ "models": tuple(sorted(
1771
+ group["models"].values(),
1772
+ key=lambda item: (-float(item["cost_usd"]), str(item["model"])),
1773
+ )),
1774
+ "sessions": tuple(sorted(
1775
+ group["session_rows"].values(),
1776
+ key=lambda item: str(item["last_activity"]), reverse=True,
1777
+ )),
1429
1778
  })
1430
1779
  rows.sort(key=lambda row: (-float(row["cost_usd"]), str(row["label"]), str(row["key"])))
1431
1780
  return {
@@ -1482,6 +1831,7 @@ def _build_codex_native_weekly_view(
1482
1831
  bucket_by_entry: dict[int, str] = {}
1483
1832
  display_tz = ZoneInfo(display_tz_name) if display_tz_name else None
1484
1833
  labels: dict[str, str] = {}
1834
+ periods_by_bucket: dict[str, CodexWeeklyPeriod] = {}
1485
1835
  for entry in entries:
1486
1836
  timestamp = getattr(entry, "timestamp").astimezone(UTC)
1487
1837
  root_key = str(getattr(entry, "source_root_key", "") or "")
@@ -1501,13 +1851,29 @@ def _build_codex_native_weekly_view(
1501
1851
  if display_tz is not None else period.start_at.astimezone()
1502
1852
  )
1503
1853
  labels[bucket] = local_start.strftime("%m-%d %H:%M")
1854
+ periods_by_bucket[bucket] = period
1504
1855
 
1505
1856
  rows = _aggregate_codex_buckets(
1506
1857
  converted,
1507
1858
  key_fn=lambda entry: bucket_by_entry[id(entry)],
1508
1859
  speed=speed,
1509
1860
  )
1510
- display_rows = tuple(replace(row, bucket=labels[row.bucket]) for row in rows)
1861
+ display_rows = tuple(
1862
+ replace(
1863
+ row,
1864
+ bucket=labels[row.bucket],
1865
+ period_start_at=periods_by_bucket[row.bucket].start_at,
1866
+ period_end_at=periods_by_bucket[row.bucket].end_at,
1867
+ used_pct=periods_by_bucket[row.bucket].used_percent,
1868
+ dollar_per_pct=(
1869
+ row.cost_usd / periods_by_bucket[row.bucket].used_percent
1870
+ if periods_by_bucket[row.bucket].used_percent is not None
1871
+ and periods_by_bucket[row.bucket].used_percent > 0
1872
+ else None
1873
+ ),
1874
+ )
1875
+ for row in rows
1876
+ )
1511
1877
  return CodexWeeklyView(
1512
1878
  rows=display_rows,
1513
1879
  total_cost_usd=stable_sum(row.cost_usd for row in display_rows),
@@ -1661,7 +2027,11 @@ def build_codex_source_state(
1661
2027
  entries, now_utc=context.now_utc, tz_name=context.display_tz_name, speed=context.speed,
1662
2028
  )
1663
2029
  )
1664
- quota = _quota_read_model(context, quota_observations)
2030
+ quota = _quota_read_model(
2031
+ context,
2032
+ quota_observations,
2033
+ accounting_entries=visible_accounting_entries,
2034
+ )
1665
2035
  quota_blocks = _quota_wire(
1666
2036
  context.stats_conn,
1667
2037
  accounting_entries=visible_accounting_entries,
@@ -1677,6 +2047,14 @@ def build_codex_source_state(
1677
2047
  context, budget_entries, cost_events=budget_cost_events,
1678
2048
  )
1679
2049
  conversation_metadata = _codex_conversation_metadata(context.cache_conn)
2050
+ cache_report = _codex_cache_report_wire(
2051
+ visible_accounting_entries,
2052
+ metadata=conversation_metadata,
2053
+ now_utc=context.now_utc,
2054
+ display_tz_name=context.display_tz_name,
2055
+ speed=context.speed,
2056
+ anomaly_threshold_pp=context.cache_report_anomaly_threshold_pp,
2057
+ )
1680
2058
  projects = (
1681
2059
  _partial_projects_wire(visible_accounting_entries, conversation_metadata)
1682
2060
  if metadata_incomplete else _projects_wire(
@@ -1781,7 +2159,12 @@ def build_codex_source_state(
1781
2159
  "projected": projected_budget_rows,
1782
2160
  },
1783
2161
  "projects": projects,
1784
- "alerts": {"rows": alerts},
2162
+ "alerts": {
2163
+ "rows": alerts,
2164
+ "actual_thresholds": context.codex_quota_actual_thresholds,
2165
+ "projected_thresholds": context.codex_quota_projected_thresholds,
2166
+ },
2167
+ "cache_report": cache_report,
1785
2168
  },
1786
2169
  clock_data={"codex_budget_cost_events": budget_cost_events},
1787
2170
  )