@mastra/dynamodb 0.14.5 → 0.14.6-alpha.1
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 +752 -0
- package/package.json +14 -5
- package/src/entities/eval.ts +0 -102
- package/src/entities/index.ts +0 -27
- package/src/entities/message.ts +0 -143
- package/src/entities/resource.ts +0 -57
- package/src/entities/score.ts +0 -317
- package/src/entities/thread.ts +0 -66
- package/src/entities/trace.ts +0 -129
- package/src/entities/utils.ts +0 -51
- package/src/entities/workflow-snapshot.ts +0 -56
- package/src/index.ts +0 -1
- package/src/storage/docker-compose.yml +0 -16
- package/src/storage/domains/legacy-evals/index.ts +0 -243
- package/src/storage/domains/memory/index.ts +0 -987
- package/src/storage/domains/operations/index.ts +0 -435
- package/src/storage/domains/score/index.ts +0 -292
- package/src/storage/domains/traces/index.ts +0 -286
- package/src/storage/domains/workflows/index.ts +0 -334
- package/src/storage/index.test.ts +0 -1420
- package/src/storage/index.ts +0 -538
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
2
|
-
import type { ScoreRowData, ScoringSource } from '@mastra/core/scores';
|
|
3
|
-
import { ScoresStorage } from '@mastra/core/storage';
|
|
4
|
-
import type { PaginationInfo, StoragePagination } from '@mastra/core/storage';
|
|
5
|
-
import type { Service } from 'electrodb';
|
|
6
|
-
|
|
7
|
-
export class ScoresStorageDynamoDB extends ScoresStorage {
|
|
8
|
-
private service: Service<Record<string, any>>;
|
|
9
|
-
constructor({ service }: { service: Service<Record<string, any>> }) {
|
|
10
|
-
super();
|
|
11
|
-
this.service = service;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Helper function to parse score data (handle JSON fields)
|
|
15
|
-
private parseScoreData(data: any): ScoreRowData {
|
|
16
|
-
return {
|
|
17
|
-
...data,
|
|
18
|
-
// Convert date strings back to Date objects for consistency
|
|
19
|
-
createdAt: data.createdAt ? new Date(data.createdAt) : new Date(),
|
|
20
|
-
updatedAt: data.updatedAt ? new Date(data.updatedAt) : new Date(),
|
|
21
|
-
// JSON fields are already transformed by the entity's getters
|
|
22
|
-
} as ScoreRowData;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async getScoreById({ id }: { id: string }): Promise<ScoreRowData | null> {
|
|
26
|
-
this.logger.debug('Getting score by ID', { id });
|
|
27
|
-
try {
|
|
28
|
-
const result = await this.service.entities.score.get({ entity: 'score', id }).go();
|
|
29
|
-
|
|
30
|
-
if (!result.data) {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return this.parseScoreData(result.data);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
throw new MastraError(
|
|
37
|
-
{
|
|
38
|
-
id: 'STORAGE_DYNAMODB_STORE_GET_SCORE_BY_ID_FAILED',
|
|
39
|
-
domain: ErrorDomain.STORAGE,
|
|
40
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
41
|
-
details: { id },
|
|
42
|
-
},
|
|
43
|
-
error,
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async saveScore(score: Omit<ScoreRowData, 'id' | 'createdAt' | 'updatedAt'>): Promise<{ score: ScoreRowData }> {
|
|
49
|
-
this.logger.debug('Saving score', { scorerId: score.scorerId, runId: score.runId });
|
|
50
|
-
|
|
51
|
-
const now = new Date();
|
|
52
|
-
const scoreId = `score-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
53
|
-
|
|
54
|
-
const scoreData = {
|
|
55
|
-
entity: 'score',
|
|
56
|
-
id: scoreId,
|
|
57
|
-
scorerId: score.scorerId,
|
|
58
|
-
traceId: score.traceId || '',
|
|
59
|
-
runId: score.runId,
|
|
60
|
-
scorer: typeof score.scorer === 'string' ? score.scorer : JSON.stringify(score.scorer),
|
|
61
|
-
preprocessStepResult:
|
|
62
|
-
typeof score.preprocessStepResult === 'string'
|
|
63
|
-
? score.preprocessStepResult
|
|
64
|
-
: JSON.stringify(score.preprocessStepResult),
|
|
65
|
-
analyzeStepResult:
|
|
66
|
-
typeof score.analyzeStepResult === 'string' ? score.analyzeStepResult : JSON.stringify(score.analyzeStepResult),
|
|
67
|
-
score: score.score,
|
|
68
|
-
reason: score.reason,
|
|
69
|
-
preprocessPrompt: score.preprocessPrompt,
|
|
70
|
-
generateScorePrompt: score.generateScorePrompt,
|
|
71
|
-
analyzePrompt: score.analyzePrompt,
|
|
72
|
-
reasonPrompt: score.reasonPrompt,
|
|
73
|
-
input: typeof score.input === 'string' ? score.input : JSON.stringify(score.input),
|
|
74
|
-
output: typeof score.output === 'string' ? score.output : JSON.stringify(score.output),
|
|
75
|
-
additionalContext:
|
|
76
|
-
typeof score.additionalContext === 'string' ? score.additionalContext : JSON.stringify(score.additionalContext),
|
|
77
|
-
runtimeContext:
|
|
78
|
-
typeof score.runtimeContext === 'string' ? score.runtimeContext : JSON.stringify(score.runtimeContext),
|
|
79
|
-
entityType: score.entityType,
|
|
80
|
-
entityData: typeof score.entity === 'string' ? score.entity : JSON.stringify(score.entity),
|
|
81
|
-
entityId: score.entityId,
|
|
82
|
-
source: score.source,
|
|
83
|
-
resourceId: score.resourceId || '',
|
|
84
|
-
threadId: score.threadId || '',
|
|
85
|
-
createdAt: now.toISOString(),
|
|
86
|
-
updatedAt: now.toISOString(),
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
await this.service.entities.score.upsert(scoreData).go();
|
|
91
|
-
|
|
92
|
-
const savedScore: ScoreRowData = {
|
|
93
|
-
...score,
|
|
94
|
-
id: scoreId,
|
|
95
|
-
createdAt: now,
|
|
96
|
-
updatedAt: now,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
return { score: savedScore };
|
|
100
|
-
} catch (error) {
|
|
101
|
-
throw new MastraError(
|
|
102
|
-
{
|
|
103
|
-
id: 'STORAGE_DYNAMODB_STORE_SAVE_SCORE_FAILED',
|
|
104
|
-
domain: ErrorDomain.STORAGE,
|
|
105
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
106
|
-
details: { scorerId: score.scorerId, runId: score.runId },
|
|
107
|
-
},
|
|
108
|
-
error,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async getScoresByScorerId({
|
|
114
|
-
scorerId,
|
|
115
|
-
pagination,
|
|
116
|
-
entityId,
|
|
117
|
-
entityType,
|
|
118
|
-
source,
|
|
119
|
-
}: {
|
|
120
|
-
scorerId: string;
|
|
121
|
-
pagination: StoragePagination;
|
|
122
|
-
entityId?: string;
|
|
123
|
-
entityType?: string;
|
|
124
|
-
source?: ScoringSource;
|
|
125
|
-
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
126
|
-
try {
|
|
127
|
-
// Query scores by scorer ID using the GSI
|
|
128
|
-
const query = this.service.entities.score.query.byScorer({ entity: 'score', scorerId });
|
|
129
|
-
|
|
130
|
-
// Get all scores for this scorer ID (DynamoDB doesn't support OFFSET/LIMIT)
|
|
131
|
-
const results = await query.go();
|
|
132
|
-
let allScores = results.data.map((data: any) => this.parseScoreData(data));
|
|
133
|
-
|
|
134
|
-
// Apply additional filters if provided
|
|
135
|
-
if (entityId) {
|
|
136
|
-
allScores = allScores.filter((score: ScoreRowData) => score.entityId === entityId);
|
|
137
|
-
}
|
|
138
|
-
if (entityType) {
|
|
139
|
-
allScores = allScores.filter((score: ScoreRowData) => score.entityType === entityType);
|
|
140
|
-
}
|
|
141
|
-
if (source) {
|
|
142
|
-
allScores = allScores.filter((score: ScoreRowData) => score.source === source);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Sort by createdAt DESC (newest first)
|
|
146
|
-
allScores.sort((a: ScoreRowData, b: ScoreRowData) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
147
|
-
|
|
148
|
-
// Apply pagination in memory
|
|
149
|
-
const startIndex = pagination.page * pagination.perPage;
|
|
150
|
-
const endIndex = startIndex + pagination.perPage;
|
|
151
|
-
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
152
|
-
|
|
153
|
-
// Calculate pagination info
|
|
154
|
-
const total = allScores.length;
|
|
155
|
-
const hasMore = endIndex < total;
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
scores: paginatedScores,
|
|
159
|
-
pagination: {
|
|
160
|
-
total,
|
|
161
|
-
page: pagination.page,
|
|
162
|
-
perPage: pagination.perPage,
|
|
163
|
-
hasMore,
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
} catch (error) {
|
|
167
|
-
throw new MastraError(
|
|
168
|
-
{
|
|
169
|
-
id: 'STORAGE_DYNAMODB_STORE_GET_SCORES_BY_SCORER_ID_FAILED',
|
|
170
|
-
domain: ErrorDomain.STORAGE,
|
|
171
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
172
|
-
details: {
|
|
173
|
-
scorerId: scorerId || '',
|
|
174
|
-
entityId: entityId || '',
|
|
175
|
-
entityType: entityType || '',
|
|
176
|
-
source: source || '',
|
|
177
|
-
page: pagination.page,
|
|
178
|
-
perPage: pagination.perPage,
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
error,
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async getScoresByRunId({
|
|
187
|
-
runId,
|
|
188
|
-
pagination,
|
|
189
|
-
}: {
|
|
190
|
-
runId: string;
|
|
191
|
-
pagination: StoragePagination;
|
|
192
|
-
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
193
|
-
this.logger.debug('Getting scores by run ID', { runId, pagination });
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
// Query scores by run ID using the GSI
|
|
197
|
-
const query = this.service.entities.score.query.byRun({ entity: 'score', runId });
|
|
198
|
-
|
|
199
|
-
// Get all scores for this run ID
|
|
200
|
-
const results = await query.go();
|
|
201
|
-
const allScores = results.data.map((data: any) => this.parseScoreData(data));
|
|
202
|
-
|
|
203
|
-
// Sort by createdAt DESC (newest first)
|
|
204
|
-
allScores.sort((a: ScoreRowData, b: ScoreRowData) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
205
|
-
|
|
206
|
-
// Apply pagination in memory
|
|
207
|
-
const startIndex = pagination.page * pagination.perPage;
|
|
208
|
-
const endIndex = startIndex + pagination.perPage;
|
|
209
|
-
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
210
|
-
|
|
211
|
-
// Calculate pagination info
|
|
212
|
-
const total = allScores.length;
|
|
213
|
-
const hasMore = endIndex < total;
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
scores: paginatedScores,
|
|
217
|
-
pagination: {
|
|
218
|
-
total,
|
|
219
|
-
page: pagination.page,
|
|
220
|
-
perPage: pagination.perPage,
|
|
221
|
-
hasMore,
|
|
222
|
-
},
|
|
223
|
-
};
|
|
224
|
-
} catch (error) {
|
|
225
|
-
throw new MastraError(
|
|
226
|
-
{
|
|
227
|
-
id: 'STORAGE_DYNAMODB_STORE_GET_SCORES_BY_RUN_ID_FAILED',
|
|
228
|
-
domain: ErrorDomain.STORAGE,
|
|
229
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
230
|
-
details: { runId, page: pagination.page, perPage: pagination.perPage },
|
|
231
|
-
},
|
|
232
|
-
error,
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async getScoresByEntityId({
|
|
238
|
-
entityId,
|
|
239
|
-
entityType,
|
|
240
|
-
pagination,
|
|
241
|
-
}: {
|
|
242
|
-
entityId: string;
|
|
243
|
-
entityType: string;
|
|
244
|
-
pagination: StoragePagination;
|
|
245
|
-
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
246
|
-
this.logger.debug('Getting scores by entity ID', { entityId, entityType, pagination });
|
|
247
|
-
|
|
248
|
-
try {
|
|
249
|
-
// Use the byEntityData index which uses entityId as the primary key
|
|
250
|
-
const query = this.service.entities.score.query.byEntityData({ entity: 'score', entityId });
|
|
251
|
-
|
|
252
|
-
// Get all scores for this entity ID
|
|
253
|
-
const results = await query.go();
|
|
254
|
-
let allScores = results.data.map((data: any) => this.parseScoreData(data));
|
|
255
|
-
|
|
256
|
-
// Filter by entityType since the index only uses entityId
|
|
257
|
-
allScores = allScores.filter((score: ScoreRowData) => score.entityType === entityType);
|
|
258
|
-
|
|
259
|
-
// Sort by createdAt DESC (newest first)
|
|
260
|
-
allScores.sort((a: ScoreRowData, b: ScoreRowData) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
261
|
-
|
|
262
|
-
// Apply pagination in memory
|
|
263
|
-
const startIndex = pagination.page * pagination.perPage;
|
|
264
|
-
const endIndex = startIndex + pagination.perPage;
|
|
265
|
-
const paginatedScores = allScores.slice(startIndex, endIndex);
|
|
266
|
-
|
|
267
|
-
// Calculate pagination info
|
|
268
|
-
const total = allScores.length;
|
|
269
|
-
const hasMore = endIndex < total;
|
|
270
|
-
|
|
271
|
-
return {
|
|
272
|
-
scores: paginatedScores,
|
|
273
|
-
pagination: {
|
|
274
|
-
total,
|
|
275
|
-
page: pagination.page,
|
|
276
|
-
perPage: pagination.perPage,
|
|
277
|
-
hasMore,
|
|
278
|
-
},
|
|
279
|
-
};
|
|
280
|
-
} catch (error) {
|
|
281
|
-
throw new MastraError(
|
|
282
|
-
{
|
|
283
|
-
id: 'STORAGE_DYNAMODB_STORE_GET_SCORES_BY_ENTITY_ID_FAILED',
|
|
284
|
-
domain: ErrorDomain.STORAGE,
|
|
285
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
286
|
-
details: { entityId, entityType, page: pagination.page, perPage: pagination.perPage },
|
|
287
|
-
},
|
|
288
|
-
error,
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
2
|
-
import { TABLE_TRACES, TracesStorage } from '@mastra/core/storage';
|
|
3
|
-
import type { PaginationInfo, StorageGetTracesPaginatedArg } from '@mastra/core/storage';
|
|
4
|
-
import type { Trace } from '@mastra/core/telemetry';
|
|
5
|
-
import type { Service } from 'electrodb';
|
|
6
|
-
import type { StoreOperationsDynamoDB } from '../operations';
|
|
7
|
-
|
|
8
|
-
export class TracesStorageDynamoDB extends TracesStorage {
|
|
9
|
-
private service: Service<Record<string, any>>;
|
|
10
|
-
private operations: StoreOperationsDynamoDB;
|
|
11
|
-
constructor({ service, operations }: { service: Service<Record<string, any>>; operations: StoreOperationsDynamoDB }) {
|
|
12
|
-
super();
|
|
13
|
-
|
|
14
|
-
this.service = service;
|
|
15
|
-
this.operations = operations;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Trace operations
|
|
19
|
-
async getTraces(args: {
|
|
20
|
-
name?: string;
|
|
21
|
-
scope?: string;
|
|
22
|
-
page: number;
|
|
23
|
-
perPage: number;
|
|
24
|
-
attributes?: Record<string, string>;
|
|
25
|
-
filters?: Record<string, any>;
|
|
26
|
-
}): Promise<any[]> {
|
|
27
|
-
const { name, scope, page, perPage } = args;
|
|
28
|
-
this.logger.debug('Getting traces', { name, scope, page, perPage });
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
let query;
|
|
32
|
-
|
|
33
|
-
// Determine which index to use based on the provided filters
|
|
34
|
-
// Provide *all* composite key components for the relevant index
|
|
35
|
-
if (name) {
|
|
36
|
-
query = this.service.entities.trace.query.byName({ entity: 'trace', name });
|
|
37
|
-
} else if (scope) {
|
|
38
|
-
query = this.service.entities.trace.query.byScope({ entity: 'trace', scope });
|
|
39
|
-
} else {
|
|
40
|
-
this.logger.warn('Performing a scan operation on traces - consider using a more specific query');
|
|
41
|
-
query = this.service.entities.trace.scan;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let items: any[] = [];
|
|
45
|
-
let cursor = null;
|
|
46
|
-
let pagesFetched = 0;
|
|
47
|
-
const startPage = page > 0 ? page : 1;
|
|
48
|
-
|
|
49
|
-
do {
|
|
50
|
-
const results: { data: any[]; cursor: string | null } = await query.go({ cursor, limit: perPage });
|
|
51
|
-
pagesFetched++;
|
|
52
|
-
if (pagesFetched === startPage) {
|
|
53
|
-
items = results.data;
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
cursor = results.cursor;
|
|
57
|
-
if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
} while (cursor && pagesFetched < startPage);
|
|
61
|
-
|
|
62
|
-
return items;
|
|
63
|
-
} catch (error) {
|
|
64
|
-
throw new MastraError(
|
|
65
|
-
{
|
|
66
|
-
id: 'STORAGE_DYNAMODB_STORE_GET_TRACES_FAILED',
|
|
67
|
-
domain: ErrorDomain.STORAGE,
|
|
68
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
69
|
-
},
|
|
70
|
-
error,
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async batchTraceInsert({ records }: { records: Record<string, any>[] }): Promise<void> {
|
|
76
|
-
this.logger.debug('Batch inserting traces', { count: records.length });
|
|
77
|
-
|
|
78
|
-
if (!records.length) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
// Add 'entity' type to each record before passing to generic batchInsert
|
|
84
|
-
const recordsToSave = records.map(rec => ({ entity: 'trace', ...rec }));
|
|
85
|
-
await this.operations.batchInsert({
|
|
86
|
-
tableName: TABLE_TRACES,
|
|
87
|
-
records: recordsToSave, // Pass records with 'entity' included
|
|
88
|
-
});
|
|
89
|
-
} catch (error) {
|
|
90
|
-
throw new MastraError(
|
|
91
|
-
{
|
|
92
|
-
id: 'STORAGE_DYNAMODB_STORE_BATCH_TRACE_INSERT_FAILED',
|
|
93
|
-
domain: ErrorDomain.STORAGE,
|
|
94
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
95
|
-
details: { count: records.length },
|
|
96
|
-
},
|
|
97
|
-
error,
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async getTracesPaginated(args: StorageGetTracesPaginatedArg): Promise<PaginationInfo & { traces: Trace[] }> {
|
|
103
|
-
const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
|
|
104
|
-
this.logger.debug('Getting traces with pagination', { name, scope, page, perPage, attributes, filters, dateRange });
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
let query;
|
|
108
|
-
|
|
109
|
-
// Determine which index to use based on the provided filters
|
|
110
|
-
if (name) {
|
|
111
|
-
query = this.service.entities.trace.query.byName({ entity: 'trace', name });
|
|
112
|
-
} else if (scope) {
|
|
113
|
-
query = this.service.entities.trace.query.byScope({ entity: 'trace', scope });
|
|
114
|
-
} else {
|
|
115
|
-
this.logger.warn('Performing a scan operation on traces - consider using a more specific query');
|
|
116
|
-
query = this.service.entities.trace.scan;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// For DynamoDB, we need to fetch all data and apply pagination in memory
|
|
120
|
-
// since DynamoDB doesn't support traditional offset-based pagination
|
|
121
|
-
const results = await query.go({
|
|
122
|
-
order: 'desc',
|
|
123
|
-
pages: 'all', // Get all pages to apply filtering and pagination
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
if (!results.data.length) {
|
|
127
|
-
return {
|
|
128
|
-
traces: [],
|
|
129
|
-
total: 0,
|
|
130
|
-
page,
|
|
131
|
-
perPage,
|
|
132
|
-
hasMore: false,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Apply filters in memory
|
|
137
|
-
let filteredData = results.data;
|
|
138
|
-
|
|
139
|
-
// Filter by attributes if provided
|
|
140
|
-
if (attributes) {
|
|
141
|
-
filteredData = filteredData.filter((item: Record<string, any>) => {
|
|
142
|
-
try {
|
|
143
|
-
// Handle the case where attributes might be stored as "[object Object]" or JSON string
|
|
144
|
-
let itemAttributes: Record<string, any> = {};
|
|
145
|
-
|
|
146
|
-
if (item.attributes) {
|
|
147
|
-
if (typeof item.attributes === 'string') {
|
|
148
|
-
if (item.attributes === '[object Object]') {
|
|
149
|
-
// This means the object was stringified incorrectly
|
|
150
|
-
itemAttributes = {};
|
|
151
|
-
} else {
|
|
152
|
-
try {
|
|
153
|
-
itemAttributes = JSON.parse(item.attributes);
|
|
154
|
-
} catch {
|
|
155
|
-
itemAttributes = {};
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
} else if (typeof item.attributes === 'object') {
|
|
159
|
-
itemAttributes = item.attributes;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return Object.entries(attributes).every(([key, value]) => itemAttributes[key] === value);
|
|
164
|
-
} catch (e) {
|
|
165
|
-
this.logger.warn('Failed to parse attributes during filtering', { item, error: e });
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Filter by date range if provided
|
|
172
|
-
if (dateRange?.start) {
|
|
173
|
-
filteredData = filteredData.filter((item: Record<string, any>) => {
|
|
174
|
-
const itemDate = new Date(item.createdAt);
|
|
175
|
-
return itemDate >= dateRange.start!;
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (dateRange?.end) {
|
|
180
|
-
filteredData = filteredData.filter((item: Record<string, any>) => {
|
|
181
|
-
const itemDate = new Date(item.createdAt);
|
|
182
|
-
return itemDate <= dateRange.end!;
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Apply pagination
|
|
187
|
-
const total = filteredData.length;
|
|
188
|
-
const start = page * perPage;
|
|
189
|
-
const end = start + perPage;
|
|
190
|
-
const paginatedData = filteredData.slice(start, end);
|
|
191
|
-
|
|
192
|
-
const traces = paginatedData.map((item: any) => {
|
|
193
|
-
// Handle the case where attributes might be stored as "[object Object]" or JSON string
|
|
194
|
-
let attributes: Record<string, any> | undefined;
|
|
195
|
-
if (item.attributes) {
|
|
196
|
-
if (typeof item.attributes === 'string') {
|
|
197
|
-
if (item.attributes === '[object Object]') {
|
|
198
|
-
attributes = undefined;
|
|
199
|
-
} else {
|
|
200
|
-
try {
|
|
201
|
-
attributes = JSON.parse(item.attributes);
|
|
202
|
-
} catch {
|
|
203
|
-
attributes = undefined;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
} else if (typeof item.attributes === 'object') {
|
|
207
|
-
attributes = item.attributes;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
let status: Record<string, any> | undefined;
|
|
212
|
-
if (item.status) {
|
|
213
|
-
if (typeof item.status === 'string') {
|
|
214
|
-
try {
|
|
215
|
-
status = JSON.parse(item.status);
|
|
216
|
-
} catch {
|
|
217
|
-
status = undefined;
|
|
218
|
-
}
|
|
219
|
-
} else if (typeof item.status === 'object') {
|
|
220
|
-
status = item.status;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
let events: any[] | undefined;
|
|
225
|
-
if (item.events) {
|
|
226
|
-
if (typeof item.events === 'string') {
|
|
227
|
-
try {
|
|
228
|
-
events = JSON.parse(item.events);
|
|
229
|
-
} catch {
|
|
230
|
-
events = undefined;
|
|
231
|
-
}
|
|
232
|
-
} else if (Array.isArray(item.events)) {
|
|
233
|
-
events = item.events;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
let links: any[] | undefined;
|
|
238
|
-
if (item.links) {
|
|
239
|
-
if (typeof item.links === 'string') {
|
|
240
|
-
try {
|
|
241
|
-
links = JSON.parse(item.links);
|
|
242
|
-
} catch {
|
|
243
|
-
links = undefined;
|
|
244
|
-
}
|
|
245
|
-
} else if (Array.isArray(item.links)) {
|
|
246
|
-
links = item.links;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
id: item.id,
|
|
252
|
-
parentSpanId: item.parentSpanId,
|
|
253
|
-
name: item.name,
|
|
254
|
-
traceId: item.traceId,
|
|
255
|
-
scope: item.scope,
|
|
256
|
-
kind: item.kind,
|
|
257
|
-
attributes,
|
|
258
|
-
status,
|
|
259
|
-
events,
|
|
260
|
-
links,
|
|
261
|
-
other: item.other,
|
|
262
|
-
startTime: item.startTime,
|
|
263
|
-
endTime: item.endTime,
|
|
264
|
-
createdAt: item.createdAt,
|
|
265
|
-
};
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
return {
|
|
269
|
-
traces,
|
|
270
|
-
total,
|
|
271
|
-
page,
|
|
272
|
-
perPage,
|
|
273
|
-
hasMore: end < total,
|
|
274
|
-
};
|
|
275
|
-
} catch (error) {
|
|
276
|
-
throw new MastraError(
|
|
277
|
-
{
|
|
278
|
-
id: 'STORAGE_DYNAMODB_STORE_GET_TRACES_PAGINATED_FAILED',
|
|
279
|
-
domain: ErrorDomain.STORAGE,
|
|
280
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
281
|
-
},
|
|
282
|
-
error,
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|