lvwerra HF Staff Claude Fable 5 commited on
Commit
8baf1a7
·
1 Parent(s): 77658da

Codex usage: label quota windows by length, not slot position

Browse files

The Usage card assumed codex reports the 5-hour window as 'primary'
and the weekly as 'secondary', but codex doesn't always order them
that way — so a block that led with the weekly got shown as '5-hour'
(and the real 5-hour vanished). Classify each window by its
window_minutes instead (<=6h is the 5-hour bucket, longer is weekly),
scanning newest lines first to fill both. Verified against normal,
reversed, and weekly-only blocks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Files changed (1) hide show
  1. server/src/usage.js +13 -3
server/src/usage.js CHANGED
@@ -146,17 +146,27 @@ async function tailOf(p, bytes = 262_144) {
146
  function codexQuota() {
147
  return cached('codexq', async () => {
148
  const win = (w) => (w ? { usedPercent: w.used_percent, windowMinutes: w.window_minutes, resetsAt: toEpochSec(w.resets_at) } : null);
149
- // newest files first, newest lines first, bounded work per file
 
 
 
 
150
  for (const f of (await codexRollouts()).slice(0, 20)) {
151
  let lines;
152
  try { lines = (await tailOf(f.p)).split('\n'); } catch { continue; }
153
- for (let i = lines.length - 1; i >= 0; i--) {
154
  const ln = lines[i];
155
  if (!ln) continue;
156
  let j; try { j = JSON.parse(ln); } catch { continue; }
157
  const r = findRateLimits(j);
158
- if (r) return { fiveHour: win(r.primary), weekly: win(r.secondary) };
 
 
 
 
 
159
  }
 
160
  }
161
  return null;
162
  });
 
146
  function codexQuota() {
147
  return cached('codexq', async () => {
148
  const win = (w) => (w ? { usedPercent: w.used_percent, windowMinutes: w.window_minutes, resetsAt: toEpochSec(w.resets_at) } : null);
149
+ // Classify each window by its length, NOT by primary/secondary position:
150
+ // codex doesn't always report the 5-hour window first, so trusting the
151
+ // slot mislabeled the weekly as "5-hour" (and hid the real 5-hour). A
152
+ // window <= 6h is the 5-hour bucket; anything longer is the weekly.
153
+ let fiveHour = null, weekly = null;
154
  for (const f of (await codexRollouts()).slice(0, 20)) {
155
  let lines;
156
  try { lines = (await tailOf(f.p)).split('\n'); } catch { continue; }
157
+ for (let i = lines.length - 1; i >= 0 && !(fiveHour && weekly); i--) {
158
  const ln = lines[i];
159
  if (!ln) continue;
160
  let j; try { j = JSON.parse(ln); } catch { continue; }
161
  const r = findRateLimits(j);
162
+ if (!r) continue;
163
+ for (const w of [r.primary, r.secondary]) {
164
+ if (!w || w.window_minutes == null) continue;
165
+ if (w.window_minutes <= 360) { if (!fiveHour) fiveHour = win(w); }
166
+ else if (!weekly) weekly = win(w);
167
+ }
168
  }
169
+ if (fiveHour || weekly) return { fiveHour, weekly };
170
  }
171
  return null;
172
  });