@askdb/rag 0.2.0-beta.1
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/LICENSE +201 -0
- package/NOTICE +10 -0
- package/README.md +76 -0
- package/bin/askdb-rag.js +2 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunker/chunker.d.ts +36 -0
- package/dist/chunker/chunker.d.ts.map +1 -0
- package/dist/chunker/chunker.js +426 -0
- package/dist/chunker/chunker.js.map +1 -0
- package/dist/chunker/index.d.ts +10 -0
- package/dist/chunker/index.d.ts.map +1 -0
- package/dist/chunker/index.js +14 -0
- package/dist/chunker/index.js.map +1 -0
- package/dist/chunker/options.d.ts +24 -0
- package/dist/chunker/options.d.ts.map +1 -0
- package/dist/chunker/options.js +3 -0
- package/dist/chunker/options.js.map +1 -0
- package/dist/chunker/sources.d.ts +30 -0
- package/dist/chunker/sources.d.ts.map +1 -0
- package/dist/chunker/sources.js +68 -0
- package/dist/chunker/sources.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +332 -0
- package/dist/cli.js.map +1 -0
- package/dist/embedders/ai-sdk.d.ts +22 -0
- package/dist/embedders/ai-sdk.d.ts.map +1 -0
- package/dist/embedders/ai-sdk.js +36 -0
- package/dist/embedders/ai-sdk.js.map +1 -0
- package/dist/embedders/openai.d.ts +21 -0
- package/dist/embedders/openai.d.ts.map +1 -0
- package/dist/embedders/openai.js +24 -0
- package/dist/embedders/openai.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer/hash.d.ts +3 -0
- package/dist/indexer/hash.d.ts.map +1 -0
- package/dist/indexer/hash.js +6 -0
- package/dist/indexer/hash.js.map +1 -0
- package/dist/indexer/index.d.ts +72 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +196 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/lock-file.d.ts +23 -0
- package/dist/indexer/lock-file.d.ts.map +1 -0
- package/dist/indexer/lock-file.js +33 -0
- package/dist/indexer/lock-file.js.map +1 -0
- package/dist/log-events.d.ts +23 -0
- package/dist/log-events.d.ts.map +1 -0
- package/dist/log-events.js +22 -0
- package/dist/log-events.js.map +1 -0
- package/dist/stores/file.d.ts +42 -0
- package/dist/stores/file.d.ts.map +1 -0
- package/dist/stores/file.js +127 -0
- package/dist/stores/file.js.map +1 -0
- package/dist/stores/memory.d.ts +34 -0
- package/dist/stores/memory.d.ts.map +1 -0
- package/dist/stores/memory.js +118 -0
- package/dist/stores/memory.js.map +1 -0
- package/dist/stores/pgvector.d.ts +41 -0
- package/dist/stores/pgvector.d.ts.map +1 -0
- package/dist/stores/pgvector.js +176 -0
- package/dist/stores/pgvector.js.map +1 -0
- package/dist/types.d.ts +89 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +100 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { chunkSchema } from "../chunker/index.js";
|
|
2
|
+
import { AskDbRagLogEvent } from "../log-events.js";
|
|
3
|
+
import { chunkContentHash } from "./hash.js";
|
|
4
|
+
import { readLockFile, writeLockFile, } from "./lock-file.js";
|
|
5
|
+
const DEFAULT_BATCH_SIZE = 64;
|
|
6
|
+
export async function buildSchemaIndex(options) {
|
|
7
|
+
const { embedder, store, embedderId, lockFilePath, chunkOptions, batchSize = DEFAULT_BATCH_SIZE, logger, correlationId, onProgress, } = options;
|
|
8
|
+
const sources = normalizeIndexSources(options);
|
|
9
|
+
const baseLogContext = correlationId
|
|
10
|
+
? { correlationId }
|
|
11
|
+
: {};
|
|
12
|
+
// 1. Chunk
|
|
13
|
+
const chunkResult = chunkSchema(sources, chunkOptions);
|
|
14
|
+
const chunks = chunkResult.chunks;
|
|
15
|
+
// 2. Sensitive log emit (counts only)
|
|
16
|
+
if (chunkResult.stats.sensitiveExcluded > 0) {
|
|
17
|
+
logger?.info({
|
|
18
|
+
...baseLogContext,
|
|
19
|
+
event: AskDbRagLogEvent.SensitiveChunksExcluded,
|
|
20
|
+
count: chunkResult.stats.sensitiveExcluded,
|
|
21
|
+
}, "sensitive describable-layer chunks excluded");
|
|
22
|
+
}
|
|
23
|
+
if (chunkOptions?.includeSensitiveDescribable === true &&
|
|
24
|
+
chunkResult.stats.sensitiveIncluded > 0) {
|
|
25
|
+
logger?.info({
|
|
26
|
+
...baseLogContext,
|
|
27
|
+
event: AskDbRagLogEvent.SensitiveChunksIncluded,
|
|
28
|
+
count: chunkResult.stats.sensitiveIncluded,
|
|
29
|
+
}, "sensitive describable-layer chunks INCLUDED (opt-in)");
|
|
30
|
+
}
|
|
31
|
+
// 3. Decide which chunks need (re-)embedding via lock-file/store hashes.
|
|
32
|
+
const previousLock = lockFilePath ? readLockFile(lockFilePath) : undefined;
|
|
33
|
+
const storeHashes = (await store.hashesByPrefix?.("chunk:")) ?? {};
|
|
34
|
+
const previousHashes = { ...storeHashes, ...(previousLock?.hashes ?? {}) };
|
|
35
|
+
const previousEmbedderId = previousLock?.embedderId;
|
|
36
|
+
const embedderChanged = embedderId !== undefined &&
|
|
37
|
+
previousEmbedderId !== undefined &&
|
|
38
|
+
embedderId !== previousEmbedderId;
|
|
39
|
+
const newHashes = {};
|
|
40
|
+
const toEmbed = [];
|
|
41
|
+
let reused = 0;
|
|
42
|
+
for (const c of chunks) {
|
|
43
|
+
const hash = chunkContentHash(c.text);
|
|
44
|
+
newHashes[c.id] = hash;
|
|
45
|
+
if (!embedderChanged && previousHashes[c.id] === hash) {
|
|
46
|
+
reused++;
|
|
47
|
+
// Chunk text unchanged AND embedder unchanged — assume the store still
|
|
48
|
+
// holds the embedding from a prior run. The retriever uses store.query
|
|
49
|
+
// directly so we don't need to round-trip the vector.
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
toEmbed.push(c);
|
|
53
|
+
}
|
|
54
|
+
logger?.info({
|
|
55
|
+
...baseLogContext,
|
|
56
|
+
event: AskDbRagLogEvent.IndexingStarted,
|
|
57
|
+
totalChunks: chunks.length,
|
|
58
|
+
toEmbed: toEmbed.length,
|
|
59
|
+
reused,
|
|
60
|
+
embedderChanged,
|
|
61
|
+
}, "rag indexing started");
|
|
62
|
+
onProgress?.({
|
|
63
|
+
kind: "started",
|
|
64
|
+
totalChunks: chunks.length,
|
|
65
|
+
toEmbed: toEmbed.length,
|
|
66
|
+
reused,
|
|
67
|
+
});
|
|
68
|
+
if (reused > 0) {
|
|
69
|
+
logger?.info({
|
|
70
|
+
...baseLogContext,
|
|
71
|
+
event: AskDbRagLogEvent.ChunksReused,
|
|
72
|
+
count: reused,
|
|
73
|
+
}, "rag chunks reused from lock file");
|
|
74
|
+
}
|
|
75
|
+
// 4. Embed in batches and upsert.
|
|
76
|
+
let embeddedCount = 0;
|
|
77
|
+
for (let i = 0; i < toEmbed.length; i += batchSize) {
|
|
78
|
+
const batch = toEmbed.slice(i, i + batchSize);
|
|
79
|
+
const vectors = await embedder(batch.map((c) => c.text));
|
|
80
|
+
if (vectors.length !== batch.length) {
|
|
81
|
+
throw new Error(`Embedder returned ${vectors.length} vectors for ${batch.length} inputs.`);
|
|
82
|
+
}
|
|
83
|
+
await store.upsert(batch.map((c, idx) => ({
|
|
84
|
+
id: c.id,
|
|
85
|
+
vector: vectors[idx],
|
|
86
|
+
hash: newHashes[c.id],
|
|
87
|
+
payload: {
|
|
88
|
+
id: c.id,
|
|
89
|
+
type: c.type,
|
|
90
|
+
text: c.text,
|
|
91
|
+
schemaId: c.schemaId,
|
|
92
|
+
refs: c.refs,
|
|
93
|
+
sensitive: c.sensitive,
|
|
94
|
+
},
|
|
95
|
+
})));
|
|
96
|
+
embeddedCount += batch.length;
|
|
97
|
+
for (const c of batch) {
|
|
98
|
+
logger?.info({
|
|
99
|
+
...baseLogContext,
|
|
100
|
+
event: AskDbRagLogEvent.ChunkIndexed,
|
|
101
|
+
chunkType: c.type,
|
|
102
|
+
textChars: c.text.length,
|
|
103
|
+
}, "rag chunk indexed");
|
|
104
|
+
}
|
|
105
|
+
onProgress?.({
|
|
106
|
+
kind: "embedded",
|
|
107
|
+
embedded: embeddedCount,
|
|
108
|
+
total: toEmbed.length,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// 5. Drop chunks present in the previous lock but absent now (artifact pruning).
|
|
112
|
+
const previousIds = new Set(Object.keys(previousHashes));
|
|
113
|
+
const currentIds = new Set(chunks.map((c) => c.id));
|
|
114
|
+
const orphaned = [];
|
|
115
|
+
for (const id of previousIds) {
|
|
116
|
+
if (!currentIds.has(id))
|
|
117
|
+
orphaned.push(id);
|
|
118
|
+
}
|
|
119
|
+
if (orphaned.length > 0) {
|
|
120
|
+
await store.delete(orphaned);
|
|
121
|
+
}
|
|
122
|
+
// 6. Persist lock file.
|
|
123
|
+
if (lockFilePath) {
|
|
124
|
+
const lock = {
|
|
125
|
+
version: 1,
|
|
126
|
+
schemaId: sources.schema.schemaId,
|
|
127
|
+
embedderId,
|
|
128
|
+
hashes: newHashes,
|
|
129
|
+
updatedAt: new Date().toISOString(),
|
|
130
|
+
};
|
|
131
|
+
writeLockFile(lockFilePath, lock);
|
|
132
|
+
}
|
|
133
|
+
logger?.info({
|
|
134
|
+
...baseLogContext,
|
|
135
|
+
event: AskDbRagLogEvent.IndexingCompleted,
|
|
136
|
+
embedded: embeddedCount,
|
|
137
|
+
reused,
|
|
138
|
+
total: chunks.length,
|
|
139
|
+
}, "rag indexing completed");
|
|
140
|
+
onProgress?.({ kind: "completed", embedded: embeddedCount, reused });
|
|
141
|
+
// 7. Build retriever bound to the same embedder + store.
|
|
142
|
+
const retriever = async ({ question, k = 8, filter }) => {
|
|
143
|
+
const [vector] = await embedder([question]);
|
|
144
|
+
const results = await store.query(vector, k, filter);
|
|
145
|
+
logger?.info({
|
|
146
|
+
...baseLogContext,
|
|
147
|
+
event: AskDbRagLogEvent.RetrievalCompleted,
|
|
148
|
+
questionChars: question.length,
|
|
149
|
+
k,
|
|
150
|
+
resultCount: results.length,
|
|
151
|
+
}, "rag retrieval completed");
|
|
152
|
+
return results;
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
retriever,
|
|
156
|
+
stats: {
|
|
157
|
+
chunksTotal: chunks.length,
|
|
158
|
+
chunksIndexed: embeddedCount,
|
|
159
|
+
chunksReused: reused,
|
|
160
|
+
sensitiveExcluded: chunkResult.stats.sensitiveExcluded,
|
|
161
|
+
sensitiveIncluded: chunkResult.stats.sensitiveIncluded,
|
|
162
|
+
},
|
|
163
|
+
chunks,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function normalizeIndexSources(options) {
|
|
167
|
+
const input = options.schema ?? options.sources;
|
|
168
|
+
if (!input) {
|
|
169
|
+
throw new Error("buildSchemaIndex requires `schema` (or legacy `sources`).");
|
|
170
|
+
}
|
|
171
|
+
if ("schema" in input && "tables" in input)
|
|
172
|
+
return input;
|
|
173
|
+
return { schema: input, tables: {} };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Build a `Retriever` bound to an existing store + embedder, **without**
|
|
177
|
+
* indexing. Useful when the store is already populated by another process
|
|
178
|
+
* (CI, an external pgvector instance, etc.).
|
|
179
|
+
*/
|
|
180
|
+
export function createRetriever(args) {
|
|
181
|
+
return async ({ question, k = 8, filter }) => {
|
|
182
|
+
const [vector] = await args.embedder([question]);
|
|
183
|
+
const results = await args.store.query(vector, k, filter);
|
|
184
|
+
args.logger?.info({
|
|
185
|
+
...(args.correlationId ? { correlationId: args.correlationId } : {}),
|
|
186
|
+
event: AskDbRagLogEvent.RetrievalCompleted,
|
|
187
|
+
questionChars: question.length,
|
|
188
|
+
k,
|
|
189
|
+
resultCount: results.length,
|
|
190
|
+
}, "rag retrieval completed");
|
|
191
|
+
return results;
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
export { readLockFile, writeLockFile, } from "./lock-file.js";
|
|
195
|
+
export { chunkContentHash } from "./hash.js";
|
|
196
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `schema.lock.json` shape — machine-managed pointer used by the indexer to
|
|
3
|
+
* skip re-embedding chunks whose content hasn't changed.
|
|
4
|
+
*
|
|
5
|
+
* Lives next to the v2 artifact (`<schemaId>.schema/schema.lock.json`).
|
|
6
|
+
*/
|
|
7
|
+
export type SchemaLockFile = {
|
|
8
|
+
/** File-format version. Bump on breaking shape changes. */
|
|
9
|
+
version: 1;
|
|
10
|
+
/** Schema id this lock is bound to. */
|
|
11
|
+
schemaId: string;
|
|
12
|
+
/** Embedder id the embeddings were produced with (e.g. `openai:text-embedding-3-small`). */
|
|
13
|
+
embedderId?: string;
|
|
14
|
+
/** Vector dimensions (informational; the store enforces). */
|
|
15
|
+
dimensions?: number;
|
|
16
|
+
/** chunkId → SHA-256 content hash of the chunk's text. */
|
|
17
|
+
hashes: Record<string, string>;
|
|
18
|
+
/** ISO 8601 timestamp of last successful index. */
|
|
19
|
+
updatedAt?: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function readLockFile(path: string): SchemaLockFile | undefined;
|
|
22
|
+
export declare function writeLockFile(path: string, lock: SchemaLockFile): void;
|
|
23
|
+
//# sourceMappingURL=lock-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock-file.d.ts","sourceRoot":"","sources":["../../src/indexer/lock-file.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,2DAA2D;IAC3D,OAAO,EAAE,CAAC,CAAC;IACX,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAqBrE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAQtE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
export function readLockFile(path) {
|
|
3
|
+
let raw;
|
|
4
|
+
try {
|
|
5
|
+
raw = readFileSync(path, "utf8");
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
parsed = JSON.parse(raw);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
if (typeof parsed !== "object" ||
|
|
18
|
+
parsed === null ||
|
|
19
|
+
parsed.version !== 1) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
export function writeLockFile(path, lock) {
|
|
25
|
+
// Sort keys for deterministic on-disk output.
|
|
26
|
+
const sortedHashes = {};
|
|
27
|
+
for (const k of Object.keys(lock.hashes).sort()) {
|
|
28
|
+
sortedHashes[k] = lock.hashes[k];
|
|
29
|
+
}
|
|
30
|
+
const out = { ...lock, hashes: sortedHashes };
|
|
31
|
+
writeFileSync(path, JSON.stringify(out, null, 2) + "\n", "utf8");
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=lock-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock-file.js","sourceRoot":"","sources":["../../src/indexer/lock-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAuBtD,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IACE,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACd,MAAgC,CAAC,OAAO,KAAK,CAAC,EAC/C,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,MAAwB,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,IAAoB;IAC9D,8CAA8C;IAC9C,MAAM,YAAY,GAA2B,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAChD,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,MAAM,GAAG,GAAmB,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAC9D,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured `event` constants for `@askdb/rag` logs. Reused from any
|
|
3
|
+
* `AskDbLogger` implementation — same shape as `@askdb/core` events
|
|
4
|
+
* (Phase 2 logging contract).
|
|
5
|
+
*/
|
|
6
|
+
export declare const AskDbRagLogEvent: {
|
|
7
|
+
/** Indexing started (chunk count + reuse intent). */
|
|
8
|
+
readonly IndexingStarted: "askdb.rag.indexing_started";
|
|
9
|
+
/** A single chunk was embedded and upserted. */
|
|
10
|
+
readonly ChunkIndexed: "askdb.rag.chunk_indexed";
|
|
11
|
+
/** Counts of chunks reused from the lock file (no embedding call). */
|
|
12
|
+
readonly ChunksReused: "askdb.rag.chunks_reused";
|
|
13
|
+
/** Indexing finished (totals). */
|
|
14
|
+
readonly IndexingCompleted: "askdb.rag.indexing_completed";
|
|
15
|
+
/** Counts only — describable-layer chunks dropped because the source content references a sensitive column. */
|
|
16
|
+
readonly SensitiveChunksExcluded: "askdb.rag.sensitive_chunks_excluded";
|
|
17
|
+
/** Counts only — opt-in `includeSensitiveDescribable: true` is in effect. */
|
|
18
|
+
readonly SensitiveChunksIncluded: "askdb.rag.sensitive_chunks_included";
|
|
19
|
+
/** Retrieval ran for a question; counts only. */
|
|
20
|
+
readonly RetrievalCompleted: "askdb.rag.retrieval_completed";
|
|
21
|
+
};
|
|
22
|
+
export type AskDbRagLogEventName = (typeof AskDbRagLogEvent)[keyof typeof AskDbRagLogEvent];
|
|
23
|
+
//# sourceMappingURL=log-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-events.d.ts","sourceRoot":"","sources":["../src/log-events.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;IAC3B,qDAAqD;;IAErD,gDAAgD;;IAEhD,sEAAsE;;IAEtE,kCAAkC;;IAElC,+GAA+G;;IAE/G,6EAA6E;;IAE7E,iDAAiD;;CAEzC,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured `event` constants for `@askdb/rag` logs. Reused from any
|
|
3
|
+
* `AskDbLogger` implementation — same shape as `@askdb/core` events
|
|
4
|
+
* (Phase 2 logging contract).
|
|
5
|
+
*/
|
|
6
|
+
export const AskDbRagLogEvent = {
|
|
7
|
+
/** Indexing started (chunk count + reuse intent). */
|
|
8
|
+
IndexingStarted: "askdb.rag.indexing_started",
|
|
9
|
+
/** A single chunk was embedded and upserted. */
|
|
10
|
+
ChunkIndexed: "askdb.rag.chunk_indexed",
|
|
11
|
+
/** Counts of chunks reused from the lock file (no embedding call). */
|
|
12
|
+
ChunksReused: "askdb.rag.chunks_reused",
|
|
13
|
+
/** Indexing finished (totals). */
|
|
14
|
+
IndexingCompleted: "askdb.rag.indexing_completed",
|
|
15
|
+
/** Counts only — describable-layer chunks dropped because the source content references a sensitive column. */
|
|
16
|
+
SensitiveChunksExcluded: "askdb.rag.sensitive_chunks_excluded",
|
|
17
|
+
/** Counts only — opt-in `includeSensitiveDescribable: true` is in effect. */
|
|
18
|
+
SensitiveChunksIncluded: "askdb.rag.sensitive_chunks_included",
|
|
19
|
+
/** Retrieval ran for a question; counts only. */
|
|
20
|
+
RetrievalCompleted: "askdb.rag.retrieval_completed",
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=log-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-events.js","sourceRoot":"","sources":["../src/log-events.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,qDAAqD;IACrD,eAAe,EAAE,4BAA4B;IAC7C,gDAAgD;IAChD,YAAY,EAAE,yBAAyB;IACvC,sEAAsE;IACtE,YAAY,EAAE,yBAAyB;IACvC,kCAAkC;IAClC,iBAAiB,EAAE,8BAA8B;IACjD,+GAA+G;IAC/G,uBAAuB,EAAE,qCAAqC;IAC9D,6EAA6E;IAC7E,uBAAuB,EAAE,qCAAqC;IAC9D,iDAAiD;IACjD,kBAAkB,EAAE,+BAA+B;CAC3C,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { VectorStore } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* File-backed vector store. Embeddings serialize as a versioned binary file
|
|
4
|
+
* (`*.embeddings.bin`); payload metadata lives next to it as JSON.
|
|
5
|
+
*
|
|
6
|
+
* Behavior matches in-memory exactly — query is cosine over the same Float32
|
|
7
|
+
* vectors. The binary format keeps embeddings compact and round-tripping
|
|
8
|
+
* deterministic across runs.
|
|
9
|
+
*
|
|
10
|
+
* On-disk layout:
|
|
11
|
+
* `<basePath>.embeddings.bin` binary header + vectors
|
|
12
|
+
* `<basePath>.embeddings.json` payloads + metadata (one JSON file)
|
|
13
|
+
*
|
|
14
|
+
* Format (binary):
|
|
15
|
+
* magic 4B "ARAG"
|
|
16
|
+
* version 4B uint32 LE — currently 1
|
|
17
|
+
* dims 4B uint32 LE
|
|
18
|
+
* count 4B uint32 LE
|
|
19
|
+
* vectors count * dims * 4B Float32 LE
|
|
20
|
+
*
|
|
21
|
+
* Format (json):
|
|
22
|
+
* {
|
|
23
|
+
* "version": 1,
|
|
24
|
+
* "schemaId"?: string,
|
|
25
|
+
* "dimensions": number,
|
|
26
|
+
* "records": [{ id, payload, hash? }] // same order as binary vectors
|
|
27
|
+
* }
|
|
28
|
+
*/
|
|
29
|
+
export type FileStoreOptions = {
|
|
30
|
+
/** Path prefix; the store will write `<prefix>.embeddings.bin` and `<prefix>.embeddings.json`. */
|
|
31
|
+
basePath: string;
|
|
32
|
+
/** Auto-flush after each upsert. Default true. */
|
|
33
|
+
autoFlush?: boolean;
|
|
34
|
+
};
|
|
35
|
+
export type FileStore = VectorStore & {
|
|
36
|
+
/** Persist current state to disk. */
|
|
37
|
+
flush(): void;
|
|
38
|
+
/** Number of vectors currently held. */
|
|
39
|
+
size(): number;
|
|
40
|
+
};
|
|
41
|
+
export declare function createFileStore(options: FileStoreOptions): FileStore;
|
|
42
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/stores/file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAKV,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kGAAkG;IAClG,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACpC,qCAAqC;IACrC,KAAK,IAAI,IAAI,CAAC;IACd,wCAAwC;IACxC,IAAI,IAAI,MAAM,CAAC;CAChB,CAAC;AAKF,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAmCpE"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { createMemoryStore } from "./memory.js";
|
|
3
|
+
const MAGIC = "ARAG";
|
|
4
|
+
const FORMAT_VERSION = 1;
|
|
5
|
+
export function createFileStore(options) {
|
|
6
|
+
const { basePath } = options;
|
|
7
|
+
const autoFlush = options.autoFlush !== false;
|
|
8
|
+
const binPath = `${basePath}.embeddings.bin`;
|
|
9
|
+
const metaPath = `${basePath}.embeddings.json`;
|
|
10
|
+
const memory = createMemoryStore();
|
|
11
|
+
hydrate(memory, binPath, metaPath);
|
|
12
|
+
const flush = () => {
|
|
13
|
+
const snap = memory.snapshot();
|
|
14
|
+
writeBinary(binPath, snap.records.map((r) => r.vector), snap.dimensions ?? 0);
|
|
15
|
+
writeMeta(metaPath, snap);
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
async upsert(records) {
|
|
19
|
+
await memory.upsert(records);
|
|
20
|
+
if (autoFlush)
|
|
21
|
+
flush();
|
|
22
|
+
},
|
|
23
|
+
async query(vector, k, filter) {
|
|
24
|
+
return memory.query(vector, k, filter);
|
|
25
|
+
},
|
|
26
|
+
async delete(ids) {
|
|
27
|
+
await memory.delete(ids);
|
|
28
|
+
if (autoFlush)
|
|
29
|
+
flush();
|
|
30
|
+
},
|
|
31
|
+
async hashesByPrefix(prefix) {
|
|
32
|
+
return (await memory.hashesByPrefix?.(prefix)) ?? {};
|
|
33
|
+
},
|
|
34
|
+
flush,
|
|
35
|
+
size() {
|
|
36
|
+
return memory.size();
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function hydrate(memory, binPath, metaPath) {
|
|
41
|
+
if (!existsSync(binPath) || !existsSync(metaPath))
|
|
42
|
+
return;
|
|
43
|
+
const meta = JSON.parse(readFileSync(metaPath, "utf8"));
|
|
44
|
+
if (meta.version !== FORMAT_VERSION) {
|
|
45
|
+
throw new Error(`Unsupported file-store version ${meta.version} at ${metaPath} (expected ${FORMAT_VERSION}).`);
|
|
46
|
+
}
|
|
47
|
+
const buf = readFileSync(binPath);
|
|
48
|
+
const { dimensions, count, vectors } = readBinary(buf);
|
|
49
|
+
if (count !== meta.records.length) {
|
|
50
|
+
throw new Error(`File-store mismatch: ${binPath} has ${count} vectors but ${metaPath} has ${meta.records.length} records.`);
|
|
51
|
+
}
|
|
52
|
+
if (dimensions !== meta.dimensions) {
|
|
53
|
+
throw new Error(`File-store dimension mismatch: bin=${dimensions} json=${meta.dimensions}.`);
|
|
54
|
+
}
|
|
55
|
+
memory.restore({
|
|
56
|
+
dimensions,
|
|
57
|
+
records: meta.records.map((r, i) => ({
|
|
58
|
+
id: r.id,
|
|
59
|
+
vector: vectors[i],
|
|
60
|
+
payload: r.payload,
|
|
61
|
+
hash: r.hash,
|
|
62
|
+
})),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function writeBinary(path, vectors, dims) {
|
|
66
|
+
const count = vectors.length;
|
|
67
|
+
const headerSize = 16; // magic(4) + version(4) + dims(4) + count(4)
|
|
68
|
+
const totalSize = headerSize + count * dims * 4;
|
|
69
|
+
const buf = Buffer.alloc(totalSize);
|
|
70
|
+
buf.write(MAGIC, 0, 4, "ascii");
|
|
71
|
+
buf.writeUInt32LE(FORMAT_VERSION, 4);
|
|
72
|
+
buf.writeUInt32LE(dims, 8);
|
|
73
|
+
buf.writeUInt32LE(count, 12);
|
|
74
|
+
let offset = headerSize;
|
|
75
|
+
for (const v of vectors) {
|
|
76
|
+
if (v.length !== dims) {
|
|
77
|
+
throw new Error(`Vector dimension mismatch on flush: expected ${dims}, got ${v.length}.`);
|
|
78
|
+
}
|
|
79
|
+
for (let i = 0; i < v.length; i++) {
|
|
80
|
+
buf.writeFloatLE(v[i], offset);
|
|
81
|
+
offset += 4;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
writeFileSync(path, buf);
|
|
85
|
+
}
|
|
86
|
+
function readBinary(buf) {
|
|
87
|
+
if (buf.length < 16)
|
|
88
|
+
throw new Error(`File-store binary too small: ${buf.length} bytes.`);
|
|
89
|
+
const magic = buf.toString("ascii", 0, 4);
|
|
90
|
+
if (magic !== MAGIC) {
|
|
91
|
+
throw new Error(`File-store magic mismatch: expected ${MAGIC}, got ${magic}.`);
|
|
92
|
+
}
|
|
93
|
+
const version = buf.readUInt32LE(4);
|
|
94
|
+
if (version !== FORMAT_VERSION) {
|
|
95
|
+
throw new Error(`Unsupported file-store binary version ${version} (expected ${FORMAT_VERSION}).`);
|
|
96
|
+
}
|
|
97
|
+
const dimensions = buf.readUInt32LE(8);
|
|
98
|
+
const count = buf.readUInt32LE(12);
|
|
99
|
+
const expectedSize = 16 + count * dimensions * 4;
|
|
100
|
+
if (buf.length !== expectedSize) {
|
|
101
|
+
throw new Error(`File-store binary size mismatch: expected ${expectedSize}, got ${buf.length}.`);
|
|
102
|
+
}
|
|
103
|
+
const vectors = [];
|
|
104
|
+
let offset = 16;
|
|
105
|
+
for (let i = 0; i < count; i++) {
|
|
106
|
+
const v = new Float32Array(dimensions);
|
|
107
|
+
for (let j = 0; j < dimensions; j++) {
|
|
108
|
+
v[j] = buf.readFloatLE(offset);
|
|
109
|
+
offset += 4;
|
|
110
|
+
}
|
|
111
|
+
vectors.push(v);
|
|
112
|
+
}
|
|
113
|
+
return { dimensions, count, vectors };
|
|
114
|
+
}
|
|
115
|
+
function writeMeta(path, snap) {
|
|
116
|
+
const meta = {
|
|
117
|
+
version: FORMAT_VERSION,
|
|
118
|
+
dimensions: snap.dimensions ?? 0,
|
|
119
|
+
records: snap.records.map((r) => ({
|
|
120
|
+
id: r.id,
|
|
121
|
+
payload: r.payload,
|
|
122
|
+
hash: r.hash,
|
|
123
|
+
})),
|
|
124
|
+
};
|
|
125
|
+
writeFileSync(path, JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/stores/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ChunkPayload, VectorStore } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* In-memory cosine-similarity vector store. Zero deps. Default for tests
|
|
4
|
+
* and small schemas. Vectors live in `Float32Array`s for compact storage
|
|
5
|
+
* and faster dot products on the hot path.
|
|
6
|
+
*/
|
|
7
|
+
export type MemoryStore = VectorStore & {
|
|
8
|
+
/** Number of vectors currently stored (test/diagnostic helper). */
|
|
9
|
+
size(): number;
|
|
10
|
+
/** Snapshot of all records — used by the file-backed store on flush. */
|
|
11
|
+
snapshot(): {
|
|
12
|
+
dimensions: number | undefined;
|
|
13
|
+
records: {
|
|
14
|
+
id: string;
|
|
15
|
+
vector: Float32Array;
|
|
16
|
+
payload: ChunkPayload;
|
|
17
|
+
hash?: string;
|
|
18
|
+
}[];
|
|
19
|
+
};
|
|
20
|
+
/** Restore from a snapshot — used by the file-backed store on hydrate. */
|
|
21
|
+
restore(snapshot: {
|
|
22
|
+
dimensions?: number;
|
|
23
|
+
records: {
|
|
24
|
+
id: string;
|
|
25
|
+
vector: Float32Array | number[];
|
|
26
|
+
payload: ChunkPayload;
|
|
27
|
+
hash?: string;
|
|
28
|
+
}[];
|
|
29
|
+
}): void;
|
|
30
|
+
/** Attach a content hash to an id (used by the indexer for skip-reembed bookkeeping). */
|
|
31
|
+
setHash(id: string, hash: string): void;
|
|
32
|
+
};
|
|
33
|
+
export declare function createMemoryStore(): MemoryStore;
|
|
34
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAIZ,WAAW,EACZ,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG;IACtC,mEAAmE;IACnE,IAAI,IAAI,MAAM,CAAC;IACf,wEAAwE;IACxE,QAAQ,IAAI;QACV,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,YAAY,CAAC;YAAC,OAAO,EAAE,YAAY,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KACvF,CAAC;IACF,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,EAAE;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE;YACP,EAAE,EAAE,MAAM,CAAC;YACX,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,CAAC;YAChC,OAAO,EAAE,YAAY,CAAC;YACtB,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,EAAE,CAAC;KACL,GAAG,IAAI,CAAC;IACT,yFAAyF;IACzF,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC,CAAC;AAEF,wBAAgB,iBAAiB,IAAI,WAAW,CAqG/C"}
|