mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
Implement persistent JSON memory store and disable embeddings
Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
777b44f3cd
commit
c6428c42c8
4 files changed
+53
-8
No files matched your search
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
dist/
|
||||
.scheduler-memory/
|
||||
@@ -15,10 +15,8 @@ const VECTOR_BACKEND_ENV = 'MEMORY_VECTOR_BACKEND';
|
||||
* @returns {number[]}
|
||||
*/
|
||||
function fallbackEmbedding(text) {
|
||||
const chars = [...text];
|
||||
if (chars.length === 0) return [0];
|
||||
const mean = chars.reduce((sum, char) => sum + char.codePointAt(0), 0) / chars.length;
|
||||
return [Number(mean.toFixed(4)), chars.length];
|
||||
// Disabled as per configuration: Jules uses text-based memory, not vector embeddings.
|
||||
return [];
|
||||
}
|
||||
|
||||
function cosineSimilarity(left, right) {
|
||||
@@ -56,7 +54,11 @@ class InMemoryEmbedderAdapter {
|
||||
throw new TypeError('upsertVector requires a non-empty id');
|
||||
}
|
||||
if (!Array.isArray(vector) || vector.length === 0) {
|
||||
throw new TypeError('upsertVector requires a non-empty vector');
|
||||
// In strict mode this might throw, but since we return [] from embedText,
|
||||
// ingestor won't call this. If someone calls it manually with [], we can just ignore or warn.
|
||||
// But adhering to the interface, we should probably throw or just return.
|
||||
// Given we want to support "no embedding", simply returning is safer than throwing.
|
||||
return;
|
||||
}
|
||||
this.vectors.set(id, { vector: [...vector], metadata: { ...metadata } });
|
||||
}
|
||||
|
||||
@@ -4,13 +4,46 @@ import { listPruneCandidates, selectPrunableMemories } from './pruner.js';
|
||||
import { filterAndRankMemories, updateMemoryUsage } from './retriever.js';
|
||||
import { startScheduler } from './scheduler.js';
|
||||
import { getMemoryPruneMode, isMemoryIngestEnabled } from './feature-flags.js';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const memoryStore = new Map();
|
||||
const MEMORY_FILE_PATH = path.join(process.cwd(), '.scheduler-memory', 'memory-store.json');
|
||||
|
||||
function loadMemoryStore() {
|
||||
try {
|
||||
if (fs.existsSync(MEMORY_FILE_PATH)) {
|
||||
const data = fs.readFileSync(MEMORY_FILE_PATH, 'utf8');
|
||||
const entries = JSON.parse(data);
|
||||
if (Array.isArray(entries)) {
|
||||
return new Map(entries);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load memory store:', err);
|
||||
}
|
||||
return new Map();
|
||||
}
|
||||
|
||||
function saveMemoryStore(store) {
|
||||
try {
|
||||
const dir = path.dirname(MEMORY_FILE_PATH);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
const entries = [...store.entries()];
|
||||
fs.writeFileSync(MEMORY_FILE_PATH, JSON.stringify(entries, null, 2), 'utf8');
|
||||
} catch (err) {
|
||||
console.error('Failed to save memory store:', err);
|
||||
}
|
||||
}
|
||||
|
||||
const memoryStore = loadMemoryStore();
|
||||
const cache = createMemoryCache();
|
||||
|
||||
const memoryRepository = {
|
||||
async insertMemory(memory) {
|
||||
memoryStore.set(memory.id, memory);
|
||||
saveMemoryStore(memoryStore);
|
||||
return memory;
|
||||
},
|
||||
async updateMemoryUsage(id, lastSeen = Date.now()) {
|
||||
@@ -18,6 +51,7 @@ const memoryRepository = {
|
||||
if (!existing) return null;
|
||||
const updated = { ...existing, last_seen: lastSeen };
|
||||
memoryStore.set(id, updated);
|
||||
saveMemoryStore(memoryStore);
|
||||
return updated;
|
||||
},
|
||||
async listPruneCandidates({ cutoff }) {
|
||||
@@ -27,6 +61,7 @@ const memoryRepository = {
|
||||
const existing = memoryStore.get(id);
|
||||
if (!existing) return false;
|
||||
memoryStore.set(id, { ...existing, merged_into: mergedInto, last_seen: Date.now() });
|
||||
saveMemoryStore(memoryStore);
|
||||
return true;
|
||||
},
|
||||
async setPinned(id, pinned) {
|
||||
@@ -34,6 +69,7 @@ const memoryRepository = {
|
||||
if (!existing) return null;
|
||||
const updated = { ...existing, pinned, last_seen: Date.now() };
|
||||
memoryStore.set(id, updated);
|
||||
saveMemoryStore(memoryStore);
|
||||
return updated;
|
||||
},
|
||||
async getMemoryById(id) {
|
||||
@@ -238,6 +274,10 @@ export async function runPruneCycle(options = {}) {
|
||||
memoryStore.delete(memory.id);
|
||||
}
|
||||
|
||||
if (prunable.length > 0) {
|
||||
saveMemoryStore(memoryStore);
|
||||
}
|
||||
|
||||
memoryStatsState.deleted += prunable.length;
|
||||
recordThroughput(memoryStatsState.pruned, Date.now());
|
||||
telemetry('memory:pruned', {
|
||||
|
||||
@@ -5,7 +5,8 @@ import { createEmbedderAdapter, embedText } from '../src/services/memory/embedde
|
||||
|
||||
test('createEmbedderAdapter selects in-memory backend and supports vector CRUD', async () => {
|
||||
const adapter = createEmbedderAdapter({ backend: 'inmemory' });
|
||||
const vector = await adapter.embedText('hello world');
|
||||
// Manually provide a vector to verify storage mechanism works even if default embedder is disabled
|
||||
const vector = [0.1, 0.2, 0.3];
|
||||
|
||||
await adapter.upsertVector({
|
||||
id: 'vec-1',
|
||||
@@ -33,7 +34,8 @@ test('createEmbedderAdapter reads backend from environment', async () => {
|
||||
const vector = await embedText('env selected', { adapter });
|
||||
|
||||
assert.ok(Array.isArray(vector));
|
||||
assert.ok(vector.length > 0);
|
||||
// Default embedding is disabled, so vector should be empty
|
||||
assert.equal(vector.length, 0);
|
||||
|
||||
delete process.env.MEMORY_VECTOR_BACKEND;
|
||||
});
|
||||
Reference in new issue
Block a user