mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:30:43 +00:00
refactor(memory): collapse legacy memory registration state
This commit is contained in:
@@ -1689,53 +1689,6 @@ describe("resolveModel", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("lets official openai-codex metadata override legacy unmarked models-add rows", () => {
|
||||
mockDiscoveredModel(discoverModels, {
|
||||
provider: "openai-codex",
|
||||
modelId: "gpt-5.5",
|
||||
templateModel: {
|
||||
...buildOpenAICodexForwardCompatExpectation("gpt-5.5"),
|
||||
name: "GPT-5.5",
|
||||
cost: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
|
||||
contextWindow: 400_000,
|
||||
},
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
models: {
|
||||
providers: {
|
||||
"openai-codex": {
|
||||
baseUrl: "https://chatgpt.com/backend-api",
|
||||
api: "openai-codex-responses",
|
||||
models: [
|
||||
{
|
||||
...makeModel("gpt-5.5"),
|
||||
api: "openai-codex-responses",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
|
||||
contextWindow: 400_000,
|
||||
contextTokens: 272_000,
|
||||
maxTokens: 128_000,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
const result = resolveModelForTest("openai-codex", "gpt-5.5", "/tmp/agent", cfg);
|
||||
|
||||
expect(result.error).toBeUndefined();
|
||||
expect(result.model).toMatchObject({
|
||||
provider: "openai-codex",
|
||||
id: "gpt-5.5",
|
||||
cost: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
|
||||
contextWindow: 400_000,
|
||||
maxTokens: 128_000,
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves openai-codex gpt-5.5 even when discovery omits the OAuth catalog row", () => {
|
||||
const result = resolveModelForTest("openai-codex", "gpt-5.5");
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
shouldSuppressBuiltInModel,
|
||||
shouldUnconditionallySuppress,
|
||||
} from "../model-suppression.js";
|
||||
import { isLegacyModelsAddCodexMetadataModel } from "../openai-codex-models-add-legacy.js";
|
||||
import { discoverAuthStorage, discoverModels } from "../pi-model-discovery.js";
|
||||
import {
|
||||
attachModelProviderRequestTransport,
|
||||
@@ -397,12 +396,10 @@ function resolveConfiguredProviderConfig(
|
||||
}
|
||||
|
||||
function isModelsAddMetadataModel(params: {
|
||||
provider: string;
|
||||
model: NonNullable<InlineProviderConfig["models"]>[number] | undefined;
|
||||
}) {
|
||||
return (
|
||||
(params.model as { metadataSource?: unknown } | undefined)?.metadataSource === "models-add" ||
|
||||
isLegacyModelsAddCodexMetadataModel(params)
|
||||
(params.model as { metadataSource?: unknown } | undefined)?.metadataSource === "models-add"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -528,8 +525,7 @@ function applyConfiguredProviderOverrides(params: {
|
||||
? findConfiguredProviderModel(providerConfig, params.provider, discoveredModel.id)
|
||||
: undefined);
|
||||
const metadataOverrideModel =
|
||||
params.preferDiscoveredModelMetadata &&
|
||||
isModelsAddMetadataModel({ provider: params.provider, model: configuredModel })
|
||||
params.preferDiscoveredModelMetadata && isModelsAddMetadataModel({ model: configuredModel })
|
||||
? undefined
|
||||
: configuredModel;
|
||||
const discoveredHeaders = sanitizeModelHeaders(discoveredModel.headers, {
|
||||
|
||||
@@ -5,7 +5,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
import {
|
||||
clearMemoryPluginState,
|
||||
registerMemoryFlushPlanResolver,
|
||||
registerMemoryCapability,
|
||||
type MemoryFlushPlanResolver,
|
||||
} from "../../plugins/memory-state.js";
|
||||
import type { TemplateContext } from "../templating.js";
|
||||
import {
|
||||
@@ -21,6 +22,10 @@ const runEmbeddedPiAgentMock = vi.fn();
|
||||
const refreshQueuedFollowupSessionMock = vi.fn();
|
||||
const incrementCompactionCountMock = vi.fn();
|
||||
|
||||
function registerMemoryFlushPlanResolverForTest(resolver: MemoryFlushPlanResolver): void {
|
||||
registerMemoryCapability("memory-core", { flushPlanResolver: resolver });
|
||||
}
|
||||
|
||||
function createReplyOperation() {
|
||||
return {
|
||||
abortSignal: new AbortController().signal,
|
||||
@@ -34,7 +39,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-memory-unit-"));
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 4_000,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 20_000,
|
||||
@@ -180,7 +185,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
});
|
||||
|
||||
it("runs memory flush on the configured maintenance model without active fallbacks", async () => {
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 4_000,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 20_000,
|
||||
@@ -317,7 +322,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
`${JSON.stringify({ message: { role: "user", content: "x".repeat(5_000) } })}\n`,
|
||||
"utf8",
|
||||
);
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 1,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 0,
|
||||
@@ -367,7 +372,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
`${JSON.stringify({ message: { role: "user", content: "x".repeat(5_000) } })}\n`,
|
||||
"utf8",
|
||||
);
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 1,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 0,
|
||||
@@ -442,7 +447,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
})}\n`,
|
||||
"utf8",
|
||||
);
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 4_000,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 0,
|
||||
@@ -493,7 +498,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
})}\n`,
|
||||
"utf8",
|
||||
);
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 4_000,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 0,
|
||||
@@ -553,7 +558,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 4_000,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 0,
|
||||
@@ -725,7 +730,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 4_000,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 20_000,
|
||||
|
||||
@@ -17,7 +17,8 @@ import {
|
||||
} from "../../infra/diagnostic-events.js";
|
||||
import {
|
||||
clearMemoryPluginState,
|
||||
registerMemoryFlushPlanResolver,
|
||||
registerMemoryCapability,
|
||||
type MemoryFlushPlanResolver,
|
||||
} from "../../plugins/memory-state.js";
|
||||
import type { TemplateContext } from "../templating.js";
|
||||
import type { FollowupRun, QueueSettings } from "./queue.js";
|
||||
@@ -37,6 +38,10 @@ function createCliBackendTestConfig() {
|
||||
};
|
||||
}
|
||||
|
||||
function registerMemoryFlushPlanResolverForTest(resolver: MemoryFlushPlanResolver): void {
|
||||
registerMemoryCapability("memory-core", { flushPlanResolver: resolver });
|
||||
}
|
||||
|
||||
const runEmbeddedPiAgentMock = vi.fn();
|
||||
const runCliAgentMock = vi.fn();
|
||||
const runWithModelFallbackMock = vi.fn();
|
||||
@@ -2140,7 +2145,7 @@ describe("runReplyAgent fallback reasoning tags", () => {
|
||||
});
|
||||
|
||||
it("enforces <final> during memory flush on fallback providers", async () => {
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
registerMemoryFlushPlanResolverForTest(() => ({
|
||||
softThresholdTokens: 1_000,
|
||||
forceFlushTranscriptBytes: 1_000_000_000,
|
||||
reserveTokensFloor: 20_000,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { migrateLegacyRuntimeModelRef } from "../../../agents/model-runtime-aliases.js";
|
||||
import { isLegacyModelsAddCodexMetadataModel } from "../../../agents/openai-codex-models-add-legacy.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import { resolveSingleAccountKeysToMove } from "../../../channels/plugins/setup-promotion-helpers.js";
|
||||
import { resolveNormalizedProviderModelMaxTokens } from "../../../config/defaults.js";
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
} from "../../../shared/string-coerce.js";
|
||||
import { sanitizeForLog } from "../../../terminal/ansi.js";
|
||||
import { isRecord } from "./legacy-config-record-shared.js";
|
||||
import { isLegacyModelsAddCodexMetadataModel } from "./legacy-models-add-metadata.js";
|
||||
export { normalizeLegacyTalkConfig } from "./legacy-talk-config-normalizer.js";
|
||||
|
||||
export function normalizeLegacyCommandsConfig(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { ModelDefinitionConfig } from "../config/types.models.js";
|
||||
import { isLegacyModelsAddCodexMetadataModel } from "./openai-codex-models-add-legacy.js";
|
||||
import type { ModelDefinitionConfig } from "../../../config/types.models.js";
|
||||
import { isLegacyModelsAddCodexMetadataModel } from "./legacy-models-add-metadata.js";
|
||||
|
||||
function buildLegacyModel(id: string): Partial<ModelDefinitionConfig> {
|
||||
return {
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ModelDefinitionConfig } from "../config/types.models.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import type { ModelDefinitionConfig } from "../../../config/types.models.js";
|
||||
|
||||
const LEGACY_MODELS_ADD_CODEX_MODEL_IDS = new Set(["gpt-5.5", "gpt-5.5-pro"]);
|
||||
|
||||
@@ -16,11 +16,9 @@ import {
|
||||
buildMemoryPromptSection,
|
||||
getMemoryRuntime,
|
||||
listMemoryCorpusSupplements,
|
||||
registerMemoryCapability,
|
||||
registerMemoryCorpusSupplement,
|
||||
registerMemoryFlushPlanResolver,
|
||||
registerMemoryPromptSupplement,
|
||||
registerMemoryPromptSection,
|
||||
registerMemoryRuntime,
|
||||
resolveMemoryFlushPlan,
|
||||
} from "./memory-state.js";
|
||||
import type { PluginRecord } from "./registry-types.js";
|
||||
@@ -590,22 +588,24 @@ describe("clearPluginLoaderCache", () => {
|
||||
search: async () => [],
|
||||
get: async () => null,
|
||||
});
|
||||
registerMemoryPromptSection(() => ["stale memory section"]);
|
||||
registerMemoryPromptSupplement("memory-wiki", () => ["stale wiki supplement"]);
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
softThresholdTokens: 1,
|
||||
forceFlushTranscriptBytes: 2,
|
||||
reserveTokensFloor: 3,
|
||||
prompt: "stale",
|
||||
systemPrompt: "stale",
|
||||
relativePath: "memory/stale.md",
|
||||
}));
|
||||
registerMemoryRuntime({
|
||||
async getMemorySearchManager() {
|
||||
return { manager: null };
|
||||
},
|
||||
resolveMemoryBackendConfig() {
|
||||
return { backend: "builtin" as const };
|
||||
registerMemoryCapability("memory-core", {
|
||||
promptBuilder: () => ["stale memory section"],
|
||||
flushPlanResolver: () => ({
|
||||
softThresholdTokens: 1,
|
||||
forceFlushTranscriptBytes: 2,
|
||||
reserveTokensFloor: 3,
|
||||
prompt: "stale",
|
||||
systemPrompt: "stale",
|
||||
relativePath: "memory/stale.md",
|
||||
}),
|
||||
runtime: {
|
||||
async getMemorySearchManager() {
|
||||
return { manager: null };
|
||||
},
|
||||
resolveMemoryBackendConfig() {
|
||||
return { backend: "builtin" as const };
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(buildMemoryPromptSection({ availableTools: new Set() })).toEqual([
|
||||
@@ -652,7 +652,9 @@ describe("clearPluginRegistryLoadCache", () => {
|
||||
id: "still-live",
|
||||
create: async () => ({ provider: null }),
|
||||
});
|
||||
registerMemoryPromptSection(() => ["still live"]);
|
||||
registerMemoryCapability("memory-core", {
|
||||
promptBuilder: () => ["still live"],
|
||||
});
|
||||
|
||||
clearPluginRegistryLoadCache();
|
||||
|
||||
|
||||
@@ -73,11 +73,9 @@ import {
|
||||
listActiveMemoryPublicArtifacts,
|
||||
listMemoryCorpusSupplements,
|
||||
listMemoryPromptSupplements,
|
||||
registerMemoryCapability,
|
||||
registerMemoryCorpusSupplement,
|
||||
registerMemoryFlushPlanResolver,
|
||||
registerMemoryPromptSupplement,
|
||||
registerMemoryPromptSection,
|
||||
registerMemoryRuntime,
|
||||
resolveMemoryFlushPlan,
|
||||
} from "./memory-state.js";
|
||||
import { ensureOpenClawPluginSdkAlias } from "./plugin-sdk-dist-alias.js";
|
||||
@@ -2388,16 +2386,7 @@ module.exports = { id: "throws-after-import", register() {} };`,
|
||||
search: async () => [],
|
||||
get: async () => null,
|
||||
});
|
||||
registerMemoryPromptSection(() => ["active memory section"]);
|
||||
registerMemoryPromptSupplement("memory-wiki", () => ["active wiki supplement"]);
|
||||
registerMemoryFlushPlanResolver(() => ({
|
||||
softThresholdTokens: 1,
|
||||
forceFlushTranscriptBytes: 2,
|
||||
reserveTokensFloor: 3,
|
||||
prompt: "active",
|
||||
systemPrompt: "active",
|
||||
relativePath: "memory/active.md",
|
||||
}));
|
||||
const activeRuntime = {
|
||||
async getMemorySearchManager() {
|
||||
return { manager: null, error: "active" };
|
||||
@@ -2406,7 +2395,18 @@ module.exports = { id: "throws-after-import", register() {} };`,
|
||||
return { backend: "builtin" as const };
|
||||
},
|
||||
};
|
||||
registerMemoryRuntime(activeRuntime);
|
||||
registerMemoryCapability("memory-core", {
|
||||
promptBuilder: () => ["active memory section"],
|
||||
flushPlanResolver: () => ({
|
||||
softThresholdTokens: 1,
|
||||
forceFlushTranscriptBytes: 2,
|
||||
reserveTokensFloor: 3,
|
||||
prompt: "active",
|
||||
systemPrompt: "active",
|
||||
relativePath: "memory/active.md",
|
||||
}),
|
||||
runtime: activeRuntime,
|
||||
});
|
||||
const plugin = writePlugin({
|
||||
id: "snapshot-memory",
|
||||
filename: "snapshot-memory.cjs",
|
||||
|
||||
@@ -93,9 +93,6 @@ import {
|
||||
import {
|
||||
clearMemoryPluginState,
|
||||
getMemoryCapabilityRegistration,
|
||||
getMemoryFlushPlanResolver,
|
||||
getMemoryPromptSectionBuilder,
|
||||
getMemoryRuntime,
|
||||
listMemoryCorpusSupplements,
|
||||
listMemoryPromptSupplements,
|
||||
restoreMemoryPluginState,
|
||||
@@ -245,10 +242,7 @@ type CachedPluginState = {
|
||||
agentHarnesses: ReturnType<typeof listRegisteredAgentHarnesses>;
|
||||
compactionProviders: ReturnType<typeof listRegisteredCompactionProviders>;
|
||||
memoryEmbeddingProviders: ReturnType<typeof listRegisteredMemoryEmbeddingProviders>;
|
||||
memoryFlushPlanResolver: ReturnType<typeof getMemoryFlushPlanResolver>;
|
||||
memoryPromptBuilder: ReturnType<typeof getMemoryPromptSectionBuilder>;
|
||||
memoryPromptSupplements: ReturnType<typeof listMemoryPromptSupplements>;
|
||||
memoryRuntime: ReturnType<typeof getMemoryRuntime>;
|
||||
};
|
||||
|
||||
const MAX_PLUGIN_REGISTRY_CACHE_ENTRIES = 128;
|
||||
@@ -1487,10 +1481,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
restoreMemoryPluginState({
|
||||
capability: cached.state.memoryCapability,
|
||||
corpusSupplements: cached.state.memoryCorpusSupplements,
|
||||
promptBuilder: cached.state.memoryPromptBuilder,
|
||||
promptSupplements: cached.state.memoryPromptSupplements,
|
||||
flushPlanResolver: cached.state.memoryFlushPlanResolver,
|
||||
runtime: cached.state.memoryRuntime,
|
||||
});
|
||||
activatePluginRegistry(
|
||||
cached.state.registry,
|
||||
@@ -2303,11 +2294,8 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
const previousDetachedTaskRuntimeRegistration = getDetachedTaskLifecycleRuntimeRegistration();
|
||||
const previousMemoryCapability = getMemoryCapabilityRegistration();
|
||||
const previousMemoryEmbeddingProviders = listRegisteredMemoryEmbeddingProviders();
|
||||
const previousMemoryFlushPlanResolver = getMemoryFlushPlanResolver();
|
||||
const previousMemoryPromptBuilder = getMemoryPromptSectionBuilder();
|
||||
const previousMemoryCorpusSupplements = listMemoryCorpusSupplements();
|
||||
const previousMemoryPromptSupplements = listMemoryPromptSupplements();
|
||||
const previousMemoryRuntime = getMemoryRuntime();
|
||||
|
||||
try {
|
||||
withProfile(
|
||||
@@ -2324,10 +2312,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
restoreMemoryPluginState({
|
||||
capability: previousMemoryCapability,
|
||||
corpusSupplements: previousMemoryCorpusSupplements,
|
||||
promptBuilder: previousMemoryPromptBuilder,
|
||||
promptSupplements: previousMemoryPromptSupplements,
|
||||
flushPlanResolver: previousMemoryFlushPlanResolver,
|
||||
runtime: previousMemoryRuntime,
|
||||
});
|
||||
}
|
||||
registry.plugins.push(record);
|
||||
@@ -2342,10 +2327,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
restoreMemoryPluginState({
|
||||
capability: previousMemoryCapability,
|
||||
corpusSupplements: previousMemoryCorpusSupplements,
|
||||
promptBuilder: previousMemoryPromptBuilder,
|
||||
promptSupplements: previousMemoryPromptSupplements,
|
||||
flushPlanResolver: previousMemoryFlushPlanResolver,
|
||||
runtime: previousMemoryRuntime,
|
||||
});
|
||||
recordPluginError({
|
||||
logger,
|
||||
@@ -2413,10 +2395,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
agentHarnesses: listRegisteredAgentHarnesses(),
|
||||
compactionProviders: listRegisteredCompactionProviders(),
|
||||
memoryEmbeddingProviders: listRegisteredMemoryEmbeddingProviders(),
|
||||
memoryFlushPlanResolver: getMemoryFlushPlanResolver(),
|
||||
memoryPromptBuilder: getMemoryPromptSectionBuilder(),
|
||||
memoryPromptSupplements: listMemoryPromptSupplements(),
|
||||
memoryRuntime: getMemoryRuntime(),
|
||||
},
|
||||
onlyPluginIds,
|
||||
);
|
||||
|
||||
@@ -4,8 +4,6 @@ import {
|
||||
buildMemoryPromptSection,
|
||||
clearMemoryPluginState,
|
||||
getMemoryCapabilityRegistration,
|
||||
getMemoryFlushPlanResolver,
|
||||
getMemoryPromptSectionBuilder,
|
||||
getMemoryRuntime,
|
||||
hasMemoryRuntime,
|
||||
listMemoryCorpusSupplements,
|
||||
@@ -54,10 +52,7 @@ function createMemoryStateSnapshot() {
|
||||
return {
|
||||
capability: getMemoryCapabilityRegistration(),
|
||||
corpusSupplements: listMemoryCorpusSupplements(),
|
||||
promptBuilder: getMemoryPromptSectionBuilder(),
|
||||
promptSupplements: listMemoryPromptSupplements(),
|
||||
flushPlanResolver: getMemoryFlushPlanResolver(),
|
||||
runtime: getMemoryRuntime(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -66,16 +61,13 @@ function registerMemoryState(params: {
|
||||
relativePath?: string;
|
||||
runtime?: ReturnType<typeof createMemoryRuntime>;
|
||||
}) {
|
||||
if (params.promptSection) {
|
||||
registerMemoryPromptSection(() => params.promptSection ?? []);
|
||||
}
|
||||
if (params.relativePath) {
|
||||
const relativePath = params.relativePath;
|
||||
registerMemoryFlushPlanResolver(() => createMemoryFlushPlan(relativePath));
|
||||
}
|
||||
if (params.runtime) {
|
||||
registerMemoryRuntime(params.runtime);
|
||||
}
|
||||
registerMemoryCapability("memory-core", {
|
||||
...(params.promptSection ? { promptBuilder: () => params.promptSection ?? [] } : {}),
|
||||
...(params.relativePath
|
||||
? { flushPlanResolver: () => createMemoryFlushPlan(params.relativePath ?? "") }
|
||||
: {}),
|
||||
...(params.runtime ? { runtime: params.runtime } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
describe("memory plugin state", () => {
|
||||
@@ -102,7 +94,22 @@ describe("memory plugin state", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("prefers the registered memory capability over legacy split state", async () => {
|
||||
it("adapts deprecated split registration to the unified memory capability", async () => {
|
||||
const runtime = createMemoryRuntime();
|
||||
|
||||
registerMemoryPromptSection(() => ["legacy prompt"]);
|
||||
registerMemoryFlushPlanResolver(() => createMemoryFlushPlan("memory/legacy.md"));
|
||||
registerMemoryRuntime(runtime);
|
||||
|
||||
expect(buildMemoryPromptSection({ availableTools: new Set() })).toEqual(["legacy prompt"]);
|
||||
expect(resolveMemoryFlushPlan({})?.relativePath).toBe("memory/legacy.md");
|
||||
expect(getMemoryRuntime()).toBe(runtime);
|
||||
expect(getMemoryCapabilityRegistration()).toMatchObject({
|
||||
pluginId: "legacy-memory-v1",
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers the registered memory capability over earlier legacy split state", async () => {
|
||||
const runtime = createMemoryRuntime();
|
||||
|
||||
registerMemoryPromptSection(() => ["legacy prompt"]);
|
||||
|
||||
@@ -137,19 +137,12 @@ export type MemoryPluginCapabilityRegistration = {
|
||||
capability: MemoryPluginCapability;
|
||||
};
|
||||
|
||||
const LEGACY_MEMORY_COMPAT_PLUGIN_ID = "legacy-memory-v1";
|
||||
|
||||
type MemoryPluginState = {
|
||||
capability?: MemoryPluginCapabilityRegistration;
|
||||
corpusSupplements: MemoryCorpusSupplementRegistration[];
|
||||
promptSupplements: MemoryPromptSupplementRegistration[];
|
||||
// LEGACY(memory-v1): kept for external plugins still registering the older
|
||||
// split memory surfaces. Prefer `registerMemoryCapability(...)`.
|
||||
promptBuilder?: MemoryPromptSectionBuilder;
|
||||
// LEGACY(memory-v1): remove after external memory plugins migrate to the
|
||||
// unified capability registration path.
|
||||
flushPlanResolver?: MemoryFlushPlanResolver;
|
||||
// LEGACY(memory-v1): remove after external memory plugins migrate to the
|
||||
// unified capability registration path.
|
||||
runtime?: MemoryPluginRuntime;
|
||||
};
|
||||
|
||||
const memoryPluginState: MemoryPluginState = {
|
||||
@@ -175,6 +168,14 @@ export function registerMemoryCapability(
|
||||
memoryPluginState.capability = { pluginId, capability: { ...capability } };
|
||||
}
|
||||
|
||||
function patchMemoryCapability(pluginId: string, patch: MemoryPluginCapability): void {
|
||||
const current =
|
||||
memoryPluginState.capability?.pluginId === pluginId
|
||||
? memoryPluginState.capability.capability
|
||||
: {};
|
||||
registerMemoryCapability(pluginId, { ...current, ...patch });
|
||||
}
|
||||
|
||||
export function getMemoryCapabilityRegistration(): MemoryPluginCapabilityRegistration | undefined {
|
||||
return memoryPluginState.capability
|
||||
? {
|
||||
@@ -190,7 +191,14 @@ export function listMemoryCorpusSupplements(): MemoryCorpusSupplementRegistratio
|
||||
|
||||
/** @deprecated Use registerMemoryCapability(pluginId, { promptBuilder }) instead. */
|
||||
export function registerMemoryPromptSection(builder: MemoryPromptSectionBuilder): void {
|
||||
memoryPluginState.promptBuilder = builder;
|
||||
registerMemoryPromptSectionForPlugin(LEGACY_MEMORY_COMPAT_PLUGIN_ID, builder);
|
||||
}
|
||||
|
||||
export function registerMemoryPromptSectionForPlugin(
|
||||
pluginId: string,
|
||||
builder: MemoryPromptSectionBuilder,
|
||||
): void {
|
||||
patchMemoryCapability(pluginId, { promptBuilder: builder });
|
||||
}
|
||||
|
||||
export function registerMemoryPromptSupplement(
|
||||
@@ -209,9 +217,7 @@ export function buildMemoryPromptSection(params: {
|
||||
citationsMode?: MemoryCitationsMode;
|
||||
}): string[] {
|
||||
const primary = normalizeMemoryPromptLines(
|
||||
memoryPluginState.capability?.capability.promptBuilder?.(params) ??
|
||||
memoryPluginState.promptBuilder?.(params) ??
|
||||
[],
|
||||
memoryPluginState.capability?.capability.promptBuilder?.(params) ?? [],
|
||||
);
|
||||
const supplements = memoryPluginState.promptSupplements
|
||||
// Keep supplement order stable even if plugin registration order changes.
|
||||
@@ -228,7 +234,7 @@ function normalizeMemoryPromptLines(value: unknown): string[] {
|
||||
}
|
||||
|
||||
export function getMemoryPromptSectionBuilder(): MemoryPromptSectionBuilder | undefined {
|
||||
return memoryPluginState.capability?.capability.promptBuilder ?? memoryPluginState.promptBuilder;
|
||||
return memoryPluginState.capability?.capability.promptBuilder;
|
||||
}
|
||||
|
||||
export function listMemoryPromptSupplements(): MemoryPromptSupplementRegistration[] {
|
||||
@@ -237,34 +243,41 @@ export function listMemoryPromptSupplements(): MemoryPromptSupplementRegistratio
|
||||
|
||||
/** @deprecated Use registerMemoryCapability(pluginId, { flushPlanResolver }) instead. */
|
||||
export function registerMemoryFlushPlanResolver(resolver: MemoryFlushPlanResolver): void {
|
||||
memoryPluginState.flushPlanResolver = resolver;
|
||||
registerMemoryFlushPlanResolverForPlugin(LEGACY_MEMORY_COMPAT_PLUGIN_ID, resolver);
|
||||
}
|
||||
|
||||
export function registerMemoryFlushPlanResolverForPlugin(
|
||||
pluginId: string,
|
||||
resolver: MemoryFlushPlanResolver,
|
||||
): void {
|
||||
patchMemoryCapability(pluginId, { flushPlanResolver: resolver });
|
||||
}
|
||||
|
||||
export function resolveMemoryFlushPlan(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
nowMs?: number;
|
||||
}): MemoryFlushPlan | null {
|
||||
return (
|
||||
memoryPluginState.capability?.capability.flushPlanResolver?.(params) ??
|
||||
memoryPluginState.flushPlanResolver?.(params) ??
|
||||
null
|
||||
);
|
||||
return memoryPluginState.capability?.capability.flushPlanResolver?.(params) ?? null;
|
||||
}
|
||||
|
||||
export function getMemoryFlushPlanResolver(): MemoryFlushPlanResolver | undefined {
|
||||
return (
|
||||
memoryPluginState.capability?.capability.flushPlanResolver ??
|
||||
memoryPluginState.flushPlanResolver
|
||||
);
|
||||
return memoryPluginState.capability?.capability.flushPlanResolver;
|
||||
}
|
||||
|
||||
/** @deprecated Use registerMemoryCapability(pluginId, { runtime }) instead. */
|
||||
export function registerMemoryRuntime(runtime: MemoryPluginRuntime): void {
|
||||
memoryPluginState.runtime = runtime;
|
||||
registerMemoryRuntimeForPlugin(LEGACY_MEMORY_COMPAT_PLUGIN_ID, runtime);
|
||||
}
|
||||
|
||||
export function registerMemoryRuntimeForPlugin(
|
||||
pluginId: string,
|
||||
runtime: MemoryPluginRuntime,
|
||||
): void {
|
||||
patchMemoryCapability(pluginId, { runtime });
|
||||
}
|
||||
|
||||
export function getMemoryRuntime(): MemoryPluginRuntime | undefined {
|
||||
return memoryPluginState.capability?.capability.runtime ?? memoryPluginState.runtime;
|
||||
return memoryPluginState.capability?.capability.runtime;
|
||||
}
|
||||
|
||||
export function hasMemoryRuntime(): boolean {
|
||||
@@ -318,19 +331,13 @@ export function restoreMemoryPluginState(state: MemoryPluginState): void {
|
||||
}
|
||||
: undefined;
|
||||
memoryPluginState.corpusSupplements = [...state.corpusSupplements];
|
||||
memoryPluginState.promptBuilder = state.promptBuilder;
|
||||
memoryPluginState.promptSupplements = [...state.promptSupplements];
|
||||
memoryPluginState.flushPlanResolver = state.flushPlanResolver;
|
||||
memoryPluginState.runtime = state.runtime;
|
||||
}
|
||||
|
||||
export function clearMemoryPluginState(): void {
|
||||
memoryPluginState.capability = undefined;
|
||||
memoryPluginState.corpusSupplements = [];
|
||||
memoryPluginState.promptBuilder = undefined;
|
||||
memoryPluginState.promptSupplements = [];
|
||||
memoryPluginState.flushPlanResolver = undefined;
|
||||
memoryPluginState.runtime = undefined;
|
||||
}
|
||||
|
||||
export const _resetMemoryPluginState = clearMemoryPluginState;
|
||||
|
||||
@@ -92,10 +92,10 @@ import {
|
||||
import {
|
||||
registerMemoryCapability,
|
||||
registerMemoryCorpusSupplement,
|
||||
registerMemoryFlushPlanResolver,
|
||||
registerMemoryFlushPlanResolverForPlugin,
|
||||
registerMemoryPromptSupplement,
|
||||
registerMemoryPromptSection,
|
||||
registerMemoryRuntime,
|
||||
registerMemoryPromptSectionForPlugin,
|
||||
registerMemoryRuntimeForPlugin,
|
||||
} from "./memory-state.js";
|
||||
import { normalizeRegisteredProvider } from "./provider-validation.js";
|
||||
import { createEmptyPluginRegistry } from "./registry-empty.js";
|
||||
@@ -2380,7 +2380,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
registerMemoryPromptSection(builder);
|
||||
registerMemoryPromptSectionForPlugin(record.id, builder);
|
||||
},
|
||||
registerMemoryPromptSupplement: (builder) => {
|
||||
if (typeof builder !== "function") {
|
||||
@@ -2415,7 +2415,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
registerMemoryFlushPlanResolver(resolver);
|
||||
registerMemoryFlushPlanResolverForPlugin(record.id, resolver);
|
||||
},
|
||||
registerMemoryRuntime: (runtime) => {
|
||||
if (!hasKind(record.kind, "memory")) {
|
||||
@@ -2435,7 +2435,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
registerMemoryRuntime(runtime);
|
||||
registerMemoryRuntimeForPlugin(record.id, runtime);
|
||||
},
|
||||
registerMemoryEmbeddingProvider: (adapter) => {
|
||||
if (hasKind(record.kind, "memory")) {
|
||||
|
||||
Reference in New Issue
Block a user