@lotargo/memory_plugin 1.1.5 → 1.1.6

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.
@@ -10,13 +10,17 @@ export const DEFAULT_CONFIG = {
10
10
  embeddingModel: "Xenova/multilingual-e5-small",
11
11
  rerankerModel: "none", // "none" | "Xenova/bge-reranker-base" | custom HF model
12
12
  rerankerEnabled: false,
13
+ batchSize: 12, // Ingestion vector batch size [1 - 256] (default 12)
14
+ gpuAttentionBudget: 2000000, // GPU micro-batch attention budget [1M - 16M] (default 2.0M ~1.5GB VRAM)
15
+ onnxThreads: 0, // ONNX WASM threads: 0 = auto-detect CPU cores, or 1-16
16
+ executionDevice: "cpu", // "cpu" | "webgpu"
13
17
  };
14
18
 
15
19
  let cachedConfig = null;
16
20
 
17
21
  export function getConfig() {
18
22
  if (cachedConfig) {
19
- return { ...cachedConfig };
23
+ return cachedConfig;
20
24
  }
21
25
 
22
26
  ensureDir();
@@ -25,27 +29,27 @@ export function getConfig() {
25
29
  try {
26
30
  const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
27
31
  const parsed = JSON.parse(raw);
28
- cachedConfig = { ...DEFAULT_CONFIG, ...parsed };
29
- return { ...cachedConfig };
32
+ cachedConfig = Object.freeze({ ...DEFAULT_CONFIG, ...parsed });
33
+ return cachedConfig;
30
34
  } catch (err) {
31
35
  console.warn("Failed to read config file, falling back to defaults:", err.message);
32
36
  }
33
37
  }
34
38
 
35
- cachedConfig = { ...DEFAULT_CONFIG };
39
+ cachedConfig = Object.freeze({ ...DEFAULT_CONFIG });
36
40
  saveConfig(cachedConfig);
37
- return { ...cachedConfig };
41
+ return cachedConfig;
38
42
  }
39
43
 
40
44
  export function saveConfig(newConfig) {
41
45
  ensureDir();
42
- cachedConfig = { ...DEFAULT_CONFIG, ...newConfig };
46
+ cachedConfig = Object.freeze({ ...DEFAULT_CONFIG, ...newConfig });
43
47
  try {
44
48
  fs.writeFileSync(CONFIG_FILE, JSON.stringify(cachedConfig, null, 2), "utf-8");
45
49
  } catch (err) {
46
50
  console.error("Failed to write config file:", err.message);
47
51
  }
48
- return { ...cachedConfig };
52
+ return cachedConfig;
49
53
  }
50
54
 
