@nxgt/ai-memory 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +173 -2
- package/dist/actions.d.ts +9 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/chunks/index-082dnty9.js +188 -0
- package/dist/chunks/index-082dnty9.js.map +14 -0
- package/dist/chunks/index-vj24rcgc.js +119 -0
- package/dist/chunks/index-vj24rcgc.js.map +13 -0
- package/dist/compact.d.ts +38 -0
- package/dist/compact.d.ts.map +1 -0
- package/dist/contract-actions.d.ts +3 -0
- package/dist/contract-actions.d.ts.map +1 -0
- package/dist/contract-messages.d.ts +3 -0
- package/dist/contract-messages.d.ts.map +1 -0
- package/dist/contract-owner.d.ts +3 -0
- package/dist/contract-owner.d.ts.map +1 -0
- package/dist/contract-samples.d.ts +6 -0
- package/dist/contract-samples.d.ts.map +1 -0
- package/dist/contract-threads.d.ts +3 -0
- package/dist/contract-threads.d.ts.map +1 -0
- package/dist/contract.d.ts +9 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/guards.d.ts +5 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/in-process.d.ts +8 -0
- package/dist/in-process.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +167 -0
- package/dist/index.js.map +13 -0
- package/dist/messages.d.ts +7 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/mongo-actions.d.ts +8 -0
- package/dist/mongo-actions.d.ts.map +1 -0
- package/dist/mongo-collection.d.ts +32 -0
- package/dist/mongo-collection.d.ts.map +1 -0
- package/dist/mongo-ctx.d.ts +12 -0
- package/dist/mongo-ctx.d.ts.map +1 -0
- package/dist/mongo-fields.d.ts +41 -0
- package/dist/mongo-fields.d.ts.map +1 -0
- package/dist/mongo-map.d.ts +8 -0
- package/dist/mongo-map.d.ts.map +1 -0
- package/dist/mongo-messages.d.ts +7 -0
- package/dist/mongo-messages.d.ts.map +1 -0
- package/dist/mongo-store.d.ts +16 -0
- package/dist/mongo-store.d.ts.map +1 -0
- package/dist/mongo-threads.d.ts +13 -0
- package/dist/mongo-threads.d.ts.map +1 -0
- package/dist/mongo.d.ts +4 -0
- package/dist/mongo.d.ts.map +1 -0
- package/dist/mongo.js +271 -0
- package/dist/mongo.js.map +16 -0
- package/dist/paging.d.ts +8 -0
- package/dist/paging.d.ts.map +1 -0
- package/dist/pairs.d.ts +14 -0
- package/dist/pairs.d.ts.map +1 -0
- package/dist/redis-actions.d.ts +8 -0
- package/dist/redis-actions.d.ts.map +1 -0
- package/dist/redis-ctx.d.ts +37 -0
- package/dist/redis-ctx.d.ts.map +1 -0
- package/dist/redis-map.d.ts +13 -0
- package/dist/redis-map.d.ts.map +1 -0
- package/dist/redis-messages.d.ts +7 -0
- package/dist/redis-messages.d.ts.map +1 -0
- package/dist/redis-scripts.d.ts +40 -0
- package/dist/redis-scripts.d.ts.map +1 -0
- package/dist/redis-store.d.ts +21 -0
- package/dist/redis-store.d.ts.map +1 -0
- package/dist/redis-threads.d.ts +16 -0
- package/dist/redis-threads.d.ts.map +1 -0
- package/dist/redis.d.ts +3 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +373 -0
- package/dist/redis.js.map +16 -0
- package/dist/schemas.d.ts +124 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/state.d.ts +28 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/summarize.d.ts +11 -0
- package/dist/summarize.d.ts.map +1 -0
- package/dist/testing.d.ts +3 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +264 -0
- package/dist/testing.js.map +15 -0
- package/dist/threads.d.ts +7 -0
- package/dist/threads.d.ts.map +1 -0
- package/dist/truncate.d.ts +17 -0
- package/dist/truncate.d.ts.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -0
- package/docs/README.md +9 -0
- package/docs/guide/memory.md +327 -0
- package/docs/roadmap.md +31 -0
- package/docs/troubleshooting.md +171 -0
- package/package.json +85 -5
package/dist/mongo.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MemoryError2,
|
|
3
|
+
assertOwner,
|
|
4
|
+
notFound,
|
|
5
|
+
messageSchema2,
|
|
6
|
+
pageLimit,
|
|
7
|
+
assertUuidCursor
|
|
8
|
+
} from "./chunks/index-vj24rcgc.js";
|
|
9
|
+
|
|
10
|
+
// src/mongo-fields.ts
|
|
11
|
+
var threadFields = {
|
|
12
|
+
id: "_id",
|
|
13
|
+
ownerId: "ownerId",
|
|
14
|
+
title: "title",
|
|
15
|
+
meta: "meta",
|
|
16
|
+
messageCount: "messageCount",
|
|
17
|
+
summary: "summary",
|
|
18
|
+
createdAt: "createdAt",
|
|
19
|
+
updatedAt: "updatedAt",
|
|
20
|
+
expiresAt: "expiresAt"
|
|
21
|
+
};
|
|
22
|
+
var messageFields = {
|
|
23
|
+
id: "_id",
|
|
24
|
+
threadId: "threadId",
|
|
25
|
+
ownerId: "ownerId",
|
|
26
|
+
seq: "seq",
|
|
27
|
+
message: "message",
|
|
28
|
+
createdAt: "createdAt",
|
|
29
|
+
actions: "actions",
|
|
30
|
+
expiresAt: "expiresAt"
|
|
31
|
+
};
|
|
32
|
+
var threadIndexes = [
|
|
33
|
+
{ key: { ownerId: 1, _id: -1 } }
|
|
34
|
+
];
|
|
35
|
+
var messageIndexes = [
|
|
36
|
+
{ key: { ownerId: 1, threadId: 1, seq: 1 }, unique: true },
|
|
37
|
+
{ key: { ownerId: 1, "actions.id": 1 } }
|
|
38
|
+
];
|
|
39
|
+
var expiresAtIndex = {
|
|
40
|
+
key: { expiresAt: 1 },
|
|
41
|
+
expireAfterSeconds: 0
|
|
42
|
+
};
|
|
43
|
+
// src/mongo-actions.ts
|
|
44
|
+
import {
|
|
45
|
+
toProposedAction
|
|
46
|
+
} from "@nxgt/ai-core";
|
|
47
|
+
|
|
48
|
+
// src/mongo-ctx.ts
|
|
49
|
+
async function ownThread(c, id, ownerId) {
|
|
50
|
+
assertOwner(ownerId);
|
|
51
|
+
return c.threads.findOne({ _id: id, ownerId });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/mongo-map.ts
|
|
55
|
+
function toThread(doc) {
|
|
56
|
+
const { _id, expiresAt: _expiresAt, ...rest } = doc;
|
|
57
|
+
return { id: _id, ...rest };
|
|
58
|
+
}
|
|
59
|
+
function toStoredMessage(doc) {
|
|
60
|
+
return {
|
|
61
|
+
id: doc["_id"],
|
|
62
|
+
threadId: doc["threadId"],
|
|
63
|
+
seq: doc["seq"],
|
|
64
|
+
message: doc["message"],
|
|
65
|
+
createdAt: doc["createdAt"]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function pickAction(doc, actionId) {
|
|
69
|
+
const list = doc?.["actions"] ?? [];
|
|
70
|
+
return list.find((a) => a.id === actionId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/mongo-actions.ts
|
|
74
|
+
async function newActions(c, ownerId, list) {
|
|
75
|
+
const seen = new Set;
|
|
76
|
+
const result = [];
|
|
77
|
+
for (const message of list) {
|
|
78
|
+
const fresh = [];
|
|
79
|
+
for (const part of message.parts) {
|
|
80
|
+
if (part.type !== "proposed-action" || seen.has(part.id))
|
|
81
|
+
continue;
|
|
82
|
+
seen.add(part.id);
|
|
83
|
+
const known = await c.messages.findOne({
|
|
84
|
+
ownerId,
|
|
85
|
+
"actions.id": part.id
|
|
86
|
+
});
|
|
87
|
+
if (!known)
|
|
88
|
+
fresh.push(toProposedAction(part));
|
|
89
|
+
}
|
|
90
|
+
result.push(fresh);
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
async function findAction(c, actionId, ownerId) {
|
|
95
|
+
assertOwner(ownerId);
|
|
96
|
+
const doc = await c.messages.findOne({ ownerId, "actions.id": actionId });
|
|
97
|
+
return pickAction(doc, actionId);
|
|
98
|
+
}
|
|
99
|
+
async function updateAction(c, actionId, ownerId, patch) {
|
|
100
|
+
assertOwner(ownerId);
|
|
101
|
+
const doc = await c.messages.findOneAndUpdate({ ownerId, actions: { $elemMatch: { id: actionId, status: "pending" } } }, {
|
|
102
|
+
$set: {
|
|
103
|
+
"actions.$.status": patch.status,
|
|
104
|
+
...patch.result === undefined ? {} : { "actions.$.result": patch.result }
|
|
105
|
+
}
|
|
106
|
+
}, { returnDocument: "after" });
|
|
107
|
+
return pickAction(doc, actionId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/mongo-messages.ts
|
|
111
|
+
async function append(c, threadId, ownerId, list) {
|
|
112
|
+
assertOwner(ownerId);
|
|
113
|
+
const parsed = list.map((m) => messageSchema2.parse(m));
|
|
114
|
+
const at = c.now();
|
|
115
|
+
const before = await c.threads.findOneAndUpdate({ _id: threadId, ownerId }, {
|
|
116
|
+
$inc: { messageCount: parsed.length },
|
|
117
|
+
$set: { updatedAt: at, ...c.expiry(at) }
|
|
118
|
+
}, { returnDocument: "before" });
|
|
119
|
+
if (!before)
|
|
120
|
+
throw notFound(threadId);
|
|
121
|
+
const first = before["messageCount"] + 1;
|
|
122
|
+
const actions = await newActions(c, ownerId, parsed);
|
|
123
|
+
const docs = parsed.map((message, i) => ({
|
|
124
|
+
_id: Bun.randomUUIDv7(),
|
|
125
|
+
threadId,
|
|
126
|
+
ownerId,
|
|
127
|
+
seq: first + i,
|
|
128
|
+
message,
|
|
129
|
+
createdAt: at,
|
|
130
|
+
...(actions[i]?.length ?? 0) > 0 ? { actions: actions[i] } : {},
|
|
131
|
+
...c.expiry(at)
|
|
132
|
+
}));
|
|
133
|
+
if (docs.length > 0) {
|
|
134
|
+
await c.messages.insertMany(docs);
|
|
135
|
+
if (!await ownThread(c, threadId, ownerId)) {
|
|
136
|
+
await c.messages.deleteMany({ threadId, ownerId });
|
|
137
|
+
throw notFound(threadId);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
await slide(c, threadId, ownerId, at);
|
|
141
|
+
return docs.map(toStoredMessage);
|
|
142
|
+
}
|
|
143
|
+
async function slide(c, threadId, ownerId, at) {
|
|
144
|
+
const expiry = c.expiry(at);
|
|
145
|
+
if (Object.keys(expiry).length === 0)
|
|
146
|
+
return;
|
|
147
|
+
await c.messages.updateMany({ threadId, ownerId }, { $set: expiry });
|
|
148
|
+
}
|
|
149
|
+
async function load(c, threadId, ownerId, opts) {
|
|
150
|
+
const doc = await ownThread(c, threadId, ownerId);
|
|
151
|
+
if (!doc)
|
|
152
|
+
return;
|
|
153
|
+
const thread = toThread(doc);
|
|
154
|
+
let after = 0;
|
|
155
|
+
if (opts.sinceSummary && thread.summary) {
|
|
156
|
+
const through = await c.messages.findOne({
|
|
157
|
+
_id: thread.summary.throughMessageId,
|
|
158
|
+
threadId,
|
|
159
|
+
ownerId
|
|
160
|
+
});
|
|
161
|
+
after = through?.["seq"] ?? 0;
|
|
162
|
+
}
|
|
163
|
+
const docs = await c.messages.find({ threadId, ownerId, seq: { $gt: after } }).sort({ seq: 1 }).toArray();
|
|
164
|
+
return {
|
|
165
|
+
thread,
|
|
166
|
+
...thread.summary ? { summary: thread.summary } : {},
|
|
167
|
+
messages: docs.map(toStoredMessage)
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function setSummary(c, threadId, ownerId, input) {
|
|
171
|
+
if (!await ownThread(c, threadId, ownerId))
|
|
172
|
+
throw notFound(threadId);
|
|
173
|
+
const through = await c.messages.findOne({
|
|
174
|
+
_id: input.throughMessageId,
|
|
175
|
+
threadId,
|
|
176
|
+
ownerId
|
|
177
|
+
});
|
|
178
|
+
if (!through) {
|
|
179
|
+
throw new MemoryError2("MESSAGE_NOT_FOUND", `Message "${input.throughMessageId}" is not in thread "${threadId}".`);
|
|
180
|
+
}
|
|
181
|
+
const at = c.now();
|
|
182
|
+
const summary = {
|
|
183
|
+
text: input.text,
|
|
184
|
+
throughMessageId: input.throughMessageId,
|
|
185
|
+
tokens: input.tokens,
|
|
186
|
+
createdAt: at
|
|
187
|
+
};
|
|
188
|
+
const done = await c.threads.updateOne({ _id: threadId, ownerId }, { $set: { summary, updatedAt: at, ...c.expiry(at) } });
|
|
189
|
+
if (done.matchedCount === 0)
|
|
190
|
+
throw notFound(threadId);
|
|
191
|
+
await slide(c, threadId, ownerId, at);
|
|
192
|
+
return summary;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/mongo-threads.ts
|
|
196
|
+
async function createThread(c, input) {
|
|
197
|
+
assertOwner(input.ownerId);
|
|
198
|
+
const at = c.now();
|
|
199
|
+
const doc = {
|
|
200
|
+
_id: Bun.randomUUIDv7(),
|
|
201
|
+
ownerId: input.ownerId,
|
|
202
|
+
...input.title === undefined ? {} : { title: input.title },
|
|
203
|
+
...input.meta === undefined ? {} : { meta: input.meta },
|
|
204
|
+
messageCount: 0,
|
|
205
|
+
createdAt: at,
|
|
206
|
+
updatedAt: at,
|
|
207
|
+
...c.expiry(at)
|
|
208
|
+
};
|
|
209
|
+
await c.threads.insertOne(doc);
|
|
210
|
+
return toThread(doc);
|
|
211
|
+
}
|
|
212
|
+
async function getThread(c, id, ownerId) {
|
|
213
|
+
const doc = await ownThread(c, id, ownerId);
|
|
214
|
+
return doc ? toThread(doc) : undefined;
|
|
215
|
+
}
|
|
216
|
+
async function listThreads(c, ownerId, page) {
|
|
217
|
+
assertOwner(ownerId);
|
|
218
|
+
const limit = pageLimit(page);
|
|
219
|
+
assertUuidCursor(page.cursor);
|
|
220
|
+
const docs = await c.threads.find({
|
|
221
|
+
ownerId,
|
|
222
|
+
...page.cursor === undefined ? {} : { _id: { $lt: page.cursor } }
|
|
223
|
+
}).sort({ _id: -1 }).limit(limit + 1).toArray();
|
|
224
|
+
const items = docs.slice(0, limit);
|
|
225
|
+
const last = items[items.length - 1];
|
|
226
|
+
return {
|
|
227
|
+
items: items.map(toThread),
|
|
228
|
+
...docs.length > limit && last ? { nextCursor: last["_id"] } : {}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
async function deleteThread(c, id, ownerId) {
|
|
232
|
+
if (!await ownThread(c, id, ownerId))
|
|
233
|
+
return false;
|
|
234
|
+
await c.messages.deleteMany({ threadId: id, ownerId });
|
|
235
|
+
await c.threads.deleteOne({ _id: id, ownerId });
|
|
236
|
+
await c.messages.deleteMany({ threadId: id, ownerId });
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/mongo-store.ts
|
|
241
|
+
function mongoMemory(options) {
|
|
242
|
+
const ttl = options.expireAfterSeconds;
|
|
243
|
+
const c = {
|
|
244
|
+
threads: options.threads,
|
|
245
|
+
messages: options.messages,
|
|
246
|
+
now: options.now ?? Date.now,
|
|
247
|
+
expiry: (at) => ttl === undefined ? {} : { expiresAt: new Date(at + ttl * 1000) }
|
|
248
|
+
};
|
|
249
|
+
return {
|
|
250
|
+
createThread: (input) => createThread(c, input),
|
|
251
|
+
getThread: (id, ownerId) => getThread(c, id, ownerId),
|
|
252
|
+
listThreads: (ownerId, page = {}) => listThreads(c, ownerId, page),
|
|
253
|
+
append: (id, ownerId, list) => append(c, id, ownerId, list),
|
|
254
|
+
load: (id, ownerId, opts) => load(c, id, ownerId, opts),
|
|
255
|
+
setSummary: (id, ownerId, input) => setSummary(c, id, ownerId, input),
|
|
256
|
+
findAction: (id, ownerId) => findAction(c, id, ownerId),
|
|
257
|
+
updateAction: (id, ownerId, patch) => updateAction(c, id, ownerId, patch),
|
|
258
|
+
deleteThread: (id, ownerId) => deleteThread(c, id, ownerId)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
export {
|
|
262
|
+
expiresAtIndex,
|
|
263
|
+
messageFields,
|
|
264
|
+
messageIndexes,
|
|
265
|
+
mongoMemory,
|
|
266
|
+
threadFields,
|
|
267
|
+
threadIndexes
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
//# debugId=96A98DAE51319F1364756E2164756E21
|
|
271
|
+
//# sourceMappingURL=mongo.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/mongo-fields.ts", "../src/mongo-actions.ts", "../src/mongo-ctx.ts", "../src/mongo-map.ts", "../src/mongo-messages.ts", "../src/mongo-threads.ts", "../src/mongo-store.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * The fields the store writes on a thread document. `_id` is the thread id.\n * `expiresAt` is a `Date`, set only with `expireAfterSeconds`.\n */\nexport const threadFields = {\n\tid: '_id',\n\townerId: 'ownerId',\n\ttitle: 'title',\n\tmeta: 'meta',\n\tmessageCount: 'messageCount',\n\tsummary: 'summary',\n\tcreatedAt: 'createdAt',\n\tupdatedAt: 'updatedAt',\n\texpiresAt: 'expiresAt',\n} as const;\n\n/**\n * The fields the store writes on a message document. `_id` is the message id;\n * `actions` holds the state of the proposed actions the message carries.\n */\nexport const messageFields = {\n\tid: '_id',\n\tthreadId: 'threadId',\n\townerId: 'ownerId',\n\tseq: 'seq',\n\tmessage: 'message',\n\tcreatedAt: 'createdAt',\n\tactions: 'actions',\n\texpiresAt: 'expiresAt',\n} as const;\n\nexport interface MongoIndex {\n\tkey: Record<string, 1 | -1>;\n\tunique?: boolean;\n\texpireAfterSeconds?: number;\n}\n\n/** Indexes to create on the threads collection (`createIndex(key, options)`). */\nexport const threadIndexes: readonly MongoIndex[] = [\n\t{ key: { ownerId: 1, _id: -1 } },\n];\n\n/** Indexes to create on the messages collection. */\nexport const messageIndexes: readonly MongoIndex[] = [\n\t{ key: { ownerId: 1, threadId: 1, seq: 1 }, unique: true },\n\t{ key: { ownerId: 1, 'actions.id': 1 } },\n];\n\n/** The TTL index, on both collections, when `expireAfterSeconds` is set. */\nexport const expiresAtIndex: MongoIndex = {\n\tkey: { expiresAt: 1 },\n\texpireAfterSeconds: 0,\n};\n",
|
|
6
|
+
"import {\n\ttype Message,\n\ttype ProposedAction,\n\ttoProposedAction,\n} from '@nxgt/ai-core';\nimport { assertOwner, type Ctx } from './mongo-ctx';\nimport { pickAction } from './mongo-map';\nimport type { ActionPatch } from './types';\n\n/** Per message, the actions it proposes that this owner has not recorded yet. */\nexport async function newActions(\n\tc: Ctx,\n\townerId: string,\n\tlist: readonly Message[],\n): Promise<ProposedAction[][]> {\n\tconst seen = new Set<string>();\n\tconst result: ProposedAction[][] = [];\n\tfor (const message of list) {\n\t\tconst fresh: ProposedAction[] = [];\n\t\tfor (const part of message.parts) {\n\t\t\tif (part.type !== 'proposed-action' || seen.has(part.id)) continue;\n\t\t\tseen.add(part.id);\n\t\t\tconst known = await c.messages.findOne({\n\t\t\t\townerId,\n\t\t\t\t'actions.id': part.id,\n\t\t\t});\n\t\t\tif (!known) fresh.push(toProposedAction(part));\n\t\t}\n\t\tresult.push(fresh);\n\t}\n\treturn result;\n}\n\nexport async function findAction(c: Ctx, actionId: string, ownerId: string) {\n\tassertOwner(ownerId);\n\tconst doc = await c.messages.findOne({ ownerId, 'actions.id': actionId });\n\treturn pickAction(doc, actionId);\n}\n\nexport async function updateAction(\n\tc: Ctx,\n\tactionId: string,\n\townerId: string,\n\tpatch: ActionPatch,\n) {\n\tassertOwner(ownerId);\n\t// One atomic write: it matches only while the action is still pending.\n\tconst doc = await c.messages.findOneAndUpdate(\n\t\t{ ownerId, actions: { $elemMatch: { id: actionId, status: 'pending' } } },\n\t\t{\n\t\t\t$set: {\n\t\t\t\t'actions.$.status': patch.status,\n\t\t\t\t...(patch.result === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { 'actions.$.result': patch.result }),\n\t\t\t},\n\t\t},\n\t\t{ returnDocument: 'after' },\n\t);\n\treturn pickAction(doc, actionId);\n}\n",
|
|
7
|
+
"import { assertOwner } from './guards';\nimport type { MongoCollection, MongoDoc } from './mongo-collection';\n\n/** What the functions of the sibling files share. */\nexport interface Ctx {\n\tthreads: MongoCollection;\n\tmessages: MongoCollection;\n\tnow: () => number;\n\t/** `{ expiresAt }` for a write at `at`, or nothing without a TTL. */\n\texpiry: (at: number) => MongoDoc;\n}\n\nexport { assertOwner, notFound } from './guards';\n\nexport async function ownThread(c: Ctx, id: string, ownerId: string) {\n\tassertOwner(ownerId);\n\treturn c.threads.findOne({ _id: id, ownerId });\n}\n",
|
|
8
|
+
"import type { ProposedAction } from '@nxgt/ai-core';\nimport type { MongoDoc } from './mongo-collection';\nimport type { StoredMessage, Thread } from './types';\n\n/** A thread document as the application reads it: no storage-only field. */\nexport function toThread(doc: MongoDoc): Thread {\n\tconst { _id, expiresAt: _expiresAt, ...rest } = doc;\n\treturn { id: _id, ...rest } as Thread;\n}\n\nexport function toStoredMessage(doc: MongoDoc): StoredMessage {\n\treturn {\n\t\tid: doc['_id'],\n\t\tthreadId: doc['threadId'],\n\t\tseq: doc['seq'],\n\t\tmessage: doc['message'],\n\t\tcreatedAt: doc['createdAt'],\n\t} as StoredMessage;\n}\n\nexport function pickAction(\n\tdoc: MongoDoc | null,\n\tactionId: string,\n): ProposedAction | undefined {\n\tconst list = (doc?.['actions'] ?? []) as ProposedAction[];\n\treturn list.find((a) => a.id === actionId);\n}\n",
|
|
9
|
+
"import type { Message } from '@nxgt/ai-core';\nimport { MemoryError } from './errors';\nimport { newActions } from './mongo-actions';\nimport type { MongoDoc } from './mongo-collection';\nimport { assertOwner, type Ctx, notFound, ownThread } from './mongo-ctx';\nimport { toStoredMessage, toThread } from './mongo-map';\nimport { messageSchema } from './schemas';\nimport type {\n\tLoadedThread,\n\tLoadOptions,\n\tStoredMessage,\n\tSummaryInput,\n\tThreadSummary,\n} from './types';\n\nexport async function append(\n\tc: Ctx,\n\tthreadId: string,\n\townerId: string,\n\tlist: readonly Message[],\n): Promise<StoredMessage[]> {\n\tassertOwner(ownerId);\n\t// Parsing strips unknown keys; schemas.types.spec.ts guards the cast.\n\tconst parsed = list.map((m) => messageSchema.parse(m) as Message);\n\tconst at = c.now();\n\t// The $inc reserves the seq range, so concurrent appends never share one.\n\tconst before = await c.threads.findOneAndUpdate(\n\t\t{ _id: threadId, ownerId },\n\t\t{\n\t\t\t$inc: { messageCount: parsed.length },\n\t\t\t$set: { updatedAt: at, ...c.expiry(at) },\n\t\t},\n\t\t{ returnDocument: 'before' },\n\t);\n\tif (!before) throw notFound(threadId);\n\tconst first = (before['messageCount'] as number) + 1;\n\tconst actions = await newActions(c, ownerId, parsed);\n\tconst docs = parsed.map(\n\t\t(message, i): MongoDoc => ({\n\t\t\t_id: Bun.randomUUIDv7(),\n\t\t\tthreadId,\n\t\t\townerId,\n\t\t\tseq: first + i,\n\t\t\tmessage,\n\t\t\tcreatedAt: at,\n\t\t\t...((actions[i]?.length ?? 0) > 0 ? { actions: actions[i] } : {}),\n\t\t\t...c.expiry(at),\n\t\t}),\n\t);\n\tif (docs.length > 0) {\n\t\tawait c.messages.insertMany(docs);\n\t\t// A delete that ran meanwhile cannot see this batch: drop it.\n\t\tif (!(await ownThread(c, threadId, ownerId))) {\n\t\t\tawait c.messages.deleteMany({ threadId, ownerId });\n\t\t\tthrow notFound(threadId);\n\t\t}\n\t}\n\tawait slide(c, threadId, ownerId, at);\n\treturn docs.map(toStoredMessage);\n}\n\n/**\n * A thread expires as one unit: every write moves all its messages to the\n * thread's `expiresAt`, so the oldest never expire first.\n */\nasync function slide(c: Ctx, threadId: string, ownerId: string, at: number) {\n\tconst expiry = c.expiry(at);\n\tif (Object.keys(expiry).length === 0) return;\n\tawait c.messages.updateMany({ threadId, ownerId }, { $set: expiry });\n}\n\nexport async function load(\n\tc: Ctx,\n\tthreadId: string,\n\townerId: string,\n\topts: LoadOptions,\n): Promise<LoadedThread | undefined> {\n\tconst doc = await ownThread(c, threadId, ownerId);\n\tif (!doc) return undefined;\n\tconst thread = toThread(doc);\n\tlet after = 0;\n\tif (opts.sinceSummary && thread.summary) {\n\t\tconst through = await c.messages.findOne({\n\t\t\t_id: thread.summary.throughMessageId,\n\t\t\tthreadId,\n\t\t\townerId,\n\t\t});\n\t\tafter = (through?.['seq'] as number | undefined) ?? 0;\n\t}\n\tconst docs = await c.messages\n\t\t.find({ threadId, ownerId, seq: { $gt: after } })\n\t\t.sort({ seq: 1 })\n\t\t.toArray();\n\treturn {\n\t\tthread,\n\t\t...(thread.summary ? { summary: thread.summary } : {}),\n\t\tmessages: docs.map(toStoredMessage),\n\t};\n}\n\nexport async function setSummary(\n\tc: Ctx,\n\tthreadId: string,\n\townerId: string,\n\tinput: SummaryInput,\n): Promise<ThreadSummary> {\n\tif (!(await ownThread(c, threadId, ownerId))) throw notFound(threadId);\n\tconst through = await c.messages.findOne({\n\t\t_id: input.throughMessageId,\n\t\tthreadId,\n\t\townerId,\n\t});\n\tif (!through) {\n\t\tthrow new MemoryError(\n\t\t\t'MESSAGE_NOT_FOUND',\n\t\t\t`Message \"${input.throughMessageId}\" is not in thread \"${threadId}\".`,\n\t\t);\n\t}\n\tconst at = c.now();\n\tconst summary: ThreadSummary = {\n\t\ttext: input.text,\n\t\tthroughMessageId: input.throughMessageId,\n\t\ttokens: input.tokens,\n\t\tcreatedAt: at,\n\t};\n\tconst done = await c.threads.updateOne(\n\t\t{ _id: threadId, ownerId },\n\t\t{ $set: { summary, updatedAt: at, ...c.expiry(at) } },\n\t);\n\tif (done.matchedCount === 0) throw notFound(threadId);\n\tawait slide(c, threadId, ownerId, at);\n\treturn summary;\n}\n",
|
|
10
|
+
"import type { MongoDoc } from './mongo-collection';\nimport { assertOwner, type Ctx, ownThread } from './mongo-ctx';\nimport { toThread } from './mongo-map';\nimport { assertUuidCursor, pageLimit } from './paging';\nimport type { CreateThreadInput, Page, Thread, ThreadPage } from './types';\n\nexport async function createThread(\n\tc: Ctx,\n\tinput: CreateThreadInput,\n): Promise<Thread> {\n\tassertOwner(input.ownerId);\n\tconst at = c.now();\n\tconst doc: MongoDoc = {\n\t\t_id: Bun.randomUUIDv7(),\n\t\townerId: input.ownerId,\n\t\t...(input.title === undefined ? {} : { title: input.title }),\n\t\t...(input.meta === undefined ? {} : { meta: input.meta }),\n\t\tmessageCount: 0,\n\t\tcreatedAt: at,\n\t\tupdatedAt: at,\n\t\t...c.expiry(at),\n\t};\n\tawait c.threads.insertOne(doc);\n\treturn toThread(doc);\n}\n\nexport async function getThread(c: Ctx, id: string, ownerId: string) {\n\tconst doc = await ownThread(c, id, ownerId);\n\treturn doc ? toThread(doc) : undefined;\n}\n\nexport async function listThreads(\n\tc: Ctx,\n\townerId: string,\n\tpage: Page,\n): Promise<ThreadPage> {\n\tassertOwner(ownerId);\n\tconst limit = pageLimit(page);\n\tassertUuidCursor(page.cursor);\n\tconst docs = await c.threads\n\t\t.find({\n\t\t\townerId,\n\t\t\t...(page.cursor === undefined ? {} : { _id: { $lt: page.cursor } }),\n\t\t})\n\t\t.sort({ _id: -1 })\n\t\t.limit(limit + 1)\n\t\t.toArray();\n\tconst items = docs.slice(0, limit);\n\tconst last = items[items.length - 1];\n\treturn {\n\t\titems: items.map(toThread),\n\t\t...(docs.length > limit && last\n\t\t\t? { nextCursor: last['_id'] as string }\n\t\t\t: {}),\n\t};\n}\n\n/**\n * Messages, the thread, then the messages again. A crash in between leaves an\n * empty thread that a retry removes. An `append` racing the delete either\n * inserts before the last sweep (removed here) or checks the thread after its\n * insert and removes its own batch.\n */\nexport async function deleteThread(c: Ctx, id: string, ownerId: string) {\n\tif (!(await ownThread(c, id, ownerId))) return false;\n\tawait c.messages.deleteMany({ threadId: id, ownerId });\n\tawait c.threads.deleteOne({ _id: id, ownerId });\n\tawait c.messages.deleteMany({ threadId: id, ownerId });\n\treturn true;\n}\n",
|
|
11
|
+
"import * as actions from './mongo-actions';\nimport type { MongoCollection } from './mongo-collection';\nimport type { Ctx } from './mongo-ctx';\nimport * as messages from './mongo-messages';\nimport * as threads from './mongo-threads';\nimport type { MemoryStore } from './types';\n\nexport interface MongoMemoryOptions {\n\tthreads: MongoCollection;\n\tmessages: MongoCollection;\n\t/**\n\t * Documents expire this long after their last write (a thread) or their\n\t * insert (a message); needs `expiresAtIndex` on both collections.\n\t */\n\texpireAfterSeconds?: number;\n\t/** The clock, in epoch ms; default `Date.now`. */\n\tnow?: () => number;\n}\n\n/** A store over two MongoDB collections; see the guide for indexes and TTL. */\nexport function mongoMemory(options: MongoMemoryOptions): MemoryStore {\n\tconst ttl = options.expireAfterSeconds;\n\tconst c: Ctx = {\n\t\tthreads: options.threads,\n\t\tmessages: options.messages,\n\t\tnow: options.now ?? Date.now,\n\t\texpiry: (at) =>\n\t\t\tttl === undefined ? {} : { expiresAt: new Date(at + ttl * 1000) },\n\t};\n\treturn {\n\t\tcreateThread: (input) => threads.createThread(c, input),\n\t\tgetThread: (id, ownerId) => threads.getThread(c, id, ownerId),\n\t\tlistThreads: (ownerId, page = {}) => threads.listThreads(c, ownerId, page),\n\t\tappend: (id, ownerId, list) => messages.append(c, id, ownerId, list),\n\t\tload: (id, ownerId, opts) => messages.load(c, id, ownerId, opts),\n\t\tsetSummary: (id, ownerId, input) =>\n\t\t\tmessages.setSummary(c, id, ownerId, input),\n\t\tfindAction: (id, ownerId) => actions.findAction(c, id, ownerId),\n\t\tupdateAction: (id, ownerId, patch) =>\n\t\t\tactions.updateAction(c, id, ownerId, patch),\n\t\tdeleteThread: (id, ownerId) => threads.deleteThread(c, id, ownerId),\n\t};\n}\n"
|
|
12
|
+
],
|
|
13
|
+
"mappings": "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",
|
|
14
|
+
"debugId": "96A98DAE51319F1364756E2164756E21",
|
|
15
|
+
"names": []
|
|
16
|
+
}
|
package/dist/paging.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { MemoryError } from './errors.js';
|
|
2
|
+
import type { Page } from './types.js';
|
|
3
|
+
/** The page size, clamped to 1..100 and 20 by default. */
|
|
4
|
+
export declare const pageLimit: (page: Page) => number;
|
|
5
|
+
export declare const invalidCursor: (cursor: string) => MemoryError;
|
|
6
|
+
/** For the stores whose cursor is a thread id: throws on any other string. */
|
|
7
|
+
export declare function assertUuidCursor(cursor: string | undefined): void;
|
|
8
|
+
//# sourceMappingURL=paging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paging.d.ts","sourceRoot":"","sources":["../src/paging.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAOpC,0DAA0D;AAC1D,eAAO,MAAM,SAAS,GAAI,MAAM,IAAI,KAAG,MACuB,CAAC;AAE/D,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,KAAG,WAI7C,CAAC;AAEH,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAEjE"}
|
package/dist/pairs.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { StoredMessage } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The first index a fold may keep from: `from`, moved back until no kept tool
|
|
4
|
+
* result answers a tool call that the fold would swallow. A call is always
|
|
5
|
+
* before its result, so this is the only way to split a pair.
|
|
6
|
+
*/
|
|
7
|
+
export declare function safeCut(messages: readonly StoredMessage[], from: number): number;
|
|
8
|
+
/**
|
|
9
|
+
* `safeCut`, then moved back until the kept messages start on a user turn,
|
|
10
|
+
* which providers require of a conversation. When no user turn lies before the
|
|
11
|
+
* cut, the pair-safe cut stands and the kept messages start on another role.
|
|
12
|
+
*/
|
|
13
|
+
export declare function userCut(messages: readonly StoredMessage[], from: number): number;
|
|
14
|
+
//# sourceMappingURL=pairs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairs.d.ts","sourceRoot":"","sources":["../src/pairs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,UAIvE;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,UAUvE"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Message, type ProposedAction } from '@nxgt/ai-core';
|
|
2
|
+
import { type Ctx } from './redis-ctx.js';
|
|
3
|
+
import type { ActionPatch } from './types.js';
|
|
4
|
+
/** Per message, the actions it proposes, once each within the batch. */
|
|
5
|
+
export declare function proposedIn(list: readonly Message[]): ProposedAction[][];
|
|
6
|
+
export declare function findAction(c: Ctx, actionId: string, ownerId: string): Promise<ProposedAction | undefined>;
|
|
7
|
+
export declare function updateAction(c: Ctx, actionId: string, ownerId: string, patch: ActionPatch): Promise<ProposedAction | undefined>;
|
|
8
|
+
//# sourceMappingURL=redis-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-actions.d.ts","sourceRoot":"","sources":["../src/redis-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,cAAc,EAEnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAe,KAAK,GAAG,EAAiB,MAAM,aAAa,CAAC;AAGnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,cAAc,EAAE,EAAE,CAWvE;AAED,wBAAsB,UAAU,CAC/B,CAAC,EAAE,GAAG,EACN,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAgBrC;AAED,wBAAsB,YAAY,CACjC,CAAC,EAAE,GAAG,EACN,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,uCAmBlB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { assertOwner, notFound } from './guards.js';
|
|
2
|
+
export { assertOwner, notFound };
|
|
3
|
+
/**
|
|
4
|
+
* The one method the store calls: `RedisClient.send` of Bun, the client that
|
|
5
|
+
* `connectRedis` and `openRedis` of `@nxgt/redis` hand out.
|
|
6
|
+
*/
|
|
7
|
+
export interface RedisCommands {
|
|
8
|
+
send(command: string, args: string[]): Promise<unknown>;
|
|
9
|
+
}
|
|
10
|
+
/** What the functions of the sibling files share. */
|
|
11
|
+
export interface Ctx {
|
|
12
|
+
redis: RedisCommands;
|
|
13
|
+
prefix: string;
|
|
14
|
+
now: () => number;
|
|
15
|
+
/** The expiry as an epoch ms for a write at `at`, or `'0'` without a TTL. */
|
|
16
|
+
expireAt: (at: number) => string;
|
|
17
|
+
}
|
|
18
|
+
/** The keys of one thread and of one owner. */
|
|
19
|
+
export declare const keys: {
|
|
20
|
+
thread: (c: Ctx, id: string) => string;
|
|
21
|
+
messages: (c: Ctx, id: string) => string;
|
|
22
|
+
ids: (c: Ctx, id: string) => string;
|
|
23
|
+
actionIndex: (c: Ctx, id: string) => string;
|
|
24
|
+
owner: (c: Ctx, ownerId: string) => string;
|
|
25
|
+
/**
|
|
26
|
+
* The prefix of an owner's action keys; the action id follows it. The
|
|
27
|
+
* owner's length comes first, so `a:b` + `c` never meets `a` + `b:c`.
|
|
28
|
+
*/
|
|
29
|
+
actionPrefix: (c: Ctx, ownerId: string) => string;
|
|
30
|
+
};
|
|
31
|
+
/** Runs a Lua script atomically. */
|
|
32
|
+
export declare function evalLua(c: Ctx, script: string, scriptKeys: readonly string[], args: readonly string[]): Promise<unknown>;
|
|
33
|
+
/** The thread's fields as a Redis hash holds them, or `undefined` when absent. */
|
|
34
|
+
export declare function readThreadHash(c: Ctx, id: string): Promise<Record<string, string> | undefined>;
|
|
35
|
+
/** The owner's thread hash, or `undefined` when absent or another owner's. */
|
|
36
|
+
export declare function ownThread(c: Ctx, id: string, ownerId: string): Promise<Record<string, string> | undefined>;
|
|
37
|
+
//# sourceMappingURL=redis-ctx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-ctx.d.ts","sourceRoot":"","sources":["../src/redis-ctx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AAEjC;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxD;AAED,qDAAqD;AACrD,MAAM,WAAW,GAAG;IACnB,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,6EAA6E;IAC7E,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;CACjC;AAED,+CAA+C;AAC/C,eAAO,MAAM,IAAI;gBACJ,GAAG,MAAM,MAAM;kBACb,GAAG,MAAM,MAAM;aACpB,GAAG,MAAM,MAAM;qBACP,GAAG,MAAM,MAAM;eACrB,GAAG,WAAW,MAAM;IAC/B;;;OAGG;sBACe,GAAG,WAAW,MAAM;CAEtC,CAAC;AAEF,oCAAoC;AACpC,wBAAgB,OAAO,CACtB,CAAC,EAAE,GAAG,EACN,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,IAAI,EAAE,SAAS,MAAM,EAAE,GACrB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,kFAAkF;AAClF,wBAAsB,cAAc,CACnC,CAAC,EAAE,GAAG,EACN,EAAE,EAAE,MAAM,GACR,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CAc7C;AAED,8EAA8E;AAC9E,wBAAsB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,+CAIlE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Message, ProposedAction } from '@nxgt/ai-core';
|
|
2
|
+
import type { StoredMessage, Thread } from './types.js';
|
|
3
|
+
/** A thread hash as the application reads it. */
|
|
4
|
+
export declare function toThread(id: string, hash: Record<string, string>): Thread;
|
|
5
|
+
/** What a sorted-set member holds; the seq is its position, not stored. */
|
|
6
|
+
export interface MessageMember {
|
|
7
|
+
id: string;
|
|
8
|
+
message: Message;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function toStoredMessage(threadId: string, seq: number, member: MessageMember): StoredMessage;
|
|
12
|
+
export declare function parseAction(raw: unknown): ProposedAction | undefined;
|
|
13
|
+
//# sourceMappingURL=redis-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-map.d.ts","sourceRoot":"","sources":["../src/redis-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAiB,MAAM,SAAS,CAAC;AAEpE,iDAAiD;AACjD,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAazE;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,aAAa,GACnB,aAAa,CAQf;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAIpE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Message } from '@nxgt/ai-core';
|
|
2
|
+
import { type Ctx } from './redis-ctx.js';
|
|
3
|
+
import type { LoadedThread, LoadOptions, StoredMessage, SummaryInput, ThreadSummary } from './types.js';
|
|
4
|
+
export declare function append(c: Ctx, threadId: string, ownerId: string, list: readonly Message[]): Promise<StoredMessage[]>;
|
|
5
|
+
export declare function load(c: Ctx, threadId: string, ownerId: string, opts: LoadOptions): Promise<LoadedThread | undefined>;
|
|
6
|
+
export declare function setSummary(c: Ctx, threadId: string, ownerId: string, input: SummaryInput): Promise<ThreadSummary>;
|
|
7
|
+
//# sourceMappingURL=redis-messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-messages.d.ts","sourceRoot":"","sources":["../src/redis-messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAEN,KAAK,GAAG,EAKR,MAAM,aAAa,CAAC;AAIrB,OAAO,KAAK,EACX,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,EACb,MAAM,SAAS,CAAC;AAUjB,wBAAsB,MAAM,CAC3B,CAAC,EAAE,GAAG,EACN,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,OAAO,EAAE,GACtB,OAAO,CAAC,aAAa,EAAE,CAAC,CAiC1B;AAED,wBAAsB,IAAI,CACzB,CAAC,EAAE,GAAG,EACN,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,WAAW,GACf,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CA6BnC;AAED,wBAAsB,UAAU,CAC/B,CAAC,EAAE,GAAG,EACN,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,YAAY,GACjB,OAAO,CAAC,aAAa,CAAC,CA2BxB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Lua scripts of the store. A script runs as one command, so each
|
|
3
|
+
* read-then-write below is atomic: no other client sees it half done.
|
|
4
|
+
*
|
|
5
|
+
* `ARGV` values that are an expiry hold an epoch ms, or `'0'` for no TTL.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* KEYS: thread hash, owner index. ARGV: id, ownerId, at, expireAt, then
|
|
9
|
+
* field/value pairs (`title`, `meta`). Writes the thread and indexes it.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CREATE_THREAD = "\nlocal function slide(key, at)\n\tif at ~= '0' then redis.call('PEXPIREAT', key, at) end\nend\nlocal function slideThread(keys, at)\n\tfor j = 1, 4 do slide(keys[j], at) end\n\tfor _, key in ipairs(redis.call('SMEMBERS', keys[4])) do slide(key, at) end\nend\n\nlocal fields = {'ownerId', ARGV[2], 'messageCount', '0', 'createdAt', ARGV[3], 'updatedAt', ARGV[3]}\nfor i = 5, #ARGV do fields[#fields + 1] = ARGV[i] end\nredis.call('HSET', KEYS[1], unpack(fields))\nredis.call('ZADD', KEYS[2], 0, ARGV[1])\nslide(KEYS[1], ARGV[4])\nreturn 1\n";
|
|
12
|
+
/**
|
|
13
|
+
* KEYS: thread hash, messages sorted set, id index, action index. ARGV:
|
|
14
|
+
* ownerId, updatedAt, expireAt, action key prefix, thread id, count, then per message
|
|
15
|
+
* its id, its JSON, its action count and per action its id and its body.
|
|
16
|
+
* Returns the first seq of the range it reserved, or nil when the thread is
|
|
17
|
+
* absent or another owner's. The seq range, the messages and the actions are
|
|
18
|
+
* written in the same step, so a reader never sees a gap.
|
|
19
|
+
*/
|
|
20
|
+
export declare const APPEND = "\nlocal function slide(key, at)\n\tif at ~= '0' then redis.call('PEXPIREAT', key, at) end\nend\nlocal function slideThread(keys, at)\n\tfor j = 1, 4 do slide(keys[j], at) end\n\tfor _, key in ipairs(redis.call('SMEMBERS', keys[4])) do slide(key, at) end\nend\n\nif redis.call('HGET', KEYS[1], 'ownerId') ~= ARGV[1] then return false end\nlocal n = tonumber(ARGV[6])\nlocal first = redis.call('HINCRBY', KEYS[1], 'messageCount', n) - n + 1\nredis.call('HSET', KEYS[1], 'updatedAt', ARGV[2])\nlocal i = 7\nfor k = 0, n - 1 do\n\tlocal id, json, count = ARGV[i], ARGV[i + 1], tonumber(ARGV[i + 2])\n\ti = i + 3\n\tredis.call('ZADD', KEYS[2], first + k, json)\n\tredis.call('HSET', KEYS[3], id, first + k)\n\tfor _ = 1, count do\n\t\tlocal key = ARGV[4] .. ARGV[i]\n\t\tif redis.call('HSETNX', key, 'status', 'pending') == 1 then\n\t\t\tredis.call('HSET', key, 'body', ARGV[i + 1], 'thread', ARGV[5])\n\t\t\tredis.call('SADD', KEYS[4], key)\n\t\tend\n\t\ti = i + 2\n\tend\nend\nslideThread(KEYS, ARGV[3])\nreturn first\n";
|
|
21
|
+
/**
|
|
22
|
+
* KEYS: thread hash, messages, id index, action index. ARGV: ownerId,
|
|
23
|
+
* summary JSON, updatedAt, expireAt. Returns 1, or nil when the thread is
|
|
24
|
+
* absent or another owner's.
|
|
25
|
+
*/
|
|
26
|
+
export declare const SET_SUMMARY = "\nlocal function slide(key, at)\n\tif at ~= '0' then redis.call('PEXPIREAT', key, at) end\nend\nlocal function slideThread(keys, at)\n\tfor j = 1, 4 do slide(keys[j], at) end\n\tfor _, key in ipairs(redis.call('SMEMBERS', keys[4])) do slide(key, at) end\nend\n\nif redis.call('HGET', KEYS[1], 'ownerId') ~= ARGV[1] then return false end\nredis.call('HSET', KEYS[1], 'summary', ARGV[2], 'updatedAt', ARGV[3])\nslideThread(KEYS, ARGV[4])\nreturn 1\n";
|
|
27
|
+
/**
|
|
28
|
+
* KEYS: action hash. ARGV: status, '1' when a result follows, the result
|
|
29
|
+
* JSON, expireAt, key prefix. Moves a pending action on and slides the
|
|
30
|
+
* thread's keys and actions with it; returns 1, or nil when it is absent or
|
|
31
|
+
* no longer pending.
|
|
32
|
+
*/
|
|
33
|
+
export declare const UPDATE_ACTION = "\nlocal function slide(key, at)\n\tif at ~= '0' then redis.call('PEXPIREAT', key, at) end\nend\nlocal function slideThread(keys, at)\n\tfor j = 1, 4 do slide(keys[j], at) end\n\tfor _, key in ipairs(redis.call('SMEMBERS', keys[4])) do slide(key, at) end\nend\n\nif redis.call('HGET', KEYS[1], 'status') ~= 'pending' then return false end\nredis.call('HSET', KEYS[1], 'status', ARGV[1])\nif ARGV[2] == '1' then redis.call('HSET', KEYS[1], 'result', ARGV[3]) end\nif ARGV[4] ~= '0' then\n\tlocal id = redis.call('HGET', KEYS[1], 'thread')\n\tlocal base = ARGV[5] .. ':'\n\tslideThread({base .. 'thread:' .. id, base .. 'messages:' .. id, base .. 'ids:' .. id, base .. 'actions:' .. id}, ARGV[4])\nend\nreturn 1\n";
|
|
34
|
+
/**
|
|
35
|
+
* KEYS: thread hash, messages, id index, action index, owner index. ARGV:
|
|
36
|
+
* ownerId, thread id. Removes the thread, its messages and its actions in
|
|
37
|
+
* one step; returns 1, or nil when the thread is absent or another owner's.
|
|
38
|
+
*/
|
|
39
|
+
export declare const DELETE_THREAD = "\nif redis.call('HGET', KEYS[1], 'ownerId') ~= ARGV[1] then return false end\nfor _, key in ipairs(redis.call('SMEMBERS', KEYS[4])) do redis.call('DEL', key) end\nredis.call('DEL', KEYS[2], KEYS[3], KEYS[4], KEYS[1])\nredis.call('ZREM', KEYS[5], ARGV[2])\nreturn 1\n";
|
|
40
|
+
//# sourceMappingURL=redis-scripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-scripts.d.ts","sourceRoot":"","sources":["../src/redis-scripts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH;;;GAGG;AACH,eAAO,MAAM,aAAa,iiBAOzB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,6/BAsBlB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,ocAKvB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,2sBAUzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,+QAMzB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RedisCommands } from './redis-ctx.js';
|
|
2
|
+
import type { MemoryStore } from './types.js';
|
|
3
|
+
export interface RedisMemoryOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The client: `connection.client` of `connectRedis`, or the `client` of an
|
|
6
|
+
* instance of `openRedis`, both from `@nxgt/redis`.
|
|
7
|
+
*/
|
|
8
|
+
client: RedisCommands;
|
|
9
|
+
/** What every key starts with; default `ai-memory`. */
|
|
10
|
+
prefix?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Keys expire this long after the thread's last write, so the whole thread
|
|
13
|
+
* goes together.
|
|
14
|
+
*/
|
|
15
|
+
expireAfterSeconds?: number;
|
|
16
|
+
/** The clock, in epoch ms; default `Date.now`. */
|
|
17
|
+
now?: () => number;
|
|
18
|
+
}
|
|
19
|
+
/** A store over Redis keys; see the guide for the layout, expiry and limits. */
|
|
20
|
+
export declare function redisMemory(options: RedisMemoryOptions): MemoryStore;
|
|
21
|
+
//# sourceMappingURL=redis-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-store.d.ts","sourceRoot":"","sources":["../src/redis-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAO,aAAa,EAAE,MAAM,aAAa,CAAC;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IAClC;;;OAGG;IACH,MAAM,EAAE,aAAa,CAAC;IACtB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAqBpE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Ctx } from './redis-ctx.js';
|
|
2
|
+
import type { CreateThreadInput, Page, Thread, ThreadPage } from './types.js';
|
|
3
|
+
export declare function createThread(c: Ctx, input: CreateThreadInput): Promise<Thread>;
|
|
4
|
+
export declare function getThread(c: Ctx, id: string, ownerId: string): Promise<Thread | undefined>;
|
|
5
|
+
/**
|
|
6
|
+
* Newest first, by id. The owner index can name a thread that has expired:
|
|
7
|
+
* it is skipped and removed, and the page is filled from the ones after it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function listThreads(c: Ctx, ownerId: string, page: Page): Promise<ThreadPage>;
|
|
10
|
+
/**
|
|
11
|
+
* One script removes the messages, the actions, the thread and its owner
|
|
12
|
+
* index entry, so a crash or a racing `append` never leaves keys nobody can
|
|
13
|
+
* reach: the append finds the thread gone.
|
|
14
|
+
*/
|
|
15
|
+
export declare function deleteThread(c: Ctx, id: string, ownerId: string): Promise<boolean>;
|
|
16
|
+
//# sourceMappingURL=redis-threads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-threads.d.ts","sourceRoot":"","sources":["../src/redis-threads.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,GAAG,EAKR,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3E,wBAAsB,YAAY,CACjC,CAAC,EAAE,GAAG,EACN,KAAK,EAAE,iBAAiB,GACtB,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,+BAGlE;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAChC,CAAC,EAAE,GAAG,EACN,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,GACR,OAAO,CAAC,UAAU,CAAC,CA+BrB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,oBAerE"}
|
package/dist/redis.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../src/redis.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,KAAK,kBAAkB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|