Files
clawdbot/src/infra/provider-usage.fetch.gemini.ts
2026-01-14 05:40:03 +00:00

82 lines
2.0 KiB
TypeScript

import { fetchJson } from "./provider-usage.fetch.shared.js";
import { clampPercent, PROVIDER_LABELS } from "./provider-usage.shared.js";
import type {
ProviderUsageSnapshot,
UsageProviderId,
UsageWindow,
} from "./provider-usage.types.js";
type GeminiUsageResponse = {
buckets?: Array<{ modelId?: string; remainingFraction?: number }>;
};
export async function fetchGeminiUsage(
token: string,
timeoutMs: number,
fetchFn: typeof fetch,
provider: UsageProviderId,
): Promise<ProviderUsageSnapshot> {
const res = await fetchJson(
"https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota",
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: "{}",
},
timeoutMs,
fetchFn,
);
if (!res.ok) {
return {
provider,
displayName: PROVIDER_LABELS[provider],
windows: [],
error: `HTTP ${res.status}`,
};
}
const data = (await res.json()) as GeminiUsageResponse;
const quotas: Record<string, number> = {};
for (const bucket of data.buckets || []) {
const model = bucket.modelId || "unknown";
const frac = bucket.remainingFraction ?? 1;
if (!quotas[model] || frac < quotas[model]) quotas[model] = frac;
}
const windows: UsageWindow[] = [];
let proMin = 1;
let flashMin = 1;
let hasPro = false;
let hasFlash = false;
for (const [model, frac] of Object.entries(quotas)) {
const lower = model.toLowerCase();
if (lower.includes("pro")) {
hasPro = true;
if (frac < proMin) proMin = frac;
}
if (lower.includes("flash")) {
hasFlash = true;
if (frac < flashMin) flashMin = frac;
}
}
if (hasPro)
windows.push({
label: "Pro",
usedPercent: clampPercent((1 - proMin) * 100),
});
if (hasFlash)
windows.push({
label: "Flash",
usedPercent: clampPercent((1 - flashMin) * 100),
});
return { provider, displayName: PROVIDER_LABELS[provider], windows };
}