
























@@ -12,7 +12,7 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi }
1212import "./test-runtime-mocks.js";
1313import type { MemoryIndexManager } from "./index.js";
1414import { closeAllMemorySearchManagers, getMemorySearchManager } from "./index.js";
15-import { EMBEDDING_PROBE_CACHE_TTL_MS } from "./manager.js";
15+import { closeMemoryIndexManagersForAgent, EMBEDDING_PROBE_CACHE_TTL_MS } from "./manager.js";
1616import {
1717DEFAULT_LOCAL_MODEL,
1818registerBuiltInMemoryEmbeddingProviders,
@@ -28,6 +28,9 @@ afterAll(() => {
28282929let embedBatchCalls = 0;
3030let embedBatchInputCalls = 0;
31+let providerCloseCalls = 0;
32+let providerCloseFailuresRemaining = 0;
33+let providerCloseGate: Promise<void> | null = null;
3134let providerCalls: Array<{ provider?: string; model?: string; outputDimensionality?: number }> = [];
3235let forceNoProvider = false;
3336@@ -65,6 +68,14 @@ vi.mock("./embeddings.js", () => {
6568provider: {
6669id: providerId,
6770 model,
71+close: async () => {
72+providerCloseCalls += 1;
73+await providerCloseGate;
74+if (providerCloseFailuresRemaining > 0) {
75+providerCloseFailuresRemaining -= 1;
76+throw new Error("provider close failed");
77+}
78+},
6879embedQuery: async (text: string) => embedText(text),
6980embedBatch: async (texts: string[]) => {
7081embedBatchCalls += 1;
@@ -188,6 +199,9 @@ describe("memory index", () => {
188199registerBuiltInMemoryEmbeddingProviders({ registerMemoryEmbeddingProvider: registerAdapter });
189200embedBatchCalls = 0;
190201embedBatchInputCalls = 0;
202+providerCloseCalls = 0;
203+providerCloseFailuresRemaining = 0;
204+providerCloseGate = null;
191205providerCalls = [];
192206forceNoProvider = false;
193207@@ -354,6 +368,96 @@ describe("memory index", () => {
354368}
355369});
356370371+it("closes embedding providers when memory index managers close", async () => {
372+const cfg = createCfg({
373+storePath: indexMainPath,
374+hybrid: { enabled: true, vectorWeight: 0.5, textWeight: 0.5 },
375+});
376+const manager = await getFreshManager(cfg);
377+378+await manager.probeEmbeddingAvailability();
379+expect(providerCloseCalls).toBe(0);
380+381+await manager.close();
382+await manager.close();
383+384+expect(providerCloseCalls).toBe(1);
385+});
386+387+it("closes embedding providers before waiting for pending sync to settle", async () => {
388+const cfg = createCfg({
389+storePath: indexMainPath,
390+hybrid: { enabled: true, vectorWeight: 0.5, textWeight: 0.5 },
391+});
392+const manager = await getFreshManager(cfg);
393+await manager.probeEmbeddingAvailability();
394+let resolveSync: () => void = () => {};
395+(manager as unknown as { syncing: Promise<void> }).syncing = new Promise<void>((resolve) => {
396+resolveSync = resolve;
397+});
398+399+const closePromise = manager.close();
400+await vi.waitFor(() => {
401+expect(providerCloseCalls).toBe(1);
402+});
403+let closeSettled = false;
404+void closePromise.then(() => {
405+closeSettled = true;
406+});
407+await Promise.resolve();
408+409+expect(closeSettled).toBe(false);
410+resolveSync();
411+await closePromise;
412+});
413+414+it("evicts scoped memory index managers before close settles", async () => {
415+let releaseProviderClose: () => void = () => {};
416+providerCloseGate = new Promise<void>((resolve) => {
417+releaseProviderClose = resolve;
418+});
419+const cfg = createCfg({
420+storePath: indexMainPath,
421+hybrid: { enabled: true, vectorWeight: 0.5, textWeight: 0.5 },
422+});
423+const first = requireManager(await getMemorySearchManager({ cfg, agentId: "main" }));
424+managersForCleanup.add(first);
425+await first.probeEmbeddingAvailability();
426+const closePromise = closeMemoryIndexManagersForAgent({ cfg, agentId: "main" });
427+let second: MemoryIndexManager | null = null;
428+try {
429+await vi.waitFor(() => {
430+expect(providerCloseCalls).toBe(1);
431+});
432+433+second = requireManager(await getMemorySearchManager({ cfg, agentId: "main" }));
434+managersForCleanup.add(second);
435+expect(second).not.toBe(first);
436+} finally {
437+releaseProviderClose();
438+providerCloseGate = null;
439+}
440+await closePromise;
441+442+const third = requireManager(await getMemorySearchManager({ cfg, agentId: "main" }));
443+managersForCleanup.add(third);
444+expect(third).toBe(second);
445+});
446+447+it("retries embedding provider close before releasing the manager", async () => {
448+providerCloseFailuresRemaining = 1;
449+const cfg = createCfg({
450+storePath: indexMainPath,
451+hybrid: { enabled: true, vectorWeight: 0.5, textWeight: 0.5 },
452+});
453+const manager = await getFreshManager(cfg);
454+455+await manager.probeEmbeddingAvailability();
456+await manager.close();
457+458+expect(providerCloseCalls).toBe(2);
459+});
460+357461it("indexes multimodal image and audio files from extra paths with Gemini structured inputs", async () => {
358462const mediaDir = path.join(workspaceDir, "media-memory");
359463await fs.mkdir(mediaDir, { recursive: true });
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。