@mastra/pg 0.10.1 → 0.10.2-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +16 -0
- package/dist/_tsup-dts-rollup.d.cts +68 -5
- package/dist/_tsup-dts-rollup.d.ts +68 -5
- package/dist/index.cjs +307 -144
- package/dist/index.js +307 -144
- package/package.json +11 -10
- package/src/storage/index.test.ts +472 -52
- package/src/storage/index.ts +459 -179
package/src/storage/index.ts
CHANGED
|
@@ -91,6 +91,7 @@ export class PostgresStore extends MastraStorage {
|
|
|
91
91
|
return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/** @deprecated use getEvals instead */
|
|
94
95
|
async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
|
|
95
96
|
try {
|
|
96
97
|
const baseQuery = `SELECT * FROM ${this.getTableName(TABLE_EVALS)} WHERE agent_name = $1`;
|
|
@@ -153,115 +154,127 @@ export class PostgresStore extends MastraStorage {
|
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
async getTraces({
|
|
157
|
-
name
|
|
158
|
-
scope
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
fromDate
|
|
164
|
-
toDate
|
|
165
|
-
}:
|
|
157
|
+
public async getTraces(args: {
|
|
158
|
+
name?: string;
|
|
159
|
+
scope?: string;
|
|
160
|
+
attributes?: Record<string, string>;
|
|
161
|
+
filters?: Record<string, any>;
|
|
162
|
+
page: number;
|
|
163
|
+
perPage?: number;
|
|
164
|
+
fromDate?: Date;
|
|
165
|
+
toDate?: Date;
|
|
166
|
+
}): Promise<any[]>;
|
|
167
|
+
public async getTraces(args: {
|
|
166
168
|
name?: string;
|
|
167
169
|
scope?: string;
|
|
168
170
|
page: number;
|
|
171
|
+
perPage?: number;
|
|
172
|
+
attributes?: Record<string, string>;
|
|
173
|
+
filters?: Record<string, any>;
|
|
174
|
+
fromDate?: Date;
|
|
175
|
+
toDate?: Date;
|
|
176
|
+
returnPaginationResults: true;
|
|
177
|
+
}): Promise<{
|
|
178
|
+
traces: any[];
|
|
179
|
+
total: number;
|
|
180
|
+
page: number;
|
|
169
181
|
perPage: number;
|
|
182
|
+
hasMore: boolean;
|
|
183
|
+
}>;
|
|
184
|
+
public async getTraces(args: {
|
|
185
|
+
name?: string;
|
|
186
|
+
scope?: string;
|
|
187
|
+
page: number;
|
|
188
|
+
perPage?: number;
|
|
170
189
|
attributes?: Record<string, string>;
|
|
171
190
|
filters?: Record<string, any>;
|
|
172
191
|
fromDate?: Date;
|
|
173
192
|
toDate?: Date;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
193
|
+
returnPaginationResults?: boolean;
|
|
194
|
+
}): Promise<
|
|
195
|
+
| any[]
|
|
196
|
+
| {
|
|
197
|
+
traces: any[];
|
|
198
|
+
total: number;
|
|
199
|
+
page: number;
|
|
200
|
+
perPage: number;
|
|
201
|
+
hasMore: boolean;
|
|
202
|
+
}
|
|
203
|
+
> {
|
|
204
|
+
const {
|
|
205
|
+
name,
|
|
206
|
+
scope,
|
|
207
|
+
page,
|
|
208
|
+
perPage: perPageInput,
|
|
209
|
+
attributes,
|
|
210
|
+
filters,
|
|
211
|
+
fromDate,
|
|
212
|
+
toDate,
|
|
213
|
+
returnPaginationResults,
|
|
214
|
+
} = args;
|
|
215
|
+
|
|
216
|
+
const perPage = perPageInput !== undefined ? perPageInput : 100; // Default perPage
|
|
217
|
+
const currentOffset = page * perPage;
|
|
218
|
+
|
|
219
|
+
const queryParams: any[] = [];
|
|
181
220
|
const conditions: string[] = [];
|
|
221
|
+
let paramIndex = 1;
|
|
222
|
+
|
|
182
223
|
if (name) {
|
|
183
|
-
conditions.push(`name LIKE
|
|
224
|
+
conditions.push(`name LIKE $${paramIndex++}`);
|
|
225
|
+
queryParams.push(`${name}%`); // Add wildcard for LIKE
|
|
184
226
|
}
|
|
185
227
|
if (scope) {
|
|
186
|
-
conditions.push(`scope =
|
|
228
|
+
conditions.push(`scope = $${paramIndex++}`);
|
|
229
|
+
queryParams.push(scope);
|
|
187
230
|
}
|
|
188
231
|
if (attributes) {
|
|
189
|
-
Object.
|
|
232
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
190
233
|
const parsedKey = parseSqlIdentifier(key, 'attribute key');
|
|
191
|
-
conditions.push(`attributes->>'${parsedKey}' =
|
|
234
|
+
conditions.push(`attributes->>'${parsedKey}' = $${paramIndex++}`);
|
|
235
|
+
queryParams.push(value);
|
|
192
236
|
});
|
|
193
237
|
}
|
|
194
|
-
|
|
195
238
|
if (filters) {
|
|
196
|
-
Object.entries(filters).forEach(([key]) => {
|
|
239
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
197
240
|
const parsedKey = parseSqlIdentifier(key, 'filter key');
|
|
198
|
-
conditions.push(
|
|
241
|
+
conditions.push(`"${parsedKey}" = $${paramIndex++}`); // Ensure filter keys are quoted if they are column names
|
|
242
|
+
queryParams.push(value);
|
|
199
243
|
});
|
|
200
244
|
}
|
|
201
|
-
|
|
202
245
|
if (fromDate) {
|
|
203
|
-
conditions.push(`createdAt >=
|
|
246
|
+
conditions.push(`"createdAt" >= $${paramIndex++}`);
|
|
247
|
+
queryParams.push(fromDate);
|
|
204
248
|
}
|
|
205
|
-
|
|
206
249
|
if (toDate) {
|
|
207
|
-
conditions.push(`createdAt <=
|
|
250
|
+
conditions.push(`"createdAt" <= $${paramIndex++}`);
|
|
251
|
+
queryParams.push(toDate);
|
|
208
252
|
}
|
|
209
253
|
|
|
210
254
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
211
255
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (scope) {
|
|
217
|
-
args.push(scope);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (attributes) {
|
|
221
|
-
for (const [_key, value] of Object.entries(attributes)) {
|
|
222
|
-
args.push(value);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (filters) {
|
|
227
|
-
for (const [, value] of Object.entries(filters)) {
|
|
228
|
-
args.push(value);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (fromDate) {
|
|
233
|
-
args.push(fromDate.toISOString());
|
|
234
|
-
}
|
|
256
|
+
// Get total count
|
|
257
|
+
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_TRACES)} ${whereClause}`;
|
|
258
|
+
const countResult = await this.db.one(countQuery, queryParams);
|
|
259
|
+
const total = parseInt(countResult.count, 10);
|
|
235
260
|
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
scope: string;
|
|
246
|
-
kind: string;
|
|
247
|
-
events: any[];
|
|
248
|
-
links: any[];
|
|
249
|
-
status: any;
|
|
250
|
-
attributes: Record<string, any>;
|
|
251
|
-
startTime: string;
|
|
252
|
-
endTime: string;
|
|
253
|
-
other: any;
|
|
254
|
-
createdAt: string;
|
|
255
|
-
}>(
|
|
256
|
-
`SELECT * FROM ${this.getTableName(TABLE_TRACES)} ${whereClause} ORDER BY "createdAt" DESC LIMIT ${limit} OFFSET ${offset}`,
|
|
257
|
-
args,
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
if (!result) {
|
|
261
|
+
if (total === 0 && returnPaginationResults) {
|
|
262
|
+
return {
|
|
263
|
+
traces: [],
|
|
264
|
+
total: 0,
|
|
265
|
+
page,
|
|
266
|
+
perPage,
|
|
267
|
+
hasMore: false,
|
|
268
|
+
};
|
|
269
|
+
} else if (total === 0) {
|
|
261
270
|
return [];
|
|
262
271
|
}
|
|
263
272
|
|
|
264
|
-
|
|
273
|
+
const dataQuery = `SELECT * FROM ${this.getTableName(TABLE_TRACES)} ${whereClause} ORDER BY "createdAt" DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
274
|
+
const finalQueryParams = [...queryParams, perPage, currentOffset];
|
|
275
|
+
|
|
276
|
+
const rows = await this.db.manyOrNone<any>(dataQuery, finalQueryParams);
|
|
277
|
+
const traces = rows.map(row => ({
|
|
265
278
|
id: row.id,
|
|
266
279
|
parentSpanId: row.parentSpanId,
|
|
267
280
|
traceId: row.traceId,
|
|
@@ -276,7 +289,19 @@ export class PostgresStore extends MastraStorage {
|
|
|
276
289
|
endTime: row.endTime,
|
|
277
290
|
other: row.other,
|
|
278
291
|
createdAt: row.createdAt,
|
|
279
|
-
}))
|
|
292
|
+
}));
|
|
293
|
+
|
|
294
|
+
if (returnPaginationResults) {
|
|
295
|
+
return {
|
|
296
|
+
traces,
|
|
297
|
+
total,
|
|
298
|
+
page,
|
|
299
|
+
perPage,
|
|
300
|
+
hasMore: currentOffset + traces.length < total,
|
|
301
|
+
};
|
|
302
|
+
} else {
|
|
303
|
+
return traces;
|
|
304
|
+
}
|
|
280
305
|
}
|
|
281
306
|
|
|
282
307
|
private async setupSchema() {
|
|
@@ -466,30 +491,82 @@ export class PostgresStore extends MastraStorage {
|
|
|
466
491
|
}
|
|
467
492
|
}
|
|
468
493
|
|
|
469
|
-
async getThreadsByResourceId(
|
|
494
|
+
public async getThreadsByResourceId(args: { resourceId: string }): Promise<StorageThreadType[]>;
|
|
495
|
+
public async getThreadsByResourceId(args: { resourceId: string; page: number; perPage?: number }): Promise<{
|
|
496
|
+
threads: StorageThreadType[];
|
|
497
|
+
total: number;
|
|
498
|
+
page: number;
|
|
499
|
+
perPage: number;
|
|
500
|
+
hasMore: boolean;
|
|
501
|
+
}>;
|
|
502
|
+
public async getThreadsByResourceId(args: { resourceId: string; page?: number; perPage?: number }): Promise<
|
|
503
|
+
| StorageThreadType[]
|
|
504
|
+
| {
|
|
505
|
+
threads: StorageThreadType[];
|
|
506
|
+
total: number;
|
|
507
|
+
page: number;
|
|
508
|
+
perPage: number;
|
|
509
|
+
hasMore: boolean;
|
|
510
|
+
}
|
|
511
|
+
> {
|
|
512
|
+
const { resourceId, page, perPage: perPageInput } = args;
|
|
513
|
+
|
|
470
514
|
try {
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
515
|
+
const baseQuery = `FROM ${this.getTableName(TABLE_THREADS)} WHERE "resourceId" = $1`;
|
|
516
|
+
const queryParams: any[] = [resourceId];
|
|
517
|
+
|
|
518
|
+
if (page !== undefined) {
|
|
519
|
+
const perPage = perPageInput !== undefined ? perPageInput : 100;
|
|
520
|
+
const currentOffset = page * perPage;
|
|
521
|
+
|
|
522
|
+
const countQuery = `SELECT COUNT(*) ${baseQuery}`;
|
|
523
|
+
const countResult = await this.db.one(countQuery, queryParams);
|
|
524
|
+
const total = parseInt(countResult.count, 10);
|
|
525
|
+
|
|
526
|
+
if (total === 0) {
|
|
527
|
+
return {
|
|
528
|
+
threads: [],
|
|
529
|
+
total: 0,
|
|
530
|
+
page,
|
|
531
|
+
perPage,
|
|
532
|
+
hasMore: false,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
483
535
|
|
|
484
|
-
|
|
485
|
-
...
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
536
|
+
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC LIMIT $2 OFFSET $3`;
|
|
537
|
+
const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
|
|
538
|
+
|
|
539
|
+
const threads = (rows || []).map(thread => ({
|
|
540
|
+
...thread,
|
|
541
|
+
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
542
|
+
createdAt: thread.createdAt, // Assuming already Date objects or ISO strings
|
|
543
|
+
updatedAt: thread.updatedAt,
|
|
544
|
+
}));
|
|
545
|
+
|
|
546
|
+
return {
|
|
547
|
+
threads,
|
|
548
|
+
total,
|
|
549
|
+
page,
|
|
550
|
+
perPage,
|
|
551
|
+
hasMore: currentOffset + threads.length < total,
|
|
552
|
+
};
|
|
553
|
+
} else {
|
|
554
|
+
// Non-paginated path
|
|
555
|
+
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC`;
|
|
556
|
+
const rows = await this.db.manyOrNone(dataQuery, queryParams);
|
|
557
|
+
return (rows || []).map(thread => ({
|
|
558
|
+
...thread,
|
|
559
|
+
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
560
|
+
createdAt: thread.createdAt,
|
|
561
|
+
updatedAt: thread.updatedAt,
|
|
562
|
+
}));
|
|
563
|
+
}
|
|
490
564
|
} catch (error) {
|
|
491
|
-
|
|
492
|
-
|
|
565
|
+
this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
|
|
566
|
+
if (page !== undefined) {
|
|
567
|
+
return { threads: [], total: 0, page, perPage: perPageInput || 100, hasMore: false };
|
|
568
|
+
}
|
|
569
|
+
return [];
|
|
493
570
|
}
|
|
494
571
|
}
|
|
495
572
|
|
|
@@ -586,95 +663,203 @@ export class PostgresStore extends MastraStorage {
|
|
|
586
663
|
}
|
|
587
664
|
}
|
|
588
665
|
|
|
589
|
-
async getMessages
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
)
|
|
624
|
-
)
|
|
625
|
-
ORDER BY m."createdAt" DESC
|
|
626
|
-
`,
|
|
627
|
-
[
|
|
628
|
-
threadId,
|
|
629
|
-
include.map(i => i.id),
|
|
630
|
-
Math.max(...include.map(i => i.withPreviousMessages || 0)),
|
|
631
|
-
Math.max(...include.map(i => i.withNextMessages || 0)),
|
|
632
|
-
],
|
|
633
|
-
);
|
|
634
|
-
|
|
635
|
-
messages.push(...includeResult);
|
|
666
|
+
public async getMessages(args: StorageGetMessagesArg & { format?: 'v1' }): Promise<MastraMessageV1[]>;
|
|
667
|
+
public async getMessages(args: StorageGetMessagesArg & { format: 'v2' }): Promise<MastraMessageV2[]>;
|
|
668
|
+
public async getMessages(
|
|
669
|
+
args: StorageGetMessagesArg & {
|
|
670
|
+
format?: 'v1' | 'v2';
|
|
671
|
+
page: number;
|
|
672
|
+
perPage?: number;
|
|
673
|
+
fromDate?: Date;
|
|
674
|
+
toDate?: Date;
|
|
675
|
+
},
|
|
676
|
+
): Promise<{
|
|
677
|
+
messages: MastraMessageV1[] | MastraMessageV2[];
|
|
678
|
+
total: number;
|
|
679
|
+
page: number;
|
|
680
|
+
perPage: number;
|
|
681
|
+
hasMore: boolean;
|
|
682
|
+
}>;
|
|
683
|
+
public async getMessages(
|
|
684
|
+
args: StorageGetMessagesArg & {
|
|
685
|
+
format?: 'v1' | 'v2';
|
|
686
|
+
page?: number;
|
|
687
|
+
perPage?: number;
|
|
688
|
+
fromDate?: Date;
|
|
689
|
+
toDate?: Date;
|
|
690
|
+
},
|
|
691
|
+
): Promise<
|
|
692
|
+
| MastraMessageV1[]
|
|
693
|
+
| MastraMessageV2[]
|
|
694
|
+
| {
|
|
695
|
+
messages: MastraMessageV1[] | MastraMessageV2[];
|
|
696
|
+
total: number;
|
|
697
|
+
page: number;
|
|
698
|
+
perPage: number;
|
|
699
|
+
hasMore: boolean;
|
|
636
700
|
}
|
|
701
|
+
> {
|
|
702
|
+
const { threadId, format, page, perPage: perPageInput, fromDate, toDate, selectBy } = args;
|
|
637
703
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
`
|
|
641
|
-
SELECT
|
|
642
|
-
id,
|
|
643
|
-
content,
|
|
644
|
-
role,
|
|
645
|
-
type,
|
|
646
|
-
"createdAt",
|
|
647
|
-
thread_id AS "threadId"
|
|
648
|
-
FROM ${this.getTableName(TABLE_MESSAGES)}
|
|
649
|
-
WHERE thread_id = $1
|
|
650
|
-
AND id != ALL($2)
|
|
651
|
-
ORDER BY "createdAt" DESC
|
|
652
|
-
LIMIT $3
|
|
653
|
-
`,
|
|
654
|
-
[threadId, messages.map(m => m.id), limit],
|
|
655
|
-
);
|
|
704
|
+
const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId"`;
|
|
705
|
+
const orderByStatement = `ORDER BY "createdAt" DESC`;
|
|
656
706
|
|
|
657
|
-
|
|
707
|
+
try {
|
|
708
|
+
if (page !== undefined) {
|
|
709
|
+
const perPage = perPageInput !== undefined ? perPageInput : 40;
|
|
710
|
+
const currentOffset = page * perPage;
|
|
658
711
|
|
|
659
|
-
|
|
660
|
-
|
|
712
|
+
const conditions: string[] = [`thread_id = $1`];
|
|
713
|
+
const queryParams: any[] = [threadId];
|
|
714
|
+
let paramIndex = 2;
|
|
661
715
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
716
|
+
if (fromDate) {
|
|
717
|
+
conditions.push(`"createdAt" >= $${paramIndex++}`);
|
|
718
|
+
queryParams.push(fromDate);
|
|
719
|
+
}
|
|
720
|
+
if (toDate) {
|
|
721
|
+
conditions.push(`"createdAt" <= $${paramIndex++}`);
|
|
722
|
+
queryParams.push(toDate);
|
|
723
|
+
}
|
|
724
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
725
|
+
|
|
726
|
+
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_MESSAGES)} ${whereClause}`;
|
|
727
|
+
const countResult = await this.db.one(countQuery, queryParams);
|
|
728
|
+
const total = parseInt(countResult.count, 10);
|
|
729
|
+
|
|
730
|
+
if (total === 0) {
|
|
731
|
+
return {
|
|
732
|
+
messages: [],
|
|
733
|
+
total: 0,
|
|
734
|
+
page,
|
|
735
|
+
perPage,
|
|
736
|
+
hasMore: false,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const dataQuery = `${selectStatement} FROM ${this.getTableName(TABLE_MESSAGES)} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
741
|
+
const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
|
|
742
|
+
|
|
743
|
+
const fetchedMessages = (rows || []).map(message => {
|
|
744
|
+
if (typeof message.content === 'string') {
|
|
745
|
+
try {
|
|
746
|
+
message.content = JSON.parse(message.content);
|
|
747
|
+
} catch {
|
|
748
|
+
/* ignore */
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
if (message.type === 'v2') delete message.type;
|
|
752
|
+
return message as MastraMessageV1;
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
const messagesToReturn =
|
|
756
|
+
format === 'v2'
|
|
757
|
+
? fetchedMessages.map(
|
|
758
|
+
m =>
|
|
759
|
+
({
|
|
760
|
+
...m,
|
|
761
|
+
content: m.content || { format: 2, parts: [{ type: 'text', text: '' }] },
|
|
762
|
+
}) as MastraMessageV2,
|
|
763
|
+
)
|
|
764
|
+
: fetchedMessages;
|
|
765
|
+
|
|
766
|
+
return {
|
|
767
|
+
messages: messagesToReturn,
|
|
768
|
+
total,
|
|
769
|
+
page,
|
|
770
|
+
perPage,
|
|
771
|
+
hasMore: currentOffset + fetchedMessages.length < total,
|
|
772
|
+
};
|
|
773
|
+
} else {
|
|
774
|
+
// Non-paginated path: Handle selectBy.include or selectBy.last
|
|
775
|
+
let rows: any[] = [];
|
|
776
|
+
const include = selectBy?.include || [];
|
|
777
|
+
|
|
778
|
+
if (include.length) {
|
|
779
|
+
rows = await this.db.manyOrNone(
|
|
780
|
+
`
|
|
781
|
+
WITH ordered_messages AS (
|
|
782
|
+
SELECT
|
|
783
|
+
*,
|
|
784
|
+
ROW_NUMBER() OVER (${orderByStatement}) as row_num
|
|
785
|
+
FROM ${this.getTableName(TABLE_MESSAGES)}
|
|
786
|
+
WHERE thread_id = $1
|
|
787
|
+
)
|
|
788
|
+
SELECT
|
|
789
|
+
m.id,
|
|
790
|
+
m.content,
|
|
791
|
+
m.role,
|
|
792
|
+
m.type,
|
|
793
|
+
m."createdAt",
|
|
794
|
+
m.thread_id AS "threadId"
|
|
795
|
+
FROM ordered_messages m
|
|
796
|
+
WHERE m.id = ANY($2)
|
|
797
|
+
OR EXISTS (
|
|
798
|
+
SELECT 1 FROM ordered_messages target
|
|
799
|
+
WHERE target.id = ANY($2)
|
|
800
|
+
AND (
|
|
801
|
+
-- Get previous messages based on the max withPreviousMessages
|
|
802
|
+
(m.row_num <= target.row_num + $3 AND m.row_num > target.row_num)
|
|
803
|
+
OR
|
|
804
|
+
-- Get next messages based on the max withNextMessages
|
|
805
|
+
(m.row_num >= target.row_num - $4 AND m.row_num < target.row_num)
|
|
806
|
+
)
|
|
807
|
+
)
|
|
808
|
+
ORDER BY m."createdAt" ASC
|
|
809
|
+
`, // Keep ASC for final sorting after fetching context
|
|
810
|
+
[
|
|
811
|
+
threadId,
|
|
812
|
+
include.map(i => i.id),
|
|
813
|
+
Math.max(0, ...include.map(i => i.withPreviousMessages || 0)), // Ensure non-negative
|
|
814
|
+
Math.max(0, ...include.map(i => i.withNextMessages || 0)), // Ensure non-negative
|
|
815
|
+
],
|
|
816
|
+
);
|
|
817
|
+
} else {
|
|
818
|
+
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
819
|
+
if (limit === 0 && selectBy?.last !== false) {
|
|
820
|
+
// if last is explicitly false, we fetch all
|
|
821
|
+
// Do nothing, rows will be empty, and we return empty array later.
|
|
822
|
+
} else {
|
|
823
|
+
let query = `${selectStatement} FROM ${this.getTableName(TABLE_MESSAGES)} WHERE thread_id = $1 ${orderByStatement}`;
|
|
824
|
+
const queryParams: any[] = [threadId];
|
|
825
|
+
if (limit !== undefined && selectBy?.last !== false) {
|
|
826
|
+
query += ` LIMIT $2`;
|
|
827
|
+
queryParams.push(limit);
|
|
828
|
+
}
|
|
829
|
+
rows = await this.db.manyOrNone(query, queryParams);
|
|
669
830
|
}
|
|
670
831
|
}
|
|
671
|
-
if (message.type === `v2`) delete message.type;
|
|
672
|
-
});
|
|
673
832
|
|
|
674
|
-
|
|
833
|
+
const fetchedMessages = (rows || []).map(message => {
|
|
834
|
+
if (typeof message.content === 'string') {
|
|
835
|
+
try {
|
|
836
|
+
message.content = JSON.parse(message.content);
|
|
837
|
+
} catch {
|
|
838
|
+
/* ignore */
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (message.type === 'v2') delete message.type;
|
|
842
|
+
return message as MastraMessageV1;
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
// Sort all messages by creation date
|
|
846
|
+
const sortedMessages = fetchedMessages.sort(
|
|
847
|
+
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),
|
|
848
|
+
);
|
|
849
|
+
|
|
850
|
+
return format === 'v2'
|
|
851
|
+
? sortedMessages.map(
|
|
852
|
+
m =>
|
|
853
|
+
({ ...m, content: m.content || { format: 2, parts: [{ type: 'text', text: '' }] } }) as MastraMessageV2,
|
|
854
|
+
)
|
|
855
|
+
: sortedMessages;
|
|
856
|
+
}
|
|
675
857
|
} catch (error) {
|
|
676
|
-
|
|
677
|
-
|
|
858
|
+
this.logger.error('Error getting messages:', error);
|
|
859
|
+
if (page !== undefined) {
|
|
860
|
+
return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
|
|
861
|
+
}
|
|
862
|
+
return [];
|
|
678
863
|
}
|
|
679
864
|
}
|
|
680
865
|
|
|
@@ -948,4 +1133,99 @@ export class PostgresStore extends MastraStorage {
|
|
|
948
1133
|
async close(): Promise<void> {
|
|
949
1134
|
this.pgp.end();
|
|
950
1135
|
}
|
|
1136
|
+
|
|
1137
|
+
async getEvals(options?: {
|
|
1138
|
+
agentName?: string;
|
|
1139
|
+
type?: 'test' | 'live';
|
|
1140
|
+
page?: number;
|
|
1141
|
+
perPage?: number;
|
|
1142
|
+
limit?: number;
|
|
1143
|
+
offset?: number;
|
|
1144
|
+
fromDate?: Date;
|
|
1145
|
+
toDate?: Date;
|
|
1146
|
+
}): Promise<{
|
|
1147
|
+
evals: EvalRow[];
|
|
1148
|
+
total: number;
|
|
1149
|
+
page?: number;
|
|
1150
|
+
perPage?: number;
|
|
1151
|
+
hasMore?: boolean;
|
|
1152
|
+
}> {
|
|
1153
|
+
const { agentName, type, page, perPage, limit, offset, fromDate, toDate } = options || {};
|
|
1154
|
+
|
|
1155
|
+
const conditions: string[] = [];
|
|
1156
|
+
const queryParams: any[] = [];
|
|
1157
|
+
let paramIndex = 1;
|
|
1158
|
+
|
|
1159
|
+
if (agentName) {
|
|
1160
|
+
conditions.push(`agent_name = $${paramIndex++}`);
|
|
1161
|
+
queryParams.push(agentName);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
if (type === 'test') {
|
|
1165
|
+
conditions.push(`(test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL)`);
|
|
1166
|
+
} else if (type === 'live') {
|
|
1167
|
+
conditions.push(`(test_info IS NULL OR test_info->>'testPath' IS NULL)`);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
if (fromDate) {
|
|
1171
|
+
conditions.push(`created_at >= $${paramIndex++}`);
|
|
1172
|
+
queryParams.push(fromDate);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
if (toDate) {
|
|
1176
|
+
conditions.push(`created_at <= $${paramIndex++}`);
|
|
1177
|
+
queryParams.push(toDate);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
1181
|
+
|
|
1182
|
+
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_EVALS)} ${whereClause}`;
|
|
1183
|
+
const countResult = await this.db.one(countQuery, queryParams);
|
|
1184
|
+
const total = parseInt(countResult.count, 10);
|
|
1185
|
+
|
|
1186
|
+
let currentLimit: number;
|
|
1187
|
+
let currentOffset: number;
|
|
1188
|
+
let currentPage: number | undefined = page;
|
|
1189
|
+
let currentPerPage: number | undefined = perPage;
|
|
1190
|
+
let hasMore = false;
|
|
1191
|
+
|
|
1192
|
+
if (limit !== undefined && offset !== undefined) {
|
|
1193
|
+
currentLimit = limit;
|
|
1194
|
+
currentOffset = offset;
|
|
1195
|
+
currentPage = undefined;
|
|
1196
|
+
currentPerPage = undefined;
|
|
1197
|
+
hasMore = currentOffset + currentLimit < total;
|
|
1198
|
+
} else if (page !== undefined && perPage !== undefined) {
|
|
1199
|
+
currentLimit = perPage;
|
|
1200
|
+
currentOffset = page * perPage;
|
|
1201
|
+
hasMore = currentOffset + currentLimit < total;
|
|
1202
|
+
} else {
|
|
1203
|
+
currentLimit = perPage || 100;
|
|
1204
|
+
currentOffset = (page || 0) * currentLimit;
|
|
1205
|
+
if (page === undefined) currentPage = 0;
|
|
1206
|
+
if (currentPerPage === undefined) currentPerPage = currentLimit;
|
|
1207
|
+
hasMore = currentOffset + currentLimit < total;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if (total === 0) {
|
|
1211
|
+
return {
|
|
1212
|
+
evals: [],
|
|
1213
|
+
total: 0,
|
|
1214
|
+
page: currentPage,
|
|
1215
|
+
perPage: currentPerPage,
|
|
1216
|
+
hasMore: false,
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
const dataQuery = `SELECT * FROM ${this.getTableName(TABLE_EVALS)} ${whereClause} ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
1221
|
+
const rows = await this.db.manyOrNone(dataQuery, [...queryParams, currentLimit, currentOffset]);
|
|
1222
|
+
|
|
1223
|
+
return {
|
|
1224
|
+
evals: rows?.map(row => this.transformEvalRow(row)) ?? [],
|
|
1225
|
+
total,
|
|
1226
|
+
page: currentPage,
|
|
1227
|
+
perPage: currentPerPage,
|
|
1228
|
+
hasMore,
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
951
1231
|
}
|