Skip to content

Commit e1fc0f2

Browse files
authored
Merge pull request #834 from lidge-jun/agent/fix-quota-100-classification-815
fix(codex): distinguish exhausted quota from unknown
2 parents 75b903a + 1a7ba04 commit e1fc0f2

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

src/codex/quota.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ const MONTHLY_WINDOW_MIN_MINUTES = MONTHLY_WINDOW_MIN_SECONDS / 60;
5050

5151
const accountQuota = new Map<string, StoredAccountQuota>();
5252

53-
export const CODEX_UNKNOWN_USAGE_SCORE = 100;
53+
// Valid upstream percentages are normalized to 0..100. Keep "unknown" outside that domain so an
54+
// actually exhausted account is still eligible for threshold rotation.
55+
export const CODEX_UNKNOWN_USAGE_SCORE = 101;
5456
export const CODEX_EXHAUSTED_USAGE_PERCENT = 100;
5557

5658
export function isCodexQuotaExhausted(

tests/codex-routing.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ describe("codex routing", () => {
127127
test("usage score treats unknown quota conservatively", () => {
128128
expect(computeCodexUsageScore(null)).toBe(CODEX_UNKNOWN_USAGE_SCORE);
129129
expect(computeCodexUsageScore({})).toBe(CODEX_UNKNOWN_USAGE_SCORE);
130+
expect(computeCodexUsageScore({ weeklyPercent: 100 })).toBe(100);
131+
expect(CODEX_UNKNOWN_USAGE_SCORE).toBeGreaterThan(100);
130132
});
131133

132134
test("bulk pause exhaustion requires an explicit 100% relevant window", () => {
@@ -146,6 +148,25 @@ describe("codex routing", () => {
146148
expect(resolveCodexAccountForThread("new-thread", config)).toBe("b");
147149
});
148150

151+
test("known 100% weekly usage is exhausted, not unknown, and switches accounts", () => {
152+
const config = makeConfig();
153+
updateAccountQuota("a", 100);
154+
updateAccountQuota("b", 20);
155+
expect(resolveCodexAccountForThread("known-100-weekly", config)).toBe("b");
156+
});
157+
158+
test("known 100% Go monthly usage follows threshold switching", () => {
159+
const config = makeConfig({
160+
codexAccounts: [
161+
{ id: "a", email: "a@test", plan: "go", isMain: false },
162+
{ id: "b", email: "b@test", plan: "go", isMain: false },
163+
],
164+
});
165+
updateAccountQuota("a", 1, undefined, 100);
166+
updateAccountQuota("b", 99, undefined, 20);
167+
expect(resolveCodexAccountForThread("known-100-go-monthly", config)).toBe("b");
168+
});
169+
149170
test("missing OpenAI mode defaults to pool and rotates from hot main to a cool added account", () => {
150171
writeFileSync(join(TEST_DIR, "auth.json"), JSON.stringify({
151172
tokens: { access_token: "main-access", account_id: "main-chatgpt-id" },

0 commit comments

Comments
 (0)