mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 20:32:25 +03:00
fix(antigravity): keep unreported quota fraction unknown (#7138)
Merged after batch validation on a combined worktree cut from `release/v3.8.51` with #9944 and #9908. **Evidence** - Focused tests: 36/36 pass on the combined tree, including `convertUsageToQuotaInfo skips Antigravity quota entries with an unknown fraction` and the `#6295` regression that guards the same class of bug on another provider. - Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS. - The red `check-file-size` reproduces byte-identical on the pure `release/v3.8.51` tip — inherited base-red, not from this PR. The red CI run here dates from 2026-09-15 against an older base. Thanks, @Ardem2025 — this is the smallest diff of your batch and arguably the one with the widest blast radius avoided. Writing `remainingPercentage: 0` for an unreported fraction made "we don't know" numerically indistinguishable from "fully exhausted" to every downstream consumer of the quota cache; omitting the field so preflight fails open is the correct read of the upstream's silence.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- Fix Antigravity quota parsing treating an unreported `remainingFraction` as 0% remaining instead of unknown, which made a genuinely exhausted quota indistinguishable from one the upstream simply didn't report.
|
||||
@@ -654,12 +654,12 @@ export async function getAntigravityUsage(
|
||||
|
||||
const liveQuota = userQuotaEntries.get(modelKey);
|
||||
const quotaSource = liveQuota || quotaInfo;
|
||||
const rawFraction = toNumber(quotaSource.remainingFraction, -1);
|
||||
const rawFraction = toNumber(quotaSource.remainingFraction, Number.NaN);
|
||||
const resetAt = parseResetTime(quotaSource.resetTime);
|
||||
// Distinguish "upstream did not report remainingFraction" from "remaining is 0%".
|
||||
// fetchAvailableModels is a catalog view and can be stale/full; retrieveUserQuota is
|
||||
// the source of truth for actual Gemini consumption when it includes the model.
|
||||
const fractionReported = rawFraction >= 0;
|
||||
const fractionReported = Number.isFinite(rawFraction);
|
||||
if (!fractionReported) {
|
||||
console.warn(
|
||||
`[Antigravity] model ${modelKey} returned no remainingFraction — quota unknown`
|
||||
@@ -669,18 +669,22 @@ export async function getAntigravityUsage(
|
||||
// Models with no resetTime AND a reported full fraction are unlimited
|
||||
// (e.g. tab-completion models). Unreported fraction is NEVER unlimited.
|
||||
const isUnlimited = fractionReported && !resetAt && remainingFraction >= 1;
|
||||
const remainingPercentage = remainingFraction * 100;
|
||||
const QUOTA_NORMALIZED_BASE = 1000;
|
||||
const total = QUOTA_NORMALIZED_BASE;
|
||||
const total = fractionReported ? QUOTA_NORMALIZED_BASE : 0;
|
||||
const remaining = Math.round(total * remainingFraction);
|
||||
const used = isUnlimited ? 0 : Math.max(0, total - remaining);
|
||||
|
||||
quotas[modelKey] = applyLocalUsageFallback(
|
||||
{
|
||||
// An omitted fraction is unknown, not a 0% sentinel. Keep the reset
|
||||
// timestamp for display, but omit numeric quota fields so cache and
|
||||
// preflight fail open rather than turning uncertainty into exhaustion.
|
||||
used,
|
||||
total: isUnlimited ? 0 : total,
|
||||
resetAt,
|
||||
remainingPercentage: isUnlimited ? 100 : remainingPercentage,
|
||||
...(fractionReported && {
|
||||
remainingPercentage: isUnlimited ? 100 : remainingFraction * 100,
|
||||
}),
|
||||
unlimited: isUnlimited,
|
||||
fractionReported,
|
||||
quotaSource: liveQuota ? "retrieveUserQuota" : "fetchAvailableModels",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Tests for open-sse/services/usage.ts — Antigravity quota parsing.
|
||||
*
|
||||
* Verifies that remainingFraction is correctly parsed:
|
||||
* - undefined → 0% remaining (exhausted quota)
|
||||
* - undefined → unknown quota (not exhausted)
|
||||
* - 0 → 0% remaining (exhausted quota, explicit)
|
||||
* - 1.0 → 100% remaining (full quota)
|
||||
* - 1.0 without resetTime → unlimited (e.g. tab-completion)
|
||||
@@ -28,7 +28,7 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => {
|
||||
projectId: undefined,
|
||||
};
|
||||
|
||||
it("defaults to 0% remaining when remainingFraction is undefined", async () => {
|
||||
it("treats a missing remainingFraction as unknown rather than exhausted", async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = async () =>
|
||||
({
|
||||
@@ -53,9 +53,10 @@ describe("getUsageForProvider (antigravity in usage.ts)", () => {
|
||||
if ("quotas" in result) {
|
||||
const quota = result.quotas["gemini-3.7-flash-high"];
|
||||
assert.ok(quota, "should have quota for gemini-3.7-flash-high");
|
||||
assert.equal(quota.remainingPercentage, 0, "remaining should be 0%");
|
||||
assert.equal(quota.remainingPercentage, undefined, "unknown quota must not become 0%");
|
||||
assert.equal(quota.fractionReported, false, "missing fraction should be marked unknown");
|
||||
assert.equal(quota.unlimited, false, "should not be unlimited");
|
||||
assert.equal(quota.used > 0, true, "used should be > 0 when quota is exhausted");
|
||||
assert.equal(quota.used, 0, "unknown quota must not report usage");
|
||||
}
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
|
||||
@@ -142,6 +142,15 @@ test("registerGenericQuotaFetchers registers Claude, GLM, and OpenCode Go via th
|
||||
// semantics are exercised by the source code review.
|
||||
});
|
||||
|
||||
test("convertUsageToQuotaInfo skips Antigravity quota entries with an unknown fraction", () => {
|
||||
const result = convertUsageToQuotaInfo({
|
||||
quotas: {
|
||||
gemini: { fractionReported: false, resetAt: "2026-05-14T20:00:00Z" },
|
||||
},
|
||||
});
|
||||
assert.equal(result, null);
|
||||
});
|
||||
|
||||
test.afterEach(() => {
|
||||
__setGenericUsageFetcherForTests(null);
|
||||
__resetGenericQuotaFetcherForTests();
|
||||
@@ -329,7 +338,11 @@ test("in-flight fetch must not drop a concurrent 429 force-refresh", async () =>
|
||||
assert.equal(first?.percentUsed, 0.2);
|
||||
|
||||
const second = await fetchGenericQuota(connectionId, connection);
|
||||
assert.equal(calls.length, 2, "concurrent 429 must not let the in-flight recache wipe force-refresh");
|
||||
assert.equal(
|
||||
calls.length,
|
||||
2,
|
||||
"concurrent 429 must not let the in-flight recache wipe force-refresh"
|
||||
);
|
||||
assert.equal(calls[1]?.forceRefresh, true);
|
||||
assert.equal(second?.percentUsed, 0.9);
|
||||
invalidateGenericQuotaCache("agy", connectionId);
|
||||
|
||||
Reference in New Issue
Block a user