Spaces:
Running
Running
| // core/memory.js — persistent USER MEMORY (LibreChat memory semantics, substrate-native). | |
| // Memories are key/value facts (key validated /^[a-z_]+$/, value a complete sentence) stored as | |
| // κ-objects with pointers in the boot index. They inject into the system block as the | |
| // "# Existing memory:" section, and the model maintains them itself through two LOCAL tools | |
| // (set_memory / delete_memory) armed alongside MCP tools in the agentic loop — no server, | |
| // every write content-addressed and verifiable. | |
| const LC = { lc: "https://librechat.ai/ns#" }; | |
| export const KEY_RE = /^[a-z_]+$/; | |
| export function makeMemory(chatStore, { tokenLimit = 2000 } = {}) { | |
| const { store, getIndex, newId } = chatStore; | |
| const putIndex = async (idx) => { const b = new TextEncoder().encode(JSON.stringify(idx)); return store.backend.putRaw ? store.backend.putRaw("index:org.hologram.HoloQ", b) : store.backend.put("index:org.hologram.HoloQ", b); }; | |
| const tokensOf = (s) => Math.ceil((s || "").length / 4); // a fair, dependency-free estimate | |
| async function list() { return (await getIndex()).memories || []; } | |
| async function set(key, value) { | |
| key = String(key || "").toLowerCase().trim(); | |
| if (!KEY_RE.test(key)) return { ok: false, error: "invalid key — use lowercase letters and underscores" }; | |
| const idx = await getIndex(); | |
| idx.memories = idx.memories || []; | |
| const used = idx.memories.filter((m) => m.key !== key).reduce((n, m) => n + (m.tokenCount || 0), 0); | |
| const tc = tokensOf(value); | |
| if (used + tc > tokenLimit) return { ok: false, error: `memory full: ${used}+${tc} > ${tokenLimit} tokens` }; | |
| const obj = await store.makeObject({ | |
| type: ["schema:Statement", "prov:Entity"], context: [LC], | |
| "lc:key": key, "lc:value": String(value || ""), "lc:tokenCount": tc, | |
| "schema:dateModified": new Date().toISOString(), | |
| }); | |
| const i = idx.memories.findIndex((m) => m.key === key); | |
| const ptr = { key, kappa: obj.id, value: String(value || ""), tokenCount: tc, updated_at: new Date().toISOString() }; | |
| if (i >= 0) idx.memories[i] = ptr; else idx.memories.push(ptr); | |
| await putIndex(idx); | |
| return { ok: true, key, kappa: obj.id }; | |
| } | |
| async function remove(key) { | |
| const idx = await getIndex(); | |
| idx.memories = (idx.memories || []).filter((m) => m.key !== key); | |
| await putIndex(idx); | |
| return { ok: true }; | |
| } | |
| // The system-block injection ("# Existing memory:" — the LibreChat convention). | |
| async function injection() { | |
| const mems = await list(); | |
| if (!mems.length) return ""; | |
| const used = mems.reduce((n, m) => n + (m.tokenCount || 0), 0); | |
| return `# Memory Status:\nCurrent memory usage: ${used} tokens\nToken limit: ${tokenLimit} tokens\nRemaining capacity: ${Math.max(0, tokenLimit - used)} tokens\n\n# Existing memory:\n` + | |
| mems.map((m) => `- ${m.key}: ${m.value}`).join("\n"); | |
| } | |
| // The two LOCAL tools the agentic loop arms (same shape as MCP hub tools). | |
| function localTools() { | |
| return [ | |
| { | |
| def: { name: "set_memory", description: "Remember a fact about the user across conversations. Only when the user explicitly asks (\"remember that…\"). key: lowercase_with_underscores; value: one complete sentence.", inputSchema: { type: "object", properties: { key: { type: "string" }, value: { type: "string" } }, required: ["key", "value"] } }, | |
| serverName: "memory", call: async (a) => { const r = await set(a.key, a.value); return { text: JSON.stringify(r), isError: !r.ok }; }, | |
| }, | |
| { | |
| def: { name: "delete_memory", description: "Forget a remembered fact, by key. Only when the user explicitly asks.", inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] } }, | |
| serverName: "memory", call: async (a) => { const r = await remove(a.key); return { text: JSON.stringify(r), isError: false }; }, | |
| }, | |
| ]; | |
| } | |
| return { list, set, remove, injection, localTools, tokenLimit }; | |
| } | |