sigma-memory 0.1.2 → 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.
@@ -0,0 +1,384 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { dirname } from 'path';
3
+ import initSqlJs, { type Database } from 'sql.js';
4
+ import type { VectorSearchResult } from './types.js';
5
+
6
+ /**
7
+ * Helper for loading ESM-only modules from a CJS context.
8
+ * Uses native import() via Function constructor to bypass
9
+ * TypeScript's CJS transformation of dynamic imports.
10
+ */
11
+ const esmImport = new Function(
12
+ 'specifier',
13
+ 'return import(specifier)'
14
+ ) as (specifier: string) => Promise<any>;
15
+
16
+ /**
17
+ * Self-contained vector store using sql.js (SQLite via WebAssembly) and
18
+ * @huggingface/transformers for local embeddings.
19
+ *
20
+ * Zero configuration — works out of the box on any platform.
21
+ * No native compilation required.
22
+ */
23
+ export class VectorStore {
24
+ private db: Database | null = null;
25
+ private pipeline: any = null;
26
+ private dbPath: string;
27
+ private initialized = false;
28
+ private initPromise: Promise<void> | null = null;
29
+ private modelPromise: Promise<void> | null = null;
30
+
31
+ constructor(dbPath: string) {
32
+ this.dbPath = dbPath;
33
+ }
34
+
35
+ /**
36
+ * Initialize the vector store: sets up the SQLite database.
37
+ * The embedding model is loaded lazily on first embed operation.
38
+ * Safe to call multiple times — only initializes once.
39
+ */
40
+ async init(): Promise<void> {
41
+ if (this.initialized) return;
42
+ if (this.initPromise) return this.initPromise;
43
+
44
+ this.initPromise = this._init();
45
+ await this.initPromise;
46
+ }
47
+
48
+ private async _init(): Promise<void> {
49
+ // Ensure directory exists
50
+ const dir = dirname(this.dbPath);
51
+ if (!existsSync(dir)) {
52
+ mkdirSync(dir, { recursive: true });
53
+ }
54
+
55
+ // Initialize sql.js (loads WASM automatically)
56
+ const SQL = await initSqlJs();
57
+
58
+ // Load existing DB or create a new one
59
+ if (existsSync(this.dbPath)) {
60
+ const fileBuffer = readFileSync(this.dbPath);
61
+ this.db = new SQL.Database(fileBuffer);
62
+ } else {
63
+ this.db = new SQL.Database();
64
+ }
65
+
66
+ // Create schema
67
+ this.db.run(`
68
+ CREATE TABLE IF NOT EXISTS documents (
69
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
70
+ file TEXT NOT NULL,
71
+ chunk_index INTEGER NOT NULL,
72
+ content TEXT NOT NULL,
73
+ embedding BLOB NOT NULL,
74
+ updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
75
+ UNIQUE(file, chunk_index)
76
+ )
77
+ `);
78
+
79
+ // Index for fast file lookups
80
+ this.db.run('CREATE INDEX IF NOT EXISTS idx_documents_file ON documents(file)');
81
+
82
+ this.persist();
83
+ this.initialized = true;
84
+ }
85
+
86
+ /**
87
+ * Load the embedding model. Called lazily on first embed operation.
88
+ * Downloads the model on first use (~23MB for all-MiniLM-L6-v2).
89
+ */
90
+ private async loadEmbeddingModel(): Promise<void> {
91
+ if (this.pipeline) return;
92
+ if (this.modelPromise) return this.modelPromise;
93
+
94
+ this.modelPromise = (async () => {
95
+ console.log('[VectorStore] Loading embedding model (Xenova/all-MiniLM-L6-v2)...');
96
+ console.log('[VectorStore] First run may download the model (~23MB).');
97
+
98
+ // Dynamic ESM import for @huggingface/transformers (ESM-only package)
99
+ const { pipeline: createPipeline } = await esmImport('@huggingface/transformers');
100
+
101
+ this.pipeline = await createPipeline(
102
+ 'feature-extraction',
103
+ 'Xenova/all-MiniLM-L6-v2'
104
+ );
105
+
106
+ console.log('[VectorStore] Embedding model loaded.');
107
+ })();
108
+
109
+ await this.modelPromise;
110
+ }
111
+
112
+ /**
113
+ * Ensure the embedding model is loaded before use.
114
+ */
115
+ private async ensurePipeline(): Promise<void> {
116
+ if (!this.pipeline) {
117
+ await this.loadEmbeddingModel();
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Persist the in-memory SQLite database to disk.
123
+ */
124
+ private persist(): void {
125
+ if (!this.db) return;
126
+ const data = this.db.export();
127
+ const buffer = Buffer.from(data);
128
+ writeFileSync(this.dbPath, buffer);
129
+ }
130
+
131
+ /**
132
+ * Embed text into a Float32Array vector (384 dimensions).
133
+ */
134
+ private async embed(text: string): Promise<Float32Array> {
135
+ await this.ensurePipeline();
136
+
137
+ const output = await this.pipeline(text, { pooling: 'mean', normalize: true });
138
+ return new Float32Array(output.data);
139
+ }
140
+
141
+ /**
142
+ * Chunk text into overlapping segments.
143
+ *
144
+ * Strategy:
145
+ * 1. Split by paragraphs (double newline)
146
+ * 2. If a paragraph exceeds 500 chars, split by sentences
147
+ * 3. Each chunk gets a 100-char overlap with the previous chunk
148
+ */
149
+ private chunkText(text: string): string[] {
150
+ const MAX_CHUNK_SIZE = 500;
151
+ const OVERLAP = 100;
152
+
153
+ // Split by paragraphs (double newline)
154
+ const paragraphs = text.split(/\n\s*\n/).filter(p => p.trim().length > 0);
155
+
156
+ const rawChunks: string[] = [];
157
+
158
+ for (const paragraph of paragraphs) {
159
+ const trimmed = paragraph.trim();
160
+
161
+ if (trimmed.length <= MAX_CHUNK_SIZE) {
162
+ rawChunks.push(trimmed);
163
+ } else {
164
+ // Split long paragraphs by sentences
165
+ const sentences = trimmed.split(/(?<=[.!?])\s+/);
166
+ let current = '';
167
+
168
+ for (const sentence of sentences) {
169
+ if (current.length + sentence.length + 1 > MAX_CHUNK_SIZE && current.length > 0) {
170
+ rawChunks.push(current.trim());
171
+ current = sentence;
172
+ } else {
173
+ current = current ? current + ' ' + sentence : sentence;
174
+ }
175
+ }
176
+
177
+ if (current.trim().length > 0) {
178
+ rawChunks.push(current.trim());
179
+ }
180
+ }
181
+ }
182
+
183
+ if (rawChunks.length === 0) return [];
184
+
185
+ // Apply overlap: each chunk (except the first) gets the last 100 chars
186
+ // of the previous chunk prepended
187
+ const chunks: string[] = [rawChunks[0]];
188
+
189
+ for (let i = 1; i < rawChunks.length; i++) {
190
+ const prevChunk = rawChunks[i - 1];
191
+ const overlap = prevChunk.slice(-OVERLAP);
192
+ chunks.push(overlap + ' ' + rawChunks[i]);
193
+ }
194
+
195
+ return chunks;
196
+ }
197
+
198
+ /**
199
+ * Serialize a Float32Array to a Buffer for BLOB storage.
200
+ */
201
+ private serializeEmbedding(embedding: Float32Array): Uint8Array {
202
+ return new Uint8Array(embedding.buffer, embedding.byteOffset, embedding.byteLength);
203
+ }
204
+
205
+ /**
206
+ * Deserialize a BLOB (Uint8Array) back to Float32Array.
207
+ */
208
+ private deserializeEmbedding(blob: Uint8Array): Float32Array {
209
+ // Create a proper copy to ensure alignment
210
+ const buffer = new ArrayBuffer(blob.byteLength);
211
+ new Uint8Array(buffer).set(blob);
212
+ return new Float32Array(buffer);
213
+ }
214
+
215
+ /**
216
+ * Compute cosine similarity between two vectors.
217
+ * Both vectors should be normalized (which they are from the model),
218
+ * so this is equivalent to the dot product.
219
+ */
220
+ private cosineSimilarity(a: Float32Array, b: Float32Array): number {
221
+ let dotProduct = 0;
222
+ let normA = 0;
223
+ let normB = 0;
224
+
225
+ for (let i = 0; i < a.length; i++) {
226
+ dotProduct += a[i] * b[i];
227
+ normA += a[i] * a[i];
228
+ normB += b[i] * b[i];
229
+ }
230
+
231
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
232
+ if (denominator === 0) return 0;
233
+
234
+ return dotProduct / denominator;
235
+ }
236
+
237
+ /**
238
+ * Add a document to the vector store.
239
+ * Chunks the content, embeds each chunk, and stores in the DB.
240
+ * Replaces any existing chunks for this file.
241
+ */
242
+ async addDocument(file: string, content: string): Promise<void> {
243
+ if (!this.db) throw new Error('VectorStore not initialized. Call init() first.');
244
+
245
+ const chunks = this.chunkText(content);
246
+ if (chunks.length === 0) return;
247
+
248
+ // Remove existing chunks for this file
249
+ this.db.run('DELETE FROM documents WHERE file = ?', [file]);
250
+
251
+ // Embed and insert each chunk
252
+ for (let i = 0; i < chunks.length; i++) {
253
+ const embedding = await this.embed(chunks[i]);
254
+ const embeddingBlob = this.serializeEmbedding(embedding);
255
+ const now = new Date().toISOString();
256
+
257
+ this.db.run(
258
+ 'INSERT INTO documents (file, chunk_index, content, embedding, updated_at) VALUES (?, ?, ?, ?, ?)',
259
+ [file, i, chunks[i], embeddingBlob as any, now]
260
+ );
261
+ }
262
+
263
+ this.persist();
264
+ }
265
+
266
+ /**
267
+ * Search the vector store for content similar to the query.
268
+ * Returns top-k results sorted by cosine similarity (descending).
269
+ *
270
+ * For performance with large DBs (>10K chunks), embeddings are loaded
271
+ * as Float32Array and compared using optimized JS computation.
272
+ */
273
+ async search(query: string, limit: number = 10): Promise<VectorSearchResult[]> {
274
+ if (!this.db) throw new Error('VectorStore not initialized. Call init() first.');
275
+
276
+ // Embed the query
277
+ const queryEmbedding = await this.embed(query);
278
+
279
+ // Load all documents with embeddings
280
+ const results = this.db.exec(
281
+ 'SELECT file, chunk_index, content, embedding FROM documents'
282
+ );
283
+
284
+ if (results.length === 0 || results[0].values.length === 0) {
285
+ return [];
286
+ }
287
+
288
+ // Compute cosine similarity for each document
289
+ const scored: VectorSearchResult[] = [];
290
+
291
+ for (const row of results[0].values) {
292
+ const [file, chunkIndex, content, embeddingBlob] = row as [string, number, string, Uint8Array];
293
+
294
+ const docEmbedding = this.deserializeEmbedding(
295
+ embeddingBlob instanceof Uint8Array ? embeddingBlob : new Uint8Array(embeddingBlob as any)
296
+ );
297
+
298
+ const score = this.cosineSimilarity(queryEmbedding, docEmbedding);
299
+
300
+ scored.push({
301
+ file: file as string,
302
+ chunkIndex: chunkIndex as number,
303
+ content: content as string,
304
+ score
305
+ });
306
+ }
307
+
308
+ // Sort by score descending and return top-k
309
+ scored.sort((a, b) => b.score - a.score);
310
+ return scored.slice(0, limit);
311
+ }
312
+
313
+ /**
314
+ * Remove all chunks for a given file.
315
+ */
316
+ async removeDocument(file: string): Promise<void> {
317
+ if (!this.db) throw new Error('VectorStore not initialized. Call init() first.');
318
+
319
+ this.db.run('DELETE FROM documents WHERE file = ?', [file]);
320
+ this.persist();
321
+ }
322
+
323
+ /**
324
+ * Full reindex from a file map (filename → content).
325
+ * Clears all existing data and re-indexes everything.
326
+ */
327
+ async reindex(files: Map<string, string>): Promise<void> {
328
+ if (!this.db) throw new Error('VectorStore not initialized. Call init() first.');
329
+
330
+ // Clear all existing documents
331
+ this.db.run('DELETE FROM documents');
332
+
333
+ // Re-add all files
334
+ for (const [file, content] of files) {
335
+ await this.addDocument(file, content);
336
+ }
337
+
338
+ this.persist();
339
+ }
340
+
341
+ /**
342
+ * Get statistics about the vector store.
343
+ */
344
+ getStats(): { documentCount: number; chunkCount: number; lastUpdate: string } {
345
+ if (!this.db) {
346
+ return { documentCount: 0, chunkCount: 0, lastUpdate: '' };
347
+ }
348
+
349
+ const countResult = this.db.exec(
350
+ 'SELECT COUNT(DISTINCT file) as docs, COUNT(*) as chunks FROM documents'
351
+ );
352
+ const updateResult = this.db.exec(
353
+ 'SELECT MAX(updated_at) as last_update FROM documents'
354
+ );
355
+
356
+ const docs = countResult.length > 0 && countResult[0].values.length > 0
357
+ ? (countResult[0].values[0][0] as number)
358
+ : 0;
359
+
360
+ const chunks = countResult.length > 0 && countResult[0].values.length > 0
361
+ ? (countResult[0].values[0][1] as number)
362
+ : 0;
363
+
364
+ const lastUpdate = updateResult.length > 0 && updateResult[0].values.length > 0
365
+ ? ((updateResult[0].values[0][0] as string) || '')
366
+ : '';
367
+
368
+ return { documentCount: docs, chunkCount: chunks, lastUpdate };
369
+ }
370
+
371
+ /**
372
+ * Close the database connection and persist to disk.
373
+ */
374
+ close(): void {
375
+ if (this.db) {
376
+ this.persist();
377
+ this.db.close();
378
+ this.db = null;
379
+ }
380
+ this.initialized = false;
381
+ this.initPromise = null;
382
+ this.modelPromise = null;
383
+ }
384
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Type declarations for external dependencies.
3
+ * Minimal declarations to avoid needing @types packages.
4
+ */
5
+
6
+ declare module 'sql.js' {
7
+ interface Database {
8
+ run(sql: string, params?: any[]): Database;
9
+ exec(sql: string): Array<{ columns: string[]; values: any[][] }>;
10
+ prepare(sql: string): Statement;
11
+ export(): Uint8Array;
12
+ close(): void;
13
+ }
14
+
15
+ interface Statement {
16
+ bind(params?: any[]): boolean;
17
+ step(): boolean;
18
+ get(): any[];
19
+ getAsObject(): Record<string, any>;
20
+ free(): void;
21
+ run(params?: any[]): void;
22
+ }
23
+
24
+ interface SqlJsStatic {
25
+ Database: new (data?: ArrayLike<number> | null) => Database;
26
+ }
27
+
28
+ export type { Database, Statement, SqlJsStatic };
29
+ export default function initSqlJs(config?: any): Promise<SqlJsStatic>;
30
+ }
package/dist/qmd.d.ts DELETED
@@ -1,35 +0,0 @@
1
- import type { MemoryConfig, SearchResult } from './types.js';
2
- export declare class QMDManager {
3
- private config;
4
- private qmdCommand;
5
- constructor(config: MemoryConfig);
6
- /**
7
- * Retourne true si QMD est installé et fonctionnel
8
- */
9
- isAvailable(): boolean;
10
- /**
11
- * Lance `qmd query` et parse les résultats
12
- */
13
- search(query: string, maxResults?: number): Promise<SearchResult[]>;
14
- /**
15
- * Lance `qmd update` pour reindexer
16
- */
17
- update(): Promise<boolean>;
18
- /**
19
- * Lance `qmd embed` pour recalculer les embeddings
20
- */
21
- embed(): Promise<boolean>;
22
- /**
23
- * Liste les collections QMD
24
- */
25
- collections(): Promise<string[]>;
26
- /**
27
- * Retourne le statut (nb fichiers, nb chunks, dernière mise à jour)
28
- */
29
- status(): Promise<{
30
- files: number;
31
- chunks: number;
32
- lastUpdate: string | null;
33
- } | null>;
34
- }
35
- //# sourceMappingURL=qmd.d.ts.map
package/dist/qmd.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"qmd.d.ts","sourceRoot":"","sources":["../src/qmd.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE7D,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,YAAY;IAKhC;;OAEG;IACH,WAAW,IAAI,OAAO;IAiBtB;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,SAAK,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAsCrE;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBhC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAkB/B;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IA8BtC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CA4B7F"}
package/dist/qmd.js DELETED
@@ -1,162 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.QMDManager = void 0;
4
- const child_process_1 = require("child_process");
5
- class QMDManager {
6
- config;
7
- qmdCommand;
8
- constructor(config) {
9
- this.config = config;
10
- this.qmdCommand = config.qmdCommand || 'qmd';
11
- }
12
- /**
13
- * Retourne true si QMD est installé et fonctionnel
14
- */
15
- isAvailable() {
16
- if (!this.config.qmdEnabled) {
17
- return false;
18
- }
19
- try {
20
- // Teste si le binaire QMD est accessible
21
- (0, child_process_1.execSync)(`${this.qmdCommand} --version`, {
22
- stdio: 'ignore',
23
- timeout: 5000
24
- });
25
- return true;
26
- }
27
- catch (error) {
28
- return false;
29
- }
30
- }
31
- /**
32
- * Lance `qmd query` et parse les résultats
33
- */
34
- async search(query, maxResults = 10) {
35
- if (!this.isAvailable()) {
36
- return [];
37
- }
38
- try {
39
- const output = (0, child_process_1.execSync)(`${this.qmdCommand} query "${query.replace(/"/g, '\\"')}" --limit ${maxResults} --format json`, {
40
- encoding: 'utf8',
41
- timeout: 30000,
42
- cwd: this.config.memoryDir
43
- });
44
- if (!output.trim()) {
45
- return [];
46
- }
47
- const result = JSON.parse(output);
48
- // Le format attendu est { results: [{file, line, content, score}] }
49
- if (result.results && Array.isArray(result.results)) {
50
- return result.results.map((item) => ({
51
- file: item.file || '',
52
- line: item.line || 0,
53
- content: item.content || '',
54
- score: item.score || 0
55
- }));
56
- }
57
- return [];
58
- }
59
- catch (error) {
60
- // QMD search failed silently
61
- return [];
62
- }
63
- }
64
- /**
65
- * Lance `qmd update` pour reindexer
66
- */
67
- async update() {
68
- if (!this.isAvailable()) {
69
- return false;
70
- }
71
- try {
72
- (0, child_process_1.execSync)(`${this.qmdCommand} update`, {
73
- stdio: 'ignore',
74
- timeout: 60000,
75
- cwd: this.config.memoryDir
76
- });
77
- return true;
78
- }
79
- catch (error) {
80
- // QMD update failed silently
81
- return false;
82
- }
83
- }
84
- /**
85
- * Lance `qmd embed` pour recalculer les embeddings
86
- */
87
- async embed() {
88
- if (!this.isAvailable()) {
89
- return false;
90
- }
91
- try {
92
- (0, child_process_1.execSync)(`${this.qmdCommand} embed`, {
93
- stdio: 'ignore',
94
- timeout: 300000, // 5 minutes pour les embeddings
95
- cwd: this.config.memoryDir
96
- });
97
- return true;
98
- }
99
- catch (error) {
100
- // QMD embed failed silently
101
- return false;
102
- }
103
- }
104
- /**
105
- * Liste les collections QMD
106
- */
107
- async collections() {
108
- if (!this.isAvailable()) {
109
- return [];
110
- }
111
- try {
112
- const output = (0, child_process_1.execSync)(`${this.qmdCommand} collections --format json`, {
113
- encoding: 'utf8',
114
- timeout: 10000,
115
- cwd: this.config.memoryDir
116
- });
117
- if (!output.trim()) {
118
- return [];
119
- }
120
- const result = JSON.parse(output);
121
- // Retourne la liste des noms de collections
122
- if (result.collections && Array.isArray(result.collections)) {
123
- return result.collections.map((col) => col.name || col);
124
- }
125
- return [];
126
- }
127
- catch (error) {
128
- // QMD collections failed silently
129
- return [];
130
- }
131
- }
132
- /**
133
- * Retourne le statut (nb fichiers, nb chunks, dernière mise à jour)
134
- */
135
- async status() {
136
- if (!this.isAvailable()) {
137
- return null;
138
- }
139
- try {
140
- const output = (0, child_process_1.execSync)(`${this.qmdCommand} status --format json`, {
141
- encoding: 'utf8',
142
- timeout: 10000,
143
- cwd: this.config.memoryDir
144
- });
145
- if (!output.trim()) {
146
- return { files: 0, chunks: 0, lastUpdate: null };
147
- }
148
- const result = JSON.parse(output);
149
- return {
150
- files: result.files || 0,
151
- chunks: result.chunks || 0,
152
- lastUpdate: result.lastUpdate || null
153
- };
154
- }
155
- catch (error) {
156
- // QMD status check failed silently
157
- return { files: 0, chunks: 0, lastUpdate: null };
158
- }
159
- }
160
- }
161
- exports.QMDManager = QMDManager;
162
- //# sourceMappingURL=qmd.js.map
package/dist/qmd.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"qmd.js","sourceRoot":"","sources":["../src/qmd.ts"],"names":[],"mappings":"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"}