Files
Moltbot/src/infra/provider-usage.fetch.gemini.ts
2026-02-19 12:51:30 +00:00

88 lines
2.0 KiB
TypeScript

import { buildUsageHttpErrorSnapshot, 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 buildUsageHttpErrorSnapshot({
provider,
status: 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 };
}