@mastra/dynamodb 0.0.0-add-libsql-changeset-20250910154739
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 +875 -0
- package/LICENSE.md +15 -0
- package/README.md +144 -0
- package/dist/entities/eval.d.ts +102 -0
- package/dist/entities/eval.d.ts.map +1 -0
- package/dist/entities/index.d.ts +746 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/message.d.ts +100 -0
- package/dist/entities/message.d.ts.map +1 -0
- package/dist/entities/resource.d.ts +54 -0
- package/dist/entities/resource.d.ts.map +1 -0
- package/dist/entities/score.d.ts +229 -0
- package/dist/entities/score.d.ts.map +1 -0
- package/dist/entities/thread.d.ts +69 -0
- package/dist/entities/thread.d.ts.map +1 -0
- package/dist/entities/trace.d.ts +127 -0
- package/dist/entities/trace.d.ts.map +1 -0
- package/dist/entities/utils.d.ts +21 -0
- package/dist/entities/utils.d.ts.map +1 -0
- package/dist/entities/workflow-snapshot.d.ts +74 -0
- package/dist/entities/workflow-snapshot.d.ts.map +1 -0
- package/dist/index.cjs +3139 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3137 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/domains/legacy-evals/index.d.ts +19 -0
- package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +89 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -0
- package/dist/storage/domains/operations/index.d.ts +69 -0
- package/dist/storage/domains/operations/index.d.ts.map +1 -0
- package/dist/storage/domains/score/index.d.ts +43 -0
- package/dist/storage/domains/score/index.d.ts.map +1 -0
- package/dist/storage/domains/traces/index.d.ts +28 -0
- package/dist/storage/domains/traces/index.d.ts.map +1 -0
- package/dist/storage/domains/workflows/index.d.ts +50 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +249 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/package.json +70 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3139 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var clientDynamodb = require('@aws-sdk/client-dynamodb');
|
|
4
|
+
var libDynamodb = require('@aws-sdk/lib-dynamodb');
|
|
5
|
+
var error = require('@mastra/core/error');
|
|
6
|
+
var storage = require('@mastra/core/storage');
|
|
7
|
+
var electrodb = require('electrodb');
|
|
8
|
+
var agent = require('@mastra/core/agent');
|
|
9
|
+
|
|
10
|
+
// src/storage/index.ts
|
|
11
|
+
|
|
12
|
+
// src/entities/utils.ts
|
|
13
|
+
var baseAttributes = {
|
|
14
|
+
createdAt: {
|
|
15
|
+
type: "string",
|
|
16
|
+
required: true,
|
|
17
|
+
readOnly: true,
|
|
18
|
+
// Convert Date to ISO string on set
|
|
19
|
+
set: (value) => {
|
|
20
|
+
if (value instanceof Date) {
|
|
21
|
+
return value.toISOString();
|
|
22
|
+
}
|
|
23
|
+
return value || (/* @__PURE__ */ new Date()).toISOString();
|
|
24
|
+
},
|
|
25
|
+
// Initialize with current timestamp if not provided
|
|
26
|
+
default: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
27
|
+
},
|
|
28
|
+
updatedAt: {
|
|
29
|
+
type: "string",
|
|
30
|
+
required: true,
|
|
31
|
+
// Convert Date to ISO string on set
|
|
32
|
+
set: (value) => {
|
|
33
|
+
if (value instanceof Date) {
|
|
34
|
+
return value.toISOString();
|
|
35
|
+
}
|
|
36
|
+
return value || (/* @__PURE__ */ new Date()).toISOString();
|
|
37
|
+
},
|
|
38
|
+
// Always use current timestamp when creating/updating
|
|
39
|
+
default: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
40
|
+
},
|
|
41
|
+
metadata: {
|
|
42
|
+
type: "string",
|
|
43
|
+
// JSON stringified
|
|
44
|
+
// Stringify objects on set
|
|
45
|
+
set: (value) => {
|
|
46
|
+
if (value && typeof value !== "string") {
|
|
47
|
+
return JSON.stringify(value);
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
},
|
|
51
|
+
// Parse JSON string to object on get
|
|
52
|
+
get: (value) => {
|
|
53
|
+
if (value) {
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(value);
|
|
56
|
+
} catch {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/entities/eval.ts
|
|
66
|
+
var evalEntity = new electrodb.Entity({
|
|
67
|
+
model: {
|
|
68
|
+
entity: "eval",
|
|
69
|
+
version: "1",
|
|
70
|
+
service: "mastra"
|
|
71
|
+
},
|
|
72
|
+
attributes: {
|
|
73
|
+
entity: {
|
|
74
|
+
type: "string",
|
|
75
|
+
required: true
|
|
76
|
+
},
|
|
77
|
+
...baseAttributes,
|
|
78
|
+
input: {
|
|
79
|
+
type: "string",
|
|
80
|
+
required: true
|
|
81
|
+
},
|
|
82
|
+
output: {
|
|
83
|
+
type: "string",
|
|
84
|
+
required: true
|
|
85
|
+
},
|
|
86
|
+
result: {
|
|
87
|
+
type: "string",
|
|
88
|
+
// JSON stringified
|
|
89
|
+
required: true,
|
|
90
|
+
// Stringify object on set
|
|
91
|
+
set: (value) => {
|
|
92
|
+
if (value && typeof value !== "string") {
|
|
93
|
+
return JSON.stringify(value);
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
},
|
|
97
|
+
// Parse JSON string to object on get
|
|
98
|
+
get: (value) => {
|
|
99
|
+
if (value) {
|
|
100
|
+
return JSON.parse(value);
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
agent_name: {
|
|
106
|
+
type: "string",
|
|
107
|
+
required: true
|
|
108
|
+
},
|
|
109
|
+
metric_name: {
|
|
110
|
+
type: "string",
|
|
111
|
+
required: true
|
|
112
|
+
},
|
|
113
|
+
instructions: {
|
|
114
|
+
type: "string",
|
|
115
|
+
required: true
|
|
116
|
+
},
|
|
117
|
+
test_info: {
|
|
118
|
+
type: "string",
|
|
119
|
+
// JSON stringified
|
|
120
|
+
required: false,
|
|
121
|
+
// Stringify object on set
|
|
122
|
+
set: (value) => {
|
|
123
|
+
if (value && typeof value !== "string") {
|
|
124
|
+
return JSON.stringify(value);
|
|
125
|
+
}
|
|
126
|
+
return value;
|
|
127
|
+
},
|
|
128
|
+
// Parse JSON string to object on get
|
|
129
|
+
get: (value) => {
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
global_run_id: {
|
|
134
|
+
type: "string",
|
|
135
|
+
required: true
|
|
136
|
+
},
|
|
137
|
+
run_id: {
|
|
138
|
+
type: "string",
|
|
139
|
+
required: true
|
|
140
|
+
},
|
|
141
|
+
created_at: {
|
|
142
|
+
type: "string",
|
|
143
|
+
required: true,
|
|
144
|
+
// Initialize with current timestamp if not provided
|
|
145
|
+
default: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
146
|
+
// Convert Date to ISO string on set
|
|
147
|
+
set: (value) => {
|
|
148
|
+
if (value instanceof Date) {
|
|
149
|
+
return value.toISOString();
|
|
150
|
+
}
|
|
151
|
+
return value || (/* @__PURE__ */ new Date()).toISOString();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
indexes: {
|
|
156
|
+
primary: {
|
|
157
|
+
pk: { field: "pk", composite: ["entity", "run_id"] },
|
|
158
|
+
sk: { field: "sk", composite: [] }
|
|
159
|
+
},
|
|
160
|
+
byAgent: {
|
|
161
|
+
index: "gsi1",
|
|
162
|
+
pk: { field: "gsi1pk", composite: ["entity", "agent_name"] },
|
|
163
|
+
sk: { field: "gsi1sk", composite: ["created_at"] }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
var messageEntity = new electrodb.Entity({
|
|
168
|
+
model: {
|
|
169
|
+
entity: "message",
|
|
170
|
+
version: "1",
|
|
171
|
+
service: "mastra"
|
|
172
|
+
},
|
|
173
|
+
attributes: {
|
|
174
|
+
entity: {
|
|
175
|
+
type: "string",
|
|
176
|
+
required: true
|
|
177
|
+
},
|
|
178
|
+
...baseAttributes,
|
|
179
|
+
id: {
|
|
180
|
+
type: "string",
|
|
181
|
+
required: true
|
|
182
|
+
},
|
|
183
|
+
threadId: {
|
|
184
|
+
type: "string",
|
|
185
|
+
required: true
|
|
186
|
+
},
|
|
187
|
+
content: {
|
|
188
|
+
type: "string",
|
|
189
|
+
required: true,
|
|
190
|
+
// Stringify content object on set if it's not already a string
|
|
191
|
+
set: (value) => {
|
|
192
|
+
if (value && typeof value !== "string") {
|
|
193
|
+
return JSON.stringify(value);
|
|
194
|
+
}
|
|
195
|
+
return value;
|
|
196
|
+
},
|
|
197
|
+
// Parse JSON string to object on get ONLY if it looks like JSON
|
|
198
|
+
get: (value) => {
|
|
199
|
+
if (value && typeof value === "string") {
|
|
200
|
+
try {
|
|
201
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
202
|
+
return JSON.parse(value);
|
|
203
|
+
}
|
|
204
|
+
} catch {
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return value;
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
role: {
|
|
212
|
+
type: "string",
|
|
213
|
+
required: true
|
|
214
|
+
},
|
|
215
|
+
type: {
|
|
216
|
+
type: "string",
|
|
217
|
+
default: "text"
|
|
218
|
+
},
|
|
219
|
+
resourceId: {
|
|
220
|
+
type: "string",
|
|
221
|
+
required: false
|
|
222
|
+
},
|
|
223
|
+
toolCallIds: {
|
|
224
|
+
type: "string",
|
|
225
|
+
required: false,
|
|
226
|
+
set: (value) => {
|
|
227
|
+
if (Array.isArray(value)) {
|
|
228
|
+
return JSON.stringify(value);
|
|
229
|
+
}
|
|
230
|
+
return value;
|
|
231
|
+
},
|
|
232
|
+
// Parse JSON string to array on get
|
|
233
|
+
get: (value) => {
|
|
234
|
+
if (value && typeof value === "string") {
|
|
235
|
+
try {
|
|
236
|
+
return JSON.parse(value);
|
|
237
|
+
} catch {
|
|
238
|
+
return value;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return value;
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
toolCallArgs: {
|
|
245
|
+
type: "string",
|
|
246
|
+
required: false,
|
|
247
|
+
set: (value) => {
|
|
248
|
+
if (value && typeof value !== "string") {
|
|
249
|
+
return JSON.stringify(value);
|
|
250
|
+
}
|
|
251
|
+
return value;
|
|
252
|
+
},
|
|
253
|
+
// Parse JSON string to object on get
|
|
254
|
+
get: (value) => {
|
|
255
|
+
if (value && typeof value === "string") {
|
|
256
|
+
try {
|
|
257
|
+
return JSON.parse(value);
|
|
258
|
+
} catch {
|
|
259
|
+
return value;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return value;
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
toolNames: {
|
|
266
|
+
type: "string",
|
|
267
|
+
required: false,
|
|
268
|
+
set: (value) => {
|
|
269
|
+
if (Array.isArray(value)) {
|
|
270
|
+
return JSON.stringify(value);
|
|
271
|
+
}
|
|
272
|
+
return value;
|
|
273
|
+
},
|
|
274
|
+
// Parse JSON string to array on get
|
|
275
|
+
get: (value) => {
|
|
276
|
+
if (value && typeof value === "string") {
|
|
277
|
+
try {
|
|
278
|
+
return JSON.parse(value);
|
|
279
|
+
} catch {
|
|
280
|
+
return value;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return value;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
indexes: {
|
|
288
|
+
primary: {
|
|
289
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
290
|
+
sk: { field: "sk", composite: ["entity"] }
|
|
291
|
+
},
|
|
292
|
+
byThread: {
|
|
293
|
+
index: "gsi1",
|
|
294
|
+
pk: { field: "gsi1pk", composite: ["entity", "threadId"] },
|
|
295
|
+
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
var resourceEntity = new electrodb.Entity({
|
|
300
|
+
model: {
|
|
301
|
+
entity: "resource",
|
|
302
|
+
version: "1",
|
|
303
|
+
service: "mastra"
|
|
304
|
+
},
|
|
305
|
+
attributes: {
|
|
306
|
+
entity: {
|
|
307
|
+
type: "string",
|
|
308
|
+
required: true
|
|
309
|
+
},
|
|
310
|
+
...baseAttributes,
|
|
311
|
+
id: {
|
|
312
|
+
type: "string",
|
|
313
|
+
required: true
|
|
314
|
+
},
|
|
315
|
+
workingMemory: {
|
|
316
|
+
type: "string",
|
|
317
|
+
required: false
|
|
318
|
+
},
|
|
319
|
+
metadata: {
|
|
320
|
+
type: "string",
|
|
321
|
+
required: false,
|
|
322
|
+
// Stringify content object on set if it's not already a string
|
|
323
|
+
set: (value) => {
|
|
324
|
+
if (value && typeof value !== "string") {
|
|
325
|
+
return JSON.stringify(value);
|
|
326
|
+
}
|
|
327
|
+
return value;
|
|
328
|
+
},
|
|
329
|
+
// Parse JSON string to object on get ONLY if it looks like JSON
|
|
330
|
+
get: (value) => {
|
|
331
|
+
if (value && typeof value === "string") {
|
|
332
|
+
try {
|
|
333
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
334
|
+
return JSON.parse(value);
|
|
335
|
+
}
|
|
336
|
+
} catch {
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return value;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
indexes: {
|
|
345
|
+
primary: {
|
|
346
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
347
|
+
sk: { field: "sk", composite: ["entity"] }
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
var scoreEntity = new electrodb.Entity({
|
|
352
|
+
model: {
|
|
353
|
+
entity: "score",
|
|
354
|
+
version: "1",
|
|
355
|
+
service: "mastra"
|
|
356
|
+
},
|
|
357
|
+
attributes: {
|
|
358
|
+
entity: {
|
|
359
|
+
type: "string",
|
|
360
|
+
required: true
|
|
361
|
+
},
|
|
362
|
+
...baseAttributes,
|
|
363
|
+
id: {
|
|
364
|
+
type: "string",
|
|
365
|
+
required: true
|
|
366
|
+
},
|
|
367
|
+
scorerId: {
|
|
368
|
+
type: "string",
|
|
369
|
+
required: true
|
|
370
|
+
},
|
|
371
|
+
traceId: {
|
|
372
|
+
type: "string",
|
|
373
|
+
required: false
|
|
374
|
+
},
|
|
375
|
+
runId: {
|
|
376
|
+
type: "string",
|
|
377
|
+
required: true
|
|
378
|
+
},
|
|
379
|
+
scorer: {
|
|
380
|
+
type: "string",
|
|
381
|
+
required: true,
|
|
382
|
+
set: (value) => {
|
|
383
|
+
if (value && typeof value !== "string") {
|
|
384
|
+
return JSON.stringify(value);
|
|
385
|
+
}
|
|
386
|
+
return value;
|
|
387
|
+
},
|
|
388
|
+
get: (value) => {
|
|
389
|
+
if (value && typeof value === "string") {
|
|
390
|
+
try {
|
|
391
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
392
|
+
return JSON.parse(value);
|
|
393
|
+
}
|
|
394
|
+
} catch {
|
|
395
|
+
return value;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return value;
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
extractStepResult: {
|
|
402
|
+
type: "string",
|
|
403
|
+
required: false,
|
|
404
|
+
set: (value) => {
|
|
405
|
+
if (value && typeof value !== "string") {
|
|
406
|
+
return JSON.stringify(value);
|
|
407
|
+
}
|
|
408
|
+
return value;
|
|
409
|
+
},
|
|
410
|
+
get: (value) => {
|
|
411
|
+
if (value && typeof value === "string") {
|
|
412
|
+
try {
|
|
413
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
414
|
+
return JSON.parse(value);
|
|
415
|
+
}
|
|
416
|
+
} catch {
|
|
417
|
+
return value;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return value;
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
preprocessStepResult: {
|
|
424
|
+
type: "string",
|
|
425
|
+
required: false,
|
|
426
|
+
set: (value) => {
|
|
427
|
+
if (value && typeof value !== "string") {
|
|
428
|
+
return JSON.stringify(value);
|
|
429
|
+
}
|
|
430
|
+
return value;
|
|
431
|
+
},
|
|
432
|
+
get: (value) => {
|
|
433
|
+
if (value && typeof value === "string") {
|
|
434
|
+
try {
|
|
435
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
436
|
+
return JSON.parse(value);
|
|
437
|
+
}
|
|
438
|
+
} catch {
|
|
439
|
+
return value;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return value;
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
analyzeStepResult: {
|
|
446
|
+
type: "string",
|
|
447
|
+
required: false,
|
|
448
|
+
set: (value) => {
|
|
449
|
+
if (value && typeof value !== "string") {
|
|
450
|
+
return JSON.stringify(value);
|
|
451
|
+
}
|
|
452
|
+
return value;
|
|
453
|
+
},
|
|
454
|
+
get: (value) => {
|
|
455
|
+
if (value && typeof value === "string") {
|
|
456
|
+
try {
|
|
457
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
458
|
+
return JSON.parse(value);
|
|
459
|
+
}
|
|
460
|
+
} catch {
|
|
461
|
+
return value;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return value;
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
score: {
|
|
468
|
+
type: "number",
|
|
469
|
+
required: true
|
|
470
|
+
},
|
|
471
|
+
reason: {
|
|
472
|
+
type: "string",
|
|
473
|
+
required: false
|
|
474
|
+
},
|
|
475
|
+
extractPrompt: {
|
|
476
|
+
type: "string",
|
|
477
|
+
required: false
|
|
478
|
+
},
|
|
479
|
+
analyzePrompt: {
|
|
480
|
+
type: "string",
|
|
481
|
+
required: false
|
|
482
|
+
},
|
|
483
|
+
// Deprecated in favor of generateReasonPrompt
|
|
484
|
+
reasonPrompt: {
|
|
485
|
+
type: "string",
|
|
486
|
+
required: false
|
|
487
|
+
},
|
|
488
|
+
generateScorePrompt: {
|
|
489
|
+
type: "string",
|
|
490
|
+
required: false
|
|
491
|
+
},
|
|
492
|
+
generateReasonPrompt: {
|
|
493
|
+
type: "string",
|
|
494
|
+
required: false
|
|
495
|
+
},
|
|
496
|
+
input: {
|
|
497
|
+
type: "string",
|
|
498
|
+
required: true,
|
|
499
|
+
set: (value) => {
|
|
500
|
+
if (value && typeof value !== "string") {
|
|
501
|
+
return JSON.stringify(value);
|
|
502
|
+
}
|
|
503
|
+
return value;
|
|
504
|
+
},
|
|
505
|
+
get: (value) => {
|
|
506
|
+
if (value && typeof value === "string") {
|
|
507
|
+
try {
|
|
508
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
509
|
+
return JSON.parse(value);
|
|
510
|
+
}
|
|
511
|
+
} catch {
|
|
512
|
+
return value;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return value;
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
output: {
|
|
519
|
+
type: "string",
|
|
520
|
+
required: true,
|
|
521
|
+
set: (value) => {
|
|
522
|
+
if (value && typeof value !== "string") {
|
|
523
|
+
return JSON.stringify(value);
|
|
524
|
+
}
|
|
525
|
+
return value;
|
|
526
|
+
},
|
|
527
|
+
get: (value) => {
|
|
528
|
+
if (value && typeof value === "string") {
|
|
529
|
+
try {
|
|
530
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
531
|
+
return JSON.parse(value);
|
|
532
|
+
}
|
|
533
|
+
} catch {
|
|
534
|
+
return value;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return value;
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
additionalContext: {
|
|
541
|
+
type: "string",
|
|
542
|
+
required: false,
|
|
543
|
+
set: (value) => {
|
|
544
|
+
if (value && typeof value !== "string") {
|
|
545
|
+
return JSON.stringify(value);
|
|
546
|
+
}
|
|
547
|
+
return value;
|
|
548
|
+
},
|
|
549
|
+
get: (value) => {
|
|
550
|
+
if (value && typeof value === "string") {
|
|
551
|
+
try {
|
|
552
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
553
|
+
return JSON.parse(value);
|
|
554
|
+
}
|
|
555
|
+
} catch {
|
|
556
|
+
return value;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return value;
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
runtimeContext: {
|
|
563
|
+
type: "string",
|
|
564
|
+
required: false,
|
|
565
|
+
set: (value) => {
|
|
566
|
+
if (value && typeof value !== "string") {
|
|
567
|
+
return JSON.stringify(value);
|
|
568
|
+
}
|
|
569
|
+
return value;
|
|
570
|
+
},
|
|
571
|
+
get: (value) => {
|
|
572
|
+
if (value && typeof value === "string") {
|
|
573
|
+
try {
|
|
574
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
575
|
+
return JSON.parse(value);
|
|
576
|
+
}
|
|
577
|
+
} catch {
|
|
578
|
+
return value;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return value;
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
entityType: {
|
|
585
|
+
type: "string",
|
|
586
|
+
required: false
|
|
587
|
+
},
|
|
588
|
+
entityData: {
|
|
589
|
+
type: "string",
|
|
590
|
+
required: false,
|
|
591
|
+
set: (value) => {
|
|
592
|
+
if (value && typeof value !== "string") {
|
|
593
|
+
return JSON.stringify(value);
|
|
594
|
+
}
|
|
595
|
+
return value;
|
|
596
|
+
},
|
|
597
|
+
get: (value) => {
|
|
598
|
+
if (value && typeof value === "string") {
|
|
599
|
+
try {
|
|
600
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
601
|
+
return JSON.parse(value);
|
|
602
|
+
}
|
|
603
|
+
} catch {
|
|
604
|
+
return value;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return value;
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
entityId: {
|
|
611
|
+
type: "string",
|
|
612
|
+
required: false
|
|
613
|
+
},
|
|
614
|
+
source: {
|
|
615
|
+
type: "string",
|
|
616
|
+
required: true
|
|
617
|
+
},
|
|
618
|
+
resourceId: {
|
|
619
|
+
type: "string",
|
|
620
|
+
required: false
|
|
621
|
+
},
|
|
622
|
+
threadId: {
|
|
623
|
+
type: "string",
|
|
624
|
+
required: false
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
indexes: {
|
|
628
|
+
primary: {
|
|
629
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
630
|
+
sk: { field: "sk", composite: ["entity"] }
|
|
631
|
+
},
|
|
632
|
+
byScorer: {
|
|
633
|
+
index: "gsi1",
|
|
634
|
+
pk: { field: "gsi1pk", composite: ["entity", "scorerId"] },
|
|
635
|
+
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
636
|
+
},
|
|
637
|
+
byRun: {
|
|
638
|
+
index: "gsi2",
|
|
639
|
+
pk: { field: "gsi2pk", composite: ["entity", "runId"] },
|
|
640
|
+
sk: { field: "gsi2sk", composite: ["createdAt"] }
|
|
641
|
+
},
|
|
642
|
+
byTrace: {
|
|
643
|
+
index: "gsi3",
|
|
644
|
+
pk: { field: "gsi3pk", composite: ["entity", "traceId"] },
|
|
645
|
+
sk: { field: "gsi3sk", composite: ["createdAt"] }
|
|
646
|
+
},
|
|
647
|
+
byEntityData: {
|
|
648
|
+
index: "gsi4",
|
|
649
|
+
pk: { field: "gsi4pk", composite: ["entity", "entityId"] },
|
|
650
|
+
sk: { field: "gsi4sk", composite: ["createdAt"] }
|
|
651
|
+
},
|
|
652
|
+
byResource: {
|
|
653
|
+
index: "gsi5",
|
|
654
|
+
pk: { field: "gsi5pk", composite: ["entity", "resourceId"] },
|
|
655
|
+
sk: { field: "gsi5sk", composite: ["createdAt"] }
|
|
656
|
+
},
|
|
657
|
+
byThread: {
|
|
658
|
+
index: "gsi6",
|
|
659
|
+
pk: { field: "gsi6pk", composite: ["entity", "threadId"] },
|
|
660
|
+
sk: { field: "gsi6sk", composite: ["createdAt"] }
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
var threadEntity = new electrodb.Entity({
|
|
665
|
+
model: {
|
|
666
|
+
entity: "thread",
|
|
667
|
+
version: "1",
|
|
668
|
+
service: "mastra"
|
|
669
|
+
},
|
|
670
|
+
attributes: {
|
|
671
|
+
entity: {
|
|
672
|
+
type: "string",
|
|
673
|
+
required: true
|
|
674
|
+
},
|
|
675
|
+
...baseAttributes,
|
|
676
|
+
id: {
|
|
677
|
+
type: "string",
|
|
678
|
+
required: true
|
|
679
|
+
},
|
|
680
|
+
resourceId: {
|
|
681
|
+
type: "string",
|
|
682
|
+
required: true
|
|
683
|
+
},
|
|
684
|
+
title: {
|
|
685
|
+
type: "string",
|
|
686
|
+
required: true
|
|
687
|
+
},
|
|
688
|
+
metadata: {
|
|
689
|
+
type: "string",
|
|
690
|
+
required: false,
|
|
691
|
+
// Stringify metadata object on set if it's not already a string
|
|
692
|
+
set: (value) => {
|
|
693
|
+
if (value && typeof value !== "string") {
|
|
694
|
+
return JSON.stringify(value);
|
|
695
|
+
}
|
|
696
|
+
return value;
|
|
697
|
+
},
|
|
698
|
+
// Parse JSON string to object on get
|
|
699
|
+
get: (value) => {
|
|
700
|
+
if (value && typeof value === "string") {
|
|
701
|
+
try {
|
|
702
|
+
if (value.startsWith("{") || value.startsWith("[")) {
|
|
703
|
+
return JSON.parse(value);
|
|
704
|
+
}
|
|
705
|
+
} catch {
|
|
706
|
+
return value;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return value;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
},
|
|
713
|
+
indexes: {
|
|
714
|
+
primary: {
|
|
715
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
716
|
+
sk: { field: "sk", composite: ["id"] }
|
|
717
|
+
},
|
|
718
|
+
byResource: {
|
|
719
|
+
index: "gsi1",
|
|
720
|
+
pk: { field: "gsi1pk", composite: ["entity", "resourceId"] },
|
|
721
|
+
sk: { field: "gsi1sk", composite: ["createdAt"] }
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
var traceEntity = new electrodb.Entity({
|
|
726
|
+
model: {
|
|
727
|
+
entity: "trace",
|
|
728
|
+
version: "1",
|
|
729
|
+
service: "mastra"
|
|
730
|
+
},
|
|
731
|
+
attributes: {
|
|
732
|
+
entity: {
|
|
733
|
+
type: "string",
|
|
734
|
+
required: true
|
|
735
|
+
},
|
|
736
|
+
...baseAttributes,
|
|
737
|
+
id: {
|
|
738
|
+
type: "string",
|
|
739
|
+
required: true
|
|
740
|
+
},
|
|
741
|
+
parentSpanId: {
|
|
742
|
+
type: "string",
|
|
743
|
+
required: false
|
|
744
|
+
},
|
|
745
|
+
name: {
|
|
746
|
+
type: "string",
|
|
747
|
+
required: true
|
|
748
|
+
},
|
|
749
|
+
traceId: {
|
|
750
|
+
type: "string",
|
|
751
|
+
required: true
|
|
752
|
+
},
|
|
753
|
+
scope: {
|
|
754
|
+
type: "string",
|
|
755
|
+
required: true
|
|
756
|
+
},
|
|
757
|
+
kind: {
|
|
758
|
+
type: "number",
|
|
759
|
+
required: true
|
|
760
|
+
},
|
|
761
|
+
attributes: {
|
|
762
|
+
type: "string",
|
|
763
|
+
// JSON stringified
|
|
764
|
+
required: false,
|
|
765
|
+
// Stringify object on set
|
|
766
|
+
set: (value) => {
|
|
767
|
+
if (value && typeof value !== "string") {
|
|
768
|
+
return JSON.stringify(value);
|
|
769
|
+
}
|
|
770
|
+
return value;
|
|
771
|
+
},
|
|
772
|
+
// Parse JSON string to object on get
|
|
773
|
+
get: (value) => {
|
|
774
|
+
return value ? JSON.parse(value) : value;
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
status: {
|
|
778
|
+
type: "string",
|
|
779
|
+
// JSON stringified
|
|
780
|
+
required: false,
|
|
781
|
+
// Stringify object on set
|
|
782
|
+
set: (value) => {
|
|
783
|
+
if (value && typeof value !== "string") {
|
|
784
|
+
return JSON.stringify(value);
|
|
785
|
+
}
|
|
786
|
+
return value;
|
|
787
|
+
},
|
|
788
|
+
// Parse JSON string to object on get
|
|
789
|
+
get: (value) => {
|
|
790
|
+
return value;
|
|
791
|
+
}
|
|
792
|
+
},
|
|
793
|
+
events: {
|
|
794
|
+
type: "string",
|
|
795
|
+
// JSON stringified
|
|
796
|
+
required: false,
|
|
797
|
+
// Stringify object on set
|
|
798
|
+
set: (value) => {
|
|
799
|
+
if (value && typeof value !== "string") {
|
|
800
|
+
return JSON.stringify(value);
|
|
801
|
+
}
|
|
802
|
+
return value;
|
|
803
|
+
},
|
|
804
|
+
// Parse JSON string to object on get
|
|
805
|
+
get: (value) => {
|
|
806
|
+
return value;
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
links: {
|
|
810
|
+
type: "string",
|
|
811
|
+
// JSON stringified
|
|
812
|
+
required: false,
|
|
813
|
+
// Stringify object on set
|
|
814
|
+
set: (value) => {
|
|
815
|
+
if (value && typeof value !== "string") {
|
|
816
|
+
return JSON.stringify(value);
|
|
817
|
+
}
|
|
818
|
+
return value;
|
|
819
|
+
},
|
|
820
|
+
// Parse JSON string to object on get
|
|
821
|
+
get: (value) => {
|
|
822
|
+
return value;
|
|
823
|
+
}
|
|
824
|
+
},
|
|
825
|
+
other: {
|
|
826
|
+
type: "string",
|
|
827
|
+
required: false
|
|
828
|
+
},
|
|
829
|
+
startTime: {
|
|
830
|
+
type: "number",
|
|
831
|
+
required: true
|
|
832
|
+
},
|
|
833
|
+
endTime: {
|
|
834
|
+
type: "number",
|
|
835
|
+
required: true
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
indexes: {
|
|
839
|
+
primary: {
|
|
840
|
+
pk: { field: "pk", composite: ["entity", "id"] },
|
|
841
|
+
sk: { field: "sk", composite: [] }
|
|
842
|
+
},
|
|
843
|
+
byName: {
|
|
844
|
+
index: "gsi1",
|
|
845
|
+
pk: { field: "gsi1pk", composite: ["entity", "name"] },
|
|
846
|
+
sk: { field: "gsi1sk", composite: ["startTime"] }
|
|
847
|
+
},
|
|
848
|
+
byScope: {
|
|
849
|
+
index: "gsi2",
|
|
850
|
+
pk: { field: "gsi2pk", composite: ["entity", "scope"] },
|
|
851
|
+
sk: { field: "gsi2sk", composite: ["startTime"] }
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
var workflowSnapshotEntity = new electrodb.Entity({
|
|
856
|
+
model: {
|
|
857
|
+
entity: "workflow_snapshot",
|
|
858
|
+
version: "1",
|
|
859
|
+
service: "mastra"
|
|
860
|
+
},
|
|
861
|
+
attributes: {
|
|
862
|
+
entity: {
|
|
863
|
+
type: "string",
|
|
864
|
+
required: true
|
|
865
|
+
},
|
|
866
|
+
...baseAttributes,
|
|
867
|
+
workflow_name: {
|
|
868
|
+
type: "string",
|
|
869
|
+
required: true
|
|
870
|
+
},
|
|
871
|
+
run_id: {
|
|
872
|
+
type: "string",
|
|
873
|
+
required: true
|
|
874
|
+
},
|
|
875
|
+
snapshot: {
|
|
876
|
+
type: "string",
|
|
877
|
+
// JSON stringified
|
|
878
|
+
required: true,
|
|
879
|
+
// Stringify snapshot object on set
|
|
880
|
+
set: (value) => {
|
|
881
|
+
if (value && typeof value !== "string") {
|
|
882
|
+
return JSON.stringify(value);
|
|
883
|
+
}
|
|
884
|
+
return value;
|
|
885
|
+
},
|
|
886
|
+
// Parse JSON string to object on get
|
|
887
|
+
get: (value) => {
|
|
888
|
+
return value ? JSON.parse(value) : value;
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
resourceId: {
|
|
892
|
+
type: "string",
|
|
893
|
+
required: false
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
indexes: {
|
|
897
|
+
primary: {
|
|
898
|
+
pk: { field: "pk", composite: ["entity", "workflow_name"] },
|
|
899
|
+
sk: { field: "sk", composite: ["run_id"] }
|
|
900
|
+
},
|
|
901
|
+
// GSI to allow querying by run_id efficiently without knowing the workflow_name
|
|
902
|
+
gsi2: {
|
|
903
|
+
index: "gsi2",
|
|
904
|
+
pk: { field: "gsi2pk", composite: ["entity", "run_id"] },
|
|
905
|
+
sk: { field: "gsi2sk", composite: ["workflow_name"] }
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
// src/entities/index.ts
|
|
911
|
+
function getElectroDbService(client, tableName) {
|
|
912
|
+
return new electrodb.Service(
|
|
913
|
+
{
|
|
914
|
+
thread: threadEntity,
|
|
915
|
+
message: messageEntity,
|
|
916
|
+
eval: evalEntity,
|
|
917
|
+
trace: traceEntity,
|
|
918
|
+
workflow_snapshot: workflowSnapshotEntity,
|
|
919
|
+
resource: resourceEntity,
|
|
920
|
+
score: scoreEntity
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
client,
|
|
924
|
+
table: tableName
|
|
925
|
+
}
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
var LegacyEvalsDynamoDB = class extends storage.LegacyEvalsStorage {
|
|
929
|
+
service;
|
|
930
|
+
tableName;
|
|
931
|
+
constructor({ service, tableName }) {
|
|
932
|
+
super();
|
|
933
|
+
this.service = service;
|
|
934
|
+
this.tableName = tableName;
|
|
935
|
+
}
|
|
936
|
+
// Eval operations
|
|
937
|
+
async getEvalsByAgentName(agentName, type) {
|
|
938
|
+
this.logger.debug("Getting evals for agent", { agentName, type });
|
|
939
|
+
try {
|
|
940
|
+
const query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
|
|
941
|
+
const results = await query.go({ order: "desc", limit: 100 });
|
|
942
|
+
if (!results.data.length) {
|
|
943
|
+
return [];
|
|
944
|
+
}
|
|
945
|
+
let filteredData = results.data;
|
|
946
|
+
if (type) {
|
|
947
|
+
filteredData = filteredData.filter((evalRecord) => {
|
|
948
|
+
try {
|
|
949
|
+
const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
|
|
950
|
+
if (type === "test" && !testInfo) {
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
if (type === "live" && testInfo) {
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
} catch (e) {
|
|
957
|
+
this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
|
|
958
|
+
}
|
|
959
|
+
return true;
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
return filteredData.map((evalRecord) => {
|
|
963
|
+
try {
|
|
964
|
+
return {
|
|
965
|
+
input: evalRecord.input,
|
|
966
|
+
output: evalRecord.output,
|
|
967
|
+
// Safely parse result and test_info
|
|
968
|
+
result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
|
|
969
|
+
agentName: evalRecord.agent_name,
|
|
970
|
+
createdAt: evalRecord.created_at,
|
|
971
|
+
// Keep as string from DDB?
|
|
972
|
+
metricName: evalRecord.metric_name,
|
|
973
|
+
instructions: evalRecord.instructions,
|
|
974
|
+
runId: evalRecord.run_id,
|
|
975
|
+
globalRunId: evalRecord.global_run_id,
|
|
976
|
+
testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
|
|
977
|
+
};
|
|
978
|
+
} catch (parseError) {
|
|
979
|
+
this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
|
|
980
|
+
return {
|
|
981
|
+
agentName: evalRecord.agent_name,
|
|
982
|
+
createdAt: evalRecord.created_at,
|
|
983
|
+
runId: evalRecord.run_id,
|
|
984
|
+
globalRunId: evalRecord.global_run_id
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
} catch (error$1) {
|
|
989
|
+
throw new error.MastraError(
|
|
990
|
+
{
|
|
991
|
+
id: "STORAGE_DYNAMODB_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
|
|
992
|
+
domain: error.ErrorDomain.STORAGE,
|
|
993
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
994
|
+
details: { agentName }
|
|
995
|
+
},
|
|
996
|
+
error$1
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
async getEvals(options = {}) {
|
|
1001
|
+
const { agentName, type, page = 0, perPage = 100, dateRange } = options;
|
|
1002
|
+
this.logger.debug("Getting evals with pagination", { agentName, type, page, perPage, dateRange });
|
|
1003
|
+
try {
|
|
1004
|
+
let query;
|
|
1005
|
+
if (agentName) {
|
|
1006
|
+
query = this.service.entities.eval.query.byAgent({ entity: "eval", agent_name: agentName });
|
|
1007
|
+
} else {
|
|
1008
|
+
query = this.service.entities.eval.query.byEntity({ entity: "eval" });
|
|
1009
|
+
}
|
|
1010
|
+
const results = await query.go({
|
|
1011
|
+
order: "desc",
|
|
1012
|
+
pages: "all"
|
|
1013
|
+
// Get all pages to apply filtering and pagination
|
|
1014
|
+
});
|
|
1015
|
+
if (!results.data.length) {
|
|
1016
|
+
return {
|
|
1017
|
+
evals: [],
|
|
1018
|
+
total: 0,
|
|
1019
|
+
page,
|
|
1020
|
+
perPage,
|
|
1021
|
+
hasMore: false
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
let filteredData = results.data;
|
|
1025
|
+
if (type) {
|
|
1026
|
+
filteredData = filteredData.filter((evalRecord) => {
|
|
1027
|
+
try {
|
|
1028
|
+
const testInfo = evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0;
|
|
1029
|
+
if (type === "test" && !testInfo) {
|
|
1030
|
+
return false;
|
|
1031
|
+
}
|
|
1032
|
+
if (type === "live" && testInfo) {
|
|
1033
|
+
return false;
|
|
1034
|
+
}
|
|
1035
|
+
} catch (e) {
|
|
1036
|
+
this.logger.warn("Failed to parse test_info during filtering", { record: evalRecord, error: e });
|
|
1037
|
+
}
|
|
1038
|
+
return true;
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
if (dateRange) {
|
|
1042
|
+
const fromDate = dateRange.start;
|
|
1043
|
+
const toDate = dateRange.end;
|
|
1044
|
+
filteredData = filteredData.filter((evalRecord) => {
|
|
1045
|
+
const recordDate = new Date(evalRecord.created_at);
|
|
1046
|
+
if (fromDate && recordDate < fromDate) {
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
if (toDate && recordDate > toDate) {
|
|
1050
|
+
return false;
|
|
1051
|
+
}
|
|
1052
|
+
return true;
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
const total = filteredData.length;
|
|
1056
|
+
const start = page * perPage;
|
|
1057
|
+
const end = start + perPage;
|
|
1058
|
+
const paginatedData = filteredData.slice(start, end);
|
|
1059
|
+
const evals = paginatedData.map((evalRecord) => {
|
|
1060
|
+
try {
|
|
1061
|
+
return {
|
|
1062
|
+
input: evalRecord.input,
|
|
1063
|
+
output: evalRecord.output,
|
|
1064
|
+
result: evalRecord.result && typeof evalRecord.result === "string" ? JSON.parse(evalRecord.result) : void 0,
|
|
1065
|
+
agentName: evalRecord.agent_name,
|
|
1066
|
+
createdAt: evalRecord.created_at,
|
|
1067
|
+
metricName: evalRecord.metric_name,
|
|
1068
|
+
instructions: evalRecord.instructions,
|
|
1069
|
+
runId: evalRecord.run_id,
|
|
1070
|
+
globalRunId: evalRecord.global_run_id,
|
|
1071
|
+
testInfo: evalRecord.test_info && typeof evalRecord.test_info === "string" ? JSON.parse(evalRecord.test_info) : void 0
|
|
1072
|
+
};
|
|
1073
|
+
} catch (parseError) {
|
|
1074
|
+
this.logger.error("Failed to parse eval record", { record: evalRecord, error: parseError });
|
|
1075
|
+
return {
|
|
1076
|
+
agentName: evalRecord.agent_name,
|
|
1077
|
+
createdAt: evalRecord.created_at,
|
|
1078
|
+
runId: evalRecord.run_id,
|
|
1079
|
+
globalRunId: evalRecord.global_run_id
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
const hasMore = end < total;
|
|
1084
|
+
return {
|
|
1085
|
+
evals,
|
|
1086
|
+
total,
|
|
1087
|
+
page,
|
|
1088
|
+
perPage,
|
|
1089
|
+
hasMore
|
|
1090
|
+
};
|
|
1091
|
+
} catch (error$1) {
|
|
1092
|
+
throw new error.MastraError(
|
|
1093
|
+
{
|
|
1094
|
+
id: "STORAGE_DYNAMODB_STORE_GET_EVALS_FAILED",
|
|
1095
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1096
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1097
|
+
details: {
|
|
1098
|
+
agentName: agentName || "all",
|
|
1099
|
+
type: type || "all",
|
|
1100
|
+
page,
|
|
1101
|
+
perPage
|
|
1102
|
+
}
|
|
1103
|
+
},
|
|
1104
|
+
error$1
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
var MemoryStorageDynamoDB = class extends storage.MemoryStorage {
|
|
1110
|
+
service;
|
|
1111
|
+
constructor({ service }) {
|
|
1112
|
+
super();
|
|
1113
|
+
this.service = service;
|
|
1114
|
+
}
|
|
1115
|
+
// Helper function to parse message data (handle JSON fields)
|
|
1116
|
+
parseMessageData(data) {
|
|
1117
|
+
return {
|
|
1118
|
+
...data,
|
|
1119
|
+
// Ensure dates are Date objects if needed (ElectroDB might return strings)
|
|
1120
|
+
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
1121
|
+
updatedAt: data.updatedAt ? new Date(data.updatedAt) : void 0
|
|
1122
|
+
// Other fields like content, toolCallArgs etc. are assumed to be correctly
|
|
1123
|
+
// transformed by the ElectroDB entity getters.
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
// Helper function to transform and sort threads
|
|
1127
|
+
transformAndSortThreads(rawThreads, orderBy, sortDirection) {
|
|
1128
|
+
return rawThreads.map((data) => ({
|
|
1129
|
+
...data,
|
|
1130
|
+
// Convert date strings back to Date objects for consistency
|
|
1131
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
1132
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
1133
|
+
})).sort((a, b) => {
|
|
1134
|
+
const fieldA = orderBy === "createdAt" ? a.createdAt : a.updatedAt;
|
|
1135
|
+
const fieldB = orderBy === "createdAt" ? b.createdAt : b.updatedAt;
|
|
1136
|
+
const comparison = fieldA.getTime() - fieldB.getTime();
|
|
1137
|
+
return sortDirection === "DESC" ? -comparison : comparison;
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
async getThreadById({ threadId }) {
|
|
1141
|
+
this.logger.debug("Getting thread by ID", { threadId });
|
|
1142
|
+
try {
|
|
1143
|
+
const result = await this.service.entities.thread.get({ entity: "thread", id: threadId }).go();
|
|
1144
|
+
if (!result.data) {
|
|
1145
|
+
return null;
|
|
1146
|
+
}
|
|
1147
|
+
const data = result.data;
|
|
1148
|
+
return {
|
|
1149
|
+
...data,
|
|
1150
|
+
// Convert date strings back to Date objects for consistency
|
|
1151
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
1152
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt
|
|
1153
|
+
// metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
|
|
1154
|
+
// metadata is already transformed by the entity's getter
|
|
1155
|
+
};
|
|
1156
|
+
} catch (error$1) {
|
|
1157
|
+
throw new error.MastraError(
|
|
1158
|
+
{
|
|
1159
|
+
id: "STORAGE_DYNAMODB_STORE_GET_THREAD_BY_ID_FAILED",
|
|
1160
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1161
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1162
|
+
details: { threadId }
|
|
1163
|
+
},
|
|
1164
|
+
error$1
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
|
|
1170
|
+
*/
|
|
1171
|
+
async getThreadsByResourceId(args) {
|
|
1172
|
+
const resourceId = args.resourceId;
|
|
1173
|
+
const orderBy = this.castThreadOrderBy(args.orderBy);
|
|
1174
|
+
const sortDirection = this.castThreadSortDirection(args.sortDirection);
|
|
1175
|
+
this.logger.debug("Getting threads by resource ID", { resourceId, orderBy, sortDirection });
|
|
1176
|
+
try {
|
|
1177
|
+
const result = await this.service.entities.thread.query.byResource({ entity: "thread", resourceId }).go();
|
|
1178
|
+
if (!result.data.length) {
|
|
1179
|
+
return [];
|
|
1180
|
+
}
|
|
1181
|
+
return this.transformAndSortThreads(result.data, orderBy, sortDirection);
|
|
1182
|
+
} catch (error$1) {
|
|
1183
|
+
throw new error.MastraError(
|
|
1184
|
+
{
|
|
1185
|
+
id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
|
|
1186
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1187
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1188
|
+
details: { resourceId }
|
|
1189
|
+
},
|
|
1190
|
+
error$1
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
async saveThread({ thread }) {
|
|
1195
|
+
this.logger.debug("Saving thread", { threadId: thread.id });
|
|
1196
|
+
const now = /* @__PURE__ */ new Date();
|
|
1197
|
+
const threadData = {
|
|
1198
|
+
entity: "thread",
|
|
1199
|
+
id: thread.id,
|
|
1200
|
+
resourceId: thread.resourceId,
|
|
1201
|
+
title: thread.title || `Thread ${thread.id}`,
|
|
1202
|
+
createdAt: thread.createdAt?.toISOString() || now.toISOString(),
|
|
1203
|
+
updatedAt: thread.updatedAt?.toISOString() || now.toISOString(),
|
|
1204
|
+
metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
|
|
1205
|
+
};
|
|
1206
|
+
try {
|
|
1207
|
+
await this.service.entities.thread.upsert(threadData).go();
|
|
1208
|
+
return {
|
|
1209
|
+
id: thread.id,
|
|
1210
|
+
resourceId: thread.resourceId,
|
|
1211
|
+
title: threadData.title,
|
|
1212
|
+
createdAt: thread.createdAt || now,
|
|
1213
|
+
updatedAt: now,
|
|
1214
|
+
metadata: thread.metadata
|
|
1215
|
+
};
|
|
1216
|
+
} catch (error$1) {
|
|
1217
|
+
throw new error.MastraError(
|
|
1218
|
+
{
|
|
1219
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_THREAD_FAILED",
|
|
1220
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1221
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1222
|
+
details: { threadId: thread.id }
|
|
1223
|
+
},
|
|
1224
|
+
error$1
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
async updateThread({
|
|
1229
|
+
id,
|
|
1230
|
+
title,
|
|
1231
|
+
metadata
|
|
1232
|
+
}) {
|
|
1233
|
+
this.logger.debug("Updating thread", { threadId: id });
|
|
1234
|
+
try {
|
|
1235
|
+
const existingThread = await this.getThreadById({ threadId: id });
|
|
1236
|
+
if (!existingThread) {
|
|
1237
|
+
throw new Error(`Thread not found: ${id}`);
|
|
1238
|
+
}
|
|
1239
|
+
const now = /* @__PURE__ */ new Date();
|
|
1240
|
+
const updateData = {
|
|
1241
|
+
updatedAt: now.toISOString()
|
|
1242
|
+
};
|
|
1243
|
+
if (title) {
|
|
1244
|
+
updateData.title = title;
|
|
1245
|
+
}
|
|
1246
|
+
if (metadata) {
|
|
1247
|
+
const existingMetadata = existingThread.metadata ? typeof existingThread.metadata === "string" ? JSON.parse(existingThread.metadata) : existingThread.metadata : {};
|
|
1248
|
+
const mergedMetadata = { ...existingMetadata, ...metadata };
|
|
1249
|
+
updateData.metadata = JSON.stringify(mergedMetadata);
|
|
1250
|
+
}
|
|
1251
|
+
await this.service.entities.thread.update({ entity: "thread", id }).set(updateData).go();
|
|
1252
|
+
return {
|
|
1253
|
+
...existingThread,
|
|
1254
|
+
title: title || existingThread.title,
|
|
1255
|
+
metadata: metadata ? { ...existingThread.metadata, ...metadata } : existingThread.metadata,
|
|
1256
|
+
updatedAt: now
|
|
1257
|
+
};
|
|
1258
|
+
} catch (error$1) {
|
|
1259
|
+
throw new error.MastraError(
|
|
1260
|
+
{
|
|
1261
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_THREAD_FAILED",
|
|
1262
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1263
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1264
|
+
details: { threadId: id }
|
|
1265
|
+
},
|
|
1266
|
+
error$1
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
async deleteThread({ threadId }) {
|
|
1271
|
+
this.logger.debug("Deleting thread", { threadId });
|
|
1272
|
+
try {
|
|
1273
|
+
const messages = await this.getMessages({ threadId });
|
|
1274
|
+
if (messages.length > 0) {
|
|
1275
|
+
const batchSize = 25;
|
|
1276
|
+
for (let i = 0; i < messages.length; i += batchSize) {
|
|
1277
|
+
const batch = messages.slice(i, i + batchSize);
|
|
1278
|
+
await Promise.all(
|
|
1279
|
+
batch.map(
|
|
1280
|
+
(message) => this.service.entities.message.delete({
|
|
1281
|
+
entity: "message",
|
|
1282
|
+
id: message.id,
|
|
1283
|
+
threadId: message.threadId
|
|
1284
|
+
}).go()
|
|
1285
|
+
)
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
await this.service.entities.thread.delete({ entity: "thread", id: threadId }).go();
|
|
1290
|
+
} catch (error$1) {
|
|
1291
|
+
throw new error.MastraError(
|
|
1292
|
+
{
|
|
1293
|
+
id: "STORAGE_DYNAMODB_STORE_DELETE_THREAD_FAILED",
|
|
1294
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1295
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1296
|
+
details: { threadId }
|
|
1297
|
+
},
|
|
1298
|
+
error$1
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
async getMessages({
|
|
1303
|
+
threadId,
|
|
1304
|
+
resourceId,
|
|
1305
|
+
selectBy,
|
|
1306
|
+
format
|
|
1307
|
+
}) {
|
|
1308
|
+
this.logger.debug("Getting messages", { threadId, selectBy });
|
|
1309
|
+
try {
|
|
1310
|
+
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
1311
|
+
const messages = [];
|
|
1312
|
+
const limit = storage.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
1313
|
+
if (selectBy?.include?.length) {
|
|
1314
|
+
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1315
|
+
if (includeMessages) {
|
|
1316
|
+
messages.push(...includeMessages);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
if (limit !== 0) {
|
|
1320
|
+
const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
|
|
1321
|
+
let results;
|
|
1322
|
+
if (limit !== Number.MAX_SAFE_INTEGER && limit > 0) {
|
|
1323
|
+
results = await query.go({ limit, order: "desc" });
|
|
1324
|
+
results.data = results.data.reverse();
|
|
1325
|
+
} else {
|
|
1326
|
+
results = await query.go();
|
|
1327
|
+
}
|
|
1328
|
+
let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg);
|
|
1329
|
+
allThreadMessages.sort((a, b) => {
|
|
1330
|
+
const timeA = a.createdAt.getTime();
|
|
1331
|
+
const timeB = b.createdAt.getTime();
|
|
1332
|
+
if (timeA === timeB) {
|
|
1333
|
+
return a.id.localeCompare(b.id);
|
|
1334
|
+
}
|
|
1335
|
+
return timeA - timeB;
|
|
1336
|
+
});
|
|
1337
|
+
messages.push(...allThreadMessages);
|
|
1338
|
+
}
|
|
1339
|
+
messages.sort((a, b) => {
|
|
1340
|
+
const timeA = a.createdAt.getTime();
|
|
1341
|
+
const timeB = b.createdAt.getTime();
|
|
1342
|
+
if (timeA === timeB) {
|
|
1343
|
+
return a.id.localeCompare(b.id);
|
|
1344
|
+
}
|
|
1345
|
+
return timeA - timeB;
|
|
1346
|
+
});
|
|
1347
|
+
const uniqueMessages = messages.filter(
|
|
1348
|
+
(message, index, self) => index === self.findIndex((m) => m.id === message.id)
|
|
1349
|
+
);
|
|
1350
|
+
const list = new agent.MessageList({ threadId, resourceId }).add(uniqueMessages, "memory");
|
|
1351
|
+
if (format === `v2`) return list.get.all.v2();
|
|
1352
|
+
return list.get.all.v1();
|
|
1353
|
+
} catch (error$1) {
|
|
1354
|
+
throw new error.MastraError(
|
|
1355
|
+
{
|
|
1356
|
+
id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_FAILED",
|
|
1357
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1358
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1359
|
+
details: { threadId, resourceId: resourceId ?? "" }
|
|
1360
|
+
},
|
|
1361
|
+
error$1
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
async getMessagesById({
|
|
1366
|
+
messageIds,
|
|
1367
|
+
format
|
|
1368
|
+
}) {
|
|
1369
|
+
this.logger.debug("Getting messages by ID", { messageIds });
|
|
1370
|
+
if (messageIds.length === 0) return [];
|
|
1371
|
+
try {
|
|
1372
|
+
const results = await Promise.all(
|
|
1373
|
+
messageIds.map((id) => this.service.entities.message.query.primary({ entity: "message", id }).go())
|
|
1374
|
+
);
|
|
1375
|
+
const data = results.map((result) => result.data).flat(1);
|
|
1376
|
+
let parsedMessages = data.map((data2) => this.parseMessageData(data2)).filter((msg) => "content" in msg);
|
|
1377
|
+
const uniqueMessages = parsedMessages.filter(
|
|
1378
|
+
(message, index, self) => index === self.findIndex((m) => m.id === message.id)
|
|
1379
|
+
);
|
|
1380
|
+
const list = new agent.MessageList().add(uniqueMessages, "memory");
|
|
1381
|
+
if (format === `v1`) return list.get.all.v1();
|
|
1382
|
+
return list.get.all.v2();
|
|
1383
|
+
} catch (error$1) {
|
|
1384
|
+
throw new error.MastraError(
|
|
1385
|
+
{
|
|
1386
|
+
id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_BY_ID_FAILED",
|
|
1387
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1388
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1389
|
+
details: { messageIds: JSON.stringify(messageIds) }
|
|
1390
|
+
},
|
|
1391
|
+
error$1
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
async saveMessages(args) {
|
|
1396
|
+
const { messages, format = "v1" } = args;
|
|
1397
|
+
this.logger.debug("Saving messages", { count: messages.length });
|
|
1398
|
+
if (!messages.length) {
|
|
1399
|
+
return [];
|
|
1400
|
+
}
|
|
1401
|
+
const threadId = messages[0]?.threadId;
|
|
1402
|
+
if (!threadId) {
|
|
1403
|
+
throw new Error("Thread ID is required");
|
|
1404
|
+
}
|
|
1405
|
+
const messagesToSave = messages.map((msg) => {
|
|
1406
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1407
|
+
return {
|
|
1408
|
+
entity: "message",
|
|
1409
|
+
// Add entity type
|
|
1410
|
+
id: msg.id,
|
|
1411
|
+
threadId: msg.threadId,
|
|
1412
|
+
role: msg.role,
|
|
1413
|
+
type: msg.type,
|
|
1414
|
+
resourceId: msg.resourceId,
|
|
1415
|
+
// Ensure complex fields are stringified if not handled by attribute setters
|
|
1416
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
1417
|
+
toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
|
|
1418
|
+
toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
|
|
1419
|
+
toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
|
|
1420
|
+
createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
|
|
1421
|
+
updatedAt: now
|
|
1422
|
+
// Add updatedAt
|
|
1423
|
+
};
|
|
1424
|
+
});
|
|
1425
|
+
try {
|
|
1426
|
+
const savedMessageIds = [];
|
|
1427
|
+
for (const messageData of messagesToSave) {
|
|
1428
|
+
if (!messageData.entity) {
|
|
1429
|
+
this.logger.error("Missing entity property in message data for create", { messageData });
|
|
1430
|
+
throw new Error("Internal error: Missing entity property during saveMessages");
|
|
1431
|
+
}
|
|
1432
|
+
try {
|
|
1433
|
+
await this.service.entities.message.put(messageData).go();
|
|
1434
|
+
savedMessageIds.push(messageData.id);
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
for (const savedId of savedMessageIds) {
|
|
1437
|
+
try {
|
|
1438
|
+
await this.service.entities.message.delete({ entity: "message", id: savedId }).go();
|
|
1439
|
+
} catch (rollbackError) {
|
|
1440
|
+
this.logger.error("Failed to rollback message during save error", {
|
|
1441
|
+
messageId: savedId,
|
|
1442
|
+
error: rollbackError
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
throw error;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
|
|
1450
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1451
|
+
}).go();
|
|
1452
|
+
const list = new agent.MessageList().add(messages, "memory");
|
|
1453
|
+
if (format === `v1`) return list.get.all.v1();
|
|
1454
|
+
return list.get.all.v2();
|
|
1455
|
+
} catch (error$1) {
|
|
1456
|
+
throw new error.MastraError(
|
|
1457
|
+
{
|
|
1458
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_MESSAGES_FAILED",
|
|
1459
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1460
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1461
|
+
details: { count: messages.length }
|
|
1462
|
+
},
|
|
1463
|
+
error$1
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
1468
|
+
const { resourceId, page = 0, perPage = 100 } = args;
|
|
1469
|
+
const orderBy = this.castThreadOrderBy(args.orderBy);
|
|
1470
|
+
const sortDirection = this.castThreadSortDirection(args.sortDirection);
|
|
1471
|
+
this.logger.debug("Getting threads by resource ID with pagination", {
|
|
1472
|
+
resourceId,
|
|
1473
|
+
page,
|
|
1474
|
+
perPage,
|
|
1475
|
+
orderBy,
|
|
1476
|
+
sortDirection
|
|
1477
|
+
});
|
|
1478
|
+
try {
|
|
1479
|
+
const query = this.service.entities.thread.query.byResource({ entity: "thread", resourceId });
|
|
1480
|
+
const results = await query.go();
|
|
1481
|
+
const allThreads = this.transformAndSortThreads(results.data, orderBy, sortDirection);
|
|
1482
|
+
const startIndex = page * perPage;
|
|
1483
|
+
const endIndex = startIndex + perPage;
|
|
1484
|
+
const paginatedThreads = allThreads.slice(startIndex, endIndex);
|
|
1485
|
+
const total = allThreads.length;
|
|
1486
|
+
const hasMore = endIndex < total;
|
|
1487
|
+
return {
|
|
1488
|
+
threads: paginatedThreads,
|
|
1489
|
+
total,
|
|
1490
|
+
page,
|
|
1491
|
+
perPage,
|
|
1492
|
+
hasMore
|
|
1493
|
+
};
|
|
1494
|
+
} catch (error$1) {
|
|
1495
|
+
throw new error.MastraError(
|
|
1496
|
+
{
|
|
1497
|
+
id: "STORAGE_DYNAMODB_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
|
|
1498
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1499
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1500
|
+
details: { resourceId, page, perPage }
|
|
1501
|
+
},
|
|
1502
|
+
error$1
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
async getMessagesPaginated(args) {
|
|
1507
|
+
const { threadId, resourceId, selectBy, format = "v1" } = args;
|
|
1508
|
+
const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
|
|
1509
|
+
const fromDate = dateRange?.start;
|
|
1510
|
+
const toDate = dateRange?.end;
|
|
1511
|
+
const limit = storage.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
1512
|
+
this.logger.debug("Getting messages with pagination", { threadId, page, perPage, fromDate, toDate, limit });
|
|
1513
|
+
try {
|
|
1514
|
+
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
1515
|
+
let messages = [];
|
|
1516
|
+
if (selectBy?.include?.length) {
|
|
1517
|
+
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1518
|
+
if (includeMessages) {
|
|
1519
|
+
messages.push(...includeMessages);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
if (limit !== 0) {
|
|
1523
|
+
const query = this.service.entities.message.query.byThread({ entity: "message", threadId });
|
|
1524
|
+
let results;
|
|
1525
|
+
if (limit !== Number.MAX_SAFE_INTEGER && limit > 0) {
|
|
1526
|
+
results = await query.go({ limit, order: "desc" });
|
|
1527
|
+
results.data = results.data.reverse();
|
|
1528
|
+
} else {
|
|
1529
|
+
results = await query.go();
|
|
1530
|
+
}
|
|
1531
|
+
let allThreadMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg);
|
|
1532
|
+
allThreadMessages.sort((a, b) => {
|
|
1533
|
+
const timeA = a.createdAt.getTime();
|
|
1534
|
+
const timeB = b.createdAt.getTime();
|
|
1535
|
+
if (timeA === timeB) {
|
|
1536
|
+
return a.id.localeCompare(b.id);
|
|
1537
|
+
}
|
|
1538
|
+
return timeA - timeB;
|
|
1539
|
+
});
|
|
1540
|
+
const excludeIds = messages.map((m) => m.id);
|
|
1541
|
+
if (excludeIds.length > 0) {
|
|
1542
|
+
allThreadMessages = allThreadMessages.filter((msg) => !excludeIds.includes(msg.id));
|
|
1543
|
+
}
|
|
1544
|
+
messages.push(...allThreadMessages);
|
|
1545
|
+
}
|
|
1546
|
+
messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
1547
|
+
if (fromDate || toDate) {
|
|
1548
|
+
messages = messages.filter((msg) => {
|
|
1549
|
+
const createdAt = new Date(msg.createdAt).getTime();
|
|
1550
|
+
if (fromDate && createdAt < new Date(fromDate).getTime()) return false;
|
|
1551
|
+
if (toDate && createdAt > new Date(toDate).getTime()) return false;
|
|
1552
|
+
return true;
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
const total = messages.length;
|
|
1556
|
+
const start = page * perPage;
|
|
1557
|
+
const end = start + perPage;
|
|
1558
|
+
const paginatedMessages = messages.slice(start, end);
|
|
1559
|
+
const hasMore = end < total;
|
|
1560
|
+
const list = new agent.MessageList({ threadId, resourceId }).add(paginatedMessages, "memory");
|
|
1561
|
+
const finalMessages = format === "v2" ? list.get.all.v2() : list.get.all.v1();
|
|
1562
|
+
return {
|
|
1563
|
+
messages: finalMessages,
|
|
1564
|
+
total,
|
|
1565
|
+
page,
|
|
1566
|
+
perPage,
|
|
1567
|
+
hasMore
|
|
1568
|
+
};
|
|
1569
|
+
} catch (error$1) {
|
|
1570
|
+
const mastraError = new error.MastraError(
|
|
1571
|
+
{
|
|
1572
|
+
id: "STORAGE_DYNAMODB_STORE_GET_MESSAGES_PAGINATED_FAILED",
|
|
1573
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1574
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1575
|
+
details: { threadId, resourceId: resourceId ?? "" }
|
|
1576
|
+
},
|
|
1577
|
+
error$1
|
|
1578
|
+
);
|
|
1579
|
+
this.logger?.trackException?.(mastraError);
|
|
1580
|
+
this.logger?.error?.(mastraError.toString());
|
|
1581
|
+
return { messages: [], total: 0, page, perPage, hasMore: false };
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
// Helper method to get included messages with context
|
|
1585
|
+
async _getIncludedMessages(threadId, selectBy) {
|
|
1586
|
+
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
1587
|
+
if (!selectBy?.include?.length) {
|
|
1588
|
+
return [];
|
|
1589
|
+
}
|
|
1590
|
+
const includeMessages = [];
|
|
1591
|
+
for (const includeItem of selectBy.include) {
|
|
1592
|
+
try {
|
|
1593
|
+
const { id, threadId: targetThreadId, withPreviousMessages = 0, withNextMessages = 0 } = includeItem;
|
|
1594
|
+
const searchThreadId = targetThreadId || threadId;
|
|
1595
|
+
this.logger.debug("Getting included messages for", {
|
|
1596
|
+
id,
|
|
1597
|
+
targetThreadId,
|
|
1598
|
+
searchThreadId,
|
|
1599
|
+
withPreviousMessages,
|
|
1600
|
+
withNextMessages
|
|
1601
|
+
});
|
|
1602
|
+
const query = this.service.entities.message.query.byThread({ entity: "message", threadId: searchThreadId });
|
|
1603
|
+
const results = await query.go();
|
|
1604
|
+
const allMessages = results.data.map((data) => this.parseMessageData(data)).filter((msg) => "content" in msg && typeof msg.content === "object");
|
|
1605
|
+
this.logger.debug("Found messages in thread", {
|
|
1606
|
+
threadId: searchThreadId,
|
|
1607
|
+
messageCount: allMessages.length,
|
|
1608
|
+
messageIds: allMessages.map((m) => m.id)
|
|
1609
|
+
});
|
|
1610
|
+
allMessages.sort((a, b) => {
|
|
1611
|
+
const timeA = a.createdAt.getTime();
|
|
1612
|
+
const timeB = b.createdAt.getTime();
|
|
1613
|
+
if (timeA === timeB) {
|
|
1614
|
+
return a.id.localeCompare(b.id);
|
|
1615
|
+
}
|
|
1616
|
+
return timeA - timeB;
|
|
1617
|
+
});
|
|
1618
|
+
const targetIndex = allMessages.findIndex((msg) => msg.id === id);
|
|
1619
|
+
if (targetIndex === -1) {
|
|
1620
|
+
this.logger.warn("Target message not found", { id, threadId: searchThreadId });
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
this.logger.debug("Found target message at index", { id, targetIndex, totalMessages: allMessages.length });
|
|
1624
|
+
const startIndex = Math.max(0, targetIndex - withPreviousMessages);
|
|
1625
|
+
const endIndex = Math.min(allMessages.length, targetIndex + withNextMessages + 1);
|
|
1626
|
+
const contextMessages = allMessages.slice(startIndex, endIndex);
|
|
1627
|
+
this.logger.debug("Context messages", {
|
|
1628
|
+
startIndex,
|
|
1629
|
+
endIndex,
|
|
1630
|
+
contextCount: contextMessages.length,
|
|
1631
|
+
contextIds: contextMessages.map((m) => m.id)
|
|
1632
|
+
});
|
|
1633
|
+
includeMessages.push(...contextMessages);
|
|
1634
|
+
} catch (error) {
|
|
1635
|
+
this.logger.warn("Failed to get included message", { messageId: includeItem.id, error });
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
this.logger.debug("Total included messages", {
|
|
1639
|
+
count: includeMessages.length,
|
|
1640
|
+
ids: includeMessages.map((m) => m.id)
|
|
1641
|
+
});
|
|
1642
|
+
return includeMessages;
|
|
1643
|
+
}
|
|
1644
|
+
async updateMessages(args) {
|
|
1645
|
+
const { messages } = args;
|
|
1646
|
+
this.logger.debug("Updating messages", { count: messages.length });
|
|
1647
|
+
if (!messages.length) {
|
|
1648
|
+
return [];
|
|
1649
|
+
}
|
|
1650
|
+
const updatedMessages = [];
|
|
1651
|
+
const affectedThreadIds = /* @__PURE__ */ new Set();
|
|
1652
|
+
try {
|
|
1653
|
+
for (const updateData of messages) {
|
|
1654
|
+
const { id, ...updates } = updateData;
|
|
1655
|
+
const existingMessage = await this.service.entities.message.get({ entity: "message", id }).go();
|
|
1656
|
+
if (!existingMessage.data) {
|
|
1657
|
+
this.logger.warn("Message not found for update", { id });
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
const existingMsg = this.parseMessageData(existingMessage.data);
|
|
1661
|
+
const originalThreadId = existingMsg.threadId;
|
|
1662
|
+
affectedThreadIds.add(originalThreadId);
|
|
1663
|
+
const updatePayload = {
|
|
1664
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1665
|
+
};
|
|
1666
|
+
if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
|
|
1667
|
+
if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
|
|
1668
|
+
if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
|
|
1669
|
+
if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
|
|
1670
|
+
updatePayload.threadId = updates.threadId;
|
|
1671
|
+
affectedThreadIds.add(updates.threadId);
|
|
1672
|
+
}
|
|
1673
|
+
if (updates.content) {
|
|
1674
|
+
const existingContent = existingMsg.content;
|
|
1675
|
+
let newContent = { ...existingContent };
|
|
1676
|
+
if (updates.content.metadata !== void 0) {
|
|
1677
|
+
newContent.metadata = {
|
|
1678
|
+
...existingContent.metadata || {},
|
|
1679
|
+
...updates.content.metadata || {}
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
if (updates.content.content !== void 0) {
|
|
1683
|
+
newContent.content = updates.content.content;
|
|
1684
|
+
}
|
|
1685
|
+
if ("parts" in updates.content && updates.content.parts !== void 0) {
|
|
1686
|
+
newContent.parts = updates.content.parts;
|
|
1687
|
+
}
|
|
1688
|
+
updatePayload.content = JSON.stringify(newContent);
|
|
1689
|
+
}
|
|
1690
|
+
await this.service.entities.message.update({ entity: "message", id }).set(updatePayload).go();
|
|
1691
|
+
const updatedMessage = await this.service.entities.message.get({ entity: "message", id }).go();
|
|
1692
|
+
if (updatedMessage.data) {
|
|
1693
|
+
updatedMessages.push(this.parseMessageData(updatedMessage.data));
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
for (const threadId of affectedThreadIds) {
|
|
1697
|
+
await this.service.entities.thread.update({ entity: "thread", id: threadId }).set({
|
|
1698
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1699
|
+
}).go();
|
|
1700
|
+
}
|
|
1701
|
+
return updatedMessages;
|
|
1702
|
+
} catch (error$1) {
|
|
1703
|
+
throw new error.MastraError(
|
|
1704
|
+
{
|
|
1705
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_MESSAGES_FAILED",
|
|
1706
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1707
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1708
|
+
details: { count: messages.length }
|
|
1709
|
+
},
|
|
1710
|
+
error$1
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
async getResourceById({ resourceId }) {
|
|
1715
|
+
this.logger.debug("Getting resource by ID", { resourceId });
|
|
1716
|
+
try {
|
|
1717
|
+
const result = await this.service.entities.resource.get({ entity: "resource", id: resourceId }).go();
|
|
1718
|
+
if (!result.data) {
|
|
1719
|
+
return null;
|
|
1720
|
+
}
|
|
1721
|
+
const data = result.data;
|
|
1722
|
+
return {
|
|
1723
|
+
...data,
|
|
1724
|
+
// Convert date strings back to Date objects for consistency
|
|
1725
|
+
createdAt: typeof data.createdAt === "string" ? new Date(data.createdAt) : data.createdAt,
|
|
1726
|
+
updatedAt: typeof data.updatedAt === "string" ? new Date(data.updatedAt) : data.updatedAt,
|
|
1727
|
+
// Ensure workingMemory is always returned as a string, regardless of automatic parsing
|
|
1728
|
+
workingMemory: typeof data.workingMemory === "object" ? JSON.stringify(data.workingMemory) : data.workingMemory
|
|
1729
|
+
// metadata is already transformed by the entity's getter
|
|
1730
|
+
};
|
|
1731
|
+
} catch (error$1) {
|
|
1732
|
+
throw new error.MastraError(
|
|
1733
|
+
{
|
|
1734
|
+
id: "STORAGE_DYNAMODB_STORE_GET_RESOURCE_BY_ID_FAILED",
|
|
1735
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1736
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1737
|
+
details: { resourceId }
|
|
1738
|
+
},
|
|
1739
|
+
error$1
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
async saveResource({ resource }) {
|
|
1744
|
+
this.logger.debug("Saving resource", { resourceId: resource.id });
|
|
1745
|
+
const now = /* @__PURE__ */ new Date();
|
|
1746
|
+
const resourceData = {
|
|
1747
|
+
entity: "resource",
|
|
1748
|
+
id: resource.id,
|
|
1749
|
+
workingMemory: resource.workingMemory,
|
|
1750
|
+
metadata: resource.metadata ? JSON.stringify(resource.metadata) : void 0,
|
|
1751
|
+
createdAt: resource.createdAt?.toISOString() || now.toISOString(),
|
|
1752
|
+
updatedAt: now.toISOString()
|
|
1753
|
+
};
|
|
1754
|
+
try {
|
|
1755
|
+
await this.service.entities.resource.upsert(resourceData).go();
|
|
1756
|
+
return {
|
|
1757
|
+
id: resource.id,
|
|
1758
|
+
workingMemory: resource.workingMemory,
|
|
1759
|
+
metadata: resource.metadata,
|
|
1760
|
+
createdAt: resource.createdAt || now,
|
|
1761
|
+
updatedAt: now
|
|
1762
|
+
};
|
|
1763
|
+
} catch (error$1) {
|
|
1764
|
+
throw new error.MastraError(
|
|
1765
|
+
{
|
|
1766
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_RESOURCE_FAILED",
|
|
1767
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1768
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1769
|
+
details: { resourceId: resource.id }
|
|
1770
|
+
},
|
|
1771
|
+
error$1
|
|
1772
|
+
);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
async updateResource({
|
|
1776
|
+
resourceId,
|
|
1777
|
+
workingMemory,
|
|
1778
|
+
metadata
|
|
1779
|
+
}) {
|
|
1780
|
+
this.logger.debug("Updating resource", { resourceId });
|
|
1781
|
+
try {
|
|
1782
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1783
|
+
if (!existingResource) {
|
|
1784
|
+
const newResource = {
|
|
1785
|
+
id: resourceId,
|
|
1786
|
+
workingMemory,
|
|
1787
|
+
metadata: metadata || {},
|
|
1788
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1789
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1790
|
+
};
|
|
1791
|
+
return this.saveResource({ resource: newResource });
|
|
1792
|
+
}
|
|
1793
|
+
const now = /* @__PURE__ */ new Date();
|
|
1794
|
+
const updateData = {
|
|
1795
|
+
updatedAt: now.toISOString()
|
|
1796
|
+
};
|
|
1797
|
+
if (workingMemory !== void 0) {
|
|
1798
|
+
updateData.workingMemory = workingMemory;
|
|
1799
|
+
}
|
|
1800
|
+
if (metadata) {
|
|
1801
|
+
const existingMetadata = existingResource.metadata || {};
|
|
1802
|
+
const mergedMetadata = { ...existingMetadata, ...metadata };
|
|
1803
|
+
updateData.metadata = JSON.stringify(mergedMetadata);
|
|
1804
|
+
}
|
|
1805
|
+
await this.service.entities.resource.update({ entity: "resource", id: resourceId }).set(updateData).go();
|
|
1806
|
+
return {
|
|
1807
|
+
...existingResource,
|
|
1808
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1809
|
+
metadata: metadata ? { ...existingResource.metadata, ...metadata } : existingResource.metadata,
|
|
1810
|
+
updatedAt: now
|
|
1811
|
+
};
|
|
1812
|
+
} catch (error$1) {
|
|
1813
|
+
throw new error.MastraError(
|
|
1814
|
+
{
|
|
1815
|
+
id: "STORAGE_DYNAMODB_STORE_UPDATE_RESOURCE_FAILED",
|
|
1816
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1817
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1818
|
+
details: { resourceId }
|
|
1819
|
+
},
|
|
1820
|
+
error$1
|
|
1821
|
+
);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
};
|
|
1825
|
+
var StoreOperationsDynamoDB = class extends storage.StoreOperations {
|
|
1826
|
+
client;
|
|
1827
|
+
tableName;
|
|
1828
|
+
service;
|
|
1829
|
+
constructor({
|
|
1830
|
+
service,
|
|
1831
|
+
tableName,
|
|
1832
|
+
client
|
|
1833
|
+
}) {
|
|
1834
|
+
super();
|
|
1835
|
+
this.service = service;
|
|
1836
|
+
this.client = client;
|
|
1837
|
+
this.tableName = tableName;
|
|
1838
|
+
}
|
|
1839
|
+
async hasColumn() {
|
|
1840
|
+
return true;
|
|
1841
|
+
}
|
|
1842
|
+
async dropTable() {
|
|
1843
|
+
}
|
|
1844
|
+
// Helper methods for entity/table mapping
|
|
1845
|
+
getEntityNameForTable(tableName) {
|
|
1846
|
+
const mapping = {
|
|
1847
|
+
[storage.TABLE_THREADS]: "thread",
|
|
1848
|
+
[storage.TABLE_MESSAGES]: "message",
|
|
1849
|
+
[storage.TABLE_WORKFLOW_SNAPSHOT]: "workflow_snapshot",
|
|
1850
|
+
[storage.TABLE_EVALS]: "eval",
|
|
1851
|
+
[storage.TABLE_SCORERS]: "score",
|
|
1852
|
+
[storage.TABLE_TRACES]: "trace",
|
|
1853
|
+
[storage.TABLE_RESOURCES]: "resource",
|
|
1854
|
+
[storage.TABLE_AI_SPANS]: "ai_span"
|
|
1855
|
+
};
|
|
1856
|
+
return mapping[tableName] || null;
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Pre-processes a record to ensure Date objects are converted to ISO strings
|
|
1860
|
+
* This is necessary because ElectroDB validation happens before setters are applied
|
|
1861
|
+
*/
|
|
1862
|
+
preprocessRecord(record) {
|
|
1863
|
+
const processed = { ...record };
|
|
1864
|
+
if (processed.createdAt instanceof Date) {
|
|
1865
|
+
processed.createdAt = processed.createdAt.toISOString();
|
|
1866
|
+
}
|
|
1867
|
+
if (processed.updatedAt instanceof Date) {
|
|
1868
|
+
processed.updatedAt = processed.updatedAt.toISOString();
|
|
1869
|
+
}
|
|
1870
|
+
if (processed.created_at instanceof Date) {
|
|
1871
|
+
processed.created_at = processed.created_at.toISOString();
|
|
1872
|
+
}
|
|
1873
|
+
if (processed.result && typeof processed.result === "object") {
|
|
1874
|
+
processed.result = JSON.stringify(processed.result);
|
|
1875
|
+
}
|
|
1876
|
+
if (processed.test_info && typeof processed.test_info === "object") {
|
|
1877
|
+
processed.test_info = JSON.stringify(processed.test_info);
|
|
1878
|
+
} else if (processed.test_info === void 0 || processed.test_info === null) {
|
|
1879
|
+
delete processed.test_info;
|
|
1880
|
+
}
|
|
1881
|
+
if (processed.snapshot && typeof processed.snapshot === "object") {
|
|
1882
|
+
processed.snapshot = JSON.stringify(processed.snapshot);
|
|
1883
|
+
}
|
|
1884
|
+
if (processed.attributes && typeof processed.attributes === "object") {
|
|
1885
|
+
processed.attributes = JSON.stringify(processed.attributes);
|
|
1886
|
+
}
|
|
1887
|
+
if (processed.status && typeof processed.status === "object") {
|
|
1888
|
+
processed.status = JSON.stringify(processed.status);
|
|
1889
|
+
}
|
|
1890
|
+
if (processed.events && typeof processed.events === "object") {
|
|
1891
|
+
processed.events = JSON.stringify(processed.events);
|
|
1892
|
+
}
|
|
1893
|
+
if (processed.links && typeof processed.links === "object") {
|
|
1894
|
+
processed.links = JSON.stringify(processed.links);
|
|
1895
|
+
}
|
|
1896
|
+
return processed;
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Validates that the required DynamoDB table exists and is accessible.
|
|
1900
|
+
* This does not check the table structure - it assumes the table
|
|
1901
|
+
* was created with the correct structure via CDK/CloudFormation.
|
|
1902
|
+
*/
|
|
1903
|
+
async validateTableExists() {
|
|
1904
|
+
try {
|
|
1905
|
+
const command = new clientDynamodb.DescribeTableCommand({
|
|
1906
|
+
TableName: this.tableName
|
|
1907
|
+
});
|
|
1908
|
+
await this.client.send(command);
|
|
1909
|
+
return true;
|
|
1910
|
+
} catch (error$1) {
|
|
1911
|
+
if (error$1.name === "ResourceNotFoundException") {
|
|
1912
|
+
return false;
|
|
1913
|
+
}
|
|
1914
|
+
throw new error.MastraError(
|
|
1915
|
+
{
|
|
1916
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
|
|
1917
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1918
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1919
|
+
details: { tableName: this.tableName }
|
|
1920
|
+
},
|
|
1921
|
+
error$1
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* This method is modified for DynamoDB with ElectroDB single-table design.
|
|
1927
|
+
* It assumes the table is created and managed externally via CDK/CloudFormation.
|
|
1928
|
+
*
|
|
1929
|
+
* This implementation only validates that the required table exists and is accessible.
|
|
1930
|
+
* No table creation is attempted - we simply check if we can access the table.
|
|
1931
|
+
*/
|
|
1932
|
+
async createTable({ tableName }) {
|
|
1933
|
+
this.logger.debug("Validating access to externally managed table", { tableName, physicalTable: this.tableName });
|
|
1934
|
+
try {
|
|
1935
|
+
const tableExists = await this.validateTableExists();
|
|
1936
|
+
if (!tableExists) {
|
|
1937
|
+
this.logger.error(
|
|
1938
|
+
`Table ${this.tableName} does not exist or is not accessible. It should be created via CDK/CloudFormation.`
|
|
1939
|
+
);
|
|
1940
|
+
throw new Error(
|
|
1941
|
+
`Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
this.logger.debug(`Table ${this.tableName} exists and is accessible`);
|
|
1945
|
+
} catch (error$1) {
|
|
1946
|
+
this.logger.error("Error validating table access", { tableName: this.tableName, error: error$1 });
|
|
1947
|
+
throw new error.MastraError(
|
|
1948
|
+
{
|
|
1949
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED",
|
|
1950
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1951
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1952
|
+
details: { tableName: this.tableName }
|
|
1953
|
+
},
|
|
1954
|
+
error$1
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
async insert({ tableName, record }) {
|
|
1959
|
+
this.logger.debug("DynamoDB insert called", { tableName });
|
|
1960
|
+
const entityName = this.getEntityNameForTable(tableName);
|
|
1961
|
+
if (!entityName || !this.service.entities[entityName]) {
|
|
1962
|
+
throw new error.MastraError({
|
|
1963
|
+
id: "STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS",
|
|
1964
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1965
|
+
category: error.ErrorCategory.USER,
|
|
1966
|
+
text: "No entity defined for tableName",
|
|
1967
|
+
details: { tableName }
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
try {
|
|
1971
|
+
const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
|
|
1972
|
+
await this.service.entities[entityName].create(dataToSave).go();
|
|
1973
|
+
} catch (error$1) {
|
|
1974
|
+
throw new error.MastraError(
|
|
1975
|
+
{
|
|
1976
|
+
id: "STORAGE_DYNAMODB_STORE_INSERT_FAILED",
|
|
1977
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1978
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1979
|
+
details: { tableName }
|
|
1980
|
+
},
|
|
1981
|
+
error$1
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
async alterTable(_args) {
|
|
1986
|
+
}
|
|
1987
|
+
/**
|
|
1988
|
+
* Clear all items from a logical "table" (entity type)
|
|
1989
|
+
*/
|
|
1990
|
+
async clearTable({ tableName }) {
|
|
1991
|
+
this.logger.debug("DynamoDB clearTable called", { tableName });
|
|
1992
|
+
const entityName = this.getEntityNameForTable(tableName);
|
|
1993
|
+
if (!entityName || !this.service.entities[entityName]) {
|
|
1994
|
+
throw new error.MastraError({
|
|
1995
|
+
id: "STORAGE_DYNAMODB_STORE_CLEAR_TABLE_INVALID_ARGS",
|
|
1996
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1997
|
+
category: error.ErrorCategory.USER,
|
|
1998
|
+
text: "No entity defined for tableName",
|
|
1999
|
+
details: { tableName }
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
try {
|
|
2003
|
+
const result = await this.service.entities[entityName].scan.go({ pages: "all" });
|
|
2004
|
+
if (!result.data.length) {
|
|
2005
|
+
this.logger.debug(`No records found to clear for ${tableName}`);
|
|
2006
|
+
return;
|
|
2007
|
+
}
|
|
2008
|
+
this.logger.debug(`Found ${result.data.length} records to delete for ${tableName}`);
|
|
2009
|
+
const keysToDelete = result.data.map((item) => {
|
|
2010
|
+
const key = { entity: entityName };
|
|
2011
|
+
switch (entityName) {
|
|
2012
|
+
case "thread":
|
|
2013
|
+
if (!item.id) throw new Error(`Missing required key 'id' for entity 'thread'`);
|
|
2014
|
+
key.id = item.id;
|
|
2015
|
+
break;
|
|
2016
|
+
case "message":
|
|
2017
|
+
if (!item.id) throw new Error(`Missing required key 'id' for entity 'message'`);
|
|
2018
|
+
key.id = item.id;
|
|
2019
|
+
break;
|
|
2020
|
+
case "workflow_snapshot":
|
|
2021
|
+
if (!item.workflow_name)
|
|
2022
|
+
throw new Error(`Missing required key 'workflow_name' for entity 'workflow_snapshot'`);
|
|
2023
|
+
if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflow_snapshot'`);
|
|
2024
|
+
key.workflow_name = item.workflow_name;
|
|
2025
|
+
key.run_id = item.run_id;
|
|
2026
|
+
break;
|
|
2027
|
+
case "eval":
|
|
2028
|
+
if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'eval'`);
|
|
2029
|
+
key.run_id = item.run_id;
|
|
2030
|
+
break;
|
|
2031
|
+
case "trace":
|
|
2032
|
+
if (!item.id) throw new Error(`Missing required key 'id' for entity 'trace'`);
|
|
2033
|
+
key.id = item.id;
|
|
2034
|
+
break;
|
|
2035
|
+
case "score":
|
|
2036
|
+
if (!item.id) throw new Error(`Missing required key 'id' for entity 'score'`);
|
|
2037
|
+
key.id = item.id;
|
|
2038
|
+
break;
|
|
2039
|
+
default:
|
|
2040
|
+
this.logger.warn(`Unknown entity type encountered during clearTable: ${entityName}`);
|
|
2041
|
+
throw new Error(`Cannot construct delete key for unknown entity type: ${entityName}`);
|
|
2042
|
+
}
|
|
2043
|
+
return key;
|
|
2044
|
+
});
|
|
2045
|
+
const batchSize = 25;
|
|
2046
|
+
for (let i = 0; i < keysToDelete.length; i += batchSize) {
|
|
2047
|
+
const batchKeys = keysToDelete.slice(i, i + batchSize);
|
|
2048
|
+
await this.service.entities[entityName].delete(batchKeys).go();
|
|
2049
|
+
}
|
|
2050
|
+
this.logger.debug(`Successfully cleared all records for ${tableName}`);
|
|
2051
|
+
} catch (error$1) {
|
|
2052
|
+
throw new error.MastraError(
|
|
2053
|
+
{
|
|
2054
|
+
id: "STORAGE_DYNAMODB_STORE_CLEAR_TABLE_FAILED",
|
|
2055
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2056
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2057
|
+
details: { tableName }
|
|
2058
|
+
},
|
|
2059
|
+
error$1
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
/**
|
|
2064
|
+
* Insert multiple records as a batch
|
|
2065
|
+
*/
|
|
2066
|
+
async batchInsert({ tableName, records }) {
|
|
2067
|
+
this.logger.debug("DynamoDB batchInsert called", { tableName, count: records.length });
|
|
2068
|
+
const entityName = this.getEntityNameForTable(tableName);
|
|
2069
|
+
if (!entityName || !this.service.entities[entityName]) {
|
|
2070
|
+
throw new error.MastraError({
|
|
2071
|
+
id: "STORAGE_DYNAMODB_STORE_BATCH_INSERT_INVALID_ARGS",
|
|
2072
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2073
|
+
category: error.ErrorCategory.USER,
|
|
2074
|
+
text: "No entity defined for tableName",
|
|
2075
|
+
details: { tableName }
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
2078
|
+
const recordsToSave = records.map((rec) => ({ entity: entityName, ...this.preprocessRecord(rec) }));
|
|
2079
|
+
const batchSize = 25;
|
|
2080
|
+
const batches = [];
|
|
2081
|
+
for (let i = 0; i < recordsToSave.length; i += batchSize) {
|
|
2082
|
+
const batch = recordsToSave.slice(i, i + batchSize);
|
|
2083
|
+
batches.push(batch);
|
|
2084
|
+
}
|
|
2085
|
+
try {
|
|
2086
|
+
for (const batch of batches) {
|
|
2087
|
+
for (const recordData of batch) {
|
|
2088
|
+
if (!recordData.entity) {
|
|
2089
|
+
this.logger.error("Missing entity property in record data for batchInsert", { recordData, tableName });
|
|
2090
|
+
throw new Error(`Internal error: Missing entity property during batchInsert for ${tableName}`);
|
|
2091
|
+
}
|
|
2092
|
+
this.logger.debug("Attempting to create record in batchInsert:", { entityName, recordData });
|
|
2093
|
+
await this.service.entities[entityName].create(recordData).go();
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
} catch (error$1) {
|
|
2097
|
+
throw new error.MastraError(
|
|
2098
|
+
{
|
|
2099
|
+
id: "STORAGE_DYNAMODB_STORE_BATCH_INSERT_FAILED",
|
|
2100
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2101
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2102
|
+
details: { tableName }
|
|
2103
|
+
},
|
|
2104
|
+
error$1
|
|
2105
|
+
);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Load a record by its keys
|
|
2110
|
+
*/
|
|
2111
|
+
async load({ tableName, keys }) {
|
|
2112
|
+
this.logger.debug("DynamoDB load called", { tableName, keys });
|
|
2113
|
+
const entityName = this.getEntityNameForTable(tableName);
|
|
2114
|
+
if (!entityName || !this.service.entities[entityName]) {
|
|
2115
|
+
throw new error.MastraError({
|
|
2116
|
+
id: "STORAGE_DYNAMODB_STORE_LOAD_INVALID_ARGS",
|
|
2117
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2118
|
+
category: error.ErrorCategory.USER,
|
|
2119
|
+
text: "No entity defined for tableName",
|
|
2120
|
+
details: { tableName }
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
try {
|
|
2124
|
+
const keyObject = { entity: entityName, ...keys };
|
|
2125
|
+
const result = await this.service.entities[entityName].get(keyObject).go();
|
|
2126
|
+
if (!result.data) {
|
|
2127
|
+
return null;
|
|
2128
|
+
}
|
|
2129
|
+
let data = result.data;
|
|
2130
|
+
return data;
|
|
2131
|
+
} catch (error$1) {
|
|
2132
|
+
throw new error.MastraError(
|
|
2133
|
+
{
|
|
2134
|
+
id: "STORAGE_DYNAMODB_STORE_LOAD_FAILED",
|
|
2135
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2136
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2137
|
+
details: { tableName }
|
|
2138
|
+
},
|
|
2139
|
+
error$1
|
|
2140
|
+
);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2144
|
+
var ScoresStorageDynamoDB = class extends storage.ScoresStorage {
|
|
2145
|
+
service;
|
|
2146
|
+
constructor({ service }) {
|
|
2147
|
+
super();
|
|
2148
|
+
this.service = service;
|
|
2149
|
+
}
|
|
2150
|
+
// Helper function to parse score data (handle JSON fields)
|
|
2151
|
+
parseScoreData(data) {
|
|
2152
|
+
return {
|
|
2153
|
+
...data,
|
|
2154
|
+
// Convert date strings back to Date objects for consistency
|
|
2155
|
+
createdAt: data.createdAt ? new Date(data.createdAt) : /* @__PURE__ */ new Date(),
|
|
2156
|
+
updatedAt: data.updatedAt ? new Date(data.updatedAt) : /* @__PURE__ */ new Date()
|
|
2157
|
+
// JSON fields are already transformed by the entity's getters
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
async getScoreById({ id }) {
|
|
2161
|
+
this.logger.debug("Getting score by ID", { id });
|
|
2162
|
+
try {
|
|
2163
|
+
const result = await this.service.entities.score.get({ entity: "score", id }).go();
|
|
2164
|
+
if (!result.data) {
|
|
2165
|
+
return null;
|
|
2166
|
+
}
|
|
2167
|
+
return this.parseScoreData(result.data);
|
|
2168
|
+
} catch (error$1) {
|
|
2169
|
+
throw new error.MastraError(
|
|
2170
|
+
{
|
|
2171
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORE_BY_ID_FAILED",
|
|
2172
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2173
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2174
|
+
details: { id }
|
|
2175
|
+
},
|
|
2176
|
+
error$1
|
|
2177
|
+
);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
async saveScore(score) {
|
|
2181
|
+
this.logger.debug("Saving score", { scorerId: score.scorerId, runId: score.runId });
|
|
2182
|
+
const now = /* @__PURE__ */ new Date();
|
|
2183
|
+
const scoreId = `score-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
2184
|
+
const scoreData = {
|
|
2185
|
+
entity: "score",
|
|
2186
|
+
id: scoreId,
|
|
2187
|
+
scorerId: score.scorerId,
|
|
2188
|
+
traceId: score.traceId || "",
|
|
2189
|
+
runId: score.runId,
|
|
2190
|
+
scorer: typeof score.scorer === "string" ? score.scorer : JSON.stringify(score.scorer),
|
|
2191
|
+
preprocessStepResult: typeof score.preprocessStepResult === "string" ? score.preprocessStepResult : JSON.stringify(score.preprocessStepResult),
|
|
2192
|
+
analyzeStepResult: typeof score.analyzeStepResult === "string" ? score.analyzeStepResult : JSON.stringify(score.analyzeStepResult),
|
|
2193
|
+
score: score.score,
|
|
2194
|
+
reason: score.reason,
|
|
2195
|
+
preprocessPrompt: score.preprocessPrompt,
|
|
2196
|
+
generateScorePrompt: score.generateScorePrompt,
|
|
2197
|
+
analyzePrompt: score.analyzePrompt,
|
|
2198
|
+
reasonPrompt: score.reasonPrompt,
|
|
2199
|
+
input: typeof score.input === "string" ? score.input : JSON.stringify(score.input),
|
|
2200
|
+
output: typeof score.output === "string" ? score.output : JSON.stringify(score.output),
|
|
2201
|
+
additionalContext: typeof score.additionalContext === "string" ? score.additionalContext : JSON.stringify(score.additionalContext),
|
|
2202
|
+
runtimeContext: typeof score.runtimeContext === "string" ? score.runtimeContext : JSON.stringify(score.runtimeContext),
|
|
2203
|
+
entityType: score.entityType,
|
|
2204
|
+
entityData: typeof score.entity === "string" ? score.entity : JSON.stringify(score.entity),
|
|
2205
|
+
entityId: score.entityId,
|
|
2206
|
+
source: score.source,
|
|
2207
|
+
resourceId: score.resourceId || "",
|
|
2208
|
+
threadId: score.threadId || "",
|
|
2209
|
+
createdAt: now.toISOString(),
|
|
2210
|
+
updatedAt: now.toISOString()
|
|
2211
|
+
};
|
|
2212
|
+
try {
|
|
2213
|
+
await this.service.entities.score.upsert(scoreData).go();
|
|
2214
|
+
const savedScore = {
|
|
2215
|
+
...score,
|
|
2216
|
+
id: scoreId,
|
|
2217
|
+
createdAt: now,
|
|
2218
|
+
updatedAt: now
|
|
2219
|
+
};
|
|
2220
|
+
return { score: savedScore };
|
|
2221
|
+
} catch (error$1) {
|
|
2222
|
+
throw new error.MastraError(
|
|
2223
|
+
{
|
|
2224
|
+
id: "STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED",
|
|
2225
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2226
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2227
|
+
details: { scorerId: score.scorerId, runId: score.runId }
|
|
2228
|
+
},
|
|
2229
|
+
error$1
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
async getScoresByScorerId({
|
|
2234
|
+
scorerId,
|
|
2235
|
+
pagination,
|
|
2236
|
+
entityId,
|
|
2237
|
+
entityType,
|
|
2238
|
+
source
|
|
2239
|
+
}) {
|
|
2240
|
+
try {
|
|
2241
|
+
const query = this.service.entities.score.query.byScorer({ entity: "score", scorerId });
|
|
2242
|
+
const results = await query.go();
|
|
2243
|
+
let allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2244
|
+
if (entityId) {
|
|
2245
|
+
allScores = allScores.filter((score) => score.entityId === entityId);
|
|
2246
|
+
}
|
|
2247
|
+
if (entityType) {
|
|
2248
|
+
allScores = allScores.filter((score) => score.entityType === entityType);
|
|
2249
|
+
}
|
|
2250
|
+
if (source) {
|
|
2251
|
+
allScores = allScores.filter((score) => score.source === source);
|
|
2252
|
+
}
|
|
2253
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2254
|
+
const startIndex = pagination.page * pagination.perPage;
|
|
2255
|
+
const endIndex = startIndex + pagination.perPage;
|
|
2256
|
+
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
2257
|
+
const total = allScores.length;
|
|
2258
|
+
const hasMore = endIndex < total;
|
|
2259
|
+
return {
|
|
2260
|
+
scores: paginatedScores,
|
|
2261
|
+
pagination: {
|
|
2262
|
+
total,
|
|
2263
|
+
page: pagination.page,
|
|
2264
|
+
perPage: pagination.perPage,
|
|
2265
|
+
hasMore
|
|
2266
|
+
}
|
|
2267
|
+
};
|
|
2268
|
+
} catch (error$1) {
|
|
2269
|
+
throw new error.MastraError(
|
|
2270
|
+
{
|
|
2271
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
|
|
2272
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2273
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2274
|
+
details: {
|
|
2275
|
+
scorerId: scorerId || "",
|
|
2276
|
+
entityId: entityId || "",
|
|
2277
|
+
entityType: entityType || "",
|
|
2278
|
+
source: source || "",
|
|
2279
|
+
page: pagination.page,
|
|
2280
|
+
perPage: pagination.perPage
|
|
2281
|
+
}
|
|
2282
|
+
},
|
|
2283
|
+
error$1
|
|
2284
|
+
);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
async getScoresByRunId({
|
|
2288
|
+
runId,
|
|
2289
|
+
pagination
|
|
2290
|
+
}) {
|
|
2291
|
+
this.logger.debug("Getting scores by run ID", { runId, pagination });
|
|
2292
|
+
try {
|
|
2293
|
+
const query = this.service.entities.score.query.byRun({ entity: "score", runId });
|
|
2294
|
+
const results = await query.go();
|
|
2295
|
+
const allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2296
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2297
|
+
const startIndex = pagination.page * pagination.perPage;
|
|
2298
|
+
const endIndex = startIndex + pagination.perPage;
|
|
2299
|
+
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
2300
|
+
const total = allScores.length;
|
|
2301
|
+
const hasMore = endIndex < total;
|
|
2302
|
+
return {
|
|
2303
|
+
scores: paginatedScores,
|
|
2304
|
+
pagination: {
|
|
2305
|
+
total,
|
|
2306
|
+
page: pagination.page,
|
|
2307
|
+
perPage: pagination.perPage,
|
|
2308
|
+
hasMore
|
|
2309
|
+
}
|
|
2310
|
+
};
|
|
2311
|
+
} catch (error$1) {
|
|
2312
|
+
throw new error.MastraError(
|
|
2313
|
+
{
|
|
2314
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_RUN_ID_FAILED",
|
|
2315
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2316
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2317
|
+
details: { runId, page: pagination.page, perPage: pagination.perPage }
|
|
2318
|
+
},
|
|
2319
|
+
error$1
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
async getScoresByEntityId({
|
|
2324
|
+
entityId,
|
|
2325
|
+
entityType,
|
|
2326
|
+
pagination
|
|
2327
|
+
}) {
|
|
2328
|
+
this.logger.debug("Getting scores by entity ID", { entityId, entityType, pagination });
|
|
2329
|
+
try {
|
|
2330
|
+
const query = this.service.entities.score.query.byEntityData({ entity: "score", entityId });
|
|
2331
|
+
const results = await query.go();
|
|
2332
|
+
let allScores = results.data.map((data) => this.parseScoreData(data));
|
|
2333
|
+
allScores = allScores.filter((score) => score.entityType === entityType);
|
|
2334
|
+
allScores.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2335
|
+
const startIndex = pagination.page * pagination.perPage;
|
|
2336
|
+
const endIndex = startIndex + pagination.perPage;
|
|
2337
|
+
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
2338
|
+
const total = allScores.length;
|
|
2339
|
+
const hasMore = endIndex < total;
|
|
2340
|
+
return {
|
|
2341
|
+
scores: paginatedScores,
|
|
2342
|
+
pagination: {
|
|
2343
|
+
total,
|
|
2344
|
+
page: pagination.page,
|
|
2345
|
+
perPage: pagination.perPage,
|
|
2346
|
+
hasMore
|
|
2347
|
+
}
|
|
2348
|
+
};
|
|
2349
|
+
} catch (error$1) {
|
|
2350
|
+
throw new error.MastraError(
|
|
2351
|
+
{
|
|
2352
|
+
id: "STORAGE_DYNAMODB_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
2353
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2354
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2355
|
+
details: { entityId, entityType, page: pagination.page, perPage: pagination.perPage }
|
|
2356
|
+
},
|
|
2357
|
+
error$1
|
|
2358
|
+
);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
};
|
|
2362
|
+
var TracesStorageDynamoDB = class extends storage.TracesStorage {
|
|
2363
|
+
service;
|
|
2364
|
+
operations;
|
|
2365
|
+
constructor({ service, operations }) {
|
|
2366
|
+
super();
|
|
2367
|
+
this.service = service;
|
|
2368
|
+
this.operations = operations;
|
|
2369
|
+
}
|
|
2370
|
+
// Trace operations
|
|
2371
|
+
async getTraces(args) {
|
|
2372
|
+
const { name, scope, page, perPage } = args;
|
|
2373
|
+
this.logger.debug("Getting traces", { name, scope, page, perPage });
|
|
2374
|
+
try {
|
|
2375
|
+
let query;
|
|
2376
|
+
if (name) {
|
|
2377
|
+
query = this.service.entities.trace.query.byName({ entity: "trace", name });
|
|
2378
|
+
} else if (scope) {
|
|
2379
|
+
query = this.service.entities.trace.query.byScope({ entity: "trace", scope });
|
|
2380
|
+
} else {
|
|
2381
|
+
this.logger.warn("Performing a scan operation on traces - consider using a more specific query");
|
|
2382
|
+
query = this.service.entities.trace.scan;
|
|
2383
|
+
}
|
|
2384
|
+
let items = [];
|
|
2385
|
+
let cursor = null;
|
|
2386
|
+
let pagesFetched = 0;
|
|
2387
|
+
const startPage = page > 0 ? page : 1;
|
|
2388
|
+
do {
|
|
2389
|
+
const results = await query.go({ cursor, limit: perPage });
|
|
2390
|
+
pagesFetched++;
|
|
2391
|
+
if (pagesFetched === startPage) {
|
|
2392
|
+
items = results.data;
|
|
2393
|
+
break;
|
|
2394
|
+
}
|
|
2395
|
+
cursor = results.cursor;
|
|
2396
|
+
if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
|
|
2397
|
+
break;
|
|
2398
|
+
}
|
|
2399
|
+
} while (cursor && pagesFetched < startPage);
|
|
2400
|
+
return items;
|
|
2401
|
+
} catch (error$1) {
|
|
2402
|
+
throw new error.MastraError(
|
|
2403
|
+
{
|
|
2404
|
+
id: "STORAGE_DYNAMODB_STORE_GET_TRACES_FAILED",
|
|
2405
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2406
|
+
category: error.ErrorCategory.THIRD_PARTY
|
|
2407
|
+
},
|
|
2408
|
+
error$1
|
|
2409
|
+
);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
async batchTraceInsert({ records }) {
|
|
2413
|
+
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
2414
|
+
if (!records.length) {
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
try {
|
|
2418
|
+
const recordsToSave = records.map((rec) => ({ entity: "trace", ...rec }));
|
|
2419
|
+
await this.operations.batchInsert({
|
|
2420
|
+
tableName: storage.TABLE_TRACES,
|
|
2421
|
+
records: recordsToSave
|
|
2422
|
+
// Pass records with 'entity' included
|
|
2423
|
+
});
|
|
2424
|
+
} catch (error$1) {
|
|
2425
|
+
throw new error.MastraError(
|
|
2426
|
+
{
|
|
2427
|
+
id: "STORAGE_DYNAMODB_STORE_BATCH_TRACE_INSERT_FAILED",
|
|
2428
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2429
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2430
|
+
details: { count: records.length }
|
|
2431
|
+
},
|
|
2432
|
+
error$1
|
|
2433
|
+
);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
async getTracesPaginated(args) {
|
|
2437
|
+
const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
|
|
2438
|
+
this.logger.debug("Getting traces with pagination", { name, scope, page, perPage, attributes, filters, dateRange });
|
|
2439
|
+
try {
|
|
2440
|
+
let query;
|
|
2441
|
+
if (name) {
|
|
2442
|
+
query = this.service.entities.trace.query.byName({ entity: "trace", name });
|
|
2443
|
+
} else if (scope) {
|
|
2444
|
+
query = this.service.entities.trace.query.byScope({ entity: "trace", scope });
|
|
2445
|
+
} else {
|
|
2446
|
+
this.logger.warn("Performing a scan operation on traces - consider using a more specific query");
|
|
2447
|
+
query = this.service.entities.trace.scan;
|
|
2448
|
+
}
|
|
2449
|
+
const results = await query.go({
|
|
2450
|
+
order: "desc",
|
|
2451
|
+
pages: "all"
|
|
2452
|
+
// Get all pages to apply filtering and pagination
|
|
2453
|
+
});
|
|
2454
|
+
if (!results.data.length) {
|
|
2455
|
+
return {
|
|
2456
|
+
traces: [],
|
|
2457
|
+
total: 0,
|
|
2458
|
+
page,
|
|
2459
|
+
perPage,
|
|
2460
|
+
hasMore: false
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
let filteredData = results.data;
|
|
2464
|
+
if (attributes) {
|
|
2465
|
+
filteredData = filteredData.filter((item) => {
|
|
2466
|
+
try {
|
|
2467
|
+
let itemAttributes = {};
|
|
2468
|
+
if (item.attributes) {
|
|
2469
|
+
if (typeof item.attributes === "string") {
|
|
2470
|
+
if (item.attributes === "[object Object]") {
|
|
2471
|
+
itemAttributes = {};
|
|
2472
|
+
} else {
|
|
2473
|
+
try {
|
|
2474
|
+
itemAttributes = JSON.parse(item.attributes);
|
|
2475
|
+
} catch {
|
|
2476
|
+
itemAttributes = {};
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
} else if (typeof item.attributes === "object") {
|
|
2480
|
+
itemAttributes = item.attributes;
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
return Object.entries(attributes).every(([key, value]) => itemAttributes[key] === value);
|
|
2484
|
+
} catch (e) {
|
|
2485
|
+
this.logger.warn("Failed to parse attributes during filtering", { item, error: e });
|
|
2486
|
+
return false;
|
|
2487
|
+
}
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
if (dateRange?.start) {
|
|
2491
|
+
filteredData = filteredData.filter((item) => {
|
|
2492
|
+
const itemDate = new Date(item.createdAt);
|
|
2493
|
+
return itemDate >= dateRange.start;
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
if (dateRange?.end) {
|
|
2497
|
+
filteredData = filteredData.filter((item) => {
|
|
2498
|
+
const itemDate = new Date(item.createdAt);
|
|
2499
|
+
return itemDate <= dateRange.end;
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
const total = filteredData.length;
|
|
2503
|
+
const start = page * perPage;
|
|
2504
|
+
const end = start + perPage;
|
|
2505
|
+
const paginatedData = filteredData.slice(start, end);
|
|
2506
|
+
const traces = paginatedData.map((item) => {
|
|
2507
|
+
let attributes2;
|
|
2508
|
+
if (item.attributes) {
|
|
2509
|
+
if (typeof item.attributes === "string") {
|
|
2510
|
+
if (item.attributes === "[object Object]") {
|
|
2511
|
+
attributes2 = void 0;
|
|
2512
|
+
} else {
|
|
2513
|
+
try {
|
|
2514
|
+
attributes2 = JSON.parse(item.attributes);
|
|
2515
|
+
} catch {
|
|
2516
|
+
attributes2 = void 0;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
} else if (typeof item.attributes === "object") {
|
|
2520
|
+
attributes2 = item.attributes;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
let status;
|
|
2524
|
+
if (item.status) {
|
|
2525
|
+
if (typeof item.status === "string") {
|
|
2526
|
+
try {
|
|
2527
|
+
status = JSON.parse(item.status);
|
|
2528
|
+
} catch {
|
|
2529
|
+
status = void 0;
|
|
2530
|
+
}
|
|
2531
|
+
} else if (typeof item.status === "object") {
|
|
2532
|
+
status = item.status;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
let events;
|
|
2536
|
+
if (item.events) {
|
|
2537
|
+
if (typeof item.events === "string") {
|
|
2538
|
+
try {
|
|
2539
|
+
events = JSON.parse(item.events);
|
|
2540
|
+
} catch {
|
|
2541
|
+
events = void 0;
|
|
2542
|
+
}
|
|
2543
|
+
} else if (Array.isArray(item.events)) {
|
|
2544
|
+
events = item.events;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
let links;
|
|
2548
|
+
if (item.links) {
|
|
2549
|
+
if (typeof item.links === "string") {
|
|
2550
|
+
try {
|
|
2551
|
+
links = JSON.parse(item.links);
|
|
2552
|
+
} catch {
|
|
2553
|
+
links = void 0;
|
|
2554
|
+
}
|
|
2555
|
+
} else if (Array.isArray(item.links)) {
|
|
2556
|
+
links = item.links;
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
return {
|
|
2560
|
+
id: item.id,
|
|
2561
|
+
parentSpanId: item.parentSpanId,
|
|
2562
|
+
name: item.name,
|
|
2563
|
+
traceId: item.traceId,
|
|
2564
|
+
scope: item.scope,
|
|
2565
|
+
kind: item.kind,
|
|
2566
|
+
attributes: attributes2,
|
|
2567
|
+
status,
|
|
2568
|
+
events,
|
|
2569
|
+
links,
|
|
2570
|
+
other: item.other,
|
|
2571
|
+
startTime: item.startTime,
|
|
2572
|
+
endTime: item.endTime,
|
|
2573
|
+
createdAt: item.createdAt
|
|
2574
|
+
};
|
|
2575
|
+
});
|
|
2576
|
+
return {
|
|
2577
|
+
traces,
|
|
2578
|
+
total,
|
|
2579
|
+
page,
|
|
2580
|
+
perPage,
|
|
2581
|
+
hasMore: end < total
|
|
2582
|
+
};
|
|
2583
|
+
} catch (error$1) {
|
|
2584
|
+
throw new error.MastraError(
|
|
2585
|
+
{
|
|
2586
|
+
id: "STORAGE_DYNAMODB_STORE_GET_TRACES_PAGINATED_FAILED",
|
|
2587
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2588
|
+
category: error.ErrorCategory.THIRD_PARTY
|
|
2589
|
+
},
|
|
2590
|
+
error$1
|
|
2591
|
+
);
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
};
|
|
2595
|
+
function formatWorkflowRun(snapshotData) {
|
|
2596
|
+
return {
|
|
2597
|
+
workflowName: snapshotData.workflow_name,
|
|
2598
|
+
runId: snapshotData.run_id,
|
|
2599
|
+
snapshot: snapshotData.snapshot,
|
|
2600
|
+
createdAt: new Date(snapshotData.createdAt),
|
|
2601
|
+
updatedAt: new Date(snapshotData.updatedAt),
|
|
2602
|
+
resourceId: snapshotData.resourceId
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
var WorkflowStorageDynamoDB = class extends storage.WorkflowsStorage {
|
|
2606
|
+
service;
|
|
2607
|
+
constructor({ service }) {
|
|
2608
|
+
super();
|
|
2609
|
+
this.service = service;
|
|
2610
|
+
}
|
|
2611
|
+
updateWorkflowResults({
|
|
2612
|
+
// workflowName,
|
|
2613
|
+
// runId,
|
|
2614
|
+
// stepId,
|
|
2615
|
+
// result,
|
|
2616
|
+
// runtimeContext,
|
|
2617
|
+
}) {
|
|
2618
|
+
throw new Error("Method not implemented.");
|
|
2619
|
+
}
|
|
2620
|
+
updateWorkflowState({
|
|
2621
|
+
// workflowName,
|
|
2622
|
+
// runId,
|
|
2623
|
+
// opts,
|
|
2624
|
+
}) {
|
|
2625
|
+
throw new Error("Method not implemented.");
|
|
2626
|
+
}
|
|
2627
|
+
// Workflow operations
|
|
2628
|
+
async persistWorkflowSnapshot({
|
|
2629
|
+
workflowName,
|
|
2630
|
+
runId,
|
|
2631
|
+
snapshot
|
|
2632
|
+
}) {
|
|
2633
|
+
this.logger.debug("Persisting workflow snapshot", { workflowName, runId });
|
|
2634
|
+
try {
|
|
2635
|
+
const resourceId = "resourceId" in snapshot ? snapshot.resourceId : void 0;
|
|
2636
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2637
|
+
const data = {
|
|
2638
|
+
entity: "workflow_snapshot",
|
|
2639
|
+
// Add entity type
|
|
2640
|
+
workflow_name: workflowName,
|
|
2641
|
+
run_id: runId,
|
|
2642
|
+
snapshot: JSON.stringify(snapshot),
|
|
2643
|
+
// Stringify the snapshot object
|
|
2644
|
+
createdAt: now,
|
|
2645
|
+
updatedAt: now,
|
|
2646
|
+
resourceId
|
|
2647
|
+
};
|
|
2648
|
+
await this.service.entities.workflow_snapshot.upsert(data).go();
|
|
2649
|
+
} catch (error$1) {
|
|
2650
|
+
throw new error.MastraError(
|
|
2651
|
+
{
|
|
2652
|
+
id: "STORAGE_DYNAMODB_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
|
|
2653
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2654
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2655
|
+
details: { workflowName, runId }
|
|
2656
|
+
},
|
|
2657
|
+
error$1
|
|
2658
|
+
);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
async loadWorkflowSnapshot({
|
|
2662
|
+
workflowName,
|
|
2663
|
+
runId
|
|
2664
|
+
}) {
|
|
2665
|
+
this.logger.debug("Loading workflow snapshot", { workflowName, runId });
|
|
2666
|
+
try {
|
|
2667
|
+
const result = await this.service.entities.workflow_snapshot.get({
|
|
2668
|
+
entity: "workflow_snapshot",
|
|
2669
|
+
// Add entity type
|
|
2670
|
+
workflow_name: workflowName,
|
|
2671
|
+
run_id: runId
|
|
2672
|
+
}).go();
|
|
2673
|
+
if (!result.data?.snapshot) {
|
|
2674
|
+
return null;
|
|
2675
|
+
}
|
|
2676
|
+
return result.data.snapshot;
|
|
2677
|
+
} catch (error$1) {
|
|
2678
|
+
throw new error.MastraError(
|
|
2679
|
+
{
|
|
2680
|
+
id: "STORAGE_DYNAMODB_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
|
|
2681
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2682
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2683
|
+
details: { workflowName, runId }
|
|
2684
|
+
},
|
|
2685
|
+
error$1
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
async getWorkflowRuns(args) {
|
|
2690
|
+
this.logger.debug("Getting workflow runs", { args });
|
|
2691
|
+
try {
|
|
2692
|
+
const limit = args?.limit || 10;
|
|
2693
|
+
const offset = args?.offset || 0;
|
|
2694
|
+
let query;
|
|
2695
|
+
if (args?.workflowName) {
|
|
2696
|
+
query = this.service.entities.workflow_snapshot.query.primary({
|
|
2697
|
+
entity: "workflow_snapshot",
|
|
2698
|
+
// Add entity type
|
|
2699
|
+
workflow_name: args.workflowName
|
|
2700
|
+
});
|
|
2701
|
+
} else {
|
|
2702
|
+
this.logger.warn("Performing a scan operation on workflow snapshots - consider using a more specific query");
|
|
2703
|
+
query = this.service.entities.workflow_snapshot.scan;
|
|
2704
|
+
}
|
|
2705
|
+
const allMatchingSnapshots = [];
|
|
2706
|
+
let cursor = null;
|
|
2707
|
+
const DYNAMODB_PAGE_SIZE = 100;
|
|
2708
|
+
do {
|
|
2709
|
+
const pageResults = await query.go({
|
|
2710
|
+
limit: DYNAMODB_PAGE_SIZE,
|
|
2711
|
+
cursor
|
|
2712
|
+
});
|
|
2713
|
+
if (pageResults.data && pageResults.data.length > 0) {
|
|
2714
|
+
let pageFilteredData = pageResults.data;
|
|
2715
|
+
if (args?.fromDate || args?.toDate) {
|
|
2716
|
+
pageFilteredData = pageFilteredData.filter((snapshot) => {
|
|
2717
|
+
const createdAt = new Date(snapshot.createdAt);
|
|
2718
|
+
if (args.fromDate && createdAt < args.fromDate) {
|
|
2719
|
+
return false;
|
|
2720
|
+
}
|
|
2721
|
+
if (args.toDate && createdAt > args.toDate) {
|
|
2722
|
+
return false;
|
|
2723
|
+
}
|
|
2724
|
+
return true;
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2727
|
+
if (args?.resourceId) {
|
|
2728
|
+
pageFilteredData = pageFilteredData.filter((snapshot) => {
|
|
2729
|
+
return snapshot.resourceId === args.resourceId;
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
allMatchingSnapshots.push(...pageFilteredData);
|
|
2733
|
+
}
|
|
2734
|
+
cursor = pageResults.cursor;
|
|
2735
|
+
} while (cursor);
|
|
2736
|
+
if (!allMatchingSnapshots.length) {
|
|
2737
|
+
return { runs: [], total: 0 };
|
|
2738
|
+
}
|
|
2739
|
+
const total = allMatchingSnapshots.length;
|
|
2740
|
+
const paginatedData = allMatchingSnapshots.slice(offset, offset + limit);
|
|
2741
|
+
const runs = paginatedData.map((snapshot) => formatWorkflowRun(snapshot));
|
|
2742
|
+
return {
|
|
2743
|
+
runs,
|
|
2744
|
+
total
|
|
2745
|
+
};
|
|
2746
|
+
} catch (error$1) {
|
|
2747
|
+
throw new error.MastraError(
|
|
2748
|
+
{
|
|
2749
|
+
id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUNS_FAILED",
|
|
2750
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2751
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2752
|
+
details: { workflowName: args?.workflowName || "", resourceId: args?.resourceId || "" }
|
|
2753
|
+
},
|
|
2754
|
+
error$1
|
|
2755
|
+
);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
async getWorkflowRunById(args) {
|
|
2759
|
+
const { runId, workflowName } = args;
|
|
2760
|
+
this.logger.debug("Getting workflow run by ID", { runId, workflowName });
|
|
2761
|
+
console.log("workflowName", workflowName);
|
|
2762
|
+
console.log("runId", runId);
|
|
2763
|
+
try {
|
|
2764
|
+
if (workflowName) {
|
|
2765
|
+
this.logger.debug("WorkflowName provided, using direct GET operation.");
|
|
2766
|
+
const result2 = await this.service.entities.workflow_snapshot.get({
|
|
2767
|
+
entity: "workflow_snapshot",
|
|
2768
|
+
// Entity type for PK
|
|
2769
|
+
workflow_name: workflowName,
|
|
2770
|
+
run_id: runId
|
|
2771
|
+
}).go();
|
|
2772
|
+
console.log("result", result2);
|
|
2773
|
+
if (!result2.data) {
|
|
2774
|
+
return null;
|
|
2775
|
+
}
|
|
2776
|
+
const snapshot2 = result2.data.snapshot;
|
|
2777
|
+
return {
|
|
2778
|
+
workflowName: result2.data.workflow_name,
|
|
2779
|
+
runId: result2.data.run_id,
|
|
2780
|
+
snapshot: snapshot2,
|
|
2781
|
+
createdAt: new Date(result2.data.createdAt),
|
|
2782
|
+
updatedAt: new Date(result2.data.updatedAt),
|
|
2783
|
+
resourceId: result2.data.resourceId
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
this.logger.debug(
|
|
2787
|
+
'WorkflowName not provided. Attempting to find workflow run by runId using GSI. Ensure GSI (e.g., "byRunId") is defined on the workflowSnapshot entity with run_id as its key and provisioned in DynamoDB.'
|
|
2788
|
+
);
|
|
2789
|
+
const result = await this.service.entities.workflow_snapshot.query.gsi2({ entity: "workflow_snapshot", run_id: runId }).go();
|
|
2790
|
+
const matchingRunDbItem = result.data && result.data.length > 0 ? result.data[0] : null;
|
|
2791
|
+
if (!matchingRunDbItem) {
|
|
2792
|
+
return null;
|
|
2793
|
+
}
|
|
2794
|
+
const snapshot = matchingRunDbItem.snapshot;
|
|
2795
|
+
return {
|
|
2796
|
+
workflowName: matchingRunDbItem.workflow_name,
|
|
2797
|
+
runId: matchingRunDbItem.run_id,
|
|
2798
|
+
snapshot,
|
|
2799
|
+
createdAt: new Date(matchingRunDbItem.createdAt),
|
|
2800
|
+
updatedAt: new Date(matchingRunDbItem.updatedAt),
|
|
2801
|
+
resourceId: matchingRunDbItem.resourceId
|
|
2802
|
+
};
|
|
2803
|
+
} catch (error$1) {
|
|
2804
|
+
throw new error.MastraError(
|
|
2805
|
+
{
|
|
2806
|
+
id: "STORAGE_DYNAMODB_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
|
|
2807
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2808
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2809
|
+
details: { runId, workflowName: args?.workflowName || "" }
|
|
2810
|
+
},
|
|
2811
|
+
error$1
|
|
2812
|
+
);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
};
|
|
2816
|
+
|
|
2817
|
+
// src/storage/index.ts
|
|
2818
|
+
var DynamoDBStore = class extends storage.MastraStorage {
|
|
2819
|
+
tableName;
|
|
2820
|
+
client;
|
|
2821
|
+
service;
|
|
2822
|
+
hasInitialized = null;
|
|
2823
|
+
stores;
|
|
2824
|
+
constructor({ name, config }) {
|
|
2825
|
+
super({ name });
|
|
2826
|
+
try {
|
|
2827
|
+
if (!config.tableName || typeof config.tableName !== "string" || config.tableName.trim() === "") {
|
|
2828
|
+
throw new Error("DynamoDBStore: config.tableName must be provided and cannot be empty.");
|
|
2829
|
+
}
|
|
2830
|
+
if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
|
|
2831
|
+
throw new Error(
|
|
2832
|
+
`DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2835
|
+
const dynamoClient = new clientDynamodb.DynamoDBClient({
|
|
2836
|
+
region: config.region || "us-east-1",
|
|
2837
|
+
endpoint: config.endpoint,
|
|
2838
|
+
credentials: config.credentials
|
|
2839
|
+
});
|
|
2840
|
+
this.tableName = config.tableName;
|
|
2841
|
+
this.client = libDynamodb.DynamoDBDocumentClient.from(dynamoClient);
|
|
2842
|
+
this.service = getElectroDbService(this.client, this.tableName);
|
|
2843
|
+
const operations = new StoreOperationsDynamoDB({
|
|
2844
|
+
service: this.service,
|
|
2845
|
+
tableName: this.tableName,
|
|
2846
|
+
client: this.client
|
|
2847
|
+
});
|
|
2848
|
+
const traces = new TracesStorageDynamoDB({ service: this.service, operations });
|
|
2849
|
+
const workflows = new WorkflowStorageDynamoDB({ service: this.service });
|
|
2850
|
+
const memory = new MemoryStorageDynamoDB({ service: this.service });
|
|
2851
|
+
const scores = new ScoresStorageDynamoDB({ service: this.service });
|
|
2852
|
+
this.stores = {
|
|
2853
|
+
operations,
|
|
2854
|
+
legacyEvals: new LegacyEvalsDynamoDB({ service: this.service, tableName: this.tableName }),
|
|
2855
|
+
traces,
|
|
2856
|
+
workflows,
|
|
2857
|
+
memory,
|
|
2858
|
+
scores
|
|
2859
|
+
};
|
|
2860
|
+
} catch (error$1) {
|
|
2861
|
+
throw new error.MastraError(
|
|
2862
|
+
{
|
|
2863
|
+
id: "STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED",
|
|
2864
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2865
|
+
category: error.ErrorCategory.USER
|
|
2866
|
+
},
|
|
2867
|
+
error$1
|
|
2868
|
+
);
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
get supports() {
|
|
2872
|
+
return {
|
|
2873
|
+
selectByIncludeResourceScope: true,
|
|
2874
|
+
resourceWorkingMemory: true,
|
|
2875
|
+
hasColumn: false,
|
|
2876
|
+
createTable: false,
|
|
2877
|
+
deleteMessages: false
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
/**
|
|
2881
|
+
* Validates that the required DynamoDB table exists and is accessible.
|
|
2882
|
+
* This does not check the table structure - it assumes the table
|
|
2883
|
+
* was created with the correct structure via CDK/CloudFormation.
|
|
2884
|
+
*/
|
|
2885
|
+
async validateTableExists() {
|
|
2886
|
+
try {
|
|
2887
|
+
const command = new clientDynamodb.DescribeTableCommand({
|
|
2888
|
+
TableName: this.tableName
|
|
2889
|
+
});
|
|
2890
|
+
await this.client.send(command);
|
|
2891
|
+
return true;
|
|
2892
|
+
} catch (error$1) {
|
|
2893
|
+
if (error$1.name === "ResourceNotFoundException") {
|
|
2894
|
+
return false;
|
|
2895
|
+
}
|
|
2896
|
+
throw new error.MastraError(
|
|
2897
|
+
{
|
|
2898
|
+
id: "STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED",
|
|
2899
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2900
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2901
|
+
details: { tableName: this.tableName }
|
|
2902
|
+
},
|
|
2903
|
+
error$1
|
|
2904
|
+
);
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
/**
|
|
2908
|
+
* Initialize storage, validating the externally managed table is accessible.
|
|
2909
|
+
* For the single-table design, we only validate once that we can access
|
|
2910
|
+
* the table that was created via CDK/CloudFormation.
|
|
2911
|
+
*/
|
|
2912
|
+
async init() {
|
|
2913
|
+
if (this.hasInitialized === null) {
|
|
2914
|
+
this.hasInitialized = this._performInitializationAndStore();
|
|
2915
|
+
}
|
|
2916
|
+
try {
|
|
2917
|
+
await this.hasInitialized;
|
|
2918
|
+
} catch (error$1) {
|
|
2919
|
+
throw new error.MastraError(
|
|
2920
|
+
{
|
|
2921
|
+
id: "STORAGE_DYNAMODB_STORE_INIT_FAILED",
|
|
2922
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2923
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2924
|
+
details: { tableName: this.tableName }
|
|
2925
|
+
},
|
|
2926
|
+
error$1
|
|
2927
|
+
);
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
/**
|
|
2931
|
+
* Performs the actual table validation and stores the promise.
|
|
2932
|
+
* Handles resetting the stored promise on failure to allow retries.
|
|
2933
|
+
*/
|
|
2934
|
+
_performInitializationAndStore() {
|
|
2935
|
+
return this.validateTableExists().then((exists) => {
|
|
2936
|
+
if (!exists) {
|
|
2937
|
+
throw new Error(
|
|
2938
|
+
`Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`
|
|
2939
|
+
);
|
|
2940
|
+
}
|
|
2941
|
+
return true;
|
|
2942
|
+
}).catch((err) => {
|
|
2943
|
+
this.hasInitialized = null;
|
|
2944
|
+
throw err;
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
async createTable({ tableName, schema }) {
|
|
2948
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
2949
|
+
}
|
|
2950
|
+
async alterTable(_args) {
|
|
2951
|
+
return this.stores.operations.alterTable(_args);
|
|
2952
|
+
}
|
|
2953
|
+
async clearTable({ tableName }) {
|
|
2954
|
+
return this.stores.operations.clearTable({ tableName });
|
|
2955
|
+
}
|
|
2956
|
+
async dropTable({ tableName }) {
|
|
2957
|
+
return this.stores.operations.dropTable({ tableName });
|
|
2958
|
+
}
|
|
2959
|
+
async insert({ tableName, record }) {
|
|
2960
|
+
return this.stores.operations.insert({ tableName, record });
|
|
2961
|
+
}
|
|
2962
|
+
async batchInsert({ tableName, records }) {
|
|
2963
|
+
return this.stores.operations.batchInsert({ tableName, records });
|
|
2964
|
+
}
|
|
2965
|
+
async load({ tableName, keys }) {
|
|
2966
|
+
return this.stores.operations.load({ tableName, keys });
|
|
2967
|
+
}
|
|
2968
|
+
// Thread operations
|
|
2969
|
+
async getThreadById({ threadId }) {
|
|
2970
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
2971
|
+
}
|
|
2972
|
+
async getThreadsByResourceId(args) {
|
|
2973
|
+
return this.stores.memory.getThreadsByResourceId(args);
|
|
2974
|
+
}
|
|
2975
|
+
async saveThread({ thread }) {
|
|
2976
|
+
return this.stores.memory.saveThread({ thread });
|
|
2977
|
+
}
|
|
2978
|
+
async updateThread({
|
|
2979
|
+
id,
|
|
2980
|
+
title,
|
|
2981
|
+
metadata
|
|
2982
|
+
}) {
|
|
2983
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2984
|
+
}
|
|
2985
|
+
async deleteThread({ threadId }) {
|
|
2986
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
2987
|
+
}
|
|
2988
|
+
async getMessages({
|
|
2989
|
+
threadId,
|
|
2990
|
+
resourceId,
|
|
2991
|
+
selectBy,
|
|
2992
|
+
format
|
|
2993
|
+
}) {
|
|
2994
|
+
return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
|
|
2995
|
+
}
|
|
2996
|
+
async getMessagesById({
|
|
2997
|
+
messageIds,
|
|
2998
|
+
format
|
|
2999
|
+
}) {
|
|
3000
|
+
return this.stores.memory.getMessagesById({ messageIds, format });
|
|
3001
|
+
}
|
|
3002
|
+
async saveMessages(args) {
|
|
3003
|
+
return this.stores.memory.saveMessages(args);
|
|
3004
|
+
}
|
|
3005
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
3006
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
3007
|
+
}
|
|
3008
|
+
async getMessagesPaginated(args) {
|
|
3009
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
3010
|
+
}
|
|
3011
|
+
async updateMessages(_args) {
|
|
3012
|
+
return this.stores.memory.updateMessages(_args);
|
|
3013
|
+
}
|
|
3014
|
+
// Trace operations
|
|
3015
|
+
async getTraces(args) {
|
|
3016
|
+
return this.stores.traces.getTraces(args);
|
|
3017
|
+
}
|
|
3018
|
+
async batchTraceInsert({ records }) {
|
|
3019
|
+
return this.stores.traces.batchTraceInsert({ records });
|
|
3020
|
+
}
|
|
3021
|
+
async getTracesPaginated(_args) {
|
|
3022
|
+
return this.stores.traces.getTracesPaginated(_args);
|
|
3023
|
+
}
|
|
3024
|
+
// Workflow operations
|
|
3025
|
+
async updateWorkflowResults({
|
|
3026
|
+
workflowName,
|
|
3027
|
+
runId,
|
|
3028
|
+
stepId,
|
|
3029
|
+
result,
|
|
3030
|
+
runtimeContext
|
|
3031
|
+
}) {
|
|
3032
|
+
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
|
|
3033
|
+
}
|
|
3034
|
+
async updateWorkflowState({
|
|
3035
|
+
workflowName,
|
|
3036
|
+
runId,
|
|
3037
|
+
opts
|
|
3038
|
+
}) {
|
|
3039
|
+
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
3040
|
+
}
|
|
3041
|
+
async persistWorkflowSnapshot({
|
|
3042
|
+
workflowName,
|
|
3043
|
+
runId,
|
|
3044
|
+
snapshot
|
|
3045
|
+
}) {
|
|
3046
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
3047
|
+
}
|
|
3048
|
+
async loadWorkflowSnapshot({
|
|
3049
|
+
workflowName,
|
|
3050
|
+
runId
|
|
3051
|
+
}) {
|
|
3052
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
3053
|
+
}
|
|
3054
|
+
async getWorkflowRuns(args) {
|
|
3055
|
+
return this.stores.workflows.getWorkflowRuns(args);
|
|
3056
|
+
}
|
|
3057
|
+
async getWorkflowRunById(args) {
|
|
3058
|
+
return this.stores.workflows.getWorkflowRunById(args);
|
|
3059
|
+
}
|
|
3060
|
+
async getResourceById({ resourceId }) {
|
|
3061
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
3062
|
+
}
|
|
3063
|
+
async saveResource({ resource }) {
|
|
3064
|
+
return this.stores.memory.saveResource({ resource });
|
|
3065
|
+
}
|
|
3066
|
+
async updateResource({
|
|
3067
|
+
resourceId,
|
|
3068
|
+
workingMemory,
|
|
3069
|
+
metadata
|
|
3070
|
+
}) {
|
|
3071
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
3072
|
+
}
|
|
3073
|
+
// Eval operations
|
|
3074
|
+
async getEvalsByAgentName(agentName, type) {
|
|
3075
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
3076
|
+
}
|
|
3077
|
+
async getEvals(options) {
|
|
3078
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
3079
|
+
}
|
|
3080
|
+
/**
|
|
3081
|
+
* Closes the DynamoDB client connection and cleans up resources.
|
|
3082
|
+
* Should be called when the store is no longer needed, e.g., at the end of tests or application shutdown.
|
|
3083
|
+
*/
|
|
3084
|
+
async close() {
|
|
3085
|
+
this.logger.debug("Closing DynamoDB client for store:", { name: this.name });
|
|
3086
|
+
try {
|
|
3087
|
+
this.client.destroy();
|
|
3088
|
+
this.logger.debug("DynamoDB client closed successfully for store:", { name: this.name });
|
|
3089
|
+
} catch (error$1) {
|
|
3090
|
+
throw new error.MastraError(
|
|
3091
|
+
{
|
|
3092
|
+
id: "STORAGE_DYNAMODB_STORE_CLOSE_FAILED",
|
|
3093
|
+
domain: error.ErrorDomain.STORAGE,
|
|
3094
|
+
category: error.ErrorCategory.THIRD_PARTY
|
|
3095
|
+
},
|
|
3096
|
+
error$1
|
|
3097
|
+
);
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
/**
|
|
3101
|
+
* SCORERS - Not implemented
|
|
3102
|
+
*/
|
|
3103
|
+
async getScoreById({ id: _id }) {
|
|
3104
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
3105
|
+
}
|
|
3106
|
+
async saveScore(_score) {
|
|
3107
|
+
return this.stores.scores.saveScore(_score);
|
|
3108
|
+
}
|
|
3109
|
+
async getScoresByRunId({
|
|
3110
|
+
runId: _runId,
|
|
3111
|
+
pagination: _pagination
|
|
3112
|
+
}) {
|
|
3113
|
+
return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
|
|
3114
|
+
}
|
|
3115
|
+
async getScoresByEntityId({
|
|
3116
|
+
entityId: _entityId,
|
|
3117
|
+
entityType: _entityType,
|
|
3118
|
+
pagination: _pagination
|
|
3119
|
+
}) {
|
|
3120
|
+
return this.stores.scores.getScoresByEntityId({
|
|
3121
|
+
entityId: _entityId,
|
|
3122
|
+
entityType: _entityType,
|
|
3123
|
+
pagination: _pagination
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
async getScoresByScorerId({
|
|
3127
|
+
scorerId,
|
|
3128
|
+
source,
|
|
3129
|
+
entityId,
|
|
3130
|
+
entityType,
|
|
3131
|
+
pagination
|
|
3132
|
+
}) {
|
|
3133
|
+
return this.stores.scores.getScoresByScorerId({ scorerId, source, entityId, entityType, pagination });
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
|
|
3137
|
+
exports.DynamoDBStore = DynamoDBStore;
|
|
3138
|
+
//# sourceMappingURL=index.cjs.map
|
|
3139
|
+
//# sourceMappingURL=index.cjs.map
|