import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { clearMemoryEmbeddingProviders, registerMemoryEmbeddingProvider, type MemoryEmbeddingProviderAdapter, } from "./memory-embedding-providers.js"; const mocks = vi.hoisted(() => ({ resolvePluginCapabilityProviders: vi.fn< typeof import("./capability-provider-runtime.js").resolvePluginCapabilityProviders >(() => []), resolvePluginCapabilityProvider: vi.fn< typeof import("./capability-provider-runtime.js").resolvePluginCapabilityProvider >(() => undefined), })); vi.mock("./capability-provider-runtime.js", () => ({ resolvePluginCapabilityProvider: mocks.resolvePluginCapabilityProvider, resolvePluginCapabilityProviders: mocks.resolvePluginCapabilityProviders, })); let runtimeModule: typeof import("./memory-embedding-provider-runtime.js"); function createCapabilityAdapter(id: string): MemoryEmbeddingProviderAdapter { return { id, create: async () => ({ provider: null }), }; } beforeEach(async () => { clearMemoryEmbeddingProviders(); mocks.resolvePluginCapabilityProviders.mockReset(); mocks.resolvePluginCapabilityProviders.mockReturnValue([]); mocks.resolvePluginCapabilityProvider.mockReset(); mocks.resolvePluginCapabilityProvider.mockReturnValue(undefined); runtimeModule = await import("./memory-embedding-provider-runtime.js"); }); afterEach(() => { clearMemoryEmbeddingProviders(); }); describe("memory embedding provider runtime resolution", () => { it("merges registered and declared capability fallback adapters", () => { registerMemoryEmbeddingProvider({ id: "registered", create: async () => ({ provider: null }), }); mocks.resolvePluginCapabilityProviders.mockReturnValue([createCapabilityAdapter("capability")]); expect(runtimeModule.listMemoryEmbeddingProviders().map((adapter) => adapter.id)).toEqual([ "registered", "capability", ]); expect(runtimeModule.getMemoryEmbeddingProvider("registered")?.id).toBe("registered"); expect(mocks.resolvePluginCapabilityProviders).toHaveBeenCalledTimes(1); }); it("falls back to declared capability adapters when the registry is cold", () => { mocks.resolvePluginCapabilityProviders.mockReturnValue([createCapabilityAdapter("ollama")]); mocks.resolvePluginCapabilityProvider.mockReturnValue(createCapabilityAdapter("ollama")); expect(runtimeModule.listMemoryEmbeddingProviders().map((adapter) => adapter.id)).toEqual([ "ollama", ]); expect(runtimeModule.getMemoryEmbeddingProvider("ollama")?.id).toBe("ollama"); expect(mocks.resolvePluginCapabilityProviders).toHaveBeenCalledTimes(1); expect(mocks.resolvePluginCapabilityProvider).toHaveBeenCalledWith({ key: "memoryEmbeddingProviders", providerId: "ollama", cfg: undefined, }); }); it("uses a configured provider api as the memory adapter owner", () => { const ollamaAdapter = createCapabilityAdapter("ollama"); mocks.resolvePluginCapabilityProvider.mockImplementation(({ providerId }) => providerId === "ollama" ? ollamaAdapter : undefined, ); expect( runtimeModule.getMemoryEmbeddingProvider("ollama-5080", { models: { providers: { "ollama-5080": { api: "ollama", baseUrl: "http://10.0.0.8:11435", models: [], }, }, }, } as never), ).toBe(ollamaAdapter); expect(mocks.resolvePluginCapabilityProvider).toHaveBeenCalledWith({ key: "memoryEmbeddingProviders", providerId: "ollama-5080", cfg: expect.any(Object), }); expect(mocks.resolvePluginCapabilityProvider).toHaveBeenCalledWith({ key: "memoryEmbeddingProviders", providerId: "ollama", cfg: expect.any(Object), }); }); it("uses registered adapters through a configured provider api", () => { const ollamaAdapter = createCapabilityAdapter("ollama"); registerMemoryEmbeddingProvider(ollamaAdapter); expect( runtimeModule.getMemoryEmbeddingProvider("ollama-gpu1", { models: { providers: { "ollama-gpu1": { api: "ollama", baseUrl: "http://ollama-host:11435", models: [], }, }, }, } as never), ).toBe(ollamaAdapter); expect(mocks.resolvePluginCapabilityProvider).not.toHaveBeenCalled(); }); it("prefers registered adapters over declared capability fallback adapters with the same id", () => { const registered = { id: "openai", create: async () => ({ provider: null }), } satisfies MemoryEmbeddingProviderAdapter; registerMemoryEmbeddingProvider({ ...registered, }); mocks.resolvePluginCapabilityProviders.mockReturnValue([createCapabilityAdapter("openai")]); expect(runtimeModule.getMemoryEmbeddingProvider("openai")).toEqual( expect.objectContaining({ id: "openai" }), ); expect(runtimeModule.listMemoryEmbeddingProviders().map((adapter) => adapter.id)).toEqual([ "openai", ]); expect(mocks.resolvePluginCapabilityProviders).toHaveBeenCalledTimes(1); }); });