@cloudbase/agent-adapter-langgraph 0.0.2
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/CHANGELOG.md +37 -0
- package/README.md +125 -0
- package/dist/index.d.mts +289 -0
- package/dist/index.d.ts +289 -0
- package/dist/index.js +1090 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1062 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +55 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1090 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AGKitPropertiesAnnotation: () => AGKitPropertiesAnnotation,
|
|
34
|
+
AGKitStateAnnotation: () => AGKitStateAnnotation,
|
|
35
|
+
LanggraphAgent: () => LanggraphAgent,
|
|
36
|
+
TDAISaver: () => TDAISaver,
|
|
37
|
+
TDAIStore: () => TDAIStore
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/agent.ts
|
|
42
|
+
var import_client = require("@ag-ui/client");
|
|
43
|
+
var import_abstract = require("@cloudbase/agent-agents/abstract");
|
|
44
|
+
var import_rxjs = require("rxjs");
|
|
45
|
+
var import_langgraph = require("@langchain/langgraph");
|
|
46
|
+
|
|
47
|
+
// src/util.ts
|
|
48
|
+
var import_tools = require("@langchain/core/tools");
|
|
49
|
+
var import_v4 = __toESM(require("zod/v4"));
|
|
50
|
+
function convertActionsToDynamicStructuredTools(actions) {
|
|
51
|
+
return actions.map(convertActionToDynamicStructuredTool);
|
|
52
|
+
}
|
|
53
|
+
function convertActionToDynamicStructuredTool(actionInput) {
|
|
54
|
+
return new import_tools.DynamicStructuredTool({
|
|
55
|
+
name: actionInput.name,
|
|
56
|
+
description: actionInput.description,
|
|
57
|
+
schema: convertJsonSchemaToZodSchema(actionInput.parameters, true),
|
|
58
|
+
func: async () => {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function convertJsonSchemaToZodSchema(jsonSchema, required) {
|
|
64
|
+
if (jsonSchema.type === "object") {
|
|
65
|
+
const spec = {};
|
|
66
|
+
if (!jsonSchema.properties || !Object.keys(jsonSchema.properties).length) {
|
|
67
|
+
return !required ? import_v4.default.object(spec).optional() : import_v4.default.object(spec);
|
|
68
|
+
}
|
|
69
|
+
for (const [key, value] of Object.entries(jsonSchema.properties)) {
|
|
70
|
+
spec[key] = convertJsonSchemaToZodSchema(
|
|
71
|
+
value,
|
|
72
|
+
jsonSchema.required ? jsonSchema.required.includes(key) : false
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
let schema = import_v4.default.object(spec).describe(jsonSchema.description);
|
|
76
|
+
return required ? schema : schema.optional();
|
|
77
|
+
} else if (jsonSchema.type === "string") {
|
|
78
|
+
let schema = import_v4.default.string().describe(jsonSchema.description);
|
|
79
|
+
return required ? schema : schema.optional();
|
|
80
|
+
} else if (jsonSchema.type === "number") {
|
|
81
|
+
let schema = import_v4.default.number().describe(jsonSchema.description);
|
|
82
|
+
return required ? schema : schema.optional();
|
|
83
|
+
} else if (jsonSchema.type === "boolean") {
|
|
84
|
+
let schema = import_v4.default.boolean().describe(jsonSchema.description);
|
|
85
|
+
return required ? schema : schema.optional();
|
|
86
|
+
} else if (jsonSchema.type === "array") {
|
|
87
|
+
let itemSchema = convertJsonSchemaToZodSchema(jsonSchema.items, true);
|
|
88
|
+
let schema = import_v4.default.array(itemSchema).describe(jsonSchema.description);
|
|
89
|
+
return required ? schema : schema.optional();
|
|
90
|
+
}
|
|
91
|
+
throw new Error("Invalid JSON schema");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/agent.ts
|
|
95
|
+
var AGKitPropertiesAnnotation = import_langgraph.Annotation.Root({
|
|
96
|
+
actions: import_langgraph.Annotation
|
|
97
|
+
});
|
|
98
|
+
var AGKitStateAnnotation = import_langgraph.Annotation.Root({
|
|
99
|
+
agKit: import_langgraph.Annotation,
|
|
100
|
+
...import_langgraph.MessagesAnnotation.spec
|
|
101
|
+
});
|
|
102
|
+
var LanggraphAgent = class extends import_abstract.AbstractAgent {
|
|
103
|
+
constructor(agentConfig) {
|
|
104
|
+
super(agentConfig);
|
|
105
|
+
this.compiledWorkflow = agentConfig.compiledWorkflow;
|
|
106
|
+
}
|
|
107
|
+
run(input) {
|
|
108
|
+
return new import_rxjs.Observable((subscriber) => {
|
|
109
|
+
this._run(subscriber, input);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async _run(subscriber, input) {
|
|
113
|
+
const { messages, runId, threadId } = input;
|
|
114
|
+
const oldMessageIds = /* @__PURE__ */ new Set();
|
|
115
|
+
if (this.compiledWorkflow.checkpointer && typeof this.compiledWorkflow.checkpointer !== "boolean") {
|
|
116
|
+
const res = await this.compiledWorkflow.getState({
|
|
117
|
+
configurable: { thread_id: threadId }
|
|
118
|
+
});
|
|
119
|
+
const oldMessages = res.values?.messages || [];
|
|
120
|
+
oldMessages.filter((x) => x.id).forEach((x) => {
|
|
121
|
+
oldMessageIds.add(x.id);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
subscriber.next({
|
|
125
|
+
type: import_client.EventType.RUN_STARTED,
|
|
126
|
+
threadId,
|
|
127
|
+
runId
|
|
128
|
+
});
|
|
129
|
+
const streamEventInput = input.forwardedProps?.resume ? new import_langgraph.Command({
|
|
130
|
+
resume: JSON.stringify(input.forwardedProps?.resume?.payload)
|
|
131
|
+
}) : {
|
|
132
|
+
messages: aguiMessagesToLangChain(messages),
|
|
133
|
+
agKit: {
|
|
134
|
+
actions: convertActionsToDynamicStructuredTools(
|
|
135
|
+
input.tools.map((x) => ({
|
|
136
|
+
...x,
|
|
137
|
+
parameters: typeof x.parameters === "string" ? JSON.parse(x.parameters) : x.parameters
|
|
138
|
+
}))
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const stream = this.compiledWorkflow.streamEvents(streamEventInput, {
|
|
143
|
+
version: "v2",
|
|
144
|
+
runId,
|
|
145
|
+
configurable: {
|
|
146
|
+
thread_id: threadId
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
const chatModelRuns = [];
|
|
150
|
+
const handledToolCallIds = /* @__PURE__ */ new Set();
|
|
151
|
+
let interrupt;
|
|
152
|
+
let currentToolCall = null;
|
|
153
|
+
for await (const event of stream) {
|
|
154
|
+
if (event.event.startsWith("ChannelWrite<")) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (event.event === "on_chat_model_start") {
|
|
158
|
+
chatModelRuns.push({ runId: event.run_id });
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (event.event === "on_chat_model_stream") {
|
|
162
|
+
if (Array.isArray(event.data.chunk?.tool_call_chunks) && event.data.chunk?.tool_call_chunks?.length > 0) {
|
|
163
|
+
event.data.chunk.tool_call_chunks.map((x) => ({
|
|
164
|
+
...x,
|
|
165
|
+
args: typeof x.args === "string" ? x.args : x.args ? JSON.stringify(x.args) : ""
|
|
166
|
+
})).forEach((toolCall) => {
|
|
167
|
+
if (currentToolCall) {
|
|
168
|
+
if (toolCall.id && currentToolCall.id !== toolCall.id) {
|
|
169
|
+
subscriber.next({
|
|
170
|
+
toolCallId: currentToolCall.id,
|
|
171
|
+
type: import_client.EventType.TOOL_CALL_END
|
|
172
|
+
});
|
|
173
|
+
if (toolCall.name && toolCall.id) {
|
|
174
|
+
currentToolCall = toolCall;
|
|
175
|
+
subscriber.next({
|
|
176
|
+
toolCallId: currentToolCall.id,
|
|
177
|
+
toolCallName: currentToolCall.name,
|
|
178
|
+
type: import_client.EventType.TOOL_CALL_START
|
|
179
|
+
});
|
|
180
|
+
if (currentToolCall.args) {
|
|
181
|
+
subscriber.next({
|
|
182
|
+
toolCallId: currentToolCall.id,
|
|
183
|
+
delta: currentToolCall.args,
|
|
184
|
+
type: import_client.EventType.TOOL_CALL_ARGS
|
|
185
|
+
});
|
|
186
|
+
if (isValidJson(currentToolCall.args)) {
|
|
187
|
+
subscriber.next({
|
|
188
|
+
toolCallId: currentToolCall.id,
|
|
189
|
+
type: import_client.EventType.TOOL_CALL_END
|
|
190
|
+
});
|
|
191
|
+
currentToolCall = null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
if (toolCall.args) {
|
|
197
|
+
currentToolCall.args += toolCall.args;
|
|
198
|
+
subscriber.next({
|
|
199
|
+
toolCallId: currentToolCall.id,
|
|
200
|
+
delta: toolCall.args,
|
|
201
|
+
type: import_client.EventType.TOOL_CALL_ARGS
|
|
202
|
+
});
|
|
203
|
+
if (isValidJson(currentToolCall.args)) {
|
|
204
|
+
subscriber.next({
|
|
205
|
+
toolCallId: currentToolCall.id,
|
|
206
|
+
type: import_client.EventType.TOOL_CALL_END
|
|
207
|
+
});
|
|
208
|
+
currentToolCall = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
if (toolCall.name && toolCall.id) {
|
|
214
|
+
currentToolCall = toolCall;
|
|
215
|
+
subscriber.next({
|
|
216
|
+
toolCallId: toolCall.id,
|
|
217
|
+
toolCallName: toolCall.name,
|
|
218
|
+
type: import_client.EventType.TOOL_CALL_START
|
|
219
|
+
});
|
|
220
|
+
if (toolCall.args) {
|
|
221
|
+
subscriber.next({
|
|
222
|
+
toolCallId: toolCall.id,
|
|
223
|
+
delta: toolCall.args,
|
|
224
|
+
type: import_client.EventType.TOOL_CALL_ARGS
|
|
225
|
+
});
|
|
226
|
+
if (isValidJson(toolCall.args)) {
|
|
227
|
+
subscriber.next({
|
|
228
|
+
toolCallId: toolCall.id,
|
|
229
|
+
type: import_client.EventType.TOOL_CALL_END
|
|
230
|
+
});
|
|
231
|
+
currentToolCall = null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const chatModelRun = chatModelRuns.find(
|
|
239
|
+
(run) => run.runId === event.run_id
|
|
240
|
+
);
|
|
241
|
+
if (!chatModelRun) {
|
|
242
|
+
subscriber.next({
|
|
243
|
+
type: import_client.EventType.RUN_ERROR,
|
|
244
|
+
message: `Received a message from an unknown chat model run. Run Id: ${event.run_id}`
|
|
245
|
+
});
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (!chatModelRun.messageId) {
|
|
249
|
+
const messageId = event.data.chunk.id;
|
|
250
|
+
chatModelRun.messageId = messageId;
|
|
251
|
+
subscriber.next({
|
|
252
|
+
messageId,
|
|
253
|
+
type: import_client.EventType.TEXT_MESSAGE_START,
|
|
254
|
+
role: "assistant"
|
|
255
|
+
});
|
|
256
|
+
const delta = event.data.chunk.content;
|
|
257
|
+
typeof delta === "string" && delta && subscriber.next({
|
|
258
|
+
messageId: chatModelRun.messageId,
|
|
259
|
+
type: import_client.EventType.TEXT_MESSAGE_CONTENT,
|
|
260
|
+
delta
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
} else {
|
|
264
|
+
if (chatModelRun.messageId !== event.data.chunk.id) {
|
|
265
|
+
subscriber.next({
|
|
266
|
+
type: import_client.EventType.RUN_ERROR,
|
|
267
|
+
message: `Received a message of unknown message id from current run. Run Id: ${event.run_id} Message Id from current run: ${chatModelRun.messageId} Message Id from received message: ${event.data.chunk.id}`
|
|
268
|
+
});
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const delta = event.data.chunk.content;
|
|
272
|
+
typeof delta === "string" && delta && subscriber.next({
|
|
273
|
+
messageId: chatModelRun.messageId,
|
|
274
|
+
type: import_client.EventType.TEXT_MESSAGE_CONTENT,
|
|
275
|
+
delta
|
|
276
|
+
});
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (event.event === "on_chat_model_end") {
|
|
281
|
+
const chatModelRun = chatModelRuns.find(
|
|
282
|
+
(run) => run.runId === event.run_id
|
|
283
|
+
);
|
|
284
|
+
if (!chatModelRun) {
|
|
285
|
+
subscriber.next({
|
|
286
|
+
type: import_client.EventType.RUN_ERROR,
|
|
287
|
+
message: `Received a on_chat_model_end event from an unknown chat model run. Run Id: ${event.run_id}`
|
|
288
|
+
});
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
subscriber.next({
|
|
292
|
+
type: import_client.EventType.TEXT_MESSAGE_END,
|
|
293
|
+
messageId: chatModelRun.messageId
|
|
294
|
+
});
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (event.event === "on_chain_end") {
|
|
298
|
+
const messages2 = event.data.output?.messages;
|
|
299
|
+
if (Array.isArray(messages2)) {
|
|
300
|
+
const inputMessages = event.data.input?.messages;
|
|
301
|
+
const lastInputMessage = inputMessages?.[inputMessages?.length - 1];
|
|
302
|
+
const messageId = lastInputMessage?.id;
|
|
303
|
+
const toolCallMessages = messages2.filter((x) => x.id && !oldMessageIds.has(x.id)).filter((x) => x?.tool_call_id);
|
|
304
|
+
toolCallMessages.forEach((x) => {
|
|
305
|
+
if (handledToolCallIds.has(x.tool_call_id)) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
subscriber.next({
|
|
309
|
+
toolCallId: x.tool_call_id,
|
|
310
|
+
type: import_client.EventType.TOOL_CALL_RESULT,
|
|
311
|
+
content: x.content,
|
|
312
|
+
messageId
|
|
313
|
+
});
|
|
314
|
+
handledToolCallIds.add(x.tool_call_id);
|
|
315
|
+
});
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (event.event === "on_chain_stream" && event.data.chunk?.__interrupt__ && Array.isArray(event.data.chunk.__interrupt__) && event.data.chunk.__interrupt__.length > 0) {
|
|
320
|
+
const rawInterrupt = event.data.chunk.__interrupt__[0];
|
|
321
|
+
interrupt = {
|
|
322
|
+
id: rawInterrupt.id,
|
|
323
|
+
// TODO: replace with actual reason
|
|
324
|
+
reason: "agent requested interrupt",
|
|
325
|
+
payload: rawInterrupt.value
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (interrupt) {
|
|
330
|
+
subscriber.next({
|
|
331
|
+
type: import_client.EventType.RUN_FINISHED,
|
|
332
|
+
threadId,
|
|
333
|
+
runId,
|
|
334
|
+
outcome: "interrupt",
|
|
335
|
+
interrupt
|
|
336
|
+
});
|
|
337
|
+
} else {
|
|
338
|
+
subscriber.next({
|
|
339
|
+
type: import_client.EventType.RUN_FINISHED,
|
|
340
|
+
threadId,
|
|
341
|
+
runId
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
clone() {
|
|
346
|
+
const workflow = this.compiledWorkflow;
|
|
347
|
+
this.compiledWorkflow = void 0;
|
|
348
|
+
const cloned = super.clone();
|
|
349
|
+
this.compiledWorkflow = workflow;
|
|
350
|
+
cloned.compiledWorkflow = workflow;
|
|
351
|
+
return cloned;
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
function aguiMessagesToLangChain(messages) {
|
|
355
|
+
return messages.map((message, index) => {
|
|
356
|
+
switch (message.role) {
|
|
357
|
+
case "user":
|
|
358
|
+
return {
|
|
359
|
+
id: message.id,
|
|
360
|
+
role: message.role,
|
|
361
|
+
content: message.content,
|
|
362
|
+
type: "human"
|
|
363
|
+
};
|
|
364
|
+
case "assistant":
|
|
365
|
+
return {
|
|
366
|
+
id: message.id,
|
|
367
|
+
type: "ai",
|
|
368
|
+
role: message.role,
|
|
369
|
+
content: message.content ?? "",
|
|
370
|
+
tool_calls: (message.toolCalls ?? []).map((tc) => ({
|
|
371
|
+
id: tc.id,
|
|
372
|
+
name: tc.function.name,
|
|
373
|
+
args: JSON.parse(tc.function.arguments),
|
|
374
|
+
type: "tool_call"
|
|
375
|
+
}))
|
|
376
|
+
};
|
|
377
|
+
case "system":
|
|
378
|
+
return {
|
|
379
|
+
id: message.id,
|
|
380
|
+
role: message.role,
|
|
381
|
+
content: message.content,
|
|
382
|
+
type: "system"
|
|
383
|
+
};
|
|
384
|
+
case "tool":
|
|
385
|
+
return {
|
|
386
|
+
content: message.content,
|
|
387
|
+
role: message.role,
|
|
388
|
+
type: message.role,
|
|
389
|
+
tool_call_id: message.toolCallId,
|
|
390
|
+
id: message.id
|
|
391
|
+
};
|
|
392
|
+
default:
|
|
393
|
+
console.error(`Message role ${message.role} is not implemented`);
|
|
394
|
+
throw new Error("message role is not supported.");
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
function isValidJson(json) {
|
|
399
|
+
try {
|
|
400
|
+
JSON.parse(json);
|
|
401
|
+
return true;
|
|
402
|
+
} catch (e) {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/checkpoint.ts
|
|
408
|
+
var import_langgraph2 = require("@langchain/langgraph");
|
|
409
|
+
var import_agent_agents = require("@cloudbase/agent-agents");
|
|
410
|
+
var TDAISaver = class extends import_langgraph2.BaseCheckpointSaver {
|
|
411
|
+
constructor(config) {
|
|
412
|
+
super();
|
|
413
|
+
const {
|
|
414
|
+
checkpointType = "checkpoints",
|
|
415
|
+
checkpointWritesType = "checkpoint_writes",
|
|
416
|
+
...clientConfig
|
|
417
|
+
} = config;
|
|
418
|
+
this.memoryClient = new import_agent_agents.MemoryClient(clientConfig);
|
|
419
|
+
this.checkpointType = checkpointType;
|
|
420
|
+
this.checkpointWritesType = checkpointWritesType;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Retrieves a checkpoint from TDAI Memory based on the provided config.
|
|
424
|
+
* If the config contains a "checkpoint_id" key, the checkpoint with the matching
|
|
425
|
+
* thread ID and checkpoint ID is retrieved. Otherwise, the latest checkpoint
|
|
426
|
+
* for the given thread ID is retrieved.
|
|
427
|
+
*/
|
|
428
|
+
async getTuple(config) {
|
|
429
|
+
try {
|
|
430
|
+
const {
|
|
431
|
+
thread_id,
|
|
432
|
+
checkpoint_ns = "",
|
|
433
|
+
checkpoint_id
|
|
434
|
+
} = config.configurable ?? {};
|
|
435
|
+
if (!thread_id) {
|
|
436
|
+
return void 0;
|
|
437
|
+
}
|
|
438
|
+
const query = {
|
|
439
|
+
collection: this.checkpointType,
|
|
440
|
+
checkpoint_ns
|
|
441
|
+
};
|
|
442
|
+
if (checkpoint_id) {
|
|
443
|
+
query.checkpoint_id = checkpoint_id;
|
|
444
|
+
}
|
|
445
|
+
const { events = [] } = await this.memoryClient.queryEvents({
|
|
446
|
+
sessionId: thread_id,
|
|
447
|
+
where: query,
|
|
448
|
+
orderBy: { checkpoint_id: import_agent_agents.Order.DESCENDING },
|
|
449
|
+
limit: 1
|
|
450
|
+
});
|
|
451
|
+
if (events.length === 0) {
|
|
452
|
+
return void 0;
|
|
453
|
+
}
|
|
454
|
+
const doc = events[0];
|
|
455
|
+
const configurableValues = {
|
|
456
|
+
checkpoint_ns,
|
|
457
|
+
checkpoint_id: doc.checkpoint_id
|
|
458
|
+
};
|
|
459
|
+
const checkpoint = doc.checkpoint;
|
|
460
|
+
const { events: serializedWrites = [] } = await this.memoryClient.queryEvents({
|
|
461
|
+
sessionId: thread_id,
|
|
462
|
+
where: {
|
|
463
|
+
collection: this.checkpointWritesType,
|
|
464
|
+
...configurableValues
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
const pendingWrites = serializedWrites.map(
|
|
468
|
+
(serializedWrite) => {
|
|
469
|
+
return [
|
|
470
|
+
serializedWrite.task_id,
|
|
471
|
+
serializedWrite.channel,
|
|
472
|
+
serializedWrite.value
|
|
473
|
+
];
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
const metadata = doc.metadata || {};
|
|
477
|
+
return {
|
|
478
|
+
config: { configurable: configurableValues },
|
|
479
|
+
checkpoint,
|
|
480
|
+
pendingWrites,
|
|
481
|
+
metadata,
|
|
482
|
+
parentConfig: doc.parent_checkpoint_id != null ? {
|
|
483
|
+
configurable: {
|
|
484
|
+
thread_id,
|
|
485
|
+
checkpoint_ns,
|
|
486
|
+
checkpoint_id: doc.parent_checkpoint_id
|
|
487
|
+
}
|
|
488
|
+
} : void 0
|
|
489
|
+
};
|
|
490
|
+
} catch (error) {
|
|
491
|
+
console.error("Error getting checkpoint:", error);
|
|
492
|
+
return void 0;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Retrieve a list of checkpoint tuples from TDAI Memory based on the provided config.
|
|
497
|
+
* The checkpoints are ordered by checkpoint ID in descending order (newest first).
|
|
498
|
+
*/
|
|
499
|
+
async *list(config, options) {
|
|
500
|
+
const { limit, before, filter } = options ?? {};
|
|
501
|
+
if (!config?.configurable?.thread_id) {
|
|
502
|
+
throw new Error("Thread ID is required");
|
|
503
|
+
}
|
|
504
|
+
const query = {
|
|
505
|
+
collection: this.checkpointType
|
|
506
|
+
};
|
|
507
|
+
if (config?.configurable?.checkpoint_ns !== void 0 && config?.configurable?.checkpoint_ns !== null) {
|
|
508
|
+
query.checkpoint_ns = config.configurable.checkpoint_ns;
|
|
509
|
+
}
|
|
510
|
+
if (filter) {
|
|
511
|
+
Object.entries(filter).forEach(([key, value]) => {
|
|
512
|
+
query[`metadata.${key}`] = value;
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
if (before) {
|
|
516
|
+
query.checkpoint_id = { $lt: before.configurable?.checkpoint_id };
|
|
517
|
+
}
|
|
518
|
+
const { events = [] } = await this.memoryClient.queryEvents({
|
|
519
|
+
sessionId: config?.configurable?.thread_id || "default",
|
|
520
|
+
where: query,
|
|
521
|
+
orderBy: { checkpoint_id: import_agent_agents.Order.DESCENDING },
|
|
522
|
+
limit
|
|
523
|
+
});
|
|
524
|
+
for (const doc of events) {
|
|
525
|
+
const checkpoint = doc.checkpoint;
|
|
526
|
+
const metadata = doc.metadata || {};
|
|
527
|
+
yield {
|
|
528
|
+
config: {
|
|
529
|
+
configurable: {
|
|
530
|
+
thread_id: doc.thread_id,
|
|
531
|
+
checkpoint_ns: doc.checkpoint_ns,
|
|
532
|
+
checkpoint_id: doc.checkpoint_id
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
checkpoint,
|
|
536
|
+
metadata,
|
|
537
|
+
parentConfig: doc.parent_checkpoint_id ? {
|
|
538
|
+
configurable: {
|
|
539
|
+
thread_id: doc.thread_id,
|
|
540
|
+
checkpoint_ns: doc.checkpoint_ns,
|
|
541
|
+
checkpoint_id: doc.parent_checkpoint_id
|
|
542
|
+
}
|
|
543
|
+
} : void 0
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Saves a checkpoint to TDAI Memory. The checkpoint is associated with the
|
|
549
|
+
* provided config and its parent config (if any).
|
|
550
|
+
*/
|
|
551
|
+
async put(config, checkpoint, metadata) {
|
|
552
|
+
try {
|
|
553
|
+
const thread_id = config.configurable?.thread_id;
|
|
554
|
+
const checkpoint_ns = config.configurable?.checkpoint_ns ?? "";
|
|
555
|
+
const checkpoint_id = checkpoint.id;
|
|
556
|
+
if (thread_id === void 0) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
`The provided config must contain a configurable field with a "thread_id" field.`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
const doc = {
|
|
562
|
+
collection: this.checkpointType,
|
|
563
|
+
checkpoint_ns,
|
|
564
|
+
checkpoint_id,
|
|
565
|
+
parent_checkpoint_id: config.configurable?.checkpoint_id,
|
|
566
|
+
checkpoint,
|
|
567
|
+
metadata
|
|
568
|
+
};
|
|
569
|
+
const { events = [] } = await this.memoryClient.queryEvents({
|
|
570
|
+
sessionId: thread_id,
|
|
571
|
+
where: {
|
|
572
|
+
collection: this.checkpointType,
|
|
573
|
+
checkpoint_ns,
|
|
574
|
+
checkpoint_id
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
if (events[0]) {
|
|
578
|
+
this.memoryClient.deleteEvent({
|
|
579
|
+
sessionId: thread_id,
|
|
580
|
+
eventId: events[0].id
|
|
581
|
+
// messages: doc,
|
|
582
|
+
});
|
|
583
|
+
} else {
|
|
584
|
+
await this.memoryClient.appendEvent({
|
|
585
|
+
sessionId: thread_id,
|
|
586
|
+
messages: doc
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
configurable: {
|
|
591
|
+
thread_id,
|
|
592
|
+
checkpoint_ns,
|
|
593
|
+
checkpoint_id
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
} catch (error) {
|
|
597
|
+
console.error("Error saving checkpoint:", error);
|
|
598
|
+
throw error;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Saves intermediate writes associated with a checkpoint to TDAI Memory.
|
|
603
|
+
*/
|
|
604
|
+
async putWrites(config, writes, taskId) {
|
|
605
|
+
try {
|
|
606
|
+
const thread_id = config.configurable?.thread_id;
|
|
607
|
+
const checkpoint_ns = config.configurable?.checkpoint_ns;
|
|
608
|
+
const checkpoint_id = config.configurable?.checkpoint_id;
|
|
609
|
+
if (thread_id === void 0 || checkpoint_ns === void 0 || checkpoint_id === void 0) {
|
|
610
|
+
throw new Error(
|
|
611
|
+
`The provided config must contain a configurable field with "thread_id", "checkpoint_ns" and "checkpoint_id" fields.`
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
const writePromises = writes.map(async ([channel, value], idx) => {
|
|
615
|
+
const writeDoc = {
|
|
616
|
+
collection: this.checkpointWritesType,
|
|
617
|
+
checkpoint_ns,
|
|
618
|
+
checkpoint_id,
|
|
619
|
+
task_id: taskId,
|
|
620
|
+
idx,
|
|
621
|
+
channel,
|
|
622
|
+
value
|
|
623
|
+
// Store directly as JSON
|
|
624
|
+
};
|
|
625
|
+
return this.memoryClient.appendEvent({
|
|
626
|
+
sessionId: thread_id,
|
|
627
|
+
messages: writeDoc
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
await Promise.all(writePromises);
|
|
631
|
+
} catch (error) {
|
|
632
|
+
console.error("Error storing writes:", error);
|
|
633
|
+
throw error;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Delete all checkpoints and writes for a thread from TDAI Memory.
|
|
638
|
+
*/
|
|
639
|
+
async deleteThread(threadId) {
|
|
640
|
+
try {
|
|
641
|
+
await this.memoryClient.deleteSession({
|
|
642
|
+
sessionId: threadId
|
|
643
|
+
});
|
|
644
|
+
} catch (error) {
|
|
645
|
+
console.error("Error deleting thread:", error);
|
|
646
|
+
throw error;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Close the memory client connection
|
|
651
|
+
*/
|
|
652
|
+
close() {
|
|
653
|
+
this.memoryClient.close();
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// src/store/tdai-store.ts
|
|
658
|
+
var import_langgraph3 = require("@langchain/langgraph");
|
|
659
|
+
var TDAIStore = class extends import_langgraph3.BaseStore {
|
|
660
|
+
constructor(config) {
|
|
661
|
+
super();
|
|
662
|
+
this.isSetup = false;
|
|
663
|
+
this.isClosed = false;
|
|
664
|
+
this.client = config.memoryClient;
|
|
665
|
+
this.namespacePrefix = config.namespacePrefix || [];
|
|
666
|
+
this.ttlConfig = config.ttl;
|
|
667
|
+
this.ensureTables = config.ensureTables ?? true;
|
|
668
|
+
this.sessionId = config.sessionId || "default_session";
|
|
669
|
+
this.defaultStrategy = config.defaultStrategy || "store";
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Create a storage key from namespace and key
|
|
673
|
+
*/
|
|
674
|
+
createStorageKey(namespace, key) {
|
|
675
|
+
const fullNamespace = [...this.namespacePrefix, ...namespace];
|
|
676
|
+
return `${fullNamespace.join(":")}:${key}`;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Parse a storage key back to namespace and key
|
|
680
|
+
*/
|
|
681
|
+
parseStorageKey(storageKey) {
|
|
682
|
+
const parts = storageKey.split(":");
|
|
683
|
+
const prefixLength = this.namespacePrefix.length;
|
|
684
|
+
const namespace = parts.slice(prefixLength, -1);
|
|
685
|
+
const key = parts[parts.length - 1];
|
|
686
|
+
return { namespace, key };
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Put an item with optional TTL.
|
|
690
|
+
*/
|
|
691
|
+
async put(namespace, key, value, index, options) {
|
|
692
|
+
if (!this.isSetup && this.ensureTables) {
|
|
693
|
+
await this.setup();
|
|
694
|
+
}
|
|
695
|
+
const storageKey = this.createStorageKey(namespace, key);
|
|
696
|
+
const content = JSON.stringify({
|
|
697
|
+
storageKey,
|
|
698
|
+
namespace,
|
|
699
|
+
key,
|
|
700
|
+
value,
|
|
701
|
+
index,
|
|
702
|
+
ttl: options?.ttl || this.ttlConfig?.defaultTtlSeconds
|
|
703
|
+
});
|
|
704
|
+
await this.client.appendRecord({
|
|
705
|
+
sessionId: this.sessionId,
|
|
706
|
+
content,
|
|
707
|
+
strategy: this.defaultStrategy
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Get an item by namespace and key.
|
|
712
|
+
*/
|
|
713
|
+
async get(namespace, key) {
|
|
714
|
+
if (!this.isSetup && this.ensureTables) {
|
|
715
|
+
await this.setup();
|
|
716
|
+
}
|
|
717
|
+
const storageKey = this.createStorageKey(namespace, key);
|
|
718
|
+
try {
|
|
719
|
+
const { records = [] } = await this.client.searchRecords({
|
|
720
|
+
content: storageKey,
|
|
721
|
+
sessionId: this.sessionId,
|
|
722
|
+
limit: 1
|
|
723
|
+
});
|
|
724
|
+
if (!records.length) {
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
const record = records[0];
|
|
728
|
+
const data = JSON.parse(record.record_content);
|
|
729
|
+
if (data.ttl && record.created_at) {
|
|
730
|
+
const createdTime = new Date(record.created_at).getTime();
|
|
731
|
+
const now = Date.now();
|
|
732
|
+
if (now > createdTime + data.ttl * 1e3) {
|
|
733
|
+
await this.delete(namespace, key);
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
namespace,
|
|
739
|
+
key,
|
|
740
|
+
value: data.value,
|
|
741
|
+
createdAt: new Date(record.created_at || Date.now()),
|
|
742
|
+
updatedAt: new Date(record.updated_at || Date.now())
|
|
743
|
+
};
|
|
744
|
+
} catch (error) {
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Delete an item by namespace and key.
|
|
750
|
+
*/
|
|
751
|
+
async delete(namespace, key) {
|
|
752
|
+
if (!this.isSetup && this.ensureTables) {
|
|
753
|
+
await this.setup();
|
|
754
|
+
}
|
|
755
|
+
const storageKey = this.createStorageKey(namespace, key);
|
|
756
|
+
try {
|
|
757
|
+
const { records = [] } = await this.client.searchRecords({
|
|
758
|
+
content: storageKey,
|
|
759
|
+
sessionId: this.sessionId,
|
|
760
|
+
limit: 1
|
|
761
|
+
});
|
|
762
|
+
const record = records[0];
|
|
763
|
+
if (record) {
|
|
764
|
+
await this.client.deleteRecord({
|
|
765
|
+
sessionId: this.sessionId,
|
|
766
|
+
recordId: record.record_id
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
} catch (error) {
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* List namespaces with optional filtering.
|
|
774
|
+
*/
|
|
775
|
+
async listNamespaces(options = {}) {
|
|
776
|
+
if (!this.isSetup && this.ensureTables) {
|
|
777
|
+
await this.setup();
|
|
778
|
+
}
|
|
779
|
+
const { prefix, suffix, maxDepth, limit = 100, offset = 0 } = options;
|
|
780
|
+
try {
|
|
781
|
+
const { records = [] } = await this.client.queryRecords({
|
|
782
|
+
limit: 1e3
|
|
783
|
+
// Large limit to get all records
|
|
784
|
+
});
|
|
785
|
+
const namespaceSet = /* @__PURE__ */ new Set();
|
|
786
|
+
for (const record of records) {
|
|
787
|
+
try {
|
|
788
|
+
const data = JSON.parse(record.record_content);
|
|
789
|
+
if (data.namespace) {
|
|
790
|
+
const namespace = data.namespace;
|
|
791
|
+
if (prefix && prefix.length > 0) {
|
|
792
|
+
const hasPrefix = prefix.every((p, i) => namespace[i] === p);
|
|
793
|
+
if (!hasPrefix) continue;
|
|
794
|
+
}
|
|
795
|
+
if (suffix && suffix.length > 0) {
|
|
796
|
+
const namespaceSuffix = namespace.slice(-suffix.length);
|
|
797
|
+
if (JSON.stringify(namespaceSuffix) !== JSON.stringify(suffix)) {
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (maxDepth !== void 0 && namespace.length > maxDepth) {
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
namespaceSet.add(JSON.stringify(namespace));
|
|
805
|
+
}
|
|
806
|
+
} catch (error) {
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const namespaces = Array.from(namespaceSet).map((ns) => JSON.parse(ns)).sort().slice(offset, offset + limit);
|
|
810
|
+
return namespaces;
|
|
811
|
+
} catch (error) {
|
|
812
|
+
return [];
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Execute multiple operations in a single batch.
|
|
817
|
+
*/
|
|
818
|
+
async batch(operations) {
|
|
819
|
+
if (!this.isSetup && this.ensureTables) {
|
|
820
|
+
await this.setup();
|
|
821
|
+
}
|
|
822
|
+
const results = [];
|
|
823
|
+
for (const operation of operations) {
|
|
824
|
+
if ("namespacePrefix" in operation) {
|
|
825
|
+
results.push(await this.executeSearch(operation));
|
|
826
|
+
} else if ("key" in operation && !("value" in operation)) {
|
|
827
|
+
const getOp = operation;
|
|
828
|
+
results.push(await this.get(getOp.namespace, getOp.key));
|
|
829
|
+
} else if ("value" in operation) {
|
|
830
|
+
const putOp = operation;
|
|
831
|
+
if (putOp.value !== null) {
|
|
832
|
+
await this.put(
|
|
833
|
+
putOp.namespace,
|
|
834
|
+
putOp.key,
|
|
835
|
+
putOp.value,
|
|
836
|
+
putOp.index,
|
|
837
|
+
putOp.options
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
results.push(void 0);
|
|
841
|
+
} else if ("matchConditions" in operation) {
|
|
842
|
+
const listOp = operation;
|
|
843
|
+
results.push(await this.executeListNamespaces(listOp));
|
|
844
|
+
} else {
|
|
845
|
+
throw new Error(
|
|
846
|
+
`Unsupported operation type: ${JSON.stringify(operation)}`
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return results;
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Execute search operation
|
|
854
|
+
*/
|
|
855
|
+
async executeSearch(operation) {
|
|
856
|
+
const { namespacePrefix, ...searchOptions } = operation;
|
|
857
|
+
return this.search(namespacePrefix, searchOptions);
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Execute list namespaces operation
|
|
861
|
+
*/
|
|
862
|
+
async executeListNamespaces(operation) {
|
|
863
|
+
const { matchConditions, maxDepth, limit = 100, offset = 0 } = operation;
|
|
864
|
+
let prefix;
|
|
865
|
+
let suffix;
|
|
866
|
+
if (matchConditions && matchConditions.length > 0) {
|
|
867
|
+
for (const condition of matchConditions) {
|
|
868
|
+
if (condition.matchType === "prefix") {
|
|
869
|
+
prefix = condition.path;
|
|
870
|
+
} else if (condition.matchType === "suffix") {
|
|
871
|
+
suffix = condition.path;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return this.listNamespaces({
|
|
876
|
+
prefix,
|
|
877
|
+
suffix,
|
|
878
|
+
maxDepth,
|
|
879
|
+
limit,
|
|
880
|
+
offset
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Initialize the store.
|
|
885
|
+
*/
|
|
886
|
+
async setup() {
|
|
887
|
+
if (this.isSetup) return;
|
|
888
|
+
if (this.ttlConfig?.sweepIntervalMinutes) {
|
|
889
|
+
const intervalMs = this.ttlConfig.sweepIntervalMinutes * 60 * 1e3;
|
|
890
|
+
this.sweepInterval = setInterval(async () => {
|
|
891
|
+
try {
|
|
892
|
+
await this.sweepExpiredItems();
|
|
893
|
+
} catch (error) {
|
|
894
|
+
console.error("Error during TTL sweep:", error);
|
|
895
|
+
}
|
|
896
|
+
}, intervalMs);
|
|
897
|
+
}
|
|
898
|
+
this.isSetup = true;
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Start the store.
|
|
902
|
+
*/
|
|
903
|
+
async start() {
|
|
904
|
+
if (this.ensureTables && !this.isSetup) {
|
|
905
|
+
await this.setup();
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Stop the store and close all connections.
|
|
910
|
+
*/
|
|
911
|
+
async stop() {
|
|
912
|
+
if (this.isClosed) return;
|
|
913
|
+
if (this.sweepInterval) {
|
|
914
|
+
clearInterval(this.sweepInterval);
|
|
915
|
+
this.sweepInterval = void 0;
|
|
916
|
+
}
|
|
917
|
+
this.client.close();
|
|
918
|
+
this.isClosed = true;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Manually sweep expired items from the store.
|
|
922
|
+
*/
|
|
923
|
+
async sweepExpiredItems() {
|
|
924
|
+
if (!this.isSetup && this.ensureTables) {
|
|
925
|
+
await this.setup();
|
|
926
|
+
}
|
|
927
|
+
try {
|
|
928
|
+
const { records = [] } = await this.client.queryRecords({
|
|
929
|
+
limit: 1e3
|
|
930
|
+
});
|
|
931
|
+
let cleanedCount = 0;
|
|
932
|
+
const now = Date.now();
|
|
933
|
+
for (const record of records) {
|
|
934
|
+
try {
|
|
935
|
+
const data = JSON.parse(record.record_content);
|
|
936
|
+
if (data.ttl && data.createdAt) {
|
|
937
|
+
const createdTime = new Date(data.createdAt).getTime();
|
|
938
|
+
if (now > createdTime + data.ttl * 1e3) {
|
|
939
|
+
await this.client.deleteRecord({
|
|
940
|
+
sessionId: this.sessionId,
|
|
941
|
+
recordId: record.record_id
|
|
942
|
+
});
|
|
943
|
+
cleanedCount++;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
} catch (error) {
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return cleanedCount;
|
|
950
|
+
} catch (error) {
|
|
951
|
+
return 0;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Get statistics about the store.
|
|
956
|
+
*/
|
|
957
|
+
async getStats() {
|
|
958
|
+
if (!this.isSetup && this.ensureTables) {
|
|
959
|
+
await this.setup();
|
|
960
|
+
}
|
|
961
|
+
try {
|
|
962
|
+
const { records = [] } = await this.client.queryRecords({
|
|
963
|
+
limit: 1e3
|
|
964
|
+
});
|
|
965
|
+
let totalItems = 0;
|
|
966
|
+
let expiredItems = 0;
|
|
967
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
968
|
+
const dates = [];
|
|
969
|
+
const now = Date.now();
|
|
970
|
+
for (const record of records) {
|
|
971
|
+
try {
|
|
972
|
+
const data = JSON.parse(record.record_content);
|
|
973
|
+
totalItems++;
|
|
974
|
+
if (data.namespace) {
|
|
975
|
+
namespaces.add(data.namespace.join(":"));
|
|
976
|
+
}
|
|
977
|
+
if (data.createdAt) {
|
|
978
|
+
dates.push(new Date(data.createdAt));
|
|
979
|
+
}
|
|
980
|
+
if (data.ttl && data.createdAt) {
|
|
981
|
+
const createdTime = new Date(data.createdAt).getTime();
|
|
982
|
+
if (now > createdTime + data.ttl * 1e3) {
|
|
983
|
+
expiredItems++;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
} catch (error) {
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
const oldestItem = dates.length > 0 ? new Date(Math.min(...dates.map((d) => d.getTime()))) : null;
|
|
990
|
+
const newestItem = dates.length > 0 ? new Date(Math.max(...dates.map((d) => d.getTime()))) : null;
|
|
991
|
+
return {
|
|
992
|
+
totalItems,
|
|
993
|
+
expiredItems,
|
|
994
|
+
namespaceCount: namespaces.size,
|
|
995
|
+
oldestItem,
|
|
996
|
+
newestItem
|
|
997
|
+
};
|
|
998
|
+
} catch (error) {
|
|
999
|
+
return {
|
|
1000
|
+
totalItems: 0,
|
|
1001
|
+
expiredItems: 0,
|
|
1002
|
+
namespaceCount: 0,
|
|
1003
|
+
oldestItem: null,
|
|
1004
|
+
newestItem: null
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Search for items in the store with support for text search and filtering.
|
|
1010
|
+
*/
|
|
1011
|
+
async search(namespacePrefix, options = {}) {
|
|
1012
|
+
if (!this.isSetup && this.ensureTables) {
|
|
1013
|
+
await this.setup();
|
|
1014
|
+
}
|
|
1015
|
+
const { filter, query, limit = 10, offset = 0, refreshTtl } = options;
|
|
1016
|
+
try {
|
|
1017
|
+
const namespaceKey = [...this.namespacePrefix, ...namespacePrefix].join(
|
|
1018
|
+
":"
|
|
1019
|
+
);
|
|
1020
|
+
let searchResult;
|
|
1021
|
+
if (query) {
|
|
1022
|
+
searchResult = await this.client.searchRecords({
|
|
1023
|
+
content: query,
|
|
1024
|
+
limit: limit + offset
|
|
1025
|
+
});
|
|
1026
|
+
} else {
|
|
1027
|
+
searchResult = await this.client.queryRecords({
|
|
1028
|
+
limit: limit + offset
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
const items = [];
|
|
1032
|
+
const now = Date.now();
|
|
1033
|
+
if (searchResult.records) {
|
|
1034
|
+
for (const record of searchResult.records.slice(
|
|
1035
|
+
offset,
|
|
1036
|
+
offset + limit
|
|
1037
|
+
)) {
|
|
1038
|
+
try {
|
|
1039
|
+
const data = JSON.parse(record.record_content);
|
|
1040
|
+
if (namespaceKey && !data.storageKey?.startsWith(namespaceKey)) {
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
if (data.ttl && data.createdAt) {
|
|
1044
|
+
const createdTime = new Date(data.createdAt).getTime();
|
|
1045
|
+
if (now > createdTime + data.ttl * 1e3) {
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
if (filter) {
|
|
1050
|
+
let matches = true;
|
|
1051
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
1052
|
+
if (data.value[key] !== value) {
|
|
1053
|
+
matches = false;
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
if (!matches) continue;
|
|
1058
|
+
}
|
|
1059
|
+
const item = {
|
|
1060
|
+
namespace: data.namespace,
|
|
1061
|
+
key: data.key,
|
|
1062
|
+
value: data.value,
|
|
1063
|
+
createdAt: new Date(record.created_at || Date.now()),
|
|
1064
|
+
updatedAt: new Date(record.updated_at || Date.now())
|
|
1065
|
+
};
|
|
1066
|
+
items.push(item);
|
|
1067
|
+
if (refreshTtl && this.ttlConfig?.defaultTtlSeconds) {
|
|
1068
|
+
await this.put(data.namespace, data.key, data.value, void 0, {
|
|
1069
|
+
ttl: this.ttlConfig.defaultTtlSeconds
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return items;
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
return [];
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1083
|
+
0 && (module.exports = {
|
|
1084
|
+
AGKitPropertiesAnnotation,
|
|
1085
|
+
AGKitStateAnnotation,
|
|
1086
|
+
LanggraphAgent,
|
|
1087
|
+
TDAISaver,
|
|
1088
|
+
TDAIStore
|
|
1089
|
+
});
|
|
1090
|
+
//# sourceMappingURL=index.js.map
|