refactor: move provider seams behind plugin sdk surfaces

This commit is contained in:
Peter Steinberger 2026-03-27 23:24:36 +00:00
parent 4f0ad16a00
commit 4ca07559ab
124 changed files with 1170 additions and 812 deletions

View File

@ -3144,7 +3144,7 @@
"exportName": "buildChannelOutboundSessionRoute",
"kind": "function",
"source": {
"line": 181,
"line": 182,
"path": "src/plugin-sdk/core.ts"
}
},
@ -3189,7 +3189,7 @@
"exportName": "createChannelPluginBase",
"kind": "function",
"source": {
"line": 527,
"line": 528,
"path": "src/plugin-sdk/core.ts"
}
},
@ -3198,16 +3198,25 @@
"exportName": "createChatChannelPlugin",
"kind": "function",
"source": {
"line": 504,
"line": 505,
"path": "src/plugin-sdk/core.ts"
}
},
{
"declaration": "export function createSubsystemLogger(subsystem: string): SubsystemLogger;",
"exportName": "createSubsystemLogger",
"kind": "function",
"source": {
"line": 308,
"path": "src/logging/subsystem.ts"
}
},
{
"declaration": "export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedChannelPluginEntry<TPlugin>;",
"exportName": "defineChannelPluginEntry",
"kind": "function",
"source": {
"line": 274,
"line": 275,
"path": "src/plugin-sdk/core.ts"
}
},
@ -3225,7 +3234,7 @@
"exportName": "defineSetupPluginEntry",
"kind": "function",
"source": {
"line": 311,
"line": 312,
"path": "src/plugin-sdk/core.ts"
}
},
@ -3423,7 +3432,7 @@
"exportName": "stripChannelTargetPrefix",
"kind": "function",
"source": {
"line": 161,
"line": 162,
"path": "src/plugin-sdk/core.ts"
}
},
@ -3432,7 +3441,7 @@
"exportName": "stripTargetKindPrefix",
"kind": "function",
"source": {
"line": 173,
"line": 174,
"path": "src/plugin-sdk/core.ts"
}
},
@ -3513,7 +3522,7 @@
"exportName": "ChannelOutboundSessionRouteParams",
"kind": "type",
"source": {
"line": 156,
"line": 157,
"path": "src/plugin-sdk/core.ts"
}
},

View File

