Skip to content

Commit b90f6b2

Browse files
author
X
committed
-
1 parent 9443e3c commit b90f6b2

File tree

11 files changed

+2978
-5
lines changed

11 files changed

+2978
-5
lines changed

capabilities/cognition/cognition-api.js

Lines changed: 461 additions & 0 deletions
Large diffs are not rendered by default.

capabilities/cognition/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @fileoverview Cognition Module Index
3+
* Exports all cognition-related modules for registration.
4+
*/
5+
6+
export { default as EmbeddingStore } from './semantic/embedding-store.js';
7+
export { default as SemanticMemory } from './semantic/semantic-memory.js';
8+
export { default as KnowledgeGraph } from './symbolic/knowledge-graph.js';
9+
export { default as RuleEngine } from './symbolic/rule-engine.js';
10+
export { default as SymbolGrounder } from './symbolic/symbol-grounder.js';
11+
export { default as CognitionAPI } from './cognition-api.js';
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
/**
2+
* @fileoverview Embedding Store
3+
* IndexedDB-backed storage for semantic memory embeddings.
4+
* Provides vector storage, similarity search, and LRU pruning.
5+
*/
6+
7+
const EmbeddingStore = {
8+
metadata: {
9+
id: 'EmbeddingStore',
10+
version: '1.0.0',
11+
dependencies: ['Utils'],
12+
async: true,
13+
type: 'service'
14+
},
15+
16+
factory: (deps) => {
17+
const { Utils } = deps;
18+
const { logger, generateId, Errors } = Utils;
19+
20+
const DB_NAME = 'reploid-semantic-v1';
21+
const STORE_MEMORIES = 'memories';
22+
const STORE_VOCAB = 'vocabulary';
23+
const MAX_MEMORIES = 10000;
24+
25+
let db = null;
26+
27+
// --- Database Setup ---
28+
29+
const openDB = () => {
30+
return new Promise((resolve, reject) => {
31+
if (db) return resolve(db);
32+
33+
const request = indexedDB.open(DB_NAME, 1);
34+
35+
request.onupgradeneeded = (event) => {
36+
const d = event.target.result;
37+
38+
// Memories store
39+
if (!d.objectStoreNames.contains(STORE_MEMORIES)) {
40+
const memStore = d.createObjectStore(STORE_MEMORIES, { keyPath: 'id' });
41+
memStore.createIndex('timestamp', 'timestamp', { unique: false });
42+
memStore.createIndex('domain', 'domain', { unique: false });
43+
memStore.createIndex('accessCount', 'accessCount', { unique: false });
44+
}
45+
46+
// Vocabulary store
47+
if (!d.objectStoreNames.contains(STORE_VOCAB)) {
48+
const vocabStore = d.createObjectStore(STORE_VOCAB, { keyPath: 'token' });
49+
vocabStore.createIndex('frequency', 'frequency', { unique: false });
50+
vocabStore.createIndex('lastSeen', 'lastSeen', { unique: false });
51+
}
52+
};
53+
54+
request.onsuccess = (e) => {
55+
db = e.target.result;
56+
logger.info('[EmbeddingStore] Database connected');
57+
resolve(db);
58+
};
59+
60+
request.onerror = () => {
61+
reject(new Errors.StateError('Failed to open EmbeddingStore DB'));
62+
};
63+
});
64+
};
65+
66+
const init = async () => {
67+
await openDB();
68+
return true;
69+
};
70+
71+
// --- Memory Operations ---
72+
73+
const addMemory = async (memory) => {
74+
await openDB();
75+
const id = memory.id || generateId('mem');
76+
77+
const entry = {
78+
id,
79+
content: memory.content,
80+
embedding: memory.embedding, // Float32Array or array
81+
domain: memory.domain || 'general',
82+
timestamp: Date.now(),
83+
accessCount: 0,
84+
source: memory.source || 'assistant',
85+
metadata: memory.metadata || {}
86+
};
87+
88+
return new Promise((resolve, reject) => {
89+
const tx = db.transaction([STORE_MEMORIES], 'readwrite');
90+
const store = tx.objectStore(STORE_MEMORIES);
91+
store.put(entry).onsuccess = () => {
92+
logger.debug(`[EmbeddingStore] Added memory: ${id}`);
93+
resolve(id);
94+
};
95+
tx.onerror = () => reject(new Errors.ArtifactError(`Failed to add memory: ${id}`));
96+
});
97+
};
98+
99+
const getMemory = async (id) => {
100+
await openDB();
101+
return new Promise((resolve, reject) => {
102+
const tx = db.transaction([STORE_MEMORIES], 'readonly');
103+
const req = tx.objectStore(STORE_MEMORIES).get(id);
104+
req.onsuccess = () => resolve(req.result || null);
105+
req.onerror = () => reject(new Errors.ArtifactError(`Failed to get memory: ${id}`));
106+
});
107+
};
108+
109+
const getAllMemories = async () => {
110+
await openDB();
111+
return new Promise((resolve, reject) => {
112+
const tx = db.transaction([STORE_MEMORIES], 'readonly');
113+
const req = tx.objectStore(STORE_MEMORIES).getAll();
114+
req.onsuccess = () => resolve(req.result || []);
115+
req.onerror = () => reject(new Errors.ArtifactError('Failed to get all memories'));
116+
});
117+
};
118+
119+
const updateAccessCount = async (id) => {
120+
await openDB();
121+
const memory = await getMemory(id);
122+
if (!memory) return;
123+
124+
memory.accessCount = (memory.accessCount || 0) + 1;
125+
126+
return new Promise((resolve, reject) => {
127+
const tx = db.transaction([STORE_MEMORIES], 'readwrite');
128+
const store = tx.objectStore(STORE_MEMORIES);
129+
store.put(memory).onsuccess = () => resolve(true);
130+
tx.onerror = () => reject(new Errors.ArtifactError(`Failed to update access count: ${id}`));
131+
});
132+
};
133+
134+
const deleteMemory = async (id) => {
135+
await openDB();
136+
return new Promise((resolve, reject) => {
137+
const tx = db.transaction([STORE_MEMORIES], 'readwrite');
138+
const req = tx.objectStore(STORE_MEMORIES).delete(id);
139+
req.onsuccess = () => {
140+
logger.debug(`[EmbeddingStore] Deleted memory: ${id}`);
141+
resolve(true);
142+
};
143+
req.onerror = () => reject(new Errors.ArtifactError(`Failed to delete memory: ${id}`));
144+
});
145+
};
146+
147+
// --- Similarity Search ---
148+
149+
const cosineSimilarity = (a, b) => {
150+
if (!a || !b || a.length !== b.length) return 0;
151+
152+
let dotProduct = 0;
153+
let normA = 0;
154+
let normB = 0;
155+
156+
for (let i = 0; i < a.length; i++) {
157+
dotProduct += a[i] * b[i];
158+
normA += a[i] * a[i];
159+
normB += b[i] * b[i];
160+
}
161+
162+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
163+
return magnitude === 0 ? 0 : dotProduct / magnitude;
164+
};
165+
166+
const searchSimilar = async (queryEmbedding, topK = 5, minSimilarity = 0.5) => {
167+
const memories = await getAllMemories();
168+
const queryArray = Array.isArray(queryEmbedding)
169+
? queryEmbedding
170+
: Array.from(queryEmbedding);
171+
172+
const scored = memories
173+
.filter(m => m.embedding && m.embedding.length > 0)
174+
.map(m => {
175+
const embArray = Array.isArray(m.embedding)
176+
? m.embedding
177+
: Array.from(m.embedding);
178+
const similarity = cosineSimilarity(queryArray, embArray);
179+
return { memory: m, similarity };
180+
})
181+
.filter(item => item.similarity >= minSimilarity)
182+
.sort((a, b) => b.similarity - a.similarity)
183+
.slice(0, topK);
184+
185+
// Update access counts for returned memories
186+
for (const item of scored) {
187+
await updateAccessCount(item.memory.id);
188+
}
189+
190+
return scored;
191+
};
192+
193+
// --- Vocabulary Operations ---
194+
195+
const updateVocabulary = async (tokens) => {
196+
await openDB();
197+
const now = Date.now();
198+
199+
return new Promise((resolve, reject) => {
200+
const tx = db.transaction([STORE_VOCAB], 'readwrite');
201+
const store = tx.objectStore(STORE_VOCAB);
202+
203+
let completed = 0;
204+
const total = tokens.length;
205+
206+
for (const token of tokens) {
207+
const getReq = store.get(token);
208+
getReq.onsuccess = () => {
209+
const existing = getReq.result;
210+
const entry = existing || { token, frequency: 0, domains: [] };
211+
entry.frequency += 1;
212+
entry.lastSeen = now;
213+
store.put(entry);
214+
completed++;
215+
if (completed === total) resolve(true);
216+
};
217+
}
218+
219+
if (total === 0) resolve(true);
220+
tx.onerror = () => reject(new Errors.ArtifactError('Failed to update vocabulary'));
221+
});
222+
};
223+
224+
const getVocabulary = async () => {
225+
await openDB();
226+
return new Promise((resolve, reject) => {
227+
const tx = db.transaction([STORE_VOCAB], 'readonly');
228+
const req = tx.objectStore(STORE_VOCAB).getAll();
229+
req.onsuccess = () => resolve(req.result || []);
230+
req.onerror = () => reject(new Errors.ArtifactError('Failed to get vocabulary'));
231+
});
232+
};
233+
234+
// --- Maintenance ---
235+
236+
const pruneOldMemories = async (maxAge = 7 * 24 * 60 * 60 * 1000) => {
237+
const memories = await getAllMemories();
238+
const now = Date.now();
239+
const cutoff = now - maxAge;
240+
241+
// Sort by accessCount (LRU), then timestamp
242+
const candidates = memories
243+
.filter(m => m.timestamp < cutoff)
244+
.sort((a, b) => a.accessCount - b.accessCount || a.timestamp - b.timestamp);
245+
246+
// Delete excess memories
247+
const toDelete = candidates.slice(0, Math.max(0, memories.length - MAX_MEMORIES));
248+
let deleted = 0;
249+
250+
for (const memory of toDelete) {
251+
await deleteMemory(memory.id);
252+
deleted++;
253+
}
254+
255+
if (deleted > 0) {
256+
logger.info(`[EmbeddingStore] Pruned ${deleted} old memories`);
257+
}
258+
259+
return deleted;
260+
};
261+
262+
const getStats = async () => {
263+
const memories = await getAllMemories();
264+
const vocab = await getVocabulary();
265+
266+
return {
267+
memoryCount: memories.length,
268+
vocabularySize: vocab.length,
269+
maxMemories: MAX_MEMORIES,
270+
oldestMemory: memories.length > 0
271+
? Math.min(...memories.map(m => m.timestamp))
272+
: null
273+
};
274+
};
275+
276+
const clear = async () => {
277+
await openDB();
278+
return new Promise((resolve, reject) => {
279+
const tx = db.transaction([STORE_MEMORIES, STORE_VOCAB], 'readwrite');
280+
tx.objectStore(STORE_MEMORIES).clear();
281+
tx.objectStore(STORE_VOCAB).clear();
282+
tx.oncomplete = () => {
283+
logger.info('[EmbeddingStore] Cleared all data');
284+
resolve(true);
285+
};
286+
tx.onerror = () => reject(new Errors.ArtifactError('Failed to clear store'));
287+
});
288+
};
289+
290+
return {
291+
init,
292+
addMemory,
293+
getMemory,
294+
getAllMemories,
295+
deleteMemory,
296+
searchSimilar,
297+
updateVocabulary,
298+
getVocabulary,
299+
pruneOldMemories,
300+
getStats,
301+
clear
302+
};
303+
}
304+
};
305+
306+
export default EmbeddingStore;

0 commit comments

Comments
 (0)