fix(ai): ranked antigravity bottleneck counters first
The google-antigravity ranking strategy originally returned the most-pressured counter as primary and the runner-up as secondary. AuthStorage compares the secondary ranking metrics before primary, so credentials with a dangerous bottleneck but a healthy runner-up counter could outrank credentials with balanced headroom. Return the bottleneck as secondary and the runner-up as primary, and update the strategy contract test to lock the ordering invariant. Fixes #2187
This commit is contained in:
@@ -312,9 +312,14 @@ const ANTIGRAVITY_DAILY_WINDOW_MS = 24 * 60 * 60 * 1000;
|
||||
* Anthropic / OpenAI) per tier per window, and {@link fetchAntigravityUsage}
|
||||
* sorts them ascending by `remainingFraction` — so `limits[0]` is always the
|
||||
* most-pressured counter for the credential, and `limits[1]` (when present)
|
||||
* is the next-most-pressured. Mapping those to {primary, secondary} lets the
|
||||
* ranker compare two credentials on both their bottleneck counter and their
|
||||
* runner-up before falling back to round-robin.
|
||||
* is the next-most-pressured counter.
|
||||
*
|
||||
* `AuthStorage` compares the `secondary*` ranking metrics before `primary*`
|
||||
* because other providers model a long-window budget as secondary. Antigravity
|
||||
* does not expose a short/long split; every counter is a sibling bottleneck.
|
||||
* Therefore the most-pressured counter goes in `secondary`, with the runner-up
|
||||
* in `primary`, so proactive account selection always ranks the bottleneck
|
||||
* before any healthier sibling counter.
|
||||
*
|
||||
* The Antigravity API exposes `resetTime` but not window duration, so the
|
||||
* drain-rate calculation depends on `windowDefaults`. Antigravity quotas are
|
||||
@@ -324,7 +329,7 @@ const ANTIGRAVITY_DAILY_WINDOW_MS = 24 * 60 * 60 * 1000;
|
||||
*/
|
||||
export const antigravityRankingStrategy: CredentialRankingStrategy = {
|
||||
findWindowLimits(report) {
|
||||
return { primary: report.limits[0], secondary: report.limits[1] };
|
||||
return { primary: report.limits[1], secondary: report.limits[0] };
|
||||
},
|
||||
windowDefaults: {
|
||||
primaryMs: ANTIGRAVITY_DAILY_WINDOW_MS,
|
||||
|
||||
@@ -257,18 +257,20 @@ describe("antigravity ranking strategy", () => {
|
||||
};
|
||||
}
|
||||
|
||||
it("maps the most-pressured counter to primary and the runner-up to secondary", () => {
|
||||
it("maps the most-pressured counter to secondary because AuthStorage compares secondary first", () => {
|
||||
// fetchAntigravityUsage sorts ascending by remainingFraction, so a real
|
||||
// report's limits[0] is always the bottleneck — the ranking strategy
|
||||
// MUST surface that as the primary signal, not the first model alphabetically.
|
||||
// report's limits[0] is always the bottleneck. AuthStorage compares the
|
||||
// secondary ranking metrics before primary, so Antigravity must put the
|
||||
// bottleneck there; otherwise [5%, 90%] remaining can beat [40%, 40%]
|
||||
// because the runner-up counter looks healthier.
|
||||
const report = {
|
||||
provider: "google-antigravity" as const,
|
||||
fetchedAt: Date.now(),
|
||||
limits: [makeLimit(0.05, "Anthropic"), makeLimit(0.4, "Google"), makeLimit(0.9, "OpenAI")],
|
||||
};
|
||||
const { primary, secondary } = antigravityRankingStrategy.findWindowLimits(report);
|
||||
expect(primary?.label).toBe("Anthropic");
|
||||
expect(secondary?.label).toBe("Google");
|
||||
expect(secondary?.label).toBe("Anthropic");
|
||||
expect(primary?.label).toBe("Google");
|
||||
});
|
||||
|
||||
it("returns undefined windows when the credential has no usage limits", () => {
|
||||
|
||||
Reference in New Issue
Block a user