mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-17 18:10:51 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
274
openclaw/extensions/minimax/provider-registration.ts
Normal file
274
openclaw/extensions/minimax/provider-registration.ts
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import type {
|
||||
OpenClawPluginApi,
|
||||
ProviderAuthContext,
|
||||
ProviderAuthResult,
|
||||
ProviderCatalogContext,
|
||||
} from "openclaw/plugin-sdk/plugin-entry";
|
||||
import {
|
||||
MINIMAX_OAUTH_MARKER,
|
||||
ensureAuthProfileStore,
|
||||
listProfilesForProvider,
|
||||
} from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
|
||||
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { MINIMAX_FAST_MODE_STREAM_HOOKS } from "openclaw/plugin-sdk/provider-stream-family";
|
||||
import { fetchMinimaxUsage } from "openclaw/plugin-sdk/provider-usage";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { isMiniMaxModernModelId, MINIMAX_DEFAULT_MODEL_ID } from "./api.js";
|
||||
import type { MiniMaxRegion } from "./oauth.js";
|
||||
import { applyMinimaxApiConfig, applyMinimaxApiConfigCn } from "./onboard.js";
|
||||
import { buildMinimaxPortalProvider, buildMinimaxProvider } from "./provider-catalog.js";
|
||||
|
||||
const API_PROVIDER_ID = "minimax";
|
||||
const PORTAL_PROVIDER_ID = "minimax-portal";
|
||||
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 MINIMAX_WIZARD_GROUP = {
|
||||
groupId: "minimax",
|
||||
groupLabel: "MiniMax",
|
||||
groupHint: "M2.7 (recommended)",
|
||||
} as const;
|
||||
const HYBRID_ANTHROPIC_OPENAI_REPLAY_HOOKS = buildProviderReplayFamilyHooks({
|
||||
family: "hybrid-anthropic-openai",
|
||||
anthropicModelDropThinkingBlocks: true,
|
||||
});
|
||||
const MINIMAX_PROVIDER_HOOKS = {
|
||||
...HYBRID_ANTHROPIC_OPENAI_REPLAY_HOOKS,
|
||||
...MINIMAX_FAST_MODE_STREAM_HOOKS,
|
||||
resolveReasoningOutputMode: () => "native" as const,
|
||||
};
|
||||
|
||||
function getDefaultBaseUrl(region: MiniMaxRegion): string {
|
||||
return region === "cn" ? DEFAULT_BASE_URL_CN : DEFAULT_BASE_URL_GLOBAL;
|
||||
}
|
||||
|
||||
function resolveMinimaxRegionLabel(region: MiniMaxRegion): string {
|
||||
return region === "cn" ? "CN" : "Global";
|
||||
}
|
||||
|
||||
function resolveMinimaxEndpointHint(region: MiniMaxRegion): string {
|
||||
return region === "cn" ? "CN endpoint - api.minimaxi.com" : "Global endpoint - api.minimax.io";
|
||||
}
|
||||
|
||||
function apiModelRef(modelId: string): string {
|
||||
return `${API_PROVIDER_ID}/${modelId}`;
|
||||
}
|
||||
|
||||
function portalModelRef(modelId: string): string {
|
||||
return `${PORTAL_PROVIDER_ID}/${modelId}`;
|
||||
}
|
||||
|
||||
function buildPortalProviderCatalog(params: { baseUrl: string; apiKey: string }) {
|
||||
return {
|
||||
...buildMinimaxPortalProvider(),
|
||||
baseUrl: params.baseUrl,
|
||||
apiKey: params.apiKey,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveApiCatalog(ctx: ProviderCatalogContext) {
|
||||
const apiKey = ctx.resolveProviderApiKey(API_PROVIDER_ID).apiKey;
|
||||
if (!apiKey) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
provider: {
|
||||
...buildMinimaxProvider(ctx.env),
|
||||
apiKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function resolvePortalCatalog(ctx: ProviderCatalogContext) {
|
||||
const explicitProvider = ctx.config.models?.providers?.[PORTAL_PROVIDER_ID];
|
||||
const envApiKey = ctx.resolveProviderApiKey(PORTAL_PROVIDER_ID).apiKey;
|
||||
const authStore = ensureAuthProfileStore(ctx.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
const hasProfiles = listProfilesForProvider(authStore, PORTAL_PROVIDER_ID).length > 0;
|
||||
const explicitApiKey = normalizeOptionalString(explicitProvider?.apiKey);
|
||||
const apiKey = envApiKey ?? explicitApiKey ?? (hasProfiles ? MINIMAX_OAUTH_MARKER : undefined);
|
||||
if (!apiKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const explicitBaseUrl = normalizeOptionalString(explicitProvider?.baseUrl);
|
||||
|
||||
return {
|
||||
provider: buildPortalProviderCatalog({
|
||||
baseUrl: explicitBaseUrl || buildMinimaxPortalProvider(ctx.env).baseUrl,
|
||||
apiKey,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function createOAuthHandler(region: MiniMaxRegion) {
|
||||
const defaultBaseUrl = getDefaultBaseUrl(region);
|
||||
const regionLabel = resolveMinimaxRegionLabel(region);
|
||||
|
||||
return async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {
|
||||
const progress = ctx.prompter.progress(`Starting MiniMax OAuth (${regionLabel})…`);
|
||||
try {
|
||||
const { loginMiniMaxPortalOAuth } = await import("./oauth.runtime.js");
|
||||
const result = await loginMiniMaxPortalOAuth({
|
||||
openUrl: ctx.openUrl,
|
||||
note: ctx.prompter.note,
|
||||
progress,
|
||||
region,
|
||||
});
|
||||
|
||||
progress.stop("MiniMax OAuth complete");
|
||||
|
||||
if (result.notification_message) {
|
||||
await ctx.prompter.note(result.notification_message, "MiniMax OAuth");
|
||||
}
|
||||
|
||||
const baseUrl = result.resourceUrl || defaultBaseUrl;
|
||||
|
||||
return buildOauthProviderAuthResult({
|
||||
providerId: PORTAL_PROVIDER_ID,
|
||||
defaultModel: portalModelRef(DEFAULT_MODEL),
|
||||
access: result.access,
|
||||
refresh: result.refresh,
|
||||
expires: result.expires,
|
||||
configPatch: {
|
||||
models: {
|
||||
providers: {
|
||||
[PORTAL_PROVIDER_ID]: {
|
||||
baseUrl,
|
||||
api: "anthropic-messages",
|
||||
authHeader: true,
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
[portalModelRef("MiniMax-M2.7")]: { alias: "minimax-m2.7" },
|
||||
[portalModelRef("MiniMax-M2.7-highspeed")]: {
|
||||
alias: "minimax-m2.7-highspeed",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
notes: [
|
||||
"MiniMax OAuth tokens auto-refresh. Re-run login if refresh fails or access is revoked.",
|
||||
`Base URL defaults to ${defaultBaseUrl}. Override models.providers.${PORTAL_PROVIDER_ID}.baseUrl if needed.`,
|
||||
...(result.notification_message ? [result.notification_message] : []),
|
||||
],
|
||||
});
|
||||
} catch (err) {
|
||||
const errorMsg = formatErrorMessage(err);
|
||||
progress.stop(`MiniMax OAuth failed: ${errorMsg}`);
|
||||
await ctx.prompter.note(
|
||||
"If OAuth fails, verify your MiniMax account has portal access and try again.",
|
||||
"MiniMax OAuth",
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createMinimaxApiKeyMethod(region: MiniMaxRegion) {
|
||||
const regionLabel = resolveMinimaxRegionLabel(region);
|
||||
const endpointHint = resolveMinimaxEndpointHint(region);
|
||||
const isCn = region === "cn";
|
||||
return createProviderApiKeyAuthMethod({
|
||||
providerId: API_PROVIDER_ID,
|
||||
methodId: isCn ? "api-cn" : "api-global",
|
||||
label: `MiniMax API key (${regionLabel})`,
|
||||
hint: endpointHint,
|
||||
optionKey: "minimaxApiKey",
|
||||
flagName: "--minimax-api-key",
|
||||
envVar: "MINIMAX_API_KEY",
|
||||
promptMessage: isCn
|
||||
? "Enter MiniMax CN API key (sk-api- or sk-cp-)\nhttps://platform.minimaxi.com/user-center/basic-information/interface-key"
|
||||
: "Enter MiniMax API key (sk-api- or sk-cp-)\nhttps://platform.minimax.io/user-center/basic-information/interface-key",
|
||||
profileId: isCn ? "minimax:cn" : "minimax:global",
|
||||
allowProfile: false,
|
||||
defaultModel: apiModelRef(DEFAULT_MODEL),
|
||||
expectedProviders: isCn ? ["minimax", "minimax-cn"] : ["minimax"],
|
||||
applyConfig: (cfg) => (isCn ? applyMinimaxApiConfigCn(cfg) : applyMinimaxApiConfig(cfg)),
|
||||
wizard: {
|
||||
choiceId: isCn ? "minimax-cn-api" : "minimax-global-api",
|
||||
choiceLabel: `MiniMax API key (${regionLabel})`,
|
||||
choiceHint: endpointHint,
|
||||
...MINIMAX_WIZARD_GROUP,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function createMinimaxOAuthMethod(region: MiniMaxRegion) {
|
||||
const regionLabel = resolveMinimaxRegionLabel(region);
|
||||
const endpointHint = resolveMinimaxEndpointHint(region);
|
||||
const isCn = region === "cn";
|
||||
return {
|
||||
id: isCn ? "oauth-cn" : "oauth",
|
||||
label: `MiniMax OAuth (${regionLabel})`,
|
||||
hint: endpointHint,
|
||||
kind: "device_code" as const,
|
||||
wizard: {
|
||||
choiceId: isCn ? "minimax-cn-oauth" : "minimax-global-oauth",
|
||||
choiceLabel: `MiniMax OAuth (${regionLabel})`,
|
||||
choiceHint: endpointHint,
|
||||
...MINIMAX_WIZARD_GROUP,
|
||||
},
|
||||
run: createOAuthHandler(region),
|
||||
};
|
||||
}
|
||||
|
||||
export function registerMinimaxProviders(api: OpenClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: API_PROVIDER_ID,
|
||||
label: PROVIDER_LABEL,
|
||||
hookAliases: ["minimax-cn"],
|
||||
docsPath: "/providers/minimax",
|
||||
envVars: ["MINIMAX_API_KEY"],
|
||||
auth: [createMinimaxApiKeyMethod("global"), createMinimaxApiKeyMethod("cn")],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
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({
|
||||
providerIds: [API_PROVIDER_ID, PORTAL_PROVIDER_ID],
|
||||
envDirect: MINIMAX_USAGE_ENV_VAR_KEYS.map((name) => ctx.env[name]),
|
||||
});
|
||||
return apiKey ? { token: apiKey } : null;
|
||||
},
|
||||
...MINIMAX_PROVIDER_HOOKS,
|
||||
isModernModelRef: ({ modelId }) => isMiniMaxModernModelId(modelId),
|
||||
fetchUsageSnapshot: async (ctx) =>
|
||||
await fetchMinimaxUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
|
||||
});
|
||||
|
||||
api.registerProvider({
|
||||
id: PORTAL_PROVIDER_ID,
|
||||
label: PROVIDER_LABEL,
|
||||
hookAliases: ["minimax-portal-cn"],
|
||||
docsPath: "/providers/minimax",
|
||||
envVars: ["MINIMAX_OAUTH_TOKEN", "MINIMAX_API_KEY"],
|
||||
catalog: {
|
||||
run: async (ctx) => resolvePortalCatalog(ctx),
|
||||
},
|
||||
auth: [createMinimaxOAuthMethod("global"), createMinimaxOAuthMethod("cn")],
|
||||
...MINIMAX_PROVIDER_HOOKS,
|
||||
isModernModelRef: ({ modelId }) => isMiniMaxModernModelId(modelId),
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue