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,322 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.VectorStore = void 0;
7
+ const fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ const sql_js_1 = __importDefault(require("sql.js"));
10
+ /**
11
+ * Helper for loading ESM-only modules from a CJS context.
12
+ * Uses native import() via Function constructor to bypass
13
+ * TypeScript's CJS transformation of dynamic imports.
14
+ */
15
+ const esmImport = new Function('specifier', 'return import(specifier)');
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
+ class VectorStore {
24
+ db = null;
25
+ pipeline = null;
26
+ dbPath;
27
+ initialized = false;
28
+ initPromise = null;
29
+ modelPromise = null;
30
+ constructor(dbPath) {
31
+ this.dbPath = dbPath;
32
+ }
33
+ /**
34
+ * Initialize the vector store: sets up the SQLite database.
35
+ * The embedding model is loaded lazily on first embed operation.
36
+ * Safe to call multiple times — only initializes once.
37
+ */
38
+ async init() {
39
+ if (this.initialized)
40
+ return;
41
+ if (this.initPromise)
42
+ return this.initPromise;
43
+ this.initPromise = this._init();
44
+ await this.initPromise;
45
+ }
46
+ async _init() {
47
+ // Ensure directory exists
48
+ const dir = (0, path_1.dirname)(this.dbPath);
49
+ if (!(0, fs_1.existsSync)(dir)) {
50
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
51
+ }
52
+ // Initialize sql.js (loads WASM automatically)
53
+ const SQL = await (0, sql_js_1.default)();
54
+ // Load existing DB or create a new one
55
+ if ((0, fs_1.existsSync)(this.dbPath)) {
56
+ const fileBuffer = (0, fs_1.readFileSync)(this.dbPath);
57
+ this.db = new SQL.Database(fileBuffer);
58
+ }
59
+ else {
60
+ this.db = new SQL.Database();
61
+ }
62
+ // Create schema
63
+ this.db.run(`
64
+ CREATE TABLE IF NOT EXISTS documents (
65
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
66
+ file TEXT NOT NULL,
67
+ chunk_index INTEGER NOT NULL,
68
+ content TEXT NOT NULL,
69
+ embedding BLOB NOT NULL,
70
+ updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
71
+ UNIQUE(file, chunk_index)
72
+ )
73
+ `);
74
+ // Index for fast file lookups
75
+ this.db.run('CREATE INDEX IF NOT EXISTS idx_documents_file ON documents(file)');
76
+ this.persist();
77
+ this.initialized = true;
78
+ }
79
+ /**
80
+ * Load the embedding model. Called lazily on first embed operation.
81
+ * Downloads the model on first use (~23MB for all-MiniLM-L6-v2).
82
+ */
83
+ async loadEmbeddingModel() {
84
+ if (this.pipeline)
85
+ return;
86
+ if (this.modelPromise)
87
+ return this.modelPromise;
88
+ this.modelPromise = (async () => {
89
+ console.log('[VectorStore] Loading embedding model (Xenova/all-MiniLM-L6-v2)...');
90
+ console.log('[VectorStore] First run may download the model (~23MB).');
91
+ // Dynamic ESM import for @huggingface/transformers (ESM-only package)
92
+ const { pipeline: createPipeline } = await esmImport('@huggingface/transformers');
93
+ this.pipeline = await createPipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
94
+ console.log('[VectorStore] Embedding model loaded.');
95
+ })();
96
+ await this.modelPromise;
97
+ }
98
+ /**
99
+ * Ensure the embedding model is loaded before use.
100
+ */
101
+ async ensurePipeline() {
102
+ if (!this.pipeline) {
103
+ await this.loadEmbeddingModel();
104
+ }
105
+ }
106
+ /**
107
+ * Persist the in-memory SQLite database to disk.
108
+ */
109
+ persist() {
110
+ if (!this.db)
111
+ return;
112
+ const data = this.db.export();
113
+ const buffer = Buffer.from(data);
114
+ (0, fs_1.writeFileSync)(this.dbPath, buffer);
115
+ }
116
+ /**
117
+ * Embed text into a Float32Array vector (384 dimensions).
118
+ */
119
+ async embed(text) {
120
+ await this.ensurePipeline();
121
+ const output = await this.pipeline(text, { pooling: 'mean', normalize: true });
122
+ return new Float32Array(output.data);
123
+ }
124
+ /**
125
+ * Chunk text into overlapping segments.
126
+ *
127
+ * Strategy:
128
+ * 1. Split by paragraphs (double newline)
129
+ * 2. If a paragraph exceeds 500 chars, split by sentences
130
+ * 3. Each chunk gets a 100-char overlap with the previous chunk
131
+ */
132
+ chunkText(text) {
133
+ const MAX_CHUNK_SIZE = 500;
134
+ const OVERLAP = 100;
135
+ // Split by paragraphs (double newline)
136
+ const paragraphs = text.split(/\n\s*\n/).filter(p => p.trim().length > 0);
137
+ const rawChunks = [];
138
+ for (const paragraph of paragraphs) {
139
+ const trimmed = paragraph.trim();
140
+ if (trimmed.length <= MAX_CHUNK_SIZE) {
141
+ rawChunks.push(trimmed);
142
+ }
143
+ else {
144
+ // Split long paragraphs by sentences
145
+ const sentences = trimmed.split(/(?<=[.!?])\s+/);
146
+ let current = '';
147
+ for (const sentence of sentences) {
148
+ if (current.length + sentence.length + 1 > MAX_CHUNK_SIZE && current.length > 0) {
149
+ rawChunks.push(current.trim());
150
+ current = sentence;
151
+ }
152
+ else {
153
+ current = current ? current + ' ' + sentence : sentence;
154
+ }
155
+ }
156
+ if (current.trim().length > 0) {
157
+ rawChunks.push(current.trim());
158
+ }
159
+ }
160
+ }
161
+ if (rawChunks.length === 0)
162
+ return [];
163
+ // Apply overlap: each chunk (except the first) gets the last 100 chars
164
+ // of the previous chunk prepended
165
+ const chunks = [rawChunks[0]];
166
+ for (let i = 1; i < rawChunks.length; i++) {
167
+ const prevChunk = rawChunks[i - 1];
168
+ const overlap = prevChunk.slice(-OVERLAP);
169
+ chunks.push(overlap + ' ' + rawChunks[i]);
170
+ }
171
+ return chunks;
172
+ }
173
+ /**
174
+ * Serialize a Float32Array to a Buffer for BLOB storage.
175
+ */
176
+ serializeEmbedding(embedding) {
177
+ return new Uint8Array(embedding.buffer, embedding.byteOffset, embedding.byteLength);
178
+ }
179
+ /**
180
+ * Deserialize a BLOB (Uint8Array) back to Float32Array.
181
+ */
182
+ deserializeEmbedding(blob) {
183
+ // Create a proper copy to ensure alignment
184
+ const buffer = new ArrayBuffer(blob.byteLength);
185
+ new Uint8Array(buffer).set(blob);
186
+ return new Float32Array(buffer);
187
+ }
188
+ /**
189
+ * Compute cosine similarity between two vectors.
190
+ * Both vectors should be normalized (which they are from the model),
191
+ * so this is equivalent to the dot product.
192
+ */
193
+ cosineSimilarity(a, b) {
194
+ let dotProduct = 0;
195
+ let normA = 0;
196
+ let normB = 0;
197
+ for (let i = 0; i < a.length; i++) {
198
+ dotProduct += a[i] * b[i];
199
+ normA += a[i] * a[i];
200
+ normB += b[i] * b[i];
201
+ }
202
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
203
+ if (denominator === 0)
204
+ return 0;
205
+ return dotProduct / denominator;
206
+ }
207
+ /**
208
+ * Add a document to the vector store.
209
+ * Chunks the content, embeds each chunk, and stores in the DB.
210
+ * Replaces any existing chunks for this file.
211
+ */
212
+ async addDocument(file, content) {
213
+ if (!this.db)
214
+ throw new Error('VectorStore not initialized. Call init() first.');
215
+ const chunks = this.chunkText(content);
216
+ if (chunks.length === 0)
217
+ return;
218
+ // Remove existing chunks for this file
219
+ this.db.run('DELETE FROM documents WHERE file = ?', [file]);
220
+ // Embed and insert each chunk
221
+ for (let i = 0; i < chunks.length; i++) {
222
+ const embedding = await this.embed(chunks[i]);
223
+ const embeddingBlob = this.serializeEmbedding(embedding);
224
+ const now = new Date().toISOString();
225
+ this.db.run('INSERT INTO documents (file, chunk_index, content, embedding, updated_at) VALUES (?, ?, ?, ?, ?)', [file, i, chunks[i], embeddingBlob, now]);
226
+ }
227
+ this.persist();
228
+ }
229
+ /**
230
+ * Search the vector store for content similar to the query.
231
+ * Returns top-k results sorted by cosine similarity (descending).
232
+ *
233
+ * For performance with large DBs (>10K chunks), embeddings are loaded
234
+ * as Float32Array and compared using optimized JS computation.
235
+ */
236
+ async search(query, limit = 10) {
237
+ if (!this.db)
238
+ throw new Error('VectorStore not initialized. Call init() first.');
239
+ // Embed the query
240
+ const queryEmbedding = await this.embed(query);
241
+ // Load all documents with embeddings
242
+ const results = this.db.exec('SELECT file, chunk_index, content, embedding FROM documents');
243
+ if (results.length === 0 || results[0].values.length === 0) {
244
+ return [];
245
+ }
246
+ // Compute cosine similarity for each document
247
+ const scored = [];
248
+ for (const row of results[0].values) {
249
+ const [file, chunkIndex, content, embeddingBlob] = row;
250
+ const docEmbedding = this.deserializeEmbedding(embeddingBlob instanceof Uint8Array ? embeddingBlob : new Uint8Array(embeddingBlob));
251
+ const score = this.cosineSimilarity(queryEmbedding, docEmbedding);
252
+ scored.push({
253
+ file: file,
254
+ chunkIndex: chunkIndex,
255
+ content: content,
256
+ score
257
+ });
258
+ }
259
+ // Sort by score descending and return top-k
260
+ scored.sort((a, b) => b.score - a.score);
261
+ return scored.slice(0, limit);
262
+ }
263
+ /**
264
+ * Remove all chunks for a given file.
265
+ */
266
+ async removeDocument(file) {
267
+ if (!this.db)
268
+ throw new Error('VectorStore not initialized. Call init() first.');
269
+ this.db.run('DELETE FROM documents WHERE file = ?', [file]);
270
+ this.persist();
271
+ }
272
+ /**
273
+ * Full reindex from a file map (filename → content).
274
+ * Clears all existing data and re-indexes everything.
275
+ */
276
+ async reindex(files) {
277
+ if (!this.db)
278
+ throw new Error('VectorStore not initialized. Call init() first.');
279
+ // Clear all existing documents
280
+ this.db.run('DELETE FROM documents');
281
+ // Re-add all files
282
+ for (const [file, content] of files) {
283
+ await this.addDocument(file, content);
284
+ }
285
+ this.persist();
286
+ }
287
+ /**
288
+ * Get statistics about the vector store.
289
+ */
290
+ getStats() {
291
+ if (!this.db) {
292
+ return { documentCount: 0, chunkCount: 0, lastUpdate: '' };
293
+ }
294
+ const countResult = this.db.exec('SELECT COUNT(DISTINCT file) as docs, COUNT(*) as chunks FROM documents');
295
+ const updateResult = this.db.exec('SELECT MAX(updated_at) as last_update FROM documents');
296
+ const docs = countResult.length > 0 && countResult[0].values.length > 0
297
+ ? countResult[0].values[0][0]
298
+ : 0;
299
+ const chunks = countResult.length > 0 && countResult[0].values.length > 0
300
+ ? countResult[0].values[0][1]
301
+ : 0;
302
+ const lastUpdate = updateResult.length > 0 && updateResult[0].values.length > 0
303
+ ? (updateResult[0].values[0][0] || '')
304
+ : '';
305
+ return { documentCount: docs, chunkCount: chunks, lastUpdate };
306
+ }
307
+ /**
308
+ * Close the database connection and persist to disk.
309
+ */
310
+ close() {
311
+ if (this.db) {
312
+ this.persist();
313
+ this.db.close();
314
+ this.db = null;
315
+ }
316
+ this.initialized = false;
317
+ this.initPromise = null;
318
+ this.modelPromise = null;
319
+ }
320
+ }
321
+ exports.VectorStore = VectorStore;
322
+ //# sourceMappingURL=vector-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector-store.js","sourceRoot":"","sources":["../src/vector-store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,17 +1,20 @@
1
1
  {
2
2
  "name": "sigma-memory",
3
- "version": "0.1.2",
4
- "description": "Persistent memory for Phi Code \u2014 QMD vector search + Ontology graph + Markdown notes",
3
+ "version": "0.2.0",
4
+ "description": "Persistent memory for Phi Code embedded vector search + Ontology graph + Markdown notes",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
9
  "clean": "rm -rf dist"
10
10
  },
11
- "dependencies": {},
11
+ "dependencies": {
12
+ "@huggingface/transformers": "^3.8.1",
13
+ "sql.js": "^1.14.1"
14
+ },
12
15
  "devDependencies": {
13
- "typescript": "^5.4.0",
14
- "@types/node": "^20.0.0"
16
+ "@types/node": "^20.0.0",
17
+ "typescript": "^5.4.0"
15
18
  },
16
19
  "repository": {
17
20
  "type": "git",
@@ -21,6 +24,7 @@
21
24
  "keywords": [
22
25
  "memory",
23
26
  "vector-search",
27
+ "embeddings",
24
28
  "ontology",
25
29
  "phi-code"
26
30
  ],
@@ -30,4 +34,4 @@
30
34
  "src",
31
35
  "README.md"
32
36
  ]
33
- }
37
+ }
package/src/index.ts CHANGED
@@ -3,13 +3,13 @@ import { homedir } from 'os';
3
3
  import { existsSync, mkdirSync } from 'fs';
4
4
  import { NotesManager } from './notes.js';
5
5
  import { OntologyManager } from './ontology.js';
6
- import { QMDManager } from './qmd.js';
6
+ import { VectorStore } from './vector-store.js';
7
7
  import type { MemoryConfig, UnifiedSearchResult, MemoryStatus } from './types.js';
