mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-30 19:32:27 +00:00
refactor: move provider seams behind plugin sdk surfaces
This commit is contained in:
@@ -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"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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"}
|
||||
|
||||
7
extensions/amazon-bedrock/api.ts
Normal file
7
extensions/amazon-bedrock/api.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export {
|
||||
discoverBedrockModels,
|
||||
mergeImplicitBedrockProvider,
|
||||
resetBedrockDiscoveryCacheForTest,
|
||||
resolveBedrockConfigApiKey,
|
||||
resolveImplicitBedrockProvider,
|
||||
} from "./discovery.js";
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -653,7 +653,6 @@ describe("monitorDiscordProvider", () => {
|
||||
retry_after: 193.632,
|
||||
global: false,
|
||||
},
|
||||
request,
|
||||
);
|
||||
rateLimitError.discordCode = 30034;
|
||||
clientHandleDeployRequestMock.mockRejectedValueOnce(rateLimitError);
|
||||
|
||||
@@ -1183,3 +1183,5 @@ export const __testing = {
|
||||
shouldLogVerboseForTesting = mock;
|
||||
},
|
||||
};
|
||||
|
||||
export const resolveDiscordRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy;
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
27
extensions/google/model-id.ts
Normal file
27
extensions/google/model-id.ts
Normal 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;
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -536,3 +536,5 @@ export const __testing = {
|
||||
resolveIMessageRuntimeGroupPolicy: resolveOpenProviderRuntimeGroupPolicy,
|
||||
resolveDefaultGroupPolicy,
|
||||
};
|
||||
|
||||
export const resolveIMessageRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export {
|
||||
applyModelStudioNativeStreamingUsageCompat,
|
||||
buildModelStudioDefaultModelDefinition,
|
||||
buildModelStudioModelDefinition,
|
||||
isNativeModelStudioBaseUrl,
|
||||
MODELSTUDIO_BASE_URL,
|
||||
MODELSTUDIO_CN_BASE_URL,
|
||||
MODELSTUDIO_DEFAULT_COST,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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]>({
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -4,3 +4,4 @@ export {
|
||||
SGLANG_MODEL_PLACEHOLDER,
|
||||
SGLANG_PROVIDER_LABEL,
|
||||
} from "./defaults.js";
|
||||
export { buildSglangProvider } from "./models.js";
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
23
extensions/sglang/models.ts
Normal file
23
extensions/sglang/models.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -85,6 +85,7 @@ export {
|
||||
export {
|
||||
createTelegramThreadBindingManager,
|
||||
getTelegramThreadBindingManager,
|
||||
resetTelegramThreadBindingsForTests,
|
||||
setTelegramThreadBindingIdleTimeoutBySessionKey,
|
||||
setTelegramThreadBindingMaxAgeBySessionKey,
|
||||
} from "./src/thread-bindings.js";
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -4,3 +4,4 @@ export {
|
||||
VLLM_MODEL_PLACEHOLDER,
|
||||
VLLM_PROVIDER_LABEL,
|
||||
} from "./defaults.js";
|
||||
export { buildVllmProvider } from "./models.js";
|
||||
|
||||
@@ -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
23
extensions/vllm/models.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
19
extensions/whatsapp/src/creds-files.ts
Normal file
19
extensions/whatsapp/src/creds-files.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
21
extensions/xai/model-id.ts
Normal file
21
extensions/xai/model-id.ts
Normal 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;
|
||||
}
|
||||
20
package.json
20
package.json
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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"]>;
|
||||
|
||||
|
||||
@@ -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"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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";
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
62
src/plugin-sdk/account-core.ts
Normal file
62
src/plugin-sdk/account-core.ts
Normal 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];
|
||||
}
|
||||
8
src/plugin-sdk/amazon-bedrock.ts
Normal file
8
src/plugin-sdk/amazon-bedrock.ts
Normal 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";
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
export {
|
||||
buildFeishuConversationId,
|
||||
createFeishuThreadBindingManager,
|
||||
feishuSessionBindingAdapterChannels,
|
||||
feishuThreadBindingTesting,
|
||||
parseFeishuDirectConversationId,
|
||||
parseFeishuConversationId,
|
||||
parseFeishuTargetId,
|
||||
|
||||
4
src/plugin-sdk/google-model-id.ts
Normal file
4
src/plugin-sdk/google-model-id.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export {
|
||||
normalizeAntigravityModelId,
|
||||
normalizeGoogleModelId,
|
||||
} from "../../extensions/google/model-id.js";
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -4,3 +4,4 @@ export {
|
||||
probeIMessage,
|
||||
sendMessageIMessage,
|
||||
} from "../../extensions/imessage/runtime-api.js";
|
||||
export type { IMessageProbe } from "../../extensions/imessage/runtime-api.js";
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user