Skip to content

[Feature]: Allow Anthropic account-pool quota routing to select by weekly usage #2539

Closed

Description

@Yoonkeee

Area

Authentication and account pool

What are you trying to accomplish?

I run the Claude OAuth account pool (anthropicAccountPool.enabled: true) with several Claude subscriptions, and I want new sessions to be spread evenly across those accounts over a rolling 7-day window rather than only over the current 5-hour window.

Anthropic enforces two limits per account at the same time: a 5-hour rolling window and a weekly (7-day) rolling window. On a heavy coding day the 5-hour numbers of two accounts can look nearly identical while their weekly budgets are far apart — one account may be at 20% weekly and the other at 85% weekly. Today the pool treats those two accounts as interchangeable, so it keeps feeding the account that is close to burning its weekly allowance. By Thursday that account is weekly-limited for the rest of the week while the other one still has most of its weekly budget unused.

The outcome I need is: let the operator choose which quota window the pool optimizes for, so a multi-day workload finishes the week with the accounts drained roughly evenly instead of one account exhausted and the rest idle.

What prevents this today?

The pool's quota routing strategy scores accounts only by the 5-hour window. On current dev (98ed186c7), src/oauth/anthropic-routing.ts has:

function hasKnownUsage(accountId: string): boolean {
  const quota = getCachedProviderAccountQuota(PROVIDER, accountId);
  return typeof quota?.fiveHourPercent === "number" && Number.isFinite(quota.fiveHourPercent);
}

function usageScore(accountId: string): number {
  const quota = getCachedProviderAccountQuota(PROVIDER, accountId);
  if (!quota || typeof quota.fiveHourPercent !== "number" || !Number.isFinite(quota.fiveHourPercent)) {
    return UNKNOWN_USAGE_SCORE;
  }
  return Math.max(0, Math.min(100, quota.fiveHourPercent));
}

fiveHourPercent is the only usage field the whole module reads — it appears at lines 147, 152, and 155, and weeklyPercent appears nowhere in the file. Every usage-aware decision therefore inherits the 5-hour-only view: the lowest-usage pick for a new session, the fill-first "is the active account still under threshold" check, and the autoSwitchThreshold comparison.

The weekly number is already collected and cached — src/providers/quota.ts carries weeklyPercent on the per-account quota record (declared at line 94, populated at lines 518/664/713/749, and surfaced in the dashboard and ocx account output). So the data the router would need is present and fresh; it is simply never consulted when choosing an account.

There is no configuration escape hatch either. AnthropicAccountPoolConfig (src/oauth/anthropic-routing.ts:45-53) exposes only enabled, autoSwitchThreshold, strategy, and stickyLimit — nothing selects the window. The only workarounds available to an operator today are to manually disable an account before it burns its weekly budget, or to set autoSwitchThreshold very low, which changes 5-hour churn behaviour without ever looking at the weekly number.

What should OpenCodex do?

Add an opt-in quotaWindow setting to anthropicAccountPool that selects which quota window usage-aware selection scores against:

  • "five-hour" (default) — exactly today's behaviour. When quotaWindow is absent, unset, or unrecognized, account selection must be byte-for-byte identical to current dev. Existing installs must observe no change whatsoever.
  • "weekly" (new, opt-in) — rank candidates by their weekly usage percentage so a multi-day workload drains accounts evenly across the 7-day window.
  • "max-utilization" (new, opt-in) — rank by the higher of the two known percentages, i.e. by whichever window is closer to its limit, for operators who want the most conservative pick.

Behaviour that must be preserved and constrained:

  1. Session affinity is unchanged. The window only affects which account a new session binds to. A live session keeps its account exactly as it does today; there is no mid-session rebalancing.
  2. 429 cooldown and failover are unchanged. Cooldown, quarantine, reauth handling, and the per-request failover cap keep their current semantics. Selection must never return null in a case where it returns an account today.
  3. Weekly mode must not pick a 5-hour-exhausted account. An account whose 5-hour window is already at 100% is unusable right now no matter how good its weekly number looks. In weekly mode such accounts must be skipped — unless skipping them would leave no candidate at all, in which case the existing behaviour wins and an account is still returned.
  4. Unknown usage never wins. An account with no collected/parsed value for the configured window must sort last, never first, exactly as unknown 5-hour usage does today (UNKNOWN_USAGE_SCORE). A never-probed account must not be mistaken for an idle one.
  5. Deterministic ties. When two accounts score equal in the configured window, break the tie by the lower 5-hour percentage; if that is still equal, keep the existing stable ordering so selection stays reproducible.
  6. Scope. The setting belongs to the Anthropic pool only. It applies to the strategies that actually consult usage (quota and fill-first) and is inert under round-robin, which ignores usage by design. It should be reachable from the dashboard's account-pool settings and the management API alongside strategy and autoSwitchThreshold, and documented in the provider/Claude Code docs.

