Repository navigation
feat(routing): same-provider earliest-expiry-first account scheduling - #403
Conversation
caigee-cmd
left a comment
There was a problem hiding this comment.
The scheduling boundary is the part that matters, and it is drawn correctly:
- opt-in
fefoonly; round-robin / WRR / fill-first stay unchanged - rank inside one provider family, never across Qoder vs WorkBuddy
- only WorkBuddy personal packs with an explicit
account-fefoscope and a realCycleEndTime - enterprise / shared / 100-row pages clear the scope
- Qoder subscription
expiresAt, Trae/Devin/Codex/Command Code reset windows, and token lifetimes never become a credit expiry - same UTC+8 day still rotates; unknown-date accounts are fallbacks
- after-use quota refresh is coalesced and off the request path
AfterAccountUse on finishRequestLog does cover Chat Completions, Anthropic Messages, and Responses (finishCompatibility → finishRequestLog). Skipping RequestStatusStreaming so the hook waits for the body is correct.
Independently useful even without FEFO: soonestExpiry no longer advertises a depleted or already-expired pack.
Do not merge as-is: the PR conflicts with main after #400 (mostly hashed internal/webui/static). Rebase, re-run cd frontend && npm run sync, and this is ready to approve.
caigee-cmd
left a comment
There was a problem hiding this comment.
Architecture check against AGENTS.md / docs/ARCHITECTURE_SUMMARY.md — the layering is correct, still blocked on rebase.
- Pick stays in
executor.fefo.go/PickRouteis Pool scheduling. Runtime does not choose accounts. Matches “Pool / 选号 live in executor; runtime does not own pick strategy”. - Refresh stays in
runtime.RefreshFEFOQuotaAfterUse, the quota fetch gate, and the stale-package probe are account lifecycle, not a public-protocol concern. - Gateway does not import runtime.
AfterAccountUseis an injected callback fromapp. That is required: executorChatStreamreturns at response headers, the body is relayed in gateway, so after-use cannot live in executor without firing too early.finishRequestLogalready covers Chat Completions, Messages, and Responses. accountsstays a data type.RoutingStrategyFEFOandQuotaPackage.Scopeare entity fields. Noaccounts→executor/runtimeimport.- Provider maps upstream facts. WorkBuddy sets/clears
account-fefofromget-user-resource; executor never invents QoderexpiresAtor token lifetimes as credit expiry. Personal-account routing, not billing.
Still do not merge until rebased onto current main with cd frontend && npm run sync.
6440f1d to
0e5b4ff
Compare
Rebased onto main after #403; regenerated webui static assets.
Rebased onto main after #403; regenerated webui static assets. Co-authored-by: caigee-cmd <caigee-cmd@users.noreply.github.com>
Summary
feforouting strategy that ranks accounts only inside the same provider family by the nearest spendable credit-package expiry. Same UTC+8 expiry day rotates fairly; a provider with no package dates keeps round-robin behavior, and unknown-date accounts are fallback candidates inside a dated provider. Existing round-robin, weighted round-robin, fill-first, affinity, pinning, provider/region gates, cooldowns, and failover are unchanged.expires_atsummary and use the billing UTC+8 clock for the package query window.Provider data boundary
get-user-resourcereturns per-packageCycleEndTime, so these accounts can be ranked by real package expiry./api/v2/quota/usageexposes only the subscription cycle boundary (expiresAt) plus aggregate user/add-on/org buckets. It does not expose per-package credit expiry. A Qoder five-account example therefore cannot be ranked by actual credit expiry with the current upstream API; Qoder accounts stay in the unknown-date cohort and keep the existing rotation. No subscription reset or token lifetime is reused as a credit expiry.Test plan
go build ./...go vet ./...go test ./internal/accounts ./internal/gateway ./internal/control ./internal/providers/workbuddygo test ./internal/executor(excluding two pre-existing Windows/time-sensitive failures also present onmain:TestRepeatedFailuresBackOff,TestBackoffResetsOnKindChange)go test ./internal/runtime(excluding pre-existing Windows path failureTestQoderRuntimeSpecSelectsCNCLIAndConfigDir)go test ./internal/api -run 'TestSystemSettingsRoutePersists.*RoutingStrategy'cd frontend && npm run build && npm run lint && npm run syncchangelog/unreleased/fefo-credit-expiry-routing.mdcd worker && npm test- no worker changeLive paid-account A/B acceptance was not run in this environment; routing, quota refresh, cooldown/saturation escape, same-day rotation, cross-provider isolation, and legacy-strategy compatibility are covered by synthetic Go tests.