From 5c3c7180cc040ebae66b933fc919161afe98c5d9 Mon Sep 17 00:00:00 2001 From: youzini Date: Fri, 15 May 2026 15:58:37 +0000 Subject: [PATCH] chore(vector): update TriviumDB PoC to 0.7 --- .gitignore | 1 + package-lock.json | 10 +-- package.json | 3 +- tests/triviumdb-poc.mjs | 157 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 163 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 2c34420..c2d00e5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ plan_global_task_regex.md docs/BME六大功能全景解析.xlsx ST-BME_backup_6f78abcb-9aea-45b1-a8ad-fbbd8e4075f0-cx4dad.json plans/mvu-extra-analysis-guard.md +tests/.tmp-*/ diff --git a/package-lock.json b/package-lock.json index e953de5..ce5365b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "ST-BME", + "name": "ST-Bionic-Memory-Ecology", "lockfileVersion": 3, "requires": true, "packages": { "": { "dependencies": { - "triviumdb": "^0.4.41" + "triviumdb": "0.7.1" }, "devDependencies": { "dexie": "4.0.8", @@ -30,9 +30,9 @@ } }, "node_modules/triviumdb": { - "version": "0.4.41", - "resolved": "https://registry.npmjs.org/triviumdb/-/triviumdb-0.4.41.tgz", - "integrity": "sha512-2onLIrmVxB+Vfjbk5c939dovkP6SKXJnrdQ8ICz2DHdFA9iYW9XBQUvxcz+vRSk/iRdLUDr1ypYb9dV82xi8PQ==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/triviumdb/-/triviumdb-0.7.1.tgz", + "integrity": "sha512-PjKQptYiaTNAsSWn4xRqXcdO4qV1SvknZ0QpSs+HnnQEoiug4y0yb3wAnHOsC4rxink26Lb0lgV8At7JpsSaLA==", "license": "Apache-2.0" } } diff --git a/package.json b/package.json index 6401d88..1390fd7 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version:bump-manifest": "node scripts/bump-manifest-version.mjs", "build:native:wasm": "node scripts/build-native-wasm.mjs", "test:p0": "node tests/p0-regressions.mjs", + "test:triviumdb-poc": "node tests/triviumdb-poc.mjs", "test:runtime-history": "node tests/runtime-history.mjs", "test:graph-persistence": "node tests/graph-persistence.mjs", "test:hide-engine": "node tests/hide-engine.mjs", @@ -31,7 +32,7 @@ "check": "node scripts/check-syntax.mjs" }, "dependencies": { - "triviumdb": "^0.4.41" + "triviumdb": "0.7.1" }, "devDependencies": { "dexie": "4.0.8", diff --git a/tests/triviumdb-poc.mjs b/tests/triviumdb-poc.mjs index e721a0a..02f7ccd 100644 --- a/tests/triviumdb-poc.mjs +++ b/tests/triviumdb-poc.mjs @@ -27,6 +27,36 @@ function assert(condition, message) { } } +function normalizeNodeId(id) { + const numeric = Number(id); + return Number.isFinite(numeric) ? numeric : id; +} + +function idSet(values = []) { + return new Set(Array.from(values || [], normalizeNodeId)); +} + +function hasMethod(db, name) { + return typeof db?.[name] === "function"; +} + +function findByPayload(db, condition = {}) { + if (hasMethod(db, "filterWhere")) { + return db.filterWhere(condition); + } + if (hasMethod(db, "tql")) { + const query = `FIND ${JSON.stringify(condition)} RETURN *`; + return db.tql(query).map((row) => row?._).filter(Boolean); + } + throw new Error("TriviumDB has neither filterWhere nor tql FIND support"); +} + +function maybeClose(db) { + if (hasMethod(db, "close")) { + db.close(); + } +} + function vecFrom(seed, dim = DIM) { const out = []; for (let i = 0; i < dim; i += 1) { @@ -204,6 +234,9 @@ async function main() { loadModule: false, crud: false, search: false, + tql: false, + textHybrid: false, + migration: false, flushReopen: false, walRecovery: false, dimensionMismatch: false, @@ -312,7 +345,7 @@ async function main() { const neighborsAfterUnlink = db.neighbors(id1, 1); assert(!neighborsAfterUnlink.includes(id3), "unlink should remove edge id1->id3"); - const floorMatched = db.filterWhere({ sourceFloor: 1 }); + const floorMatched = findByPayload(db, { sourceFloor: 1 }); assert(floorMatched.length >= 1, "filterWhere(sourceFloor=1) should return at least one node"); assert( floorMatched.every((item) => item.payload?.sourceFloor === 1), @@ -324,9 +357,52 @@ async function main() { assert(got3 === null, "deleted node should not be retrievable"); db.flush(); + maybeClose(db); keyStatus.crud = true; }); + await runCase("tql-find-match-search", async () => { + const dbPath = path.join(TMP_ROOT, "tql.tdb"); + const db = new TriviumDB(dbPath, DIM, "f32", "normal"); + if (!hasMethod(db, "tql")) { + report.notes.push("TQL API unavailable; skipping v0.7 query coverage"); + maybeClose(db); + keyStatus.tql = true; + return; + } + + const base = vecFrom(33); + const idA = db.insert(base, { + chatId: "chat-tql", + type: "event", + sourceFloor: 31, + label: "tql-a", + text: "Alice opens the archive door", + }); + const idB = db.insert(near(base, 0.003), { + chatId: "chat-tql", + type: "event", + sourceFloor: 32, + label: "tql-b", + text: "Bob carries the archive key", + }); + db.link(idA, idB, "related", 0.82); + + const found = db.tql('FIND {chatId: "chat-tql", sourceFloor: {$gte: 31}} RETURN * LIMIT 5'); + const foundIds = idSet(found.map((row) => row?._?.id)); + assert(foundIds.has(idA) && foundIds.has(idB), "TQL FIND should return both inserted nodes"); + + const matched = db.tql(`MATCH (a {id: ${idA}})-[:related]->(b) RETURN b`); + const matchedIds = idSet(matched.map((row) => row?.b?.id)); + assert(matchedIds.has(idB), "TQL MATCH should traverse related edge"); + + const searched = db.tql(`SEARCH VECTOR ${JSON.stringify(near(base, 0.001))} TOP 2 RETURN *`); + assert(Array.isArray(searched) && searched.length >= 1, "TQL SEARCH should return vector hits"); + + maybeClose(db); + keyStatus.tql = true; + }); + await runCase("search-expandDepth", async () => { const dbPath = path.join(TMP_ROOT, "search.tdb"); const db = new TriviumDB(dbPath, DIM, "f32", "normal"); @@ -356,7 +432,7 @@ async function main() { const query = near(clusterBase, 0.002); const depth0 = db.search(query, 2, 0, -1); assert(Array.isArray(depth0) && depth0.length >= 1, "search depth0 should return hits"); - const depth0Ids = new Set(depth0.map((h) => h.id)); + const depth0Ids = idSet(depth0.map((h) => h.id)); assert(depth0Ids.has(idAnchor) || depth0Ids.has(idNear), "depth0 should include anchor cluster"); const depth1 = db.search(query, 2, 1, -1); @@ -365,6 +441,48 @@ async function main() { keyStatus.search = true; }); + await runCase("search-advanced-text-hybrid", async () => { + const dbPath = path.join(TMP_ROOT, "hybrid.tdb"); + const db = new TriviumDB(dbPath, DIM, "f32", "normal"); + const base = vecFrom(44); + const idA = db.insert(base, { + chatId: "chat-hybrid", + label: "hybrid-a", + text: "scarlet archive door memory", + }); + const idB = db.insert(near(base, 0.004), { + chatId: "chat-hybrid", + label: "hybrid-b", + text: "scarlet archive key memory", + }); + db.link(idA, idB, "related", 0.8); + + if (hasMethod(db, "indexText") && hasMethod(db, "buildTextIndex")) { + db.indexText(idA, "scarlet archive door memory"); + db.indexText(idB, "scarlet archive key memory"); + db.buildTextIndex(); + } + + if (hasMethod(db, "searchAdvanced")) { + const advancedHits = db.searchAdvanced(near(base, 0.001), { + topK: 5, + expandDepth: 1, + minScore: -1, + enableTextHybridSearch: true, + customQueryText: "scarlet archive", + }); + assert(Array.isArray(advancedHits) && advancedHits.length >= 1, "searchAdvanced should return hits"); + } + + if (hasMethod(db, "searchHybrid")) { + const hybridHits = db.searchHybrid(near(base, 0.001), "scarlet archive", 5, 1, -1, 0.7); + assert(Array.isArray(hybridHits) && hybridHits.length >= 1, "searchHybrid should return hits"); + } + + maybeClose(db); + keyStatus.textHybrid = true; + }); + await runCase("flush-reopen-consistency", async () => { const dbPath = path.join(TMP_ROOT, "flush-reopen.tdb"); const writer = await spawnNode(["--child-flush-writer", dbPath, String(DIM)], { @@ -485,6 +603,37 @@ async function main() { keyStatus.dimensionMismatch = true; }); + await runCase("dimension-migrate-zero-vector", async () => { + const dbPath = path.join(TMP_ROOT, "migrate-source.tdb"); + const newPath = path.join(TMP_ROOT, "migrate-target.tdb"); + const db = new TriviumDB(dbPath, DIM, "f32", "normal"); + if (!hasMethod(db, "migrate")) { + report.notes.push("migrate API unavailable; skipping v0.7 dimension migration coverage"); + maybeClose(db); + keyStatus.migration = true; + return; + } + + const idA = db.insert(vecFrom(61), { chatId: "chat-migrate", label: "migrate-a" }); + const idB = db.insert(near(vecFrom(61), 0.01), { chatId: "chat-migrate", label: "migrate-b" }); + db.link(idA, idB, "related", 0.77); + db.flush(); + const migratedIds = db.migrate(newPath, DIM + 2); + assert(idSet(migratedIds).has(idA) && idSet(migratedIds).has(idB), "migrate should return migrated ids"); + maybeClose(db); + + const migrated = new TriviumDB(newPath, DIM + 2, "f32", "normal"); + assert(typeof migrated.dim === "function" ? migrated.dim() === DIM + 2 : true, "migrated db dim should match target dim"); + const gotA = migrated.get(idA); + assert(gotA?.payload?.label === "migrate-a", "migrate should preserve payload"); + assert(Array.isArray(gotA?.vector) && gotA.vector.length === DIM + 2, "migrate should create target-dim vectors"); + assert(gotA.vector.every((value) => Number(value) === 0), "migrated vectors should be zero-filled for re-embedding"); + const nearA = migrated.neighbors(idA, 1); + assert(idSet(nearA).has(idB), "migrate should preserve graph edges"); + maybeClose(migrated); + keyStatus.migration = true; + }); + await runCase("benchmark-100plus-nodes", async () => { const dbPath = path.join(TMP_ROOT, "benchmark.tdb"); const db = new TriviumDB(dbPath, DIM, "f32", "normal"); @@ -563,6 +712,7 @@ async function main() { assert(openedByChild.code !== 0, "second process open should fail while parent instance is active"); } + maybeClose(db); keyStatus.concurrentSafety = true; }); @@ -570,6 +720,9 @@ async function main() { keyStatus.loadModule, keyStatus.crud, keyStatus.search, + keyStatus.tql, + keyStatus.textHybrid, + keyStatus.migration, keyStatus.flushReopen, keyStatus.walRecovery, keyStatus.dimensionMismatch,