| |
| |
| |
| |
| |
| |
| |
| |
| 'use strict'; |
| const fs = require('fs'); |
| const path = require('path'); |
| const crypto = require('crypto'); |
|
|
| function relmemFile(cacheDir, selfDir, otherName) { |
| const h = crypto.createHash('sha1').update(path.resolve(selfDir)).digest('hex').slice(0, 10); |
| const o = otherName.toLowerCase().replace(/[^a-z0-9]/g, ''); |
| return path.join(cacheDir, `relmem-${h}-${o}.jsonl`); |
| } |
|
|
| |
| function saveTurns(cacheDir, selfDir, otherName, texts) { |
| const f = relmemFile(cacheDir, selfDir, otherName); |
| const lines = texts.filter(t => t && t.trim()).map(t => JSON.stringify({ t: t.trim() })); |
| if (lines.length) fs.appendFileSync(f, lines.join('\n') + '\n'); |
| return f; |
| } |
|
|
| function loadTurns(cacheDir, selfDir, otherName) { |
| const f = relmemFile(cacheDir, selfDir, otherName); |
| if (!fs.existsSync(f)) return []; |
| return fs.readFileSync(f, 'utf8').trim().split('\n').filter(Boolean).map(l => { try { return JSON.parse(l).t; } catch (_) { return null; } }).filter(Boolean); |
| } |
|
|
| function splitSentences(t) { |
| return (t.split(/(?<=[.!?β¦])\s+|\n+/).map(s => s.trim()).filter(s => s.split(/\s+/).length >= 4 && s.split(/\s+/).length <= 45)); |
| } |
|
|
| |
| |
| |
| async function injectInto(store, emb, texts, embedAsync, srcTag) { |
| const seen = new Set(); |
| const newFrags = []; |
| for (const t of texts) for (const sent of splitSentences(t)) { |
| const key = sent.toLowerCase().replace(/[^a-z0-9 ]/g, '').replace(/\s+/g, ' ').trim(); |
| if (seen.has(key)) continue; seen.add(key); |
| newFrags.push({ text: sent, posTag: 'body', sentenceInitial: true, tier: 0, src: srcTag || 'relmem', prompt: null, promptIdx: -1, nativePos: 0.5 }); |
| } |
| if (!newFrags.length) return { emb, injectedIdx: new Set() }; |
| const d = emb.d, n0 = emb.n, k = newFrags.length; |
| const vecs = []; |
| for (const f of newFrags) vecs.push(await embedAsync(f.text)); |
| const merged = new Float32Array((n0 + k) * d); |
| merged.set(emb.vectors, 0); |
| for (let i = 0; i < k; i++) { const v = vecs[i]; if (v) for (let j = 0; j < d; j++) merged[(n0 + i) * d + j] = v[j]; } |
| const injectedIdx = new Set(); |
| for (let i = 0; i < k; i++) { |
| store.fragments.push(newFrags[i]); |
| injectedIdx.add(n0 + i); |
| for (const w of (newFrags[i].text.toLowerCase().match(/[a-z0-9']+/g) || [])) store.vocab.add(w); |
| } |
| return { emb: { vectors: merged, n: n0 + k, d }, injectedIdx }; |
| } |
|
|
| module.exports = { relmemFile, saveTurns, loadTurns, injectInto }; |
|
|