@agentskit/memory 0.8.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-G5S2A3MJ.js +48 -0
- package/dist/chunk-G5S2A3MJ.js.map +1 -0
- package/dist/index.cjs +563 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +246 -32
- package/dist/index.d.ts +246 -32
- package/dist/index.js +548 -53
- package/dist/index.js.map +1 -1
- package/dist/personalization.cjs +44 -0
- package/dist/personalization.cjs.map +1 -0
- package/dist/personalization.d.cts +32 -0
- package/dist/personalization.d.ts +32 -0
- package/dist/personalization.js +3 -0
- package/dist/personalization.js.map +1 -0
- package/package.json +15 -9
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/personalization.ts
|
|
9
|
+
function createInMemoryPersonalization() {
|
|
10
|
+
const profiles = /* @__PURE__ */ new Map();
|
|
11
|
+
return {
|
|
12
|
+
async get(subjectId) {
|
|
13
|
+
const hit = profiles.get(subjectId);
|
|
14
|
+
return hit ? { ...hit, traits: { ...hit.traits } } : null;
|
|
15
|
+
},
|
|
16
|
+
async set(profile) {
|
|
17
|
+
profiles.set(profile.subjectId, {
|
|
18
|
+
...profile,
|
|
19
|
+
traits: { ...profile.traits },
|
|
20
|
+
updatedAt: profile.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
async merge(subjectId, traits) {
|
|
24
|
+
const existing = profiles.get(subjectId);
|
|
25
|
+
const next = {
|
|
26
|
+
subjectId,
|
|
27
|
+
traits: { ...existing?.traits ?? {}, ...traits },
|
|
28
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
29
|
+
};
|
|
30
|
+
profiles.set(subjectId, next);
|
|
31
|
+
return { ...next, traits: { ...next.traits } };
|
|
32
|
+
},
|
|
33
|
+
async delete(subjectId) {
|
|
34
|
+
profiles.delete(subjectId);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function renderProfileContext(profile) {
|
|
39
|
+
if (!profile || Object.keys(profile.traits).length === 0) return "";
|
|
40
|
+
const lines = Object.entries(profile.traits).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `- ${key}: ${typeof value === "string" ? value : JSON.stringify(value)}`);
|
|
41
|
+
if (lines.length === 0) return "";
|
|
42
|
+
return `## User profile
|
|
43
|
+
${lines.join("\n")}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { __require, createInMemoryPersonalization, renderProfileContext };
|
|
47
|
+
//# sourceMappingURL=chunk-G5S2A3MJ.js.map
|
|
48
|
+
//# sourceMappingURL=chunk-G5S2A3MJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/personalization.ts"],"names":[],"mappings":";;;;;;;;AA0BO,SAAS,6BAAA,GAAsD;AACpE,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAoC;AAEzD,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,SAAA,EAAW;AACnB,MAAA,MAAM,GAAA,GAAM,QAAA,CAAS,GAAA,CAAI,SAAS,CAAA;AAClC,MAAA,OAAO,GAAA,GAAM,EAAE,GAAG,GAAA,EAAK,MAAA,EAAQ,EAAE,GAAG,GAAA,CAAI,MAAA,EAAO,EAAE,GAAI,IAAA;AAAA,IACvD,CAAA;AAAA,IACA,MAAM,IAAI,OAAA,EAAS;AACjB,MAAA,QAAA,CAAS,GAAA,CAAI,QAAQ,SAAA,EAAW;AAAA,QAC9B,GAAG,OAAA;AAAA,QACH,MAAA,EAAQ,EAAE,GAAG,OAAA,CAAQ,MAAA,EAAO;AAAA,QAC5B,WAAW,OAAA,CAAQ,SAAA,IAAA,iBAAa,IAAI,IAAA,IAAO,WAAA;AAAY,OACxD,CAAA;AAAA,IACH,CAAA;AAAA,IACA,MAAM,KAAA,CAAM,SAAA,EAAW,MAAA,EAAQ;AAC7B,MAAA,MAAM,QAAA,GAAW,QAAA,CAAS,GAAA,CAAI,SAAS,CAAA;AACvC,MAAA,MAAM,IAAA,GAA+B;AAAA,QACnC,SAAA;AAAA,QACA,MAAA,EAAQ,EAAE,GAAI,QAAA,EAAU,UAAU,EAAC,EAAI,GAAG,MAAA,EAAO;AAAA,QACjD,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,OACpC;AACA,MAAA,QAAA,CAAS,GAAA,CAAI,WAAW,IAAI,CAAA;AAC5B,MAAA,OAAO,EAAE,GAAG,IAAA,EAAM,MAAA,EAAQ,EAAE,GAAG,IAAA,CAAK,QAAO,EAAE;AAAA,IAC/C,CAAA;AAAA,IACA,MAAM,OAAO,SAAA,EAAW;AACtB,MAAA,QAAA,CAAS,OAAO,SAAS,CAAA;AAAA,IAC3B;AAAA,GACF;AACF;AAOO,SAAS,qBAAqB,OAAA,EAAgD;AACnF,EAAA,IAAI,CAAC,WAAW,MAAA,CAAO,IAAA,CAAK,QAAQ,MAAM,CAAA,CAAE,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AACjE,EAAA,MAAM,QAAQ,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,MAAM,EACxC,MAAA,CAAO,CAAC,GAAG,KAAK,CAAA,KAAM,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,QAAQ,KAAA,KAAU,EAAE,CAAA,CAC3E,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM,KAAK,GAAG,CAAA,EAAA,EAAK,OAAO,KAAA,KAAU,WAAW,KAAA,GAAQ,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA,CAAE,CAAA;AACjG,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAC/B,EAAA,OAAO,CAAA;AAAA,EAAoB,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAC7C","file":"chunk-G5S2A3MJ.js","sourcesContent":["/**\n * Personalization — a persisted profile per subject (user id,\n * account id, device). The agent reads it on every run to\n * condition responses; the runtime updates it when new facts\n * appear.\n */\n\nexport interface PersonalizationProfile {\n subjectId: string\n /** Human-editable notes, facts, preferences. */\n traits: Record<string, unknown>\n /** ISO timestamp of the latest update. */\n updatedAt: string\n}\n\nexport interface PersonalizationStore {\n get: (subjectId: string) => Promise<PersonalizationProfile | null>\n set: (profile: PersonalizationProfile) => Promise<void>\n merge: (subjectId: string, traits: Record<string, unknown>) => Promise<PersonalizationProfile>\n delete?: (subjectId: string) => Promise<void>\n}\n\n/**\n * In-memory personalization store — tests, single-process demos.\n * Bring your own for production (Postgres, Redis, DynamoDB).\n */\nexport function createInMemoryPersonalization(): PersonalizationStore {\n const profiles = new Map<string, PersonalizationProfile>()\n\n return {\n async get(subjectId) {\n const hit = profiles.get(subjectId)\n return hit ? { ...hit, traits: { ...hit.traits } } : null\n },\n async set(profile) {\n profiles.set(profile.subjectId, {\n ...profile,\n traits: { ...profile.traits },\n updatedAt: profile.updatedAt || new Date().toISOString(),\n })\n },\n async merge(subjectId, traits) {\n const existing = profiles.get(subjectId)\n const next: PersonalizationProfile = {\n subjectId,\n traits: { ...(existing?.traits ?? {}), ...traits },\n updatedAt: new Date().toISOString(),\n }\n profiles.set(subjectId, next)\n return { ...next, traits: { ...next.traits } }\n },\n async delete(subjectId) {\n profiles.delete(subjectId)\n },\n }\n}\n\n/**\n * Render a profile into a system-prompt fragment the runtime can\n * prepend. Kept intentionally short — full profile dumps bloat\n * context and leak unnecessary detail to the model.\n */\nexport function renderProfileContext(profile: PersonalizationProfile | null): string {\n if (!profile || Object.keys(profile.traits).length === 0) return ''\n const lines = Object.entries(profile.traits)\n .filter(([, value]) => value !== undefined && value !== null && value !== '')\n .map(([key, value]) => `- ${key}: ${typeof value === 'string' ? value : JSON.stringify(value)}`)\n if (lines.length === 0) return ''\n return `## User profile\\n${lines.join('\\n')}`\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@agentskit/core');
|
|
4
|
+
var security = require('@agentskit/core/security');
|
|
5
|
+
var promises = require('fs/promises');
|
|
6
|
+
var path = require('path');
|
|
4
7
|
|
|
5
8
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
9
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
@@ -21,7 +24,11 @@ function fileChatMemory(path) {
|
|
|
21
24
|
},
|
|
22
25
|
async save(messages) {
|
|
23
26
|
const fs = await import('fs/promises');
|
|
24
|
-
await fs.writeFile(
|
|
27
|
+
await fs.writeFile(
|
|
28
|
+
path,
|
|
29
|
+
JSON.stringify(core.serializeMessages(messages), null, 2),
|
|
30
|
+
{ encoding: "utf8", mode: 384 }
|
|
31
|
+
);
|
|
25
32
|
},
|
|
26
33
|
async clear() {
|
|
27
34
|
try {
|
|
@@ -197,7 +204,7 @@ async function createRedisClientAdapter(url) {
|
|
|
197
204
|
return await client.keys(pattern);
|
|
198
205
|
},
|
|
199
206
|
async disconnect() {
|
|
200
|
-
await client.
|
|
207
|
+
await client.close();
|
|
201
208
|
},
|
|
202
209
|
async call(command, ...args) {
|
|
203
210
|
return await client.sendCommand([command, ...args.map(String)]);
|
|
@@ -425,16 +432,23 @@ function createVectraStore(dirPath) {
|
|
|
425
432
|
return {
|
|
426
433
|
async upsert(docs) {
|
|
427
434
|
const idx = await getIndex();
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
435
|
+
await idx.beginUpdate();
|
|
436
|
+
try {
|
|
437
|
+
for (const doc of docs) {
|
|
438
|
+
await idx.insertItem({
|
|
439
|
+
vector: doc.vector,
|
|
440
|
+
metadata: { _id: doc.id, ...doc.metadata }
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
await idx.endUpdate();
|
|
444
|
+
} catch (err) {
|
|
445
|
+
idx.cancelUpdate();
|
|
446
|
+
throw err;
|
|
433
447
|
}
|
|
434
448
|
},
|
|
435
449
|
async query(vector, topK) {
|
|
436
450
|
const idx = await getIndex();
|
|
437
|
-
const results = await idx.queryItems(vector, topK);
|
|
451
|
+
const results = await idx.queryItems(vector, "", topK);
|
|
438
452
|
return results.map((r) => ({
|
|
439
453
|
id: String(r.item.metadata._id ?? ""),
|
|
440
454
|
score: r.score,
|
|
@@ -1259,13 +1273,550 @@ function createHierarchicalMemory(options) {
|
|
|
1259
1273
|
};
|
|
1260
1274
|
}
|
|
1261
1275
|
|
|
1276
|
+
// src/forget.ts
|
|
1277
|
+
function isForgettable(value) {
|
|
1278
|
+
return !!value && typeof value === "object" && "forgetSubject" in value && typeof value.forgetSubject === "function";
|
|
1279
|
+
}
|
|
1280
|
+
async function hash(input) {
|
|
1281
|
+
const encoded = new TextEncoder().encode(input);
|
|
1282
|
+
const digest = await crypto.subtle.digest("SHA-256", encoded);
|
|
1283
|
+
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1284
|
+
}
|
|
1285
|
+
async function forgetSubject(memories, subjectId) {
|
|
1286
|
+
const reports = [];
|
|
1287
|
+
for (const memory of memories) {
|
|
1288
|
+
if (!isForgettable(memory)) continue;
|
|
1289
|
+
reports.push(await memory.forgetSubject(subjectId));
|
|
1290
|
+
}
|
|
1291
|
+
const totalDeleted = reports.reduce((sum, r) => sum + r.deletedCount, 0);
|
|
1292
|
+
const evidenceHash = await hash(
|
|
1293
|
+
JSON.stringify({ subjectId, reports: reports.map((r) => ({ b: r.backend, n: r.deletedCount, at: r.at })) })
|
|
1294
|
+
);
|
|
1295
|
+
return { subjectId, reports, totalDeleted, evidenceHash };
|
|
1296
|
+
}
|
|
1297
|
+
function makeForgettable(memory, options) {
|
|
1298
|
+
return Object.assign(memory, {
|
|
1299
|
+
__agentskitBackend: options.backend,
|
|
1300
|
+
forgetSubject: async (subjectId) => {
|
|
1301
|
+
const ids = await options.listIds(subjectId);
|
|
1302
|
+
const failures = [];
|
|
1303
|
+
try {
|
|
1304
|
+
await options.deleteIds(ids);
|
|
1305
|
+
} catch (err) {
|
|
1306
|
+
for (const id of ids) failures.push({ id, reason: err.message });
|
|
1307
|
+
}
|
|
1308
|
+
return {
|
|
1309
|
+
backend: options.backend,
|
|
1310
|
+
deletedCount: ids.length - failures.length,
|
|
1311
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1312
|
+
failures: failures.length ? failures : void 0
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
async function transform(input, opts) {
|
|
1318
|
+
if (opts.mode === "tokenize") {
|
|
1319
|
+
if (!opts.vault) {
|
|
1320
|
+
throw new core.ConfigError({
|
|
1321
|
+
code: core.ErrorCodes.AK_CONFIG_INVALID,
|
|
1322
|
+
message: 'wrapMemoryWithRedaction: vault is required when mode is "tokenize"'
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
if (!opts.allowedRoles) {
|
|
1326
|
+
throw new core.ConfigError({
|
|
1327
|
+
code: core.ErrorCodes.AK_CONFIG_INVALID,
|
|
1328
|
+
message: 'wrapMemoryWithRedaction: allowedRoles is required when mode is "tokenize"'
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
const result = await security.tokenize(input, {
|
|
1332
|
+
rules: opts.rules,
|
|
1333
|
+
vault: opts.vault,
|
|
1334
|
+
allowedRoles: opts.allowedRoles,
|
|
1335
|
+
audit: opts.audit
|
|
1336
|
+
});
|
|
1337
|
+
return result.value;
|
|
1338
|
+
}
|
|
1339
|
+
return security.createPIIRedactor({ rules: opts.rules }).redact(input).value;
|
|
1340
|
+
}
|
|
1341
|
+
function wrapChatMemoryWithRedaction(inner, options) {
|
|
1342
|
+
return {
|
|
1343
|
+
load: () => inner.load(),
|
|
1344
|
+
save: async (messages) => {
|
|
1345
|
+
const redacted = await Promise.all(
|
|
1346
|
+
messages.map(async (m) => ({
|
|
1347
|
+
...m,
|
|
1348
|
+
content: await transform(m.content ?? "", options)
|
|
1349
|
+
}))
|
|
1350
|
+
);
|
|
1351
|
+
await inner.save(redacted);
|
|
1352
|
+
},
|
|
1353
|
+
clear: inner.clear ? () => inner.clear() : void 0
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
function wrapVectorMemoryWithRedaction(inner, options) {
|
|
1357
|
+
return {
|
|
1358
|
+
store: async (docs) => {
|
|
1359
|
+
const redacted = await Promise.all(
|
|
1360
|
+
docs.map(async (d) => ({
|
|
1361
|
+
...d,
|
|
1362
|
+
content: await transform(d.content, options)
|
|
1363
|
+
}))
|
|
1364
|
+
);
|
|
1365
|
+
await inner.store(redacted);
|
|
1366
|
+
},
|
|
1367
|
+
search: (embedding, opts) => inner.search(embedding, opts),
|
|
1368
|
+
delete: inner.delete ? (ids) => inner.delete(ids) : void 0
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/kv-store-types.ts
|
|
1373
|
+
var isExpired = (entry, ttlSeconds, now) => {
|
|
1374
|
+
if (ttlSeconds === void 0) return false;
|
|
1375
|
+
return now - entry.insertedAt > ttlSeconds * 1e3;
|
|
1376
|
+
};
|
|
1377
|
+
var enforceMaxMessages = (map, maxMessages) => {
|
|
1378
|
+
if (maxMessages === void 0) return;
|
|
1379
|
+
while (map.size > maxMessages) {
|
|
1380
|
+
const oldest = map.keys().next().value;
|
|
1381
|
+
if (oldest === void 0) break;
|
|
1382
|
+
map.delete(oldest);
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
// src/kv-store-basic.ts
|
|
1387
|
+
var createInMemoryStore = (config) => {
|
|
1388
|
+
const store = /* @__PURE__ */ new Map();
|
|
1389
|
+
const now = () => Date.now();
|
|
1390
|
+
return {
|
|
1391
|
+
id: "in-memory",
|
|
1392
|
+
async get(key) {
|
|
1393
|
+
const entry = store.get(key);
|
|
1394
|
+
if (!entry) return void 0;
|
|
1395
|
+
if (isExpired(entry, config.ttlSeconds, now())) {
|
|
1396
|
+
store.delete(key);
|
|
1397
|
+
return void 0;
|
|
1398
|
+
}
|
|
1399
|
+
return entry.value;
|
|
1400
|
+
},
|
|
1401
|
+
async set(key, value) {
|
|
1402
|
+
store.set(key, { value, insertedAt: now() });
|
|
1403
|
+
enforceMaxMessages(store, config.maxMessages);
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
};
|
|
1407
|
+
var createFileStore = (config) => {
|
|
1408
|
+
const path$1 = config.path;
|
|
1409
|
+
let cache;
|
|
1410
|
+
const load = async () => {
|
|
1411
|
+
if (cache) return cache;
|
|
1412
|
+
try {
|
|
1413
|
+
const parsed = JSON.parse(await promises.readFile(path$1, "utf8"));
|
|
1414
|
+
cache = new Map(Object.entries(parsed));
|
|
1415
|
+
} catch (err) {
|
|
1416
|
+
if (err.code === "ENOENT") cache = /* @__PURE__ */ new Map();
|
|
1417
|
+
else throw err;
|
|
1418
|
+
}
|
|
1419
|
+
return cache;
|
|
1420
|
+
};
|
|
1421
|
+
const persist = async (map) => {
|
|
1422
|
+
await promises.mkdir(path.dirname(path$1), { recursive: true });
|
|
1423
|
+
await promises.writeFile(path$1, JSON.stringify(Object.fromEntries(map), null, 2), { encoding: "utf8", mode: 384 });
|
|
1424
|
+
};
|
|
1425
|
+
return {
|
|
1426
|
+
id: `file:${path$1}`,
|
|
1427
|
+
async get(key) {
|
|
1428
|
+
const map = await load();
|
|
1429
|
+
const entry = map.get(key);
|
|
1430
|
+
if (!entry) return void 0;
|
|
1431
|
+
if (isExpired(entry, config.ttlSeconds, Date.now())) {
|
|
1432
|
+
map.delete(key);
|
|
1433
|
+
await persist(map);
|
|
1434
|
+
return void 0;
|
|
1435
|
+
}
|
|
1436
|
+
return entry.value;
|
|
1437
|
+
},
|
|
1438
|
+
async set(key, value) {
|
|
1439
|
+
const map = await load();
|
|
1440
|
+
map.set(key, { value, insertedAt: Date.now() });
|
|
1441
|
+
enforceMaxMessages(map, config.maxMessages);
|
|
1442
|
+
await persist(map);
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
};
|
|
1446
|
+
var resolveLocalStorage = () => {
|
|
1447
|
+
const maybe = globalThis.localStorage;
|
|
1448
|
+
return maybe && typeof maybe.getItem === "function" && typeof maybe.setItem === "function" ? maybe : void 0;
|
|
1449
|
+
};
|
|
1450
|
+
var defaultLocalStoragePath = () => `${process.cwd()}/.agentskit/memory-localstorage.json`;
|
|
1451
|
+
var createLocalStorageStore = ({
|
|
1452
|
+
config,
|
|
1453
|
+
storage = resolveLocalStorage(),
|
|
1454
|
+
filePath = defaultLocalStoragePath()
|
|
1455
|
+
}) => {
|
|
1456
|
+
const key = config.key;
|
|
1457
|
+
let cache;
|
|
1458
|
+
const mapFromJson = (raw) => raw ? new Map(Object.entries(JSON.parse(raw))) : /* @__PURE__ */ new Map();
|
|
1459
|
+
const loadFromFile = async () => {
|
|
1460
|
+
if (cache) return cache;
|
|
1461
|
+
try {
|
|
1462
|
+
cache = mapFromJson(await promises.readFile(filePath, "utf8"));
|
|
1463
|
+
} catch (err) {
|
|
1464
|
+
if (err.code === "ENOENT") cache = /* @__PURE__ */ new Map();
|
|
1465
|
+
else throw err;
|
|
1466
|
+
}
|
|
1467
|
+
return cache;
|
|
1468
|
+
};
|
|
1469
|
+
const load = async () => storage ? mapFromJson(storage.getItem(key)) : loadFromFile();
|
|
1470
|
+
const persist = async (map) => {
|
|
1471
|
+
const raw = JSON.stringify(Object.fromEntries(map), null, 2);
|
|
1472
|
+
if (storage) {
|
|
1473
|
+
storage.setItem(key, raw);
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
await promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
1477
|
+
await promises.writeFile(filePath, raw, { encoding: "utf8", mode: 384 });
|
|
1478
|
+
};
|
|
1479
|
+
return {
|
|
1480
|
+
id: storage ? `localstorage:${key}` : `localstorage-file:${filePath}:${key}`,
|
|
1481
|
+
async get(itemKey) {
|
|
1482
|
+
const map = await load();
|
|
1483
|
+
const entry = map.get(itemKey);
|
|
1484
|
+
if (!entry) return void 0;
|
|
1485
|
+
if (isExpired(entry, config.ttlSeconds, Date.now())) {
|
|
1486
|
+
map.delete(itemKey);
|
|
1487
|
+
await persist(map);
|
|
1488
|
+
return void 0;
|
|
1489
|
+
}
|
|
1490
|
+
return entry.value;
|
|
1491
|
+
},
|
|
1492
|
+
async set(itemKey, value) {
|
|
1493
|
+
const map = await load();
|
|
1494
|
+
map.set(itemKey, { value, insertedAt: Date.now() });
|
|
1495
|
+
enforceMaxMessages(map, config.maxMessages);
|
|
1496
|
+
await persist(map);
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
};
|
|
1500
|
+
|
|
1501
|
+
// src/kv-store-sqlite.ts
|
|
1502
|
+
var createSqliteStore = ({ config, open }) => {
|
|
1503
|
+
const db = open(config.path);
|
|
1504
|
+
db.exec(
|
|
1505
|
+
`CREATE TABLE IF NOT EXISTS memory (
|
|
1506
|
+
key TEXT PRIMARY KEY,
|
|
1507
|
+
value TEXT NOT NULL,
|
|
1508
|
+
inserted_at INTEGER NOT NULL
|
|
1509
|
+
)`
|
|
1510
|
+
);
|
|
1511
|
+
const getStmt = db.prepare("SELECT value, inserted_at FROM memory WHERE key = ?");
|
|
1512
|
+
const setStmt = db.prepare(
|
|
1513
|
+
"INSERT INTO memory(key, value, inserted_at) VALUES(?, ?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value, inserted_at=excluded.inserted_at"
|
|
1514
|
+
);
|
|
1515
|
+
const delStmt = db.prepare("DELETE FROM memory WHERE key = ?");
|
|
1516
|
+
const oldestStmt = db.prepare("SELECT key FROM memory ORDER BY inserted_at ASC LIMIT 1");
|
|
1517
|
+
const countStmt = db.prepare("SELECT COUNT(*) as n FROM memory");
|
|
1518
|
+
const enforce = () => {
|
|
1519
|
+
if (config.maxMessages === void 0) return;
|
|
1520
|
+
const countResult = countStmt.get();
|
|
1521
|
+
let count = countResult ? countResult.n : 0;
|
|
1522
|
+
while (count > config.maxMessages) {
|
|
1523
|
+
const oldest = oldestStmt.get();
|
|
1524
|
+
if (!oldest) break;
|
|
1525
|
+
delStmt.run(oldest.key);
|
|
1526
|
+
count -= 1;
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
return {
|
|
1530
|
+
id: `sqlite:${config.path}`,
|
|
1531
|
+
async get(key) {
|
|
1532
|
+
const row = getStmt.get(key);
|
|
1533
|
+
if (!row) return void 0;
|
|
1534
|
+
if (isExpired({ insertedAt: row.inserted_at }, config.ttlSeconds, Date.now())) {
|
|
1535
|
+
delStmt.run(key);
|
|
1536
|
+
return void 0;
|
|
1537
|
+
}
|
|
1538
|
+
return JSON.parse(row.value);
|
|
1539
|
+
},
|
|
1540
|
+
async set(key, value) {
|
|
1541
|
+
setStmt.run(key, JSON.stringify(value), Date.now());
|
|
1542
|
+
enforce();
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
};
|
|
1546
|
+
var tryDefaultSqliteOpener = async () => {
|
|
1547
|
+
try {
|
|
1548
|
+
const moduleId = "better-sqlite3";
|
|
1549
|
+
const mod = await import(
|
|
1550
|
+
/* @vite-ignore */
|
|
1551
|
+
moduleId
|
|
1552
|
+
);
|
|
1553
|
+
const Ctor = mod.default;
|
|
1554
|
+
if (typeof Ctor !== "function") return void 0;
|
|
1555
|
+
return (path) => new Ctor(path);
|
|
1556
|
+
} catch {
|
|
1557
|
+
return void 0;
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
var createRedisStore = ({ config, client }) => {
|
|
1561
|
+
const prefix = config.prefix;
|
|
1562
|
+
const namespaced = (key) => `${prefix}${key}`;
|
|
1563
|
+
const wrap = async (op, fn) => {
|
|
1564
|
+
try {
|
|
1565
|
+
return await fn();
|
|
1566
|
+
} catch (cause) {
|
|
1567
|
+
throw new core.MemoryError({
|
|
1568
|
+
code: "AK_MEMORY_REDIS_CONNECTION_FAILED",
|
|
1569
|
+
message: `createRedisStore.${op}: redis command failed \u2014 ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
1570
|
+
cause: cause instanceof Error ? cause : void 0
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
};
|
|
1574
|
+
const enforce = async () => {
|
|
1575
|
+
if (config.maxMessages === void 0) return;
|
|
1576
|
+
const keys = await wrap("enforce", () => Promise.resolve(client.keys(`${prefix}*`)));
|
|
1577
|
+
if (keys.length <= config.maxMessages) return;
|
|
1578
|
+
const envelopes = [];
|
|
1579
|
+
for (const fullKey of keys) {
|
|
1580
|
+
const raw = await wrap("enforce", () => Promise.resolve(client.get(fullKey)));
|
|
1581
|
+
if (raw === null) continue;
|
|
1582
|
+
envelopes.push({ key: fullKey, insertedAt: JSON.parse(raw).insertedAt });
|
|
1583
|
+
}
|
|
1584
|
+
envelopes.sort((a, b) => a.insertedAt - b.insertedAt);
|
|
1585
|
+
let excess = envelopes.length - config.maxMessages;
|
|
1586
|
+
for (const entry of envelopes) {
|
|
1587
|
+
if (excess <= 0) break;
|
|
1588
|
+
await wrap("enforce", () => Promise.resolve(client.del(entry.key)));
|
|
1589
|
+
excess -= 1;
|
|
1590
|
+
}
|
|
1591
|
+
};
|
|
1592
|
+
return {
|
|
1593
|
+
id: `redis:${prefix}`,
|
|
1594
|
+
async get(key) {
|
|
1595
|
+
const raw = await wrap("get", () => Promise.resolve(client.get(namespaced(key))));
|
|
1596
|
+
if (raw === null) return void 0;
|
|
1597
|
+
const envelope = JSON.parse(raw);
|
|
1598
|
+
if (isExpired({ value: envelope.value, insertedAt: envelope.insertedAt }, config.ttlSeconds, Date.now())) {
|
|
1599
|
+
await wrap("get", () => Promise.resolve(client.del(namespaced(key))));
|
|
1600
|
+
return void 0;
|
|
1601
|
+
}
|
|
1602
|
+
return envelope.value;
|
|
1603
|
+
},
|
|
1604
|
+
async set(key, value) {
|
|
1605
|
+
const payload = JSON.stringify({ value, insertedAt: Date.now() });
|
|
1606
|
+
const options = config.ttlSeconds === void 0 ? void 0 : { EX: config.ttlSeconds };
|
|
1607
|
+
await wrap("set", () => Promise.resolve(client.set(namespaced(key), payload, options)));
|
|
1608
|
+
await enforce();
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
};
|
|
1612
|
+
var adaptIoredis = (io) => ({
|
|
1613
|
+
get: (key) => io.get(key),
|
|
1614
|
+
set: (key, value, options) => options?.EX === void 0 ? io.set(key, value) : io.set(key, value, "EX", options.EX),
|
|
1615
|
+
del: (key) => io.del(key),
|
|
1616
|
+
keys: (pattern) => io.keys(pattern)
|
|
1617
|
+
});
|
|
1618
|
+
var tryDefaultRedisClient = async (url) => {
|
|
1619
|
+
try {
|
|
1620
|
+
const moduleId = "redis";
|
|
1621
|
+
const mod = await import(
|
|
1622
|
+
/* @vite-ignore */
|
|
1623
|
+
moduleId
|
|
1624
|
+
);
|
|
1625
|
+
const createClient = mod.createClient;
|
|
1626
|
+
if (typeof createClient !== "function") return void 0;
|
|
1627
|
+
const client = createClient({ url });
|
|
1628
|
+
try {
|
|
1629
|
+
await client.connect();
|
|
1630
|
+
} catch (cause) {
|
|
1631
|
+
throw new core.MemoryError({
|
|
1632
|
+
code: "AK_MEMORY_REDIS_CONNECTION_FAILED",
|
|
1633
|
+
message: `tryDefaultRedisClient: redis connect() failed \u2014 ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
1634
|
+
cause: cause instanceof Error ? cause : void 0
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
return client;
|
|
1638
|
+
} catch (err) {
|
|
1639
|
+
if (err instanceof core.MemoryError) throw err;
|
|
1640
|
+
return void 0;
|
|
1641
|
+
}
|
|
1642
|
+
};
|
|
1643
|
+
var createVectorStore = ({
|
|
1644
|
+
config,
|
|
1645
|
+
vectorStore,
|
|
1646
|
+
embedder
|
|
1647
|
+
}) => {
|
|
1648
|
+
const collection = config.collection;
|
|
1649
|
+
const embedOne = async (text) => {
|
|
1650
|
+
const [vec] = await embedder.embed([text]);
|
|
1651
|
+
if (vec === void 0) {
|
|
1652
|
+
throw new core.MemoryError({
|
|
1653
|
+
code: "AK_MEMORY_VECTOR_EMBEDDER_REQUIRED",
|
|
1654
|
+
message: "createVectorStore: embedder returned no vector for the input text."
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
return vec;
|
|
1658
|
+
};
|
|
1659
|
+
return {
|
|
1660
|
+
id: `vector:${config.provider}:${collection}`,
|
|
1661
|
+
async get(key) {
|
|
1662
|
+
const vec = await embedOne(key);
|
|
1663
|
+
const hits = await vectorStore.query(vec, 1, { __collection: collection, __key: key });
|
|
1664
|
+
const hit = hits[0];
|
|
1665
|
+
if (hit === void 0) return void 0;
|
|
1666
|
+
if (hit.metadata["__key"] !== key) return void 0;
|
|
1667
|
+
const insertedAt = hit.metadata["__insertedAt"];
|
|
1668
|
+
if (typeof insertedAt === "number" && isExpired({ insertedAt }, config.ttlSeconds, Date.now())) {
|
|
1669
|
+
return void 0;
|
|
1670
|
+
}
|
|
1671
|
+
return hit.metadata["__value"];
|
|
1672
|
+
},
|
|
1673
|
+
async set(key, value) {
|
|
1674
|
+
const vec = await embedOne(key);
|
|
1675
|
+
await vectorStore.upsert([
|
|
1676
|
+
{
|
|
1677
|
+
chunkId: `${collection}:${key}`,
|
|
1678
|
+
vec,
|
|
1679
|
+
metadata: { __collection: collection, __key: key, __value: value, __insertedAt: Date.now() }
|
|
1680
|
+
}
|
|
1681
|
+
]);
|
|
1682
|
+
},
|
|
1683
|
+
async recall(query, k = 5) {
|
|
1684
|
+
const vec = await embedOne(query);
|
|
1685
|
+
const hits = await vectorStore.query(vec, k, { __collection: collection });
|
|
1686
|
+
const now = Date.now();
|
|
1687
|
+
const results = [];
|
|
1688
|
+
for (const hit of hits) {
|
|
1689
|
+
const insertedAt = hit.metadata["__insertedAt"];
|
|
1690
|
+
if (typeof insertedAt === "number" && isExpired({ insertedAt }, config.ttlSeconds, now)) {
|
|
1691
|
+
continue;
|
|
1692
|
+
}
|
|
1693
|
+
results.push(hit.metadata["__value"]);
|
|
1694
|
+
}
|
|
1695
|
+
return results;
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
};
|
|
1699
|
+
var MemoryBackendNotImplementedError = class extends Error {
|
|
1700
|
+
constructor(backend) {
|
|
1701
|
+
super(
|
|
1702
|
+
`Memory backend "${backend}" is not implemented. Supported: "in-memory", "file", "sqlite", "localstorage", "redis", "vector".`
|
|
1703
|
+
);
|
|
1704
|
+
this.code = "MEMORY_BACKEND_NOT_IMPLEMENTED";
|
|
1705
|
+
this.backend = backend;
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1708
|
+
var MEMORY_BACKEND_SUPPORT = {
|
|
1709
|
+
"in-memory": "supported",
|
|
1710
|
+
file: "supported",
|
|
1711
|
+
sqlite: "supported",
|
|
1712
|
+
redis: "supported",
|
|
1713
|
+
vector: "supported",
|
|
1714
|
+
localstorage: "supported"
|
|
1715
|
+
};
|
|
1716
|
+
var isMemoryBackendSupported = (backend) => MEMORY_BACKEND_SUPPORT[backend] === "supported";
|
|
1717
|
+
var createKvMemoryFromConfig = ({
|
|
1718
|
+
config,
|
|
1719
|
+
sqlite,
|
|
1720
|
+
localStorageFilePath,
|
|
1721
|
+
redis,
|
|
1722
|
+
vectorStore,
|
|
1723
|
+
embedder
|
|
1724
|
+
}) => {
|
|
1725
|
+
if (config.backend === "in-memory") return createInMemoryStore(config);
|
|
1726
|
+
if (config.backend === "file") return createFileStore(config);
|
|
1727
|
+
if (config.backend === "localstorage") {
|
|
1728
|
+
return createLocalStorageStore({
|
|
1729
|
+
config,
|
|
1730
|
+
...localStorageFilePath ? { filePath: localStorageFilePath } : {}
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
if (config.backend === "sqlite") {
|
|
1734
|
+
if (!sqlite) {
|
|
1735
|
+
throw new core.MemoryError({
|
|
1736
|
+
code: "AK_MEMORY_SQLITE_OPENER_REQUIRED",
|
|
1737
|
+
message: "createKvMemoryFromConfig: sqlite backend requires an `open` function (better-sqlite3), or call createKvMemoryFromConfigAuto which lazy-imports it."
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
return createSqliteStore({ config, open: sqlite });
|
|
1741
|
+
}
|
|
1742
|
+
if (config.backend === "redis") {
|
|
1743
|
+
if (!redis) {
|
|
1744
|
+
throw new core.MemoryError({
|
|
1745
|
+
code: "AK_MEMORY_REDIS_CLIENT_REQUIRED",
|
|
1746
|
+
message: "createKvMemoryFromConfig: redis backend requires a `redis` client, or call createKvMemoryFromConfigAuto which lazy-imports it."
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
return createRedisStore({ config, client: redis });
|
|
1750
|
+
}
|
|
1751
|
+
if (config.backend === "vector") {
|
|
1752
|
+
if (!vectorStore) {
|
|
1753
|
+
throw new core.MemoryError({
|
|
1754
|
+
code: "AK_MEMORY_VECTOR_STORE_REQUIRED",
|
|
1755
|
+
message: "createKvMemoryFromConfig: vector backend requires a `vectorStore`."
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1758
|
+
if (!embedder) {
|
|
1759
|
+
throw new core.MemoryError({
|
|
1760
|
+
code: "AK_MEMORY_VECTOR_EMBEDDER_REQUIRED",
|
|
1761
|
+
message: "createKvMemoryFromConfig: vector backend requires an `embedder`."
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
return createVectorStore({ config, vectorStore, embedder });
|
|
1765
|
+
}
|
|
1766
|
+
const exhausted = config;
|
|
1767
|
+
throw new MemoryBackendNotImplementedError(exhausted.backend);
|
|
1768
|
+
};
|
|
1769
|
+
var createKvMemoryFromConfigAuto = async (config) => {
|
|
1770
|
+
if (config.backend === "sqlite") {
|
|
1771
|
+
const sqlite = await tryDefaultSqliteOpener();
|
|
1772
|
+
if (!sqlite) {
|
|
1773
|
+
throw new core.MemoryError({
|
|
1774
|
+
code: "AK_MEMORY_PEER_MISSING",
|
|
1775
|
+
message: "createKvMemoryFromConfigAuto: sqlite backend needs `better-sqlite3` (pnpm add better-sqlite3)."
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
return createKvMemoryFromConfig({ config, sqlite });
|
|
1779
|
+
}
|
|
1780
|
+
if (config.backend === "redis") {
|
|
1781
|
+
const redis = await tryDefaultRedisClient(config.url);
|
|
1782
|
+
if (!redis) {
|
|
1783
|
+
throw new core.MemoryError({
|
|
1784
|
+
code: "AK_MEMORY_PEER_MISSING",
|
|
1785
|
+
message: "createKvMemoryFromConfigAuto: redis backend needs `redis` (pnpm add redis)."
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
return createKvMemoryFromConfig({ config, redis });
|
|
1789
|
+
}
|
|
1790
|
+
if (config.backend === "vector") {
|
|
1791
|
+
throw new core.MemoryError({
|
|
1792
|
+
code: "AK_MEMORY_VECTOR_STORE_REQUIRED",
|
|
1793
|
+
message: "createKvMemoryFromConfigAuto: vector backend requires an injected vectorStore + embedder."
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
return createKvMemoryFromConfig({ config });
|
|
1797
|
+
};
|
|
1798
|
+
|
|
1799
|
+
exports.MEMORY_BACKEND_SUPPORT = MEMORY_BACKEND_SUPPORT;
|
|
1800
|
+
exports.MemoryBackendNotImplementedError = MemoryBackendNotImplementedError;
|
|
1801
|
+
exports.adaptIoredis = adaptIoredis;
|
|
1262
1802
|
exports.chroma = chroma;
|
|
1263
1803
|
exports.createEncryptedMemory = createEncryptedMemory;
|
|
1804
|
+
exports.createFileStore = createFileStore;
|
|
1264
1805
|
exports.createHierarchicalMemory = createHierarchicalMemory;
|
|
1265
1806
|
exports.createInMemoryGraph = createInMemoryGraph;
|
|
1266
1807
|
exports.createInMemoryPersonalization = createInMemoryPersonalization;
|
|
1808
|
+
exports.createInMemoryStore = createInMemoryStore;
|
|
1809
|
+
exports.createKvMemoryFromConfig = createKvMemoryFromConfig;
|
|
1810
|
+
exports.createKvMemoryFromConfigAuto = createKvMemoryFromConfigAuto;
|
|
1811
|
+
exports.createLocalStorageStore = createLocalStorageStore;
|
|
1812
|
+
exports.createRedisStore = createRedisStore;
|
|
1813
|
+
exports.createSqliteStore = createSqliteStore;
|
|
1814
|
+
exports.createVectorStore = createVectorStore;
|
|
1267
1815
|
exports.fileChatMemory = fileChatMemory;
|
|
1268
1816
|
exports.fileVectorMemory = fileVectorMemory;
|
|
1817
|
+
exports.forgetSubject = forgetSubject;
|
|
1818
|
+
exports.isMemoryBackendSupported = isMemoryBackendSupported;
|
|
1819
|
+
exports.makeForgettable = makeForgettable;
|
|
1269
1820
|
exports.matchesFilter = matchesFilter;
|
|
1270
1821
|
exports.milvusVectorStore = milvusVectorStore;
|
|
1271
1822
|
exports.mongoAtlasVectorStore = mongoAtlasVectorStore;
|
|
@@ -1277,8 +1828,12 @@ exports.redisVectorMemory = redisVectorMemory;
|
|
|
1277
1828
|
exports.renderProfileContext = renderProfileContext;
|
|
1278
1829
|
exports.sqliteChatMemory = sqliteChatMemory;
|
|
1279
1830
|
exports.supabaseVectorStore = supabaseVectorStore;
|
|
1831
|
+
exports.tryDefaultRedisClient = tryDefaultRedisClient;
|
|
1832
|
+
exports.tryDefaultSqliteOpener = tryDefaultSqliteOpener;
|
|
1280
1833
|
exports.tursoChatMemory = tursoChatMemory;
|
|
1281
1834
|
exports.upstashVector = upstashVector;
|
|
1282
1835
|
exports.weaviateVectorStore = weaviateVectorStore;
|
|
1836
|
+
exports.wrapChatMemoryWithRedaction = wrapChatMemoryWithRedaction;
|
|
1837
|
+
exports.wrapVectorMemoryWithRedaction = wrapVectorMemoryWithRedaction;
|
|
1283
1838
|
//# sourceMappingURL=index.cjs.map
|
|
1284
1839
|
//# sourceMappingURL=index.cjs.map
|