fix(usage): route agy quota through antigravity (#3232)

Route agy quota through the Antigravity usage implementation (closes #3230).

Integrated into release/v3.8.11. Thanks @wilsonicdev.
This commit is contained in:
Wilson
2026-06-05 13:12:40 -03:00
committed by GitHub
parent 8cdfee5d90
commit 5ec8fa222a
5 changed files with 87 additions and 1 deletions

View File

@@ -1424,6 +1424,7 @@ export const USAGE_FETCHER_PROVIDERS = [
"github",
"gemini-cli",
"antigravity",
"agy",
"claude",
"codex",
"cursor",
@@ -1467,6 +1468,7 @@ export async function getUsageForProvider(
case "gemini-cli":
return await getGeminiUsage(accessToken, providerSpecificData, projectId);
case "antigravity":
case "agy":
return await getAntigravityUsage(accessToken, providerSpecificData, projectId, id, options);
case "claude":
return await getClaudeUsage(accessToken);

View File

@@ -260,6 +260,7 @@ export function parseQuotaData(provider, data) {
break;
case "antigravity":
case "agy":
if (data.quotas) {
Object.entries(data.quotas).forEach(([modelKey, quota]: [string, any]) => {
if (modelKey === "credits") {

View File

@@ -37,6 +37,7 @@ export async function getUsageForProvider(connection) {
case "gemini-cli":
return await getGeminiUsage(accessToken);
case "antigravity":
case "agy":
return await getAntigravityUsage(
accessToken,
providerSpecificData,

View File

@@ -323,7 +323,7 @@ async function syncAntigravitySubscriptionIfNeeded(
connection: ProviderConnectionLike,
usage: JsonRecord
): Promise<ProviderConnectionLike> {
if (connection.provider !== "antigravity") return connection;
if (connection.provider !== "antigravity" && connection.provider !== "agy") return connection;
const subscriptionInfo = usage.subscriptionInfo;
if (!subscriptionInfo) return connection;

View File

@@ -0,0 +1,82 @@
import test, { mock } from "node:test";
import assert from "node:assert/strict";
const usageModule = await import("../../open-sse/services/usage.ts");
const providerLimitUtils =
await import("../../src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx");
test("agy is registered for the background usage fetcher", () => {
assert.ok(
usageModule.USAGE_FETCHER_PROVIDERS.includes("agy"),
"agy should be fetched by the generic quota refresher"
);
});
test("getUsageForProvider routes agy through the Antigravity usage implementation", async () => {
const fetchMock = mock.method(globalThis, "fetch", async () => ({
ok: true,
json: async () => ({
models: {
"gemini-3.5-flash-preview": {
quotaInfo: {
remainingFraction: 0.75,
resetTime: "2026-06-06T00:00:00Z",
},
},
},
}),
}));
try {
const result = await usageModule.getUsageForProvider(
{
id: "agy-test-conn",
provider: "agy",
accessToken: "fake-token",
providerSpecificData: {},
},
{ forceRefresh: true }
);
assert.ok(result && typeof result === "object");
assert.notEqual(
(result as { message?: string }).message,
"Usage API not implemented for agy",
"agy must not fall through to the unsupported-provider branch"
);
assert.ok("quotas" in result, "agy should return quota data when upstream responds");
const quota = (result as { quotas: Record<string, any> }).quotas["gemini-3.5-flash-preview"];
assert.ok(quota, "should expose the Antigravity per-model quota for agy");
assert.equal(quota.remainingPercentage, 75);
} finally {
fetchMock.mock.restore();
}
});
test("parseQuotaData treats agy quota payloads like Antigravity", () => {
const parsed = providerLimitUtils.parseQuotaData("agy", {
quotas: {
credits: { remaining: 42 },
"gemini-3.5-flash-preview": {
used: 250,
total: 1000,
remainingPercentage: 75,
},
models: { used: 0, total: 0 },
},
});
assert.equal(
parsed.length,
2,
"credits and model quota should be rendered, models summary skipped"
);
const credits = parsed.find((quota: any) => quota.name === "credits");
assert.ok(credits, "credits quota should be rendered");
assert.equal(credits.isCredits, true);
const modelQuota = parsed.find((quota: any) => quota.name === "gemini-3.5-flash-preview");
assert.ok(modelQuota, "model quota should be rendered");
assert.equal(modelQuota.remainingPercentage, 75);
});