@ -345,16 +345,17 @@
{"declaration":"export function applyAccountNameToChannelSection(params: { cfg: OpenClawConfig; channelKey: string; accountId: string; name?: string | undefined; alwaysUseAccounts?: boolean | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"applyAccountNameToChannelSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":34,"sourcePath":"src/channels/plugins/setup-helpers.ts"}
{"declaration":"export function buildAgentSessionKey(params: { agentId: string; channel: string; accountId?: string | null | undefined; peer?: RoutePeer | null | undefined; dmScope?: \"main\" | \"per-peer\" | \"per-channel-peer\" | \"per-account-channel-peer\" | undefined; identityLinks?: Record<...> | undefined; }): string;","entrypoint":"core","exportName":"buildAgentSessionKey","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":91,"sourcePath":"src/routing/resolve-route.ts"}
{"declaration":"export function buildChannelConfigSchema(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, options?: BuildChannelConfigSchemaOptions | undefined): ChannelConfigSchema;","entrypoint":"core","exportName":"buildChannelConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":75,"sourcePath":"src/channels/plugins/config-schema.ts"}
{"declaration":"export function buildChannelOutboundSessionRoute(params: { cfg: OpenClawConfig; agentId: string; channel: string; accountId?: string | null | undefined; peer: { kind: \"direct\" | \"group\" | \"channel\"; id: string; }; chatType: \"direct\" | \"group\" | \"channel\"; from: string; to: string; threadId?: string | ... 1 more ... | undefined; }): ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"buildChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":181,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function buildChannelOutboundSessionRoute(params: { cfg: OpenClawConfig; agentId: string; channel: string; accountId?: string | null | undefined; peer: { kind: \"direct\" | \"group\" | \"channel\"; id: string; }; chatType: \"direct\" | \"group\" | \"channel\"; from: string; to: string; threadId?: string | ... 1 more ... | undefined; }): ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"buildChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":182,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function buildPluginConfigSchema(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, options?: BuildPluginConfigSchemaOptions | undefined): OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"buildPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":78,"sourcePath":"src/plugins/config-schema.ts"}
{"declaration":"export function channelTargetSchema(options?: { description?: string | undefined; } | undefined): TString;","entrypoint":"core","exportName":"channelTargetSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":38,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function channelTargetsSchema(options?: { description?: string | undefined; } | undefined): TArray<TString>;","entrypoint":"core","exportName":"channelTargetsSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":44,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function clearAccountEntryFields<TAccountEntry extends object>(params: { accounts?: Record<string, TAccountEntry> | undefined; accountId: string; fields: string[]; isValueSet?: ((value: unknown) => boolean) | undefined; markClearedOnFieldPresence?: boolean | undefined; }): { ...; };","entrypoint":"core","exportName":"clearAccountEntryFields","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":122,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function createChannelPluginBase<TResolvedAccount>(params: CreateChannelPluginBaseOptions<TResolvedAccount>): CreatedChannelPluginBase<TResolvedAccount>;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":527,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChatChannelPlugin<TResolvedAccount extends { accountId?: string | null; }, Probe = unknown, Audit = unknown>(params: { base: ChatChannelPluginBase<TResolvedAccount, Probe, Audit>; security?: ChannelSecurityAdapter<TResolvedAccount> | ChatChannelSecurityOptions<...> | undefined; pairing?: ChannelPairingAdapter | ... 1 more ... | undefined; threading?: ChannelThreadingAdapter | ... 1 more ... | undefined; outbound?: ChannelOutboundAdapter | ... 1 more ... | undefined; }): ChannelPlugin<...>;","entrypoint":"core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":504,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedChannelPluginEntry<TPlugin>;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":274,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChannelPluginBase<TResolvedAccount>(params: CreateChannelPluginBaseOptions<TResolvedAccount>): CreatedChannelPluginBase<TResolvedAccount>;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":528,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createChatChannelPlugin<TResolvedAccount extends { accountId?: string | null; }, Probe = unknown, Audit = unknown>(params: { base: ChatChannelPluginBase<TResolvedAccount, Probe, Audit>; security?: ChannelSecurityAdapter<TResolvedAccount> | ChatChannelSecurityOptions<...> | undefined; pairing?: ChannelPairingAdapter | ... 1 more ... | undefined; threading?: ChannelThreadingAdapter | ... 1 more ... | undefined; outbound?: ChannelOutboundAdapter | ... 1 more ... | undefined; }): ChannelPlugin<...>;","entrypoint":"core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":505,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function createSubsystemLogger(subsystem: string): SubsystemLogger;","entrypoint":"core","exportName":"createSubsystemLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":308,"sourcePath":"src/logging/subsystem.ts"}
{"declaration":"export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedChannelPluginEntry<TPlugin>;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":275,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":129,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
{"declaration":"export function defineSetupPluginEntry<TPlugin>(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":311,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function defineSetupPluginEntry<TPlugin>(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":312,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function delegateCompactionToRuntime(params: { sessionId: string; sessionKey?: string | undefined; sessionFile: string; tokenBudget?: number | undefined; force?: boolean | undefined; currentTokenCount?: number | undefined; compactionTarget?: \"budget\" | ... 1 more ... | undefined; customInstructions?: string | undefined; runtimeContext?: ContextEngineRuntimeContext | undefined; }): Promise<...>;","entrypoint":"core","exportName":"delegateCompactionToRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/context-engine/delegate.ts"}
{"declaration":"export function deleteAccountFromConfigSection(params: { cfg: OpenClawConfig; sectionKey: string; accountId: string; clearBaseFields?: string[] | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"deleteAccountFromConfigSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":60,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"emptyPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":108,"sourcePath":"src/plugins/config-schema.ts"}
@ -376,8 +377,8 @@
{"declaration":"export function resolveThreadSessionKeys(params: { baseSessionKey: string; threadId?: string | null | undefined; parentSessionKey?: string | undefined; useSuffix?: boolean | undefined; normalizeThreadId?: ((threadId: string) => string) | undefined; }): { ...; };","entrypoint":"core","exportName":"resolveThreadSessionKeys","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":234,"sourcePath":"src/routing/session-key.ts"}
{"declaration":"export function setAccountEnabledInConfigSection(params: { cfg: OpenClawConfig; sectionKey: string; accountId: string; enabled: boolean; allowTopLevel?: boolean | undefined; }): OpenClawConfig;","entrypoint":"core","exportName":"setAccountEnabledInConfigSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/channels/plugins/config-helpers.ts"}
{"declaration":"export function stringEnum<T extends readonly string[]>(values: T, options?: StringEnumOptions<T>): TUnsafe<T[number]>;","entrypoint":"core","exportName":"stringEnum","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":15,"sourcePath":"src/agents/schema/typebox.ts"}
{"declaration":"export function stripChannelTargetPrefix(raw: string, ...providers: string[]): string;","entrypoint":"core","exportName":"stripChannelTargetPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":161,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripTargetKindPrefix(raw: string): string;","entrypoint":"core","exportName":"stripTargetKindPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":173,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripChannelTargetPrefix(raw: string, ...providers: string[]): string;","entrypoint":"core","exportName":"stripChannelTargetPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":162,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function stripTargetKindPrefix(raw: string): string;","entrypoint":"core","exportName":"stripTargetKindPrefix","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":174,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export function tryReadSecretFileSync(filePath: string | undefined, label: string, options?: SecretFileReadOptions): string | undefined;","entrypoint":"core","exportName":"tryReadSecretFileSync","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":130,"sourcePath":"src/infra/secret-file.ts"}
{"declaration":"export const DEFAULT_ACCOUNT_ID: \"default\";","entrypoint":"core","exportName":"DEFAULT_ACCOUNT_ID","importSpecifier":"openclaw/plugin-sdk/core","kind":"const","recordType":"export","sourceLine":3,"sourcePath":"src/routing/account-id.ts"}
{"declaration":"export const DEFAULT_SECRET_FILE_MAX_BYTES: number;","entrypoint":"core","exportName":"DEFAULT_SECRET_FILE_MAX_BYTES","importSpecifier":"openclaw/plugin-sdk/core","kind":"const","recordType":"export","sourceLine":5,"sourcePath":"src/infra/secret-file.ts"}
@ -386,7 +387,7 @@
{"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"core","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelMessagingAdapter = ChannelMessagingAdapter;","entrypoint":"core","exportName":"ChannelMessagingAdapter","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":395,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundSessionRoute = ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"ChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":309,"sourcePath":"src/channels/plugins/types.core.ts"}
{"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"src/channels/plugins/types.core\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":156,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"src/channels/plugins/types.core\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":157,"sourcePath":"src/plugin-sdk/core.ts"}
{"declaration":"export type ChannelPlugin = ChannelPlugin<ResolvedAccount, Probe, Audit>;","entrypoint":"core","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":76,"sourcePath":"src/channels/plugins/types.plugin.ts"}
{"declaration":"export type GatewayBindUrlResult = GatewayBindUrlResult;","entrypoint":"core","exportName":"GatewayBindUrlResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1,"sourcePath":"src/shared/gateway-bind-url.ts"}
{"declaration":"export type GatewayRequestHandlerOptions = GatewayRequestHandlerOptions;","entrypoint":"core","exportName":"GatewayRequestHandlerOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":115,"sourcePath":"src/gateway/server-methods/types.ts"}

View File

@ -0,0 +1,7 @@
export {
discoverBedrockModels,
mergeImplicitBedrockProvider,
resetBedrockDiscoveryCacheForTest,
resolveBedrockConfigApiKey,
resolveImplicitBedrockProvider,
} from "./discovery.js";

View File

@ -3,8 +3,13 @@ import {
ListFoundationModelsCommand,
type ListFoundationModelsCommandOutput,
} from "@aws-sdk/client-bedrock";
import type { BedrockDiscoveryConfig, ModelDefinitionConfig } from "../config/types.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { createSubsystemLogger } from "openclaw/plugin-sdk/core";
import { resolveAwsSdkEnvVarName } from "openclaw/plugin-sdk/provider-auth-runtime";
import type {
BedrockDiscoveryConfig,
ModelDefinitionConfig,
ModelProviderConfig,
} from "openclaw/plugin-sdk/provider-models";
const log = createSubsystemLogger("bedrock-discovery");
@ -145,6 +150,10 @@ export function resetBedrockDiscoveryCacheForTest(): void {
hasLoggedBedrockError = false;
}
export function resolveBedrockConfigApiKey(env: NodeJS.ProcessEnv = process.env): string {
return resolveAwsSdkEnvVarName(env) ?? "AWS_PROFILE";
}
export async function discoverBedrockModels(params: {
region: string;
config?: BedrockDiscoveryConfig;
@ -219,8 +228,60 @@ export async function discoverBedrockModels(params: {
}
if (!hasLoggedBedrockError) {
hasLoggedBedrockError = true;
log.warn(`Failed to list models: ${String(error)}`);
log.warn("Failed to discover Bedrock models", {
error: error instanceof Error ? error.message : String(error),
});
}
return [];
}
}
export async function resolveImplicitBedrockProvider(params: {
config?: { models?: { bedrockDiscovery?: BedrockDiscoveryConfig } };
env?: NodeJS.ProcessEnv;
}): Promise<ModelProviderConfig | null> {
const env = params.env ?? process.env;
const discoveryConfig = params.config?.models?.bedrockDiscovery;
const enabled = discoveryConfig?.enabled;
const hasAwsCreds = resolveAwsSdkEnvVarName(env) !== undefined;
if (enabled === false) {
return null;
}
if (enabled !== true && !hasAwsCreds) {
return null;
}
const region = discoveryConfig?.region ?? env.AWS_REGION ?? env.AWS_DEFAULT_REGION ?? "us-east-1";
const models = await discoverBedrockModels({
region,
config: discoveryConfig,
});
if (models.length === 0) {
return null;
}
return {
baseUrl: `https://bedrock-runtime.${region}.amazonaws.com`,
api: "bedrock-converse-stream",
auth: "aws-sdk",
models,
};
}
export function mergeImplicitBedrockProvider(params: {
existing: ModelProviderConfig | undefined;
implicit: ModelProviderConfig;
}): ModelProviderConfig {
const { existing, implicit } = params;
if (!existing) {
return implicit;
}
return {
...implicit,
...existing,
models:
Array.isArray(existing.models) && existing.models.length > 0
? existing.models
: implicit.models,
};
}

View File

@ -1,5 +1,45 @@
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-models";
export {
ANTHROPIC_VERTEX_DEFAULT_MODEL_ID,
buildAnthropicVertexProvider,
} from "./provider-catalog.js";
export { resolveAnthropicVertexRegion } from "./region.js";
export {
hasAnthropicVertexAvailableAuth,
hasAnthropicVertexCredentials,
resolveAnthropicVertexClientRegion,
resolveAnthropicVertexConfigApiKey,
resolveAnthropicVertexProjectId,
resolveAnthropicVertexRegion,
resolveAnthropicVertexRegionFromBaseUrl,
} from "./region.js";
import { buildAnthropicVertexProvider } from "./provider-catalog.js";
import { hasAnthropicVertexAvailableAuth } from "./region.js";
export function mergeImplicitAnthropicVertexProvider(params: {
existing: ModelProviderConfig | undefined;
implicit: ModelProviderConfig;
}): ModelProviderConfig {
const { existing, implicit } = params;
if (!existing) {
return implicit;
}
return {
...implicit,
...existing,
models:
Array.isArray(existing.models) && existing.models.length > 0
? existing.models
: implicit.models,
};
}
export function resolveImplicitAnthropicVertexProvider(params?: {
env?: NodeJS.ProcessEnv;
}): ModelProviderConfig | null {
const env = params?.env ?? process.env;
if (!hasAnthropicVertexAvailableAuth(env)) {
return null;
}
return buildAnthropicVertexProvider({ env });
}

View File

@ -1,5 +1,21 @@
import { existsSync, readFileSync } from "node:fs";
import { homedir, platform } from "node:os";
import { join } from "node:path";
const ANTHROPIC_VERTEX_DEFAULT_REGION = "global";
const ANTHROPIC_VERTEX_REGION_RE = /^[a-z0-9-]+$/;
const GCP_VERTEX_CREDENTIALS_MARKER = "gcp-vertex-credentials";
const GCLOUD_DEFAULT_ADC_PATH = join(
homedir(),
".config",
"gcloud",
"application_default_credentials.json",
);
type AdcProjectFile = {
project_id?: unknown;
quota_project_id?: unknown;
};
function normalizeOptionalSecretInput(value: unknown): string | undefined {
if (typeof value !== "string") {
@ -18,3 +34,105 @@ export function resolveAnthropicVertexRegion(env: NodeJS.ProcessEnv = process.en
? region
: ANTHROPIC_VERTEX_DEFAULT_REGION;
}
export function resolveAnthropicVertexProjectId(
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
return (
normalizeOptionalSecretInput(env.ANTHROPIC_VERTEX_PROJECT_ID) ||
normalizeOptionalSecretInput(env.GOOGLE_CLOUD_PROJECT) ||
normalizeOptionalSecretInput(env.GOOGLE_CLOUD_PROJECT_ID) ||
resolveAnthropicVertexProjectIdFromAdc(env)
);
}
export function resolveAnthropicVertexRegionFromBaseUrl(baseUrl?: string): string | undefined {
const trimmed = baseUrl?.trim();
if (!trimmed) {
return undefined;
}
try {
const host = new URL(trimmed).hostname.toLowerCase();
if (host === "aiplatform.googleapis.com") {
return "global";
}
const match = /^([a-z0-9-]+)-aiplatform\.googleapis\.com$/.exec(host);
return match?.[1];
} catch {
return undefined;
}
}
export function resolveAnthropicVertexClientRegion(params?: {
baseUrl?: string;
env?: NodeJS.ProcessEnv;
}): string {
return (
resolveAnthropicVertexRegionFromBaseUrl(params?.baseUrl) ||
resolveAnthropicVertexRegion(params?.env)
);
}
function hasAnthropicVertexMetadataServerAdc(env: NodeJS.ProcessEnv = process.env): boolean {
const explicitMetadataOptIn = normalizeOptionalSecretInput(env.ANTHROPIC_VERTEX_USE_GCP_METADATA);
return explicitMetadataOptIn === "1" || explicitMetadataOptIn?.toLowerCase() === "true";
}
function resolveAnthropicVertexDefaultAdcPath(env: NodeJS.ProcessEnv = process.env): string {
return platform() === "win32"
? join(
env.APPDATA ?? join(homedir(), "AppData", "Roaming"),
"gcloud",
"application_default_credentials.json",
)
: GCLOUD_DEFAULT_ADC_PATH;
}
function resolveAnthropicVertexAdcCredentialsPath(
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
const explicitCredentialsPath = normalizeOptionalSecretInput(env.GOOGLE_APPLICATION_CREDENTIALS);
if (explicitCredentialsPath) {
return existsSync(explicitCredentialsPath) ? explicitCredentialsPath : undefined;
}
const defaultAdcPath = resolveAnthropicVertexDefaultAdcPath(env);
return existsSync(defaultAdcPath) ? defaultAdcPath : undefined;
}
function resolveAnthropicVertexProjectIdFromAdc(
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
const credentialsPath = resolveAnthropicVertexAdcCredentialsPath(env);
if (!credentialsPath) {
return undefined;
}
try {
const parsed = JSON.parse(readFileSync(credentialsPath, "utf8")) as AdcProjectFile;
return (
normalizeOptionalSecretInput(parsed.project_id) ||
normalizeOptionalSecretInput(parsed.quota_project_id)
);
} catch {
return undefined;
}
}
export function hasAnthropicVertexCredentials(env: NodeJS.ProcessEnv = process.env): boolean {
return (
hasAnthropicVertexMetadataServerAdc(env) ||
resolveAnthropicVertexAdcCredentialsPath(env) !== undefined
);
}
export function hasAnthropicVertexAvailableAuth(env: NodeJS.ProcessEnv = process.env): boolean {
return hasAnthropicVertexCredentials(env);
}
export function resolveAnthropicVertexConfigApiKey(
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
return hasAnthropicVertexAvailableAuth(env) ? GCP_VERTEX_CREDENTIALS_MARKER : undefined;
}

View File

@ -10,7 +10,6 @@ import {
CLAUDE_CLI_PROFILE_ID,
applyAuthProfileConfig,
buildTokenProfileId,
createProviderApiKeyAuthMethod,
ensureApiKeyFromOptionEnvOrPrompt,
listProfilesForProvider,
normalizeApiKeyInput,
@ -25,6 +24,7 @@ import {
validateAnthropicSetupToken,
validateApiKeyInput,
} from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { normalizeModelCompat } from "openclaw/plugin-sdk/provider-models";
import { fetchClaudeUsage } from "openclaw/plugin-sdk/provider-usage";
import { buildAnthropicCliBackend } from "./cli-backend.js";

View File

@ -1,5 +1,5 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { ensureModelAllowlistEntry } from "openclaw/plugin-sdk/provider-onboard";
import { buildBytePlusCodingProvider, buildBytePlusProvider } from "./provider-catalog.js";

View File

@ -1,11 +1,11 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import {
createProviderApiKeyAuthMethod,
resolveOAuthApiKeyMarker,
type ProviderAuthContext,
type ProviderAuthResult,
} 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 { loginChutes } from "openclaw/plugin-sdk/provider-auth-login";
import {
CHUTES_DEFAULT_MODEL_REF,

View File

@ -12,6 +12,7 @@ export * from "./src/probe.js";
export * from "./src/session-key-normalization.js";
export * from "./src/status-issues.js";
export * from "./src/targets.js";
export { resolveDiscordRuntimeGroupPolicy } from "./src/monitor/provider.js";
export {
DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS,
DISCORD_DEFAULT_LISTENER_TIMEOUT_MS,

View File

@ -653,7 +653,6 @@ describe("monitorDiscordProvider", () => {
retry_after: 193.632,
global: false,
},
request,
);
rateLimitError.discordCode = 30034;
clientHandleDeployRequestMock.mockRejectedValueOnce(rateLimitError);

View File

@ -1183,3 +1183,5 @@ export const __testing = {
shouldLogVerboseForTesting = mock;
},
};
export const resolveDiscordRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy;

View File

@ -436,15 +436,11 @@ function createMockRateLimitError(retryAfter = 0.001): RateLimitError {
"X-RateLimit-Bucket": "test-bucket",
},
});
return createCompatRateLimitError(
response,
{
message: "You are being rate limited.",
retry_after: retryAfter,
global: false,
},
request,
);
return createCompatRateLimitError(response, {
message: "You are being rate limited.",
retry_after: retryAfter,
global: false,
});
}
describe("retry rate limits", () => {

View File

@ -296,15 +296,11 @@ export async function sendDiscordVoiceMessage(
retry_after?: number;
global?: boolean;
};
throw createRateLimitError(
res,
{
message: retryData.message ?? "You are being rate limited.",
retry_after: retryData.retry_after ?? 1,
global: retryData.global ?? false,
},
uploadUrlRequest,
);
throw createRateLimitError(res, {
message: retryData.message ?? "You are being rate limited.",
retry_after: retryData.retry_after ?? 1,
global: retryData.global ?? false,
});
}
const errorBody = (await res.json().catch(() => null)) as {
code?: number;

View File

@ -8,7 +8,7 @@ import {
type SsrFPolicy,
ssrfPolicyFromAllowPrivateNetwork,
} from "openclaw/plugin-sdk/infra-runtime";
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth";
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
const DEFAULT_FAL_BASE_URL = "https://fal.run";
const DEFAULT_FAL_IMAGE_MODEL = "fal-ai/flux/dev";

View File

@ -1,5 +1,5 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { buildFalImageGenerationProvider } from "./image-generation-provider.js";
import { applyFalConfig, FAL_DEFAULT_IMAGE_MODEL_REF } from "./onboard.js";

View File

@ -1,32 +1,18 @@
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-models";
import {
applyAgentDefaultModelPrimary,
type OpenClawConfig,
} from "openclaw/plugin-sdk/provider-onboard";
import {
createGoogleThinkingPayloadWrapper,
sanitizeGoogleThinkingPayload,
} from "openclaw/plugin-sdk/provider-stream";
import { normalizeAntigravityModelId, normalizeGoogleModelId } from "./model-id.js";
export { normalizeAntigravityModelId, normalizeGoogleModelId };
export { createGoogleThinkingPayloadWrapper, sanitizeGoogleThinkingPayload };
type GoogleApiCarrier = {
api?: string | null;
};
export function normalizeGoogleModelId(id: string): string {
if (id === "gemini-3-pro") {
return "gemini-3-pro-preview";
}
if (id === "gemini-3-flash") {
return "gemini-3-flash-preview";
}
if (id === "gemini-3.1-pro") {
return "gemini-3.1-pro-preview";
}
if (id === "gemini-3.1-flash-lite") {
return "gemini-3.1-flash-lite-preview";
}
if (id === "gemini-3.1-flash" || id === "gemini-3.1-flash-preview") {
return "gemini-3-flash-preview";
}
return id;
}
type GoogleProviderConfigLike = GoogleApiCarrier & {
models?: ReadonlyArray<GoogleApiCarrier | null | undefined> | null;
};
const DEFAULT_GOOGLE_API_HOST = "generativelanguage.googleapis.com";
@ -57,6 +43,97 @@ export function normalizeGoogleApiBaseUrl(baseUrl?: string): string {
}
}
export function isGoogleGenerativeAiApi(api?: string | null): boolean {
return api === "google-generative-ai";
}
export function normalizeGoogleGenerativeAiBaseUrl(baseUrl?: string): string | undefined {
return baseUrl ? normalizeGoogleApiBaseUrl(baseUrl) : baseUrl;
}
export function resolveGoogleGenerativeAiTransport<TApi extends string | null | undefined>(params: {
api: TApi;
baseUrl?: string;
}): { api: TApi; baseUrl?: string } {
return {
api: params.api,
baseUrl: isGoogleGenerativeAiApi(params.api)
? normalizeGoogleGenerativeAiBaseUrl(params.baseUrl)
: params.baseUrl,
};
}
export function resolveGoogleGenerativeAiApiOrigin(baseUrl?: string): string {
return normalizeGoogleApiBaseUrl(baseUrl).replace(/\/v1beta$/i, "");
}
export function shouldNormalizeGoogleGenerativeAiProviderConfig(
providerKey: string,
provider: GoogleProviderConfigLike,
): boolean {
if (providerKey === "google" || providerKey === "google-vertex") {
return true;
}
if (isGoogleGenerativeAiApi(provider.api)) {
return true;
}
return provider.models?.some((model) => isGoogleGenerativeAiApi(model?.api)) ?? false;
}
export function shouldNormalizeGoogleProviderConfig(
providerKey: string,
provider: GoogleProviderConfigLike,
): boolean {
return (
providerKey === "google-antigravity" ||
shouldNormalizeGoogleGenerativeAiProviderConfig(providerKey, provider)
);
}
function normalizeProviderModels(
provider: ModelProviderConfig,
normalizeId: (id: string) => string,
): ModelProviderConfig {
const models = provider.models;
if (!Array.isArray(models) || models.length === 0) {
return provider;
}
let mutated = false;
const nextModels = models.map((model) => {
const nextId = normalizeId(model.id);
if (nextId === model.id) {
return model;
}
mutated = true;
return { ...model, id: nextId };
});
return mutated ? { ...provider, models: nextModels } : provider;
}
export function normalizeGoogleProviderConfig(
providerKey: string,
provider: ModelProviderConfig,
): ModelProviderConfig {
let nextProvider = provider;
if (shouldNormalizeGoogleGenerativeAiProviderConfig(providerKey, nextProvider)) {
const modelNormalized = normalizeProviderModels(nextProvider, normalizeGoogleModelId);
const normalizedBaseUrl = normalizeGoogleGenerativeAiBaseUrl(modelNormalized.baseUrl);
nextProvider =
normalizedBaseUrl !== modelNormalized.baseUrl
? { ...modelNormalized, baseUrl: normalizedBaseUrl ?? modelNormalized.baseUrl }
: modelNormalized;
}
if (providerKey === "google-antigravity") {
nextProvider = normalizeProviderModels(nextProvider, normalizeAntigravityModelId);
}
return nextProvider;
}
export function parseGeminiAuth(apiKey: string): { headers: Record<string, string> } {
if (apiKey.startsWith("{")) {
try {

View File

@ -8,11 +8,8 @@ import {
} from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-models";
import {
GOOGLE_GEMINI_DEFAULT_MODEL,
applyGoogleGeminiModelDefault,
createGoogleThinkingPayloadWrapper,
} from "./api.js";
import { createGoogleThinkingPayloadWrapper } from "openclaw/plugin-sdk/provider-stream";
import { GOOGLE_GEMINI_DEFAULT_MODEL, applyGoogleGeminiModelDefault } from "./api.js";
import { buildGoogleGeminiCliBackend } from "./cli-backend.js";
import { isModernGoogleModel, resolveGoogle31ForwardCompatModel } from "./provider-models.js";
import { createGeminiWebSearchProvider } from "./src/gemini-web-search-provider.js";

View File

@ -0,0 +1,27 @@
const ANTIGRAVITY_BARE_PRO_IDS = new Set(["gemini-3-pro", "gemini-3.1-pro", "gemini-3-1-pro"]);
export function normalizeGoogleModelId(id: string): string {
if (id === "gemini-3-pro") {
return "gemini-3-pro-preview";
}
if (id === "gemini-3-flash") {
return "gemini-3-flash-preview";
}
if (id === "gemini-3.1-pro") {
return "gemini-3.1-pro-preview";
}
if (id === "gemini-3.1-flash-lite") {
return "gemini-3.1-flash-lite-preview";
}
if (id === "gemini-3.1-flash" || id === "gemini-3.1-flash-preview") {
return "gemini-3-flash-preview";
}
return id;
}
export function normalizeAntigravityModelId(id: string): string {
if (ANTIGRAVITY_BARE_PRO_IDS.has(id)) {
return `${id}-low`;
}
return id;
}

View File

@ -1,5 +1,5 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { applyHuggingfaceConfig, HUGGINGFACE_DEFAULT_MODEL_REF } from "./onboard.js";
import { buildHuggingfaceProvider } from "./provider-catalog.js";

View File

@ -3,3 +3,4 @@ export * from "./src/group-policy.js";
export * from "./src/probe.js";
export * from "./src/target-parsing-helpers.js";
export * from "./src/targets.js";
export { resolveIMessageRuntimeGroupPolicy } from "./src/monitor/monitor-provider.js";

View File

@ -23,4 +23,5 @@ export {
export { monitorIMessageProvider } from "./src/monitor.js";
export type { MonitorIMessageOpts } from "./src/monitor.js";
export { probeIMessage } from "./src/probe.js";
export type { IMessageProbe } from "./src/probe.js";
export { sendMessageIMessage } from "./src/send.js";

View File

@ -536,3 +536,5 @@ export const __testing = {
resolveIMessageRuntimeGroupPolicy: resolveOpenProviderRuntimeGroupPolicy,
resolveDefaultGroupPolicy,
};
export const resolveIMessageRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy;

View File

@ -1,5 +1,5 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { isRecord } from "openclaw/plugin-sdk/text-runtime";
import { applyKimiCodeConfig, KIMI_CODING_MODEL_REF } from "./onboard.js";
import { buildKimiCodingProvider } from "./provider-catalog.js";

View File

@ -1,5 +1,5 @@
import type { ImageGenerationProvider } from "openclaw/plugin-sdk/image-generation";
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth";
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
const DEFAULT_MINIMAX_IMAGE_BASE_URL = "https://api.minimax.io";
const DEFAULT_MODEL = "image-01";

View File

@ -6,11 +6,11 @@ import {
} from "openclaw/plugin-sdk/plugin-entry";
import {
MINIMAX_OAUTH_MARKER,
createProviderApiKeyAuthMethod,
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 { fetchMinimaxUsage } from "openclaw/plugin-sdk/provider-usage";
import { isMiniMaxModernModelId, MINIMAX_DEFAULT_MODEL_ID } from "./api.js";
import {

View File

@ -1,9 +1 @@
export { applyMistralConfig, applyMistralProviderConfig } from "./onboard.js";
export {
buildMistralCatalogModels,
buildMistralModelDefinition,
MISTRAL_BASE_URL,
MISTRAL_DEFAULT_COST,
MISTRAL_DEFAULT_MODEL_ID,
MISTRAL_DEFAULT_MODEL_REF,
} from "./model-definitions.js";
export { buildMistralProvider } from "./provider-catalog.js";

View File

@ -1,6 +1,8 @@
export {
applyModelStudioNativeStreamingUsageCompat,
buildModelStudioDefaultModelDefinition,
buildModelStudioModelDefinition,
isNativeModelStudioBaseUrl,
MODELSTUDIO_BASE_URL,
MODELSTUDIO_CN_BASE_URL,
MODELSTUDIO_DEFAULT_COST,

View File

@ -1,4 +1,7 @@
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-models";
import type {
ModelDefinitionConfig,
ModelProviderConfig,
} from "openclaw/plugin-sdk/provider-models";
export const MODELSTUDIO_BASE_URL = "https://coding-intl.dashscope.aliyuncs.com/v1";
export const MODELSTUDIO_GLOBAL_BASE_URL = MODELSTUDIO_BASE_URL;
@ -91,6 +94,62 @@ export const MODELSTUDIO_MODEL_CATALOG: ReadonlyArray<ModelDefinitionConfig> = [
},
];
function normalizeModelStudioBaseUrl(baseUrl: string | undefined): string {
const trimmed = baseUrl?.trim();
if (!trimmed) {
return "";
}
try {
const url = new URL(trimmed);
url.hash = "";
url.search = "";
return url.toString().replace(/\/+$/, "").toLowerCase();
} catch {
return trimmed.replace(/\/+$/, "").toLowerCase();
}
}
export function isNativeModelStudioBaseUrl(baseUrl: string | undefined): boolean {
const normalized = normalizeModelStudioBaseUrl(baseUrl);
return (
normalized === MODELSTUDIO_BASE_URL ||
normalized === MODELSTUDIO_CN_BASE_URL ||
normalized === MODELSTUDIO_STANDARD_CN_BASE_URL ||
normalized === MODELSTUDIO_STANDARD_GLOBAL_BASE_URL
);
}
function withStreamingUsageCompat(provider: ModelProviderConfig): ModelProviderConfig {
if (!Array.isArray(provider.models) || provider.models.length === 0) {
return provider;
}
let changed = false;
const models = provider.models.map((model) => {
if (model.compat?.supportsUsageInStreaming !== undefined) {
return model;
}
changed = true;
return {
...model,
compat: {
...model.compat,
supportsUsageInStreaming: true,
},
};
});
return changed ? { ...provider, models } : provider;
}
export function applyModelStudioNativeStreamingUsageCompat(
provider: ModelProviderConfig,
): ModelProviderConfig {
return isNativeModelStudioBaseUrl(provider.baseUrl)
? withStreamingUsageCompat(provider)
: provider;
}
export function buildModelStudioModelDefinition(params: {
id: string;
name?: string;

View File

@ -1,6 +1,9 @@
export {
applyMoonshotNativeStreamingUsageCompat,
buildMoonshotProvider,
isNativeMoonshotBaseUrl,
MOONSHOT_BASE_URL,
MOONSHOT_CN_BASE_URL,
MOONSHOT_DEFAULT_MODEL_ID,
} from "./provider-catalog.js";
export { MOONSHOT_CN_BASE_URL, MOONSHOT_DEFAULT_MODEL_REF } from "./onboard.js";
export { MOONSHOT_DEFAULT_MODEL_REF } from "./onboard.js";

View File

@ -5,10 +5,9 @@ import {
import {
buildMoonshotProvider,
MOONSHOT_BASE_URL,
MOONSHOT_CN_BASE_URL,
MOONSHOT_DEFAULT_MODEL_ID,
} from "./provider-catalog.js";
export const MOONSHOT_CN_BASE_URL = "https://api.moonshot.cn/v1";
export const MOONSHOT_DEFAULT_MODEL_REF = `moonshot/${MOONSHOT_DEFAULT_MODEL_ID}`;
const moonshotPresetAppliers = createDefaultModelPresetAppliers<[string]>({

View File

@ -1,6 +1,7 @@
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-models";
export const MOONSHOT_BASE_URL = "https://api.moonshot.ai/v1";
export const MOONSHOT_CN_BASE_URL = "https://api.moonshot.cn/v1";
export const MOONSHOT_DEFAULT_MODEL_ID = "kimi-k2.5";
const MOONSHOT_DEFAULT_CONTEXT_WINDOW = 262144;
const MOONSHOT_DEFAULT_MAX_TOKENS = 262144;
@ -50,6 +51,55 @@ const MOONSHOT_MODEL_CATALOG = [
},
] as const;
function normalizeMoonshotBaseUrl(baseUrl: string | undefined): string {
const trimmed = baseUrl?.trim();
if (!trimmed) {
return "";
}
try {
const url = new URL(trimmed);
url.hash = "";
url.search = "";
return url.toString().replace(/\/+$/, "").toLowerCase();
} catch {
return trimmed.replace(/\/+$/, "").toLowerCase();
}
}
export function isNativeMoonshotBaseUrl(baseUrl: string | undefined): boolean {
const normalized = normalizeMoonshotBaseUrl(baseUrl);
return normalized === MOONSHOT_BASE_URL || normalized === MOONSHOT_CN_BASE_URL;
}
function withStreamingUsageCompat(provider: ModelProviderConfig): ModelProviderConfig {
if (!Array.isArray(provider.models) || provider.models.length === 0) {
return provider;
}
let changed = false;
const models = provider.models.map((model) => {
if (model.compat?.supportsUsageInStreaming !== undefined) {
return model;
}
changed = true;
return {
...model,
compat: {
...model.compat,
supportsUsageInStreaming: true,
},
};
});
return changed ? { ...provider, models } : provider;
}
export function applyMoonshotNativeStreamingUsageCompat(
provider: ModelProviderConfig,
): ModelProviderConfig {
return isNativeMoonshotBaseUrl(provider.baseUrl) ? withStreamingUsageCompat(provider) : provider;
}
export function buildMoonshotProvider(): ModelProviderConfig {
return {
baseUrl: MOONSHOT_BASE_URL,

View File

@ -1,5 +1,5 @@
import type { ImageGenerationProvider } from "openclaw/plugin-sdk/image-generation";
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth";
import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
import { OPENAI_DEFAULT_IMAGE_MODEL as DEFAULT_OPENAI_IMAGE_MODEL } from "./default-models.js";
const DEFAULT_OPENAI_IMAGE_BASE_URL = "https://api.openai.com/v1";

View File

@ -2,7 +2,7 @@ import {
type ProviderResolveDynamicModelContext,
type ProviderRuntimeModel,
} from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import {
DEFAULT_CONTEXT_TOKENS,
normalizeModelCompat,

View File

@ -1,5 +1,5 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { applyOpencodeGoConfig, OPENCODE_GO_DEFAULT_MODEL_REF } from "./api.js";
const PROVIDER_ID = "opencode-go";

View File

@ -1,6 +1,6 @@
import { isMiniMaxModernModelId } from "openclaw/plugin-sdk/minimax";
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { applyOpencodeZenConfig, OPENCODE_ZEN_DEFAULT_MODEL } from "./api.js";
const PROVIDER_ID = "opencode";

View File

@ -4,3 +4,4 @@ export {
SGLANG_MODEL_PLACEHOLDER,
SGLANG_PROVIDER_LABEL,
} from "./defaults.js";
export { buildSglangProvider } from "./models.js";

View File

@ -8,6 +8,7 @@ import {
SGLANG_DEFAULT_BASE_URL,
SGLANG_MODEL_PLACEHOLDER,
SGLANG_PROVIDER_LABEL,
buildSglangProvider,
} from "./api.js";
const PROVIDER_ID = "sglang";
@ -64,7 +65,7 @@ export default definePluginEntry({
return await providerSetup.discoverOpenAICompatibleSelfHostedProvider({
ctx,
providerId: PROVIDER_ID,
buildProvider: providerSetup.buildSglangProvider,
buildProvider: buildSglangProvider,
});
},
},

View File

@ -0,0 +1,23 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { discoverOpenAICompatibleLocalModels } from "openclaw/plugin-sdk/provider-setup";
import { SGLANG_DEFAULT_BASE_URL, SGLANG_PROVIDER_LABEL } from "./defaults.js";
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
export async function buildSglangProvider(params?: {
baseUrl?: string;
apiKey?: string;
}): Promise<ProviderConfig> {
const baseUrl = (params?.baseUrl?.trim() || SGLANG_DEFAULT_BASE_URL).replace(/\/+$/, "");
const models = await discoverOpenAICompatibleLocalModels({
baseUrl,
apiKey: params?.apiKey,
label: SGLANG_PROVIDER_LABEL,
});
return {
baseUrl,
api: "openai-completions",
models,
};
}

View File

@ -16,3 +16,4 @@ export * from "./src/probe.js";
export * from "./src/sent-thread-cache.js";
export * from "./src/targets.js";
export * from "./src/threading-tool-context.js";
export { resolveSlackRuntimeGroupPolicy } from "./src/monitor/provider.js";

View File

@ -584,6 +584,8 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
export { isNonRecoverableSlackAuthError } from "./reconnect-policy.js";
export const resolveSlackRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy;
export const __testing = {
publishSlackConnectedStatus,
publishSlackDisconnectedStatus,

View File

@ -85,6 +85,7 @@ export {
export {
createTelegramThreadBindingManager,
getTelegramThreadBindingManager,
resetTelegramThreadBindingsForTests,
setTelegramThreadBindingIdleTimeoutBySessionKey,
setTelegramThreadBindingMaxAgeBySessionKey,
} from "./src/thread-bindings.js";

View File

@ -808,14 +808,16 @@ export function setTelegramThreadBindingMaxAgeBySessionKey(params: {
});
}
export async function resetTelegramThreadBindingsForTests() {
for (const manager of getThreadBindingsState().managersByAccountId.values()) {
manager.stop();
}
await Promise.allSettled(getThreadBindingsState().persistQueueByAccountId.values());
getThreadBindingsState().persistQueueByAccountId.clear();
getThreadBindingsState().managersByAccountId.clear();
getThreadBindingsState().bindingsByAccountConversation.clear();
}
export const __testing = {
async resetTelegramThreadBindingsForTests() {
for (const manager of getThreadBindingsState().managersByAccountId.values()) {
manager.stop();
}
await Promise.allSettled(getThreadBindingsState().persistQueueByAccountId.values());
getThreadBindingsState().persistQueueByAccountId.clear();
getThreadBindingsState().managersByAccountId.clear();
getThreadBindingsState().bindingsByAccountConversation.clear();
},
resetTelegramThreadBindingsForTests,
};

View File

@ -4,3 +4,4 @@ export {
VLLM_MODEL_PLACEHOLDER,
VLLM_PROVIDER_LABEL,
} from "./defaults.js";
export { buildVllmProvider } from "./models.js";

View File

@ -8,6 +8,7 @@ import {
VLLM_DEFAULT_BASE_URL,
VLLM_MODEL_PLACEHOLDER,
VLLM_PROVIDER_LABEL,
buildVllmProvider,
} from "./api.js";
const PROVIDER_ID = "vllm";
@ -64,7 +65,7 @@ export default definePluginEntry({
return await providerSetup.discoverOpenAICompatibleSelfHostedProvider({
ctx,
providerId: PROVIDER_ID,
buildProvider: providerSetup.buildVllmProvider,
buildProvider: buildVllmProvider,
});
},
},

23
extensions/vllm/models.ts Normal file
View File

@ -0,0 +1,23 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { discoverOpenAICompatibleLocalModels } from "openclaw/plugin-sdk/provider-setup";
import { VLLM_DEFAULT_BASE_URL, VLLM_PROVIDER_LABEL } from "./defaults.js";
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
export async function buildVllmProvider(params?: {
baseUrl?: string;
apiKey?: string;
}): Promise<ProviderConfig> {
const baseUrl = (params?.baseUrl?.trim() || VLLM_DEFAULT_BASE_URL).replace(/\/+$/, "");
const models = await discoverOpenAICompatibleLocalModels({
baseUrl,
apiKey: params?.apiKey,
label: VLLM_PROVIDER_LABEL,
});
return {
baseUrl,
api: "openai-completions",
models,
};
}

View File

@ -1,5 +1,5 @@
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import { ensureModelAllowlistEntry } from "openclaw/plugin-sdk/provider-onboard";
import { buildDoubaoCodingProvider, buildDoubaoProvider } from "./provider-catalog.js";

View File

@ -8,9 +8,9 @@ import {
resolveMergedAccountConfig,
resolveUserPath,
type OpenClawConfig,
} from "openclaw/plugin-sdk/account-resolution";
} from "openclaw/plugin-sdk/account-core";
import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
import { hasWebCredsSync } from "./auth-store.js";
import { hasWebCredsSync } from "./creds-files.js";
import type { DmPolicy, GroupPolicy, WhatsAppAccountConfig } from "./runtime-api.js";
export type ResolvedWhatsAppAccount = {

View File

@ -9,7 +9,9 @@ import { defaultRuntime, type RuntimeEnv } from "openclaw/plugin-sdk/runtime-env
import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
import type { WebChannel } from "openclaw/plugin-sdk/text-runtime";
import { resolveUserPath } from "openclaw/plugin-sdk/text-runtime";
import { hasWebCredsSync, resolveWebCredsBackupPath, resolveWebCredsPath } from "./creds-files.js";
import { resolveComparableIdentity, type WhatsAppSelfIdentity } from "./identity.js";
export { hasWebCredsSync, resolveWebCredsBackupPath, resolveWebCredsPath };
export function resolveDefaultWebAuthDir(): string {
return path.join(resolveOAuthDir(), "whatsapp", DEFAULT_ACCOUNT_ID);
@ -17,23 +19,6 @@ export function resolveDefaultWebAuthDir(): string {
export const WA_WEB_AUTH_DIR = resolveDefaultWebAuthDir();
export function resolveWebCredsPath(authDir: string): string {
return path.join(authDir, "creds.json");
}
export function resolveWebCredsBackupPath(authDir: string): string {
return path.join(authDir, "creds.json.bak");
}
export function hasWebCredsSync(authDir: string): boolean {
try {
const stats = fsSync.statSync(resolveWebCredsPath(authDir));
return stats.isFile() && stats.size > 1;
} catch {
return false;
}
}
export function readCredsJsonRaw(filePath: string): string | null {
try {
if (!fsSync.existsSync(filePath)) {

View File

@ -0,0 +1,19 @@
import fsSync from "node:fs";
import path from "node:path";
export function resolveWebCredsPath(authDir: string): string {
return path.join(authDir, "creds.json");
}
export function resolveWebCredsBackupPath(authDir: string): string {
return path.join(authDir, "creds.json.bak");
}
export function hasWebCredsSync(authDir: string): boolean {
try {
const stats = fsSync.statSync(resolveWebCredsPath(authDir));
return stats.isFile() && stats.size > 1;
} catch {
return false;
}
}

View File

@ -10,6 +10,8 @@ export {
XAI_DEFAULT_MAX_TOKENS,
} from "./model-definitions.js";
export { isModernXaiModel, resolveXaiForwardCompatModel } from "./provider-models.js";
import { normalizeXaiModelId } from "./model-id.js";
export { normalizeXaiModelId };
export const XAI_TOOL_SCHEMA_PROFILE = "xai";
export const HTML_ENTITY_TOOL_CALL_ARGUMENTS_ENCODING = "html-entities";
@ -35,25 +37,3 @@ export function applyXaiModelCompat<T extends { compat?: unknown }>(model: T): T
} as T extends { compat?: infer TCompat } ? TCompat : never,
} as T;
}
export function normalizeXaiModelId(id: string): string {
if (id === "grok-4-fast-reasoning") {
return "grok-4-fast";
}
if (id === "grok-4-1-fast-reasoning") {
return "grok-4-1-fast";
}
if (id === "grok-4.20-experimental-beta-0304-reasoning") {
return "grok-4.20-beta-latest-reasoning";
}
if (id === "grok-4.20-experimental-beta-0304-non-reasoning") {
return "grok-4.20-beta-latest-non-reasoning";
}
if (id === "grok-4.20-reasoning") {
return "grok-4.20-beta-latest-reasoning";
}
if (id === "grok-4.20-non-reasoning") {
return "grok-4.20-beta-latest-non-reasoning";
}
return id;
}

View File

@ -0,0 +1,21 @@
export function normalizeXaiModelId(id: string): string {
if (id === "grok-4-fast-reasoning") {
return "grok-4-fast";
}
if (id === "grok-4-1-fast-reasoning") {
return "grok-4-1-fast";
}
if (id === "grok-4.20-experimental-beta-0304-reasoning") {
return "grok-4.20-beta-latest-reasoning";
}
if (id === "grok-4.20-experimental-beta-0304-non-reasoning") {
return "grok-4.20-beta-latest-non-reasoning";
}
if (id === "grok-4.20-reasoning") {
return "grok-4.20-beta-latest-reasoning";
}
if (id === "grok-4.20-non-reasoning") {
return "grok-4.20-beta-latest-non-reasoning";
}
return id;
}

View File

@ -224,6 +224,10 @@
"types": "./dist/plugin-sdk/account-helpers.d.ts",
"default": "./dist/plugin-sdk/account-helpers.js"
},
"./plugin-sdk/account-core": {
"types": "./dist/plugin-sdk/account-core.d.ts",
"default": "./dist/plugin-sdk/account-core.js"
},
"./plugin-sdk/account-id": {
"types": "./dist/plugin-sdk/account-id.d.ts",
"default": "./dist/plugin-sdk/account-id.js"
@ -236,6 +240,10 @@
"types": "./dist/plugin-sdk/agent-config-primitives.d.ts",
"default": "./dist/plugin-sdk/agent-config-primitives.js"
},
"./plugin-sdk/amazon-bedrock": {
"types": "./dist/plugin-sdk/amazon-bedrock.d.ts",
"default": "./dist/plugin-sdk/amazon-bedrock.js"
},
"./plugin-sdk/allow-from": {
"types": "./dist/plugin-sdk/allow-from.d.ts",
"default": "./dist/plugin-sdk/allow-from.js"
@ -612,6 +620,10 @@
"types": "./dist/plugin-sdk/moonshot.d.ts",
"default": "./dist/plugin-sdk/moonshot.js"
},
"./plugin-sdk/mistral": {
"types": "./dist/plugin-sdk/mistral.d.ts",
"default": "./dist/plugin-sdk/mistral.js"
},
"./plugin-sdk/msteams": {
"types": "./dist/plugin-sdk/msteams.d.ts",
"default": "./dist/plugin-sdk/msteams.js"
@ -656,6 +668,10 @@
"types": "./dist/plugin-sdk/provider-auth.d.ts",
"default": "./dist/plugin-sdk/provider-auth.js"
},
"./plugin-sdk/provider-auth-runtime": {
"types": "./dist/plugin-sdk/provider-auth-runtime.d.ts",
"default": "./dist/plugin-sdk/provider-auth-runtime.js"
},
"./plugin-sdk/provider-auth-api-key": {
"types": "./dist/plugin-sdk/provider-auth-api-key.d.ts",
"default": "./dist/plugin-sdk/provider-auth-api-key.js"
@ -868,6 +884,10 @@
"types": "./dist/plugin-sdk/volcengine.d.ts",
"default": "./dist/plugin-sdk/volcengine.js"
},
"./plugin-sdk/whatsapp-auth-presence": {
"types": "./dist/plugin-sdk/whatsapp-auth-presence.d.ts",
"default": "./dist/plugin-sdk/whatsapp-auth-presence.js"
},
"./plugin-sdk/whatsapp-core": {
"types": "./dist/plugin-sdk/whatsapp-core.d.ts",
"default": "./dist/plugin-sdk/whatsapp-core.js"

View File

@ -46,9 +46,11 @@
"testing",
"temp-path",
"account-helpers",
"account-core",
"account-id",
"account-resolution",
"agent-config-primitives",
"amazon-bedrock",
"allow-from",
"allowlist-config-edit",
"anthropic-vertex",
@ -143,6 +145,7 @@
"modelstudio",
"modelstudio-definitions",
"moonshot",
"mistral",
"msteams",
"nextcloud-talk",
"nvidia",
@ -154,6 +157,7 @@
"opencode-go",
"qianfan",
"provider-auth",
"provider-auth-runtime",
"provider-auth-api-key",
"provider-auth-result",
"provider-auth-login",
@ -207,6 +211,7 @@
"web-media",
"voice-call",
"volcengine",
"whatsapp-auth-presence",
"whatsapp-core",
"whatsapp-shared",
"whatsapp-surface",

View File

@ -3,13 +3,32 @@ import path from "node:path";
import ts from "typescript";
export const GENERATED_PLUGIN_SDK_FACADES = [
{
subpath: "amazon-bedrock",
source: "../../extensions/amazon-bedrock/api.js",
exports: [
"discoverBedrockModels",
"mergeImplicitBedrockProvider",
"resetBedrockDiscoveryCacheForTest",
"resolveBedrockConfigApiKey",
"resolveImplicitBedrockProvider",
],
},
{
subpath: "anthropic-vertex",
source: "../../extensions/anthropic-vertex/api.js",
exports: [
"ANTHROPIC_VERTEX_DEFAULT_MODEL_ID",
"buildAnthropicVertexProvider",
"hasAnthropicVertexAvailableAuth",
"hasAnthropicVertexCredentials",
"mergeImplicitAnthropicVertexProvider",
"resolveAnthropicVertexClientRegion",
"resolveAnthropicVertexConfigApiKey",
"resolveImplicitAnthropicVertexProvider",
"resolveAnthropicVertexProjectId",
"resolveAnthropicVertexRegion",
"resolveAnthropicVertexRegionFromBaseUrl",
],
},
{
@ -99,6 +118,8 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"DiscordSendResult",
"handleDiscordMessageAction",
"inspectDiscordAccount",
"isDiscordExecApprovalApprover",
"isDiscordExecApprovalClientEnabled",
"InspectedDiscordAccount",
"listDiscordAccountIds",
"listDiscordDirectoryGroupsFromConfig",
@ -111,14 +132,17 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"resolveDefaultDiscordAccountId",
"resolveDiscordAccount",
"resolveDiscordChannelId",
"resolveDiscordRuntimeGroupPolicy",
"resolveDiscordGroupRequireMention",
"resolveDiscordGroupToolPolicy",
],
typeExports: [
"DiscordComponentMessageSpec",
"DiscordProbe",
"DiscordSendComponents",
"DiscordSendEmbeds",
"DiscordSendResult",
"DiscordTokenResolution",
"InspectedDiscordAccount",
"ResolvedDiscordAccount",
],
@ -239,6 +263,8 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
exports: [
"buildFeishuConversationId",
"createFeishuThreadBindingManager",
"feishuSessionBindingAdapterChannels",
"feishuThreadBindingTesting",
"parseFeishuDirectConversationId",
"parseFeishuConversationId",
"parseFeishuTargetId",
@ -249,13 +275,19 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
source: "../../extensions/google/api.js",
exports: [
"applyGoogleGeminiModelDefault",
"createGoogleThinkingPayloadWrapper",
"DEFAULT_GOOGLE_API_BASE_URL",
"GOOGLE_GEMINI_DEFAULT_MODEL",
"isGoogleGenerativeAiApi",
"normalizeAntigravityModelId",
"normalizeGoogleApiBaseUrl",
"normalizeGoogleGenerativeAiBaseUrl",
"normalizeGoogleModelId",
"normalizeGoogleProviderConfig",
"parseGeminiAuth",
"sanitizeGoogleThinkingPayload",
"resolveGoogleGenerativeAiApiOrigin",
"resolveGoogleGenerativeAiTransport",
"shouldNormalizeGoogleProviderConfig",
"shouldNormalizeGoogleGenerativeAiProviderConfig",
],
},
{
@ -329,6 +361,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
source: "../../extensions/imessage/api.js",
exports: [
"normalizeIMessageHandle",
"resolveIMessageRuntimeGroupPolicy",
"resolveIMessageGroupRequireMention",
"resolveIMessageGroupToolPolicy",
],
@ -337,6 +370,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
subpath: "imessage-runtime",
source: "../../extensions/imessage/runtime-api.js",
exports: ["monitorIMessageProvider", "probeIMessage", "sendMessageIMessage"],
typeExports: ["IMessageProbe"],
},
{
subpath: "irc-surface",
@ -465,7 +499,11 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
{
subpath: "matrix-surface",
source: "../../extensions/matrix/api.js",
exports: ["createMatrixThreadBindingManager", "resetMatrixThreadBindingsForTests"],
exports: [
"createMatrixThreadBindingManager",
"matrixSessionBindingAdapterChannels",
"resetMatrixThreadBindingsForTests",
],
},
{
subpath: "matrix-thread-bindings",
@ -487,6 +525,8 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"buildMinimaxPortalProvider",
"buildMinimaxProvider",
"isMiniMaxModernModelId",
"MINIMAX_API_BASE_URL",
"MINIMAX_CN_API_BASE_URL",
"MINIMAX_DEFAULT_MODEL_ID",
"MINIMAX_DEFAULT_MODEL_REF",
"MINIMAX_TEXT_MODEL_CATALOG",
@ -498,6 +538,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
subpath: "modelstudio",
source: "../../extensions/modelstudio/api.js",
exports: [
"applyModelStudioNativeStreamingUsageCompat",
"buildModelStudioDefaultModelDefinition",
"buildModelStudioModelDefinition",
"MODELSTUDIO_BASE_URL",
@ -509,6 +550,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"MODELSTUDIO_STANDARD_CN_BASE_URL",
"MODELSTUDIO_STANDARD_GLOBAL_BASE_URL",
"MODELSTUDIO_MODEL_CATALOG",
"isNativeModelStudioBaseUrl",
"buildModelStudioProvider",
],
},
@ -530,7 +572,20 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
{
subpath: "moonshot",
source: "../../extensions/moonshot/api.js",
exports: ["buildMoonshotProvider"],
exports: [
"applyMoonshotNativeStreamingUsageCompat",
"buildMoonshotProvider",
"isNativeMoonshotBaseUrl",
"MOONSHOT_BASE_URL",
"MOONSHOT_CN_BASE_URL",
"MOONSHOT_DEFAULT_MODEL_ID",
"MOONSHOT_DEFAULT_MODEL_REF",
],
},
{
subpath: "mistral",
source: "../../extensions/mistral/api.js",
exports: ["buildMistralProvider"],
},
{
subpath: "nvidia",
@ -633,7 +688,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"signalMessageActions",
"SignalSender",
],
typeExports: ["ResolvedSignalAccount", "SignalSender"],
typeExports: ["ResolvedSignalAccount", "SignalProbe", "SignalSender"],
},
{
subpath: "provider-reasoning",
@ -649,6 +704,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
subpath: "sglang",
source: "../../extensions/sglang/api.js",
exports: [
"buildSglangProvider",
"SGLANG_DEFAULT_API_KEY_ENV_VAR",
"SGLANG_DEFAULT_BASE_URL",
"SGLANG_MODEL_PLACEHOLDER",
@ -722,6 +778,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"resolveDefaultSlackAccountId",
"resolveSlackAutoThreadId",
"resolveSlackGroupRequireMention",
"resolveSlackRuntimeGroupPolicy",
"resolveSlackGroupToolPolicy",
"resolveSlackReplyToMode",
"ResolvedSlackAccount",
@ -733,7 +790,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"removeSlackReaction",
"unpinSlackMessage",
],
typeExports: ["InspectedSlackAccount", "ResolvedSlackAccount"],
typeExports: ["InspectedSlackAccount", "ResolvedSlackAccount", "SlackProbe"],
},
{
subpath: "together",
@ -788,6 +845,8 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"probeTelegram",
"reactMessageTelegram",
"renameForumTopicTelegram",
"resetTelegramThreadBindingsForTests",
"resolveTelegramRuntimeGroupPolicy",
"resolveTelegramToken",
"sendMessageTelegram",
"sendPollTelegram",
@ -851,6 +910,8 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"StickerMetadata",
"TelegramButtonStyle",
"TelegramInlineButtons",
"TelegramProbe",
"TelegramTokenResolution",
],
},
{
@ -886,6 +947,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
subpath: "vllm",
source: "../../extensions/vllm/api.js",
exports: [
"buildVllmProvider",
"VLLM_DEFAULT_API_KEY_ENV_VAR",
"VLLM_DEFAULT_BASE_URL",
"VLLM_MODEL_PLACEHOLDER",
@ -955,6 +1017,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
"resolveWhatsAppGroupRequireMention",
"resolveWhatsAppGroupToolPolicy",
"resolveWhatsAppOutboundTarget",
"whatsappAccessControlTesting",
],
typeExports: [
"WebChannelStatus",
@ -966,7 +1029,12 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
{
subpath: "zalo-setup",
source: "../../extensions/zalo/api.js",
exports: ["zaloSetupAdapter", "zaloSetupWizard"],
exports: [
"evaluateZaloGroupAccess",
"resolveZaloRuntimeGroupPolicy",
"zaloSetupAdapter",
"zaloSetupWizard",
],
},
];

View File

@ -1,124 +0,0 @@
import { existsSync, readFileSync } from "node:fs";
import { homedir, platform } from "node:os";
import { join } from "node:path";
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
const ANTHROPIC_VERTEX_DEFAULT_REGION = "global";
const ANTHROPIC_VERTEX_REGION_RE = /^[a-z0-9-]+$/;
const GCLOUD_DEFAULT_ADC_PATH = join(
homedir(),
".config",
"gcloud",
"application_default_credentials.json",
);
type AdcProjectFile = {
project_id?: unknown;
quota_project_id?: unknown;
};
export function resolveAnthropicVertexProjectId(
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
return (
normalizeOptionalSecretInput(env.ANTHROPIC_VERTEX_PROJECT_ID) ||
normalizeOptionalSecretInput(env.GOOGLE_CLOUD_PROJECT) ||
normalizeOptionalSecretInput(env.GOOGLE_CLOUD_PROJECT_ID) ||
resolveAnthropicVertexProjectIdFromAdc(env)
);
}
export function resolveAnthropicVertexRegion(env: NodeJS.ProcessEnv = process.env): string {
const region =
normalizeOptionalSecretInput(env.GOOGLE_CLOUD_LOCATION) ||
normalizeOptionalSecretInput(env.CLOUD_ML_REGION);
return region && ANTHROPIC_VERTEX_REGION_RE.test(region)
? region
: ANTHROPIC_VERTEX_DEFAULT_REGION;
}
export function resolveAnthropicVertexRegionFromBaseUrl(baseUrl?: string): string | undefined {
const trimmed = baseUrl?.trim();
if (!trimmed) {
return undefined;
}
try {
const host = new URL(trimmed).hostname.toLowerCase();
if (host === "aiplatform.googleapis.com") {
return "global";
}
const match = /^([a-z0-9-]+)-aiplatform\.googleapis\.com$/.exec(host);
return match?.[1];
} catch {
return undefined;
}
}
export function resolveAnthropicVertexClientRegion(params?: {
baseUrl?: string;
env?: NodeJS.ProcessEnv;
}): string {
return (
resolveAnthropicVertexRegionFromBaseUrl(params?.baseUrl) ||
resolveAnthropicVertexRegion(params?.env)
);
}
function hasAnthropicVertexMetadataServerAdc(env: NodeJS.ProcessEnv = process.env): boolean {
const explicitMetadataOptIn = normalizeOptionalSecretInput(env.ANTHROPIC_VERTEX_USE_GCP_METADATA);
return explicitMetadataOptIn === "1" || explicitMetadataOptIn?.toLowerCase() === "true";
}
function resolveAnthropicVertexDefaultAdcPath(env: NodeJS.ProcessEnv = process.env): string {
return platform() === "win32"
? join(
env.APPDATA ?? join(homedir(), "AppData", "Roaming"),
"gcloud",
"application_default_credentials.json",
)
: GCLOUD_DEFAULT_ADC_PATH;
}
function resolveAnthropicVertexAdcCredentialsPath(
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
const explicitCredentialsPath = normalizeOptionalSecretInput(env.GOOGLE_APPLICATION_CREDENTIALS);
if (explicitCredentialsPath) {
return existsSync(explicitCredentialsPath) ? explicitCredentialsPath : undefined;
}
const defaultAdcPath = resolveAnthropicVertexDefaultAdcPath(env);
return existsSync(defaultAdcPath) ? defaultAdcPath : undefined;
}
function resolveAnthropicVertexProjectIdFromAdc(
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
const credentialsPath = resolveAnthropicVertexAdcCredentialsPath(env);
if (!credentialsPath) {
return undefined;
}
try {
const parsed = JSON.parse(readFileSync(credentialsPath, "utf8")) as AdcProjectFile;
return (
normalizeOptionalSecretInput(parsed.project_id) ||
normalizeOptionalSecretInput(parsed.quota_project_id)
);
} catch {
return undefined;
}
}
export function hasAnthropicVertexCredentials(env: NodeJS.ProcessEnv = process.env): boolean {
return (
hasAnthropicVertexMetadataServerAdc(env) ||
resolveAnthropicVertexAdcCredentialsPath(env) !== undefined
);
}
export function hasAnthropicVertexAvailableAuth(env: NodeJS.ProcessEnv = process.env): boolean {
return hasAnthropicVertexCredentials(env);
}

View File

@ -32,7 +32,7 @@ vi.mock("@anthropic-ai/vertex-sdk", () => ({
import {
resolveAnthropicVertexRegion,
resolveAnthropicVertexRegionFromBaseUrl,
} from "./anthropic-vertex-provider.js";
} from "../plugin-sdk/anthropic-vertex.js";
let createAnthropicVertexStreamFn: typeof import("./anthropic-vertex-stream.js").createAnthropicVertexStreamFn;
let createAnthropicVertexStreamFnForModel: typeof import("./anthropic-vertex-stream.js").createAnthropicVertexStreamFnForModel;

View File

@ -4,7 +4,7 @@ import { streamAnthropic, type AnthropicOptions, type Model } from "@mariozechne
import {
resolveAnthropicVertexClientRegion,
resolveAnthropicVertexProjectId,
} from "./anthropic-vertex-provider.js";
} from "../plugin-sdk/anthropic-vertex.js";
type AnthropicVertexEffort = NonNullable<AnthropicOptions["effort"]>;

View File

@ -15,7 +15,7 @@ const baseActiveAnthropicSummary = {
};
async function loadDiscovery() {
const mod = await import("./bedrock-discovery.js");
const mod = await import("../plugin-sdk/amazon-bedrock.js");
mod.resetBedrockDiscoveryCacheForTest();
return mod;
}
@ -139,4 +139,47 @@ describe("bedrock discovery", () => {
});
expect(sendMock).toHaveBeenCalledTimes(2);
});
it("resolves the Bedrock config apiKey from AWS auth env vars", async () => {
const { resolveBedrockConfigApiKey } = await loadDiscovery();
expect(
resolveBedrockConfigApiKey({
AWS_BEARER_TOKEN_BEDROCK: "bearer", // pragma: allowlist secret
AWS_PROFILE: "default",
}),
).toBe("AWS_BEARER_TOKEN_BEDROCK");
expect(resolveBedrockConfigApiKey({} as NodeJS.ProcessEnv)).toBe("AWS_PROFILE");
});
it("merges implicit Bedrock models into explicit provider overrides", async () => {
const { mergeImplicitBedrockProvider } = await loadDiscovery();
expect(
mergeImplicitBedrockProvider({
existing: {
baseUrl: "https://override.example.com",
headers: { "x-test-header": "1" },
models: [],
},
implicit: {
baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
api: "bedrock-converse-stream",
auth: "aws-sdk",
models: [
{
id: "amazon.nova-micro-v1:0",
name: "Nova",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1,
maxTokens: 1,
},
],
},
}).models?.map((model) => model.id),
).toEqual(["amazon.nova-micro-v1:0"]);
});
});

View File

@ -1,8 +0,0 @@
// Deprecated compat shim. Prefer openclaw/plugin-sdk/cloudflare-ai-gateway.
export {
buildCloudflareAiGatewayModelDefinition,
CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_ID,
CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF,
CLOUDFLARE_AI_GATEWAY_PROVIDER_ID,
resolveCloudflareAiGatewayBaseUrl,
} from "../plugin-sdk/cloudflare-ai-gateway.js";

View File

@ -5,7 +5,7 @@ import {
resolveGoogleGenerativeAiApiOrigin,
resolveGoogleGenerativeAiTransport,
shouldNormalizeGoogleGenerativeAiProviderConfig,
} from "./google-generative-ai.js";
} from "../plugin-sdk/google.js";
describe("google-generative-ai helpers", () => {
it("detects the Google Generative AI transport id", () => {

View File

@ -1,46 +0,0 @@
import { normalizeGoogleApiBaseUrl } from "../infra/google-api-base-url.js";
type GoogleApiCarrier = {
api?: string | null;
};
type GoogleProviderConfigLike = GoogleApiCarrier & {
models?: ReadonlyArray<GoogleApiCarrier | null | undefined> | null;
};
export function isGoogleGenerativeAiApi(api?: string | null): boolean {
return api === "google-generative-ai";
}
export function normalizeGoogleGenerativeAiBaseUrl(baseUrl?: string): string | undefined {
return baseUrl ? normalizeGoogleApiBaseUrl(baseUrl) : baseUrl;
}
export function resolveGoogleGenerativeAiTransport<TApi extends string | null | undefined>(params: {
api: TApi;
baseUrl?: string;
}): { api: TApi; baseUrl?: string } {
return {
api: params.api,
baseUrl: isGoogleGenerativeAiApi(params.api)
? normalizeGoogleGenerativeAiBaseUrl(params.baseUrl)
: params.baseUrl,
};
}
export function resolveGoogleGenerativeAiApiOrigin(baseUrl?: string): string {
return normalizeGoogleApiBaseUrl(baseUrl).replace(/\/v1beta$/i, "");
}
export function shouldNormalizeGoogleGenerativeAiProviderConfig(
providerKey: string,
provider: GoogleProviderConfigLike,
): boolean {
if (providerKey === "google" || providerKey === "google-vertex") {
return true;
}
if (isGoogleGenerativeAiApi(provider.api)) {
return true;
}
return provider.models?.some((model) => isGoogleGenerativeAiApi(model?.api)) ?? false;
}

View File

@ -1,10 +1,10 @@
import { describe, expect, it } from "vitest";
import {
buildHuggingfaceModelDefinition,
discoverHuggingfaceModels,
HUGGINGFACE_MODEL_CATALOG,
buildHuggingfaceModelDefinition,
isHuggingfacePolicyLocked,
} from "./huggingface-models.js";
} from "../plugin-sdk/huggingface.js";
describe("huggingface-models", () => {
it("buildHuggingfaceModelDefinition returns config with required fields", () => {

View File

@ -1,9 +0,0 @@
// Deprecated compat shim. Prefer openclaw/plugin-sdk/huggingface.
export {
buildHuggingfaceModelDefinition,
discoverHuggingfaceModels,
HUGGINGFACE_BASE_URL,
HUGGINGFACE_MODEL_CATALOG,
HUGGINGFACE_POLICY_SUFFIXES,
isHuggingfacePolicyLocked,
} from "../plugin-sdk/huggingface.js";

View File

@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { normalizeXaiModelId } from "./model-id-normalization.js";
import { normalizeXaiModelId } from "../plugin-sdk/xai.js";
describe("normalizeXaiModelId", () => {
it("maps deprecated grok 4.20 beta ids to GA ids", () => {

View File

@ -1,45 +0,0 @@
// Keep model ID normalization dependency-free so config parsing and other
// startup-only paths do not pull in provider discovery or plugin loading.
export function normalizeGoogleModelId(id: string): string {
if (id === "gemini-3-pro") {
return "gemini-3-pro-preview";
}
if (id === "gemini-3-flash") {
return "gemini-3-flash-preview";
}
if (id === "gemini-3.1-pro") {
return "gemini-3.1-pro-preview";
}
if (id === "gemini-3.1-flash-lite") {
return "gemini-3.1-flash-lite-preview";
}
// Preserve compatibility with earlier OpenClaw docs/config that pointed at a
// non-existent Gemini Flash preview ID. Google's current Flash text model is
// `gemini-3-flash-preview`.
if (id === "gemini-3.1-flash" || id === "gemini-3.1-flash-preview") {
return "gemini-3-flash-preview";
}
return id;
}
export function normalizeXaiModelId(id: string): string {
if (id === "grok-4-fast-reasoning") {
return "grok-4-fast";
}
if (id === "grok-4-1-fast-reasoning") {
return "grok-4-1-fast";
}
if (id === "grok-4.20-experimental-beta-0304-reasoning") {
return "grok-4.20-beta-latest-reasoning";
}
if (id === "grok-4.20-experimental-beta-0304-non-reasoning") {
return "grok-4.20-beta-latest-non-reasoning";
}
if (id === "grok-4.20-reasoning") {
return "grok-4.20-beta-latest-reasoning";
}
if (id === "grok-4.20-non-reasoning") {
return "grok-4.20-beta-latest-non-reasoning";
}
return id;
}

View File

@ -3,8 +3,8 @@ import { writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { resolveCloudflareAiGatewayBaseUrl } from "../plugin-sdk/cloudflare-ai-gateway.js";
import { captureEnv } from "../test-utils/env.js";
import { resolveCloudflareAiGatewayBaseUrl } from "./cloudflare-ai-gateway.js";
import { NON_ENV_SECRETREF_MARKER } from "./model-auth-markers.js";
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";

View File

@ -1,117 +0,0 @@
import type { OpenClawConfig } from "../config/config.js";
import type { ModelDefinitionConfig } from "../config/types.models.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveOllamaApiBase } from "../plugin-sdk/provider-models.js";
import { isReasoningModelHeuristic } from "../plugin-sdk/provider-reasoning.js";
import { SGLANG_DEFAULT_BASE_URL, SGLANG_PROVIDER_LABEL } from "../plugin-sdk/sglang.js";
import { VLLM_DEFAULT_BASE_URL, VLLM_PROVIDER_LABEL } from "../plugin-sdk/vllm.js";
import {
SELF_HOSTED_DEFAULT_CONTEXT_WINDOW,
SELF_HOSTED_DEFAULT_COST,
SELF_HOSTED_DEFAULT_MAX_TOKENS,
} from "./self-hosted-provider-defaults.js";
export {
buildHuggingfaceProvider,
buildKilocodeProviderWithDiscovery,
buildVeniceProvider,
buildVercelAiGatewayProvider,
} from "../plugin-sdk/provider-catalog.js";
export { resolveOllamaApiBase } from "../plugin-sdk/provider-models.js";
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
const log = createSubsystemLogger("agents/model-providers");
type OpenAICompatModelsResponse = {
data?: Array<{
id?: string;
}>;
};
async function discoverOpenAICompatibleLocalModels(params: {
baseUrl: string;
apiKey?: string;
label: string;
contextWindow?: number;
maxTokens?: number;
}): Promise<ModelDefinitionConfig[]> {
if (process.env.VITEST || process.env.NODE_ENV === "test") {
return [];
}
const trimmedBaseUrl = params.baseUrl.trim().replace(/\/+$/, "");
const url = `${trimmedBaseUrl}/models`;
try {
const trimmedApiKey = params.apiKey?.trim();
const response = await fetch(url, {
headers: trimmedApiKey ? { Authorization: `Bearer ${trimmedApiKey}` } : undefined,
signal: AbortSignal.timeout(5000),
});
if (!response.ok) {
log.warn(`Failed to discover ${params.label} models: ${response.status}`);
return [];
}
const data = (await response.json()) as OpenAICompatModelsResponse;
const models = data.data ?? [];
if (models.length === 0) {
log.warn(`No ${params.label} models found on local instance`);
return [];
}
return models
.map((model) => ({ id: typeof model.id === "string" ? model.id.trim() : "" }))
.filter((model) => Boolean(model.id))
.map((model) => {
const modelId = model.id;
return {
id: modelId,
name: modelId,
reasoning: isReasoningModelHeuristic(modelId),
input: ["text"],
cost: SELF_HOSTED_DEFAULT_COST,
contextWindow: params.contextWindow ?? SELF_HOSTED_DEFAULT_CONTEXT_WINDOW,
maxTokens: params.maxTokens ?? SELF_HOSTED_DEFAULT_MAX_TOKENS,
} satisfies ModelDefinitionConfig;
});
} catch (error) {
log.warn(`Failed to discover ${params.label} models: ${String(error)}`);
return [];
}
}
export async function buildVllmProvider(params?: {
baseUrl?: string;
apiKey?: string;
}): Promise<ProviderConfig> {
const baseUrl = (params?.baseUrl?.trim() || VLLM_DEFAULT_BASE_URL).replace(/\/+$/, "");
const models = await discoverOpenAICompatibleLocalModels({
baseUrl,
apiKey: params?.apiKey,
label: VLLM_PROVIDER_LABEL,
});
return {
baseUrl,
api: "openai-completions",
models,
};
}
export async function buildSglangProvider(params?: {
baseUrl?: string;
apiKey?: string;
}): Promise<ProviderConfig> {
const baseUrl = (params?.baseUrl?.trim() || SGLANG_DEFAULT_BASE_URL).replace(/\/+$/, "");
const models = await discoverOpenAICompatibleLocalModels({
baseUrl,
apiKey: params?.apiKey,
label: SGLANG_PROVIDER_LABEL,
});
return {
baseUrl,
api: "openai-completions",
models,
};
}

View File

@ -2,12 +2,8 @@ import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { normalizeGoogleModelId } from "./model-id-normalization.js";
import {
normalizeAntigravityModelId,
normalizeProviders,
type ProviderConfig,
} from "./models-config.providers.js";
import { normalizeAntigravityModelId, normalizeGoogleModelId } from "../plugin-sdk/google.js";
import { normalizeProviders, type ProviderConfig } from "./models-config.providers.js";
function buildModel(id: string): NonNullable<ProviderConfig["models"]>[number] {
return {

View File

@ -2,9 +2,9 @@ import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { buildKilocodeProvider } from "../plugin-sdk/kilocode.js";
import { captureEnv } from "../test-utils/env.js";
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";
import { buildKilocodeProvider } from "./models-config.providers.js";
const KILOCODE_MODEL_IDS = ["kilo/auto"];

View File

@ -2,9 +2,9 @@ import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { buildKimiCodingProvider } from "../plugin-sdk/kimi-coding.js";
import { captureEnv } from "../test-utils/env.js";
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";
import { buildKimiCodingProvider } from "./models-config.providers.js";
describe("Kimi implicit provider (#22409)", () => {
it("should include Kimi when KIMI_API_KEY is configured", async () => {

View File

@ -1,8 +1,6 @@
import { describe, expect, it } from "vitest";
import {
applyNativeStreamingUsageCompat,
buildModelStudioProvider,
} from "./models-config.providers.js";
import { buildModelStudioProvider } from "../plugin-sdk/modelstudio.js";
import { applyNativeStreamingUsageCompat } from "./models-config.providers.js";
describe("Model Studio implicit provider", () => {
it("should opt native Model Studio baseUrls into streaming usage", () => {

View File

@ -3,10 +3,10 @@ import { writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { buildNvidiaProvider } from "../plugin-sdk/nvidia.js";
import { withEnvAsync } from "../test-utils/env.js";
import { resolveApiKeyForProvider } from "./model-auth.js";
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";
import { buildNvidiaProvider } from "./models-config.providers.js";
describe("NVIDIA provider", () => {
it("should include nvidia when NVIDIA_API_KEY is configured", async () => {

View File

@ -3,8 +3,8 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { ModelDefinitionConfig } from "../config/types.models.js";
import { resolveOllamaApiBase } from "../plugin-sdk/ollama-surface.js";
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";
import { resolveOllamaApiBase } from "./models-config.providers.js";
afterEach(() => {
vi.unstubAllEnvs();

View File

@ -1,29 +1,27 @@
export {
ANTHROPIC_VERTEX_DEFAULT_MODEL_ID,
buildAnthropicVertexProvider,
buildBytePlusCodingProvider,
buildBytePlusProvider,
buildDeepSeekProvider,
} from "../plugin-sdk/provider-catalog.js";
} from "../plugin-sdk/anthropic-vertex.js";
export { buildBytePlusCodingProvider, buildBytePlusProvider } from "../plugin-sdk/byteplus.js";
export { buildDeepSeekProvider } from "../plugin-sdk/deepseek.js";
export { buildKimiCodingProvider } from "../plugin-sdk/kimi-coding.js";
export { buildKilocodeProvider } from "../plugin-sdk/kilocode.js";
export { buildMinimaxPortalProvider, buildMinimaxProvider } from "../plugin-sdk/minimax.js";
export {
buildKimiCodingProvider,
buildKilocodeProvider,
buildMinimaxPortalProvider,
buildMinimaxProvider,
MODELSTUDIO_BASE_URL,
MODELSTUDIO_DEFAULT_MODEL_ID,
buildModelStudioProvider,
buildMoonshotProvider,
buildNvidiaProvider,
buildOpenAICodexProvider,
buildOpenrouterProvider,
} from "../plugin-sdk/modelstudio.js";
export { buildMoonshotProvider } from "../plugin-sdk/moonshot.js";
export { buildNvidiaProvider } from "../plugin-sdk/nvidia.js";
export { buildOpenAICodexProvider } from "../plugin-sdk/openai.js";
export { buildOpenrouterProvider } from "../plugin-sdk/openrouter.js";
export {
QIANFAN_BASE_URL,
QIANFAN_DEFAULT_MODEL_ID,
buildQianfanProvider,
buildSyntheticProvider,
buildTogetherProvider,
buildDoubaoCodingProvider,
buildDoubaoProvider,
XIAOMI_DEFAULT_MODEL_ID,
buildXiaomiProvider,
} from "../plugin-sdk/provider-catalog.js";
} from "../plugin-sdk/qianfan.js";
export { buildSyntheticProvider } from "../plugin-sdk/synthetic.js";
export { buildTogetherProvider } from "../plugin-sdk/together.js";
export { buildDoubaoCodingProvider, buildDoubaoProvider } from "../plugin-sdk/volcengine.js";
export { XIAOMI_DEFAULT_MODEL_ID, buildXiaomiProvider } from "../plugin-sdk/xiaomi.js";

View File

@ -2,43 +2,55 @@ import type { OpenClawConfig } from "../config/config.js";
import { coerceSecretRef, resolveSecretInputRef } from "../config/types.secrets.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
buildAnthropicVertexProvider,
buildKimiCodingProvider,
buildKilocodeProvider,
mergeImplicitBedrockProvider,
resolveBedrockConfigApiKey,
resolveImplicitBedrockProvider,
} from "../plugin-sdk/amazon-bedrock.js";
import {
mergeImplicitAnthropicVertexProvider,
resolveImplicitAnthropicVertexProvider,
resolveAnthropicVertexConfigApiKey,
} from "../plugin-sdk/anthropic-vertex.js";
import {
normalizeGoogleModelId,
normalizeGoogleProviderConfig,
shouldNormalizeGoogleProviderConfig,
} from "../plugin-sdk/google.js";
import { buildKilocodeProvider } from "../plugin-sdk/kilocode.js";
import { buildKimiCodingProvider } from "../plugin-sdk/kimi-coding.js";
import {
MODELSTUDIO_BASE_URL,
MODELSTUDIO_DEFAULT_MODEL_ID,
applyModelStudioNativeStreamingUsageCompat,
buildModelStudioProvider,
buildNvidiaProvider,
} from "../plugin-sdk/modelstudio.js";
import { applyMoonshotNativeStreamingUsageCompat } from "../plugin-sdk/moonshot.js";
import { buildNvidiaProvider } from "../plugin-sdk/nvidia.js";
import { resolveOllamaApiBase } from "../plugin-sdk/ollama-surface.js";
import {
QIANFAN_BASE_URL,
QIANFAN_DEFAULT_MODEL_ID,
buildQianfanProvider,
MODELSTUDIO_BASE_URL,
MODELSTUDIO_DEFAULT_MODEL_ID,
XIAOMI_DEFAULT_MODEL_ID,
buildXiaomiProvider,
} from "../plugin-sdk/provider-catalog.js";
} from "../plugin-sdk/qianfan.js";
import { normalizeXaiModelId } from "../plugin-sdk/xai.js";
import { XIAOMI_DEFAULT_MODEL_ID, buildXiaomiProvider } from "../plugin-sdk/xiaomi.js";
import { isRecord } from "../utils.js";
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
import { hasAnthropicVertexAvailableAuth } from "./anthropic-vertex-provider.js";
import { ensureAuthProfileStore, listProfilesForProvider } from "./auth-profiles.js";
import { discoverBedrockModels } from "./bedrock-discovery.js";
import {
normalizeGoogleGenerativeAiBaseUrl,
shouldNormalizeGoogleGenerativeAiProviderConfig,
} from "./google-generative-ai.js";
import { normalizeGoogleModelId, normalizeXaiModelId } from "./model-id-normalization.js";
import { resolveOllamaApiBase } from "./models-config.providers.discovery.js";
export { buildKilocodeProvider } from "../plugin-sdk/kilocode.js";
export { buildKimiCodingProvider } from "../plugin-sdk/kimi-coding.js";
export {
buildKimiCodingProvider,
buildKilocodeProvider,
MODELSTUDIO_BASE_URL,
MODELSTUDIO_DEFAULT_MODEL_ID,
buildModelStudioProvider,
buildNvidiaProvider,
} from "../plugin-sdk/modelstudio.js";
export { buildNvidiaProvider } from "../plugin-sdk/nvidia.js";
export {
QIANFAN_BASE_URL,
QIANFAN_DEFAULT_MODEL_ID,
buildQianfanProvider,
XIAOMI_DEFAULT_MODEL_ID,
buildXiaomiProvider,
} from "../plugin-sdk/provider-catalog.js";
} from "../plugin-sdk/qianfan.js";
export { XIAOMI_DEFAULT_MODEL_ID, buildXiaomiProvider } from "../plugin-sdk/xiaomi.js";
import {
groupPluginDiscoveryProvidersByOrder,
normalizePluginDiscoveryResult,
@ -52,8 +64,9 @@ import {
resolveEnvSecretRefHeaderValueMarker,
} from "./model-auth-markers.js";
import { resolveAwsSdkEnvVarName, resolveEnvApiKey } from "./model-auth.js";
export { resolveOllamaApiBase } from "./models-config.providers.discovery.js";
export { normalizeGoogleModelId, normalizeXaiModelId };
export { resolveOllamaApiBase } from "../plugin-sdk/ollama-surface.js";
export { normalizeGoogleModelId } from "../plugin-sdk/google.js";
export { normalizeXaiModelId } from "../plugin-sdk/xai.js";
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
@ -63,20 +76,51 @@ type SecretDefaults = {
exec?: string;
};
const MOONSHOT_NATIVE_BASE_URLS = new Set([
"https://api.moonshot.ai/v1",
"https://api.moonshot.cn/v1",
]);
const MODELSTUDIO_NATIVE_BASE_URLS = new Set([
"https://coding-intl.dashscope.aliyuncs.com/v1",
"https://coding.dashscope.aliyuncs.com/v1",
"https://dashscope.aliyuncs.com/compatible-mode/v1",
"https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
]);
const log = createSubsystemLogger("agents/model-providers");
const ENV_VAR_NAME_RE = /^[A-Z_][A-Z0-9_]*$/;
const NATIVE_STREAMING_USAGE_COMPAT: Record<string, (provider: ProviderConfig) => ProviderConfig> =
{
moonshot: applyMoonshotNativeStreamingUsageCompat,
modelstudio: applyModelStudioNativeStreamingUsageCompat,
};
const PROVIDER_CONFIG_API_KEY_RESOLVERS: Partial<
Record<string, (env: NodeJS.ProcessEnv) => string | undefined>
> = {
"amazon-bedrock": resolveBedrockConfigApiKey,
"anthropic-vertex": resolveAnthropicVertexConfigApiKey,
};
const PROVIDER_IMPLICIT_MERGERS: Partial<
Record<
string,
(params: { existing: ProviderConfig | undefined; implicit: ProviderConfig }) => ProviderConfig
>
> = {
"amazon-bedrock": mergeImplicitBedrockProvider,
"anthropic-vertex": mergeImplicitAnthropicVertexProvider,
};
const CORE_IMPLICIT_PROVIDER_RESOLVERS = [
{
id: "amazon-bedrock",
resolve: (params: { config?: OpenClawConfig; env: NodeJS.ProcessEnv }) =>
resolveImplicitBedrockProvider({
config: params.config,
env: params.env,
}),
},
{
id: "anthropic-vertex",
resolve: (params: { config?: OpenClawConfig; env: NodeJS.ProcessEnv }) =>
resolveImplicitAnthropicVertexProvider({
env: params.env,
}),
},
] as const;
function resolveLiveProviderCatalogTimeoutMs(env: NodeJS.ProcessEnv): number | null {
const live =
env.OPENCLAW_LIVE_TEST === "1" || env.OPENCLAW_LIVE_GATEWAY === "1" || env.LIVE === "1";
@ -114,44 +158,6 @@ function normalizeApiKeyConfig(value: string): string {
return match?.[1] ?? trimmed;
}
function normalizeProviderBaseUrl(baseUrl: string | undefined): string {
const trimmed = baseUrl?.trim();
if (!trimmed) {
return "";
}
try {
const url = new URL(trimmed);
url.hash = "";
url.search = "";
return url.toString().replace(/\/+$/, "").toLowerCase();
} catch {
return trimmed.replace(/\/+$/, "").toLowerCase();
}
}
function withStreamingUsageCompat(provider: ProviderConfig): ProviderConfig {
if (!Array.isArray(provider.models) || provider.models.length === 0) {
return provider;
}
let changed = false;
const models = provider.models.map((model) => {
if (model.compat?.supportsUsageInStreaming !== undefined) {
return model;
}
changed = true;
return {
...model,
compat: {
...model.compat,
supportsUsageInStreaming: true,
},
};
});
return changed ? { ...provider, models } : provider;
}
export function applyNativeStreamingUsageCompat(
providers: Record<string, ProviderConfig>,
): Record<string, ProviderConfig> {
@ -159,13 +165,7 @@ export function applyNativeStreamingUsageCompat(
const nextProviders: Record<string, ProviderConfig> = {};
for (const [providerKey, provider] of Object.entries(providers)) {
const normalizedBaseUrl = normalizeProviderBaseUrl(provider.baseUrl);
const isNativeMoonshot =
providerKey === "moonshot" && MOONSHOT_NATIVE_BASE_URLS.has(normalizedBaseUrl);
const isNativeModelStudio =
providerKey === "modelstudio" && MODELSTUDIO_NATIVE_BASE_URLS.has(normalizedBaseUrl);
const nextProvider =
isNativeMoonshot || isNativeModelStudio ? withStreamingUsageCompat(provider) : provider;
const nextProvider = NATIVE_STREAMING_USAGE_COMPAT[providerKey]?.(provider) ?? provider;
nextProviders[providerKey] = nextProvider;
changed ||= nextProvider !== provider;
}
@ -309,44 +309,6 @@ function resolveApiKeyFromProfiles(params: {
return undefined;
}
const ANTIGRAVITY_BARE_PRO_IDS = new Set(["gemini-3-pro", "gemini-3.1-pro", "gemini-3-1-pro"]);
export function normalizeAntigravityModelId(id: string): string {
if (ANTIGRAVITY_BARE_PRO_IDS.has(id)) {
return `${id}-low`;
}
return id;
}
function normalizeProviderModels(
provider: ProviderConfig,
normalizeId: (id: string) => string,
): ProviderConfig {
let mutated = false;
const models = provider.models.map((model) => {
const nextId = normalizeId(model.id);
if (nextId === model.id) {
return model;
}
mutated = true;
return { ...model, id: nextId };
});
return mutated ? { ...provider, models } : provider;
}
function normalizeGoogleProvider(provider: ProviderConfig): ProviderConfig {
const modelNormalized = normalizeProviderModels(provider, normalizeGoogleModelId);
const normalizedBaseUrl = normalizeGoogleGenerativeAiBaseUrl(modelNormalized.baseUrl);
if (normalizedBaseUrl !== modelNormalized.baseUrl) {
return { ...modelNormalized, baseUrl: normalizedBaseUrl ?? modelNormalized.baseUrl };
}
return modelNormalized;
}
function normalizeAntigravityProvider(provider: ProviderConfig): ProviderConfig {
return normalizeProviderModels(provider, normalizeAntigravityModelId);
}
function normalizeSourceProviderLookup(
providers: ModelsConfig["providers"] | undefined,
): Record<string, ProviderConfig> {
@ -595,17 +557,18 @@ export function normalizeProviders(params: {
const normalizedApiKey = normalizeOptionalSecretInput(normalizedProvider.apiKey);
const hasConfiguredApiKey = Boolean(normalizedApiKey || normalizedProvider.apiKey);
if (hasModels && !hasConfiguredApiKey) {
const authMode =
normalizedProvider.auth ?? (normalizedKey === "amazon-bedrock" ? "aws-sdk" : undefined);
if (authMode === "aws-sdk") {
const authMode = normalizedProvider.auth;
const providerApiKeyResolver = PROVIDER_CONFIG_API_KEY_RESOLVERS[normalizedKey];
if (providerApiKeyResolver && (!authMode || authMode === "aws-sdk")) {
const apiKey = providerApiKeyResolver(env);
mutated = true;
normalizedProvider = { ...normalizedProvider, apiKey };
} else if (authMode === "aws-sdk") {
const apiKey = resolveAwsSdkApiKeyVarName(env);
mutated = true;
normalizedProvider = { ...normalizedProvider, apiKey };
} else {
const fromEnv =
normalizedKey === "anthropic-vertex"
? resolveEnvApiKey(normalizedKey, env)?.apiKey
: resolveEnvApiKeyVarName(normalizedKey, env);
const fromEnv = resolveEnvApiKeyVarName(normalizedKey, env);
const apiKey = fromEnv ?? profileApiKey?.apiKey;
if (apiKey?.trim()) {
if (profileApiKey && profileApiKey.source !== "plaintext") {
@ -617,20 +580,12 @@ export function normalizeProviders(params: {
}
}
if (shouldNormalizeGoogleGenerativeAiProviderConfig(normalizedKey, normalizedProvider)) {
const googleNormalized = normalizeGoogleProvider(normalizedProvider);
if (shouldNormalizeGoogleProviderConfig(normalizedKey, normalizedProvider)) {
const googleNormalized = normalizeGoogleProviderConfig(normalizedKey, normalizedProvider);
if (googleNormalized !== normalizedProvider) {
mutated = true;
normalizedProvider = googleNormalized;
}
normalizedProvider = googleNormalized;
}
if (normalizedKey === "google-antigravity") {
const antigravityNormalized = normalizeAntigravityProvider(normalizedProvider);
if (antigravityNormalized !== normalizedProvider) {
mutated = true;
}
normalizedProvider = antigravityNormalized;
}
const existing = next[normalizedKey];
@ -877,82 +832,21 @@ export async function resolveImplicitProviders(
mergeImplicitProviderSet(providers, await resolvePluginImplicitProviders(context, "paired"));
mergeImplicitProviderSet(providers, await resolvePluginImplicitProviders(context, "late"));
const implicitBedrock = await resolveImplicitBedrockProvider({
agentDir: params.agentDir,
config: params.config,
env,
});
if (implicitBedrock) {
const existing = providers["amazon-bedrock"];
providers["amazon-bedrock"] = existing
? {
...implicitBedrock,
...existing,
models:
Array.isArray(existing.models) && existing.models.length > 0
? existing.models
: implicitBedrock.models,
}
: implicitBedrock;
}
const implicitAnthropicVertex = resolveImplicitAnthropicVertexProvider({ env });
if (implicitAnthropicVertex) {
const existing = providers["anthropic-vertex"];
providers["anthropic-vertex"] = existing
? {
...implicitAnthropicVertex,
...existing,
models:
Array.isArray(existing.models) && existing.models.length > 0
? existing.models
: implicitAnthropicVertex.models,
}
: implicitAnthropicVertex;
for (const provider of CORE_IMPLICIT_PROVIDER_RESOLVERS) {
const implicit = await provider.resolve({ config: params.config, env });
if (!implicit) {
continue;
}
const merge = PROVIDER_IMPLICIT_MERGERS[provider.id];
if (!merge) {
providers[provider.id] = implicit;
continue;
}
providers[provider.id] = merge({
existing: providers[provider.id],
implicit,
});
}
return providers;
}
export function resolveImplicitAnthropicVertexProvider(params: {
env?: NodeJS.ProcessEnv;
}): ProviderConfig | null {
const env = params.env ?? process.env;
if (!hasAnthropicVertexAvailableAuth(env)) {
return null;
}
return buildAnthropicVertexProvider({ env });
}
export async function resolveImplicitBedrockProvider(params: {
agentDir: string;
config?: OpenClawConfig;
env?: NodeJS.ProcessEnv;
}): Promise<ProviderConfig | null> {
const env = params.env ?? process.env;
const discoveryConfig = params.config?.models?.bedrockDiscovery;
const enabled = discoveryConfig?.enabled;
const hasAwsCreds = resolveAwsSdkEnvVarName(env) !== undefined;
if (enabled === false) {
return null;
}
if (enabled !== true && !hasAwsCreds) {
return null;
}
const region = discoveryConfig?.region ?? env.AWS_REGION ?? env.AWS_DEFAULT_REGION ?? "us-east-1";
const models = await discoverBedrockModels({
region,
config: discoveryConfig,
});
if (models.length === 0) {
return null;
}
return {
baseUrl: `https://bedrock-runtime.${region}.amazonaws.com`,
api: "bedrock-converse-stream",
auth: "aws-sdk",
models,
} satisfies ProviderConfig;
}

View File

@ -3,10 +3,10 @@ import { writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { VERCEL_AI_GATEWAY_BASE_URL } from "../plugin-sdk/vercel-ai-gateway.js";
import { captureEnv } from "../test-utils/env.js";
import { NON_ENV_SECRETREF_MARKER } from "./model-auth-markers.js";
import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js";
import { VERCEL_AI_GATEWAY_BASE_URL } from "./vercel-ai-gateway.js";
describe("vercel-ai-gateway provider resolution", () => {
it("adds the provider with GPT-5.4 models when AI_GATEWAY_API_KEY is present", async () => {

View File

@ -1,4 +1,4 @@
import { isGoogleGenerativeAiApi } from "../google-generative-ai.js";
import { isGoogleGenerativeAiApi } from "../../plugin-sdk/google.js";
import { sanitizeGoogleTurnOrdering } from "./bootstrap.js";
export function isGoogleModelApi(api?: string | null): boolean {

View File

@ -2,6 +2,7 @@ import type { Api, Model } from "@mariozechner/pi-ai";
import type { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
import type { OpenClawConfig } from "../../config/config.js";
import type { ModelDefinitionConfig } from "../../config/types.js";
import { resolveGoogleGenerativeAiTransport } from "../../plugin-sdk/google.js";
import {
buildProviderUnknownModelHintWithPlugin,
clearProviderRuntimeHookCache,
@ -11,7 +12,6 @@ import {
} from "../../plugins/provider-runtime.js";
import { resolveOpenClawAgentDir } from "../agent-paths.js";
import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js";
import { resolveGoogleGenerativeAiTransport } from "../google-generative-ai.js";
import { buildModelAliasLines } from "../model-alias-lines.js";
import { isSecretRefHeaderValueMarker } from "../model-auth-markers.js";
import { normalizeModelCompat } from "../model-compat.js";

View File

@ -1,6 +1,6 @@
import type { RuntimeVersionEnv } from "../version.js";
import { resolveRuntimeServiceVersion } from "../version.js";
import { normalizeProviderId } from "./model-selection.js";
import { normalizeProviderId } from "./provider-id.js";
export type ProviderAttributionVerification =
| "vendor-documented"

View File

@ -1,6 +1,6 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolveProviderCapabilitiesWithPlugin as resolveProviderCapabilitiesWithPluginRuntime } from "../plugins/provider-runtime.js";
import { normalizeProviderId } from "./model-selection.js";
import { normalizeProviderId } from "./provider-id.js";
export type ProviderCapabilities = {
anthropicToolSchemaMode: "native" | "openai-functions";

View File

@ -1,7 +0,0 @@
// Deprecated compat shim. Prefer openclaw/plugin-sdk/sglang.
export {
SGLANG_DEFAULT_API_KEY_ENV_VAR,
SGLANG_DEFAULT_BASE_URL,
SGLANG_MODEL_PLACEHOLDER,
SGLANG_PROVIDER_LABEL,
} from "../plugin-sdk/sglang.js";

View File

@ -3,9 +3,9 @@
* This bypasses pi-ai's content type system which does not have a "document" type.
*/
import { resolveGoogleGenerativeAiApiOrigin } from "../../plugin-sdk/google.js";
import { isRecord } from "../../utils.js";
import { normalizeSecretInput } from "../../utils/normalize-secret-input.js";
import { resolveGoogleGenerativeAiApiOrigin } from "../google-generative-ai.js";
type PdfInput = {
base64: string;

View File

@ -1,12 +0,0 @@
// Deprecated compat shim. Prefer openclaw/plugin-sdk/vercel-ai-gateway.
export {
discoverVercelAiGatewayModels,
getStaticVercelAiGatewayModelCatalog,
VERCEL_AI_GATEWAY_BASE_URL,
VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW,
VERCEL_AI_GATEWAY_DEFAULT_COST,
VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS,
VERCEL_AI_GATEWAY_DEFAULT_MODEL_ID,
VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF,
VERCEL_AI_GATEWAY_PROVIDER_ID,
} from "../plugin-sdk/vercel-ai-gateway.js";

View File

@ -1,7 +0,0 @@
// Deprecated compat shim. Prefer openclaw/plugin-sdk/vllm.
export {
VLLM_DEFAULT_API_KEY_ENV_VAR,
VLLM_DEFAULT_BASE_URL,
VLLM_MODEL_PLACEHOLDER,
VLLM_PROVIDER_LABEL,
} from "../plugin-sdk/vllm.js";

View File

@ -1,10 +1,10 @@
import {
isDiscordExecApprovalApprover,
isDiscordExecApprovalClientEnabled,
} from "../../../extensions/discord/api.js";
import { callGateway } from "../../gateway/call.js";
import { ErrorCodes } from "../../gateway/protocol/index.js";
import { logVerbose } from "../../globals.js";
import {
isDiscordExecApprovalApprover,
isDiscordExecApprovalClientEnabled,
} from "../../plugin-sdk/discord-surface.js";
import {
isTelegramExecApprovalApprover,
isTelegramExecApprovalClientEnabled,

View File

@ -21,7 +21,6 @@ import { isModelNotFoundErrorMessage } from "../agents/live-model-errors.js";
import { isHighSignalLiveModelRef } from "../agents/live-model-filter.js";
import { isLiveProfileKeyModeEnabled, isLiveTestEnabled } from "../agents/live-test-helpers.js";
import { getApiKeyForModel } from "../agents/model-auth.js";
import { normalizeGoogleModelId } from "../agents/model-id-normalization.js";
import { shouldSuppressBuiltInModel } from "../agents/model-suppression.js";
import { ensureOpenClawModelsJson } from "../agents/models-config.js";
import { isRateLimitErrorMessage } from "../agents/pi-embedded-helpers/errors.js";
@ -29,6 +28,7 @@ import { discoverAuthStorage, discoverModels } from "../agents/pi-model-discover
import { clearRuntimeConfigSnapshot, loadConfig } from "../config/config.js";
import type { ModelsConfig, OpenClawConfig, ModelProviderConfig } from "../config/types.js";
import { isTruthyEnvValue } from "../infra/env.js";
import { normalizeGoogleModelId } from "../plugin-sdk/google.js";
import { DEFAULT_AGENT_ID } from "../routing/session-key.js";
import { stripAssistantInternalScaffolding } from "../shared/text/assistant-visible-text.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";

View File

@ -7,9 +7,10 @@ import {
resolveModelRefFromString,
type ModelRef,
} from "../agents/model-selection.js";
import { normalizeGoogleModelId, normalizeXaiModelId } from "../agents/models-config.providers.js";
import type { OpenClawConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { normalizeGoogleModelId } from "../plugin-sdk/google.js";
import { normalizeXaiModelId } from "../plugin-sdk/xai.js";
export type CachedModelPricing = {
input: number;

View File

@ -0,0 +1,62 @@
export type { OpenClawConfig } from "../config/config.js";
export { createAccountActionGate } from "../channels/plugins/account-action-gate.js";
export {
createAccountListHelpers,
describeAccountSnapshot,
listCombinedAccountIds,
mergeAccountConfig,
resolveMergedAccountConfig,
} from "../channels/plugins/account-helpers.js";
export { normalizeChatType } from "../channels/chat-type.js";
export { resolveAccountEntry, resolveNormalizedAccountEntry } from "../routing/account-lookup.js";
export {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
normalizeOptionalAccountId,
} from "../routing/session-key.js";
export { normalizeE164, pathExists, resolveUserPath } from "../utils.js";
/** Resolve an account by id, then fall back to the default account when the primary lacks credentials. */
export function resolveAccountWithDefaultFallback<TAccount>(params: {
accountId?: string | null;
normalizeAccountId: (accountId?: string | null) => string;
resolvePrimary: (accountId: string) => TAccount;
hasCredential: (account: TAccount) => boolean;
resolveDefaultAccountId: () => string;
}): TAccount {
const hasExplicitAccountId = Boolean(params.accountId?.trim());
const normalizedAccountId = params.normalizeAccountId(params.accountId);
const primary = params.resolvePrimary(normalizedAccountId);
if (hasExplicitAccountId || params.hasCredential(primary)) {
return primary;
}
const fallbackId = params.resolveDefaultAccountId();
if (fallbackId === normalizedAccountId) {
return primary;
}
const fallback = params.resolvePrimary(fallbackId);
if (!params.hasCredential(fallback)) {
return primary;
}
return fallback;
}
/** List normalized configured account ids from a raw channel account record map. */
export function listConfiguredAccountIds(params: {
accounts: Record<string, unknown> | undefined;
normalizeAccountId: (accountId: string) => string;
}): string[] {
if (!params.accounts) {
return [];
}
const ids = new Set<string>();
for (const key of Object.keys(params.accounts)) {
if (!key) {
continue;
}
ids.add(params.normalizeAccountId(key));
}
return [...ids];
}

View File

@ -0,0 +1,8 @@
// Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually.
export {
discoverBedrockModels,
mergeImplicitBedrockProvider,
resetBedrockDiscoveryCacheForTest,
resolveBedrockConfigApiKey,
resolveImplicitBedrockProvider,
} from "../../extensions/amazon-bedrock/api.js";

View File

@ -2,5 +2,13 @@
export {
ANTHROPIC_VERTEX_DEFAULT_MODEL_ID,
buildAnthropicVertexProvider,
hasAnthropicVertexAvailableAuth,
hasAnthropicVertexCredentials,
mergeImplicitAnthropicVertexProvider,
resolveAnthropicVertexClientRegion,
resolveAnthropicVertexConfigApiKey,
resolveImplicitAnthropicVertexProvider,
resolveAnthropicVertexProjectId,
resolveAnthropicVertexRegion,
resolveAnthropicVertexRegionFromBaseUrl,
} from "../../extensions/anthropic-vertex/api.js";

View File

@ -139,6 +139,7 @@ export type { SecretFileReadOptions, SecretFileReadResult } from "../infra/secre
export { resolveGatewayBindUrl } from "../shared/gateway-bind-url.js";
export type { GatewayBindUrlResult } from "../shared/gateway-bind-url.js";
export { resolveGatewayPort } from "../config/paths.js";
export { createSubsystemLogger } from "../logging/subsystem.js";
export { normalizeAtHashSlug, normalizeHyphenSlug } from "../shared/string-normalization.js";
export { resolveTailnetHostWithRunner } from "../shared/tailscale-status.js";

View File

@ -5,6 +5,8 @@ export {
createDiscordActionGate,
handleDiscordMessageAction,
inspectDiscordAccount,
isDiscordExecApprovalApprover,
isDiscordExecApprovalClientEnabled,
listDiscordAccountIds,
listDiscordDirectoryGroupsFromConfig,
listDiscordDirectoryPeersFromConfig,
@ -15,14 +17,17 @@ export {
resolveDefaultDiscordAccountId,
resolveDiscordAccount,
resolveDiscordChannelId,
resolveDiscordRuntimeGroupPolicy,
resolveDiscordGroupRequireMention,
resolveDiscordGroupToolPolicy,
} from "../../extensions/discord/api.js";
export type {
DiscordComponentMessageSpec,
DiscordProbe,
DiscordSendComponents,
DiscordSendEmbeds,
DiscordSendResult,
DiscordTokenResolution,
InspectedDiscordAccount,
ResolvedDiscordAccount,
} from "../../extensions/discord/api.js";

View File

@ -2,6 +2,8 @@
export {
buildFeishuConversationId,
createFeishuThreadBindingManager,
feishuSessionBindingAdapterChannels,
feishuThreadBindingTesting,
parseFeishuDirectConversationId,
parseFeishuConversationId,
parseFeishuTargetId,

View File

@ -0,0 +1,4 @@
export {
normalizeAntigravityModelId,
normalizeGoogleModelId,
} from "../../extensions/google/model-id.js";

View File

@ -1,11 +1,17 @@
// Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually.
export {
applyGoogleGeminiModelDefault,
createGoogleThinkingPayloadWrapper,
DEFAULT_GOOGLE_API_BASE_URL,
GOOGLE_GEMINI_DEFAULT_MODEL,
isGoogleGenerativeAiApi,
normalizeAntigravityModelId,
normalizeGoogleApiBaseUrl,
normalizeGoogleGenerativeAiBaseUrl,
normalizeGoogleModelId,
normalizeGoogleProviderConfig,
parseGeminiAuth,
sanitizeGoogleThinkingPayload,
resolveGoogleGenerativeAiApiOrigin,
resolveGoogleGenerativeAiTransport,
shouldNormalizeGoogleProviderConfig,
shouldNormalizeGoogleGenerativeAiProviderConfig,
} from "../../extensions/google/api.js";

View File

@ -15,7 +15,6 @@ export type { OpenClawConfig } from "../config/config.js";
export { describeFailoverError, isFailoverError } from "../agents/failover-error.js";
export { resolveApiKeyForProvider } from "../agents/model-auth.js";
export { normalizeGoogleModelId } from "../agents/model-id-normalization.js";
export {
resolveAgentModelFallbackValues,
resolveAgentModelPrimaryValue,
@ -27,5 +26,6 @@ export {
} from "../image-generation/provider-registry.js";
export { parseImageGenerationModelRef } from "../image-generation/model-ref.js";
export { createSubsystemLogger } from "../logging/subsystem.js";
export { normalizeGoogleModelId } from "./google.js";
export { OPENAI_DEFAULT_IMAGE_MODEL } from "./openai.js";
export { getProviderEnvVars } from "../secrets/provider-env-vars.js";

View File

@ -1,6 +1,7 @@
// Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually.
export {
normalizeIMessageHandle,
resolveIMessageRuntimeGroupPolicy,
resolveIMessageGroupRequireMention,
resolveIMessageGroupToolPolicy,
} from "../../extensions/imessage/api.js";

View File

@ -4,3 +4,4 @@ export {
probeIMessage,
sendMessageIMessage,
} from "../../extensions/imessage/runtime-api.js";
export type { IMessageProbe } from "../../extensions/imessage/runtime-api.js";

View File

@ -1,4 +1,5 @@
export type { IMessageAccountConfig } from "../config/types.js";
export type { IMessageProbe } from "./imessage-runtime.js";
export type { OpenClawConfig } from "../config/config.js";
export type {
ChannelMessageActionContext,

Some files were not shown because too many files have changed in this diff Show More