@agentskit/memory 0.5.5 → 0.7.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/README.md +34 -6
- package/dist/index.cjs +701 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +293 -2
- package/dist/index.d.ts +293 -2
- package/dist/index.js +689 -7
- package/dist/index.js.map +1 -1
- package/package.json +14 -3
package/dist/index.cjs
CHANGED
|
@@ -89,6 +89,78 @@ function sqliteChatMemory(config) {
|
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
+
var cachedSdk = null;
|
|
93
|
+
async function loadSdk() {
|
|
94
|
+
if (!cachedSdk) {
|
|
95
|
+
cachedSdk = (async () => {
|
|
96
|
+
try {
|
|
97
|
+
const moduleId = "@libsql/client";
|
|
98
|
+
return await import(
|
|
99
|
+
/* @vite-ignore */
|
|
100
|
+
moduleId
|
|
101
|
+
);
|
|
102
|
+
} catch {
|
|
103
|
+
throw new Error("Install @libsql/client to use tursoChatMemory: npm install @libsql/client");
|
|
104
|
+
}
|
|
105
|
+
})();
|
|
106
|
+
}
|
|
107
|
+
return cachedSdk;
|
|
108
|
+
}
|
|
109
|
+
function encodeMessages2(messages) {
|
|
110
|
+
return JSON.stringify(core.serializeMessages(messages));
|
|
111
|
+
}
|
|
112
|
+
function decodeMessages2(json) {
|
|
113
|
+
if (!json) return [];
|
|
114
|
+
try {
|
|
115
|
+
return core.deserializeMessages(JSON.parse(json));
|
|
116
|
+
} catch {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function tursoChatMemory(config) {
|
|
121
|
+
const conversationId = config.conversationId ?? "default";
|
|
122
|
+
let clientPromise = null;
|
|
123
|
+
const getClient = async () => {
|
|
124
|
+
if (!clientPromise) {
|
|
125
|
+
clientPromise = (async () => {
|
|
126
|
+
const sdk = await loadSdk();
|
|
127
|
+
const client = sdk.createClient({ url: config.url, authToken: config.authToken });
|
|
128
|
+
await client.execute({
|
|
129
|
+
sql: "CREATE TABLE IF NOT EXISTS conversations (id TEXT PRIMARY KEY, messages TEXT NOT NULL)"
|
|
130
|
+
});
|
|
131
|
+
return client;
|
|
132
|
+
})();
|
|
133
|
+
}
|
|
134
|
+
return clientPromise;
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
async load() {
|
|
138
|
+
const client = await getClient();
|
|
139
|
+
const result = await client.execute({
|
|
140
|
+
sql: "SELECT messages FROM conversations WHERE id = ?",
|
|
141
|
+
args: [conversationId]
|
|
142
|
+
});
|
|
143
|
+
const row = result.rows[0];
|
|
144
|
+
return decodeMessages2(row?.messages);
|
|
145
|
+
},
|
|
146
|
+
async save(messages) {
|
|
147
|
+
const client = await getClient();
|
|
148
|
+
const json = encodeMessages2(messages);
|
|
149
|
+
await client.execute({
|
|
150
|
+
sql: `INSERT INTO conversations (id, messages) VALUES (?, ?)
|
|
151
|
+
ON CONFLICT(id) DO UPDATE SET messages = excluded.messages`,
|
|
152
|
+
args: [conversationId, json]
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
async clear() {
|
|
156
|
+
const client = await getClient();
|
|
157
|
+
await client.execute({
|
|
158
|
+
sql: "DELETE FROM conversations WHERE id = ?",
|
|
159
|
+
args: [conversationId]
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
92
164
|
|
|
93
165
|
// src/redis-client.ts
|
|
94
166
|
async function createRedisClientAdapter(url) {
|
|
@@ -124,10 +196,10 @@ async function createRedisClientAdapter(url) {
|
|
|
124
196
|
}
|
|
125
197
|
|
|
126
198
|
// src/redis-chat.ts
|
|
127
|
-
function
|
|
199
|
+
function encodeMessages3(messages) {
|
|
128
200
|
return JSON.stringify(core.serializeMessages(messages));
|
|
129
201
|
}
|
|
130
|
-
function
|
|
202
|
+
function decodeMessages3(json) {
|
|
131
203
|
if (!json) return [];
|
|
132
204
|
try {
|
|
133
205
|
return core.deserializeMessages(JSON.parse(json));
|
|
@@ -149,11 +221,11 @@ function redisChatMemory(config) {
|
|
|
149
221
|
async load() {
|
|
150
222
|
const client = await getClient();
|
|
151
223
|
const json = await client.get(key);
|
|
152
|
-
return
|
|
224
|
+
return decodeMessages3(json);
|
|
153
225
|
},
|
|
154
226
|
async save(messages) {
|
|
155
227
|
const client = await getClient();
|
|
156
|
-
await client.set(key,
|
|
228
|
+
await client.set(key, encodeMessages3(messages));
|
|
157
229
|
},
|
|
158
230
|
async clear() {
|
|
159
231
|
const client = await getClient();
|
|
@@ -283,6 +355,40 @@ function redisVectorMemory(config) {
|
|
|
283
355
|
};
|
|
284
356
|
}
|
|
285
357
|
|
|
358
|
+
// src/vector/filter.ts
|
|
359
|
+
function isPrimitive(value) {
|
|
360
|
+
const t = typeof value;
|
|
361
|
+
return value === null || t === "string" || t === "number" || t === "boolean";
|
|
362
|
+
}
|
|
363
|
+
function evalOperator(actual, op) {
|
|
364
|
+
if ("$eq" in op) return actual === op.$eq;
|
|
365
|
+
if ("$ne" in op) return actual !== op.$ne;
|
|
366
|
+
if ("$in" in op) return op.$in.includes(actual);
|
|
367
|
+
if ("$nin" in op) return !op.$nin.includes(actual);
|
|
368
|
+
if ("$gt" in op) return actual > op.$gt;
|
|
369
|
+
if ("$gte" in op) return actual >= op.$gte;
|
|
370
|
+
if ("$lt" in op) return actual < op.$lt;
|
|
371
|
+
if ("$lte" in op) return actual <= op.$lte;
|
|
372
|
+
if ("$exists" in op) return actual !== void 0 === op.$exists;
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
function evalPredicate(actual, predicate) {
|
|
376
|
+
if (isPrimitive(predicate)) return actual === predicate;
|
|
377
|
+
return evalOperator(actual, predicate);
|
|
378
|
+
}
|
|
379
|
+
function matchesFilter(metadata, filter) {
|
|
380
|
+
if (!filter) return true;
|
|
381
|
+
const meta = metadata ?? {};
|
|
382
|
+
const compound = filter;
|
|
383
|
+
if (compound.$and) return compound.$and.every((f) => matchesFilter(meta, f));
|
|
384
|
+
if (compound.$or) return compound.$or.some((f) => matchesFilter(meta, f));
|
|
385
|
+
for (const [field, predicate] of Object.entries(filter)) {
|
|
386
|
+
if (field.startsWith("$")) continue;
|
|
387
|
+
if (!evalPredicate(meta[field], predicate)) return false;
|
|
388
|
+
}
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
|
|
286
392
|
// src/file-vector.ts
|
|
287
393
|
function requireVectra() {
|
|
288
394
|
try {
|
|
@@ -351,8 +457,9 @@ function fileVectorMemory(config) {
|
|
|
351
457
|
async search(embedding, options) {
|
|
352
458
|
const topK = options?.topK ?? 5;
|
|
353
459
|
const threshold = options?.threshold ?? 0;
|
|
354
|
-
const
|
|
355
|
-
|
|
460
|
+
const fetchK = options?.filter ? Math.max(topK * 4, 50) : topK;
|
|
461
|
+
const results = await store.query(embedding, fetchK);
|
|
462
|
+
return results.filter((r) => r.score >= threshold).filter((r) => matchesFilter(r.metadata, options?.filter)).slice(0, topK).map((r) => ({
|
|
356
463
|
id: r.id,
|
|
357
464
|
content: String(r.metadata.content ?? contentCache.get(r.id) ?? ""),
|
|
358
465
|
score: r.score,
|
|
@@ -366,10 +473,598 @@ function fileVectorMemory(config) {
|
|
|
366
473
|
};
|
|
367
474
|
}
|
|
368
475
|
|
|
476
|
+
// src/personalization.ts
|
|
477
|
+
function createInMemoryPersonalization() {
|
|
478
|
+
const profiles = /* @__PURE__ */ new Map();
|
|
479
|
+
return {
|
|
480
|
+
async get(subjectId) {
|
|
481
|
+
const hit = profiles.get(subjectId);
|
|
482
|
+
return hit ? { ...hit, traits: { ...hit.traits } } : null;
|
|
483
|
+
},
|
|
484
|
+
async set(profile) {
|
|
485
|
+
profiles.set(profile.subjectId, {
|
|
486
|
+
...profile,
|
|
487
|
+
traits: { ...profile.traits },
|
|
488
|
+
updatedAt: profile.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
489
|
+
});
|
|
490
|
+
},
|
|
491
|
+
async merge(subjectId, traits) {
|
|
492
|
+
const existing = profiles.get(subjectId);
|
|
493
|
+
const next = {
|
|
494
|
+
subjectId,
|
|
495
|
+
traits: { ...existing?.traits ?? {}, ...traits },
|
|
496
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
497
|
+
};
|
|
498
|
+
profiles.set(subjectId, next);
|
|
499
|
+
return { ...next, traits: { ...next.traits } };
|
|
500
|
+
},
|
|
501
|
+
async delete(subjectId) {
|
|
502
|
+
profiles.delete(subjectId);
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function renderProfileContext(profile) {
|
|
507
|
+
if (!profile || Object.keys(profile.traits).length === 0) return "";
|
|
508
|
+
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)}`);
|
|
509
|
+
if (lines.length === 0) return "";
|
|
510
|
+
return `## User profile
|
|
511
|
+
${lines.join("\n")}`;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/graph.ts
|
|
515
|
+
function createInMemoryGraph() {
|
|
516
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
517
|
+
const edges = /* @__PURE__ */ new Map();
|
|
518
|
+
const matchesNode = (node, query) => {
|
|
519
|
+
if (!query) return true;
|
|
520
|
+
if (query.kind && node.kind !== query.kind) return false;
|
|
521
|
+
return true;
|
|
522
|
+
};
|
|
523
|
+
const matchesEdge = (edge, query) => {
|
|
524
|
+
if (!query) return true;
|
|
525
|
+
if (query.label && edge.label !== query.label) return false;
|
|
526
|
+
if (query.from && edge.from !== query.from) return false;
|
|
527
|
+
if (query.to && edge.to !== query.to) return false;
|
|
528
|
+
return true;
|
|
529
|
+
};
|
|
530
|
+
return {
|
|
531
|
+
async upsertNode(node) {
|
|
532
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
533
|
+
const existing = nodes.get(node.id);
|
|
534
|
+
const merged = {
|
|
535
|
+
...existing ?? {},
|
|
536
|
+
...node,
|
|
537
|
+
createdAt: existing?.createdAt ?? now,
|
|
538
|
+
updatedAt: now
|
|
539
|
+
};
|
|
540
|
+
nodes.set(node.id, merged);
|
|
541
|
+
return merged;
|
|
542
|
+
},
|
|
543
|
+
async upsertEdge(edge) {
|
|
544
|
+
edges.set(edge.id, edge);
|
|
545
|
+
return edge;
|
|
546
|
+
},
|
|
547
|
+
async getNode(id) {
|
|
548
|
+
const hit = nodes.get(id);
|
|
549
|
+
return hit ? { ...hit } : null;
|
|
550
|
+
},
|
|
551
|
+
async findNodes(query) {
|
|
552
|
+
return Array.from(nodes.values()).filter((n) => matchesNode(n, query)).map((n) => ({ ...n }));
|
|
553
|
+
},
|
|
554
|
+
async findEdges(query) {
|
|
555
|
+
return Array.from(edges.values()).filter((e) => matchesEdge(e, query)).map((e) => ({ ...e }));
|
|
556
|
+
},
|
|
557
|
+
async neighbors(id, options = {}) {
|
|
558
|
+
const depth = Math.max(1, options.depth ?? 1);
|
|
559
|
+
const visited = /* @__PURE__ */ new Set([id]);
|
|
560
|
+
let frontier = /* @__PURE__ */ new Set([id]);
|
|
561
|
+
for (let i = 0; i < depth; i++) {
|
|
562
|
+
const next = /* @__PURE__ */ new Set();
|
|
563
|
+
for (const edge of edges.values()) {
|
|
564
|
+
if (options.label && edge.label !== options.label) continue;
|
|
565
|
+
if (frontier.has(edge.from) && !visited.has(edge.to)) next.add(edge.to);
|
|
566
|
+
if (frontier.has(edge.to) && !visited.has(edge.from)) next.add(edge.from);
|
|
567
|
+
}
|
|
568
|
+
for (const n of next) visited.add(n);
|
|
569
|
+
frontier = next;
|
|
570
|
+
if (next.size === 0) break;
|
|
571
|
+
}
|
|
572
|
+
visited.delete(id);
|
|
573
|
+
return Array.from(visited, (nid) => nodes.get(nid)).filter((n) => Boolean(n)).map((n) => ({ ...n }));
|
|
574
|
+
},
|
|
575
|
+
async deleteNode(id) {
|
|
576
|
+
nodes.delete(id);
|
|
577
|
+
for (const [eid, edge] of edges) {
|
|
578
|
+
if (edge.from === id || edge.to === id) edges.delete(eid);
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
async deleteEdge(id) {
|
|
582
|
+
edges.delete(id);
|
|
583
|
+
},
|
|
584
|
+
async clear() {
|
|
585
|
+
nodes.clear();
|
|
586
|
+
edges.clear();
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/vector/pgvector.ts
|
|
592
|
+
function formatVector(embedding) {
|
|
593
|
+
return `[${embedding.join(",")}]`;
|
|
594
|
+
}
|
|
595
|
+
function pgvector(config) {
|
|
596
|
+
const table = config.table ?? "agentskit_vectors";
|
|
597
|
+
const defaultTopK = Math.max(1, config.topK ?? 10);
|
|
598
|
+
return {
|
|
599
|
+
async store(docs) {
|
|
600
|
+
if (docs.length === 0) return;
|
|
601
|
+
const values = [];
|
|
602
|
+
const placeholders = docs.map((doc, i) => {
|
|
603
|
+
const base = i * 4;
|
|
604
|
+
values.push(doc.id, doc.content, formatVector(doc.embedding), JSON.stringify(doc.metadata ?? {}));
|
|
605
|
+
return `($${base + 1}, $${base + 2}, $${base + 3}::vector, $${base + 4}::jsonb)`;
|
|
606
|
+
}).join(", ");
|
|
607
|
+
await config.runner.query(
|
|
608
|
+
`INSERT INTO ${table} (id, content, embedding, metadata) VALUES ${placeholders}
|
|
609
|
+
ON CONFLICT (id) DO UPDATE SET content = EXCLUDED.content, embedding = EXCLUDED.embedding, metadata = EXCLUDED.metadata`,
|
|
610
|
+
values
|
|
611
|
+
);
|
|
612
|
+
},
|
|
613
|
+
async search(embedding, options = {}) {
|
|
614
|
+
const topK = options.topK ?? defaultTopK;
|
|
615
|
+
const threshold = options.threshold ?? 0;
|
|
616
|
+
const { rows } = await config.runner.query(
|
|
617
|
+
`SELECT id, content, metadata, (embedding <=> $1::vector) AS distance
|
|
618
|
+
FROM ${table}
|
|
619
|
+
ORDER BY embedding <=> $1::vector
|
|
620
|
+
LIMIT $2`,
|
|
621
|
+
[formatVector(embedding), topK]
|
|
622
|
+
);
|
|
623
|
+
return rows.map((r) => ({
|
|
624
|
+
id: r.id,
|
|
625
|
+
content: r.content,
|
|
626
|
+
metadata: r.metadata ?? void 0,
|
|
627
|
+
score: 1 - r.distance
|
|
628
|
+
})).filter((r) => (r.score ?? 0) >= threshold);
|
|
629
|
+
},
|
|
630
|
+
async delete(ids) {
|
|
631
|
+
if (ids.length === 0) return;
|
|
632
|
+
const placeholders = ids.map((_, i) => `$${i + 1}`).join(",");
|
|
633
|
+
await config.runner.query(`DELETE FROM ${table} WHERE id IN (${placeholders})`, ids);
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/vector/pinecone.ts
|
|
639
|
+
async function call(config, path, body) {
|
|
640
|
+
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
641
|
+
const response = await fetchImpl(`${config.indexUrl}${path}`, {
|
|
642
|
+
method: "POST",
|
|
643
|
+
headers: {
|
|
644
|
+
"content-type": "application/json",
|
|
645
|
+
"api-key": config.apiKey
|
|
646
|
+
},
|
|
647
|
+
body: JSON.stringify(body)
|
|
648
|
+
});
|
|
649
|
+
const text = await response.text();
|
|
650
|
+
if (!response.ok) throw new Error(`pinecone ${response.status}: ${text.slice(0, 200)}`);
|
|
651
|
+
return text.length > 0 ? JSON.parse(text) : {};
|
|
652
|
+
}
|
|
653
|
+
function pinecone(config) {
|
|
654
|
+
const defaultTopK = Math.max(1, config.topK ?? 10);
|
|
655
|
+
const namespace = config.namespace ?? "";
|
|
656
|
+
return {
|
|
657
|
+
async store(docs) {
|
|
658
|
+
if (docs.length === 0) return;
|
|
659
|
+
await call(config, "/vectors/upsert", {
|
|
660
|
+
namespace,
|
|
661
|
+
vectors: docs.map((d) => ({
|
|
662
|
+
id: d.id,
|
|
663
|
+
values: d.embedding,
|
|
664
|
+
metadata: { content: d.content, ...d.metadata ?? {} }
|
|
665
|
+
}))
|
|
666
|
+
});
|
|
667
|
+
},
|
|
668
|
+
async search(embedding, options = {}) {
|
|
669
|
+
const topK = options.topK ?? defaultTopK;
|
|
670
|
+
const threshold = options.threshold ?? 0;
|
|
671
|
+
const result = await call(config, "/query", {
|
|
672
|
+
namespace,
|
|
673
|
+
topK,
|
|
674
|
+
vector: embedding,
|
|
675
|
+
includeMetadata: true
|
|
676
|
+
});
|
|
677
|
+
return (result.matches ?? []).filter((m) => m.score >= threshold).map((m) => ({
|
|
678
|
+
id: m.id,
|
|
679
|
+
content: String((m.metadata ?? {}).content ?? ""),
|
|
680
|
+
metadata: m.metadata,
|
|
681
|
+
score: m.score
|
|
682
|
+
}));
|
|
683
|
+
},
|
|
684
|
+
async delete(ids) {
|
|
685
|
+
if (ids.length === 0) return;
|
|
686
|
+
await call(config, "/vectors/delete", { namespace, ids });
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// src/vector/qdrant.ts
|
|
692
|
+
async function call2(config, method, path, body) {
|
|
693
|
+
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
694
|
+
const response = await fetchImpl(`${config.url}${path}`, {
|
|
695
|
+
method,
|
|
696
|
+
headers: {
|
|
697
|
+
"content-type": "application/json",
|
|
698
|
+
...config.apiKey ? { "api-key": config.apiKey } : {}
|
|
699
|
+
},
|
|
700
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
701
|
+
});
|
|
702
|
+
const text = await response.text();
|
|
703
|
+
if (!response.ok) throw new Error(`qdrant ${response.status}: ${text.slice(0, 200)}`);
|
|
704
|
+
return text.length > 0 ? JSON.parse(text) : {};
|
|
705
|
+
}
|
|
706
|
+
function qdrant(config) {
|
|
707
|
+
const defaultTopK = Math.max(1, config.topK ?? 10);
|
|
708
|
+
return {
|
|
709
|
+
async store(docs) {
|
|
710
|
+
if (docs.length === 0) return;
|
|
711
|
+
await call2(config, "PUT", `/collections/${config.collection}/points`, {
|
|
712
|
+
points: docs.map((d) => ({
|
|
713
|
+
id: d.id,
|
|
714
|
+
vector: d.embedding,
|
|
715
|
+
payload: { content: d.content, ...d.metadata ?? {} }
|
|
716
|
+
}))
|
|
717
|
+
});
|
|
718
|
+
},
|
|
719
|
+
async search(embedding, options = {}) {
|
|
720
|
+
const topK = options.topK ?? defaultTopK;
|
|
721
|
+
const threshold = options.threshold ?? 0;
|
|
722
|
+
const result = await call2(config, "POST", `/collections/${config.collection}/points/search`, {
|
|
723
|
+
vector: embedding,
|
|
724
|
+
limit: topK,
|
|
725
|
+
with_payload: true
|
|
726
|
+
});
|
|
727
|
+
return (result.result ?? []).filter((m) => m.score >= threshold).map((m) => ({
|
|
728
|
+
id: String(m.id),
|
|
729
|
+
content: String((m.payload ?? {}).content ?? ""),
|
|
730
|
+
metadata: m.payload,
|
|
731
|
+
score: m.score
|
|
732
|
+
}));
|
|
733
|
+
},
|
|
734
|
+
async delete(ids) {
|
|
735
|
+
if (ids.length === 0) return;
|
|
736
|
+
await call2(config, "POST", `/collections/${config.collection}/points/delete`, {
|
|
737
|
+
points: ids
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/vector/chroma.ts
|
|
744
|
+
async function call3(config, method, path, body) {
|
|
745
|
+
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
746
|
+
const response = await fetchImpl(`${config.url}${path}`, {
|
|
747
|
+
method,
|
|
748
|
+
headers: { "content-type": "application/json" },
|
|
749
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
750
|
+
});
|
|
751
|
+
const text = await response.text();
|
|
752
|
+
if (!response.ok) throw new Error(`chroma ${response.status}: ${text.slice(0, 200)}`);
|
|
753
|
+
return text.length > 0 ? JSON.parse(text) : {};
|
|
754
|
+
}
|
|
755
|
+
function chroma(config) {
|
|
756
|
+
const defaultTopK = Math.max(1, config.topK ?? 10);
|
|
757
|
+
return {
|
|
758
|
+
async store(docs) {
|
|
759
|
+
if (docs.length === 0) return;
|
|
760
|
+
await call3(config, "POST", `/api/v1/collections/${config.collection}/upsert`, {
|
|
761
|
+
ids: docs.map((d) => d.id),
|
|
762
|
+
embeddings: docs.map((d) => d.embedding),
|
|
763
|
+
documents: docs.map((d) => d.content),
|
|
764
|
+
metadatas: docs.map((d) => d.metadata ?? {})
|
|
765
|
+
});
|
|
766
|
+
},
|
|
767
|
+
async search(embedding, options = {}) {
|
|
768
|
+
const topK = options.topK ?? defaultTopK;
|
|
769
|
+
const threshold = options.threshold ?? 0;
|
|
770
|
+
const result = await call3(config, "POST", `/api/v1/collections/${config.collection}/query`, {
|
|
771
|
+
query_embeddings: [embedding],
|
|
772
|
+
n_results: topK
|
|
773
|
+
});
|
|
774
|
+
const ids = result.ids?.[0] ?? [];
|
|
775
|
+
const documents = result.documents?.[0] ?? [];
|
|
776
|
+
const metadatas = result.metadatas?.[0] ?? [];
|
|
777
|
+
const distances = result.distances?.[0] ?? [];
|
|
778
|
+
return ids.map((id, i) => ({
|
|
779
|
+
id,
|
|
780
|
+
content: documents[i] ?? "",
|
|
781
|
+
metadata: metadatas[i],
|
|
782
|
+
score: distances[i] !== void 0 ? 1 - distances[i] : 0
|
|
783
|
+
})).filter((r) => (r.score ?? 0) >= threshold);
|
|
784
|
+
},
|
|
785
|
+
async delete(ids) {
|
|
786
|
+
if (ids.length === 0) return;
|
|
787
|
+
await call3(config, "POST", `/api/v1/collections/${config.collection}/delete`, { ids });
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// src/vector/upstash.ts
|
|
793
|
+
async function call4(config, path, body) {
|
|
794
|
+
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
795
|
+
const response = await fetchImpl(`${config.url}${path}`, {
|
|
796
|
+
method: "POST",
|
|
797
|
+
headers: {
|
|
798
|
+
"content-type": "application/json",
|
|
799
|
+
authorization: `Bearer ${config.token}`
|
|
800
|
+
},
|
|
801
|
+
body: JSON.stringify(body)
|
|
802
|
+
});
|
|
803
|
+
const text = await response.text();
|
|
804
|
+
if (!response.ok) throw new Error(`upstash-vector ${response.status}: ${text.slice(0, 200)}`);
|
|
805
|
+
return text.length > 0 ? JSON.parse(text) : {};
|
|
806
|
+
}
|
|
807
|
+
function upstashVector(config) {
|
|
808
|
+
const defaultTopK = Math.max(1, config.topK ?? 10);
|
|
809
|
+
return {
|
|
810
|
+
async store(docs) {
|
|
811
|
+
if (docs.length === 0) return;
|
|
812
|
+
await call4(
|
|
813
|
+
config,
|
|
814
|
+
"/upsert",
|
|
815
|
+
docs.map((d) => ({
|
|
816
|
+
id: d.id,
|
|
817
|
+
vector: d.embedding,
|
|
818
|
+
metadata: { content: d.content, ...d.metadata ?? {} }
|
|
819
|
+
}))
|
|
820
|
+
);
|
|
821
|
+
},
|
|
822
|
+
async search(embedding, options = {}) {
|
|
823
|
+
const topK = options.topK ?? defaultTopK;
|
|
824
|
+
const threshold = options.threshold ?? 0;
|
|
825
|
+
const result = await call4(config, "/query", { vector: embedding, topK, includeMetadata: true });
|
|
826
|
+
return (result.result ?? []).filter((m) => m.score >= threshold).map((m) => ({
|
|
827
|
+
id: m.id,
|
|
828
|
+
content: String((m.metadata ?? {}).content ?? ""),
|
|
829
|
+
metadata: m.metadata,
|
|
830
|
+
score: m.score
|
|
831
|
+
}));
|
|
832
|
+
},
|
|
833
|
+
async delete(ids) {
|
|
834
|
+
if (ids.length === 0) return;
|
|
835
|
+
await call4(config, "/delete", { ids });
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// src/vector/supabase.ts
|
|
841
|
+
var cachedSdk2 = null;
|
|
842
|
+
async function loadSdk2() {
|
|
843
|
+
if (!cachedSdk2) {
|
|
844
|
+
cachedSdk2 = (async () => {
|
|
845
|
+
try {
|
|
846
|
+
const moduleId = "@supabase/supabase-js";
|
|
847
|
+
return await import(
|
|
848
|
+
/* @vite-ignore */
|
|
849
|
+
moduleId
|
|
850
|
+
);
|
|
851
|
+
} catch {
|
|
852
|
+
throw new Error(
|
|
853
|
+
"Install @supabase/supabase-js to use supabaseVectorStore: npm install @supabase/supabase-js"
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
})();
|
|
857
|
+
}
|
|
858
|
+
return cachedSdk2;
|
|
859
|
+
}
|
|
860
|
+
function buildRunner(client) {
|
|
861
|
+
return {
|
|
862
|
+
async query(sql, params) {
|
|
863
|
+
const result = await client.rpc("agentskit_execute_sql", { sql, params });
|
|
864
|
+
if (result.error) throw new Error(`supabase: ${result.error.message}`);
|
|
865
|
+
return { rows: result.data ?? [] };
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
function supabaseVectorStore(config) {
|
|
870
|
+
let runnerPromise = null;
|
|
871
|
+
const getRunner = () => {
|
|
872
|
+
if (!runnerPromise) {
|
|
873
|
+
runnerPromise = (async () => {
|
|
874
|
+
const sdk = await loadSdk2();
|
|
875
|
+
const client = sdk.createClient(config.url, config.serviceRoleKey);
|
|
876
|
+
return buildRunner(client);
|
|
877
|
+
})();
|
|
878
|
+
}
|
|
879
|
+
return runnerPromise;
|
|
880
|
+
};
|
|
881
|
+
let backend = null;
|
|
882
|
+
const getBackend = async () => {
|
|
883
|
+
if (!backend) {
|
|
884
|
+
const runner = await getRunner();
|
|
885
|
+
backend = pgvector({
|
|
886
|
+
runner,
|
|
887
|
+
table: config.table,
|
|
888
|
+
topK: config.topK
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
return backend;
|
|
892
|
+
};
|
|
893
|
+
return {
|
|
894
|
+
async store(docs) {
|
|
895
|
+
const b = await getBackend();
|
|
896
|
+
return b.store(docs);
|
|
897
|
+
},
|
|
898
|
+
async search(embedding, options) {
|
|
899
|
+
const b = await getBackend();
|
|
900
|
+
return b.search(embedding, options);
|
|
901
|
+
},
|
|
902
|
+
async delete(ids) {
|
|
903
|
+
const b = await getBackend();
|
|
904
|
+
return b.delete?.(ids);
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// src/encrypted.ts
|
|
910
|
+
function toBase64(bytes) {
|
|
911
|
+
if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
|
|
912
|
+
let binary = "";
|
|
913
|
+
for (const b of bytes) binary += String.fromCharCode(b);
|
|
914
|
+
return btoa(binary);
|
|
915
|
+
}
|
|
916
|
+
function fromBase64(value) {
|
|
917
|
+
if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64"));
|
|
918
|
+
const binary = atob(value);
|
|
919
|
+
const bytes = new Uint8Array(binary.length);
|
|
920
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
921
|
+
return bytes;
|
|
922
|
+
}
|
|
923
|
+
async function resolveKey(subtle, material) {
|
|
924
|
+
if ("type" in material && material.type === "secret") return material;
|
|
925
|
+
const raw = material;
|
|
926
|
+
if (raw.byteLength !== 32) {
|
|
927
|
+
throw new Error(`createEncryptedMemory: key must be 32 bytes (got ${raw.byteLength})`);
|
|
928
|
+
}
|
|
929
|
+
return subtle.importKey("raw", raw, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
|
|
930
|
+
}
|
|
931
|
+
async function createEncryptedMemory(options) {
|
|
932
|
+
const subtle = options.subtle ?? globalThis.crypto?.subtle;
|
|
933
|
+
const random = options.getRandomValues ?? ((v) => globalThis.crypto.getRandomValues(v));
|
|
934
|
+
if (!subtle) throw new Error("createEncryptedMemory: SubtleCrypto not available");
|
|
935
|
+
const key = await resolveKey(subtle, options.key);
|
|
936
|
+
const aad = options.aad;
|
|
937
|
+
const encrypt = async (plain) => {
|
|
938
|
+
const iv = random(new Uint8Array(12));
|
|
939
|
+
const data = new TextEncoder().encode(plain);
|
|
940
|
+
const params = aad ? { name: "AES-GCM", iv, additionalData: aad } : { name: "AES-GCM", iv };
|
|
941
|
+
const cipher = await subtle.encrypt(params, key, data);
|
|
942
|
+
return {
|
|
943
|
+
ciphertext: toBase64(new Uint8Array(cipher)),
|
|
944
|
+
iv: toBase64(iv),
|
|
945
|
+
length: plain.length
|
|
946
|
+
};
|
|
947
|
+
};
|
|
948
|
+
const decrypt = async (envelope) => {
|
|
949
|
+
const iv = fromBase64(envelope.iv);
|
|
950
|
+
const params = aad ? { name: "AES-GCM", iv, additionalData: aad } : { name: "AES-GCM", iv };
|
|
951
|
+
const plain = await subtle.decrypt(params, key, fromBase64(envelope.ciphertext));
|
|
952
|
+
return new TextDecoder().decode(plain);
|
|
953
|
+
};
|
|
954
|
+
const encryptMessage = async (m) => {
|
|
955
|
+
if (m.metadata?.agentskitEncrypted) return m;
|
|
956
|
+
const envelope = await encrypt(m.content ?? "");
|
|
957
|
+
return {
|
|
958
|
+
...m,
|
|
959
|
+
content: "",
|
|
960
|
+
metadata: {
|
|
961
|
+
...m.metadata ?? {},
|
|
962
|
+
agentskitEncrypted: true,
|
|
963
|
+
ciphertext: envelope.ciphertext,
|
|
964
|
+
iv: envelope.iv,
|
|
965
|
+
length: envelope.length
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
};
|
|
969
|
+
const decryptMessage = async (m) => {
|
|
970
|
+
if (!m.metadata?.agentskitEncrypted) return m;
|
|
971
|
+
const envelope = {
|
|
972
|
+
ciphertext: String(m.metadata.ciphertext),
|
|
973
|
+
iv: String(m.metadata.iv),
|
|
974
|
+
length: Number(m.metadata.length ?? 0)
|
|
975
|
+
};
|
|
976
|
+
const content = await decrypt(envelope);
|
|
977
|
+
const { agentskitEncrypted: _, ciphertext: __, iv: ___, length: ____, ...rest } = m.metadata;
|
|
978
|
+
return { ...m, content, metadata: Object.keys(rest).length > 0 ? rest : void 0 };
|
|
979
|
+
};
|
|
980
|
+
return {
|
|
981
|
+
async load() {
|
|
982
|
+
const stored = await options.backing.load();
|
|
983
|
+
return Promise.all(stored.map(decryptMessage));
|
|
984
|
+
},
|
|
985
|
+
async save(messages) {
|
|
986
|
+
const encrypted = await Promise.all(messages.map(encryptMessage));
|
|
987
|
+
await options.backing.save(encrypted);
|
|
988
|
+
},
|
|
989
|
+
async clear() {
|
|
990
|
+
await options.backing.clear?.();
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// src/hierarchical.ts
|
|
996
|
+
function mergeChronological(a, b) {
|
|
997
|
+
const out = [...a, ...b];
|
|
998
|
+
out.sort((x, y) => x.createdAt.getTime() - y.createdAt.getTime());
|
|
999
|
+
return out;
|
|
1000
|
+
}
|
|
1001
|
+
function createHierarchicalMemory(options) {
|
|
1002
|
+
const workingLimit = Math.max(1, options.workingLimit ?? 50);
|
|
1003
|
+
const recallTopK = Math.max(0, options.recallTopK ?? 5);
|
|
1004
|
+
const loadAll = async (source) => [...await source.load()];
|
|
1005
|
+
return {
|
|
1006
|
+
async load() {
|
|
1007
|
+
const hot = await loadAll(options.working);
|
|
1008
|
+
if (!options.recall || recallTopK === 0) return hot;
|
|
1009
|
+
let recalled = [];
|
|
1010
|
+
try {
|
|
1011
|
+
recalled = [...await options.recall.query({ working: hot, topK: recallTopK })];
|
|
1012
|
+
} catch {
|
|
1013
|
+
recalled = [];
|
|
1014
|
+
}
|
|
1015
|
+
const hotIds = new Set(hot.map((m) => m.id));
|
|
1016
|
+
return mergeChronological(
|
|
1017
|
+
recalled.filter((m) => !hotIds.has(m.id)),
|
|
1018
|
+
hot
|
|
1019
|
+
);
|
|
1020
|
+
},
|
|
1021
|
+
async save(messages) {
|
|
1022
|
+
const knownArchival = await loadAll(options.archival);
|
|
1023
|
+
const archivalIds = new Set(knownArchival.map((m) => m.id));
|
|
1024
|
+
const fresh = messages.filter((m) => !archivalIds.has(m.id));
|
|
1025
|
+
if (fresh.length > 0) {
|
|
1026
|
+
await options.archival.save(mergeChronological(knownArchival, fresh));
|
|
1027
|
+
}
|
|
1028
|
+
const tail = messages.slice(Math.max(0, messages.length - workingLimit));
|
|
1029
|
+
const overflow = messages.slice(0, Math.max(0, messages.length - workingLimit));
|
|
1030
|
+
await options.working.save(tail);
|
|
1031
|
+
if (options.recall) {
|
|
1032
|
+
const knownOverflow = overflow.filter((m) => fresh.some((f) => f.id === m.id));
|
|
1033
|
+
for (const m of knownOverflow) await options.recall.index(m);
|
|
1034
|
+
const appended = fresh.filter((m) => !tail.some((t) => t.id === m.id));
|
|
1035
|
+
for (const m of appended) await options.recall.index(m);
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
async clear() {
|
|
1039
|
+
await options.working.clear?.();
|
|
1040
|
+
await options.archival.clear?.();
|
|
1041
|
+
},
|
|
1042
|
+
async archival() {
|
|
1043
|
+
return loadAll(options.archival);
|
|
1044
|
+
},
|
|
1045
|
+
async working() {
|
|
1046
|
+
return loadAll(options.working);
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
exports.chroma = chroma;
|
|
1052
|
+
exports.createEncryptedMemory = createEncryptedMemory;
|
|
1053
|
+
exports.createHierarchicalMemory = createHierarchicalMemory;
|
|
1054
|
+
exports.createInMemoryGraph = createInMemoryGraph;
|
|
1055
|
+
exports.createInMemoryPersonalization = createInMemoryPersonalization;
|
|
369
1056
|
exports.fileChatMemory = fileChatMemory;
|
|
370
1057
|
exports.fileVectorMemory = fileVectorMemory;
|
|
1058
|
+
exports.matchesFilter = matchesFilter;
|
|
1059
|
+
exports.pgvector = pgvector;
|
|
1060
|
+
exports.pinecone = pinecone;
|
|
1061
|
+
exports.qdrant = qdrant;
|
|
371
1062
|
exports.redisChatMemory = redisChatMemory;
|
|
372
1063
|
exports.redisVectorMemory = redisVectorMemory;
|
|
1064
|
+
exports.renderProfileContext = renderProfileContext;
|
|
373
1065
|
exports.sqliteChatMemory = sqliteChatMemory;
|
|
1066
|
+
exports.supabaseVectorStore = supabaseVectorStore;
|
|
1067
|
+
exports.tursoChatMemory = tursoChatMemory;
|
|
1068
|
+
exports.upstashVector = upstashVector;
|
|
374
1069
|
//# sourceMappingURL=index.cjs.map
|
|
375
1070
|
//# sourceMappingURL=index.cjs.map
|