perf: split provider metadata from runtime loading

This commit is contained in:
Peter Steinberger
2026-03-22 21:41:07 +00:00
parent abd948f2b7
commit 4235fb16d6
22 changed files with 106 additions and 97 deletions

View File

@@ -6,7 +6,7 @@ import {
} from "../provider-runtime.test-support.js";
import { requireProviderContractProvider } from "./registry.js";
type ResolvePluginProviders = typeof import("../providers.js").resolvePluginProviders;
type ResolvePluginProviders = typeof import("../providers.runtime.js").resolvePluginProviders;
type ResolveOwningPluginIdsForProvider =
typeof import("../providers.js").resolveOwningPluginIdsForProvider;
type ResolveNonBundledProviderPluginIds =
@@ -23,13 +23,16 @@ const resolveNonBundledProviderPluginIdsMock = vi.hoisted(() =>
);
vi.mock("../providers.js", () => ({
resolvePluginProviders: (params: unknown) => resolvePluginProvidersMock(params as never),
resolveOwningPluginIdsForProvider: (params: unknown) =>
resolveOwningPluginIdsForProviderMock(params as never),
resolveNonBundledProviderPluginIds: (params: unknown) =>
resolveNonBundledProviderPluginIdsMock(params as never),
}));
vi.mock("../providers.runtime.js", () => ({
resolvePluginProviders: (params: unknown) => resolvePluginProvidersMock(params as never),
}));
let augmentModelCatalogWithProviderPlugins: typeof import("../provider-runtime.js").augmentModelCatalogWithProviderPlugins;
let resetProviderRuntimeHookCacheForTest: typeof import("../provider-runtime.js").resetProviderRuntimeHookCacheForTest;
let resolveProviderBuiltInModelSuppression: typeof import("../provider-runtime.js").resolveProviderBuiltInModelSuppression;

View File