51
55
  export function updateConfig(partialConfig) {
@@ -1,43 +1,43 @@
1
- import { DatabaseSync } from "node:sqlite";
2
- import { join } from "path";
3
- import { existsSync, mkdirSync } from "fs";
4
- import { MEMORY_DIR, ensureDir } from "../memory.js";
5
- import { runMigrations } from "./migrations.js";
6
-
7
- let dbInstance = null;
8
-
9
- export const STORAGE_DIR = join(MEMORY_DIR, "storage");
10
- export const BLOBS_DIR = join(STORAGE_DIR, "blobs");
11
- export const MODELS_DIR = join(STORAGE_DIR, "models");
12
- export const DB_PATH = join(STORAGE_DIR, "memory.sqlite");
13
-
14
- export function getDatabase(customPath = null) {
15
- if (dbInstance && !customPath) {
16
- return dbInstance;
17
- }
18
-
19
- const dbPath = customPath || DB_PATH;
20
- const parentDir = join(dbPath, "..");
21
- if (!existsSync(parentDir)) {
22
- mkdirSync(parentDir, { recursive: true });
23
- }
24
-
25
- const db = new DatabaseSync(dbPath);
26
- db.exec("PRAGMA foreign_keys = ON;");
27
- db.exec("PRAGMA journal_mode = WAL;");
28
-
29
- runMigrations(db);
30
-
31
- if (!customPath) {
32
- dbInstance = db;
33
- }
34
-
35
- return db;
36
- }
37
-
38
- export function closeDatabase() {
39
- if (dbInstance) {
40
- dbInstance.close();
41
- dbInstance = null;
42
- }
43
- }
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { join } from "path";
3
+ import { existsSync, mkdirSync } from "fs";
4
+ import { MEMORY_DIR, ensureDir } from "../memory.js";
5
+ import { runMigrations } from "./migrations.js";
6
+
7
+ let dbInstance = null;
8
+
9
+ export const STORAGE_DIR = join(MEMORY_DIR, "storage");
10
+ export const BLOBS_DIR = join(STORAGE_DIR, "blobs");
11
+ export const MODELS_DIR = join(STORAGE_DIR, "models");
12
+ export const DB_PATH = join(STORAGE_DIR, "memory.sqlite");
13
+
14
+ export function getDatabase(customPath = null) {
15
+ if (dbInstance && !customPath) {
16
+ return dbInstance;
17
+ }
18
+
19
+ const dbPath = customPath || DB_PATH;
20
+ const parentDir = join(dbPath, "..");
21
+ if (!existsSync(parentDir)) {
22
+ mkdirSync(parentDir, { recursive: true });
23
+ }
24
+
25
+ const db = new DatabaseSync(dbPath);
26
+ db.exec("PRAGMA foreign_keys = ON;");
27
+ db.exec("PRAGMA journal_mode = WAL;");
28
+
29
+ runMigrations(db);
30
+
31
+ if (!customPath) {
32
+ dbInstance = db;
33
+ }
34
+
35
+ return db;
36
+ }
37
+
38
+ export function closeDatabase() {
39
+ if (dbInstance) {
40
+ dbInstance.close();
41
+ dbInstance = null;
42
+ }
43
+ }
@@ -1,72 +1,72 @@
1
- export function extractSymbolsFromContent(content) {
2
- const symbols = new Set();
3
-
4
- const jsTsRegex = /(?:function|class|interface|type|enum|const|let|var)\s+([a-zA-Z0-9_$]+)/g;
5
- let match;
6
- while ((match = jsTsRegex.exec(content)) !== null) {
7
- const symbol = match[1];
8
- if (symbol.length > 2 && !["const", "let", "var", "function", "class", "import", "export", "from", "return", "if", "for", "while"].includes(symbol)) {
9
- symbols.add(symbol);
10
- }
11
- }
12
-
13
- const pyRegex = /(?:def|class)\s+([a-zA-Z0-9_]+)/g;
14
- while ((match = pyRegex.exec(content)) !== null) {
15
- const symbol = match[1];
16
- if (symbol.length > 2 && !["def", "class", "self", "return", "import", "from"].includes(symbol)) {
17
- symbols.add(symbol);
18
- }
19
- }
20
-
21
- return Array.from(symbols);
22
- }
23
-
24
- export function buildGraphEdges(docId, hierarchy) {
25
- const edges = [];
26
-
27
- for (const sec of hierarchy.sections) {
28
- edges.push({
29
- source_id: docId,
30
- target_id: sec.id,
31
- relation_type: "CONTAINS",
32
- });
33
-
34
- const symbols = extractSymbolsFromContent(sec.content);
35
- for (const sym of symbols) {
36
- edges.push({
37
- source_id: sec.id,
38
- target_id: `symbol:${sym}`,
39
- relation_type: "DEFINES_SYMBOL",
40
- });
41
- }
42
- }
43
-
44
- for (const micro of hierarchy.microChunks) {
45
- edges.push({
46
- source_id: micro.section_id,
47
- target_id: micro.id,
48
- relation_type: "CONTAINS",
49
- });
50
- }
51
-
52
- return edges;
53
- }
54
-
55
- export function saveGraphEdges(db, edges) {
56
- const stmt = db.prepare(`
57
- INSERT OR IGNORE INTO graph_edges (source_id, target_id, relation_type)
58
- VALUES (?, ?, ?);
59
- `);
60
- for (const edge of edges) {
61
- stmt.run(edge.source_id, edge.target_id, edge.relation_type);
62
- }
63
- }
64
-
65
- export function getRelatedSymbols(db, sectionId) {
66
- const stmt = db.prepare(`
67
- SELECT target_id, relation_type FROM graph_edges
68
- WHERE source_id = ? AND relation_type = 'DEFINES_SYMBOL';
69
- `);
70
- const rows = stmt.all(sectionId);
71
- return rows.map((r) => r.target_id.replace("symbol:", ""));
72
- }
1
+ export function extractSymbolsFromContent(content) {
2
+ const symbols = new Set();
3
+
4
+ const jsTsRegex = /(?:function|class|interface|type|enum|const|let|var)\s+([a-zA-Z0-9_$]+)/g;
5
+ let match;
6
+ while ((match = jsTsRegex.exec(content)) !== null) {
7
+ const symbol = match[1];
8
+ if (symbol.length > 2 && !["const", "let", "var", "function", "class", "import", "export", "from", "return", "if", "for", "while"].includes(symbol)) {
9
+ symbols.add(symbol);
10
+ }
11
+ }
12
+
13
+ const pyRegex = /(?:def|class)\s+([a-zA-Z0-9_]+)/g;
14
+ while ((match = pyRegex.exec(content)) !== null) {
15
+ const symbol = match[1];
16
+ if (symbol.length > 2 && !["def", "class", "self", "return", "import", "from"].includes(symbol)) {
17
+ symbols.add(symbol);
18
+ }
19
+ }
20
+
21
+ return Array.from(symbols);
22
+ }
23
+
24
+ export function buildGraphEdges(docId, hierarchy) {
25
+ const edges = [];
26
+
27
+ for (const sec of hierarchy.sections) {
28
+ edges.push({
29
+ source_id: docId,
30
+ target_id: sec.id,
31
+ relation_type: "CONTAINS",
32
+ });
33
+
34
+ const symbols = extractSymbolsFromContent(sec.content);
35
+ for (const sym of symbols) {
36
+ edges.push({
37
+ source_id: sec.id,
38
+ target_id: `symbol:${sym}`,
39
+ relation_type: "DEFINES_SYMBOL",
40
+ });
41
+ }
42
+ }
43
+
44
+ for (const micro of hierarchy.microChunks) {
45
+ edges.push({
46
+ source_id: micro.section_id,
47
+ target_id: micro.id,
48
+ relation_type: "CONTAINS",
49
+ });
50
+ }
51
+
52
+ return edges;
53
+ }
54
+
55
+ export function saveGraphEdges(db, edges) {
56
+ const stmt = db.prepare(`
57
+ INSERT OR IGNORE INTO graph_edges (source_id, target_id, relation_type)
58
+ VALUES (?, ?, ?);
59
+ `);
60
+ for (const edge of edges) {
61
+ stmt.run(edge.source_id, edge.target_id, edge.relation_type);
62
+ }
63
+ }
64
+
65
+ export function getRelatedSymbols(db, sectionId) {
66
+ const stmt = db.prepare(`
67
+ SELECT target_id, relation_type FROM graph_edges
68
+ WHERE source_id = ? AND relation_type = 'DEFINES_SYMBOL';
69
+ `);
70
+ const rows = stmt.all(sectionId);
71
+ return rows.map((r) => r.target_id.replace("symbol:", ""));
72
+ }
@@ -1,102 +1,102 @@
1
- import { getDatabase } from "../db/database.js";
2
- import { randomUUID } from "node:crypto";
3
-
4
- export function linkFactToDocument({
5
- factKey,
6
- factText,
7
- docId,
8
- sectionId = null,
9
- startLine = null,
10
- endLine = null,
11
- relationType = "LINKS_TO",
12
- metadata = null,
13
- }) {
14
- const db = getDatabase();
15
- const id = `link_${randomUUID().substring(0, 12)}`;
16
- const now = Date.now();
17
-
18
- const doc = db
19
- .prepare("SELECT id, title, path FROM documents WHERE id = ? OR path = ? OR title = ?")
20
- .get(docId, docId, docId);
21
-
22
- if (!doc) {
23
- throw new Error(`Target document not found in knowledge base for link: ${docId}`);
24
- }
25
-
26
- const stmt = db.prepare(`
27
- INSERT INTO knowledge_links (id, fact_key, fact_text, doc_id, section_id, start_line, end_line, relation_type, metadata_json, created_at)
28
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
29
- `);
30
-
31
- stmt.run(
32
- id,
33
- factKey,
34
- factText,
35
- doc.id,
36
- sectionId || null,
37
- startLine ? Number(startLine) : null,
38
- endLine ? Number(endLine) : null,
39
- relationType || "LINKS_TO",
40
- metadata ? JSON.stringify(metadata) : null,
41
- now
42
- );
43
-
44
- const edgeStmt = db.prepare(`
45
- INSERT OR IGNORE INTO graph_edges (source_id, target_id, relation_type, metadata_json, created_at)
46
- VALUES (?, ?, ?, ?, ?)
47
- `);
48
- const targetSpec = startLine ? `${doc.id}:L${startLine}-${endLine || startLine}` : doc.id;
49
- edgeStmt.run(`fact:${factKey}:${factText.substring(0, 30)}`, targetSpec, relationType, JSON.stringify({ linkId: id }), now);
50
-
51
- return {
52
- linkId: id,
53
- factKey,
54
- factText,
55
- docId: doc.id,
56
- docTitle: doc.title || doc.path,
57
- startLine,
58
- endLine,
59
- relationType,
60
- };
61
- }
62
-
63
- export function getLinksForFact(factKey, factText) {
64
- const db = getDatabase();
65
- const stmt = db.prepare(`
66
- SELECT k.*, d.title as doc_title, d.path as doc_path
67
- FROM knowledge_links k
68
- JOIN documents d ON k.doc_id = d.id
69
- WHERE k.fact_key = ? AND (k.fact_text LIKE ? OR ? LIKE '%' || k.fact_text || '%')
70
- ORDER BY k.created_at DESC
71
- `);
72
- const queryPattern = `%${factText.substring(0, 20)}%`;
73
- return stmt.all(factKey, queryPattern, factText);
74
- }
75
-
76
- export function getLinksForDoc(docId) {
77
- const db = getDatabase();
78
- const stmt = db.prepare(`
79
- SELECT k.*, d.title as doc_title, d.path as doc_path
80
- FROM knowledge_links k
81
- JOIN documents d ON k.doc_id = d.id
82
- WHERE k.doc_id = ? OR d.path = ? OR d.title = ?
83
- ORDER BY k.created_at DESC
84
- `);
85
- return stmt.all(docId, docId, docId);
86
- }
87
-
88
- export function listAllLinks(factKey = null) {
89
- const db = getDatabase();
90
- let sql = `
91
- SELECT k.*, d.title as doc_title, d.path as doc_path
92
- FROM knowledge_links k
93
- JOIN documents d ON k.doc_id = d.id
94
- `;
95
- const params = [];
96
- if (factKey) {
97
- sql += " WHERE k.fact_key = ?";
98
- params.push(factKey);
99
- }
100
- sql += " ORDER BY k.created_at DESC";
101
- return db.prepare(sql).all(...params);
102
- }
1
+ import { getDatabase } from "../db/database.js";
2
+ import { randomUUID } from "node:crypto";
3
+
4
+ export function linkFactToDocument({
5
+ factKey,
6
+ factText,
7
+ docId,
8
+ sectionId = null,
9
+ startLine = null,
10
+ endLine = null,
11
+ relationType = "LINKS_TO",
12
+ metadata = null,
13
+ }) {
14
+ const db = getDatabase();
15
+ const id = `link_${randomUUID().substring(0, 12)}`;
16
+ const now = Date.now();
17
+
18
+ const doc = db
19
+ .prepare("SELECT id, title, path FROM documents WHERE id = ? OR path = ? OR title = ?")
20
+ .get(docId, docId, docId);
21
+
22
+ if (!doc) {
23
+ throw new Error(`Target document not found in knowledge base for link: ${docId}`);
24
+ }
25
+
26
+ const stmt = db.prepare(`
27
+ INSERT INTO knowledge_links (id, fact_key, fact_text, doc_id, section_id, start_line, end_line, relation_type, metadata_json, created_at)
28
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
29
+ `);
30
+
31
+ stmt.run(
32
+ id,
33
+ factKey,
34
+ factText,
35
+ doc.id,
36
+ sectionId || null,
37
+ startLine ? Number(startLine) : null,
38
+ endLine ? Number(endLine) : null,
39
+ relationType || "LINKS_TO",
40
+ metadata ? JSON.stringify(metadata) : null,
41
+ now
42
+ );
43
+
44
+ const edgeStmt = db.prepare(`
45
+ INSERT OR IGNORE INTO graph_edges (source_id, target_id, relation_type, metadata_json, created_at)
46
+ VALUES (?, ?, ?, ?, ?)
47
+ `);
48
+ const targetSpec = startLine ? `${doc.id}:L${startLine}-${endLine || startLine}` : doc.id;
49
+ edgeStmt.run(`fact:${factKey}:${factText.substring(0, 30)}`, targetSpec, relationType, JSON.stringify({ linkId: id }), now);
50
+
51
+ return {
52
+ linkId: id,
53
+ factKey,
54
+ factText,
55
+ docId: doc.id,
56
+ docTitle: doc.title || doc.path,
57
+ startLine,
58
+ endLine,
59
+ relationType,
60
+ };
61
+ }
62
+
63
+ export function getLinksForFact(factKey, factText) {
64
+ const db = getDatabase();
65
+ const stmt = db.prepare(`
66
+ SELECT k.*, d.title as doc_title, d.path as doc_path
67
+ FROM knowledge_links k
68
+ JOIN documents d ON k.doc_id = d.id
69
+ WHERE k.fact_key = ? AND (k.fact_text LIKE ? OR ? LIKE '%' || k.fact_text || '%')
70
+ ORDER BY k.created_at DESC
71
+ `);
72
+ const queryPattern = `%${factText.substring(0, 20)}%`;
73
+ return stmt.all(factKey, queryPattern, factText);
74
+ }
75
+
76
+ export function getLinksForDoc(docId) {
77
+ const db = getDatabase();
78
+ const stmt = db.prepare(`
79
+ SELECT k.*, d.title as doc_title, d.path as doc_path
80
+ FROM knowledge_links k
81
+ JOIN documents d ON k.doc_id = d.id
82
+ WHERE k.doc_id = ? OR d.path = ? OR d.title = ?
83
+ ORDER BY k.created_at DESC
84
+ `);
85
+ return stmt.all(docId, docId, docId);
86
+ }
87
+
88
+ export function listAllLinks(factKey = null) {
89
+ const db = getDatabase();
90
+ let sql = `
91
+ SELECT k.*, d.title as doc_title, d.path as doc_path
92
+ FROM knowledge_links k
93
+ JOIN documents d ON k.doc_id = d.id
94
+ `;
95
+ const params = [];
96
+ if (factKey) {
97
+ sql += " WHERE k.fact_key = ?";
98
+ params.push(factKey);
99
+ }
100
+ sql += " ORDER BY k.created_at DESC";
101
+ return db.prepare(sql).all(...params);
102
+ }