mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
perf(agents): reuse gateway plugin preparation (#105646)
* perf(agents): reuse gateway plugin preparation * chore: keep release notes out of PR
This commit is contained in:
committed by
GitHub
parent
e3c338f664
commit
1b281de6df
@@ -1,5 +1,6 @@
|
||||
// Load context tests cover agent and workspace context resolution for plugin runtimes.
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
|
||||
const loadConfigMock = vi.fn<typeof import("../../config/config.js").loadConfig>();
|
||||
const applyPluginAutoEnableMock =
|
||||
@@ -20,7 +21,8 @@ const metadataSnapshot = {
|
||||
policyHash: "policy",
|
||||
workspaceDir: "/resolved-workspace",
|
||||
};
|
||||
const loadPluginMetadataSnapshotMock = vi.fn(() => metadataSnapshot);
|
||||
type MetadataSnapshotMock = typeof metadataSnapshot & { pluginIds?: readonly string[] };
|
||||
const loadPluginMetadataSnapshotMock = vi.fn((): MetadataSnapshotMock => metadataSnapshot);
|
||||
const isPluginMetadataSnapshotCompatibleMock = vi.fn(() => true);
|
||||
const getCurrentPluginMetadataSnapshotMock = vi.fn(() => undefined);
|
||||
const setCurrentPluginMetadataSnapshotMock = vi.fn();
|
||||
@@ -190,6 +192,50 @@ describe("resolvePluginRuntimeLoadContext", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("reuses auto-enable results until Gateway config or metadata changes", () => {
|
||||
const rawConfig = { plugins: {} };
|
||||
const env = process.env;
|
||||
const initialSnapshot = { ...metadataSnapshot, pluginIds: ["openai"] };
|
||||
loadPluginMetadataSnapshotMock
|
||||
.mockReturnValueOnce(initialSnapshot)
|
||||
.mockReturnValueOnce({ ...initialSnapshot, pluginIds: ["openai"] })
|
||||
.mockReturnValueOnce({ ...initialSnapshot, policyHash: "changed" })
|
||||
.mockReturnValueOnce(initialSnapshot);
|
||||
|
||||
const first = resolvePluginRuntimeLoadContext({ config: rawConfig, env });
|
||||
const second = resolvePluginRuntimeLoadContext({ config: rawConfig, env });
|
||||
resolvePluginRuntimeLoadContext({ config: rawConfig, env });
|
||||
resolvePluginRuntimeLoadContext({ config: { plugins: {} }, env });
|
||||
|
||||
expect(second.config).toBe(first.config);
|
||||
expect(applyPluginAutoEnableMock).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it("invalidates auto-enable results when config or process env mutates in place", () => {
|
||||
const rawConfig: OpenClawConfig = { plugins: {} };
|
||||
const env = process.env;
|
||||
const envKey = "OPENCLAW_TEST_PLUGIN_AUTO_ENABLE_FINGERPRINT";
|
||||
const previousEnvValue = env[envKey];
|
||||
delete env[envKey];
|
||||
|
||||
try {
|
||||
resolvePluginRuntimeLoadContext({ config: rawConfig, env });
|
||||
resolvePluginRuntimeLoadContext({ config: rawConfig, env });
|
||||
rawConfig.plugins = { entries: { demo: { enabled: true } } };
|
||||
resolvePluginRuntimeLoadContext({ config: rawConfig, env });
|
||||
env[envKey] = "changed";
|
||||
resolvePluginRuntimeLoadContext({ config: rawConfig, env });
|
||||
|
||||
expect(applyPluginAutoEnableMock).toHaveBeenCalledTimes(3);
|
||||
} finally {
|
||||
if (previousEnvValue === undefined) {
|
||||
delete env[envKey];
|
||||
} else {
|
||||
env[envKey] = previousEnvValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("threads install records from the metadata snapshot into the context and load options", () => {
|
||||
const snapshotWithRecords = {
|
||||
...metadataSnapshot,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// Plugin runtime load context helpers resolve agent and workspace facts for runtime activation.
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import { getRuntimeConfig } from "../../config/config.js";
|
||||
import {
|
||||
fingerprintPluginAutoEnableConfig,
|
||||
fingerprintPluginAutoEnableEnv,
|
||||
} from "../../config/plugin-auto-enable.apply.js";
|
||||
import { applyPluginAutoEnable } from "../../config/plugin-auto-enable.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { PluginInstallRecord } from "../../config/types.plugins.js";
|
||||
@@ -14,6 +18,7 @@ import {
|
||||
import { extractPluginInstallRecordsFromInstalledPluginIndex } from "../installed-plugin-index-install-records.js";
|
||||
import type { PluginLoadOptions } from "../loader.js";
|
||||
import type { PluginManifestRegistry } from "../manifest-registry.js";
|
||||
import { registerPluginMetadataProcessMemoLifecycleClear } from "../plugin-metadata-lifecycle.js";
|
||||
import {
|
||||
isPluginMetadataSnapshotCompatible,
|
||||
resolvePluginMetadataSnapshot,
|
||||
@@ -22,6 +27,90 @@ import type { PluginLogger } from "../types.js";
|
||||
|
||||
const log = createSubsystemLogger("plugins");
|
||||
|
||||
type CurrentAutoEnableCacheEntry = {
|
||||
config: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
autoEnableConfigFingerprint: string;
|
||||
autoEnableEnvFingerprint: string;
|
||||
metadataConfigFingerprint: string | undefined;
|
||||
pluginIds: readonly string[] | undefined;
|
||||
policyHash: string;
|
||||
result: ReturnType<typeof applyPluginAutoEnable>;
|
||||
workspaceDir: string | undefined;
|
||||
};
|
||||
|
||||
let currentAutoEnableCache: CurrentAutoEnableCacheEntry | undefined;
|
||||
|
||||
registerPluginMetadataProcessMemoLifecycleClear(() => {
|
||||
currentAutoEnableCache = undefined;
|
||||
});
|
||||
|
||||
function samePluginIds(
|
||||
left: readonly string[] | undefined,
|
||||
right: readonly string[] | undefined,
|
||||
): boolean {
|
||||
return (
|
||||
left === right ||
|
||||
(left !== undefined &&
|
||||
right !== undefined &&
|
||||
left.length === right.length &&
|
||||
left.every((pluginId, index) => pluginId === right[index]))
|
||||
);
|
||||
}
|
||||
|
||||
function applyCurrentPluginAutoEnable(params: {
|
||||
config: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
workspaceDir?: string;
|
||||
manifestRegistry: PluginManifestRegistry | undefined;
|
||||
snapshot: ReturnType<typeof resolvePluginMetadataSnapshot> | undefined;
|
||||
}): ReturnType<typeof applyPluginAutoEnable> {
|
||||
if (!params.snapshot || !params.manifestRegistry || params.env !== process.env) {
|
||||
return applyPluginAutoEnable({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
manifestRegistry: params.manifestRegistry,
|
||||
discovery: params.snapshot?.discovery,
|
||||
});
|
||||
}
|
||||
// Gateway plugin metadata and config are replacement snapshots. Reuse only while
|
||||
// mutable config/env content still matches; reload/close lifecycle clears the slot.
|
||||
const workspaceDir = params.snapshot.workspaceDir ?? params.workspaceDir;
|
||||
const autoEnableConfigFingerprint = fingerprintPluginAutoEnableConfig(params.config);
|
||||
const autoEnableEnvFingerprint = fingerprintPluginAutoEnableEnv(params.env);
|
||||
const cached = currentAutoEnableCache;
|
||||
if (
|
||||
cached?.config === params.config &&
|
||||
cached.env === params.env &&
|
||||
cached.autoEnableConfigFingerprint === autoEnableConfigFingerprint &&
|
||||
cached.autoEnableEnvFingerprint === autoEnableEnvFingerprint &&
|
||||
cached.metadataConfigFingerprint === params.snapshot.configFingerprint &&
|
||||
cached.policyHash === params.snapshot.policyHash &&
|
||||
cached.workspaceDir === workspaceDir &&
|
||||
samePluginIds(cached.pluginIds, params.snapshot.pluginIds)
|
||||
) {
|
||||
return cached.result;
|
||||
}
|
||||
const result = applyPluginAutoEnable({
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
manifestRegistry: params.manifestRegistry,
|
||||
discovery: params.snapshot.discovery,
|
||||
});
|
||||
currentAutoEnableCache = {
|
||||
config: params.config,
|
||||
env: params.env,
|
||||
autoEnableConfigFingerprint,
|
||||
autoEnableEnvFingerprint,
|
||||
metadataConfigFingerprint: params.snapshot.configFingerprint,
|
||||
pluginIds: params.snapshot.pluginIds,
|
||||
policyHash: params.snapshot.policyHash,
|
||||
result,
|
||||
workspaceDir,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Resolved plugin runtime load context shared by runtime loader callers. */
|
||||
export type PluginRuntimeLoadContext = {
|
||||
rawConfig: OpenClawConfig;
|
||||
@@ -90,11 +179,12 @@ export function resolvePluginRuntimeLoadContext(
|
||||
config: rawConfig,
|
||||
activationSourceConfig: options?.activationSourceConfig,
|
||||
});
|
||||
const autoEnabled = applyPluginAutoEnable({
|
||||
const autoEnabled = applyCurrentPluginAutoEnable({
|
||||
config: rawConfig,
|
||||
env,
|
||||
workspaceDir: rawWorkspaceDir,
|
||||
manifestRegistry,
|
||||
discovery: initialMetadataSnapshot?.discovery,
|
||||
snapshot: initialMetadataSnapshot,
|
||||
});
|
||||
const config = autoEnabled.config;
|
||||
const workspaceDir =
|
||||
|
||||
Reference in New Issue
Block a user