Skip to content

Commit 6d2bc02

Browse files
rodrigourozjalehman
authored andcommitted
Memory Sync: reindex after targeted fallback
1 parent 8bcedd3 commit 6d2bc02

File tree

2 files changed

+104
-6
lines changed

2 files changed

+104
-6
lines changed

src/memory/index.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,94 @@ describe("memory index", () => {
758758
}
759759
});
760760

761+
it("runs a full reindex after fallback activates during targeted sync", async () => {
762+
const stateDir = path.join(fixtureRoot, `state-targeted-fallback-${randomUUID()}`);
763+
const sessionDir = path.join(stateDir, "agents", "main", "sessions");
764+
const sessionPath = path.join(sessionDir, "targeted-fallback.jsonl");
765+
const storePath = path.join(workspaceDir, `index-targeted-fallback-${randomUUID()}.sqlite`);
766+
const previousStateDir = process.env.OPENCLAW_STATE_DIR;
767+
process.env.OPENCLAW_STATE_DIR = stateDir;
768+
769+
await fs.mkdir(sessionDir, { recursive: true });
770+
await fs.writeFile(
771+
sessionPath,
772+
`${JSON.stringify({
773+
type: "message",
774+
message: { role: "user", content: [{ type: "text", text: "fallback transcript v1" }] },
775+
})}\n`,
776+
);
777+
778+
try {
779+
const manager = requireManager(
780+
await getMemorySearchManager({
781+
cfg: createCfg({
782+
storePath,
783+
sources: ["sessions"],
784+
sessionMemory: true,
785+
}),
786+
agentId: "main",
787+
}),
788+
);
789+
await manager.sync({ reason: "test" });
790+
791+
const internal = manager as unknown as {
792+
syncSessionFiles: (params: {
793+
targetSessionFiles?: string[];
794+
needsFullReindex: boolean;
795+
}) => Promise<void>;
796+
shouldFallbackOnError: (message: string) => boolean;
797+
activateFallbackProvider: (reason: string) => Promise<boolean>;
798+
runUnsafeReindex: (params: {
799+
reason?: string;
800+
force?: boolean;
801+
progress?: unknown;
802+
}) => Promise<void>;
803+
};
804+
const originalSyncSessionFiles = internal.syncSessionFiles.bind(manager);
805+
const originalShouldFallbackOnError = internal.shouldFallbackOnError.bind(manager);
806+
const originalActivateFallbackProvider = internal.activateFallbackProvider.bind(manager);
807+
const originalRunUnsafeReindex = internal.runUnsafeReindex.bind(manager);
808+
809+
internal.syncSessionFiles = async (params) => {
810+
if (params.targetSessionFiles?.length) {
811+
throw new Error("embedding backend failed");
812+
}
813+
return await originalSyncSessionFiles(params);
814+
};
815+
internal.shouldFallbackOnError = () => true;
816+
const activateFallbackProvider = vi.fn(async () => true);
817+
internal.activateFallbackProvider = activateFallbackProvider;
818+
const runUnsafeReindex = vi.fn(async () => {});
819+
internal.runUnsafeReindex = runUnsafeReindex;
820+
821+
await manager.sync({
822+
reason: "post-compaction",
823+
sessionFiles: [sessionPath],
824+
});
825+
826+
expect(activateFallbackProvider).toHaveBeenCalledWith("embedding backend failed");
827+
expect(runUnsafeReindex).toHaveBeenCalledWith({
828+
reason: "post-compaction",
829+
force: true,
830+
progress: undefined,
831+
});
832+
833+
internal.syncSessionFiles = originalSyncSessionFiles;
834+
internal.shouldFallbackOnError = originalShouldFallbackOnError;
835+
internal.activateFallbackProvider = originalActivateFallbackProvider;
836+
internal.runUnsafeReindex = originalRunUnsafeReindex;
837+
await manager.close?.();
838+
} finally {
839+
if (previousStateDir === undefined) {
840+
delete process.env.OPENCLAW_STATE_DIR;
841+
} else {
842+
process.env.OPENCLAW_STATE_DIR = previousStateDir;
843+
}
844+
await fs.rm(stateDir, { recursive: true, force: true });
845+
await fs.rm(storePath, { force: true });
846+
}
847+
});
848+
761849
it("reindexes when the embedding model changes", async () => {
762850
const base = createCfg({ storePath: indexModelPath });
763851
const baseAgents = base.agents!;

src/memory/manager-sync-ops.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -967,12 +967,22 @@ export abstract class MemoryManagerSyncOps {
967967
const activated =
968968
this.shouldFallbackOnError(reason) && (await this.activateFallbackProvider(reason));
969969
if (activated) {
970-
await this.syncSessionFiles({
971-
needsFullReindex: false,
972-
targetSessionFiles: Array.from(targetSessionFiles),
973-
progress: progress ?? undefined,
974-
});
975-
this.clearSyncedSessionFiles(targetSessionFiles);
970+
if (
971+
process.env.OPENCLAW_TEST_FAST === "1" &&
972+
process.env.OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX === "1"
973+
) {
974+
await this.runUnsafeReindex({
975+
reason: params?.reason,
976+
force: true,
977+
progress: progress ?? undefined,
978+
});
979+
} else {
980+
await this.runSafeReindex({
981+
reason: params?.reason,
982+
force: true,
983+
progress: progress ?? undefined,
984+
});
985+
}
976986
return;
977987
}
978988
throw err;

0 commit comments

Comments
 (0)