perf: avoid registry loads in hot tests

This commit is contained in:
Peter Steinberger
2026-04-28 01:20:40 +01:00
parent 6b1089ffe5
commit e508d81f79
8 changed files with 442 additions and 60 deletions

View File

@@ -1,8 +1,12 @@
import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";
import { resolveAuthProfileOrder } from "./auth-profiles/order.js";
import type { AuthProfileStore } from "./auth-profiles/types.js";
import { isProfileInCooldown } from "./auth-profiles/usage-state.js";
vi.mock("./provider-auth-aliases.js", () => ({
resolveProviderIdForAuth: (provider: string) => provider.trim().toLowerCase(),
}));
/**
* Integration tests for cooldown auto-expiry through resolveAuthProfileOrder.
* Verifies that profiles with expired cooldowns are treated as available and

View File

@@ -1,4 +1,4 @@
import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";
import {
ANTHROPIC_CFG,
ANTHROPIC_STORE,
@@ -6,6 +6,11 @@ import {
import { resolveAuthProfileOrder } from "./auth-profiles/order.js";
import type { AuthProfileStore } from "./auth-profiles/types.js";
vi.mock("./provider-auth-aliases.js", () => ({
resolveProviderIdForAuth: (provider: string) =>
provider.trim().toLowerCase() === "z.ai" ? "zai" : provider.trim().toLowerCase(),
}));
function makeApiKeyStore(provider: string, profileIds: string[]): AuthProfileStore {
return {
version: 1,

View File

@@ -1,5 +1,5 @@
import type { SecretRefSource } from "../config/types.secrets.js";
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { listOpenClawPluginManifestMetadata } from "../plugins/manifest-metadata-scan.js";
import { listKnownProviderEnvApiKeyNames } from "./model-auth-env-vars.js";
export const MINIMAX_OAUTH_MARKER = "minimax-oauth";
@@ -35,6 +35,13 @@ const LEGACY_ENV_API_KEY_MARKERS = [
"MINIMAX_CODE_PLAN_KEY",
];
function normalizeStringList(value: unknown): string[] {
if (!Array.isArray(value)) {
return [];
}
return value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
}
function listKnownEnvApiKeyMarkers(): Set<string> {
knownEnvApiKeyMarkersCache ??= new Set([
...listKnownProviderEnvApiKeyNames(),
@@ -48,8 +55,10 @@ export function listKnownNonSecretApiKeyMarkers(): string[] {
knownNonSecretApiKeyMarkersCache ??= [
...new Set([
...CORE_NON_SECRET_API_KEY_MARKERS,
...loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true }).plugins.flatMap(
(plugin) => (plugin.origin === "bundled" ? (plugin.nonSecretAuthMarkers ?? []) : []),
...listOpenClawPluginManifestMetadata().flatMap((plugin) =>
plugin.origin === "bundled"
? normalizeStringList(plugin.manifest.nonSecretAuthMarkers)
: [],
),
]),
];

View File

@@ -1,5 +1,6 @@
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
import { listOpenClawPluginManifestMetadata } from "../plugins/manifest-metadata-scan.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "../shared/string-coerce.js";
@@ -224,57 +225,123 @@ function isManifestProviderEndpointClass(value: string): value is ProviderEndpoi
return MANIFEST_PROVIDER_ENDPOINT_CLASSES.has(value as ProviderEndpointClass);
}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function normalizeStringList(value: unknown): string[] {
if (!Array.isArray(value)) {
return [];
}
return value
.map((entry) => normalizeOptionalString(entry))
.filter((entry): entry is string => entry !== undefined);
}
function readManifestProviderEndpoints(
manifest: Record<string, unknown>,
): ManifestProviderEndpointCacheEntry[] {
if (!Array.isArray(manifest.providerEndpoints)) {
return [];
}
const entries: ManifestProviderEndpointCacheEntry[] = [];
for (const rawEndpoint of manifest.providerEndpoints) {
if (!isRecord(rawEndpoint)) {
continue;
}
const endpointClassRaw = normalizeOptionalString(rawEndpoint.endpointClass);
if (!endpointClassRaw || !isManifestProviderEndpointClass(endpointClassRaw)) {
continue;
}
entries.push({
endpointClass: endpointClassRaw,
hosts: normalizeStringList(rawEndpoint.hosts).map((host) => host.toLowerCase()),
hostSuffixes: normalizeStringList(rawEndpoint.hostSuffixes).map((host) => host.toLowerCase()),
normalizedBaseUrls: normalizeStringList(rawEndpoint.baseUrls)
.map((baseUrl) => normalizeComparableBaseUrl(baseUrl))
.filter((baseUrl): baseUrl is string => baseUrl !== undefined),
...(normalizeOptionalString(rawEndpoint.googleVertexRegion)
? { googleVertexRegion: normalizeOptionalString(rawEndpoint.googleVertexRegion) }
: {}),
...(normalizeOptionalString(rawEndpoint.googleVertexRegionHostSuffix)
? {
googleVertexRegionHostSuffix: normalizeOptionalString(
rawEndpoint.googleVertexRegionHostSuffix,
),
}
: {}),
});
}
return entries;
}
function readManifestProviderRequests(
manifest: Record<string, unknown>,
): Array<[string, ManifestProviderRequestCacheEntry]> {
const providerRequest = manifest.providerRequest;
if (!isRecord(providerRequest) || !isRecord(providerRequest.providers)) {
return [];
}
const entries: Array<[string, ManifestProviderRequestCacheEntry]> = [];
for (const [providerRaw, requestRaw] of Object.entries(providerRequest.providers)) {
if (!isRecord(requestRaw)) {
continue;
}
const provider = normalizeLowercaseStringOrEmpty(providerRaw);
if (!provider) {
continue;
}
const compatibilityFamily =
normalizeOptionalString(requestRaw.compatibilityFamily) === "moonshot"
? "moonshot"
: undefined;
const supportsStreamingUsage = isRecord(requestRaw.openAICompletions)
? requestRaw.openAICompletions.supportsStreamingUsage
: undefined;
entries.push([
provider,
{
...(normalizeOptionalString(requestRaw.family)
? { family: normalizeOptionalString(requestRaw.family) }
: {}),
...(compatibilityFamily ? { compatibilityFamily } : {}),
...(typeof supportsStreamingUsage === "boolean"
? { supportsOpenAICompletionsStreamingUsageCompat: supportsStreamingUsage }
: {}),
},
]);
}
return entries;
}
function collectManifestProviderEndpoints(): ManifestProviderEndpointCacheEntry[] {
const entries: ManifestProviderEndpointCacheEntry[] = [];
for (const { manifest } of listOpenClawPluginManifestMetadata()) {
entries.push(...readManifestProviderEndpoints(manifest));
}
return entries;
}
function collectManifestProviderRequests(): Map<string, ManifestProviderRequestCacheEntry> {
const entries = new Map<string, ManifestProviderRequestCacheEntry>();
for (const { manifest } of listOpenClawPluginManifestMetadata()) {
for (const [provider, request] of readManifestProviderRequests(manifest)) {
entries.set(provider, request);
}
}
return entries;
}
function loadManifestProviderEndpointCache(): ManifestProviderEndpointCacheEntry[] {
if (!manifestProviderEndpointCache) {
const registry = loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true });
const entries: ManifestProviderEndpointCacheEntry[] = [];
for (const plugin of registry.plugins) {
for (const endpoint of plugin.providerEndpoints ?? []) {
if (!isManifestProviderEndpointClass(endpoint.endpointClass)) {
continue;
}
entries.push({
endpointClass: endpoint.endpointClass,
hosts: (endpoint.hosts ?? []).map((host) => host.toLowerCase()),
hostSuffixes: (endpoint.hostSuffixes ?? []).map((host) => host.toLowerCase()),
normalizedBaseUrls: (endpoint.baseUrls ?? [])
.map((baseUrl) => normalizeComparableBaseUrl(baseUrl))
.filter((baseUrl): baseUrl is string => baseUrl !== undefined),
...(endpoint.googleVertexRegion
? { googleVertexRegion: endpoint.googleVertexRegion }
: {}),
...(endpoint.googleVertexRegionHostSuffix
? { googleVertexRegionHostSuffix: endpoint.googleVertexRegionHostSuffix }
: {}),
});
}
}
manifestProviderEndpointCache = entries;
manifestProviderEndpointCache = collectManifestProviderEndpoints();
}
return manifestProviderEndpointCache;
}
function loadManifestProviderRequestCache(): Map<string, ManifestProviderRequestCacheEntry> {
if (!manifestProviderRequestCache) {
const registry = loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true });
const entries = new Map<string, ManifestProviderRequestCacheEntry>();
for (const plugin of registry.plugins) {
for (const [provider, request] of Object.entries(plugin.providerRequest?.providers ?? {})) {
entries.set(provider, {
...(request.family ? { family: request.family } : {}),
...(request.compatibilityFamily
? { compatibilityFamily: request.compatibilityFamily }
: {}),
...(request.openAICompletions?.supportsStreamingUsage !== undefined
? {
supportsOpenAICompletionsStreamingUsageCompat:
request.openAICompletions.supportsStreamingUsage,
}
: {}),
});
}
}
manifestProviderRequestCache = entries;
manifestProviderRequestCache = collectManifestProviderRequests();
}
return manifestProviderRequestCache;
}

View File

@@ -1,5 +1,5 @@
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import { withEnv } from "../test-utils/env.js";
import { createFixtureSuite } from "../test-utils/fixture-suite.js";
import { writeSkill } from "./skills.e2e-test-helpers.js";
@@ -7,6 +7,10 @@ import { createSyntheticSourceInfo } from "./skills/skill-contract.js";
import type { OpenClawSkillMetadata, SkillEntry } from "./skills/types.js";
import { buildWorkspaceSkillsPrompt } from "./skills/workspace.js";
vi.mock("./skills/plugin-skills.js", () => ({
resolvePluginSkillDirs: () => [],
}));
const fixtureSuite = createFixtureSuite("openclaw-skills-prompt-suite-");
beforeAll(async () => {

View File

@@ -3,10 +3,10 @@
// Keep provider-owned exports out of this subpath so plugin loaders can import it
// without recursing through provider-specific facades.
import { resolveProviderRequestCapabilities } from "../agents/provider-attribution.js";
import { findNormalizedProviderKey } from "../agents/provider-id.js";
import type { ModelDefinitionConfig } from "../config/types.models.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { resolveProviderRequestCapabilities } from "./provider-http.js";
import type { ModelProviderConfig } from "./provider-model-shared.js";
export type { ProviderCatalogContext, ProviderCatalogResult } from "../plugins/types.js";

View File

@@ -0,0 +1,188 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { parseJsonWithJson5Fallback } from "../utils/parse-json-compat.js";
type PluginManifestMetadataRecord = {
pluginDir: string;
manifest: Record<string, unknown>;
origin?: string;
};
type CandidateDir = {
pluginDir: string;
rank: number;
order: number;
origin?: string;
};
const OPENCLAW_PACKAGE_ROOT = fileURLToPath(new URL("../..", import.meta.url));
const PLUGIN_MANIFEST_FILENAME = "openclaw.plugin.json";
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function normalizeTrimmedString(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}
function resolveUserPath(value: string, env: NodeJS.ProcessEnv): string {
if (value === "~" || value.startsWith("~/")) {
const home = env.OPENCLAW_HOME ?? env.HOME ?? env.USERPROFILE ?? os.homedir();
return path.join(home, value.slice(2));
}
return path.resolve(value);
}
function resolveStateDir(env: NodeJS.ProcessEnv): string {
const override = normalizeTrimmedString(env.OPENCLAW_STATE_DIR);
if (override) {
return resolveUserPath(override, env);
}
const home = env.OPENCLAW_HOME ?? env.HOME ?? env.USERPROFILE ?? os.homedir();
return path.join(home, ".openclaw");
}
function areBundledPluginsDisabled(env: NodeJS.ProcessEnv): boolean {
const value = normalizeTrimmedString(env.OPENCLAW_DISABLE_BUNDLED_PLUGINS)?.toLowerCase();
return value === "1" || value === "true";
}
function hasManifestDir(root: string | undefined): root is string {
return Boolean(root && fs.existsSync(root));
}
function resolveBundledPluginRoot(env: NodeJS.ProcessEnv): string | undefined {
if (areBundledPluginsDisabled(env)) {
return undefined;
}
const override = normalizeTrimmedString(env.OPENCLAW_BUNDLED_PLUGINS_DIR);
if (override) {
return resolveUserPath(override, env);
}
const sourceRoot = path.join(OPENCLAW_PACKAGE_ROOT, "extensions");
const runtimeRoot = path.join(OPENCLAW_PACKAGE_ROOT, "dist-runtime", "extensions");
const distRoot = path.join(OPENCLAW_PACKAGE_ROOT, "dist", "extensions");
return [sourceRoot, runtimeRoot, distRoot].find(hasManifestDir);
}
function listChildPluginDirs(
root: string | undefined,
rank: number,
startOrder: number,
origin: string,
): CandidateDir[] {
if (!root || !fs.existsSync(root)) {
return [];
}
const dirs: CandidateDir[] = [];
let order = startOrder;
try {
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
if (entry.isDirectory()) {
dirs.push({ pluginDir: path.join(root, entry.name), rank, order: order++, origin });
}
}
} catch {
return [];
}
return dirs;
}
function readJsonObject(filePath: string): Record<string, unknown> | undefined {
try {
const parsed = parseJsonWithJson5Fallback(fs.readFileSync(filePath, "utf8"));
return isRecord(parsed) ? parsed : undefined;
} catch {
return undefined;
}
}
function readManifestObject(pluginDir: string): Record<string, unknown> | undefined {
return readJsonObject(path.join(pluginDir, PLUGIN_MANIFEST_FILENAME));
}
function listPersistedIndexPluginDirs(env: NodeJS.ProcessEnv, startOrder: number): CandidateDir[] {
const index = readJsonObject(path.join(resolveStateDir(env), "plugins", "installs.json"));
if (!index || !Array.isArray(index.plugins)) {
return [];
}
const dirs: CandidateDir[] = [];
let order = startOrder;
for (const rawPlugin of index.plugins) {
if (!isRecord(rawPlugin)) {
continue;
}
const rootDir = normalizeTrimmedString(rawPlugin.rootDir);
if (!rootDir) {
continue;
}
dirs.push({
pluginDir: resolveUserPath(rootDir, env),
rank: rawPlugin.origin === "bundled" ? 2 : 1,
order: order++,
origin: normalizeTrimmedString(rawPlugin.origin),
});
}
return dirs;
}
function resolveComparablePath(filePath: string): string {
try {
return fs.realpathSync(filePath);
} catch {
return path.resolve(filePath);
}
}
function uniqueCandidateDirs(candidates: CandidateDir[]): CandidateDir[] {
const byPath = new Map<string, CandidateDir>();
for (const candidate of candidates) {
const key = resolveComparablePath(candidate.pluginDir);
const existing = byPath.get(key);
if (!existing || candidate.rank < existing.rank || candidate.order < existing.order) {
byPath.set(key, candidate);
}
}
return [...byPath.values()].toSorted(
(left, right) => left.rank - right.rank || left.order - right.order,
);
}
export function listOpenClawPluginManifestMetadata(
env: NodeJS.ProcessEnv = process.env,
): PluginManifestMetadataRecord[] {
const candidates: CandidateDir[] = [];
let order = 0;
candidates.push(...listPersistedIndexPluginDirs(env, order));
order = candidates.length;
candidates.push(...listChildPluginDirs(resolveBundledPluginRoot(env), 2, order, "bundled"));
order = candidates.length;
candidates.push(
...listChildPluginDirs(path.join(resolveStateDir(env), "extensions"), 4, order, "global"),
);
const byManifestId = new Map<string, CandidateDir>();
const records: PluginManifestMetadataRecord[] = [];
for (const candidate of uniqueCandidateDirs(candidates)) {
const manifest = readManifestObject(candidate.pluginDir);
if (!manifest) {
continue;
}
const manifestId = normalizeTrimmedString(manifest.id);
if (manifestId) {
const existing = byManifestId.get(manifestId);
if (existing && existing.rank <= candidate.rank) {
continue;
}
byManifestId.set(manifestId, candidate);
}
records.push({ pluginDir: candidate.pluginDir, manifest, origin: candidate.origin });
}
return records;
}

View File

@@ -1,11 +1,115 @@
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { listOpenClawPluginManifestMetadata } from "./manifest-metadata-scan.js";
import type { PluginManifestModelIdNormalizationProvider } from "./manifest.js";
import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";
let manifestModelIdNormalizationCache:
| Map<string, PluginManifestModelIdNormalizationProvider>
| undefined;
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function normalizeTrimmedString(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}
function normalizeStringList(value: unknown): string[] {
if (!Array.isArray(value)) {
return [];
}
return value
.map((entry) => normalizeTrimmedString(entry))
.filter((entry): entry is string => entry !== undefined);
}
function normalizePrefixRules(
value: unknown,
): PluginManifestModelIdNormalizationProvider["prefixWhenBareAfterAliasStartsWith"] {
if (!Array.isArray(value)) {
return undefined;
}
const rules: NonNullable<
PluginManifestModelIdNormalizationProvider["prefixWhenBareAfterAliasStartsWith"]
> = [];
for (const rawRule of value) {
if (!isRecord(rawRule)) {
continue;
}
const modelPrefix = normalizeTrimmedString(rawRule.modelPrefix);
const prefix = normalizeTrimmedString(rawRule.prefix);
if (modelPrefix && prefix) {
rules.push({ modelPrefix, prefix });
}
}
return rules.length > 0 ? rules : undefined;
}
function normalizeModelIdNormalizationPolicy(
value: unknown,
): PluginManifestModelIdNormalizationProvider | undefined {
if (!isRecord(value)) {
return undefined;
}
const aliases: Record<string, string> = {};
if (isRecord(value.aliases)) {
for (const [aliasRaw, canonicalRaw] of Object.entries(value.aliases)) {
const alias = normalizeLowercaseStringOrEmpty(aliasRaw);
const canonical = normalizeTrimmedString(canonicalRaw);
if (alias && canonical) {
aliases[alias] = canonical;
}
}
}
const stripPrefixes = normalizeStringList(value.stripPrefixes);
const prefixWhenBare = normalizeTrimmedString(value.prefixWhenBare);
const prefixWhenBareAfterAliasStartsWith = normalizePrefixRules(
value.prefixWhenBareAfterAliasStartsWith,
);
const policy = {
...(Object.keys(aliases).length > 0 ? { aliases } : {}),
...(stripPrefixes.length > 0 ? { stripPrefixes } : {}),
...(prefixWhenBare ? { prefixWhenBare } : {}),
...(prefixWhenBareAfterAliasStartsWith ? { prefixWhenBareAfterAliasStartsWith } : {}),
} satisfies PluginManifestModelIdNormalizationProvider;
return Object.keys(policy).length > 0 ? policy : undefined;
}
function readManifestModelIdNormalizationPolicies(
manifest: Record<string, unknown>,
): Array<[string, PluginManifestModelIdNormalizationProvider]> {
const modelIdNormalization = manifest.modelIdNormalization;
if (!isRecord(modelIdNormalization) || !isRecord(modelIdNormalization.providers)) {
return [];
}
const entries: Array<[string, PluginManifestModelIdNormalizationProvider]> = [];
for (const [providerRaw, rawPolicy] of Object.entries(modelIdNormalization.providers)) {
const provider = normalizeLowercaseStringOrEmpty(providerRaw);
const policy = normalizeModelIdNormalizationPolicy(rawPolicy);
if (provider && policy) {
entries.push([provider, policy]);
}
}
return entries;
}
function collectManifestModelIdNormalizationPolicies(): Map<
string,
PluginManifestModelIdNormalizationProvider
> {
const policies = new Map<string, PluginManifestModelIdNormalizationProvider>();
for (const { manifest } of listOpenClawPluginManifestMetadata()) {
for (const [provider, policy] of readManifestModelIdNormalizationPolicies(manifest)) {
policies.set(provider, policy);
}
}
return policies;
}
function loadManifestModelIdNormalizationPolicies(): Map<
string,
PluginManifestModelIdNormalizationProvider
@@ -14,17 +118,18 @@ function loadManifestModelIdNormalizationPolicies(): Map<
return manifestModelIdNormalizationCache;
}
const policies = new Map<string, PluginManifestModelIdNormalizationProvider>();
const registry = loadPluginManifestRegistryForPluginRegistry({ includeDisabled: true });
for (const plugin of registry.plugins) {
for (const [provider, policy] of Object.entries(plugin.modelIdNormalization?.providers ?? {})) {
policies.set(provider, policy);
}
}
const policies = collectManifestModelIdNormalizationPolicies();
manifestModelIdNormalizationCache = policies;
return policies;
}
function resolveManifestModelIdNormalizationPolicy(
provider: string,
): PluginManifestModelIdNormalizationProvider | undefined {
const providerId = normalizeLowercaseStringOrEmpty(provider);
return loadManifestModelIdNormalizationPolicies().get(providerId);
}
function hasProviderPrefix(modelId: string): boolean {
return modelId.includes("/");
}
@@ -40,7 +145,7 @@ export function normalizeProviderModelIdWithManifest(params: {
modelId: string;
};
}): string | undefined {
const policy = loadManifestModelIdNormalizationPolicies().get(params.provider);
const policy = resolveManifestModelIdNormalizationPolicy(params.provider);
if (!policy) {
return undefined;
}