

























@@ -24,8 +24,10 @@ import { installTmpDirHarness } from "./test-helpers.js";
2424const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "test-key";
2525type MemoryPluginTestConfig = {
2626embedding?: {
27+provider?: string;
2728apiKey?: string;
2829model?: string;
30+baseUrl?: string;
2931dimensions?: number;
3032};
3133dbPath?: string;
@@ -144,13 +146,17 @@ describe("memory plugin e2e", () => {
144146delete process.env.TEST_MEMORY_API_KEY;
145147});
146148147-test("config schema rejects missing apiKey", async () => {
148-expect(() => {
149-memoryPlugin.configSchema?.parse?.({
150-embedding: {},
151-dbPath: getDbPath(),
152-});
153-}).toThrow("embedding.apiKey is required");
149+test("config schema accepts provider-backed embeddings without apiKey", async () => {
150+const config = memoryPlugin.configSchema?.parse?.({
151+embedding: {
152+provider: "openai",
153+},
154+dbPath: getDbPath(),
155+}) as MemoryPluginTestConfig | undefined;
156+157+expect(config?.embedding?.provider).toBe("openai");
158+expect(config?.embedding?.apiKey).toBeUndefined();
159+expect(config?.embedding?.model).toBe("text-embedding-3-small");
154160});
155161156162test("config schema validates captureMaxChars range", async () => {
@@ -232,6 +238,121 @@ describe("memory plugin e2e", () => {
232238expect(on).not.toHaveBeenCalledWith("before_agent_start", expect.any(Function));
233239});
234240241+test("uses provider adapter auth when embedding apiKey is omitted", async () => {
242+const embedQuery = vi.fn(async () => [0.1, 0.2, 0.3]);
243+const createProvider = vi.fn(async (options: Record<string, unknown>) => ({
244+provider: {
245+id: "openai",
246+model: options.model,
247+ embedQuery,
248+embedBatch: vi.fn(async () => [[0.1, 0.2, 0.3]]),
249+},
250+}));
251+const getMemoryEmbeddingProvider = vi.fn(() => ({
252+id: "openai",
253+create: createProvider,
254+}));
255+const toArray = vi.fn(async () => []);
256+const limit = vi.fn(() => ({ toArray }));
257+const vectorSearch = vi.fn(() => ({ limit }));
258+const loadLanceDbModule = vi.fn(async () => ({
259+connect: vi.fn(async () => ({
260+tableNames: vi.fn(async () => ["memories"]),
261+openTable: vi.fn(async () => ({
262+ vectorSearch,
263+countRows: vi.fn(async () => 0),
264+add: vi.fn(async () => undefined),
265+delete: vi.fn(async () => undefined),
266+})),
267+})),
268+}));
269+270+vi.resetModules();
271+vi.doMock("openclaw/plugin-sdk/memory-core-host-engine-embeddings", () => ({
272+ getMemoryEmbeddingProvider,
273+}));
274+vi.doMock("openai", () => ({
275+default: function UnexpectedOpenAI() {
276+throw new Error("direct OpenAI client should not be constructed");
277+},
278+}));
279+vi.doMock("./lancedb-runtime.js", () => ({
280+ loadLanceDbModule,
281+}));
282+283+try {
284+const { default: dynamicMemoryPlugin } = await import("./index.js");
285+const cfg = {
286+models: {
287+providers: {
288+openai: {
289+apiKey: "profile-backed-key",
290+},
291+},
292+},
293+};
294+const registerTool = vi.fn();
295+const mockApi = {
296+id: "memory-lancedb",
297+name: "Memory (LanceDB)",
298+source: "test",
299+config: cfg,
300+pluginConfig: {
301+embedding: {
302+provider: "openai",
303+model: "text-embedding-3-small",
304+},
305+dbPath: getDbPath(),
306+},
307+runtime: {
308+config: {
309+current: () => cfg,
310+},
311+agent: {
312+resolveAgentDir: vi.fn(() => "/tmp/openclaw-agent"),
313+},
314+},
315+logger: {
316+info: vi.fn(),
317+warn: vi.fn(),
318+error: vi.fn(),
319+debug: vi.fn(),
320+},
321+ registerTool,
322+registerCli: vi.fn(),
323+registerService: vi.fn(),
324+on: vi.fn(),
325+resolvePath: (filePath: string) => filePath,
326+};
327+328+dynamicMemoryPlugin.register(mockApi as any);
329+const recallTool = registerTool.mock.calls
330+.map(([tool]) => tool)
331+.find((tool) => tool.name === "memory_recall");
332+expect(recallTool).toBeTruthy();
333+334+await recallTool.execute("call-1", { query: "project memory" });
335+336+expect(getMemoryEmbeddingProvider).toHaveBeenCalledWith("openai", cfg);
337+expect(createProvider).toHaveBeenCalledWith(
338+expect.objectContaining({
339+config: cfg,
340+agentDir: "/tmp/openclaw-agent",
341+provider: "openai",
342+fallback: "none",
343+model: "text-embedding-3-small",
344+}),
345+);
346+expect(createProvider.mock.calls[0][0]).not.toHaveProperty("remote");
347+expect(embedQuery).toHaveBeenCalledWith("project memory");
348+} finally {
349+vi.doUnmock("openclaw/plugin-sdk/memory-core-host-engine-embeddings");
350+vi.doUnmock("openai");
351+vi.doUnmock("./lancedb-runtime.js");
352+vi.resetModules();
353+}
354+});
355+235356test("keeps before_prompt_build registered but inert when auto-recall is disabled", async () => {
236357const on = vi.fn();
237358const mockApi = {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。