mirror of https://github.com/openclaw/openclaw.git
fix: resolve MiniMax portal usage auth
This commit is contained in:
parent
9d684e1040
commit
fb0d60d7f3
|
|
@ -41,6 +41,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Models/MiniMax: honor `MINIMAX_API_HOST` for implicit bundled MiniMax provider catalogs so China-hosted API-key setups pick `api.minimaxi.com/anthropic` without manual provider config. (#34524) Thanks @caiqinghua.
|
||||
- Usage/MiniMax: invert remaining-style `usage_percent` fields when MiniMax reports only remaining percentage data, so usage bars stop showing nearly-full remaining quota as nearly-exhausted usage. (#60254) Thanks @jwchmodx.
|
||||
- Usage/MiniMax: prefer the chat-model `model_remains` entry and derive Coding Plan window labels from MiniMax interval timestamps so MiniMax usage snapshots stop picking zero-budget media rows and misreporting 4h windows as `5h`. (#52349) Thanks @IVY-AI-gif.
|
||||
- Usage/MiniMax: let usage snapshots treat `minimax-portal` and MiniMax CN aliases as the same MiniMax quota surface, and prefer stored MiniMax OAuth before falling back to Coding Plan keys.
|
||||
- MiniMax: advertise image input on bundled `MiniMax-M2.7` and `MiniMax-M2.7-highspeed` model definitions so image-capable flows can route through the M2.7 family correctly. (#54843) Thanks @MerlinMiao88888888.
|
||||
- Agents/exec approvals: let `exec-approvals.json` agent security override stricter gateway tool defaults so approved subagents can use `security: "full"` without falling back to allowlist enforcement again. (#60310) Thanks @lml2468.
|
||||
- Tasks/maintenance: mark stale cron runs and CLI tasks backed only by long-lived chat sessions as lost again so task cleanup does not keep dead work alive indefinitely. (#60310) Thanks @lml2468.
|
||||
|
|
|
|||
|
|
@ -35,11 +35,10 @@ title: "Usage Tracking"
|
|||
- JSON usage falls back to `stats`; `stats.cached` is normalized into
|
||||
`cacheRead`.
|
||||
- **OpenAI Codex**: OAuth tokens in auth profiles (accountId used when present).
|
||||
- **MiniMax**: API key (coding plan key; `MINIMAX_CODE_PLAN_KEY`, `MINIMAX_CODING_API_KEY`, or `MINIMAX_API_KEY`); coding-plan window labels come from provider hours/minutes fields when present, then fall back to the `start_time` / `end_time` span. MiniMax's raw `usage_percent` / `usagePercent` fields mean **remaining** quota, so OpenClaw inverts them before display; count-based fields win when present.
|
||||
- If the coding-plan endpoint returns `model_remains`, OpenClaw prefers the
|
||||
chat-model entry, derives the window label from timestamps when explicit
|
||||
`window_hours` / `window_minutes` fields are absent, and includes the model
|
||||
name in the plan label.
|
||||
<<<<<<< HEAD
|
||||
- **MiniMax**: API key or MiniMax OAuth auth profile. OpenClaw treats `minimax`, `minimax-cn`, and `minimax-portal` as the same MiniMax quota surface, prefers stored MiniMax OAuth when present, and otherwise falls back to `MINIMAX_CODE_PLAN_KEY`, `MINIMAX_CODING_API_KEY`, or `MINIMAX_API_KEY`. MiniMax's raw `usage_percent` / `usagePercent` fields mean **remaining** quota, so OpenClaw inverts them before display; count-based fields win when present.
|
||||
- Coding-plan window labels come from provider hours/minutes fields when present, then fall back to the `start_time` / `end_time` span.
|
||||
- If the coding-plan endpoint returns `model_remains`, OpenClaw prefers the chat-model entry, derives the window label from timestamps when explicit `window_hours` / `window_minutes` fields are absent, and includes the model name in the plan label.
|
||||
- **Xiaomi MiMo**: API key via env/config/auth store (`XIAOMI_API_KEY`).
|
||||
- **z.ai**: API key via env/config/auth store.
|
||||
|
||||
|
|
|
|||
|
|
@ -237,10 +237,14 @@ Current MiniMax auth choices in the wizard/CLI:
|
|||
- OpenClaw normalizes MiniMax coding-plan usage to the same `% left` display
|
||||
used by other providers. MiniMax's raw `usage_percent` / `usagePercent`
|
||||
fields are remaining quota, not consumed quota, so OpenClaw inverts them.
|
||||
<<<<<<< HEAD
|
||||
Count-based fields win when present. When the API returns `model_remains`,
|
||||
OpenClaw prefers the chat-model entry, derives the window label from
|
||||
`start_time` / `end_time` when needed, and includes the selected model name
|
||||
in the plan label so coding-plan windows are easier to distinguish.
|
||||
- Usage snapshots treat `minimax`, `minimax-cn`, and `minimax-portal` as the
|
||||
same MiniMax quota surface, and prefer stored MiniMax OAuth before falling
|
||||
back to Coding Plan key env vars.
|
||||
- Update pricing values in `models.json` if you need exact cost tracking.
|
||||
- Referral link for MiniMax Coding Plan (10% off): [https://platform.minimax.io/subscribe/coding-plan?code=DbXJTRClnb&source=link](https://platform.minimax.io/subscribe/coding-plan?code=DbXJTRClnb&source=link)
|
||||
- See [/concepts/model-providers](/concepts/model-providers) for provider rules.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { StreamFn } from "@mariozechner/pi-agent-core";
|
||||
import type { Context, Model } from "@mariozechner/pi-ai";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
registerProviderPlugin,
|
||||
requireRegisteredProvider,
|
||||
|
|
@ -150,4 +150,30 @@ describe("minimax provider hooks", () => {
|
|||
envVars: ["MINIMAX_CODE_PLAN_KEY", "MINIMAX_CODING_API_KEY"],
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers minimax-portal oauth when resolving MiniMax usage auth", async () => {
|
||||
const { providers } = await registerProviderPlugin({
|
||||
plugin: minimaxPlugin,
|
||||
id: "minimax",
|
||||
name: "MiniMax Provider",
|
||||
});
|
||||
const apiProvider = requireRegisteredProvider(providers, "minimax");
|
||||
const resolveOAuthToken = vi.fn(async (params?: { provider?: string }) =>
|
||||
params?.provider === "minimax-portal" ? { token: "portal-oauth-token" } : null,
|
||||
);
|
||||
const resolveApiKeyFromConfigAndStore = vi.fn(() => undefined);
|
||||
|
||||
await expect(
|
||||
apiProvider.resolveUsageAuth?.({
|
||||
provider: "minimax",
|
||||
config: {},
|
||||
env: {},
|
||||
resolveOAuthToken,
|
||||
resolveApiKeyFromConfigAndStore,
|
||||
} as never),
|
||||
).resolves.toEqual({ token: "portal-oauth-token" });
|
||||
|
||||
expect(resolveOAuthToken).toHaveBeenCalledWith({ provider: "minimax-portal" });
|
||||
expect(resolveApiKeyFromConfigAndStore).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ const PROVIDER_LABEL = "MiniMax";
|
|||
const DEFAULT_MODEL = MINIMAX_DEFAULT_MODEL_ID;
|
||||
const DEFAULT_BASE_URL_CN = "https://api.minimaxi.com/anthropic";
|
||||
const DEFAULT_BASE_URL_GLOBAL = "https://api.minimax.io/anthropic";
|
||||
const MINIMAX_USAGE_ENV_VAR_KEYS = [
|
||||
"MINIMAX_OAUTH_TOKEN",
|
||||
"MINIMAX_CODE_PLAN_KEY",
|
||||
"MINIMAX_CODING_API_KEY",
|
||||
"MINIMAX_API_KEY",
|
||||
] as const;
|
||||
const HYBRID_ANTHROPIC_OPENAI_REPLAY_HOOKS = buildProviderReplayFamilyHooks({
|
||||
family: "hybrid-anthropic-openai",
|
||||
anthropicModelDropThinkingBlocks: true,
|
||||
|
|
@ -238,12 +244,13 @@ export default definePluginEntry({
|
|||
run: async (ctx) => resolveApiCatalog(ctx),
|
||||
},
|
||||
resolveUsageAuth: async (ctx) => {
|
||||
const portalOauth = await ctx.resolveOAuthToken({ provider: PORTAL_PROVIDER_ID });
|
||||
if (portalOauth) {
|
||||
return portalOauth;
|
||||
}
|
||||
const apiKey = ctx.resolveApiKeyFromConfigAndStore({
|
||||
envDirect: [
|
||||
ctx.env.MINIMAX_CODE_PLAN_KEY,
|
||||
ctx.env.MINIMAX_CODING_API_KEY,
|
||||
ctx.env.MINIMAX_API_KEY,
|
||||
],
|
||||
providerIds: [API_PROVIDER_ID, PORTAL_PROVIDER_ID],
|
||||
envDirect: MINIMAX_USAGE_ENV_VAR_KEYS.map((name) => ctx.env[name]),
|
||||
});
|
||||
return apiKey ? { token: apiKey } : null;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ function resolveProviderApiKeyFromConfigAndStore(params: {
|
|||
|
||||
async function resolveOAuthToken(params: {
|
||||
state: UsageAuthState;
|
||||
provider: UsageProviderId;
|
||||
provider: string;
|
||||
}): Promise<ProviderAuth | null> {
|
||||
const order = resolveAuthProfileOrder({
|
||||
cfg: params.state.cfg,
|
||||
|
|
@ -108,7 +108,7 @@ async function resolveOAuthToken(params: {
|
|||
continue;
|
||||
}
|
||||
return {
|
||||
provider: params.provider,
|
||||
provider: params.provider as UsageProviderId,
|
||||
token: resolved.apiKey,
|
||||
accountId:
|
||||
cred.type === "oauth" && "accountId" in cred
|
||||
|
|
@ -142,10 +142,10 @@ async function resolveProviderUsageAuthViaPlugin(params: {
|
|||
providerIds: options?.providerIds ?? [params.provider],
|
||||
envDirect: options?.envDirect,
|
||||
}),
|
||||
resolveOAuthToken: async () => {
|
||||
resolveOAuthToken: async (options) => {
|
||||
const auth = await resolveOAuthToken({
|
||||
state: params.state,
|
||||
provider: params.provider,
|
||||
provider: options?.provider ?? params.provider,
|
||||
});
|
||||
return auth
|
||||
? {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ describe("provider-usage.shared", () => {
|
|||
it.each([
|
||||
{ value: "z-ai", expected: "zai" },
|
||||
{ value: " GOOGLE-GEMINI-CLI ", expected: "google-gemini-cli" },
|
||||
{ value: "minimax-portal", expected: "minimax" },
|
||||
{ value: "minimax-cn", expected: "minimax" },
|
||||
{ value: "minimax-portal-cn", expected: "minimax" },
|
||||
{ value: "unknown-provider", expected: undefined },
|
||||
{ value: undefined, expected: undefined },
|
||||
{ value: null, expected: undefined },
|
||||
|
|
|
|||
|
|
@ -32,6 +32,13 @@ export function resolveUsageProviderId(provider?: string | null): UsageProviderI
|
|||
return undefined;
|
||||
}
|
||||
const normalized = normalizeProviderId(provider);
|
||||
if (
|
||||
normalized === "minimax-portal" ||
|
||||
normalized === "minimax-cn" ||
|
||||
normalized === "minimax-portal-cn"
|
||||
) {
|
||||
return "minimax";
|
||||
}
|
||||
return usageProviders.includes(normalized as UsageProviderId)
|
||||
? (normalized as UsageProviderId)
|
||||
: undefined;
|
||||
|
|
|
|||
|
|
@ -474,7 +474,8 @@ export type ProviderPreparedRuntimeAuth = {
|
|||
* The helper methods cover the common OpenClaw auth resolution paths:
|
||||
*
|
||||
* - `resolveApiKeyFromConfigAndStore`: env/config/plain token/api_key profiles
|
||||
* - `resolveOAuthToken`: oauth/token profiles resolved through the auth store
|
||||
* - `resolveOAuthToken`: oauth/token profiles resolved through the auth store,
|
||||
* optionally for an explicit provider override
|
||||
*
|
||||
* Plugins can still do extra provider-specific work on top (for example parse a
|
||||
* token blob, read a legacy credential file, or pick between aliases).
|
||||
|
|
@ -489,7 +490,7 @@ export type ProviderResolveUsageAuthContext = {
|
|||
providerIds?: string[];
|
||||
envDirect?: Array<string | undefined>;
|
||||
}) => string | undefined;
|
||||
resolveOAuthToken: () => Promise<ProviderResolvedUsageAuth | null>;
|
||||
resolveOAuthToken: (params?: { provider?: string }) => Promise<ProviderResolvedUsageAuth | null>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue