fix(bar): preserve quota cache and reset semantics

This commit is contained in:
Tam Nhu Tran committed 2026-07-18 17:28:36 -04:00
1 parent f77ed33cc7
commit 8996c92fda
4 files changed
+163 -31

No files matched your search

@@ -29,6 +29,7 @@ import type {
CodexQuotaResult, CodexQuotaResult,
QuotaErrorMetadata, QuotaErrorMetadata,
} from '../../cliproxy/quota/quota-types'; } from '../../cliproxy/quota/quota-types';
import { shouldCacheQuotaResult } from './cliproxy-stats-routes/quota-helpers';
interface BindingWindow { interface BindingWindow {
name: string; name: string;
@@ -81,9 +82,22 @@ function pickBindingWindow(windows: BindingWindow[]): BindingWindow | null {
); );
} }
function pickEarliestReset(windows: BindingWindow[]): string | null {
return (
windows
.map((window) => ({
resetAt: window.resetAt,
timestamp: resetTimestamp(window.resetAt),
}))
.filter((reset) => Number.isFinite(reset.timestamp))
.sort((left, right) => left.timestamp - right.timestamp)[0]?.resetAt ?? null
);
}
function normalizeResult( function normalizeResult(
result: ProviderQuotaResult, result: ProviderQuotaResult,
bindingWindow: BindingWindow | null bindingWindow: BindingWindow | null,
earliestReset: string | null
): QuotaResult { ): QuotaResult {
return { return {
success: result.success, success: result.success,
@@ -94,7 +108,7 @@ function normalizeResult(
name: bindingWindow.name, name: bindingWindow.name,
displayName: bindingWindow.displayName, displayName: bindingWindow.displayName,
percentage: normalizeRemainingPercent(bindingWindow.remainingPercent), percentage: normalizeRemainingPercent(bindingWindow.remainingPercent),
resetTime: bindingWindow.resetAt, resetTime: earliestReset,
}, },
] ]
: [], : [],
@@ -116,16 +130,15 @@ function normalizeClaudeQuota(result: ClaudeQuotaResult): QuotaResult {
result.coreUsage?.fiveHour || result.coreUsage?.weekly result.coreUsage?.fiveHour || result.coreUsage?.weekly
? result.coreUsage ? result.coreUsage
: buildClaudeCoreUsageSummary(result.windows); : buildClaudeCoreUsageSummary(result.windows);
const bindingWindow = pickBindingWindow( const windows = [coreUsage?.fiveHour, coreUsage?.weekly].filter(isPresent).map((window) => ({
[coreUsage?.fiveHour, coreUsage?.weekly].filter(isPresent).map((window) => ({ name: window.rateLimitType,
name: window.rateLimitType, displayName: window.label,
displayName: window.label, remainingPercent: window.remainingPercent,
remainingPercent: window.remainingPercent, resetAt: window.resetAt,
resetAt: window.resetAt, }));
})) const bindingWindow = pickBindingWindow(windows);
);
const normalized = normalizeResult(result, bindingWindow); const normalized = normalizeResult(result, bindingWindow, pickEarliestReset(windows));
rawQuotaByNormalizedResult.set(normalized, result); rawQuotaByNormalizedResult.set(normalized, result);
return normalized; return normalized;
} }
@@ -135,16 +148,15 @@ function normalizeCodexQuota(result: CodexQuotaResult): QuotaResult {
result.coreUsage?.fiveHour || result.coreUsage?.weekly result.coreUsage?.fiveHour || result.coreUsage?.weekly
? result.coreUsage ? result.coreUsage
: buildCodexCoreUsageSummary(result.windows); : buildCodexCoreUsageSummary(result.windows);
const bindingWindow = pickBindingWindow( const windows = [coreUsage?.fiveHour, coreUsage?.weekly].filter(isPresent).map((window) => ({
[coreUsage?.fiveHour, coreUsage?.weekly].filter(isPresent).map((window) => ({ name: window.label,
name: window.label, displayName: window.label,
displayName: window.label, remainingPercent: window.remainingPercent,
remainingPercent: window.remainingPercent, resetAt: window.resetAt,
resetAt: window.resetAt, }));
})) const bindingWindow = pickBindingWindow(windows);
);
const normalized = normalizeResult(result, bindingWindow); const normalized = normalizeResult(result, bindingWindow, pickEarliestReset(windows));
rawQuotaByNormalizedResult.set(normalized, result); rawQuotaByNormalizedResult.set(normalized, result);
return normalized; return normalized;
} }
@@ -182,7 +194,17 @@ export function setCachedBarPoolAccountQuota<T>(
typeof data === 'object' && data !== null typeof data === 'object' && data !== null
? rawQuotaByNormalizedResult.get(data as unknown as QuotaResult) ? rawQuotaByNormalizedResult.get(data as unknown as QuotaResult)
: undefined; : undefined;
setCachedQuota(provider, accountId, raw ?? data); const cacheValue = raw ?? data;
if (
typeof cacheValue === 'object' &&
cacheValue !== null &&
'success' in cacheValue &&
typeof cacheValue.success === 'boolean' &&
!shouldCacheQuotaResult(cacheValue as ProviderQuotaResult)
) {
return;
}
setCachedQuota(provider, accountId, cacheValue);
} }
export function invalidateCachedBarPoolAccountQuota( export function invalidateCachedBarPoolAccountQuota(
@@ -57,7 +57,7 @@ function codexResult(overrides: Partial<CodexQuotaResult> = {}): CodexQuotaResul
}, },
weekly: { weekly: {
label: 'Secondary', label: 'Secondary',
remainingPercent: 64, remainingPercent: 24,
resetAfterSeconds: 259200, resetAfterSeconds: 259200,
resetAt: '2026-07-21T13:00:00.000Z', resetAt: '2026-07-21T13:00:00.000Z',
}, },
@@ -81,7 +81,7 @@ function createDeps(
} }
describe('Bar pool account quota adapter', () => { describe('Bar pool account quota adapter', () => {
it('maps the most restrictive Claude core window to percentage and reset', async () => { it('maps Claude percentage from the most restrictive window and reset from the earliest', async () => {
const fetchQuota = createBarPoolAccountQuotaFetcher(createDeps()); const fetchQuota = createBarPoolAccountQuotaFetcher(createDeps());
const result = await fetchQuota('claude', 'claude@example.com'); const result = await fetchQuota('claude', 'claude@example.com');
@@ -92,13 +92,13 @@ describe('Bar pool account quota adapter', () => {
name: 'seven_day', name: 'seven_day',
displayName: 'Weekly usage limit', displayName: 'Weekly usage limit',
percentage: 45, percentage: 45,
resetTime: '2026-07-21T12:00:00.000Z', resetTime: '2026-07-18T16:00:00.000Z',
}, },
]); ]);
expect(result.accountId).toBe('claude@example.com'); expect(result.accountId).toBe('claude@example.com');
}); });
it('maps the most restrictive Codex core window to percentage and reset', async () => { it('maps Codex percentage from the most restrictive window and reset from the earliest', async () => {
const fetchQuota = createBarPoolAccountQuotaFetcher(createDeps()); const fetchQuota = createBarPoolAccountQuotaFetcher(createDeps());
const result = await fetchQuota('codex', 'codex@example.com'); const result = await fetchQuota('codex', 'codex@example.com');
@@ -106,9 +106,9 @@ describe('Bar pool account quota adapter', () => {
expect(result.success).toBe(true); expect(result.success).toBe(true);
expect(result.models).toEqual([ expect(result.models).toEqual([
{ {
name: 'Primary', name: 'Secondary',
displayName: 'Primary', displayName: 'Secondary',
percentage: 36, percentage: 24,
resetTime: '2026-07-18T14:00:00.000Z', resetTime: '2026-07-18T14:00:00.000Z',
}, },
]); ]);
@@ -209,5 +209,4 @@ describe('Bar pool account quota adapter', () => {
errorCode: 'quota_not_supported', errorCode: 'quota_not_supported',
}); });
}); });
}); });
@@ -0,0 +1,111 @@
import { afterEach, describe, expect, it } from 'bun:test';
import type { QuotaResult } from '../../../src/cliproxy/quota/quota-fetcher';
import type {
ClaudeQuotaResult,
CodexQuotaResult,
} from '../../../src/cliproxy/quota/quota-types';
import {
clearQuotaCache,
getCachedQuota,
} from '../../../src/cliproxy/quota/quota-response-cache';
import {
createBarPoolAccountQuotaFetcher,
getCachedBarPoolAccountQuota,
setCachedBarPoolAccountQuota,
} from '../../../src/web-server/routes/bar-pool-account-quota-adapter';
function createFetcher(
claudeResult: ClaudeQuotaResult,
codexResult: CodexQuotaResult
): (provider: 'claude' | 'codex', accountId: string) => Promise<QuotaResult> {
return createBarPoolAccountQuotaFetcher({
fetchLegacyAccountQuota: async () => {
throw new Error('Legacy fetcher should not be called');
},
fetchClaudeQuota: async () => claudeResult,
fetchCodexQuota: async () => codexResult,
});
}
function claudeFailure(overrides: Partial<ClaudeQuotaResult> = {}): ClaudeQuotaResult {
return {
success: false,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
lastUpdated: 1,
accountId: 'claude@example.com',
...overrides,
};
}
function codexFailure(overrides: Partial<CodexQuotaResult> = {}): CodexQuotaResult {
return {
success: false,
windows: [],
coreUsage: { fiveHour: null, weekly: null },
planType: 'pro',
lastUpdated: 2,
accountId: 'codex@example.com',
...overrides,
};
}
afterEach(() => {
clearQuotaCache();
});
describe('Bar pool account quota cache policy', () => {
it('does not cache a retryable Claude timeout', async () => {
const raw = claudeFailure({
error: 'Claude OAuth usage request timeout',
errorCode: 'network_timeout',
retryable: true,
});
const fetchQuota = createFetcher(raw, codexFailure());
const result = await fetchQuota('claude', 'claude@example.com');
setCachedBarPoolAccountQuota('claude', 'claude@example.com', result);
expect(getCachedQuota('claude', 'claude@example.com')).toBeNull();
expect(getCachedBarPoolAccountQuota('claude', 'claude@example.com')).toBeNull();
});
it('does not cache a Codex 429 failure', async () => {
const raw = codexFailure({
error: 'Codex usage API rate limited',
errorCode: 'rate_limited',
httpStatus: 429,
});
const fetchQuota = createFetcher(claudeFailure(), raw);
const result = await fetchQuota('codex', 'codex@example.com');
setCachedBarPoolAccountQuota('codex', 'codex@example.com', result);
expect(getCachedQuota('codex', 'codex@example.com')).toBeNull();
expect(getCachedBarPoolAccountQuota('codex', 'codex@example.com')).toBeNull();
});
it('caches a stable Codex reauthentication failure in raw provider shape', async () => {
const raw = codexFailure({
error: 'Token expired or invalid',
errorCode: 'reauth_required',
httpStatus: 401,
needsReauth: true,
retryable: false,
});
const fetchQuota = createFetcher(claudeFailure(), raw);
const result = await fetchQuota('codex', 'codex@example.com');
setCachedBarPoolAccountQuota('codex', 'codex@example.com', result);
const shared = getCachedQuota<CodexQuotaResult>('codex', 'codex@example.com');
expect(shared).toEqual(raw);
expect((shared as CodexQuotaResult & Partial<QuotaResult>)?.models).toBeUndefined();
expect(getCachedBarPoolAccountQuota<QuotaResult>('codex', 'codex@example.com')).toMatchObject({
success: false,
models: [],
needsReauth: true,
httpStatus: 401,
});
});
});
@@ -100,13 +100,13 @@ it('stores normalized Claude quota in the existing Bar cache', async () => {
expect(fetchCount).toBe(1); expect(fetchCount).toBe(1);
expect(first[0]).toMatchObject({ expect(first[0]).toMatchObject({
quota_percentage: 45, quota_percentage: 45,
next_reset: '2026-07-21T12:00:00.000Z', next_reset: '2026-07-18T16:00:00.000Z',
quotaStatus: 'ok', quotaStatus: 'ok',
cached: false, cached: false,
}); });
expect(second[0]).toMatchObject({ expect(second[0]).toMatchObject({
quota_percentage: 45, quota_percentage: 45,
next_reset: '2026-07-21T12:00:00.000Z', next_reset: '2026-07-18T16:00:00.000Z',
quotaStatus: 'ok', quotaStatus: 'ok',
cached: true, cached: true,
}); });