From 4ae1599ea5a01adaa4c8da34d5057f61000b2de1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 6 Apr 2026 22:04:03 +0100 Subject: [PATCH] perf: extract memory adapter registration helper --- .../provider-adapter-registration.test.ts | 40 ++++++++++++ .../memory/provider-adapter-registration.ts | 11 ++++ .../src/memory/provider-adapters.test.ts | 63 ------------------- .../src/memory/provider-adapters.ts | 12 ++-- 4 files changed, 56 insertions(+), 70 deletions(-) create mode 100644 extensions/memory-core/src/memory/provider-adapter-registration.test.ts create mode 100644 extensions/memory-core/src/memory/provider-adapter-registration.ts delete mode 100644 extensions/memory-core/src/memory/provider-adapters.test.ts diff --git a/extensions/memory-core/src/memory/provider-adapter-registration.test.ts b/extensions/memory-core/src/memory/provider-adapter-registration.test.ts new file mode 100644 index 00000000000..1903422e88a --- /dev/null +++ b/extensions/memory-core/src/memory/provider-adapter-registration.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from "vitest"; +import { filterUnregisteredMemoryEmbeddingProviderAdapters } from "./provider-adapter-registration.js"; + +describe("filterUnregisteredMemoryEmbeddingProviderAdapters", () => { + it("keeps builtin adapters that are not already registered", () => { + const adapters = filterUnregisteredMemoryEmbeddingProviderAdapters({ + builtinAdapters: [ + { id: "local" }, + { id: "openai" }, + { id: "gemini" }, + { id: "voyage" }, + { id: "mistral" }, + ], + registeredAdapters: [], + }); + + expect(adapters.map((adapter) => adapter.id)).toEqual([ + "local", + "openai", + "gemini", + "voyage", + "mistral", + ]); + }); + + it("skips builtin adapters that are already registered", () => { + const adapters = filterUnregisteredMemoryEmbeddingProviderAdapters({ + builtinAdapters: [ + { id: "local" }, + { id: "openai" }, + { id: "gemini" }, + { id: "voyage" }, + { id: "mistral" }, + ], + registeredAdapters: [{ id: "local" }, { id: "gemini" }], + }); + + expect(adapters.map((adapter) => adapter.id)).toEqual(["openai", "voyage", "mistral"]); + }); +}); diff --git a/extensions/memory-core/src/memory/provider-adapter-registration.ts b/extensions/memory-core/src/memory/provider-adapter-registration.ts new file mode 100644 index 00000000000..b62a4661c98 --- /dev/null +++ b/extensions/memory-core/src/memory/provider-adapter-registration.ts @@ -0,0 +1,11 @@ +type AdapterLike = { + id: string; +}; + +export function filterUnregisteredMemoryEmbeddingProviderAdapters(params: { + builtinAdapters: readonly T[]; + registeredAdapters: readonly AdapterLike[]; +}): T[] { + const existingIds = new Set(params.registeredAdapters.map((adapter) => adapter.id)); + return params.builtinAdapters.filter((adapter) => !existingIds.has(adapter.id)); +} diff --git a/extensions/memory-core/src/memory/provider-adapters.test.ts b/extensions/memory-core/src/memory/provider-adapters.test.ts deleted file mode 100644 index 512ffc10b24..00000000000 --- a/extensions/memory-core/src/memory/provider-adapters.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { MemoryEmbeddingProviderAdapter } from "openclaw/plugin-sdk/memory-core-host-engine-embeddings"; -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { registerBuiltInMemoryEmbeddingProviders } from "./provider-adapters.js"; - -const mocks = vi.hoisted(() => ({ - listRegisteredMemoryEmbeddingProviderAdapters: vi.fn<() => MemoryEmbeddingProviderAdapter[]>( - () => [], - ), - listMemoryEmbeddingProviders: vi.fn(() => { - throw new Error("fallback capability loading should stay cold during memory-core register"); - }), -})); - -vi.mock("openclaw/plugin-sdk/memory-core-host-engine-embeddings", async () => { - const actual = await vi.importActual< - typeof import("openclaw/plugin-sdk/memory-core-host-engine-embeddings") - >("openclaw/plugin-sdk/memory-core-host-engine-embeddings"); - return { - ...actual, - listRegisteredMemoryEmbeddingProviderAdapters: - mocks.listRegisteredMemoryEmbeddingProviderAdapters, - listMemoryEmbeddingProviders: mocks.listMemoryEmbeddingProviders, - }; -}); - -beforeEach(() => { - mocks.listRegisteredMemoryEmbeddingProviderAdapters.mockReset(); - mocks.listRegisteredMemoryEmbeddingProviderAdapters.mockReturnValue([]); - mocks.listMemoryEmbeddingProviders.mockClear(); -}); - -describe("registerBuiltInMemoryEmbeddingProviders", () => { - it("uses only already-registered providers when avoiding duplicates", () => { - const ids: string[] = []; - - registerBuiltInMemoryEmbeddingProviders({ - registerMemoryEmbeddingProvider(adapter) { - ids.push(adapter.id); - }, - }); - - expect(ids).toEqual(["local", "openai", "gemini", "voyage", "mistral"]); - expect(mocks.listRegisteredMemoryEmbeddingProviderAdapters).toHaveBeenCalledTimes(1); - expect(mocks.listMemoryEmbeddingProviders).not.toHaveBeenCalled(); - }); - - it("skips builtin adapters that are already registered in the current load", () => { - mocks.listRegisteredMemoryEmbeddingProviderAdapters.mockReturnValue([ - { id: "local", create: vi.fn() } as MemoryEmbeddingProviderAdapter, - { id: "gemini", create: vi.fn() } as MemoryEmbeddingProviderAdapter, - ]); - const ids: string[] = []; - - registerBuiltInMemoryEmbeddingProviders({ - registerMemoryEmbeddingProvider(adapter) { - ids.push(adapter.id); - }, - }); - - expect(ids).toEqual(["openai", "voyage", "mistral"]); - expect(mocks.listMemoryEmbeddingProviders).not.toHaveBeenCalled(); - }); -}); diff --git a/extensions/memory-core/src/memory/provider-adapters.ts b/extensions/memory-core/src/memory/provider-adapters.ts index 45a6a757b1d..7fdc81964e0 100644 --- a/extensions/memory-core/src/memory/provider-adapters.ts +++ b/extensions/memory-core/src/memory/provider-adapters.ts @@ -21,6 +21,7 @@ import { } from "openclaw/plugin-sdk/memory-core-host-engine-embeddings"; import { resolveUserPath } from "openclaw/plugin-sdk/memory-core-host-engine-foundation"; import { getProviderEnvVars } from "openclaw/plugin-sdk/provider-env-vars"; +import { filterUnregisteredMemoryEmbeddingProviderAdapters } from "./provider-adapter-registration.js"; export type BuiltinMemoryEmbeddingProviderDoctorMetadata = { providerId: string; @@ -337,13 +338,10 @@ export function registerBuiltInMemoryEmbeddingProviders(register: { // Only inspect providers already registered in the current load. Falling back // to capability discovery here can recursively trigger plugin loading while // memory-core itself is still registering. - const existingIds = new Set( - listRegisteredMemoryEmbeddingProviderAdapters().map((adapter) => adapter.id), - ); - for (const adapter of builtinMemoryEmbeddingProviderAdapters) { - if (existingIds.has(adapter.id)) { - continue; - } + for (const adapter of filterUnregisteredMemoryEmbeddingProviderAdapters({ + builtinAdapters: builtinMemoryEmbeddingProviderAdapters, + registeredAdapters: listRegisteredMemoryEmbeddingProviderAdapters(), + })) { register.registerMemoryEmbeddingProvider(adapter); } }