diff --git a/packages/memory-host-sdk/src/host/embeddings-gemini.test.ts b/packages/memory-host-sdk/src/host/embeddings-gemini.test.ts index 3a6566ebf76..f1401dcf412 100644 --- a/packages/memory-host-sdk/src/host/embeddings-gemini.test.ts +++ b/packages/memory-host-sdk/src/host/embeddings-gemini.test.ts @@ -20,10 +20,20 @@ vi.mock("../../../../src/infra/net/fetch-guard.js", () => ({ }, })); -vi.mock("../../../../src/agents/model-auth.js", async () => { - const { createModelAuthMockModule } = - await import("../../../../src/test-utils/model-auth-mock.js"); - return createModelAuthMockModule(); +const { resolveApiKeyForProviderMock } = vi.hoisted(() => ({ + resolveApiKeyForProviderMock: vi.fn(), +})); + +vi.mock("../../../../src/agents/model-auth.js", () => { + return { + resolveApiKeyForProvider: resolveApiKeyForProviderMock, + requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => { + if (auth.apiKey) { + return auth.apiKey; + } + throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth.mode}).`); + }, + }; }); const createGeminiFetchMock = (embeddingValues = [1, 2, 3]) => diff --git a/packages/memory-host-sdk/src/host/embeddings-voyage.test.ts b/packages/memory-host-sdk/src/host/embeddings-voyage.test.ts index e3d7ef2782a..2615b8cdb5e 100644 --- a/packages/memory-host-sdk/src/host/embeddings-voyage.test.ts +++ b/packages/memory-host-sdk/src/host/embeddings-voyage.test.ts @@ -22,10 +22,20 @@ vi.mock("../../../../src/infra/net/fetch-guard.js", () => ({ }, })); -vi.mock("../../../../src/agents/model-auth.js", async () => { - const { createModelAuthMockModule } = - await import("../../../../src/test-utils/model-auth-mock.js"); - return createModelAuthMockModule(); +const { resolveApiKeyForProviderMock } = vi.hoisted(() => ({ + resolveApiKeyForProviderMock: vi.fn(), +})); + +vi.mock("../../../../src/agents/model-auth.js", () => { + return { + resolveApiKeyForProvider: resolveApiKeyForProviderMock, + requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => { + if (auth.apiKey) { + return auth.apiKey; + } + throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth.mode}).`); + }, + }; }); const createFetchMock = () => { diff --git a/packages/memory-host-sdk/src/host/embeddings.test.ts b/packages/memory-host-sdk/src/host/embeddings.test.ts index 1efb4e05e21..fe1d5abdac3 100644 --- a/packages/memory-host-sdk/src/host/embeddings.test.ts +++ b/packages/memory-host-sdk/src/host/embeddings.test.ts @@ -4,10 +4,20 @@ import { createEmbeddingProvider, DEFAULT_LOCAL_MODEL } from "./embeddings.js"; import * as nodeLlamaModule from "./node-llama.js"; import { mockPublicPinnedHostname } from "./test-helpers/ssrf.js"; -vi.mock("../../../../src/agents/model-auth.js", async () => { - const { createModelAuthMockModule } = - await import("../../../../src/test-utils/model-auth-mock.js"); - return createModelAuthMockModule(); +const { resolveApiKeyForProviderMock } = vi.hoisted(() => ({ + resolveApiKeyForProviderMock: vi.fn(), +})); + +vi.mock("../../../../src/agents/model-auth.js", () => { + return { + resolveApiKeyForProvider: resolveApiKeyForProviderMock, + requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => { + if (auth.apiKey) { + return auth.apiKey; + } + throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth.mode}).`); + }, + }; }); vi.mock("../../../../src/infra/net/fetch-guard.js", () => ({ diff --git a/src/image-generation/provider-registry.test.ts b/src/image-generation/provider-registry.test.ts index ceca9e374a6..d8f06a86fc7 100644 --- a/src/image-generation/provider-registry.test.ts +++ b/src/image-generation/provider-registry.test.ts @@ -1,19 +1,33 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { createEmptyPluginRegistry } from "../plugins/registry.js"; +import type { ImageGenerationProviderPlugin } from "../plugins/types.js"; -const { resolveRuntimePluginRegistryMock } = vi.hoisted(() => ({ - resolveRuntimePluginRegistryMock: vi.fn< - (params?: unknown) => ReturnType | undefined - >(() => undefined), +const { resolvePluginCapabilityProvidersMock } = vi.hoisted(() => ({ + resolvePluginCapabilityProvidersMock: vi.fn<() => ImageGenerationProviderPlugin[]>(() => []), })); -vi.mock("../plugins/loader.js", () => ({ - resolveRuntimePluginRegistry: resolveRuntimePluginRegistryMock, +vi.mock("../plugins/capability-provider-runtime.js", () => ({ + resolvePluginCapabilityProviders: resolvePluginCapabilityProvidersMock, })); let getImageGenerationProvider: typeof import("./provider-registry.js").getImageGenerationProvider; let listImageGenerationProviders: typeof import("./provider-registry.js").listImageGenerationProviders; +function createProvider( + params: Pick & Partial, +): ImageGenerationProviderPlugin { + return { + label: params.id, + capabilities: { + generate: {}, + edit: { enabled: false }, + }, + generateImage: async () => ({ + images: [{ buffer: Buffer.from("image"), mimeType: "image/png" }], + }), + ...params, + }; +} + describe("image-generation provider registry", () => { beforeAll(async () => { ({ getImageGenerationProvider, listImageGenerationProviders } = @@ -21,78 +35,35 @@ describe("image-generation provider registry", () => { }); beforeEach(() => { - resolveRuntimePluginRegistryMock.mockReset(); - resolveRuntimePluginRegistryMock.mockReturnValue(undefined); + resolvePluginCapabilityProvidersMock.mockReset(); + resolvePluginCapabilityProvidersMock.mockReturnValue([]); }); - it("does not load plugins when listing without config", () => { + it("delegates provider resolution to the capability provider boundary", () => { expect(listImageGenerationProviders()).toEqual([]); - expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(); + expect(resolvePluginCapabilityProvidersMock).toHaveBeenCalledWith({ + key: "imageGenerationProviders", + cfg: undefined, + }); }); it("uses active plugin providers without loading from disk", () => { - const registry = createEmptyPluginRegistry(); - registry.imageGenerationProviders.push({ - pluginId: "custom-image", - pluginName: "Custom Image", - source: "test", - provider: { - id: "custom-image", - label: "Custom Image", - capabilities: { - generate: {}, - edit: { enabled: false }, - }, - generateImage: async () => ({ - images: [{ buffer: Buffer.from("image"), mimeType: "image/png" }], - }), - }, - }); - resolveRuntimePluginRegistryMock.mockReturnValue(registry); + resolvePluginCapabilityProvidersMock.mockReturnValue([createProvider({ id: "custom-image" })]); const provider = getImageGenerationProvider("custom-image"); expect(provider?.id).toBe("custom-image"); - expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(); + expect(resolvePluginCapabilityProvidersMock).toHaveBeenCalledWith({ + key: "imageGenerationProviders", + cfg: undefined, + }); }); it("ignores prototype-like provider ids and aliases", () => { - const registry = createEmptyPluginRegistry(); - registry.imageGenerationProviders.push( - { - pluginId: "blocked-image", - pluginName: "Blocked Image", - source: "test", - provider: { - id: "__proto__", - aliases: ["constructor", "prototype"], - capabilities: { - generate: {}, - edit: { enabled: false }, - }, - generateImage: async () => ({ - images: [{ buffer: Buffer.from("image"), mimeType: "image/png" }], - }), - }, - }, - { - pluginId: "safe-image", - pluginName: "Safe Image", - source: "test", - provider: { - id: "safe-image", - aliases: ["safe-alias", "constructor"], - capabilities: { - generate: {}, - edit: { enabled: false }, - }, - generateImage: async () => ({ - images: [{ buffer: Buffer.from("image"), mimeType: "image/png" }], - }), - }, - }, - ); - resolveRuntimePluginRegistryMock.mockReturnValue(registry); + resolvePluginCapabilityProvidersMock.mockReturnValue([ + createProvider({ id: "__proto__", aliases: ["constructor", "prototype"] }), + createProvider({ id: "safe-image", aliases: ["safe-alias", "constructor"] }), + ]); expect(listImageGenerationProviders().map((provider) => provider.id)).toEqual(["safe-image"]); expect(getImageGenerationProvider("__proto__")).toBeUndefined(); diff --git a/src/memory-host-sdk/host/embeddings-gemini.test.ts b/src/memory-host-sdk/host/embeddings-gemini.test.ts index c0474ac01fe..f5d91b56215 100644 --- a/src/memory-host-sdk/host/embeddings-gemini.test.ts +++ b/src/memory-host-sdk/host/embeddings-gemini.test.ts @@ -20,9 +20,20 @@ import { type JsonFetchMock, } from "./embeddings-provider.test-support.js"; -vi.mock("../../agents/model-auth.js", async () => { - const { createModelAuthMockModule } = await import("../../test-utils/model-auth-mock.js"); - return createModelAuthMockModule(); +const { resolveApiKeyForProviderMock } = vi.hoisted(() => ({ + resolveApiKeyForProviderMock: vi.fn(), +})); + +vi.mock("../../agents/model-auth.js", () => { + return { + resolveApiKeyForProvider: resolveApiKeyForProviderMock, + requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => { + if (auth.apiKey) { + return auth.apiKey; + } + throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth.mode}).`); + }, + }; }); beforeEach(() => { diff --git a/src/memory-host-sdk/host/embeddings-voyage.test.ts b/src/memory-host-sdk/host/embeddings-voyage.test.ts index 6ca0d99fed2..9122861513e 100644 --- a/src/memory-host-sdk/host/embeddings-voyage.test.ts +++ b/src/memory-host-sdk/host/embeddings-voyage.test.ts @@ -9,9 +9,20 @@ import { } from "./embeddings-provider.test-support.js"; import { mockPublicPinnedHostname } from "./test-helpers/ssrf.js"; -vi.mock("../../agents/model-auth.js", async () => { - const { createModelAuthMockModule } = await import("../../test-utils/model-auth-mock.js"); - return createModelAuthMockModule(); +const { resolveApiKeyForProviderMock } = vi.hoisted(() => ({ + resolveApiKeyForProviderMock: vi.fn(), +})); + +vi.mock("../../agents/model-auth.js", () => { + return { + resolveApiKeyForProvider: resolveApiKeyForProviderMock, + requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => { + if (auth.apiKey) { + return auth.apiKey; + } + throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth.mode}).`); + }, + }; }); let createVoyageEmbeddingProvider: typeof import("./embeddings-voyage.js").createVoyageEmbeddingProvider; diff --git a/src/memory-host-sdk/host/embeddings.test.ts b/src/memory-host-sdk/host/embeddings.test.ts index e45c1088892..94e31566bec 100644 --- a/src/memory-host-sdk/host/embeddings.test.ts +++ b/src/memory-host-sdk/host/embeddings.test.ts @@ -17,6 +17,7 @@ const { bedrockSendMock, createOllamaEmbeddingProviderMock, defaultProviderMock, + resolveApiKeyForProviderMock, resolveCredentialsMock, } = vi.hoisted(() => ({ bedrockSendMock: vi.fn(), @@ -25,11 +26,19 @@ const { }), defaultProviderMock: vi.fn(), resolveCredentialsMock: vi.fn(), + resolveApiKeyForProviderMock: vi.fn(), })); -vi.mock("../../agents/model-auth.js", async () => { - const { createModelAuthMockModule } = await import("../../test-utils/model-auth-mock.js"); - return createModelAuthMockModule(); +vi.mock("../../agents/model-auth.js", () => { + return { + resolveApiKeyForProvider: resolveApiKeyForProviderMock, + requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => { + if (auth.apiKey) { + return auth.apiKey; + } + throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth.mode}).`); + }, + }; }); vi.mock("./embeddings-ollama.js", () => ({ diff --git a/src/video-generation/provider-registry.test.ts b/src/video-generation/provider-registry.test.ts index 866d491df6f..f7931f8e16c 100644 --- a/src/video-generation/provider-registry.test.ts +++ b/src/video-generation/provider-registry.test.ts @@ -1,19 +1,30 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { createEmptyPluginRegistry } from "../plugins/registry.js"; +import type { VideoGenerationProviderPlugin } from "../plugins/types.js"; -const { resolveRuntimePluginRegistryMock } = vi.hoisted(() => ({ - resolveRuntimePluginRegistryMock: vi.fn< - (params?: unknown) => ReturnType | undefined - >(() => undefined), +const { resolvePluginCapabilityProvidersMock } = vi.hoisted(() => ({ + resolvePluginCapabilityProvidersMock: vi.fn<() => VideoGenerationProviderPlugin[]>(() => []), })); -vi.mock("../plugins/loader.js", () => ({ - resolveRuntimePluginRegistry: resolveRuntimePluginRegistryMock, +vi.mock("../plugins/capability-provider-runtime.js", () => ({ + resolvePluginCapabilityProviders: resolvePluginCapabilityProvidersMock, })); let getVideoGenerationProvider: typeof import("./provider-registry.js").getVideoGenerationProvider; let listVideoGenerationProviders: typeof import("./provider-registry.js").listVideoGenerationProviders; +function createProvider( + params: Pick & Partial, +): VideoGenerationProviderPlugin { + return { + label: params.id, + capabilities: {}, + generateVideo: async () => ({ + videos: [{ buffer: Buffer.from("video"), mimeType: "video/mp4" }], + }), + ...params, + }; +} + describe("video-generation provider registry", () => { beforeAll(async () => { ({ getVideoGenerationProvider, listVideoGenerationProviders } = @@ -21,69 +32,35 @@ describe("video-generation provider registry", () => { }); beforeEach(() => { - resolveRuntimePluginRegistryMock.mockReset(); - resolveRuntimePluginRegistryMock.mockReturnValue(undefined); + resolvePluginCapabilityProvidersMock.mockReset(); + resolvePluginCapabilityProvidersMock.mockReturnValue([]); }); - it("does not load plugins when listing without config", () => { + it("delegates provider resolution to the capability provider boundary", () => { expect(listVideoGenerationProviders()).toEqual([]); - expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(); + expect(resolvePluginCapabilityProvidersMock).toHaveBeenCalledWith({ + key: "videoGenerationProviders", + cfg: undefined, + }); }); it("uses active plugin providers without loading from disk", () => { - const registry = createEmptyPluginRegistry(); - registry.videoGenerationProviders.push({ - pluginId: "custom-video", - pluginName: "Custom Video", - source: "test", - provider: { - id: "custom-video", - label: "Custom Video", - capabilities: {}, - generateVideo: async () => ({ - videos: [{ buffer: Buffer.from("video"), mimeType: "video/mp4" }], - }), - }, - }); - resolveRuntimePluginRegistryMock.mockReturnValue(registry); + resolvePluginCapabilityProvidersMock.mockReturnValue([createProvider({ id: "custom-video" })]); const provider = getVideoGenerationProvider("custom-video"); expect(provider?.id).toBe("custom-video"); - expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith(); + expect(resolvePluginCapabilityProvidersMock).toHaveBeenCalledWith({ + key: "videoGenerationProviders", + cfg: undefined, + }); }); it("ignores prototype-like provider ids and aliases", () => { - const registry = createEmptyPluginRegistry(); - registry.videoGenerationProviders.push( - { - pluginId: "blocked-video", - pluginName: "Blocked Video", - source: "test", - provider: { - id: "__proto__", - aliases: ["constructor", "prototype"], - capabilities: {}, - generateVideo: async () => ({ - videos: [{ buffer: Buffer.from("video"), mimeType: "video/mp4" }], - }), - }, - }, - { - pluginId: "safe-video", - pluginName: "Safe Video", - source: "test", - provider: { - id: "safe-video", - aliases: ["safe-alias", "constructor"], - capabilities: {}, - generateVideo: async () => ({ - videos: [{ buffer: Buffer.from("video"), mimeType: "video/mp4" }], - }), - }, - }, - ); - resolveRuntimePluginRegistryMock.mockReturnValue(registry); + resolvePluginCapabilityProvidersMock.mockReturnValue([ + createProvider({ id: "__proto__", aliases: ["constructor", "prototype"] }), + createProvider({ id: "safe-video", aliases: ["safe-alias", "constructor"] }), + ]); expect(listVideoGenerationProviders().map((provider) => provider.id)).toEqual(["safe-video"]); expect(getVideoGenerationProvider("__proto__")).toBeUndefined();