From 07a734d8d9c2192d670bf8db39f9a51da3d4c50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89verton=20Toffanetto?= Date: Sun, 26 Jul 2026 02:30:00 -0300 Subject: [PATCH] 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. --- packages/ai/src/usage/minimax-code.ts | 8 +++++++- .../ai/test/minimax-token-plan-usage.test.ts | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/ai/src/usage/minimax-code.ts b/packages/ai/src/usage/minimax-code.ts index 90f68ac70..9f913332d 100644 --- a/packages/ai/src/usage/minimax-code.ts +++ b/packages/ai/src/usage/minimax-code.ts @@ -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, diff --git a/packages/ai/test/minimax-token-plan-usage.test.ts b/packages/ai/test/minimax-token-plan-usage.test.ts index 463729b0d..056adc109 100644 --- a/packages/ai/test/minimax-token-plan-usage.test.ts +++ b/packages/ai/test/minimax-token-plan-usage.test.ts @@ -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"))));