fix(ai): honour the window status the endpoint reports as exhausted

The parser kept `current_*_status` but only read it to detect a not-in-plan
bucket, so an exhausted window (`status: 2`) that omits its optional remaining
percentage was dropped from the report entirely, and one that keeps a stale
percentage rendered as healthy quota. The status now outranks the percentage:
status 2 pins the window to a fully used fraction, which `usageStatus` maps to
`exhausted`, and the window survives with no percentage at all.
This commit is contained in:
Éverton Toffanetto
2026-07-26 02:30:00 -03:00
parent 50033beb5a
commit 07a734d8d9
2 changed files with 27 additions and 1 deletions
+7 -1
View File
@@ -14,6 +14,7 @@ const INTL_BASE_URL = "https://api.minimax.io";
const REMAINS_PATH = "/v1/token_plan/remains";
const HOUR_MS = 60 * 60 * 1000;
/** `current_*_status` enum reported per window: 1 normal, 2 exhausted, 3 unlimited. */
const STATUS_EXHAUSTED = 2;
const STATUS_UNLIMITED = 3;
/**
* The plan-wide token quota every chat model draws from. It is a quota category,
@@ -126,11 +127,14 @@ function buildLimit(args: {
durationMs?: number;
resetsAt?: number;
usedFraction: number | undefined;
windowStatus?: number;
usageCount?: number;
totalCount?: number;
accountId?: string;
}): UsageLimit | undefined {
const { usedFraction } = args;
// The endpoint's own status outranks the percentage: an exhausted window may
// omit it, or keep a stale one that would otherwise render as healthy quota.
const usedFraction = args.windowStatus === STATUS_EXHAUSTED ? 1 : args.usedFraction;
if (usedFraction === undefined) return undefined;
const totalCount = args.totalCount;
return {
@@ -181,6 +185,7 @@ function buildBucketLimits(provider: string, bucket: TokenPlanBucket, accountId:
durationMs: intervalDuration,
resetsAt: bucket.intervalEnd,
usedFraction: usedFractionFromRemainingPercent(bucket.intervalRemainingPercent),
windowStatus: bucket.intervalStatus,
usageCount: bucket.intervalUsageCount,
totalCount: bucket.intervalTotalCount,
accountId,
@@ -193,6 +198,7 @@ function buildBucketLimits(provider: string, bucket: TokenPlanBucket, accountId:
durationMs: weeklyDuration,
resetsAt: bucket.weeklyEnd,
usedFraction: usedFractionFromRemainingPercent(bucket.weeklyRemainingPercent),
windowStatus: bucket.weeklyStatus,
usageCount: bucket.weeklyUsageCount,
totalCount: bucket.weeklyTotalCount,
accountId,
@@ -249,6 +249,26 @@ describe("MiniMax Token Plan usage", () => {
expect(await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock })).toBeNull();
});
test("reports a window the endpoint marks exhausted, percentage or not", async () => {
const noPercentage: RemainsBucket = {
...generalBucket(),
current_interval_status: 2,
current_interval_remaining_percent: undefined,
};
const stalePercentage: RemainsBucket = { ...generalBucket(), current_interval_status: 2 };
for (const bucket of [noPercentage, stalePercentage]) {
const fetchMock: FetchImpl = () => Promise.resolve(Response.json(payloadOf(bucket)));
const report = await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock });
const interval = report?.limits.find(limit => limit.id === "general:4h");
expect(interval?.status).toBe("exhausted");
expect(interval?.amount).toMatchObject({ usedFraction: 1, remainingFraction: 0 });
expect(report?.limits.find(limit => limit.id === "general:7d")?.status).toBe("ok");
}
});
test("keeps a model that is not in the plan out of the reported quota", async () => {
const fetchMock: FetchImpl = () =>
Promise.resolve(Response.json(payloadOf(generalBucket(), notInPlanBucket("video"))));