test(mnemopi): reworked mnemopi test embedding setup and CI bucketing
- Removed the mnemopi Bun preload file and added explicit `./setup` imports in the changed test suites. - Deleted the `RUN_EMBEDDINGS` flag path and shifted `MNEMOPI_NO_EMBEDDINGS` handling into per-suite before/after hooks. - Excluded `packages/mnemopi` from the fast parallel CI test bucket with a note about missing fastembed models.
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
[test]
|
||||
preload = ["./test/setup.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[] = [];
|
||||
|
||||
|
||||
@@ -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() } });
|
||||
|
||||
@@ -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<T>(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) {
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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])),
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -49,6 +49,9 @@ const codingAgentBucketPlans: Record<CodingAgentBucket, { label: string; paralle
|
||||
// Smaller workspace packages stay separate from native/TUI/integration suites so
|
||||
// their short TS suites can run together. CI still downloads the Linux x64 native
|
||||
// addon before this bucket: shared utility barrels may load native-backed modules.
|
||||
// mnemopi is intentionally excluded — its embedding suites depend on a ~270MB
|
||||
// fastembed model absent from CI runners, so they flake/time out under the parallel
|
||||
// bucket; run `bun --cwd=packages/mnemopi test` locally instead.
|
||||
const fastWorkspacePackages = [
|
||||
"packages/hashline",
|
||||
"packages/wire",
|
||||
@@ -57,7 +60,6 @@ const fastWorkspacePackages = [
|
||||
"packages/ai",
|
||||
"packages/snapcompact",
|
||||
"packages/agent",
|
||||
"packages/mnemopi",
|
||||
];
|
||||
|
||||
// These suites cover the native package, TUI/browser-ish behavior, local servers,
|
||||
|
||||
Reference in New Issue
Block a user