feat(memory): add gemini batches + safe reindex

Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
This commit is contained in:
Peter Steinberger
2026-01-18 15:29:30 +00:00
parent be7191879a
commit 9464774133
5 changed files with 1472 additions and 715 deletions

View File

@@ -7,6 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { getMemorySearchManager, type MemoryIndexManager } from "./index.js";
let embedBatchCalls = 0;
let failEmbeddings = false;
vi.mock("./embeddings.js", () => {
const embedText = (text: string) => {
@@ -24,6 +25,9 @@ vi.mock("./embeddings.js", () => {
embedQuery: async (text: string) => embedText(text),
embedBatch: async (texts: string[]) => {
embedBatchCalls += 1;
if (failEmbeddings) {
throw new Error("mock embeddings failed");
}
return texts.map(embedText);
},
},
@@ -38,6 +42,7 @@ describe("memory index", () => {
beforeEach(async () => {
embedBatchCalls = 0;
failEmbeddings = false;
workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-mem-"));
indexPath = path.join(workspaceDir, "index.sqlite");
await fs.mkdir(path.join(workspaceDir, "memory"));
@@ -181,6 +186,43 @@ describe("memory index", () => {
expect(embedBatchCalls).toBe(afterFirst);
});
it("preserves existing index when forced reindex fails", async () => {
const cfg = {
agents: {
defaults: {
workspace: workspaceDir,
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath, vector: { enabled: false } },
sync: { watch: false, onSessionStart: false, onSearch: false },
query: { minScore: 0 },
cache: { enabled: false },
},
},
list: [{ id: "main", default: true }],
},
};
const result = await getMemorySearchManager({ cfg, agentId: "main" });
expect(result.manager).not.toBeNull();
if (!result.manager) throw new Error("manager missing");
manager = result.manager;
await manager.sync({ force: true });
const before = manager.status();
expect(before.files).toBeGreaterThan(0);
failEmbeddings = true;
await expect(manager.sync({ force: true })).rejects.toThrow(/mock embeddings failed/i);
const after = manager.status();
expect(after.files).toBe(before.files);
expect(after.chunks).toBe(before.chunks);
const files = await fs.readdir(workspaceDir);
expect(files.some((name) => name.includes(".tmp-"))).toBe(false);
});
it("finds keyword matches via hybrid search when query embedding is zero", async () => {
const cfg = {
agents: {