Example usage or interface

Configuration (opt-in; omitting quotaWindow keeps today's behaviour):

{
  "anthropicAccountPool": {
    "enabled": true,
    "strategy": "quota",
    "quotaWindow": "weekly"
  }
}

Concrete before/after with two pooled Claude accounts:

account 5-hour usage weekly usage
A 30% 85%
B 35% 20%

Before (today, and still the default): a new session picks account A, because 30% < 35% in the 5-hour window — even though A has only 15% of its weekly budget left and B has 80% left.

After, with "quotaWindow": "weekly": the same new session picks account B, because 20% < 85% in the weekly window, leaving A's remaining weekly budget for later in the week. If A's 5-hour window were instead at 100%, weekly mode would skip A regardless of its weekly figure; if B had never been probed and its weekly value were unknown, B would sort last rather than first.

Alternatives or workarounds

  • Manual account toggling / a very low autoSwitchThreshold. This is what I do today. It only manipulates the 5-hour window and requires me to watch the dashboard and disable an account by hand before it burns its weekly allowance; it is exactly the toil this proposal removes.
  • Always scoring by the weekly window (no config option). Rejected: it would silently change selection for every existing pool user, and the current 5-hour behaviour was a deliberate choice in the pool implementation. Opt-in with a "five-hour" default keeps existing installs untouched.
  • Always scoring by "whichever window is closer to its limit". Useful, but as a forced default it would surprise operators who intentionally want burst behaviour inside the 5-hour window. Offering it as the explicit "max-utilization" value gives that behaviour to operators who ask for it without imposing it.
  • A reset-time-aware "reset-pressure" score (weighting usage by how soon each window resets, so an account 5 minutes from its reset is not treated the same as one 6 days out) was considered and deliberately deferred to a separate follow-up proposal. It needs reset-timestamp trust rules and clock-skew handling of its own, and folding it in here would make this change much harder to review. This proposal stays scoped to selecting an existing, already-collected window.

Additional context

Verified related issues and pull requests on this repository (each checked with gh before filing):

Code references are against dev at 98ed186c7: src/oauth/anthropic-routing.ts (hasKnownUsage / usageScore at lines 145-156, pickLowestUsage at 194-208, AnthropicAccountPoolConfig at 45-53) and src/providers/quota.ts (weeklyPercent, line 94 and its population sites).

I am happy to implement this and open a pull request against dev if the direction is acceptable.

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

  1. added
    enhancementNew feature or request
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    proxyHTTP proxy, routing, reverse-proxy / management auth
    on Aug 25, 2026
  2. lidge-jun commented on Aug 25, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 59 / 80

    설명: 이 이슈는 클로드 계정 풀이 주간 사용량으로도 고르게 해 달라는 요청이다. 작성자는 Yoonkeee 이다. 라벨은 enhancement, account-pool, proxy 다. 지금 CURRENT dev HEAD 는 98ed186 이다. 이번 시간에 SHA 는 안 움직였다. 구현 풀은 아직 없다. 이슈를 연다. 닫지 말 것. 라벨은 바꾸지 말 것.

    HEAD 에서 주장을 다시 읽었다. src/oauth/anthropic-routing.ts 145-156줄 hasKnownUsage 와 usageScore 는 fiveHourPercent 만 본다. weeklyPercent 는 이 파일에 없다. 194-208줄 pickLowestUsage 와 290-296줄 isActiveUnderFillFirstThreshold 도 그 점수를 그대로 쓴다. 그래서 quota 전략의 새 세션 고르기와 fill-first 의 임계값 비교가 둘 다 5시간 창만 본다. AnthropicAccountPoolConfig 45-53줄은 enabled, autoSwitchThreshold, strategy, stickyLimit 만 있고 quotaWindow 칸이 없다.

    주간 숫자는 이미 있다. src/providers/quota.ts 91-95줄 ProviderQuota 에 weeklyPercent 와 weeklyResetAt 이 있다. 대시보드와 ocx 계정 출력도 그 칸을 보여 준다. 없는 것은 고를 때 그 칸을 보는 일이다. 코덱스 풀은 이미 다르다. docs-site/src/content/docs/reference/configuration/providers.md 의 autoSwitchThreshold 설명은 5시간, 주간, 30일 창 중 가장 뜨거운 값을 쓴다고 적는다. 앤트로픽 풀만 5시간에 고정되어 있다.

    제안 모양은 맞다. quotaWindow 기본은 five-hour 이고, 없거나 모르는 값은 지금과 같아야 한다. weekly 는 주간 비율로 새 세션을 고른다. max-utilization 은 두 창 중 높은 쪽이다. 세션 붙박이는 그대로다. 429 쿨다운과 페일오버도 그대로다. weekly 모드에서 5시간이 100% 인 계정은 지금 못 쓰니 건너뛴다. 다만 건너뛰면 후보가 없어지면 지금처럼 하나를 돌려 준다. 모르는 사용량은 맨 뒤다. 동점은 더 낮은 5시간, 그다음 지금 순서다. round-robin 은 사용량을 안 보니 이 칸이 잠자야 한다.

    화면에도 칸이 없다. gui/src/components/provider-workspace/AnthropicAccountPoolSettings.tsx 17-21줄과 104-109줄 PUT 본문은 enabled, threshold, strategy, stickyLimit 만 보낸다. 695 는 아직 열린 일반 풀 엔진 이슈다. 이 이슈를 695 로 닫지 말 것. 695 도 이 이슈로 닫지 말 것. 앤트로픽 창 선택은 그 엔진의 구체 질문이다. 리셋 시각을 가중하는 점수는 이슈가 미룬 그대로 다음 제안으로 둔다. types.ts 와 config.ts 가르기와는 아직 코드가 없다. 프리뷰 배포가 아니다. 내가 구현하지 않는다.

    src/oauth/anthropic-routing.ts 145-156줄 HEAD - 사용량 점수가 fiveHourPercent 만 본다
    src/oauth/anthropic-routing.ts 45-53줄 HEAD - quotaWindow 칸이 없다
    src/providers/quota.ts 94줄 HEAD - weeklyPercent 는 이미 캐시에 있다
    gui/src/components/provider-workspace/AnthropicAccountPoolSettings.tsx - 화면과 PUT 에 창 선택이 없다
    695 - 일반 풀 엔진이다. 열린 채로 둔다. 이 이슈와 합쳐 닫지 말 것

    메인테이너의 판단이 필요한 지점

    • 이 방향을 받을지. 받는 편이 맞다. 기본 five-hour 를 지키면 기존 설치가 안 바뀐다
    • 지금 구현을 시작할지. 내가 하지 않는다. 메인테이너가 방향을 정한 뒤에 작은 풀을 받는다
    • 695 로 흡수할지. 흡수하지 말 것. 695 는 일반 엔진이고 이 이슈는 앤트로픽 창이다
    • 기본을 max-utilization 로 할지. 하지 말 것. 코덱스 풀의 가장 뜨거운 창과 같아져 클로드 풀 사용자를 놀라게 한다
    • 이 이슈를 지금 닫을지. 닫지 말 것

    너의 추천
    이 이슈는 연다. 기본 five-hour 를 지키는 opt-in 칸으로 받는 편이 맞다. 구현은 anthropic-routing.ts 의 usageScore 와 hasKnownUsage 한곳과 설정 칸, 화면 칸이다. 695 는 연다. 내가 머지하지 않고 구현하지 않는다. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.

    이 댓글은 grok-bot이 작성했습니다

  3. lidge-jun commented on Aug 30, 2026

    @lidge-jun
    Owner

    Landed via #2982 at 46c3383 (carry of #2560).

  4. lidge-jun commented on Aug 30, 2026

    @lidge-jun
    Owner

    Feature is on dev via #2982 (carry of #2560) at 46c3383.

  5. lidge-jun commented on Aug 30, 2026

    @lidge-jun
    Owner

    Implemented on dev via #2982 as 46c3383.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansenhancementNew feature or requestproxyHTTP proxy, routing, reverse-proxy / management auth

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions