
























@@ -511,6 +511,117 @@ describe("memory plugin e2e", () => {
511511}
512512});
513513514+test("fails closed for auto-recall when the live plugin entry is removed", async () => {
515+const embeddingsCreate = vi.fn(async () => ({
516+data: [{ embedding: [0.1, 0.2, 0.3] }],
517+}));
518+const ensureGlobalUndiciEnvProxyDispatcher = vi.fn();
519+const loadLanceDbModule = vi.fn(async () => ({
520+connect: vi.fn(async () => ({
521+tableNames: vi.fn(async () => ["memories"]),
522+openTable: vi.fn(async () => ({
523+vectorSearch: vi.fn(() => ({ limit: vi.fn(() => ({ toArray: vi.fn(async () => []) })) })),
524+countRows: vi.fn(async () => 0),
525+add: vi.fn(async () => undefined),
526+delete: vi.fn(async () => undefined),
527+})),
528+})),
529+}));
530+let configFile: Record<string, unknown> = {
531+plugins: {
532+entries: {
533+"memory-lancedb": {
534+config: {
535+embedding: {
536+apiKey: OPENAI_API_KEY,
537+model: "text-embedding-3-small",
538+},
539+dbPath: getDbPath(),
540+autoCapture: false,
541+autoRecall: true,
542+},
543+},
544+},
545+},
546+};
547+548+vi.resetModules();
549+vi.doMock("openclaw/plugin-sdk/runtime-env", () => ({
550+ ensureGlobalUndiciEnvProxyDispatcher,
551+}));
552+vi.doMock("openai", () => ({
553+default: class MockOpenAI {
554+embeddings = { create: embeddingsCreate };
555+},
556+}));
557+vi.doMock("./lancedb-runtime.js", () => ({
558+ loadLanceDbModule,
559+}));
560+561+try {
562+const { default: dynamicMemoryPlugin } = await import("./index.js");
563+const on = vi.fn();
564+const mockApi = {
565+id: "memory-lancedb",
566+name: "Memory (LanceDB)",
567+source: "test",
568+config: {},
569+pluginConfig: {
570+embedding: {
571+apiKey: OPENAI_API_KEY,
572+model: "text-embedding-3-small",
573+},
574+dbPath: getDbPath(),
575+autoCapture: false,
576+autoRecall: true,
577+},
578+runtime: {
579+config: {
580+loadConfig: () => configFile,
581+},
582+},
583+logger: {
584+info: vi.fn(),
585+warn: vi.fn(),
586+error: vi.fn(),
587+debug: vi.fn(),
588+},
589+registerTool: vi.fn(),
590+registerCli: vi.fn(),
591+registerService: vi.fn(),
592+ on,
593+resolvePath: (p: string) => p,
594+};
595+596+dynamicMemoryPlugin.register(mockApi as any);
597+598+configFile = {
599+plugins: {
600+entries: {},
601+},
602+};
603+604+const beforePromptBuild = on.mock.calls.find(
605+([hookName]) => hookName === "before_prompt_build",
606+)?.[1];
607+expect(beforePromptBuild).toBeTypeOf("function");
608+609+const result = await beforePromptBuild?.(
610+{ prompt: "what editor should i use after memory is removed?", messages: [] },
611+{},
612+);
613+614+expect(result).toBeUndefined();
615+expect(embeddingsCreate).not.toHaveBeenCalled();
616+expect(loadLanceDbModule).not.toHaveBeenCalled();
617+} finally {
618+vi.doUnmock("openclaw/plugin-sdk/runtime-env");
619+vi.doUnmock("openai");
620+vi.doUnmock("./lancedb-runtime.js");
621+vi.resetModules();
622+}
623+});
624+514625test("runs auto-capture through the registered agent_end hook", async () => {
515626const embeddingsCreate = vi.fn(async () => ({
516627data: [{ embedding: [0.1, 0.2, 0.3] }],
@@ -744,6 +855,119 @@ describe("memory plugin e2e", () => {
744855}
745856});
746857858+test("fails closed for auto-capture when the live plugin entry is removed", async () => {
859+const embeddingsCreate = vi.fn(async () => ({
860+data: [{ embedding: [0.1, 0.2, 0.3] }],
861+}));
862+const ensureGlobalUndiciEnvProxyDispatcher = vi.fn();
863+const add = vi.fn(async () => undefined);
864+const loadLanceDbModule = vi.fn(async () => ({
865+connect: vi.fn(async () => ({
866+tableNames: vi.fn(async () => ["memories"]),
867+openTable: vi.fn(async () => ({
868+vectorSearch: vi.fn(() => ({ limit: vi.fn(() => ({ toArray: vi.fn(async () => []) })) })),
869+countRows: vi.fn(async () => 0),
870+ add,
871+delete: vi.fn(async () => undefined),
872+})),
873+})),
874+}));
875+let configFile: Record<string, unknown> = {
876+plugins: {
877+entries: {
878+"memory-lancedb": {
879+config: {
880+embedding: {
881+apiKey: OPENAI_API_KEY,
882+model: "text-embedding-3-small",
883+},
884+dbPath: getDbPath(),
885+autoCapture: true,
886+autoRecall: false,
887+},
888+},
889+},
890+},
891+};
892+893+vi.resetModules();
894+vi.doMock("openclaw/plugin-sdk/runtime-env", () => ({
895+ ensureGlobalUndiciEnvProxyDispatcher,
896+}));
897+vi.doMock("openai", () => ({
898+default: class MockOpenAI {
899+embeddings = { create: embeddingsCreate };
900+},
901+}));
902+vi.doMock("./lancedb-runtime.js", () => ({
903+ loadLanceDbModule,
904+}));
905+906+try {
907+const { default: dynamicMemoryPlugin } = await import("./index.js");
908+const on = vi.fn();
909+const mockApi = {
910+id: "memory-lancedb",
911+name: "Memory (LanceDB)",
912+source: "test",
913+config: {},
914+pluginConfig: {
915+embedding: {
916+apiKey: OPENAI_API_KEY,
917+model: "text-embedding-3-small",
918+},
919+dbPath: getDbPath(),
920+autoCapture: true,
921+autoRecall: false,
922+},
923+runtime: {
924+config: {
925+loadConfig: () => configFile,
926+},
927+},
928+logger: {
929+info: vi.fn(),
930+warn: vi.fn(),
931+error: vi.fn(),
932+debug: vi.fn(),
933+},
934+registerTool: vi.fn(),
935+registerCli: vi.fn(),
936+registerService: vi.fn(),
937+ on,
938+resolvePath: (p: string) => p,
939+};
940+941+dynamicMemoryPlugin.register(mockApi as any);
942+943+configFile = {
944+plugins: {
945+entries: {},
946+},
947+};
948+949+const agentEnd = on.mock.calls.find(([hookName]) => hookName === "agent_end")?.[1];
950+expect(agentEnd).toBeTypeOf("function");
951+952+await agentEnd?.(
953+{
954+success: true,
955+messages: [{ role: "user", content: "I prefer Helix for editing code every day." }],
956+},
957+{},
958+);
959+960+expect(embeddingsCreate).not.toHaveBeenCalled();
961+expect(loadLanceDbModule).not.toHaveBeenCalled();
962+expect(add).not.toHaveBeenCalled();
963+} finally {
964+vi.doUnmock("openclaw/plugin-sdk/runtime-env");
965+vi.doUnmock("openai");
966+vi.doUnmock("./lancedb-runtime.js");
967+vi.resetModules();
968+}
969+});
970+747971test("passes configured dimensions to OpenAI embeddings API", async () => {
748972const embeddingsCreate = vi.fn(async () => ({
749973data: [{ embedding: [0.1, 0.2, 0.3] }],
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。