diff --git a/packages/mnemopi/bunfig.toml b/packages/mnemopi/bunfig.toml deleted file mode 100644 index 8755352e5..000000000 --- a/packages/mnemopi/bunfig.toml +++ /dev/null @@ -1,2 +0,0 @@ -[test] -preload = ["./test/setup.ts"] diff --git a/packages/mnemopi/test/beam-e3-e4-e6.test.ts b/packages/mnemopi/test/beam-e3-e4-e6.test.ts index 26f21ebd7..d4ae15f09 100644 --- a/packages/mnemopi/test/beam-e3-e4-e6.test.ts +++ b/packages/mnemopi/test/beam-e3-e4-e6.test.ts @@ -1,10 +1,23 @@ import { Database } from "bun:sqlite"; -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, beforeEach, describe, expect, it } from "bun:test"; import { existsSync, mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { BeamMemory } from "@oh-my-pi/pi-mnemopi/core/beam"; +// Real embeddings (fastembed + onnxruntime-node, ~270MB) install on demand via +// `bun install` on first use. These tests never exercise embeddings — the +// consolidation dry-run touches no vectors — so disable them; otherwise the +// on-demand install hangs each test past the 5s timeout (and starves siblings +// under parallel CI). +beforeEach(() => { + process.env.MNEMOPI_NO_EMBEDDINGS = "1"; +}); + +afterEach(() => { + delete process.env.MNEMOPI_NO_EMBEDDINGS; +}); + type TempDb = { dir: string; path: string }; const tempDbs: TempDb[] = []; diff --git a/packages/mnemopi/test/embedding-model-reconcile.test.ts b/packages/mnemopi/test/embedding-model-reconcile.test.ts index ae03af7b9..3248842df 100644 --- a/packages/mnemopi/test/embedding-model-reconcile.test.ts +++ b/packages/mnemopi/test/embedding-model-reconcile.test.ts @@ -8,9 +8,9 @@ import { Database } from "bun:sqlite"; import { describe, expect, it } from "bun:test"; +import "./setup"; import { initBeam } from "@oh-my-pi/pi-mnemopi/core/beam"; import { Mnemopi } from "@oh-my-pi/pi-mnemopi/core/memory"; -import { RUN_EMBEDDINGS } from "./setup"; const OLD_MODEL = "BAAI/bge-small-en-v1.5"; const NEW_MODEL = "intfloat/multilingual-e5-large"; @@ -47,7 +47,7 @@ function countEmbeddings(memory: Mnemopi): number { return (memory.conn.query("SELECT COUNT(*) AS n FROM memory_embeddings").get() as { n: number }).n; } -describe.skipIf(!RUN_EMBEDDINGS)("reconcileEmbeddingModel on store open", () => { +describe("reconcileEmbeddingModel on store open", () => { it("wipes stale embeddings + binary vectors and re-embeds when the model changed", async () => { const { db, ids } = seedDb(OLD_MODEL); const memory = new Mnemopi({ db, embeddings: { model: NEW_MODEL, provider: fakeEmbed() } }); diff --git a/packages/mnemopi/test/embeddings-multilingual.test.ts b/packages/mnemopi/test/embeddings-multilingual.test.ts index da3e5eabd..c95cd7a5b 100644 --- a/packages/mnemopi/test/embeddings-multilingual.test.ts +++ b/packages/mnemopi/test/embeddings-multilingual.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "bun:test"; +import "./setup"; import { cosineSimilarity, embed, @@ -7,7 +8,6 @@ import { resetEmbeddingProviderForTests, setEmbeddingProviderForTests, } from "@oh-my-pi/pi-mnemopi/core/embeddings"; -import { RUN_EMBEDDINGS } from "./setup"; function withEnvValue(key: string, value: string | undefined, fn: () => T): T { const previous = process.env[key]; @@ -121,7 +121,7 @@ describe("multilingual embedding metadata", () => { }); }); -describe.skipIf(!RUN_EMBEDDINGS)("multilingual embedding ordering", () => { +describe("multilingual embedding ordering", () => { it("preserves semantic ordering with a deterministic fake multilingual provider", async () => { setEmbeddingProviderForTests({ async *embed(texts) { diff --git a/packages/mnemopi/test/identity-memory-parity.test.ts b/packages/mnemopi/test/identity-memory-parity.test.ts index 0a395ce28..41123f410 100644 --- a/packages/mnemopi/test/identity-memory-parity.test.ts +++ b/packages/mnemopi/test/identity-memory-parity.test.ts @@ -1,10 +1,19 @@ -import { afterEach, describe, expect, it } from "bun:test"; +import { afterEach, beforeEach, describe, expect, it } from "bun:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { BeamMemory } from "@oh-my-pi/pi-mnemopi/core/beam"; import { Mnemopi } from "@oh-my-pi/pi-mnemopi/core/memory"; +// Real embeddings (fastembed + onnxruntime-node, ~270MB) install on demand via +// `bun install` on first use. These tests assert identity filtering and FTS +// recall, not vector search, so disable embeddings; otherwise the on-demand +// install hangs each test past the 5s timeout (and starves siblings under +// parallel CI). +beforeEach(() => { + process.env.MNEMOPI_NO_EMBEDDINGS = "1"; +}); + const roots: string[] = []; function tempDb(): string { @@ -19,6 +28,7 @@ afterEach(() => { if (root === undefined) break; rmSync(root, { recursive: true, force: true }); } + delete process.env.MNEMOPI_NO_EMBEDDINGS; }); describe("identity memory parity", () => { diff --git a/packages/mnemopi/test/issue-1832-embedding-population.test.ts b/packages/mnemopi/test/issue-1832-embedding-population.test.ts index 7df3fd700..01dcfaed2 100644 --- a/packages/mnemopi/test/issue-1832-embedding-population.test.ts +++ b/packages/mnemopi/test/issue-1832-embedding-population.test.ts @@ -16,6 +16,7 @@ import { describe, expect, it } from "bun:test"; import { randomBytes } from "node:crypto"; import { rmSync } from "node:fs"; import { tmpdir } from "node:os"; +import "./setup"; import { cmdRemember } from "@oh-my-pi/pi-mnemopi/cli"; import { BeamMemory } from "@oh-my-pi/pi-mnemopi/core/beam"; import { Mnemopi } from "@oh-my-pi/pi-mnemopi/core/memory"; @@ -23,7 +24,6 @@ import { type ResolvedMnemopiRuntimeOptions, withMnemopiRuntimeOptions, } from "@oh-my-pi/pi-mnemopi/core/runtime-options"; -import { RUN_EMBEDDINGS } from "./setup"; interface EmbeddingRow { readonly memory_id: string; @@ -77,7 +77,7 @@ function readEmbeddings(memory: Mnemopi): EmbeddingRow[] { .all() as EmbeddingRow[]; } -describe.skipIf(!RUN_EMBEDDINGS)("issue #1832 — embedding write/read coverage", () => { +describe("issue #1832 — embedding write/read coverage", () => { it("remember() writes a row to memory_embeddings after flushExtractions()", async () => { await withFakeMemory(async (memory, calls) => { const memId = memory.remember("alpha facts about migration", { source: "test", importance: 0.5 }); diff --git a/packages/mnemopi/test/optional-embeddings.test.ts b/packages/mnemopi/test/optional-embeddings.test.ts index 46c710598..d7fbe0c53 100644 --- a/packages/mnemopi/test/optional-embeddings.test.ts +++ b/packages/mnemopi/test/optional-embeddings.test.ts @@ -1,4 +1,6 @@ import { afterEach, describe, expect, it } from "bun:test"; +import { getFastembedCacheDir } from "@oh-my-pi/pi-utils"; +import "./setup"; import { available, embed, @@ -10,9 +12,7 @@ import { } from "@oh-my-pi/pi-mnemopi/core/embeddings"; import { Mnemopi } from "@oh-my-pi/pi-mnemopi/core/memory"; import { withMnemopiRuntimeOptions } from "@oh-my-pi/pi-mnemopi/core/runtime-options"; -import { getFastembedCacheDir } from "@oh-my-pi/pi-utils"; import packageJson from "../package.json" with { type: "json" }; -import { RUN_EMBEDDINGS } from "./setup"; const ENV_KEYS = [ "NODE_ENV", @@ -202,7 +202,7 @@ describe("optional embeddings", () => { } }); - it.skipIf(!RUN_EMBEDDINGS)("uses a constructor-scoped embedding provider", async () => { + it("uses a constructor-scoped embedding provider", async () => { const memory = new Mnemopi({ embeddings: { provider: streamRows(texts => texts.map(text => [text.length, text.charCodeAt(0) || 0])), diff --git a/packages/mnemopi/test/setup.ts b/packages/mnemopi/test/setup.ts index 368c996c5..f774a1db9 100644 --- a/packages/mnemopi/test/setup.ts +++ b/packages/mnemopi/test/setup.ts @@ -62,18 +62,8 @@ class FakeLocalLlmBackend implements LlmBackend { return { choices: [{ message: { content: this.response } }] }; } } -export const RUN_EMBEDDINGS = Bun.env.EMBEDDINGS === "1"; beforeEach(() => { - // Real embeddings (fastembed + onnxruntime-node, ~270MB peers) install on - // demand via `bun install` on first use. Default the suite to the lightweight - // FTS-only mode; embedding-specific tests opt back in explicitly with withEnv() - // or a fake provider. - if (!RUN_EMBEDDINGS) { - process.env.MNEMOPI_NO_EMBEDDINGS = "1"; - } else { - delete process.env.MNEMOPI_NO_EMBEDDINGS; - } resetModuleStateForTests(); disableLocalLlmForTests(); }); @@ -81,5 +71,4 @@ beforeEach(() => { afterEach(() => { resetModuleStateForTests(); disableLocalLlmForTests(); - delete process.env.MNEMOPI_NO_EMBEDDINGS; }); diff --git a/scripts/ci-test-ts.ts b/scripts/ci-test-ts.ts index 0dd595a0e..41b536929 100755 --- a/scripts/ci-test-ts.ts +++ b/scripts/ci-test-ts.ts @@ -49,6 +49,9 @@ const codingAgentBucketPlans: Record