@vivantel/virage-store-lancedb 0.2.0

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/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @vivantel/virage-store-lancedb
2
+
3
+ [![npm](https://img.shields.io/npm/v/@vivantel/virage-store-lancedb.svg)](https://www.npmjs.com/package/@vivantel/virage-store-lancedb)
4
+
5
+ LanceDB vector store for [`@vivantel/virage-core`](../rag-core/README.md). Embedded and file-based — no server required. Data lives in a local directory (or LanceDB Cloud with an API key).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @vivantel/virage-store-lancedb @vivantel/virage-core
11
+ ```
12
+
13
+ ## Quick start (JSON config)
14
+
15
+ Local file storage — no server needed:
16
+
17
+ ```json
18
+ {
19
+ "vectorStore": {
20
+ "package": "@vivantel/virage-store-lancedb",
21
+ "config": {
22
+ "uri": "./lancedb"
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ## LanceDB Cloud
29
+
30
+ Pass your LanceDB Cloud URI and API key:
31
+
32
+ ```json
33
+ {
34
+ "vectorStore": {
35
+ "package": "@vivantel/virage-store-lancedb",
36
+ "config": {
37
+ "uri": "db://my-project",
38
+ "apiKey": "${LANCEDB_API_KEY}"
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ ## Configuration
45
+
46
+ | Option | Type | Default | Description |
47
+ |--------|------|---------|-------------|
48
+ | `uri` | `string` | **required** | Local path (`"./lancedb"`) or LanceDB Cloud URI (`"db://…"`) |
49
+ | `apiKey` | `string` | `undefined` | LanceDB Cloud API key |
50
+ | `tableName` | `string` | `"documents"` | Table name inside the database |
51
+ | `dimensions` | `number` | `1536` | Vector size — must match your embedder |
52
+
53
+ ## TypeScript usage
54
+
55
+ ```typescript
56
+ import { LanceDBVectorStore } from "@vivantel/virage-store-lancedb";
57
+
58
+ const store = new LanceDBVectorStore({
59
+ uri: "./lancedb",
60
+ tableName: "my-docs",
61
+ dimensions: 1536,
62
+ });
63
+ ```
64
+
65
+ ## Self-registration
66
+
67
+ This package declares a `"rag-plugin"` field in its `package.json`. Once installed, `virage init` discovers it automatically — no manual config required.
68
+
69
+ ```jsonc
70
+ // package.json (excerpt)
71
+ "rag-plugin": {
72
+ "type": "vectorStore",
73
+ "label": "LanceDB (embedded, file-based)",
74
+ "key": "lancedb",
75
+ "envVars": [],
76
+ "defaultConfig": { "uri": "./lancedb" }
77
+ }
78
+ ```
@@ -0,0 +1,5 @@
1
+ export { LanceDBVectorStore, type LanceDBVectorStoreOptions } from "./store.js";
2
+ import type { VectorStore } from "@vivantel/virage-core";
3
+ /** Factory used by the JSON config loader. */
4
+ export declare function createVectorStore(config: Record<string, unknown>): VectorStore;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEhF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,WAAW,CAeb"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export { LanceDBVectorStore } from "./store.js";
2
+ import { LanceDBVectorStore } from "./store.js";
3
+ /** Factory used by the JSON config loader. */
4
+ export function createVectorStore(config) {
5
+ const uri = typeof config.uri === "string" ? config.uri : undefined;
6
+ if (!uri) {
7
+ throw new Error('@vivantel/virage-store-lancedb: config.uri is required (e.g. "./lancedb" for local file storage)');
8
+ }
9
+ return new LanceDBVectorStore({
10
+ uri,
11
+ apiKey: typeof config.apiKey === "string" ? config.apiKey : undefined,
12
+ tableName: typeof config.tableName === "string" ? config.tableName : undefined,
13
+ dimensions: typeof config.dimensions === "number" ? config.dimensions : undefined,
14
+ });
15
+ }
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAkC,MAAM,YAAY,CAAC;AAGhF,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,8CAA8C;AAC9C,MAAM,UAAU,iBAAiB,CAC/B,MAA+B;IAE/B,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,kGAAkG,CACnG,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,kBAAkB,CAAC;QAC5B,GAAG;QACH,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACrE,SAAS,EACP,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACrE,UAAU,EACR,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;KACxE,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { QueryPerfReport } from "@vivantel/virage-core";
2
+ export declare function getQueryPerfReport(table: any, dimensions: number, timeframeHours: number): Promise<QueryPerfReport>;
3
+ //# sourceMappingURL=query-perf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-perf.d.ts","sourceRoot":"","sources":["../src/query-perf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAa7D,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,GAAG,EACV,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,eAAe,CAAC,CA4C1B"}
@@ -0,0 +1,49 @@
1
+ const SAMPLE_COUNT = 20;
2
+ function percentile(sorted, p) {
3
+ if (sorted.length === 0)
4
+ return -1;
5
+ return (sorted[Math.floor((p / 100) * sorted.length)] ??
6
+ sorted[sorted.length - 1] ??
7
+ 0);
8
+ }
9
+ export async function getQueryPerfReport(table, dimensions, timeframeHours) {
10
+ const zeroVector = Array(dimensions).fill(0);
11
+ const latencies = [];
12
+ for (let i = 0; i < SAMPLE_COUNT; i++) {
13
+ const start = performance.now();
14
+ try {
15
+ await table
16
+ .vectorSearch(zeroVector)
17
+ .column("embedding")
18
+ .distanceType("cosine")
19
+ .limit(10)
20
+ .toArray();
21
+ }
22
+ catch {
23
+ // Table may be empty or index unavailable — still record timing
24
+ }
25
+ latencies.push(performance.now() - start);
26
+ }
27
+ latencies.sort((a, b) => a - b);
28
+ const p50 = percentile(latencies, 50);
29
+ const p95 = percentile(latencies, 95);
30
+ const p99 = percentile(latencies, 99);
31
+ const slowQueryCount = latencies.filter((ms) => ms > 100).length;
32
+ const suggestedIndexes = [];
33
+ if (p95 > 50) {
34
+ suggestedIndexes.push(`p95 latency (${p95.toFixed(1)} ms) is high. ` +
35
+ `Consider creating an IVF-PQ index to speed up searches.`);
36
+ }
37
+ else {
38
+ suggestedIndexes.push("Query performance looks healthy.");
39
+ }
40
+ return {
41
+ timeframeHours,
42
+ p50LatencyMs: Math.round(p50 * 10) / 10,
43
+ p95LatencyMs: Math.round(p95 * 10) / 10,
44
+ p99LatencyMs: Math.round(p99 * 10) / 10,
45
+ slowQueryCount,
46
+ suggestedIndexes,
47
+ };
48
+ }
49
+ //# sourceMappingURL=query-perf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-perf.js","sourceRoot":"","sources":["../src/query-perf.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,SAAS,UAAU,CAAC,MAAgB,EAAE,CAAS;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC;IACnC,OAAO,CACL,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QACzB,CAAC,CACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAU,EACV,UAAkB,EAClB,cAAsB;IAEtB,MAAM,UAAU,GAAG,KAAK,CAAS,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,KAAK;iBACR,YAAY,CAAC,UAAU,CAAC;iBACxB,MAAM,CAAC,WAAW,CAAC;iBACnB,YAAY,CAAC,QAAQ,CAAC;iBACtB,KAAK,CAAC,EAAE,CAAC;iBACT,OAAO,EAAE,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAEhC,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACtC,MAAM,cAAc,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;IAEjE,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,IAAI,GAAG,GAAG,EAAE,EAAE,CAAC;QACb,gBAAgB,CAAC,IAAI,CACnB,gBAAgB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB;YAC5C,yDAAyD,CAC5D,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,gBAAgB,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO;QACL,cAAc;QACd,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE;QACvC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE;QACvC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE;QACvC,cAAc;QACd,gBAAgB;KACjB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=query-perf.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-perf.test.d.ts","sourceRoot":"","sources":["../src/query-perf.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { getQueryPerfReport } from "./query-perf.js";
3
+ function makeTable(searchDelayMs = 5) {
4
+ return {
5
+ vectorSearch: vi.fn().mockReturnValue({
6
+ column: vi.fn().mockReturnThis(),
7
+ distanceType: vi.fn().mockReturnThis(),
8
+ limit: vi.fn().mockReturnThis(),
9
+ toArray: vi
10
+ .fn()
11
+ .mockImplementation(() => new Promise((resolve) => setTimeout(() => resolve([]), searchDelayMs))),
12
+ }),
13
+ };
14
+ }
15
+ describe("getQueryPerfReport (LanceDB)", () => {
16
+ it("returns valid latency percentiles (p50 ≤ p95 ≤ p99)", async () => {
17
+ const report = await getQueryPerfReport(makeTable(1), 384, 24);
18
+ expect(report.timeframeHours).toBe(24);
19
+ expect(report.p50LatencyMs).toBeGreaterThanOrEqual(0);
20
+ expect(report.p95LatencyMs).toBeGreaterThanOrEqual(report.p50LatencyMs);
21
+ expect(report.p99LatencyMs).toBeGreaterThanOrEqual(report.p95LatencyMs);
22
+ });
23
+ it("timeframeHours is reflected in the report", async () => {
24
+ const report = await getQueryPerfReport(makeTable(1), 384, 48);
25
+ expect(report.timeframeHours).toBe(48);
26
+ });
27
+ it("slowQueryCount is 0 for fast queries (< 100 ms)", async () => {
28
+ const report = await getQueryPerfReport(makeTable(1), 384, 24);
29
+ expect(report.slowQueryCount).toBe(0);
30
+ });
31
+ it("returns healthy suggestion when p95 latency ≤ 50 ms", async () => {
32
+ const report = await getQueryPerfReport(makeTable(1), 384, 24);
33
+ expect(report.suggestedIndexes[0]).toMatch(/healthy/i);
34
+ });
35
+ it("suggests IVF-PQ when p95 latency > 50 ms", async () => {
36
+ const report = await getQueryPerfReport(makeTable(80), 384, 24);
37
+ expect(report.suggestedIndexes[0]).toMatch(/IVF-PQ/i);
38
+ }, 10_000);
39
+ it("records timing even when table.vectorSearch throws", async () => {
40
+ const table = {
41
+ vectorSearch: vi.fn().mockReturnValue({
42
+ column: vi.fn().mockReturnThis(),
43
+ distanceType: vi.fn().mockReturnThis(),
44
+ limit: vi.fn().mockReturnThis(),
45
+ toArray: vi.fn().mockRejectedValue(new Error("table is empty")),
46
+ }),
47
+ };
48
+ const report = await getQueryPerfReport(table, 384, 24);
49
+ // Must still return a report with valid (≥ 0) latencies
50
+ expect(report.p50LatencyMs).toBeGreaterThanOrEqual(0);
51
+ expect(report.slowQueryCount).toBeGreaterThanOrEqual(0);
52
+ });
53
+ it("uses the provided dimensions to build the zero-vector query", async () => {
54
+ const searchSpy = vi.fn().mockReturnValue({
55
+ column: vi.fn().mockReturnThis(),
56
+ distanceType: vi.fn().mockReturnThis(),
57
+ limit: vi.fn().mockReturnThis(),
58
+ toArray: vi.fn().mockResolvedValue([]),
59
+ });
60
+ await getQueryPerfReport({ vectorSearch: searchSpy }, 128, 24);
61
+ // vectorSearch should have been called with a 128-dimensional zero vector
62
+ const firstArg = searchSpy.mock.calls[0][0];
63
+ expect(firstArg).toHaveLength(128);
64
+ expect(firstArg.every((v) => v === 0)).toBe(true);
65
+ });
66
+ }, 30_000);
67
+ //# sourceMappingURL=query-perf.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-perf.test.js","sourceRoot":"","sources":["../src/query-perf.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { IndexStats } from "@vivantel/virage-core";
2
+ export declare function getIndexStats(table: any): Promise<IndexStats>;
3
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD,wBAAsB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CA0CnE"}
package/dist/stats.js ADDED
@@ -0,0 +1,40 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ export async function getIndexStats(table) {
3
+ const totalVectors = (await table.countRows());
4
+ const suggestions = [];
5
+ let indexType = "flat";
6
+ try {
7
+ const indices = (await table.listIndices());
8
+ if (indices.length > 0) {
9
+ const types = indices.map((i) => (i.indexType ?? "").toLowerCase());
10
+ if (types.some((t) => t.includes("hnsw"))) {
11
+ indexType = "hnsw";
12
+ }
13
+ else if (types.some((t) => t.includes("ivf"))) {
14
+ indexType = "ivfflat";
15
+ }
16
+ }
17
+ }
18
+ catch {
19
+ // listIndices unavailable — assume flat
20
+ }
21
+ if (totalVectors === 0) {
22
+ suggestions.push("Table is empty.");
23
+ }
24
+ else if (totalVectors > 10_000 && indexType === "flat") {
25
+ suggestions.push(`Table has ${totalVectors.toLocaleString()} vectors on a flat index. ` +
26
+ `Consider creating an IVF-PQ index for faster searches.`);
27
+ }
28
+ else {
29
+ suggestions.push("Index looks healthy.");
30
+ }
31
+ return {
32
+ totalVectors,
33
+ indexType,
34
+ annRecallAt10: -1,
35
+ indexAgeHours: -1,
36
+ deadTupleFraction: 0,
37
+ suggestions,
38
+ };
39
+ }
40
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAEA,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAU;IAC5C,MAAM,YAAY,GAAG,CAAC,MAAM,KAAK,CAAC,SAAS,EAAE,CAAW,CAAC;IACzD,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,IAAI,SAAS,GAA4B,MAAM,CAAC;IAEhD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,CAAC,MAAM,KAAK,CAAC,WAAW,EAAE,CAGxC,CAAC;QACH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YACpE,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBAC1C,SAAS,GAAG,MAAM,CAAC;YACrB,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBAChD,SAAS,GAAG,SAAS,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;IAED,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACvB,WAAW,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACtC,CAAC;SAAM,IAAI,YAAY,GAAG,MAAM,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzD,WAAW,CAAC,IAAI,CACd,aAAa,YAAY,CAAC,cAAc,EAAE,4BAA4B;YACpE,wDAAwD,CAC3D,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,WAAW,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO;QACL,YAAY;QACZ,SAAS;QACT,aAAa,EAAE,CAAC,CAAC;QACjB,aAAa,EAAE,CAAC,CAAC;QACjB,iBAAiB,EAAE,CAAC;QACpB,WAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stats.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.test.d.ts","sourceRoot":"","sources":["../src/stats.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { getIndexStats } from "./stats.js";
3
+ function makeTable(overrides = {}) {
4
+ return {
5
+ countRows: vi.fn().mockResolvedValue(0),
6
+ listIndices: vi.fn().mockResolvedValue([]),
7
+ ...overrides,
8
+ };
9
+ }
10
+ describe("getIndexStats (LanceDB)", () => {
11
+ describe("empty table", () => {
12
+ it("returns flat indexType and empty-table suggestion when table has no rows", async () => {
13
+ const table = makeTable({ countRows: vi.fn().mockResolvedValue(0) });
14
+ const stats = await getIndexStats(table);
15
+ expect(stats.totalVectors).toBe(0);
16
+ expect(stats.indexType).toBe("flat");
17
+ expect(stats.suggestions[0]).toMatch(/empty/i);
18
+ });
19
+ });
20
+ describe("flat index with various vector counts", () => {
21
+ it("returns healthy suggestion for a small table (≤ 10 000 vectors, no index)", async () => {
22
+ const stats = await getIndexStats(makeTable({ countRows: vi.fn().mockResolvedValue(500) }));
23
+ expect(stats.suggestions[0]).toMatch(/healthy/i);
24
+ });
25
+ it("returns healthy suggestion at exactly 10 000 vectors (boundary)", async () => {
26
+ const stats = await getIndexStats(makeTable({ countRows: vi.fn().mockResolvedValue(10_000) }));
27
+ expect(stats.suggestions[0]).toMatch(/healthy/i);
28
+ });
29
+ it("suggests creating an IVF-PQ index when > 10 000 vectors and no index", async () => {
30
+ const stats = await getIndexStats(makeTable({ countRows: vi.fn().mockResolvedValue(15_000) }));
31
+ expect(stats.totalVectors).toBe(15_000);
32
+ expect(stats.indexType).toBe("flat");
33
+ expect(stats.suggestions[0]).toMatch(/IVF-PQ/i);
34
+ });
35
+ });
36
+ describe("index type detection via listIndices", () => {
37
+ it("detects hnsw from HNSW indexType", async () => {
38
+ const table = makeTable({
39
+ countRows: vi.fn().mockResolvedValue(1000),
40
+ listIndices: vi
41
+ .fn()
42
+ .mockResolvedValue([{ name: "emb_idx", indexType: "HNSW" }]),
43
+ });
44
+ const stats = await getIndexStats(table);
45
+ expect(stats.indexType).toBe("hnsw");
46
+ expect(stats.suggestions[0]).toMatch(/healthy/i);
47
+ });
48
+ it("detects ivfflat from IVF_PQ indexType", async () => {
49
+ const table = makeTable({
50
+ countRows: vi.fn().mockResolvedValue(20_000),
51
+ listIndices: vi
52
+ .fn()
53
+ .mockResolvedValue([{ name: "emb_idx", indexType: "IVF_PQ" }]),
54
+ });
55
+ const stats = await getIndexStats(table);
56
+ expect(stats.indexType).toBe("ivfflat");
57
+ // Has a real index → should be healthy, not suggest IVF-PQ creation
58
+ expect(stats.suggestions[0]).toMatch(/healthy/i);
59
+ });
60
+ it("does not suggest IVF-PQ creation when a non-flat index is present (even > 10k)", async () => {
61
+ const table = makeTable({
62
+ countRows: vi.fn().mockResolvedValue(50_000),
63
+ listIndices: vi
64
+ .fn()
65
+ .mockResolvedValue([{ name: "idx", indexType: "IVF_PQ" }]),
66
+ });
67
+ const stats = await getIndexStats(table);
68
+ expect(stats.suggestions.every((s) => !/IVF-PQ creation/i.test(s))).toBe(true);
69
+ });
70
+ it("falls back to flat when listIndices throws", async () => {
71
+ const table = makeTable({
72
+ countRows: vi.fn().mockResolvedValue(500),
73
+ listIndices: vi.fn().mockRejectedValue(new Error("not supported")),
74
+ });
75
+ const stats = await getIndexStats(table);
76
+ expect(stats.indexType).toBe("flat");
77
+ });
78
+ it("falls back to flat when listIndices returns an empty array", async () => {
79
+ const table = makeTable({
80
+ countRows: vi.fn().mockResolvedValue(500),
81
+ listIndices: vi.fn().mockResolvedValue([]),
82
+ });
83
+ const stats = await getIndexStats(table);
84
+ expect(stats.indexType).toBe("flat");
85
+ });
86
+ });
87
+ describe("fixed metadata fields", () => {
88
+ it("always returns annRecallAt10 = -1, indexAgeHours = -1, deadTupleFraction = 0", async () => {
89
+ const stats = await getIndexStats(makeTable({ countRows: vi.fn().mockResolvedValue(100) }));
90
+ expect(stats.annRecallAt10).toBe(-1);
91
+ expect(stats.indexAgeHours).toBe(-1);
92
+ expect(stats.deadTupleFraction).toBe(0);
93
+ });
94
+ });
95
+ });
96
+ //# sourceMappingURL=stats.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.test.js","sourceRoot":"","sources":["../src/stats.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { VectorDocument, VectorSearchResult, VectorStore, IndexStats, QueryPerfReport, Logger } from "@vivantel/virage-core";
2
+ export interface LanceDBVectorStoreOptions {
3
+ uri: string;
4
+ apiKey?: string;
5
+ tableName?: string;
6
+ dimensions?: number;
7
+ }
8
+ export declare class LanceDBVectorStore implements VectorStore {
9
+ readonly name = "lancedb";
10
+ private readonly uri;
11
+ private readonly apiKey;
12
+ private readonly tableName;
13
+ private readonly dimensions;
14
+ private db;
15
+ private table;
16
+ private logger;
17
+ constructor(options: LanceDBVectorStoreOptions);
18
+ setLogger(logger: Logger): void;
19
+ initialize(): Promise<void>;
20
+ upsert(documents: VectorDocument[]): Promise<void>;
21
+ deleteBySourceFile(sourceFiles: string[]): Promise<void>;
22
+ getCurrentState(): Promise<Map<string, string>>;
23
+ getIndexStats(): Promise<IndexStats>;
24
+ getQueryPerfReport(timeframeHours: number): Promise<QueryPerfReport>;
25
+ search(queryEmbedding: number[], topK: number): Promise<VectorSearchResult[]>;
26
+ }
27
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,eAAe,EACf,MAAM,EACP,MAAM,uBAAuB,CAAC;AAK/B,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD,qBAAa,kBAAmB,YAAW,WAAW;IACpD,QAAQ,CAAC,IAAI,aAAa;IAE1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,OAAO,CAAC,EAAE,CAAM;IAEhB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,MAAM,CAAuB;gBAEzB,OAAO,EAAE,yBAAyB;IAU9C,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B3B,MAAM,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBlD,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAUxD,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAoB/C,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAIpC,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAIpE,MAAM,CACV,cAAc,EAAE,MAAM,EAAE,EACxB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,kBAAkB,EAAE,CAAC;CAiCjC"}
package/dist/store.js ADDED
@@ -0,0 +1,131 @@
1
+ import { Field, FixedSizeList, Float32, Schema, Utf8 } from "apache-arrow";
2
+ import { getIndexStats } from "./stats.js";
3
+ import { getQueryPerfReport } from "./query-perf.js";
4
+ const DEFAULT_TABLE = "documents";
5
+ const DEFAULT_DIMENSIONS = 1536;
6
+ export class LanceDBVectorStore {
7
+ name = "lancedb";
8
+ uri;
9
+ apiKey;
10
+ tableName;
11
+ dimensions;
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ db;
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ table;
16
+ logger = null;
17
+ constructor(options) {
18
+ if (!options.uri) {
19
+ throw new Error("LanceDBVectorStore: uri is required");
20
+ }
21
+ this.uri = options.uri;
22
+ this.apiKey = options.apiKey;
23
+ this.tableName = options.tableName ?? DEFAULT_TABLE;
24
+ this.dimensions = options.dimensions ?? DEFAULT_DIMENSIONS;
25
+ }
26
+ setLogger(logger) {
27
+ this.logger = logger.withTag("lancedb");
28
+ }
29
+ async initialize() {
30
+ this.logger?.info(`Connecting to lancedb at ${this.uri}, table: ${this.tableName}`);
31
+ // Dynamic import to avoid native-module issues at load time
32
+ const lancedb = await import("@lancedb/lancedb");
33
+ this.db = this.apiKey
34
+ ? await lancedb.connect(this.uri, { apiKey: this.apiKey })
35
+ : await lancedb.connect(this.uri);
36
+ const schema = new Schema([
37
+ new Field("id", new Utf8()),
38
+ new Field("content", new Utf8()),
39
+ new Field("embedding", new FixedSizeList(this.dimensions, new Field("item", new Float32()))),
40
+ new Field("metadata_json", new Utf8()),
41
+ new Field("source_file", new Utf8()),
42
+ new Field("commit_hash", new Utf8()),
43
+ new Field("content_hash", new Utf8()),
44
+ ]);
45
+ this.table = await this.db.createEmptyTable(this.tableName, schema, {
46
+ existOk: true,
47
+ });
48
+ this.logger?.debug(`Table "${this.tableName}" ready (${this.dimensions}d)`);
49
+ }
50
+ async upsert(documents) {
51
+ this.logger?.verbose(`Upserting ${documents.length} docs into "${this.tableName}"`);
52
+ const rows = documents.map((doc) => ({
53
+ id: doc.id ?? crypto.randomUUID(),
54
+ content: doc.content,
55
+ embedding: doc.embedding,
56
+ metadata_json: JSON.stringify(doc.metadata),
57
+ source_file: doc.sourceFile,
58
+ commit_hash: doc.commitHash,
59
+ content_hash: doc.contentHash,
60
+ }));
61
+ this.logger?.trace(` Row IDs: ${rows.map((r) => r.id.slice(0, 8)).join(", ")}`);
62
+ await this.table
63
+ .mergeInsert("id")
64
+ .whenMatchedUpdateAll()
65
+ .whenNotMatchedInsertAll()
66
+ .execute(rows);
67
+ }
68
+ async deleteBySourceFile(sourceFiles) {
69
+ if (sourceFiles.length === 0)
70
+ return;
71
+ this.logger?.verbose(`Deleting docs for ${sourceFiles.length} source file(s)`);
72
+ const escaped = sourceFiles.map((f) => f.replace(/'/g, "''"));
73
+ const list = escaped.map((f) => `'${f}'`).join(", ");
74
+ await this.table.delete(`source_file IN (${list})`);
75
+ }
76
+ async getCurrentState() {
77
+ const rows = await this.table
78
+ .query()
79
+ .select(["source_file", "commit_hash"])
80
+ .toArray();
81
+ const state = new Map();
82
+ for (const row of rows) {
83
+ const sourceFile = typeof row.source_file === "string" ? row.source_file : null;
84
+ const commitHash = typeof row.commit_hash === "string" ? row.commit_hash : null;
85
+ if (sourceFile && commitHash) {
86
+ state.set(sourceFile, commitHash);
87
+ }
88
+ }
89
+ this.logger?.verbose(`getCurrentState: ${state.size} source version(s)`);
90
+ return state;
91
+ }
92
+ async getIndexStats() {
93
+ return getIndexStats(this.table);
94
+ }
95
+ async getQueryPerfReport(timeframeHours) {
96
+ return getQueryPerfReport(this.table, this.dimensions, timeframeHours);
97
+ }
98
+ async search(queryEmbedding, topK) {
99
+ this.logger?.debug(`Search: topK=${topK}`);
100
+ const rows = await this.table
101
+ .vectorSearch(queryEmbedding)
102
+ .column("embedding")
103
+ .distanceType("cosine")
104
+ .limit(topK)
105
+ .toArray();
106
+ return rows.map((row) => {
107
+ const distance = typeof row._distance === "number" ? row._distance : 1;
108
+ return {
109
+ id: typeof row.id === "string" ? row.id : "",
110
+ content: typeof row.content === "string" ? row.content : "",
111
+ metadata: (() => {
112
+ try {
113
+ const parsed = typeof row.metadata_json === "string"
114
+ ? JSON.parse(row.metadata_json)
115
+ : {};
116
+ return parsed &&
117
+ typeof parsed === "object" &&
118
+ !Array.isArray(parsed)
119
+ ? parsed
120
+ : {};
121
+ }
122
+ catch {
123
+ return {};
124
+ }
125
+ })(),
126
+ similarity: 1 - distance,
127
+ };
128
+ });
129
+ }
130
+ }
131
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AASrD,MAAM,aAAa,GAAG,WAAW,CAAC;AAClC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,MAAM,OAAO,kBAAkB;IACpB,IAAI,GAAG,SAAS,CAAC;IAET,GAAG,CAAS;IACZ,MAAM,CAAqB;IAC3B,SAAS,CAAS;IAClB,UAAU,CAAS;IACpC,8DAA8D;IACtD,EAAE,CAAM;IAChB,8DAA8D;IACtD,KAAK,CAAM;IACX,MAAM,GAAkB,IAAI,CAAC;IAErC,YAAY,OAAkC;QAC5C,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,aAAa,CAAC;QACpD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,CAAC;IAC7D,CAAC;IAED,SAAS,CAAC,MAAc;QACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,MAAM,EAAE,IAAI,CACf,4BAA4B,IAAI,CAAC,GAAG,YAAY,IAAI,CAAC,SAAS,EAAE,CACjE,CAAC;QACF,4DAA4D;QAC5D,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAEjD,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM;YACnB,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YAC1D,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEpC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;YACxB,IAAI,KAAK,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;YAC3B,IAAI,KAAK,CAAC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC;YAChC,IAAI,KAAK,CACP,WAAW,EACX,IAAI,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC,CAAC,CACrE;YACD,IAAI,KAAK,CAAC,eAAe,EAAE,IAAI,IAAI,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,cAAc,EAAE,IAAI,IAAI,EAAE,CAAC;SACtC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE;YAClE,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;IAC9E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAA2B;QACtC,IAAI,CAAC,MAAM,EAAE,OAAO,CAClB,aAAa,SAAS,CAAC,MAAM,eAAe,IAAI,CAAC,SAAS,GAAG,CAC9D,CAAC;QACF,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACnC,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,MAAM,CAAC,UAAU,EAAE;YACjC,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC3C,WAAW,EAAE,GAAG,CAAC,UAAU;YAC3B,WAAW,EAAE,GAAG,CAAC,UAAU;YAC3B,YAAY,EAAE,GAAG,CAAC,WAAW;SAC9B,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC,MAAM,EAAE,KAAK,CAChB,cAAc,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7D,CAAC;QAEF,MAAM,IAAI,CAAC,KAAK;aACb,WAAW,CAAC,IAAI,CAAC;aACjB,oBAAoB,EAAE;aACtB,uBAAuB,EAAE;aACzB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,WAAqB;QAC5C,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACrC,IAAI,CAAC,MAAM,EAAE,OAAO,CAClB,qBAAqB,WAAW,CAAC,MAAM,iBAAiB,CACzD,CAAC;QACF,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9D,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK;aAC1B,KAAK,EAAE;aACP,MAAM,CAAC,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;aACtC,OAAO,EAAE,CAAC;QAEb,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,IAAiC,EAAE,CAAC;YACpD,MAAM,UAAU,GACd,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;YAC/D,MAAM,UAAU,GACd,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;YAC/D,IAAI,UAAU,IAAI,UAAU,EAAE,CAAC;gBAC7B,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,oBAAoB,KAAK,CAAC,IAAI,oBAAoB,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,OAAO,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,cAAsB;QAC7C,OAAO,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,MAAM,CACV,cAAwB,EACxB,IAAY;QAEZ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK;aAC1B,YAAY,CAAC,cAAc,CAAC;aAC5B,MAAM,CAAC,WAAW,CAAC;aACnB,YAAY,CAAC,QAAQ,CAAC;aACtB,KAAK,CAAC,IAAI,CAAC;aACX,OAAO,EAAE,CAAC;QAEb,OAAQ,IAAkC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACrD,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,OAAO;gBACL,EAAE,EAAE,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;gBAC5C,OAAO,EAAE,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;gBAC3D,QAAQ,EAAE,CAAC,GAAG,EAAE;oBACd,IAAI,CAAC;wBACH,MAAM,MAAM,GACV,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ;4BACnC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC;4BAC/B,CAAC,CAAC,EAAE,CAAC;wBACT,OAAO,MAAM;4BACX,OAAO,MAAM,KAAK,QAAQ;4BAC1B,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;4BACtB,CAAC,CAAE,MAAkC;4BACrC,CAAC,CAAC,EAAE,CAAC;oBACT,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO,EAAE,CAAC;oBACZ,CAAC;gBACH,CAAC,CAAC,EAAE;gBACJ,UAAU,EAAE,CAAC,GAAG,QAAQ;aACzB,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@vivantel/virage-store-lancedb",
3
+ "version": "0.2.0",
4
+ "description": "LanceDB vector store for @vivantel/rag-core (embedded, file-based)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "sideEffects": false,
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "rag-plugin": {
23
+ "type": "vectorStore",
24
+ "label": "LanceDB (embedded, file-based)",
25
+ "key": "lancedb",
26
+ "envVars": [],
27
+ "defaultConfig": {
28
+ "uri": "./lancedb"
29
+ }
30
+ },
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "type-check": "tsc --noEmit",
34
+ "test": "vitest run",
35
+ "prepublishOnly": "npm run build",
36
+ "lint": "eslint src/",
37
+ "lint:fix": "eslint src/ --fix",
38
+ "format": "prettier --write \"src/**/*.ts\"",
39
+ "fix": "npm run lint:fix && npm run format"
40
+ },
41
+ "keywords": [
42
+ "rag",
43
+ "vector-store",
44
+ "lancedb",
45
+ "vector-database",
46
+ "semantic-search",
47
+ "embedded"
48
+ ],
49
+ "author": "Vivantel",
50
+ "license": "MIT",
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/vivantel/virage",
54
+ "directory": "packages/virage-store-lancedb"
55
+ },
56
+ "dependencies": {
57
+ "@lancedb/lancedb": "^0.18.0",
58
+ "apache-arrow": "^18.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "@vivantel/virage-core": "*"
62
+ },
63
+ "devDependencies": {
64
+ "@types/node": "^25.9.1",
65
+ "@vivantel/virage-core": "0.2.0",
66
+ "typescript": "^6.0.3",
67
+ "vitest": "^4.1.8"
68
+ },
69
+ "engines": {
70
+ "node": ">=18.0.0"
71
+ }
72
+ }