Merge PR #6650: feat(ai): report MiniMax Token Plan quota in usage (@everton-dgn)

This commit is contained in:
can1357
2026-07-26 15:46:04 +02:00
4 changed files with 642 additions and 21 deletions
+1
View File
@@ -8,6 +8,7 @@
### Added
- MiniMax Token Plan accounts now report quota in `omp usage`. `GET /v1/token_plan/remains` returns one bucket per plan quota, each carrying a rolling interval window and a weekly window, so `minimax-code` surfaces real remaining percentages instead of an empty report. A model the plan does not include comes back looking like an untouched quota; those buckets are dropped from the report and named in its metadata. The mainland id `minimax-code-cn` is untouched.
- OAuth logins now stamp `authorizedAt` (epoch ms of the interactive login) on the stored credential, and every refresh-persist path preserves it. Anthropic expires the whole OAuth grant family ~30 days after authorization regardless of refresh-token rotation (observed as `invalid_grant: "Refresh token expired"` on the latest rotated token, exactly 30 days after login, across four production accounts), so the login anchor is what makes re-login deadlines computable. Exported `ANTHROPIC_OAUTH_GRANT_TTL_MS` alongside the anthropic OAuth flow.
- Added `GET /v1/credentials/disabled` to the auth broker and `AuthBrokerClient.listDisabledCredentials`: disabled-credential tombstones (`DisabledCredentialSummary` — identity, verbatim disable cause, disable timestamp; never token material) so auto-disabled accounts stay visible to clients instead of silently vanishing from the snapshot. `AuthStorage.listDisabledCredentials` serves the same data locally from SQLite; clients of brokers predating the endpoint get an empty list (404 mapped, no error).
- Added `AuthStorage.revalidateCredentials()` and the optional `AuthCredentialStore.refreshSnapshot` hook: remote broker stores re-fetch `GET /v1/snapshot` on demand so callers pairing live per-credential data with stored identities (`omp usage`) never render against the up-to-an-hour-stale disk-cached snapshot; local SQLite stores are always current and only reload.
+2
View File
@@ -54,6 +54,7 @@ import { googleGeminiCliUsageProvider } from "./usage/gemini";
import { githubCopilotUsageProvider } from "./usage/github-copilot";
import { antigravityRankingStrategy, antigravityUsageProvider } from "./usage/google-antigravity";
import { kimiUsageProvider } from "./usage/kimi";
import { minimaxCodeUsageProvider } from "./usage/minimax-code";
import { ollamaCloudUsageProvider, ollamaUsageProvider } from "./usage/ollama";
import { codexRankingStrategy, openaiCodexUsageProvider } from "./usage/openai-codex";
import {
@@ -645,6 +646,7 @@ const DEFAULT_USAGE_PROVIDERS: UsageProvider[] = [
alibabaTokenPlanUsageProvider,
openaiCodexUsageProvider,
kimiUsageProvider,
minimaxCodeUsageProvider,
antigravityUsageProvider,
googleGeminiCliUsageProvider,
ollamaUsageProvider,
+282 -21
View File
@@ -1,30 +1,291 @@
import type { UsageFetchContext, UsageFetchParams, UsageProvider, UsageReport } from "../usage";
import type {
UsageFetchContext,
UsageFetchParams,
UsageLimit,
UsageProvider,
UsageReport,
UsageStatus,
} from "../usage";
import { isRecord } from "../utils";
import { toNumber } from "./shared";
const INTL_PROVIDER = "minimax-code";
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;
/**
* MiniMax Token Plan usage provider.
*
* MiniMax Token Plan is a subscription-based service with a rolling quota system.
*
* Currently, MiniMax does not expose a usage/quota API endpoint for the Token Plan.
* Usage is tracked via the web dashboard at https://platform.minimax.io/user-center/payment/token-plan
*
* This provider exists to register support for the minimax-code provider in the
* usage system. When MiniMax adds a usage API, this can be implemented.
* The plan-wide token quota every chat model draws from. It is a quota category,
* not a catalog model id, so its limits are scoped `shared`: `AuthStorage` has no
* MiniMax ranking strategy and would otherwise match `scope.modelId` against ids
* like `MiniMax-M3` and drop the "models with usage data" mapping. Category
* buckets such as `video` meter a separate quota and keep their own model scope.
*/
async function fetchMiniMaxCodeUsage(params: UsageFetchParams, _ctx: UsageFetchContext): Promise<UsageReport | null> {
if (params.provider !== "minimax-code" && params.provider !== "minimax-code-cn") {
return null;
}
const SHARED_BUCKET = "general";
// MiniMax Token Plan does not currently expose a usage API
// Users can check their usage via the web dashboard
return null;
/** One `model_remains[]` bucket: a plan quota tracked over a rolling interval plus a weekly window. */
interface TokenPlanBucket {
modelName: string;
intervalStart?: number;
intervalEnd?: number;
intervalRemainingPercent?: number;
intervalTotalCount?: number;
intervalUsageCount?: number;
intervalStatus?: number;
weeklyStart?: number;
weeklyEnd?: number;
weeklyRemainingPercent?: number;
weeklyTotalCount?: number;
weeklyUsageCount?: number;
weeklyStatus?: number;
}
/** MiniMax reports epoch milliseconds; tolerate seconds in case a deployment differs. */
function parseTimestamp(value: unknown): number | undefined {
const parsed = toNumber(value);
if (parsed === undefined || parsed <= 0) return undefined;
return parsed < 1_000_000_000_000 ? parsed * 1000 : parsed;
}
/** `current_*_remaining_percent` is 0..100 remaining; usage fractions are 0..1 used. */
function usedFractionFromRemainingPercent(value: unknown): number | undefined {
const parsed = toNumber(value);
if (parsed === undefined || !Number.isFinite(parsed)) return undefined;
// (100 - p) / 100 keeps whole percentages exact; 1 - p / 100 does not (90 → 0.09999999999999998).
return Math.min(1, Math.max(0, (100 - parsed) / 100));
}
function usageStatus(usedFraction: number): UsageStatus {
if (usedFraction >= 1) return "exhausted";
if (usedFraction >= 0.9) return "warning";
return "ok";
}
function parseBucket(value: unknown): TokenPlanBucket | null {
if (!isRecord(value)) return null;
const modelName = typeof value.model_name === "string" ? value.model_name.trim() : "";
if (!modelName) return null;
return {
modelName,
intervalStart: parseTimestamp(value.start_time),
intervalEnd: parseTimestamp(value.end_time),
intervalRemainingPercent: toNumber(value.current_interval_remaining_percent),
intervalTotalCount: toNumber(value.current_interval_total_count),
intervalUsageCount: toNumber(value.current_interval_usage_count),
intervalStatus: toNumber(value.current_interval_status),
weeklyStart: parseTimestamp(value.weekly_start_time),
weeklyEnd: parseTimestamp(value.weekly_end_time),
weeklyRemainingPercent: toNumber(value.current_weekly_remaining_percent),
weeklyTotalCount: toNumber(value.current_weekly_total_count),
weeklyUsageCount: toNumber(value.current_weekly_usage_count),
weeklyStatus: toNumber(value.current_weekly_status),
};
}
/**
* A model outside the current plan is reported as both windows "unlimited"
* with zero totals and 100% remaining, which would otherwise read as a pristine
* quota. MiniMax's own CLI treats exactly this shape as "not in plan"
* ([MiniMax-AI/cli#173](https://github.com/MiniMax-AI/cli/issues/173)), so the
* bucket is kept out of the limits and named in `metadata.unavailableModels`.
* Zero totals alone are not enough: a live plan reports `0/0` with status 1 and
* a real remaining percentage.
*/
function isUnavailablePlan(bucket: TokenPlanBucket): boolean {
return (
bucket.intervalTotalCount === 0 &&
bucket.weeklyTotalCount === 0 &&
bucket.intervalStatus === STATUS_UNLIMITED &&
bucket.weeklyStatus === STATUS_UNLIMITED
);
}
/**
* Interval length varies per bucket (text quotas roll every few hours, media
* quotas daily), so the window id follows the reported span instead of a
* hardcoded tier. Spans that are not whole hours are labelled in minutes
* rather than rounded into a wrong hour count.
*/
function intervalWindowId(durationMs: number | undefined): { id: string; label: string } {
if (durationMs === undefined || durationMs <= 0) return { id: "interval", label: "Interval" };
if (durationMs % HOUR_MS === 0) {
const hours = durationMs / HOUR_MS;
return { id: `${hours}h`, label: `${hours} Hour` };
}
const minutes = Math.round(durationMs / 60_000);
if (minutes <= 0) return { id: "interval", label: "Interval" };
return { id: `${minutes}m`, label: `${minutes} Minute` };
}
function buildLimit(args: {
provider: string;
bucket: TokenPlanBucket;
windowId: string;
windowLabel: string;
durationMs?: number;
resetsAt?: number;
usedFraction: number | undefined;
windowStatus?: number;
usageCount?: number;
totalCount?: number;
accountId?: string;
}): UsageLimit | undefined {
// 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 {
id: `${args.bucket.modelName}:${args.windowId}`,
label: `${args.bucket.modelName.charAt(0).toUpperCase()}${args.bucket.modelName.slice(1)} ${args.windowLabel}`,
scope: {
provider: args.provider,
...(args.accountId ? { accountId: args.accountId } : {}),
...(args.bucket.modelName === SHARED_BUCKET ? { shared: true as const } : { modelId: args.bucket.modelName }),
windowId: args.windowId,
},
window: {
id: args.windowId,
label: args.windowLabel,
...(args.durationMs !== undefined && args.durationMs > 0 ? { durationMs: args.durationMs } : {}),
...(args.resetsAt ? { resetsAt: args.resetsAt } : {}),
},
amount: {
used: usedFraction * 100,
usedFraction,
remaining: 100 - usedFraction * 100,
remainingFraction: 1 - usedFraction,
unit: "percent",
},
status: usageStatus(usedFraction),
...(totalCount !== undefined && totalCount > 0
? { notes: [`Requests: ${args.usageCount ?? 0}/${totalCount}`] }
: {}),
};
}
function buildBucketLimits(provider: string, bucket: TokenPlanBucket, accountId: string | undefined): UsageLimit[] {
const intervalDuration =
bucket.intervalStart !== undefined && bucket.intervalEnd !== undefined
? bucket.intervalEnd - bucket.intervalStart
: undefined;
const weeklyDuration =
bucket.weeklyStart !== undefined && bucket.weeklyEnd !== undefined
? bucket.weeklyEnd - bucket.weeklyStart
: undefined;
const interval = intervalWindowId(intervalDuration);
return [
buildLimit({
provider,
bucket,
windowId: interval.id,
windowLabel: interval.label,
durationMs: intervalDuration,
resetsAt: bucket.intervalEnd,
usedFraction: usedFractionFromRemainingPercent(bucket.intervalRemainingPercent),
windowStatus: bucket.intervalStatus,
usageCount: bucket.intervalUsageCount,
totalCount: bucket.intervalTotalCount,
accountId,
}),
buildLimit({
provider,
bucket,
windowId: "7d",
windowLabel: "7 Day",
durationMs: weeklyDuration,
resetsAt: bucket.weeklyEnd,
usedFraction: usedFractionFromRemainingPercent(bucket.weeklyRemainingPercent),
windowStatus: bucket.weeklyStatus,
usageCount: bucket.weeklyUsageCount,
totalCount: bucket.weeklyTotalCount,
accountId,
}),
].filter((limit): limit is UsageLimit => limit !== undefined);
}
/**
* MiniMax Token Plan usage provider (international, `api.minimax.io`).
*
* `GET /v1/token_plan/remains` returns one `model_remains[]` bucket per plan
* quota (text, media, …), each carrying a rolling interval window and a weekly
* window with the remaining percentage. MiniMax answers HTTP 200 even for
* rejected credentials, so `base_resp.status_code` is the real success signal.
*/
async function fetchMiniMaxCodeUsage(params: UsageFetchParams, ctx: UsageFetchContext): Promise<UsageReport | null> {
if (params.provider !== INTL_PROVIDER) return null;
const apiKey = params.credential.apiKey;
if (params.credential.type !== "api_key" || !apiKey) return null;
try {
const configuredBaseUrl = params.baseUrl?.trim();
const baseUrl = configuredBaseUrl ? configuredBaseUrl.replace(/\/+$/, "").replace(/\/v1$/, "") : INTL_BASE_URL;
const response = await ctx.fetch(`${baseUrl}${REMAINS_PATH}`, {
headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}` },
signal: params.signal,
});
if (!response.ok) {
ctx.logger?.warn("MiniMax Token Plan usage fetch failed", {
provider: params.provider,
status: response.status,
});
return null;
}
const payload: unknown = await response.json();
if (!isRecord(payload)) return null;
const statusCode = isRecord(payload.base_resp) ? toNumber(payload.base_resp.status_code) : undefined;
if (statusCode !== 0) {
ctx.logger?.warn("MiniMax Token Plan usage response rejected", {
provider: params.provider,
statusCode: statusCode ?? "missing",
});
return null;
}
if (!Array.isArray(payload.model_remains)) return null;
const accountId = params.credential.accountId;
const limits: UsageLimit[] = [];
const models: string[] = [];
const unavailableModels: string[] = [];
for (const entry of payload.model_remains) {
const bucket = parseBucket(entry);
if (!bucket) continue;
models.push(bucket.modelName);
if (isUnavailablePlan(bucket)) {
unavailableModels.push(bucket.modelName);
continue;
}
limits.push(...buildBucketLimits(params.provider, bucket, accountId));
}
if (limits.length === 0) return null;
return {
provider: params.provider,
fetchedAt: Date.now(),
limits,
metadata: {
source: "minimax-token-plan",
models,
...(unavailableModels.length > 0 ? { unavailableModels } : {}),
...(accountId ? { accountId } : {}),
},
raw: payload,
};
} catch (error) {
ctx.logger?.warn("MiniMax Token Plan usage request failed", {
provider: params.provider,
error: error instanceof Error ? error.name : "unknown",
});
return null;
}
}
/** MiniMax Token Plan (international, `api.minimax.io`). */
export const minimaxCodeUsageProvider: UsageProvider = {
id: "minimax-code",
id: INTL_PROVIDER,
fetchUsage: fetchMiniMaxCodeUsage,
supports: (params: UsageFetchParams) =>
(params.provider === "minimax-code" || params.provider === "minimax-code-cn") &&
params.credential.type === "api_key",
supports: params =>
params.provider === INTL_PROVIDER && params.credential.type === "api_key" && Boolean(params.credential.apiKey),
};
@@ -0,0 +1,357 @@
import { describe, expect, test } from "bun:test";
import { type AuthCredentialStore, AuthStorage } from "@oh-my-pi/pi-ai/auth-storage";
import type { FetchImpl } from "@oh-my-pi/pi-ai/types";
import type { UsageFetchParams } from "@oh-my-pi/pi-ai/usage";
import { minimaxCodeUsageProvider } from "@oh-my-pi/pi-ai/usage/minimax-code";
const INTERVAL_START = 1_785_009_600_000;
const INTERVAL_END = 1_785_024_000_000;
const WEEKLY_START = 1_784_505_600_000;
const WEEKLY_END = 1_785_110_400_000;
function params(provider: "minimax-code" = "minimax-code", apiKey = "sk-cp-test"): UsageFetchParams {
return { provider, credential: { type: "api_key", apiKey }, accountKey: "account-1" };
}
function emptyStore(): AuthCredentialStore {
return {
close() {},
listAuthCredentials() {
return [];
},
updateAuthCredential() {},
deleteAuthCredential() {},
tryDisableAuthCredentialIfMatches() {
return false;
},
replaceAuthCredentialsForProvider() {
return [];
},
upsertAuthCredentialForProvider() {
return [];
},
deleteAuthCredentialsForProvider() {},
getCache() {
return null;
},
setCache() {},
cleanExpiredCache() {},
};
}
/** One `model_remains[]` entry: percentages and statuses are optional because the endpoint omits them. */
interface RemainsBucket {
model_name: string;
start_time: number;
end_time: number;
current_interval_total_count: number;
current_interval_usage_count: number;
current_interval_remaining_percent?: number;
current_interval_status?: number;
weekly_start_time: number;
weekly_end_time: number;
current_weekly_total_count: number;
current_weekly_usage_count: number;
current_weekly_remaining_percent?: number;
current_weekly_status?: number;
}
interface RemainsPayload {
model_remains: RemainsBucket[];
base_resp: { status_code: number; status_msg: string };
}
/** A live plan bucket: zero totals with status 1 still carry a real remaining percentage. */
function generalBucket(): RemainsBucket {
return {
model_name: "general",
start_time: INTERVAL_START,
end_time: INTERVAL_END,
current_interval_total_count: 0,
current_interval_usage_count: 0,
current_interval_remaining_percent: 90,
current_interval_status: 1,
weekly_start_time: WEEKLY_START,
weekly_end_time: WEEKLY_END,
current_weekly_total_count: 0,
current_weekly_usage_count: 0,
current_weekly_remaining_percent: 78,
current_weekly_status: 1,
};
}
/** A metered bucket: request counts on both windows. */
function videoBucket(): RemainsBucket {
return {
model_name: "video",
start_time: INTERVAL_END - 86_400_000,
end_time: INTERVAL_END,
current_interval_total_count: 3,
current_interval_usage_count: 1,
current_interval_remaining_percent: 100,
current_interval_status: 1,
weekly_start_time: WEEKLY_START,
weekly_end_time: WEEKLY_END,
current_weekly_total_count: 21,
current_weekly_usage_count: 1,
current_weekly_remaining_percent: 100,
current_weekly_status: 1,
};
}
function payloadOf(...buckets: RemainsBucket[]): RemainsPayload {
return { model_remains: buckets, base_resp: { status_code: 0, status_msg: "success" } };
}
function remainsPayload(): RemainsPayload {
return payloadOf(generalBucket(), videoBucket());
}
/** The bucket shape MiniMax returns for a model the plan does not include (MiniMax-AI/cli#173). */
function notInPlanBucket(modelName: string): RemainsBucket {
return {
model_name: modelName,
start_time: INTERVAL_START,
end_time: INTERVAL_END,
current_interval_total_count: 0,
current_interval_usage_count: 0,
current_interval_remaining_percent: 100,
current_interval_status: 3,
weekly_start_time: WEEKLY_START,
weekly_end_time: WEEKLY_END,
current_weekly_total_count: 0,
current_weekly_usage_count: 0,
current_weekly_remaining_percent: 100,
current_weekly_status: 3,
};
}
describe("MiniMax Token Plan usage", () => {
test("maps each quota bucket to its rolling and weekly windows", async () => {
const requests: { url: string; init?: RequestInit }[] = [];
const fetchMock: FetchImpl = (input, init) => {
requests.push({ url: String(input), init });
return Promise.resolve(Response.json(remainsPayload()));
};
const report = await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock });
expect(requests).toHaveLength(1);
expect(requests[0]?.url).toBe("https://api.minimax.io/v1/token_plan/remains");
expect(new Headers(requests[0]?.init?.headers).get("Authorization")).toBe("Bearer sk-cp-test");
expect(report?.provider).toBe("minimax-code");
expect(report?.metadata).toMatchObject({ source: "minimax-token-plan", models: ["general", "video"] });
expect(report?.limits.map(limit => limit.id)).toEqual(["general:4h", "general:7d", "video:24h", "video:7d"]);
const [intervalLimit, weeklyLimit, videoInterval, videoWeekly] = report?.limits ?? [];
expect(intervalLimit?.label).toBe("General 4 Hour");
expect(intervalLimit?.window).toEqual({
id: "4h",
label: "4 Hour",
durationMs: 14_400_000,
resetsAt: INTERVAL_END,
});
expect(intervalLimit?.amount).toEqual({
used: 10,
usedFraction: 0.1,
remaining: 90,
remainingFraction: 0.9,
unit: "percent",
});
expect(intervalLimit?.status).toBe("ok");
expect(intervalLimit?.notes).toBeUndefined();
expect(weeklyLimit?.window).toEqual({ id: "7d", label: "7 Day", durationMs: 604_800_000, resetsAt: WEEKLY_END });
expect(weeklyLimit?.amount).toEqual({
used: 22,
usedFraction: 0.22,
remaining: 78,
remainingFraction: 0.78,
unit: "percent",
});
expect(videoInterval?.label).toBe("Video 24 Hour");
expect(videoInterval?.amount.usedFraction).toBe(0);
expect(videoInterval?.notes).toEqual(["Requests: 1/3"]);
expect(videoWeekly?.notes).toEqual(["Requests: 1/21"]);
});
test("honors a configured base URL for the quota request", async () => {
// One case per trim the provider applies: trailing slash, trailing `/v1`, and both together.
for (const configured of ["https://proxy.example", "https://proxy.example/", "https://proxy.example/v1/"]) {
let requestedUrl = "";
const fetchMock: FetchImpl = input => {
requestedUrl = String(input);
return Promise.resolve(Response.json(remainsPayload()));
};
const request: UsageFetchParams = { ...params("minimax-code"), baseUrl: configured };
const report = await minimaxCodeUsageProvider.fetchUsage(request, { fetch: fetchMock });
expect(requestedUrl).toBe("https://proxy.example/v1/token_plan/remains");
expect(report?.provider).toBe("minimax-code");
}
});
test("fails closed when MiniMax rejects the key inside a 200 response", async () => {
const fetchMock: FetchImpl = () =>
Promise.resolve(
Response.json({
base_resp: { status_code: 1004, status_msg: "login fail: Please carry the API secret key" },
}),
);
expect(await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock })).toBeNull();
});
test("does not fetch without an API key credential", async () => {
let fetched = false;
const fetchMock: FetchImpl = () => {
fetched = true;
return Promise.resolve(Response.json(remainsPayload()));
};
const request: UsageFetchParams = { provider: "minimax-code", credential: { type: "oauth" } };
expect(minimaxCodeUsageProvider.supports?.(request)).toBe(false);
expect(await minimaxCodeUsageProvider.fetchUsage(request, { fetch: fetchMock })).toBeNull();
expect(fetched).toBe(false);
});
test("marks a spent window exhausted and drops buckets with no percentage", async () => {
const spentGeneral: RemainsBucket = { ...generalBucket(), current_interval_remaining_percent: 0 };
const videoWithoutPercentages: RemainsBucket = {
model_name: "video",
start_time: INTERVAL_END - 86_400_000,
end_time: INTERVAL_END,
current_interval_total_count: 3,
current_interval_usage_count: 1,
current_interval_status: 1,
weekly_start_time: WEEKLY_START,
weekly_end_time: WEEKLY_END,
current_weekly_total_count: 21,
current_weekly_usage_count: 1,
current_weekly_status: 1,
};
const fetchMock: FetchImpl = () =>
Promise.resolve(Response.json(payloadOf(spentGeneral, videoWithoutPercentages)));
const report = await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock });
expect(report?.limits.map(limit => limit.id)).toEqual(["general:4h", "general:7d"]);
expect(report?.limits[0]?.status).toBe("exhausted");
expect(report?.limits[0]?.amount.usedFraction).toBe(1);
});
test("returns null when the plan reports no quota buckets", async () => {
const fetchMock: FetchImpl = () =>
Promise.resolve(Response.json({ model_remains: [], base_resp: { status_code: 0 } }));
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"))));
const report = await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock });
expect(report?.limits.map(limit => limit.id)).toEqual(["general:4h", "general:7d"]);
expect(report?.metadata).toMatchObject({ models: ["general", "video"], unavailableModels: ["video"] });
});
test("returns null when every model is outside the plan", async () => {
const fetchMock: FetchImpl = () =>
Promise.resolve(Response.json(payloadOf(notInPlanBucket("general"), notInPlanBucket("video"))));
expect(await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock })).toBeNull();
});
test("keeps a bucket whose windows disagree about being in the plan", async () => {
const meteredWeekly: RemainsBucket = {
...notInPlanBucket("video"),
current_weekly_status: 1,
current_weekly_total_count: 21,
current_weekly_usage_count: 1,
current_weekly_remaining_percent: 40,
};
const meteredInterval: RemainsBucket = {
...notInPlanBucket("video"),
current_interval_status: 1,
current_interval_total_count: 3,
current_interval_usage_count: 1,
current_interval_remaining_percent: 40,
};
for (const bucket of [meteredWeekly, meteredInterval]) {
const fetchMock: FetchImpl = () => Promise.resolve(Response.json(payloadOf(bucket)));
const report = await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock });
expect(report?.limits.map(limit => limit.id)).toEqual(["video:4h", "video:7d"]);
expect(report?.metadata).not.toHaveProperty("unavailableModels");
}
});
test("rejects a payload with no base_resp envelope", async () => {
const fetchMock: FetchImpl = () =>
Promise.resolve(Response.json({ model_remains: remainsPayload().model_remains }));
expect(await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock })).toBeNull();
});
test("registers the Token Plan id in AuthStorage's default usage resolver", async () => {
const storage = new AuthStorage(emptyStore());
await storage.reload();
try {
expect(storage.usageProviderFor("minimax-code")).toBe(minimaxCodeUsageProvider);
expect(storage.usageProviderFor("minimax-code-cn")).toBeUndefined();
} finally {
storage.close();
}
});
test("reports the shared plan quota against catalog model ids", async () => {
const fetchMock: FetchImpl = () => Promise.resolve(Response.json(remainsPayload()));
const report = await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock });
if (!report) throw new Error("expected a usage report");
const general = report.limits.find(limit => limit.id === "general:4h");
expect(general?.scope).toEqual({ provider: "minimax-code", shared: true, windowId: "4h" });
const video = report.limits.find(limit => limit.id === "video:24h");
expect(video?.scope).toEqual({ provider: "minimax-code", modelId: "video", windowId: "24h" });
// Without a MiniMax ranking strategy AuthStorage matches `shared` or an exact
// catalog id, so a bucket-name scope would report no models at all.
const storage = new AuthStorage(emptyStore());
await storage.reload();
try {
expect(storage.getUsageReportingModelIds("minimax-code", ["MiniMax-M3", "MiniMax-M2"], [report])).toEqual([
"MiniMax-M3",
"MiniMax-M2",
]);
} finally {
storage.close();
}
});
});