8
8
 
9
9
  export class SigmaMemory {
10
10
  public readonly notes: NotesManager;
11
11
  public readonly ontology: OntologyManager;
12
- public readonly qmd: QMDManager;
12
+ public readonly vectors: VectorStore;
13
13
  private readonly config: MemoryConfig;
14
14
 
15
15
  constructor(config?: Partial<MemoryConfig>) {
@@ -17,9 +17,7 @@ export class SigmaMemory {
17
17
  const defaultConfig: MemoryConfig = {
18
18
  memoryDir: join(homedir(), '.phi', 'memory'),
19
19
  projectMemoryDir: join(process.cwd(), '.phi', 'memory'),
20
- ontologyPath: join(homedir(), '.phi', 'memory', 'ontology', 'graph.jsonl'),
21
- qmdEnabled: true,
22
- qmdCommand: 'qmd'
20
+ ontologyPath: join(homedir(), '.phi', 'memory', 'ontology', 'graph.jsonl')
23
21
  };
24
22
 
25
23
  this.config = { ...defaultConfig, ...config };
@@ -27,23 +25,23 @@ export class SigmaMemory {
27
25
  // Initialize managers
28
26
  this.notes = new NotesManager(this.config);
29
27
  this.ontology = new OntologyManager(this.config);
30
- this.qmd = new QMDManager(this.config);
28
+ this.vectors = new VectorStore(join(this.config.memoryDir, 'vectors.db'));
31
29
  }
32
30
 
33
31
  /**
34
- * Unified search: searches notes + ontology + QMD, combines results
32
+ * Unified search: searches notes + ontology + vectors, combines results
35
33
  */
36
34
  async search(query: string): Promise<UnifiedSearchResult[]> {
37
35
  const results: UnifiedSearchResult[] = [];
38
36
 
39
- // Search in notes
37
+ // Search in notes (full-text grep)
40
38
  try {
41
39
  const notesResults = this.notes.search(query);
42
40
  for (const result of notesResults) {
43
41
  results.push({
44
42
  source: 'notes',
45
43
  type: 'note',
46
- score: 0.8, // Default score for notes
44
+ score: 0.8, // Default score for text-match notes
47
45
  data: result
48
46
  });
49
47
  }
@@ -77,19 +75,19 @@ export class SigmaMemory {
77
75
  // Ontology search failed silently
78
76
  }
79
77
 
80
- // QMD vector search
78
+ // Vector similarity search
81
79
  try {
82
- const qmdResults = await this.qmd.search(query, 5);
83
- for (const result of qmdResults) {
80
+ const vectorResults = await this.vectors.search(query, 5);
81
+ for (const result of vectorResults) {
84
82
  results.push({
85
- source: 'qmd',
83
+ source: 'vectors',
86
84
  type: 'file',
87
85
  score: result.score,
88
86
  data: result
89
87
  });
90
88
  }
91
89
  } catch (error) {
92
- // QMD search failed silently
90
+ // Vector search failed silently
93
91
  }
94
92
 
95
93
  // Sort by score descending
@@ -99,7 +97,8 @@ export class SigmaMemory {
99
97
  }
100
98
 
101
99
  /**
102
- * Initialize all required directories
100
+ * Initialize all required directories and the vector store.
101
+ * On first run, this will download the embedding model and index notes.
103
102
  */
104
103
  async init(): Promise<void> {
105
104
  // Create base directories
@@ -111,12 +110,26 @@ export class SigmaMemory {
111
110
  mkdirSync(this.config.projectMemoryDir, { recursive: true });
112
111
  }
113
112
 
114
- // Initialize QMD if enabled
115
- if (this.config.qmdEnabled && this.qmd.isAvailable()) {
113
+ // Initialize vector store (DB setup only — fast)
114
+ await this.vectors.init();
115
+
116
+ // Auto-index existing notes into the vector store
117
+ await this.indexNotes();
118
+ }
119
+
120
+ /**
121
+ * Index all markdown notes into the vector store.
122
+ * Reads every .md file from the notes directory and adds it.
123
+ */
124
+ async indexNotes(): Promise<void> {
125
+ const notesList = this.notes.list();
126
+
127
+ for (const note of notesList) {
116
128
  try {
117
- await this.qmd.update();
118
- } catch (error) {
119
- // QMD initialization failed silently
129
+ const content = this.notes.read(note.name);
130
+ await this.vectors.addDocument(note.name, content);
131
+ } catch {
132
+ // Skip files that can't be read
120
133
  }
121
134
  }
122
135
  }
@@ -143,20 +156,13 @@ export class SigmaMemory {
143
156
  relationsByType: ontologyStats.relationsByType
144
157
  };
145
158
 
146
- // QMD status
147
- let qmdStatus: MemoryStatus['qmd'] = { available: false };
148
- if (this.config.qmdEnabled && this.qmd.isAvailable()) {
149
- const status = await this.qmd.status();
150
- qmdStatus = {
151
- available: true,
152
- status: status || { files: 0, chunks: 0, lastUpdate: null }
153
- };
154
- }
159
+ // Vector store status
160
+ const vectorStats = this.vectors.getStats();
155
161
 
156
162
  return {
157
163
  notes: notesStatus,
158
164
  ontology: ontologyStatus,
159
- qmd: qmdStatus
165
+ vectors: vectorStats
160
166
  };
161
167
  }
162
168
 
@@ -171,8 +177,8 @@ export class SigmaMemory {
171
177
  // Convenient exports
172
178
  export { NotesManager } from './notes.js';
173
179
  export { OntologyManager } from './ontology.js';
174
- export { QMDManager } from './qmd.js';
180
+ export { VectorStore } from './vector-store.js';
175
181
  export * from './types.js';
176
182
 
177
183
  // Default export
178
- export default SigmaMemory;
184
+ export default SigmaMemory;
package/src/types.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  export interface MemoryConfig {
2
2
  memoryDir: string; // ~/.phi/memory/
3
- projectMemoryDir: string; // .phi/memory/ (dans le projet courant)
3
+ projectMemoryDir: string; // .phi/memory/ (in the current project)
4
4
  ontologyPath: string; // ~/.phi/memory/ontology/graph.jsonl
5
- qmdEnabled: boolean;
6
- qmdCommand?: string; // Chemin vers le binaire QMD
7
5
  }
8
6
 
9
7
  export interface SearchResult {
@@ -13,6 +11,13 @@ export interface SearchResult {
13
11
  score: number;
14
12
  }
15
13
 
14
+ export interface VectorSearchResult {
15
+ file: string;
16
+ chunkIndex: number;
17
+ content: string;
18
+ score: number; // cosine similarity 0-1
19
+ }
20
+
16
21
  export interface OntologyEntity {
17
22
  id: string;
18
23
  type: 'Person' | 'Project' | 'Device' | 'Account' | 'Document' | 'Service' | 'Concept';
@@ -38,7 +43,7 @@ export interface Note {
38
43
  }
39
44
 
40
45
  export interface UnifiedSearchResult {
41
- source: 'notes' | 'ontology' | 'qmd';
46
+ source: 'notes' | 'ontology' | 'vectors';
42
47
  type?: 'entity' | 'relation' | 'note' | 'file';
43
48
  score: number;
44
49
  data: any;
@@ -56,13 +61,10 @@ export interface MemoryStatus {
56
61
  entitiesByType: Record<string, number>;
57
62
  relationsByType: Record<string, number>;
58
63
  };
59
- qmd: {
60
- available: boolean;
61
- status?: {
62
- files: number;
63
- chunks: number;
64
- lastUpdate: string | null;
65
- };
64
+ vectors: {
65
+ documentCount: number;
66
+ chunkCount: number;
67
+ lastUpdate: string;
66
68
  };
67
69
  }
68
70
 
@@ -93,4 +95,4 @@ export interface OntologyDeleteEntry {
93
95
  deletedAt: string;
94
96
  }
95
97
 
96
- export type OntologyJSONLEntry = OntologyEntityEntry | OntologyRelationEntry | OntologyDeleteEntry;
98
+ export type OntologyJSONLEntry = OntologyEntityEntry | OntologyRelationEntry | OntologyDeleteEntry;