Lumi/plugins/lumi_ai/backend/cache.js
2026-06-12 19:27:43 +02:00

71 lines
2.0 KiB
JavaScript

const fs = require("fs");
const crypto = require("crypto");
const { resolveData } = require("./paths");
class SafeAnswerCache {
constructor(getConfig) {
this.getConfig = getConfig;
this.file = resolveData("cache", "gate-answers.json");
}
key({ message, role, platform }) {
const normalized = String(message || "").toLowerCase().replace(/\s+/g, " ").trim();
return crypto.createHash("sha256")
.update(`gate-cache-v2\n${role || "user"}\n${platform || "webui"}\n${normalized}`)
.digest("hex");
}
get(input) {
const entry = this.read().entries[this.key(input)];
if (!entry) return null;
if (new Date(entry.expires_at).getTime() <= Date.now()) {
this.delete(input);
return null;
}
return entry;
}
set(input, answer) {
if (!answer?.text || answer.safe !== true) return null;
const ttlSeconds = Math.max(30, Number(this.getConfig()?.gate?.cache_ttl_seconds) || 3600);
const store = this.read();
const key = this.key(input);
store.entries[key] = {
text: String(answer.text),
links: Array.isArray(answer.links) ? answer.links.slice(0, 8) : [],
source: answer.source || null,
created_at: new Date().toISOString(),
expires_at: new Date(Date.now() + ttlSeconds * 1000).toISOString()
};
this.write(store);
return store.entries[key];
}
delete(input) {
const store = this.read();
delete store.entries[this.key(input)];
this.write(store);
}
read() {
try {
const parsed = JSON.parse(fs.readFileSync(this.file, "utf8"));
return { entries: parsed.entries || {} };
} catch {
return { entries: {} };
}
}
write(store) {
const tmp = `${this.file}.${process.pid}.${crypto.randomBytes(4).toString("hex")}.tmp`;
try {
fs.writeFileSync(tmp, `${JSON.stringify(store, null, 2)}\n`);
fs.renameSync(tmp, this.file);
} finally {
fs.rmSync(tmp, { force: true });
}
}
}
module.exports = { SafeAnswerCache };