@@ -36,7 +36,7 @@ import xiaomiPlugin from "../../../extensions/xiaomi/index.js";
import zaiPlugin from "../../../extensions/zai/index.js";
import { bundledWebSearchPluginRegistrations } from "../../bundled-web-search-registry.js";
import { createCapturedPluginRegistration } from "../captured-registration.js";
import { resolvePluginProviders } from "../providers.js";
import { resolvePluginProviders } from "../providers.runtime.js";
import type {
ImageGenerationProviderPlugin,
MediaUnderstandingProviderPlugin,

View File

@@ -9,7 +9,7 @@ import type { ProviderAuthMethod, ProviderPlugin } from "../types.js";
const resolvePluginProvidersMock = vi.fn();
vi.mock("../providers.js", () => ({
vi.mock("../providers.runtime.js", () => ({
resolvePluginProviders: (...args: unknown[]) => resolvePluginProvidersMock(...args),
}));

View File

@@ -2,13 +2,13 @@ import {
resolveProviderPluginChoice as resolveProviderPluginChoiceImpl,
runProviderModelSelectedHook as runProviderModelSelectedHookImpl,
} from "./provider-wizard.js";
import { resolvePluginProviders as resolvePluginProvidersImpl } from "./providers.js";
import { resolvePluginProviders as resolvePluginProvidersImpl } from "./providers.runtime.js";
type ResolveProviderPluginChoice =
typeof import("./provider-wizard.js").resolveProviderPluginChoice;
type RunProviderModelSelectedHook =
typeof import("./provider-wizard.js").runProviderModelSelectedHook;
type ResolvePluginProviders = typeof import("./providers.js").resolvePluginProviders;
type ResolvePluginProviders = typeof import("./providers.runtime.js").resolvePluginProviders;
export function resolveProviderPluginChoice(
...args: Parameters<ResolveProviderPluginChoice>

View File

@@ -1,7 +1,7 @@
import { normalizeProviderId } from "../agents/model-selection.js";
import type { OpenClawConfig } from "../config/config.js";
import type { ModelProviderConfig } from "../config/types.js";
import { resolvePluginProviders } from "./providers.js";
import { resolvePluginProviders } from "./providers.runtime.js";
import type { ProviderDiscoveryOrder, ProviderPlugin } from "./types.js";
const DISCOVERY_ORDER: readonly ProviderDiscoveryOrder[] = ["simple", "profile", "paired", "late"];

View File

@@ -6,7 +6,7 @@ import {
} from "./provider-runtime.test-support.js";
import type { ProviderPlugin, ProviderRuntimeModel } from "./types.js";
type ResolvePluginProviders = typeof import("./providers.js").resolvePluginProviders;
type ResolvePluginProviders = typeof import("./providers.runtime.js").resolvePluginProviders;
type ResolveNonBundledProviderPluginIds =
typeof import("./providers.js").resolveNonBundledProviderPluginIds;
type ResolveOwningPluginIdsForProvider =
@@ -21,13 +21,16 @@ const resolveOwningPluginIdsForProviderMock = vi.fn<ResolveOwningPluginIdsForPro
);
vi.mock("./providers.js", () => ({
resolvePluginProviders: (params: unknown) => resolvePluginProvidersMock(params as never),
resolveNonBundledProviderPluginIds: (params: unknown) =>
resolveNonBundledProviderPluginIdsMock(params as never),
resolveOwningPluginIdsForProvider: (params: unknown) =>
resolveOwningPluginIdsForProviderMock(params as never),
}));
vi.mock("./providers.runtime.js", () => ({
resolvePluginProviders: (params: unknown) => resolvePluginProvidersMock(params as never),
}));
let augmentModelCatalogWithProviderPlugins: typeof import("./provider-runtime.js").augmentModelCatalogWithProviderPlugins;
let buildProviderAuthDoctorHintWithPlugin: typeof import("./provider-runtime.js").buildProviderAuthDoctorHintWithPlugin;
let buildProviderMissingAuthMessageWithPlugin: typeof import("./provider-runtime.js").buildProviderMissingAuthMessageWithPlugin;

View File

@@ -8,8 +8,8 @@ import {
import {
resolveNonBundledProviderPluginIds,
resolveOwningPluginIdsForProvider,
resolvePluginProviders,
} from "./providers.js";
import { resolvePluginProviders } from "./providers.runtime.js";
import { resolvePluginCacheInputs } from "./roots.js";
import type {
ProviderAuthDoctorHintContext,

View File

@@ -9,7 +9,7 @@ import {
import type { ProviderPlugin } from "./types.js";
const resolvePluginProviders = vi.hoisted(() => vi.fn<() => ProviderPlugin[]>(() => []));
vi.mock("./providers.js", () => ({
vi.mock("./providers.runtime.js", () => ({
resolvePluginProviders,
}));

View File

@@ -3,7 +3,7 @@ import { parseModelRef } from "../agents/model-selection.js";
import { normalizeProviderId } from "../agents/model-selection.js";
import type { OpenClawConfig } from "../config/config.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import { resolvePluginProviders } from "./providers.js";
import { resolvePluginProviders } from "./providers.runtime.js";
import type {
ProviderAuthMethod,
ProviderPlugin,

View File

@@ -0,0 +1,71 @@
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
withBundledPluginAllowlistCompat,
withBundledPluginEnablementCompat,
} from "./bundled-compat.js";
import { loadOpenClawPlugins, type PluginLoadOptions } from "./loader.js";
import { createPluginLoaderLogger } from "./logger.js";
import {
resolveBundledProviderCompatPluginIds,
withBundledProviderVitestCompat,
} from "./providers.js";
import type { ProviderPlugin } from "./types.js";
const log = createSubsystemLogger("plugins");
export function resolvePluginProviders(params: {
config?: PluginLoadOptions["config"];
workspaceDir?: string;
/** Use an explicit env when plugin roots should resolve independently from process.env. */
env?: PluginLoadOptions["env"];
bundledProviderAllowlistCompat?: boolean;
bundledProviderVitestCompat?: boolean;
onlyPluginIds?: string[];
activate?: boolean;
cache?: boolean;
}): ProviderPlugin[] {
const env = params.env ?? process.env;
const bundledProviderCompatPluginIds =
params.bundledProviderAllowlistCompat || params.bundledProviderVitestCompat
? resolveBundledProviderCompatPluginIds({
config: params.config,
workspaceDir: params.workspaceDir,
env,
onlyPluginIds: params.onlyPluginIds,
})
: [];
const maybeAllowlistCompat = params.bundledProviderAllowlistCompat
? withBundledPluginAllowlistCompat({
config: params.config,
pluginIds: bundledProviderCompatPluginIds,
})
: params.config;
const maybeVitestCompat = params.bundledProviderVitestCompat
? withBundledProviderVitestCompat({
config: maybeAllowlistCompat,
pluginIds: bundledProviderCompatPluginIds,
env: params.env,
})
: maybeAllowlistCompat;
const config =
params.bundledProviderAllowlistCompat || params.bundledProviderVitestCompat
? withBundledPluginEnablementCompat({
config: maybeVitestCompat,
pluginIds: bundledProviderCompatPluginIds,
})
: maybeVitestCompat;
const registry = loadOpenClawPlugins({
config,
workspaceDir: params.workspaceDir,
env,
onlyPluginIds: params.onlyPluginIds,
cache: params.cache ?? false,
activate: params.activate ?? false,
logger: createPluginLoaderLogger(log),
});
return registry.providers.map((entry) => ({
...entry.provider,
pluginId: entry.pluginId,
}));
}

View File

@@ -12,7 +12,7 @@ vi.mock("./manifest-registry.js", () => ({
}));
let resolveOwningPluginIdsForProvider: typeof import("./providers.js").resolveOwningPluginIdsForProvider;
let resolvePluginProviders: typeof import("./providers.js").resolvePluginProviders;
let resolvePluginProviders: typeof import("./providers.runtime.js").resolvePluginProviders;
describe("resolvePluginProviders", () => {
beforeEach(async () => {
@@ -32,8 +32,8 @@ describe("resolvePluginProviders", () => {
],
diagnostics: [],
});
({ resolveOwningPluginIdsForProvider, resolvePluginProviders } =
await import("./providers.js"));
({ resolveOwningPluginIdsForProvider } = await import("./providers.js"));
({ resolvePluginProviders } = await import("./providers.runtime.js"));
});
it("forwards an explicit env to plugin loading", () => {

View File

@@ -1,19 +1,10 @@
import { normalizeProviderId } from "../agents/provider-id.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
withBundledPluginAllowlistCompat,
withBundledPluginEnablementCompat,
} from "./bundled-compat.js";
import { hasExplicitPluginConfig } from "./config-state.js";
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
import { loadOpenClawPlugins, type PluginLoadOptions } from "./loader.js";
import { createPluginLoaderLogger } from "./logger.js";
import type { PluginLoadOptions } from "./loader.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import type { ProviderPlugin } from "./types.js";
const log = createSubsystemLogger("plugins");
function withBundledProviderVitestCompat(params: {
export function withBundledProviderVitestCompat(params: {
config: PluginLoadOptions["config"];
pluginIds: readonly string[];
env?: PluginLoadOptions["env"];
@@ -41,7 +32,7 @@ function withBundledProviderVitestCompat(params: {
};
}
function resolveBundledProviderCompatPluginIds(params: {
export function resolveBundledProviderCompatPluginIds(params: {
config?: PluginLoadOptions["config"];
workspaceDir?: string;
env?: PluginLoadOptions["env"];
@@ -120,60 +111,3 @@ export function resolveNonBundledProviderPluginIds(params: {
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
}
export function resolvePluginProviders(params: {
config?: PluginLoadOptions["config"];
workspaceDir?: string;
/** Use an explicit env when plugin roots should resolve independently from process.env. */
env?: PluginLoadOptions["env"];
bundledProviderAllowlistCompat?: boolean;
bundledProviderVitestCompat?: boolean;
onlyPluginIds?: string[];
activate?: boolean;
cache?: boolean;
}): ProviderPlugin[] {
const env = params.env ?? process.env;
const bundledProviderCompatPluginIds =
params.bundledProviderAllowlistCompat || params.bundledProviderVitestCompat
? resolveBundledProviderCompatPluginIds({
config: params.config,
workspaceDir: params.workspaceDir,
env,
onlyPluginIds: params.onlyPluginIds,
})
: [];
const maybeAllowlistCompat = params.bundledProviderAllowlistCompat
? withBundledPluginAllowlistCompat({
config: params.config,
pluginIds: bundledProviderCompatPluginIds,
})
: params.config;
const maybeVitestCompat = params.bundledProviderVitestCompat
? withBundledProviderVitestCompat({
config: maybeAllowlistCompat,
pluginIds: bundledProviderCompatPluginIds,
env: params.env,
})
: maybeAllowlistCompat;
const config =
params.bundledProviderAllowlistCompat || params.bundledProviderVitestCompat
? withBundledPluginEnablementCompat({
config: maybeVitestCompat,
pluginIds: bundledProviderCompatPluginIds,
})
: maybeVitestCompat;
const registry = loadOpenClawPlugins({
config,
workspaceDir: params.workspaceDir,
env,
onlyPluginIds: params.onlyPluginIds,
cache: params.cache ?? false,
activate: params.activate ?? false,
logger: createPluginLoaderLogger(log),
});
return registry.providers.map((entry) => ({
...entry.provider,
pluginId: entry.pluginId,
}));
}