feat(ai): land the Token Plan usage provider for the international host only

Registering a second default provider fetch was flagged as needing maintainer
sign-off, and `api.minimaxi.com` cannot be exercised by anyone on this change,
so the mainland id is left exactly as it was: `minimax-code-cn` is not
registered, its base URL and provider export are gone, and `supports()` no
longer claims an id that resolves to nothing.

The parser, the not-in-plan handling and the shared scope are unchanged and keep
their tests; the suite now asserts that the CN id resolves to no usage provider,
so wiring it later is a deliberate change rather than a silent one.
This commit is contained in:
Éverton Toffanetto
2026-07-26 02:09:51 -03:00
parent d937a4b908
commit 50033beb5a
4 changed files with 12 additions and 40 deletions
+1 -1
View File
@@ -4,7 +4,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` and `minimax-code-cn` surface real remaining percentages instead of empty reports. Both ids are registered in the default usage provider set — the China id previously resolved to nothing, so it never reached a fetcher at all.
- 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.
+1 -2
View File
@@ -54,7 +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 { minimaxCodeCnUsageProvider, minimaxCodeUsageProvider } from "./usage/minimax-code";
import { minimaxCodeUsageProvider } from "./usage/minimax-code";
import { ollamaCloudUsageProvider, ollamaUsageProvider } from "./usage/ollama";
import { codexRankingStrategy, openaiCodexUsageProvider } from "./usage/openai-codex";
import {
@@ -645,7 +645,6 @@ const DEFAULT_USAGE_PROVIDERS: UsageProvider[] = [
openaiCodexUsageProvider,
kimiUsageProvider,
minimaxCodeUsageProvider,
minimaxCodeCnUsageProvider,
antigravityUsageProvider,
googleGeminiCliUsageProvider,
ollamaUsageProvider,
+3 -17
View File
@@ -10,9 +10,7 @@ import { isRecord } from "../utils";
import { toNumber } from "./shared";
const INTL_PROVIDER = "minimax-code";
const CN_PROVIDER = "minimax-code-cn";
const INTL_BASE_URL = "https://api.minimax.io";
const CN_BASE_URL = "https://api.minimaxi.com";
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. */
@@ -203,7 +201,7 @@ function buildBucketLimits(provider: string, bucket: TokenPlanBucket, accountId:
}
/**
* MiniMax Token Plan usage provider.
* 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
@@ -211,17 +209,13 @@ function buildBucketLimits(provider: string, bucket: TokenPlanBucket, accountId:
* 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 && params.provider !== CN_PROVIDER) return 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$/, "")
: params.provider === CN_PROVIDER
? CN_BASE_URL
: INTL_BASE_URL;
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,
@@ -289,11 +283,3 @@ export const minimaxCodeUsageProvider: UsageProvider = {
supports: params =>
params.provider === INTL_PROVIDER && params.credential.type === "api_key" && Boolean(params.credential.apiKey),
};
/** MiniMax Token Plan (China, `api.minimaxi.com`). */
export const minimaxCodeCnUsageProvider: UsageProvider = {
id: CN_PROVIDER,
fetchUsage: fetchMiniMaxCodeUsage,
supports: params =>
params.provider === CN_PROVIDER && params.credential.type === "api_key" && Boolean(params.credential.apiKey),
};
@@ -2,14 +2,14 @@ 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 { minimaxCodeCnUsageProvider, minimaxCodeUsageProvider } from "@oh-my-pi/pi-ai/usage/minimax-code";
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-cn", apiKey = "sk-cp-test"): UsageFetchParams {
function params(provider: "minimax-code" = "minimax-code", apiKey = "sk-cp-test"): UsageFetchParams {
return { provider, credential: { type: "api_key", apiKey }, accountKey: "account-1" };
}
@@ -176,19 +176,6 @@ describe("MiniMax Token Plan usage", () => {
expect(videoWeekly?.notes).toEqual(["Requests: 1/21"]);
});
test("routes the China provider to the mainland endpoint", async () => {
let requestedUrl = "";
const fetchMock: FetchImpl = input => {
requestedUrl = String(input);
return Promise.resolve(Response.json(remainsPayload()));
};
const report = await minimaxCodeCnUsageProvider.fetchUsage(params("minimax-code-cn"), { fetch: fetchMock });
expect(requestedUrl).toBe("https://api.minimaxi.com/v1/token_plan/remains");
expect(report?.provider).toBe("minimax-code-cn");
});
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/"]) {
@@ -266,7 +253,7 @@ describe("MiniMax Token Plan usage", () => {
const fetchMock: FetchImpl = () =>
Promise.resolve(Response.json(payloadOf(generalBucket(), notInPlanBucket("video"))));
const report = await minimaxCodeCnUsageProvider.fetchUsage(params("minimax-code-cn"), { fetch: fetchMock });
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"] });
@@ -276,7 +263,7 @@ describe("MiniMax Token Plan usage", () => {
const fetchMock: FetchImpl = () =>
Promise.resolve(Response.json(payloadOf(notInPlanBucket("general"), notInPlanBucket("video"))));
expect(await minimaxCodeCnUsageProvider.fetchUsage(params("minimax-code-cn"), { fetch: fetchMock })).toBeNull();
expect(await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock })).toBeNull();
});
test("keeps a bucket whose windows disagree about being in the plan", async () => {
@@ -298,7 +285,7 @@ describe("MiniMax Token Plan usage", () => {
for (const bucket of [meteredWeekly, meteredInterval]) {
const fetchMock: FetchImpl = () => Promise.resolve(Response.json(payloadOf(bucket)));
const report = await minimaxCodeCnUsageProvider.fetchUsage(params("minimax-code-cn"), { fetch: fetchMock });
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");
@@ -312,12 +299,12 @@ describe("MiniMax Token Plan usage", () => {
expect(await minimaxCodeUsageProvider.fetchUsage(params("minimax-code"), { fetch: fetchMock })).toBeNull();
});
test("registers both Token Plan ids in AuthStorage's default usage resolver", async () => {
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")).toBe(minimaxCodeCnUsageProvider);
expect(storage.usageProviderFor("minimax-code-cn")).toBeUndefined();
} finally {
storage.close();
}