@fgv/ts-agent-memory-sqlite-vec 5.1.0-42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.rush/temp/689b960e7e1a3b63bd68fbb91474d5c28d567861.tar.log +60 -0
- package/.rush/temp/chunked-rush-logs/ts-agent-memory-sqlite-vec.build.chunks.jsonl +9 -0
- package/.rush/temp/operation/build/all.log +9 -0
- package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
- package/.rush/temp/operation/build/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +720 -0
- package/README.md +109 -0
- package/config/api-extractor.json +38 -0
- package/config/jest.config.json +13 -0
- package/config/rig.json +6 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/packlets/sqlite-vec-index/index.js +8 -0
- package/dist/packlets/sqlite-vec-index/index.js.map +1 -0
- package/dist/packlets/sqlite-vec-index/model.js +6 -0
- package/dist/packlets/sqlite-vec-index/model.js.map +1 -0
- package/dist/packlets/sqlite-vec-index/sqliteVecFragmentIndex.js +277 -0
- package/dist/packlets/sqlite-vec-index/sqliteVecFragmentIndex.js.map +1 -0
- package/dist/packlets/sqlite-vec-index/sqliteVecVectorIndex.js +182 -0
- package/dist/packlets/sqlite-vec-index/sqliteVecVectorIndex.js.map +1 -0
- package/dist/test/unit/sqliteVecFragmentIndex.test.js +352 -0
- package/dist/test/unit/sqliteVecFragmentIndex.test.js.map +1 -0
- package/dist/test/unit/sqliteVecVectorIndex.test.js +199 -0
- package/dist/test/unit/sqliteVecVectorIndex.test.js.map +1 -0
- package/dist/ts-agent-memory-sqlite-vec.d.ts +225 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/eslint.config.js +15 -0
- package/etc/ts-agent-memory-sqlite-vec.api.md +66 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +22 -0
- package/lib/index.js.map +1 -0
- package/lib/packlets/sqlite-vec-index/index.d.ts +4 -0
- package/lib/packlets/sqlite-vec-index/index.d.ts.map +1 -0
- package/lib/packlets/sqlite-vec-index/index.js +24 -0
- package/lib/packlets/sqlite-vec-index/index.js.map +1 -0
- package/lib/packlets/sqlite-vec-index/model.d.ts +48 -0
- package/lib/packlets/sqlite-vec-index/model.d.ts.map +1 -0
- package/lib/packlets/sqlite-vec-index/model.js +7 -0
- package/lib/packlets/sqlite-vec-index/model.js.map +1 -0
- package/lib/packlets/sqlite-vec-index/sqliteVecFragmentIndex.d.ts +94 -0
- package/lib/packlets/sqlite-vec-index/sqliteVecFragmentIndex.d.ts.map +1 -0
- package/lib/packlets/sqlite-vec-index/sqliteVecFragmentIndex.js +281 -0
- package/lib/packlets/sqlite-vec-index/sqliteVecFragmentIndex.js.map +1 -0
- package/lib/packlets/sqlite-vec-index/sqliteVecVectorIndex.d.ts +80 -0
- package/lib/packlets/sqlite-vec-index/sqliteVecVectorIndex.d.ts.map +1 -0
- package/lib/packlets/sqlite-vec-index/sqliteVecVectorIndex.js +186 -0
- package/lib/packlets/sqlite-vec-index/sqliteVecVectorIndex.js.map +1 -0
- package/lib/test/unit/sqliteVecFragmentIndex.test.d.ts +2 -0
- package/lib/test/unit/sqliteVecFragmentIndex.test.d.ts.map +1 -0
- package/lib/test/unit/sqliteVecFragmentIndex.test.js +390 -0
- package/lib/test/unit/sqliteVecFragmentIndex.test.js.map +1 -0
- package/lib/test/unit/sqliteVecVectorIndex.test.d.ts +2 -0
- package/lib/test/unit/sqliteVecVectorIndex.test.d.ts.map +1 -0
- package/lib/test/unit/sqliteVecVectorIndex.test.js +237 -0
- package/lib/test/unit/sqliteVecVectorIndex.test.js.map +1 -0
- package/package.json +87 -0
- package/rush-logs/ts-agent-memory-sqlite-vec.build.cache.log +3 -0
- package/rush-logs/ts-agent-memory-sqlite-vec.build.log +9 -0
- package/src/index.ts +6 -0
- package/src/packlets/sqlite-vec-index/index.ts +8 -0
- package/src/packlets/sqlite-vec-index/model.ts +56 -0
- package/src/packlets/sqlite-vec-index/sqliteVecFragmentIndex.ts +381 -0
- package/src/packlets/sqlite-vec-index/sqliteVecVectorIndex.ts +255 -0
- package/src/test/unit/sqliteVecFragmentIndex.test.ts +466 -0
- package/src/test/unit/sqliteVecVectorIndex.test.ts +253 -0
- package/temp/build/lint/_eslint-5eVG3S6w.json +34 -0
- package/temp/build/typescript/ts_8nwakTlr.json +1 -0
- package/temp/ts-agent-memory-sqlite-vec.api.json +1167 -0
- package/temp/ts-agent-memory-sqlite-vec.api.md +66 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqliteVecFragmentIndex.test.js","sourceRoot":"","sources":["../../../src/test/unit/sqliteVecFragmentIndex.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport '@fgv/ts-utils-jest';\n\nimport BetterSqlite3 from 'better-sqlite3';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport {\n IEdgeTarget,\n IEmbeddedFragment,\n IFragmentLocator,\n IVectorQueryHit,\n MemoryId,\n MemoryScopeKey\n} from '@fgv/ts-agent-memory';\nimport { SqliteVecFragmentIndex } from '../../index';\n\nfunction target(scope: string, id: string): IEdgeTarget {\n return { scope: scope as unknown as MemoryScopeKey, id: id as unknown as MemoryId };\n}\nfunction loc(start: number, end: number): IFragmentLocator {\n return { start, end };\n}\nfunction frag(start: number, end: number, ...values: number[]): IEmbeddedFragment {\n return { locator: loc(start, end), vector: Float32Array.from(values) };\n}\n\ndescribe('SqliteVecFragmentIndex', () => {\n let db: BetterSqlite3.Database;\n\n beforeEach(() => {\n db = new BetterSqlite3(':memory:');\n });\n afterEach(() => {\n // Some tests close `db` themselves to exercise error paths; guard double-close.\n if (db.open) {\n db.close();\n }\n });\n\n async function makeIndex(): Promise<SqliteVecFragmentIndex> {\n return (await SqliteVecFragmentIndex.create({ database: db })).orThrow();\n }\n\n describe('create', () => {\n test('succeeds over a fresh database with an empty index', async () => {\n expect(await SqliteVecFragmentIndex.create({ database: db })).toSucceedAndSatisfy(\n (index: SqliteVecFragmentIndex) => {\n expect(index.recordCount).toBe(0);\n expect(index.fragmentCount).toBe(0);\n }\n );\n });\n\n test('rejects a table name that is not a simple identifier', async () => {\n expect(\n await SqliteVecFragmentIndex.create({ database: db, tableName: 'bad name; DROP TABLE x' })\n ).toFailWith(/not a simple SQL identifier/i);\n });\n\n test('fails loudly when the sqlite-vec extension cannot load (closed database)', async () => {\n const closed = new BetterSqlite3(':memory:');\n closed.close();\n expect(await SqliteVecFragmentIndex.create({ database: closed })).toFailWith(/failed to initialize/i);\n });\n\n test('recovers no dimension from a pre-existing non-vec0 table of the same name', async () => {\n db.exec('CREATE TABLE memory_fragments (foo TEXT)');\n expect(await SqliteVecFragmentIndex.create({ database: db })).toSucceedAndSatisfy(\n (index: SqliteVecFragmentIndex) => {\n expect(index.recordCount).toBe(0);\n }\n );\n });\n });\n\n describe('addFragments', () => {\n test('stores every fragment and reports the count; tracks record/fragment counts', async () => {\n const index = await makeIndex();\n expect(\n await index.addFragments(target('knowledge', 'doc-a'), [frag(0, 5, 1, 0), frag(5, 10, 0, 1)])\n ).toSucceedWith(2);\n expect(index.recordCount).toBe(1);\n expect(index.fragmentCount).toBe(2);\n });\n\n test('whole-record replace — a second addFragments drops the prior fragments', async () => {\n const index = await makeIndex();\n const t = target('knowledge', 'doc-a');\n (await index.addFragments(t, [frag(0, 5, 1, 0), frag(5, 10, 0, 1)])).orThrow();\n expect(await index.addFragments(t, [frag(0, 3, 1, 1)])).toSucceedWith(1);\n expect(index.recordCount).toBe(1);\n expect(index.fragmentCount).toBe(1);\n expect(await index.query(Float32Array.from([1, 1]), 5)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits).toHaveLength(1);\n expect(hits[0].locator).toEqual(loc(0, 3));\n }\n );\n });\n\n test('an empty fragments array drops an existing record', async () => {\n const index = await makeIndex();\n const t = target('knowledge', 'doc-a');\n (await index.addFragments(t, [frag(0, 5, 1, 0)])).orThrow();\n expect(await index.addFragments(t, [])).toSucceedWith(0);\n expect(index.recordCount).toBe(0);\n expect(index.fragmentCount).toBe(0);\n });\n\n test('an empty fragments array on a fresh index is a no-op (no table created)', async () => {\n const index = await makeIndex();\n expect(await index.addFragments(target('knowledge', 'doc-a'), [])).toSucceedWith(0);\n expect(index.recordCount).toBe(0);\n // A later real add still establishes the dimension cleanly.\n expect(await index.addFragments(target('knowledge', 'doc-b'), [frag(0, 5, 1, 0)])).toSucceedWith(1);\n });\n\n test('same stem in different scopes are distinct entries', async () => {\n const index = await makeIndex();\n (await index.addFragments(target('conversations/a', 'turn-3'), [frag(0, 5, 1, 0)])).orThrow();\n (await index.addFragments(target('conversations/b', 'turn-3'), [frag(0, 5, 0, 1)])).orThrow();\n expect(index.recordCount).toBe(2);\n expect(index.fragmentCount).toBe(2);\n });\n\n test('fails loudly on an empty fragment vector — and stores nothing', async () => {\n const index = await makeIndex();\n expect(\n await index.addFragments(target('knowledge', 'doc-a'), [frag(0, 5, 1, 0), frag(5, 10)])\n ).toFailWith(/cannot add 'knowledge\\0doc-a': empty fragment vector/i);\n expect(index.recordCount).toBe(0);\n });\n\n test('fails loudly on a fragment-dimension mismatch against the established dimension', async () => {\n const index = await makeIndex();\n (await index.addFragments(target('knowledge', 'a'), [frag(0, 5, 1, 0)])).orThrow();\n expect(await index.addFragments(target('knowledge', 'b'), [frag(0, 5, 1, 0, 0)])).toFailWith(\n /fragment dimension 3 does not match index dimension 2/i\n );\n });\n\n test('rejects a non-safe-integer locator offset up front (never persists an unreadable locator)', async () => {\n const index = await makeIndex();\n // A non-integer offset: reject before the write, with a clear message.\n expect(await index.addFragments(target('knowledge', 'doc-a'), [frag(0.5, 5, 1, 0)])).toFailWith(\n /locator \\[0\\.5, 5\\) offsets must be safe integers/i\n );\n // A too-large (non-safe) integer offset is likewise rejected.\n expect(\n await index.addFragments(target('knowledge', 'doc-b'), [frag(0, Number.MAX_SAFE_INTEGER + 1, 1, 0)])\n ).toFailWith(/offsets must be safe integers/i);\n expect(index.recordCount).toBe(0);\n });\n\n test('a failed multi-fragment add on a fresh index does not establish a dimension (all-or-nothing)', async () => {\n const index = await makeIndex();\n // dim 2 then dim 3 in the same batch: fails, and must NOT commit dim 2.\n expect(\n await index.addFragments(target('knowledge', 'doc-1'), [frag(0, 5, 1, 0), frag(5, 10, 1, 0, 0)])\n ).toFailWith(/fragment dimension 3 does not match index dimension 2/i);\n expect(index.recordCount).toBe(0);\n // A fresh dim-3 record now indexes cleanly (the index was never poisoned to dim 2).\n expect(await index.addFragments(target('knowledge', 'doc-2'), [frag(0, 5, 1, 0, 0)])).toSucceedWith(1);\n });\n\n test('fails loudly (best-effort caller unaffected) when the underlying add throws', async () => {\n const index = await makeIndex();\n (await index.addFragments(target('knowledge', 'doc-a'), [frag(0, 5, 1, 0)])).orThrow();\n db.close();\n expect(await index.addFragments(target('knowledge', 'doc-b'), [frag(0, 5, 0, 1)])).toFailWith(\n /cannot add 'knowledge\\0doc-b'/i\n );\n });\n\n test('fails loudly (never silently corrupts) when the table name collides with a non-vec0 table', async () => {\n // A plain table already occupies the default name. `CREATE VIRTUAL TABLE IF NOT\n // EXISTS` no-ops against it, so preparing the vector INSERT against the missing\n // columns must fail loudly rather than corrupt state.\n db.exec('CREATE TABLE memory_fragments (foo TEXT)');\n const index = (await SqliteVecFragmentIndex.create({ database: db })).orThrow();\n expect(await index.addFragments(target('knowledge', 'doc-a'), [frag(0, 5, 1, 0)])).toFailWith(\n /cannot add 'knowledge\\0doc-a'/i\n );\n });\n });\n\n describe('query', () => {\n async function seeded(): Promise<SqliteVecFragmentIndex> {\n const index = await makeIndex();\n (\n await index.addFragments(target('knowledge', 'doc-a'), [frag(0, 5, 1, 0), frag(5, 10, 0, 1)])\n ).orThrow();\n (await index.addFragments(target('knowledge', 'doc-b'), [frag(0, 5, 1, 1)])).orThrow();\n return index;\n }\n\n test('returns fragment hits in descending score order, each carrying its locator', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 3)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits[0].target.id).toBe('doc-a');\n expect(hits[0].locator).toEqual(loc(0, 5));\n expect(hits[0].score).toBeCloseTo(1, 5);\n expect(hits[1].target.id).toBe('doc-b');\n expect(hits[1].score).toBeCloseTo(1 / Math.sqrt(2), 5);\n expect(hits[2].target.id).toBe('doc-a');\n expect(hits[2].locator).toEqual(loc(5, 10));\n expect(hits[2].score).toBeCloseTo(0, 5);\n }\n );\n });\n\n test('truncates to topK', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 2)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits).toHaveLength(2);\n expect(hits.map((h) => h.target.id)).toEqual(['doc-a', 'doc-b']);\n }\n );\n });\n\n test('maxPerRecord caps fragments per record during selection (before the topK cut)', async () => {\n const index = await makeIndex();\n (\n await index.addFragments(target('knowledge', 'doc-a'), [\n frag(0, 5, 1, 0),\n frag(5, 10, 0.9, 0.1),\n frag(10, 15, 0.8, 0.2)\n ])\n ).orThrow();\n (await index.addFragments(target('knowledge', 'doc-b'), [frag(0, 5, 0.7, 0.3)])).orThrow();\n // Without a cap the top-2 would be doc-a twice; maxPerRecord=1 surfaces doc-b.\n expect(await index.query(Float32Array.from([1, 0]), 2, 1)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits).toHaveLength(2);\n expect(hits.map((h) => h.target.id)).toEqual(['doc-a', 'doc-b']);\n expect(hits[0].locator).toEqual(loc(0, 5));\n }\n );\n });\n\n test('maxPerRecord=0 yields no hits', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 5, 0)).toSucceedWith([]);\n });\n\n test('maxPerRecord larger than any record leaves the ranking unchanged', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 5, 10)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits).toHaveLength(3);\n expect(hits[0].target.id).toBe('doc-a');\n }\n );\n });\n\n test('returns empty for a non-positive topK', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 0)).toSucceedWith([]);\n expect(await index.query(Float32Array.from([1, 0]), -5)).toSucceedWith([]);\n });\n\n test('returns empty before any add (no established dimension)', async () => {\n const index = await makeIndex();\n expect(await index.query(Float32Array.from([1, 0, 0]), 5)).toSucceedWith([]);\n });\n\n test('returns empty when a capped query runs against an emptied (but existing) table', async () => {\n const index = await makeIndex();\n const t = target('knowledge', 'doc-a');\n (await index.addFragments(t, [frag(0, 5, 1, 0)])).orThrow();\n (await index.addFragments(t, [])).orThrow(); // table now exists but holds 0 rows\n // fetchK derives from the (zero) fragment count under a cap → short-circuits to [].\n expect(await index.query(Float32Array.from([1, 0]), 5, 2)).toSucceedWith([]);\n });\n\n test('rejects a query vector of the wrong dimension', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0, 0]), 3)).toFailWith(\n /query dimension 3 does not match index dimension 2/i\n );\n });\n\n test('a capped query stops at topK even when more ranked rows remain', async () => {\n // maxPerRecord makes the fetch span the full ranked set (3 fragments), but\n // topK=1 must return exactly one hit — the top row — and stop.\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 1, 5)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits).toHaveLength(1);\n expect(hits[0].target.id).toBe('doc-a');\n expect(hits[0].locator).toEqual(loc(0, 5));\n }\n );\n });\n\n test('fails loudly when the underlying query throws', async () => {\n const index = await seeded();\n db.close();\n expect(await index.query(Float32Array.from([1, 0]), 3)).toFailWith(/query failed/i);\n });\n });\n\n describe('remove', () => {\n test('removes every fragment of a record and is reflected in subsequent queries', async () => {\n const index = await makeIndex();\n const a = target('knowledge', 'doc-a');\n const b = target('knowledge', 'doc-b');\n (await index.addFragments(a, [frag(0, 5, 1, 0), frag(5, 10, 1, 0)])).orThrow();\n (await index.addFragments(b, [frag(0, 5, 0, 1)])).orThrow();\n expect(await index.remove(a)).toSucceedWith(a);\n expect(index.recordCount).toBe(1);\n expect(index.fragmentCount).toBe(1);\n expect(await index.query(Float32Array.from([1, 0]), 5)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits.map((h) => h.target.id)).toEqual(['doc-b']);\n }\n );\n });\n\n test('is idempotent — removing a missing target succeeds', async () => {\n const index = await makeIndex();\n (await index.addFragments(target('knowledge', 'doc-a'), [frag(0, 5, 1, 0)])).orThrow();\n expect(await index.remove(target('knowledge', 'missing'))).toSucceedWith(\n target('knowledge', 'missing')\n );\n expect(index.recordCount).toBe(1);\n });\n\n test('succeeds before any add (no table yet)', async () => {\n const index = await makeIndex();\n expect(await index.remove(target('knowledge', 'doc-a'))).toSucceedWith(target('knowledge', 'doc-a'));\n expect(index.recordCount).toBe(0);\n });\n\n test('fails loudly when the underlying remove throws', async () => {\n const index = await makeIndex();\n (await index.addFragments(target('knowledge', 'doc-a'), [frag(0, 5, 1, 0)])).orThrow();\n db.close();\n expect(await index.remove(target('knowledge', 'doc-a'))).toFailWith(\n /cannot remove 'knowledge\\0doc-a'/i\n );\n });\n });\n\n describe('safe-integer mode and corrupt persisted data', () => {\n const toBlob = (...v: number[]): Uint8Array => new Uint8Array(Float32Array.from(v).buffer);\n\n async function seededWithRow(): Promise<SqliteVecFragmentIndex> {\n // A valid add creates the table and establishes dim 2; corrupt rows are then\n // inserted directly to model externally-edited / safe-integer-mode data.\n const index = await makeIndex();\n (await index.addFragments(target('knowledge', 'doc-a'), [frag(0, 5, 1, 0)])).orThrow();\n return index;\n }\n\n function insertRaw(key: string, start: bigint, end: bigint, ...vec: number[]): void {\n db.prepare(\n 'INSERT INTO memory_fragments(target_key, embedding, start_off, end_off) VALUES (?, ?, ?, ?)'\n ).run(key, toBlob(...vec), start, end);\n }\n\n test('coerces bigint offsets (better-sqlite3 safe-integer mode) to number locators', async () => {\n const index = await seededWithRow();\n // Under safe-integer mode every integer column comes back as a bigint.\n db.defaultSafeIntegers(true);\n expect(await index.query(Float32Array.from([1, 0]), 1)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n const locator = hits[0].locator;\n expect(locator).toEqual(loc(0, 5));\n expect(typeof locator?.start).toBe('number');\n expect(typeof locator?.end).toBe('number');\n }\n );\n });\n\n test('fails loudly when a stored offset is outside the safe-integer range', async () => {\n const index = await seededWithRow();\n // Safe-integer mode returns the huge offset as a bigint (no read-time throw),\n // so the _toOffset guard is what must fire.\n db.defaultSafeIntegers(true);\n insertRaw('knowledge\\0doc-b', BigInt(2) ** BigInt(60), BigInt(0), 0, 1);\n expect(await index.query(Float32Array.from([0, 1]), 5)).toFailWith(\n /locator offset .* is not a safe integer/i\n );\n });\n\n test('fails loudly when a stored key is missing the NUL separator', async () => {\n const index = await seededWithRow();\n insertRaw('nonulkey', BigInt(0), BigInt(5), 0, 1);\n expect(await index.query(Float32Array.from([0, 1]), 5)).toFailWith(/missing scope\\/id separator/i);\n });\n });\n\n describe('custom table name', () => {\n test('two fragment indexes on distinct tables in one database are independent', async () => {\n const a = (await SqliteVecFragmentIndex.create({ database: db, tableName: 'frag_a' })).orThrow();\n const b = (await SqliteVecFragmentIndex.create({ database: db, tableName: 'frag_b' })).orThrow();\n (await a.addFragments(target('s', 'one'), [frag(0, 5, 1, 0)])).orThrow();\n expect(a.fragmentCount).toBe(1);\n expect(b.fragmentCount).toBe(0);\n });\n });\n\n describe('persistence across reopen (the durability guarantee)', () => {\n let dir: string;\n let dbPath: string;\n\n beforeEach(() => {\n dir = fs.mkdtempSync(path.join(os.tmpdir(), 'svfrag-'));\n dbPath = path.join(dir, 'fragments.db');\n });\n afterEach(() => {\n fs.rmSync(dir, { recursive: true, force: true });\n });\n\n test('fragments written to a file survive a close + reopen with no re-embed, dimension + locators recovered', async () => {\n const first = new BetterSqlite3(dbPath);\n const writeIndex = (await SqliteVecFragmentIndex.create({ database: first })).orThrow();\n (\n await writeIndex.addFragments(target('knowledge', 'doc-a'), [\n frag(0, 5, 1, 0, 0),\n frag(5, 12, 0, 1, 0)\n ])\n ).orThrow();\n (\n await writeIndex.addFragments(target('conversations/c', 'turn-1'), [frag(0, 8, 0.9, 0.1, 0)])\n ).orThrow();\n first.close();\n\n // Second session: a brand-new connection + index over the same file. No adds\n // (no re-embedding) — fragments, dimension, and locators come straight off disk.\n const second = new BetterSqlite3(dbPath);\n const reopened = (await SqliteVecFragmentIndex.create({ database: second })).orThrow();\n try {\n expect(reopened.recordCount).toBe(2);\n expect(reopened.fragmentCount).toBe(3);\n expect(await reopened.query(Float32Array.from([1, 0, 0]), 3)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits[0].target).toEqual(target('knowledge', 'doc-a'));\n expect(hits[0].locator).toEqual(loc(0, 5));\n expect(hits[0].score).toBeCloseTo(1, 5);\n // the cross-scope target round-trips its full (scope, id) and locator\n expect(hits[1].target).toEqual(target('conversations/c', 'turn-1'));\n expect(hits[1].locator).toEqual(loc(0, 8));\n }\n );\n // The recovered dimension is enforced on a post-reopen add.\n expect(await reopened.addFragments(target('knowledge', 'w'), [frag(0, 4, 1, 0)])).toFailWith(\n /fragment dimension 2 does not match index dimension 3/i\n );\n // A matching-dimension add still works and persists incrementally.\n (await reopened.addFragments(target('knowledge', 'w'), [frag(0, 4, 0, 0, 1)])).orThrow();\n expect(reopened.recordCount).toBe(3);\n } finally {\n second.close();\n }\n });\n });\n});\n"]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Erik Fortune
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
import '@fgv/ts-utils-jest';
|
|
6
|
+
import BetterSqlite3 from 'better-sqlite3';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as os from 'os';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { SqliteVecVectorIndex } from '../../index';
|
|
11
|
+
function target(scope, id) {
|
|
12
|
+
return { scope: scope, id: id };
|
|
13
|
+
}
|
|
14
|
+
function vec(...values) {
|
|
15
|
+
return Float32Array.from(values);
|
|
16
|
+
}
|
|
17
|
+
describe('SqliteVecVectorIndex', () => {
|
|
18
|
+
let db;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
db = new BetterSqlite3(':memory:');
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
db.close();
|
|
24
|
+
});
|
|
25
|
+
async function makeIndex() {
|
|
26
|
+
return (await SqliteVecVectorIndex.create({ database: db })).orThrow();
|
|
27
|
+
}
|
|
28
|
+
describe('create', () => {
|
|
29
|
+
test('succeeds over a fresh database with an empty index', async () => {
|
|
30
|
+
expect(await SqliteVecVectorIndex.create({ database: db })).toSucceedAndSatisfy((index) => {
|
|
31
|
+
expect(index.size).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
test('rejects a table name that is not a simple identifier', async () => {
|
|
35
|
+
expect(await SqliteVecVectorIndex.create({ database: db, tableName: 'bad name; DROP TABLE x' })).toFailWith(/not a simple SQL identifier/i);
|
|
36
|
+
});
|
|
37
|
+
test('fails loudly when the sqlite-vec extension cannot load (closed database)', async () => {
|
|
38
|
+
const closed = new BetterSqlite3(':memory:');
|
|
39
|
+
closed.close();
|
|
40
|
+
expect(await SqliteVecVectorIndex.create({ database: closed })).toFailWith(/failed to initialize/i);
|
|
41
|
+
});
|
|
42
|
+
test('recovers no dimension from a pre-existing non-vec0 table of the same name', async () => {
|
|
43
|
+
// A plain table that happens to share the index's name has no `float[<n>]`
|
|
44
|
+
// in its DDL, so no dimension is recovered — the index opens as if empty.
|
|
45
|
+
db.exec('CREATE TABLE memory_vectors (foo TEXT)');
|
|
46
|
+
expect(await SqliteVecVectorIndex.create({ database: db })).toSucceedAndSatisfy((index) => {
|
|
47
|
+
expect(index.size).toBe(0);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('add', () => {
|
|
52
|
+
test('adds a vector and returns the canonical scoped key, growing size', async () => {
|
|
53
|
+
const index = await makeIndex();
|
|
54
|
+
expect(await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).toSucceedWith('knowledge\0doc-a');
|
|
55
|
+
expect(index.size).toBe(1);
|
|
56
|
+
});
|
|
57
|
+
test('rejects an empty vector', async () => {
|
|
58
|
+
const index = await makeIndex();
|
|
59
|
+
expect(await index.add(target('knowledge', 'doc-a'), vec())).toFailWith(/cannot add 'knowledge\0doc-a': empty vector/i);
|
|
60
|
+
});
|
|
61
|
+
test('rejects a vector whose dimension does not match the established dimension', async () => {
|
|
62
|
+
const index = await makeIndex();
|
|
63
|
+
(await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).orThrow();
|
|
64
|
+
expect(await index.add(target('knowledge', 'doc-b'), vec(1, 0))).toFailWith(/dimension 2 does not match index dimension 3/i);
|
|
65
|
+
});
|
|
66
|
+
test('replacing an existing target keeps size stable and the new vector wins the query', async () => {
|
|
67
|
+
const index = await makeIndex();
|
|
68
|
+
(await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).orThrow();
|
|
69
|
+
(await index.add(target('knowledge', 'doc-a'), vec(0, 0, 1))).orThrow();
|
|
70
|
+
expect(index.size).toBe(1);
|
|
71
|
+
expect(await index.query(vec(0, 0, 1), 1)).toSucceedAndSatisfy((hits) => {
|
|
72
|
+
expect(hits[0].target).toEqual(target('knowledge', 'doc-a'));
|
|
73
|
+
expect(hits[0].score).toBeCloseTo(1, 5);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
test('two records that share a stem across scopes are distinct entries', async () => {
|
|
77
|
+
const index = await makeIndex();
|
|
78
|
+
(await index.add(target('conversations/a', 'turn-3'), vec(1, 0, 0))).orThrow();
|
|
79
|
+
(await index.add(target('conversations/b', 'turn-3'), vec(0, 1, 0))).orThrow();
|
|
80
|
+
expect(index.size).toBe(2);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('query', () => {
|
|
84
|
+
async function seeded() {
|
|
85
|
+
const index = await makeIndex();
|
|
86
|
+
(await index.add(target('knowledge', 'x'), vec(1, 0, 0))).orThrow();
|
|
87
|
+
(await index.add(target('knowledge', 'y'), vec(0, 1, 0))).orThrow();
|
|
88
|
+
(await index.add(target('knowledge', 'z'), vec(0.9, 0.1, 0))).orThrow();
|
|
89
|
+
return index;
|
|
90
|
+
}
|
|
91
|
+
test('returns hits in descending cosine-similarity score with round-tripped scoped targets', async () => {
|
|
92
|
+
const index = await seeded();
|
|
93
|
+
expect(await index.query(vec(1, 0, 0), 3)).toSucceedAndSatisfy((hits) => {
|
|
94
|
+
expect(hits.map((h) => h.target.id)).toEqual(['x', 'z', 'y']);
|
|
95
|
+
expect(hits[0].target.scope).toBe('knowledge');
|
|
96
|
+
expect(hits[0].score).toBeCloseTo(1, 5); // identical
|
|
97
|
+
expect(hits[2].score).toBeCloseTo(0, 5); // orthogonal
|
|
98
|
+
// scores are sorted descending
|
|
99
|
+
expect(hits[0].score).toBeGreaterThan(hits[1].score);
|
|
100
|
+
expect(hits[1].score).toBeGreaterThan(hits[2].score);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
test('honors topK', async () => {
|
|
104
|
+
const index = await seeded();
|
|
105
|
+
expect(await index.query(vec(1, 0, 0), 2)).toSucceedAndSatisfy((hits) => {
|
|
106
|
+
expect(hits).toHaveLength(2);
|
|
107
|
+
expect(hits.map((h) => h.target.id)).toEqual(['x', 'z']);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
test('returns empty for topK <= 0', async () => {
|
|
111
|
+
const index = await seeded();
|
|
112
|
+
expect(await index.query(vec(1, 0, 0), 0)).toSucceedWith([]);
|
|
113
|
+
expect(await index.query(vec(1, 0, 0), -5)).toSucceedWith([]);
|
|
114
|
+
});
|
|
115
|
+
test('returns empty before any add (no established dimension)', async () => {
|
|
116
|
+
const index = await makeIndex();
|
|
117
|
+
expect(await index.query(vec(1, 0, 0), 5)).toSucceedWith([]);
|
|
118
|
+
});
|
|
119
|
+
test('rejects a query vector of the wrong dimension', async () => {
|
|
120
|
+
const index = await seeded();
|
|
121
|
+
expect(await index.query(vec(1, 0), 3)).toFailWith(/query dimension 2 does not match index dimension 3/i);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe('remove', () => {
|
|
125
|
+
test('removes an entry and shrinks size, returning the target', async () => {
|
|
126
|
+
const index = await makeIndex();
|
|
127
|
+
(await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).orThrow();
|
|
128
|
+
(await index.add(target('knowledge', 'doc-b'), vec(0, 1, 0))).orThrow();
|
|
129
|
+
expect(await index.remove(target('knowledge', 'doc-a'))).toSucceedWith(target('knowledge', 'doc-a'));
|
|
130
|
+
expect(index.size).toBe(1);
|
|
131
|
+
expect(await index.query(vec(1, 0, 0), 5)).toSucceedAndSatisfy((hits) => {
|
|
132
|
+
expect(hits.map((h) => h.target.id)).toEqual(['doc-b']);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
test('is idempotent — removing a missing target succeeds', async () => {
|
|
136
|
+
const index = await makeIndex();
|
|
137
|
+
(await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).orThrow();
|
|
138
|
+
expect(await index.remove(target('knowledge', 'missing'))).toSucceedWith(target('knowledge', 'missing'));
|
|
139
|
+
expect(index.size).toBe(1);
|
|
140
|
+
});
|
|
141
|
+
test('succeeds before any add (no table yet)', async () => {
|
|
142
|
+
const index = await makeIndex();
|
|
143
|
+
expect(await index.remove(target('knowledge', 'doc-a'))).toSucceedWith(target('knowledge', 'doc-a'));
|
|
144
|
+
expect(index.size).toBe(0);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe('custom table name', () => {
|
|
148
|
+
test('two indexes on distinct tables in one database are independent', async () => {
|
|
149
|
+
const a = (await SqliteVecVectorIndex.create({ database: db, tableName: 'idx_a' })).orThrow();
|
|
150
|
+
const b = (await SqliteVecVectorIndex.create({ database: db, tableName: 'idx_b' })).orThrow();
|
|
151
|
+
(await a.add(target('s', 'one'), vec(1, 0))).orThrow();
|
|
152
|
+
expect(a.size).toBe(1);
|
|
153
|
+
expect(b.size).toBe(0);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('persistence across reopen (the durability guarantee)', () => {
|
|
157
|
+
let dir;
|
|
158
|
+
let dbPath;
|
|
159
|
+
beforeEach(() => {
|
|
160
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'svtest-'));
|
|
161
|
+
dbPath = path.join(dir, 'vectors.db');
|
|
162
|
+
});
|
|
163
|
+
afterEach(() => {
|
|
164
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
165
|
+
});
|
|
166
|
+
test('vectors written to a file survive a close + reopen with no re-embed, and the dimension is recovered', async () => {
|
|
167
|
+
// First session: write three embeddings, then close the connection entirely.
|
|
168
|
+
const first = new BetterSqlite3(dbPath);
|
|
169
|
+
const writeIndex = (await SqliteVecVectorIndex.create({ database: first })).orThrow();
|
|
170
|
+
(await writeIndex.add(target('knowledge', 'x'), vec(1, 0, 0))).orThrow();
|
|
171
|
+
(await writeIndex.add(target('knowledge', 'y'), vec(0, 1, 0))).orThrow();
|
|
172
|
+
(await writeIndex.add(target('conversations/c', 'turn-1'), vec(0.9, 0.1, 0))).orThrow();
|
|
173
|
+
first.close();
|
|
174
|
+
// Second session: a brand-new connection + index over the same file. No add
|
|
175
|
+
// calls (no re-embedding) — the vectors and the established dimension come
|
|
176
|
+
// straight off disk.
|
|
177
|
+
const second = new BetterSqlite3(dbPath);
|
|
178
|
+
const reopened = (await SqliteVecVectorIndex.create({ database: second })).orThrow();
|
|
179
|
+
try {
|
|
180
|
+
expect(reopened.size).toBe(3);
|
|
181
|
+
expect(await reopened.query(vec(1, 0, 0), 3)).toSucceedAndSatisfy((hits) => {
|
|
182
|
+
expect(hits.map((h) => h.target.id)).toEqual(['x', 'turn-1', 'y']);
|
|
183
|
+
expect(hits[0].score).toBeCloseTo(1, 5);
|
|
184
|
+
// the cross-scope target round-trips its full (scope, id)
|
|
185
|
+
expect(hits[1].target).toEqual(target('conversations/c', 'turn-1'));
|
|
186
|
+
});
|
|
187
|
+
// The recovered dimension is enforced on a post-reopen add.
|
|
188
|
+
expect(await reopened.add(target('knowledge', 'w'), vec(1, 0))).toFailWith(/dimension 2 does not match index dimension 3/i);
|
|
189
|
+
// A matching-dimension add still works and persists incrementally.
|
|
190
|
+
(await reopened.add(target('knowledge', 'w'), vec(0, 0, 1))).orThrow();
|
|
191
|
+
expect(reopened.size).toBe(4);
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
second.close();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
//# sourceMappingURL=sqliteVecVectorIndex.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqliteVecVectorIndex.test.js","sourceRoot":"","sources":["../../../src/test/unit/sqliteVecVectorIndex.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport '@fgv/ts-utils-jest';\n\nimport BetterSqlite3 from 'better-sqlite3';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport { IEdgeTarget, IVectorQueryHit, MemoryId, MemoryScopeKey } from '@fgv/ts-agent-memory';\nimport { SqliteVecVectorIndex } from '../../index';\n\nfunction target(scope: string, id: string): IEdgeTarget {\n return { scope: scope as unknown as MemoryScopeKey, id: id as unknown as MemoryId };\n}\nfunction vec(...values: number[]): Float32Array {\n return Float32Array.from(values);\n}\n\ndescribe('SqliteVecVectorIndex', () => {\n let db: BetterSqlite3.Database;\n\n beforeEach(() => {\n db = new BetterSqlite3(':memory:');\n });\n afterEach(() => {\n db.close();\n });\n\n async function makeIndex(): Promise<SqliteVecVectorIndex> {\n return (await SqliteVecVectorIndex.create({ database: db })).orThrow();\n }\n\n describe('create', () => {\n test('succeeds over a fresh database with an empty index', async () => {\n expect(await SqliteVecVectorIndex.create({ database: db })).toSucceedAndSatisfy(\n (index: SqliteVecVectorIndex) => {\n expect(index.size).toBe(0);\n }\n );\n });\n\n test('rejects a table name that is not a simple identifier', async () => {\n expect(\n await SqliteVecVectorIndex.create({ database: db, tableName: 'bad name; DROP TABLE x' })\n ).toFailWith(/not a simple SQL identifier/i);\n });\n\n test('fails loudly when the sqlite-vec extension cannot load (closed database)', async () => {\n const closed = new BetterSqlite3(':memory:');\n closed.close();\n expect(await SqliteVecVectorIndex.create({ database: closed })).toFailWith(/failed to initialize/i);\n });\n\n test('recovers no dimension from a pre-existing non-vec0 table of the same name', async () => {\n // A plain table that happens to share the index's name has no `float[<n>]`\n // in its DDL, so no dimension is recovered — the index opens as if empty.\n db.exec('CREATE TABLE memory_vectors (foo TEXT)');\n expect(await SqliteVecVectorIndex.create({ database: db })).toSucceedAndSatisfy(\n (index: SqliteVecVectorIndex) => {\n expect(index.size).toBe(0);\n }\n );\n });\n });\n\n describe('add', () => {\n test('adds a vector and returns the canonical scoped key, growing size', async () => {\n const index = await makeIndex();\n expect(await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).toSucceedWith('knowledge\\0doc-a');\n expect(index.size).toBe(1);\n });\n\n test('rejects an empty vector', async () => {\n const index = await makeIndex();\n expect(await index.add(target('knowledge', 'doc-a'), vec())).toFailWith(\n /cannot add 'knowledge\\0doc-a': empty vector/i\n );\n });\n\n test('rejects a vector whose dimension does not match the established dimension', async () => {\n const index = await makeIndex();\n (await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).orThrow();\n expect(await index.add(target('knowledge', 'doc-b'), vec(1, 0))).toFailWith(\n /dimension 2 does not match index dimension 3/i\n );\n });\n\n test('replacing an existing target keeps size stable and the new vector wins the query', async () => {\n const index = await makeIndex();\n (await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).orThrow();\n (await index.add(target('knowledge', 'doc-a'), vec(0, 0, 1))).orThrow();\n expect(index.size).toBe(1);\n expect(await index.query(vec(0, 0, 1), 1)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits[0].target).toEqual(target('knowledge', 'doc-a'));\n expect(hits[0].score).toBeCloseTo(1, 5);\n }\n );\n });\n\n test('two records that share a stem across scopes are distinct entries', async () => {\n const index = await makeIndex();\n (await index.add(target('conversations/a', 'turn-3'), vec(1, 0, 0))).orThrow();\n (await index.add(target('conversations/b', 'turn-3'), vec(0, 1, 0))).orThrow();\n expect(index.size).toBe(2);\n });\n });\n\n describe('query', () => {\n async function seeded(): Promise<SqliteVecVectorIndex> {\n const index = await makeIndex();\n (await index.add(target('knowledge', 'x'), vec(1, 0, 0))).orThrow();\n (await index.add(target('knowledge', 'y'), vec(0, 1, 0))).orThrow();\n (await index.add(target('knowledge', 'z'), vec(0.9, 0.1, 0))).orThrow();\n return index;\n }\n\n test('returns hits in descending cosine-similarity score with round-tripped scoped targets', async () => {\n const index = await seeded();\n expect(await index.query(vec(1, 0, 0), 3)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits.map((h) => h.target.id)).toEqual(['x', 'z', 'y']);\n expect(hits[0].target.scope).toBe('knowledge');\n expect(hits[0].score).toBeCloseTo(1, 5); // identical\n expect(hits[2].score).toBeCloseTo(0, 5); // orthogonal\n // scores are sorted descending\n expect(hits[0].score).toBeGreaterThan(hits[1].score);\n expect(hits[1].score).toBeGreaterThan(hits[2].score);\n }\n );\n });\n\n test('honors topK', async () => {\n const index = await seeded();\n expect(await index.query(vec(1, 0, 0), 2)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits).toHaveLength(2);\n expect(hits.map((h) => h.target.id)).toEqual(['x', 'z']);\n }\n );\n });\n\n test('returns empty for topK <= 0', async () => {\n const index = await seeded();\n expect(await index.query(vec(1, 0, 0), 0)).toSucceedWith([]);\n expect(await index.query(vec(1, 0, 0), -5)).toSucceedWith([]);\n });\n\n test('returns empty before any add (no established dimension)', async () => {\n const index = await makeIndex();\n expect(await index.query(vec(1, 0, 0), 5)).toSucceedWith([]);\n });\n\n test('rejects a query vector of the wrong dimension', async () => {\n const index = await seeded();\n expect(await index.query(vec(1, 0), 3)).toFailWith(\n /query dimension 2 does not match index dimension 3/i\n );\n });\n });\n\n describe('remove', () => {\n test('removes an entry and shrinks size, returning the target', async () => {\n const index = await makeIndex();\n (await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).orThrow();\n (await index.add(target('knowledge', 'doc-b'), vec(0, 1, 0))).orThrow();\n expect(await index.remove(target('knowledge', 'doc-a'))).toSucceedWith(target('knowledge', 'doc-a'));\n expect(index.size).toBe(1);\n expect(await index.query(vec(1, 0, 0), 5)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits.map((h) => h.target.id)).toEqual(['doc-b']);\n }\n );\n });\n\n test('is idempotent — removing a missing target succeeds', async () => {\n const index = await makeIndex();\n (await index.add(target('knowledge', 'doc-a'), vec(1, 0, 0))).orThrow();\n expect(await index.remove(target('knowledge', 'missing'))).toSucceedWith(\n target('knowledge', 'missing')\n );\n expect(index.size).toBe(1);\n });\n\n test('succeeds before any add (no table yet)', async () => {\n const index = await makeIndex();\n expect(await index.remove(target('knowledge', 'doc-a'))).toSucceedWith(target('knowledge', 'doc-a'));\n expect(index.size).toBe(0);\n });\n });\n\n describe('custom table name', () => {\n test('two indexes on distinct tables in one database are independent', async () => {\n const a = (await SqliteVecVectorIndex.create({ database: db, tableName: 'idx_a' })).orThrow();\n const b = (await SqliteVecVectorIndex.create({ database: db, tableName: 'idx_b' })).orThrow();\n (await a.add(target('s', 'one'), vec(1, 0))).orThrow();\n expect(a.size).toBe(1);\n expect(b.size).toBe(0);\n });\n });\n\n describe('persistence across reopen (the durability guarantee)', () => {\n let dir: string;\n let dbPath: string;\n\n beforeEach(() => {\n dir = fs.mkdtempSync(path.join(os.tmpdir(), 'svtest-'));\n dbPath = path.join(dir, 'vectors.db');\n });\n afterEach(() => {\n fs.rmSync(dir, { recursive: true, force: true });\n });\n\n test('vectors written to a file survive a close + reopen with no re-embed, and the dimension is recovered', async () => {\n // First session: write three embeddings, then close the connection entirely.\n const first = new BetterSqlite3(dbPath);\n const writeIndex = (await SqliteVecVectorIndex.create({ database: first })).orThrow();\n (await writeIndex.add(target('knowledge', 'x'), vec(1, 0, 0))).orThrow();\n (await writeIndex.add(target('knowledge', 'y'), vec(0, 1, 0))).orThrow();\n (await writeIndex.add(target('conversations/c', 'turn-1'), vec(0.9, 0.1, 0))).orThrow();\n first.close();\n\n // Second session: a brand-new connection + index over the same file. No add\n // calls (no re-embedding) — the vectors and the established dimension come\n // straight off disk.\n const second = new BetterSqlite3(dbPath);\n const reopened = (await SqliteVecVectorIndex.create({ database: second })).orThrow();\n try {\n expect(reopened.size).toBe(3);\n expect(await reopened.query(vec(1, 0, 0), 3)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits.map((h) => h.target.id)).toEqual(['x', 'turn-1', 'y']);\n expect(hits[0].score).toBeCloseTo(1, 5);\n // the cross-scope target round-trips its full (scope, id)\n expect(hits[1].target).toEqual(target('conversations/c', 'turn-1'));\n }\n );\n // The recovered dimension is enforced on a post-reopen add.\n expect(await reopened.add(target('knowledge', 'w'), vec(1, 0))).toFailWith(\n /dimension 2 does not match index dimension 3/i\n );\n // A matching-dimension add still works and persists incrementally.\n (await reopened.add(target('knowledge', 'w'), vec(0, 0, 1))).orThrow();\n expect(reopened.size).toBe(4);\n } finally {\n second.close();\n }\n });\n });\n});\n"]}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type BetterSqlite3 from 'better-sqlite3';
|
|
2
|
+
import { IEdgeTarget } from '@fgv/ts-agent-memory';
|
|
3
|
+
import { IEmbeddedFragment } from '@fgv/ts-agent-memory';
|
|
4
|
+
import { IFragmentVectorIndex } from '@fgv/ts-agent-memory';
|
|
5
|
+
import { IVectorIndex } from '@fgv/ts-agent-memory';
|
|
6
|
+
import { IVectorQueryHit } from '@fgv/ts-agent-memory';
|
|
7
|
+
import { Result } from '@fgv/ts-utils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parameters for {@link SqliteVecFragmentIndex.create}.
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export declare interface ISqliteVecFragmentIndexCreateParams {
|
|
14
|
+
/**
|
|
15
|
+
* A `better-sqlite3` `Database` the consumer owns (bring-your-own, mirroring
|
|
16
|
+
* {@link ISqliteVecVectorIndexCreateParams.database}). The consumer opens it and
|
|
17
|
+
* owns its lifecycle — this index never closes it. `create` loads the
|
|
18
|
+
* `sqlite-vec` extension onto the connection and, if the fragment table already
|
|
19
|
+
* exists (a reopened persistent file), recovers its established dimension so no
|
|
20
|
+
* re-embedding is required on open.
|
|
21
|
+
*/
|
|
22
|
+
readonly database: BetterSqlite3.Database;
|
|
23
|
+
/**
|
|
24
|
+
* Name of the `vec0` virtual table that holds the fragment embeddings. Must be a
|
|
25
|
+
* simple SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to
|
|
26
|
+
* `'memory_fragments'`. Supply a distinct name (distinct from any record-level
|
|
27
|
+
* {@link SqliteVecVectorIndex} table) to hold more than one independent index in
|
|
28
|
+
* a single database file.
|
|
29
|
+
*/
|
|
30
|
+
readonly tableName?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parameters for {@link SqliteVecVectorIndex.create}.
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export declare interface ISqliteVecVectorIndexCreateParams {
|
|
38
|
+
/**
|
|
39
|
+
* A `better-sqlite3` `Database` the consumer owns (bring-your-own, mirroring
|
|
40
|
+
* the boundary-package convention). The consumer opens it (`new Database(path)`
|
|
41
|
+
* for a persistent file, or `new Database(':memory:')` for an ephemeral index)
|
|
42
|
+
* and owns its lifecycle — this index never closes it. `create` loads the
|
|
43
|
+
* `sqlite-vec` extension onto the connection and, if the vector table already
|
|
44
|
+
* exists (a reopened persistent file), recovers its established dimension so no
|
|
45
|
+
* re-embedding is required on open.
|
|
46
|
+
*/
|
|
47
|
+
readonly database: BetterSqlite3.Database;
|
|
48
|
+
/**
|
|
49
|
+
* Name of the `vec0` virtual table that holds the embeddings. Must be a simple
|
|
50
|
+
* SQL identifier (`[A-Za-z_][A-Za-z0-9_]*`). Defaults to `'memory_vectors'`.
|
|
51
|
+
* Supply a distinct name to hold more than one independent index in a single
|
|
52
|
+
* database file.
|
|
53
|
+
*/
|
|
54
|
+
readonly tableName?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A persistent, `sqlite-vec`-backed `IFragmentVectorIndex` (from
|
|
59
|
+
* `@fgv/ts-agent-memory`) — the fragment-granular sibling of
|
|
60
|
+
* {@link SqliteVecVectorIndex}, and the **durable** counterpart to the in-memory
|
|
61
|
+
* `InMemoryFragmentCosineIndex`.
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
* Where {@link SqliteVecVectorIndex} keys one vector per record on a
|
|
65
|
+
* `target_key` primary key, this index holds **many** vectors per record — one per
|
|
66
|
+
* in-record `[start, end)` span — so it keys the `vec0` table on `target_key` as a
|
|
67
|
+
* **`PARTITION KEY`** (many rows may share it) and stores each fragment's locator
|
|
68
|
+
* offsets in two auxiliary columns (`+start_off`, `+end_off`) that ride alongside
|
|
69
|
+
* the vector and are returned on query but never filtered. A query is a brute-force
|
|
70
|
+
* `vec0` KNN scan across all partitions returning per-fragment hits, each carrying
|
|
71
|
+
* its record `target` and the matched `locator`.
|
|
72
|
+
*
|
|
73
|
+
* Semantics match `InMemoryFragmentCosineIndex` exactly: `addFragments` is
|
|
74
|
+
* whole-record-replace (a single transaction deletes every prior fragment of the
|
|
75
|
+
* target, then inserts the new set), `remove` drops every fragment of a target,
|
|
76
|
+
* and `query` applies the optional `maxPerRecord` cap **during selection, before
|
|
77
|
+
* the topK cut** — so one long document cannot crowd others out. The dimension is
|
|
78
|
+
* established by the first `addFragments` (the `vec0` column is fixed-width) and
|
|
79
|
+
* recovered from the table schema when a persistent file is reopened; similarity is
|
|
80
|
+
* cosine (`score = 1 - cosineDistance`), byte-identical to the in-memory index.
|
|
81
|
+
* Large-N ANN indexing is explicitly out of scope, same regime as the record index.
|
|
82
|
+
*
|
|
83
|
+
* The `better-sqlite3` `Database` is consumer-owned (bring-your-own): this index
|
|
84
|
+
* loads the `sqlite-vec` extension onto it and reads/writes the table, but never
|
|
85
|
+
* opens or closes the connection.
|
|
86
|
+
* @public
|
|
87
|
+
*/
|
|
88
|
+
export declare class SqliteVecFragmentIndex implements IFragmentVectorIndex {
|
|
89
|
+
private readonly _db;
|
|
90
|
+
private readonly _table;
|
|
91
|
+
/** The dimension of every stored fragment vector; `undefined` until the table exists. */
|
|
92
|
+
private _dimension;
|
|
93
|
+
/** Prepared statements; created once the table exists (established or recovered). */
|
|
94
|
+
private _stmts;
|
|
95
|
+
private constructor();
|
|
96
|
+
/** The number of records that currently have at least one stored fragment. Zero before the first add. */
|
|
97
|
+
get recordCount(): number;
|
|
98
|
+
/** The total number of fragments currently held across all records. Zero before the first add. */
|
|
99
|
+
get fragmentCount(): number;
|
|
100
|
+
/**
|
|
101
|
+
* Family-convention factory. Loads the `sqlite-vec` extension onto the supplied
|
|
102
|
+
* `better-sqlite3` connection and, if the fragment table already exists (a
|
|
103
|
+
* reopened persistent file), recovers its established dimension so no
|
|
104
|
+
* re-embedding is needed on open.
|
|
105
|
+
*
|
|
106
|
+
* @param params - See {@link ISqliteVecFragmentIndexCreateParams}.
|
|
107
|
+
* @returns `Success` with the index, or `Failure` if the table name is not a
|
|
108
|
+
* simple identifier or the extension fails to load.
|
|
109
|
+
*/
|
|
110
|
+
static create(params: ISqliteVecFragmentIndexCreateParams): Promise<Result<SqliteVecFragmentIndex>>;
|
|
111
|
+
/** {@inheritDoc IFragmentVectorIndex.addFragments} */
|
|
112
|
+
addFragments(target: IEdgeTarget, fragments: ReadonlyArray<IEmbeddedFragment>): Promise<Result<number>>;
|
|
113
|
+
/** {@inheritDoc IFragmentVectorIndex.remove} */
|
|
114
|
+
remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
|
|
115
|
+
/** {@inheritDoc IFragmentVectorIndex.query} */
|
|
116
|
+
query(vector: Float32Array, topK: number, maxPerRecord?: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
|
|
117
|
+
/** Create the fragment `vec0` virtual table with the established dimension. */
|
|
118
|
+
private _createTable;
|
|
119
|
+
/** Prepare the statements the index reuses. Requires the table to exist. */
|
|
120
|
+
private _prepare;
|
|
121
|
+
/**
|
|
122
|
+
* Recover the established dimension of an existing fragment `vec0` table from its
|
|
123
|
+
* stored `CREATE VIRTUAL TABLE` SQL (`float[<n>]`). Returns `undefined` when the
|
|
124
|
+
* table does not exist yet (a fresh database — dimension is set by the first add).
|
|
125
|
+
*/
|
|
126
|
+
private static _readExistingDimension;
|
|
127
|
+
/** Pack a `Float32Array` as the little-endian byte blob `vec0` stores. Copies, so the caller may reuse its buffer. */
|
|
128
|
+
private static _toBlob;
|
|
129
|
+
/**
|
|
130
|
+
* Reverse `edgeTargetKey` — the canonical key is `scope\0id` with NUL excluded
|
|
131
|
+
* from both components, so the first NUL splits it unambiguously. A key with no
|
|
132
|
+
* NUL cannot have been written by `edgeTargetKey`; rather than fabricate a wrong
|
|
133
|
+
* `(scope, id)` from corrupt / externally-edited table data, throw so the query
|
|
134
|
+
* surfaces it as a loud `Failure`.
|
|
135
|
+
*/
|
|
136
|
+
private static _parseKey;
|
|
137
|
+
/**
|
|
138
|
+
* Coerce a persisted locator offset to a plain `number`. `better-sqlite3` returns
|
|
139
|
+
* integer columns as `bigint` under safe-integer mode, so an offset can arrive as
|
|
140
|
+
* either; both narrow to `number` here. A value outside the safe-integer range
|
|
141
|
+
* (only reachable via corrupt / externally-edited data — the index only ever
|
|
142
|
+
* writes in-document offsets) throws rather than silently losing precision, so the
|
|
143
|
+
* query surfaces it as a loud `Failure`.
|
|
144
|
+
*/
|
|
145
|
+
private static _toOffset;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A persistent, `sqlite-vec`-backed `IVectorIndex` for `@fgv/ts-agent-memory`.
|
|
150
|
+
*
|
|
151
|
+
* @remarks
|
|
152
|
+
* This is the **durable** counterpart to the in-memory `InMemoryCosineIndex`:
|
|
153
|
+
* embeddings live in a `sqlite-vec` `vec0` virtual table inside a `better-sqlite3`
|
|
154
|
+
* database, so they survive a process restart. A consumer that wires this index
|
|
155
|
+
* into `FileTreeMemoryStore` (instead of the in-memory index) opens an existing
|
|
156
|
+
* vault **without re-embedding it** — the vectors are already on disk. New writes
|
|
157
|
+
* still flow through the store's incremental embed-on-write path; there is no core
|
|
158
|
+
* store change.
|
|
159
|
+
*
|
|
160
|
+
* The index is keyed by the canonical `edgeTargetKey` of each record's
|
|
161
|
+
* scope-qualified `(scope, id)` address (a `TEXT PRIMARY KEY` on the `vec0` table),
|
|
162
|
+
* so two records that share a filename stem across scopes never collide. The
|
|
163
|
+
* dimension is established by the first `add` (the `vec0` column is fixed-width) and
|
|
164
|
+
* recovered from the table schema when a persistent file is reopened; every later
|
|
165
|
+
* `add`/`query` must match it or fail loudly, exactly as the in-memory index does.
|
|
166
|
+
* Similarity is cosine (`distance_metric=cosine`): the returned `score` is
|
|
167
|
+
* `1 - cosineDistance`, i.e. cosine similarity in `[-1, 1]`, higher = more similar —
|
|
168
|
+
* byte-for-byte the same scoring contract as `InMemoryCosineIndex`.
|
|
169
|
+
*
|
|
170
|
+
* Query is a brute-force `vec0` KNN scan (not an ANN structure): correct and
|
|
171
|
+
* durable, appropriate for the same "thousands of records" regime the in-memory
|
|
172
|
+
* index targets. Large-N ANN indexing is explicitly out of scope — see the README.
|
|
173
|
+
*
|
|
174
|
+
* The `better-sqlite3` `Database` is consumer-owned (bring-your-own): this index
|
|
175
|
+
* loads the `sqlite-vec` extension onto it and reads/writes the table, but never
|
|
176
|
+
* opens or closes the connection.
|
|
177
|
+
* @public
|
|
178
|
+
*/
|
|
179
|
+
export declare class SqliteVecVectorIndex implements IVectorIndex {
|
|
180
|
+
private readonly _db;
|
|
181
|
+
private readonly _table;
|
|
182
|
+
/** The dimension of every stored vector; `undefined` until the table exists (first `add` or a reopened non-empty file). */
|
|
183
|
+
private _dimension;
|
|
184
|
+
/** Prepared statements; created once the table exists (established or recovered). */
|
|
185
|
+
private _stmts;
|
|
186
|
+
private constructor();
|
|
187
|
+
/** The number of vectors currently held. Zero before the first `add`. */
|
|
188
|
+
get size(): number;
|
|
189
|
+
/**
|
|
190
|
+
* Family-convention factory. Loads the `sqlite-vec` extension onto the supplied
|
|
191
|
+
* `better-sqlite3` connection and, if the vector table already exists (a reopened
|
|
192
|
+
* persistent file), recovers its established dimension so no re-embedding is
|
|
193
|
+
* needed on open.
|
|
194
|
+
*
|
|
195
|
+
* @param params - See {@link ISqliteVecVectorIndexCreateParams}.
|
|
196
|
+
* @returns `Success` with the index, or `Failure` if the table name is not a
|
|
197
|
+
* simple identifier or the extension fails to load.
|
|
198
|
+
*/
|
|
199
|
+
static create(params: ISqliteVecVectorIndexCreateParams): Promise<Result<SqliteVecVectorIndex>>;
|
|
200
|
+
/** {@inheritDoc IVectorIndex.add} */
|
|
201
|
+
add(target: IEdgeTarget, vector: Float32Array): Promise<Result<string>>;
|
|
202
|
+
/** {@inheritDoc IVectorIndex.remove} */
|
|
203
|
+
remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
|
|
204
|
+
/** {@inheritDoc IVectorIndex.query} */
|
|
205
|
+
query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
|
|
206
|
+
/** Create the `vec0` virtual table with the established dimension. */
|
|
207
|
+
private _createTable;
|
|
208
|
+
/** Prepare the statements the index reuses. Requires the table to exist. */
|
|
209
|
+
private _prepare;
|
|
210
|
+
/**
|
|
211
|
+
* Recover the established dimension of an existing `vec0` table from its stored
|
|
212
|
+
* `CREATE VIRTUAL TABLE` SQL (`float[<n>]`). Returns `undefined` when the table
|
|
213
|
+
* does not exist yet (a fresh database — dimension is set by the first `add`).
|
|
214
|
+
*/
|
|
215
|
+
private static _readExistingDimension;
|
|
216
|
+
/** Pack a `Float32Array` as the little-endian byte blob `vec0` stores. Copies, so the caller may reuse its buffer. */
|
|
217
|
+
private static _toBlob;
|
|
218
|
+
/**
|
|
219
|
+
* Reverse `edgeTargetKey` — the canonical key is `scope\0id` with NUL
|
|
220
|
+
* excluded from both components, so the first NUL splits it unambiguously.
|
|
221
|
+
*/
|
|
222
|
+
private static _parseKey;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export { }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
|
2
|
+
// It should be published with your NPM package. It should not be tracked by Git.
|
|
3
|
+
{
|
|
4
|
+
"tsdocVersion": "0.12",
|
|
5
|
+
"toolPackages": [
|
|
6
|
+
{
|
|
7
|
+
"packageName": "@microsoft/api-extractor",
|
|
8
|
+
"packageVersion": "7.58.9"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// ESLint 9 flat config
|
|
2
|
+
const nodeProfile = require('@rushstack/eslint-config/flat/profile/node');
|
|
3
|
+
const packletsPlugin = require('@rushstack/eslint-config/flat/mixins/packlets');
|
|
4
|
+
const tsdocPlugin = require('@rushstack/eslint-config/flat/mixins/tsdoc');
|
|
5
|
+
|
|
6
|
+
module.exports = [
|
|
7
|
+
...nodeProfile,
|
|
8
|
+
packletsPlugin,
|
|
9
|
+
...tsdocPlugin,
|
|
10
|
+
{
|
|
11
|
+
rules: {
|
|
12
|
+
'@rushstack/packlets/mechanics': 'warn'
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
];
|