mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-30 13:00:21 +00:00
refactor: move remaining provider policy into plugins
This commit is contained in:
@@ -41,6 +41,10 @@ export default definePluginEntry({
|
||||
},
|
||||
},
|
||||
resolveConfigApiKey: ({ env }) => resolveBedrockConfigApiKey(env),
|
||||
capabilities: {
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
},
|
||||
wrapStreamFn: ({ modelId, streamFn }) =>
|
||||
isAnthropicBedrockModel(modelId) ? streamFn : createBedrockNoCacheWrapper(streamFn),
|
||||
resolveDefaultThinkingLevel: ({ modelId }) =>
|
||||
|
||||
59
extensions/anthropic-vertex/index.test.ts
Normal file
59
extensions/anthropic-vertex/index.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
|
||||
import anthropicVertexPlugin from "./index.js";
|
||||
|
||||
describe("anthropic-vertex provider plugin", () => {
|
||||
it("resolves the ADC marker through the provider hook", () => {
|
||||
const provider = registerSingleProviderPlugin(anthropicVertexPlugin);
|
||||
|
||||
expect(
|
||||
provider.resolveConfigApiKey?.({
|
||||
provider: "anthropic-vertex",
|
||||
env: {
|
||||
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
|
||||
} as NodeJS.ProcessEnv,
|
||||
} as never),
|
||||
).toBe("gcp-vertex-credentials");
|
||||
});
|
||||
|
||||
it("merges the implicit Vertex catalog into explicit provider overrides", async () => {
|
||||
const provider = registerSingleProviderPlugin(anthropicVertexPlugin);
|
||||
|
||||
const result = await provider.catalog?.run({
|
||||
config: {
|
||||
models: {
|
||||
providers: {
|
||||
"anthropic-vertex": {
|
||||
baseUrl: "https://europe-west4-aiplatform.googleapis.com",
|
||||
headers: { "x-test-header": "1" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
env: {
|
||||
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
|
||||
GOOGLE_CLOUD_LOCATION: "us-east5",
|
||||
} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
mode: "none",
|
||||
source: "none",
|
||||
}),
|
||||
} as never);
|
||||
|
||||
expect(result).toEqual({
|
||||
provider: {
|
||||
api: "anthropic-messages",
|
||||
apiKey: "gcp-vertex-credentials",
|
||||
baseUrl: "https://europe-west4-aiplatform.googleapis.com",
|
||||
headers: { "x-test-header": "1" },
|
||||
models: [
|
||||
expect.objectContaining({ id: "claude-opus-4-6" }),
|
||||
expect.objectContaining({ id: "claude-sonnet-4-6" }),
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
44
extensions/anthropic-vertex/index.ts
Normal file
44
extensions/anthropic-vertex/index.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import {
|
||||
mergeImplicitAnthropicVertexProvider,
|
||||
resolveAnthropicVertexConfigApiKey,
|
||||
resolveImplicitAnthropicVertexProvider,
|
||||
} from "./api.js";
|
||||
|
||||
const PROVIDER_ID = "anthropic-vertex";
|
||||
|
||||
export default definePluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Anthropic Vertex Provider",
|
||||
description: "Bundled Anthropic Vertex provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Anthropic Vertex",
|
||||
docsPath: "/providers/models",
|
||||
auth: [],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: async (ctx) => {
|
||||
const implicit = await resolveImplicitAnthropicVertexProvider({
|
||||
env: ctx.env,
|
||||
});
|
||||
if (!implicit) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
provider: mergeImplicitAnthropicVertexProvider({
|
||||
existing: ctx.config.models?.providers?.[PROVIDER_ID],
|
||||
implicit,
|
||||
}),
|
||||
};
|
||||
},
|
||||
},
|
||||
resolveConfigApiKey: ({ env }) => resolveAnthropicVertexConfigApiKey(env),
|
||||
capabilities: {
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
10
extensions/anthropic-vertex/openclaw.plugin.json
Normal file
10
extensions/anthropic-vertex/openclaw.plugin.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "anthropic-vertex",
|
||||
"enabledByDefault": true,
|
||||
"providers": ["anthropic-vertex"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
12
extensions/anthropic-vertex/package.json
Normal file
12
extensions/anthropic-vertex/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic Vertex provider plugin",
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
3
extensions/anthropic-vertex/provider.contract.test.ts
Normal file
3
extensions/anthropic-vertex/provider.contract.test.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js";
|
||||
|
||||
describeProviderContracts("anthropic-vertex");
|
||||
@@ -1,9 +1,5 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import {
|
||||
mergeImplicitAnthropicVertexProvider,
|
||||
resolveImplicitAnthropicVertexProvider,
|
||||
} from "../plugin-sdk/anthropic-vertex.js";
|
||||
import {
|
||||
groupPluginDiscoveryProvidersByOrder,
|
||||
normalizePluginDiscoveryResult,
|
||||
@@ -34,20 +30,9 @@ const PROVIDER_IMPLICIT_MERGERS: Partial<
|
||||
(params: { existing: ProviderConfig | undefined; implicit: ProviderConfig }) => ProviderConfig
|
||||
>
|
||||
> = {
|
||||
"anthropic-vertex": mergeImplicitAnthropicVertexProvider,
|
||||
ollama: ({ implicit }) => implicit,
|
||||
};
|
||||
|
||||
const CORE_IMPLICIT_PROVIDER_RESOLVERS = [
|
||||
{
|
||||
id: "anthropic-vertex",
|
||||
resolve: async (params: { config?: OpenClawConfig; env: NodeJS.ProcessEnv }) =>
|
||||
resolveImplicitAnthropicVertexProvider({
|
||||
env: params.env,
|
||||
}),
|
||||
},
|
||||
] as const;
|
||||
|
||||
const PLUGIN_DISCOVERY_ORDERS = ["simple", "profile", "paired", "late"] as const;
|
||||
|
||||
type ImplicitProviderParams = {
|
||||
@@ -309,31 +294,6 @@ async function runProviderCatalogWithTimeout(
|
||||
}
|
||||
}
|
||||
|
||||
async function mergeCoreImplicitProviders(params: {
|
||||
config?: OpenClawConfig;
|
||||
explicitProviders?: Record<string, ProviderConfig> | null;
|
||||
env: NodeJS.ProcessEnv;
|
||||
providers: Record<string, ProviderConfig>;
|
||||
}): Promise<void> {
|
||||
for (const provider of CORE_IMPLICIT_PROVIDER_RESOLVERS) {
|
||||
const implicit = await provider.resolve({ config: params.config, env: params.env });
|
||||
if (!implicit) {
|
||||
continue;
|
||||
}
|
||||
const merge = PROVIDER_IMPLICIT_MERGERS[provider.id];
|
||||
params.providers[provider.id] = (merge ?? mergeImplicitProviderConfig)({
|
||||
providerId: provider.id,
|
||||
existing:
|
||||
params.providers[provider.id] ??
|
||||
resolveConfiguredImplicitProvider({
|
||||
configuredProviders: params.explicitProviders ?? params.config?.models?.providers,
|
||||
providerIds: [provider.id],
|
||||
}),
|
||||
implicit,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveImplicitProviders(
|
||||
params: ImplicitProviderParams,
|
||||
): Promise<NonNullable<OpenClawConfig["models"]>["providers"]> {
|
||||
@@ -354,12 +314,5 @@ export async function resolveImplicitProviders(
|
||||
mergeImplicitProviderSet(providers, await resolvePluginImplicitProviders(context, order));
|
||||
}
|
||||
|
||||
await mergeCoreImplicitProviders({
|
||||
config: params.config,
|
||||
explicitProviders: params.explicitProviders,
|
||||
env,
|
||||
providers,
|
||||
});
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("models-config.providers.policy", () => {
|
||||
it("resolves config apiKey markers through the local bedrock helper", async () => {
|
||||
it("resolves config apiKey markers through provider plugin hooks", async () => {
|
||||
const { resolveProviderConfigApiKeyResolver } =
|
||||
await import("./models-config.providers.policy.js");
|
||||
const env = {
|
||||
@@ -12,4 +12,32 @@ describe("models-config.providers.policy", () => {
|
||||
expect(resolver).toBeTypeOf("function");
|
||||
expect(resolver?.(env)).toBe("AWS_PROFILE");
|
||||
});
|
||||
|
||||
it("resolves anthropic-vertex ADC markers through provider plugin hooks", async () => {
|
||||
const { resolveProviderConfigApiKeyResolver } =
|
||||
await import("./models-config.providers.policy.js");
|
||||
const resolver = resolveProviderConfigApiKeyResolver("anthropic-vertex");
|
||||
|
||||
expect(resolver).toBeTypeOf("function");
|
||||
expect(
|
||||
resolver?.({
|
||||
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
|
||||
} as NodeJS.ProcessEnv),
|
||||
).toBe("gcp-vertex-credentials");
|
||||
});
|
||||
|
||||
it("normalizes Google provider config through provider plugin hooks", async () => {
|
||||
const { normalizeProviderSpecificConfig } = await import("./models-config.providers.policy.js");
|
||||
|
||||
expect(
|
||||
normalizeProviderSpecificConfig("google", {
|
||||
api: "google-generative-ai",
|
||||
baseUrl: "https://generativelanguage.googleapis.com",
|
||||
models: [],
|
||||
}),
|
||||
).toMatchObject({
|
||||
api: "google-generative-ai",
|
||||
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
import { resolveBedrockConfigApiKey } from "../plugin-sdk/amazon-bedrock.js";
|
||||
import { resolveAnthropicVertexConfigApiKey } from "../plugin-sdk/anthropic-vertex.js";
|
||||
import { normalizeGoogleProviderConfig } from "../plugin-sdk/google.js";
|
||||
import { applyModelStudioNativeStreamingUsageCompat } from "../plugin-sdk/modelstudio.js";
|
||||
import { applyMoonshotNativeStreamingUsageCompat } from "../plugin-sdk/moonshot.js";
|
||||
import {
|
||||
applyProviderNativeStreamingUsageCompatWithPlugin,
|
||||
normalizeProviderConfigWithPlugin,
|
||||
resolveProviderConfigApiKeyWithPlugin,
|
||||
resolveProviderRuntimePlugin,
|
||||
} from "../plugins/provider-runtime.js";
|
||||
import type { ProviderConfig } from "./models-config.providers.secrets.js";
|
||||
|
||||
const PROVIDER_CONFIG_API_KEY_RESOLVERS: Partial<
|
||||
Record<string, (env: NodeJS.ProcessEnv) => string | undefined>
|
||||
> = {
|
||||
"amazon-bedrock": resolveBedrockConfigApiKey,
|
||||
"anthropic-vertex": resolveAnthropicVertexConfigApiKey,
|
||||
};
|
||||
|
||||
function shouldNormalizeGoogleProviderConfigLocally(providerKey: string): boolean {
|
||||
return (
|
||||
providerKey === "google" ||
|
||||
providerKey === "google-antigravity" ||
|
||||
providerKey === "google-vertex"
|
||||
);
|
||||
}
|
||||
|
||||
export function applyNativeStreamingUsageCompat(
|
||||
providers: Record<string, ProviderConfig>,
|
||||
): Record<string, ProviderConfig> {
|
||||
@@ -28,11 +14,13 @@ export function applyNativeStreamingUsageCompat(
|
||||
|
||||
for (const [providerKey, provider] of Object.entries(providers)) {
|
||||
const nextProvider =
|
||||
providerKey === "modelstudio"
|
||||
? applyModelStudioNativeStreamingUsageCompat(provider)
|
||||
: providerKey === "moonshot"
|
||||
? applyMoonshotNativeStreamingUsageCompat(provider)
|
||||
: provider;
|
||||
applyProviderNativeStreamingUsageCompatWithPlugin({
|
||||
provider: providerKey,
|
||||
context: {
|
||||
provider: providerKey,
|
||||
providerConfig: provider,
|
||||
},
|
||||
}) ?? provider;
|
||||
nextProviders[providerKey] = nextProvider;
|
||||
changed ||= nextProvider !== provider;
|
||||
}
|
||||
@@ -44,15 +32,32 @@ export function normalizeProviderSpecificConfig(
|
||||
providerKey: string,
|
||||
provider: ProviderConfig,
|
||||
): ProviderConfig {
|
||||
if (shouldNormalizeGoogleProviderConfigLocally(providerKey)) {
|
||||
return normalizeGoogleProviderConfig(providerKey, provider);
|
||||
}
|
||||
return provider;
|
||||
return (
|
||||
normalizeProviderConfigWithPlugin({
|
||||
provider: providerKey,
|
||||
context: {
|
||||
provider: providerKey,
|
||||
providerConfig: provider,
|
||||
},
|
||||
}) ?? provider
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveProviderConfigApiKeyResolver(
|
||||
providerKey: string,
|
||||
): ((env: NodeJS.ProcessEnv) => string | undefined) | undefined {
|
||||
const fallback = PROVIDER_CONFIG_API_KEY_RESOLVERS[providerKey];
|
||||
return fallback;
|
||||
if (!resolveProviderRuntimePlugin({ provider: providerKey })?.resolveConfigApiKey) {
|
||||
return undefined;
|
||||
}
|
||||
return (env) => {
|
||||
const resolved = resolveProviderConfigApiKeyWithPlugin({
|
||||
provider: providerKey,
|
||||
env,
|
||||
context: {
|
||||
provider: providerKey,
|
||||
env,
|
||||
},
|
||||
});
|
||||
return resolved?.trim() || undefined;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,16 @@ const resolveProviderCapabilitiesWithPluginMock = vi.fn((params: { provider: str
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
};
|
||||
case "anthropic-vertex":
|
||||
return {
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
};
|
||||
case "amazon-bedrock":
|
||||
return {
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
};
|
||||
case "openai":
|
||||
return {
|
||||
providerFamily: "openai",
|
||||
|
||||
@@ -36,17 +36,6 @@ const DEFAULT_PROVIDER_CAPABILITIES: ProviderCapabilities = {
|
||||
dropThinkingBlockModelHints: [],
|
||||
};
|
||||
|
||||
const CORE_PROVIDER_CAPABILITIES: Record<string, Partial<ProviderCapabilities>> = {
|
||||
"anthropic-vertex": {
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
},
|
||||
"amazon-bedrock": {
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
},
|
||||
};
|
||||
|
||||
const PLUGIN_CAPABILITIES_FALLBACKS: Record<string, Partial<ProviderCapabilities>> = {
|
||||
anthropic: {
|
||||
providerFamily: "anthropic",
|
||||
@@ -118,7 +107,6 @@ export function resolveProviderCapabilities(
|
||||
: undefined;
|
||||
return {
|
||||
...DEFAULT_PROVIDER_CAPABILITIES,
|
||||
...CORE_PROVIDER_CAPABILITIES[normalized],
|
||||
...PLUGIN_CAPABILITIES_FALLBACKS[normalized],
|
||||
...pluginCapabilities,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user