@mastra/pg 0.12.3 → 0.12.4-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 +7 -7
- package/CHANGELOG.md +26 -0
- package/dist/_tsup-dts-rollup.d.cts +346 -63
- package/dist/_tsup-dts-rollup.d.ts +346 -63
- package/dist/index.cjs +1610 -1117
- package/dist/index.js +1611 -1118
- package/package.json +3 -3
- package/src/storage/domains/legacy-evals/index.ts +151 -0
- package/src/storage/domains/memory/index.ts +900 -0
- package/src/storage/domains/operations/index.ts +368 -0
- package/src/storage/domains/scores/index.ts +231 -0
- package/src/storage/domains/traces/index.ts +160 -0
- package/src/storage/domains/utils.ts +12 -0
- package/src/storage/domains/workflows/index.ts +253 -0
- package/src/storage/index.test.ts +5 -2389
- package/src/storage/index.ts +157 -1545
- package/src/storage/test-utils.ts +368 -0
- package/src/vector/index.test.ts +89 -0
- package/src/vector/index.ts +3 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
2
|
+
import { StoreOperations, TABLE_WORKFLOW_SNAPSHOT } from '@mastra/core/storage';
|
|
3
|
+
import type { StorageColumn, TABLE_NAMES } from '@mastra/core/storage';
|
|
4
|
+
import { parseSqlIdentifier } from '@mastra/core/utils';
|
|
5
|
+
import type { IDatabase } from 'pg-promise';
|
|
6
|
+
import { getSchemaName, getTableName } from '../utils';
|
|
7
|
+
|
|
8
|
+
export class StoreOperationsPG extends StoreOperations {
|
|
9
|
+
public client: IDatabase<{}>;
|
|
10
|
+
public schemaName?: string;
|
|
11
|
+
private setupSchemaPromise: Promise<void> | null = null;
|
|
12
|
+
private schemaSetupComplete: boolean | undefined = undefined;
|
|
13
|
+
|
|
14
|
+
constructor({ client, schemaName }: { client: IDatabase<{}>; schemaName?: string }) {
|
|
15
|
+
super();
|
|
16
|
+
this.client = client;
|
|
17
|
+
this.schemaName = schemaName;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async hasColumn(table: string, column: string): Promise<boolean> {
|
|
21
|
+
// Use this.schema to scope the check
|
|
22
|
+
const schema = this.schemaName || 'public';
|
|
23
|
+
|
|
24
|
+
const result = await this.client.oneOrNone(
|
|
25
|
+
`SELECT 1 FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND (column_name = $3 OR column_name = $4)`,
|
|
26
|
+
[schema, table, column, column.toLowerCase()],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return !!result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private async setupSchema() {
|
|
33
|
+
if (!this.schemaName || this.schemaSetupComplete) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const schemaName = getSchemaName(this.schemaName);
|
|
38
|
+
|
|
39
|
+
if (!this.setupSchemaPromise) {
|
|
40
|
+
this.setupSchemaPromise = (async () => {
|
|
41
|
+
try {
|
|
42
|
+
// First check if schema exists and we have usage permission
|
|
43
|
+
const schemaExists = await this.client.oneOrNone(
|
|
44
|
+
`
|
|
45
|
+
SELECT EXISTS (
|
|
46
|
+
SELECT 1 FROM information_schema.schemata
|
|
47
|
+
WHERE schema_name = $1
|
|
48
|
+
)
|
|
49
|
+
`,
|
|
50
|
+
[this.schemaName],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (!schemaExists?.exists) {
|
|
54
|
+
try {
|
|
55
|
+
await this.client.none(`CREATE SCHEMA IF NOT EXISTS ${schemaName}`);
|
|
56
|
+
this.logger.info(`Schema "${this.schemaName}" created successfully`);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
this.logger.error(`Failed to create schema "${this.schemaName}"`, { error });
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. ` +
|
|
61
|
+
`Either create the schema manually or grant CREATE privilege to the user.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// If we got here, schema exists and we can use it
|
|
67
|
+
this.schemaSetupComplete = true;
|
|
68
|
+
this.logger.debug(`Schema "${schemaName}" is ready for use`);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
// Reset flags so we can retry
|
|
71
|
+
this.schemaSetupComplete = undefined;
|
|
72
|
+
this.setupSchemaPromise = null;
|
|
73
|
+
throw error;
|
|
74
|
+
} finally {
|
|
75
|
+
this.setupSchemaPromise = null;
|
|
76
|
+
}
|
|
77
|
+
})();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await this.setupSchemaPromise;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
|
|
84
|
+
try {
|
|
85
|
+
if (record.createdAt) {
|
|
86
|
+
record.createdAtZ = record.createdAt;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (record.created_at) {
|
|
90
|
+
record.created_atZ = record.created_at;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (record.updatedAt) {
|
|
94
|
+
record.updatedAtZ = record.updatedAt;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const schemaName = getSchemaName(this.schemaName);
|
|
98
|
+
const columns = Object.keys(record).map(col => parseSqlIdentifier(col, 'column name'));
|
|
99
|
+
const values = Object.values(record);
|
|
100
|
+
const placeholders = values.map((_, i) => `$${i + 1}`).join(', ');
|
|
101
|
+
|
|
102
|
+
await this.client.none(
|
|
103
|
+
`INSERT INTO ${getTableName({ indexName: tableName, schemaName })} (${columns.map(c => `"${c}"`).join(', ')}) VALUES (${placeholders})`,
|
|
104
|
+
values,
|
|
105
|
+
);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new MastraError(
|
|
108
|
+
{
|
|
109
|
+
id: 'MASTRA_STORAGE_PG_STORE_INSERT_FAILED',
|
|
110
|
+
domain: ErrorDomain.STORAGE,
|
|
111
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
112
|
+
details: {
|
|
113
|
+
tableName,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
error,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
122
|
+
try {
|
|
123
|
+
const schemaName = getSchemaName(this.schemaName);
|
|
124
|
+
const tableNameWithSchema = getTableName({ indexName: tableName, schemaName });
|
|
125
|
+
await this.client.none(`TRUNCATE TABLE ${tableNameWithSchema} CASCADE`);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new MastraError(
|
|
128
|
+
{
|
|
129
|
+
id: 'MASTRA_STORAGE_PG_STORE_CLEAR_TABLE_FAILED',
|
|
130
|
+
domain: ErrorDomain.STORAGE,
|
|
131
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
132
|
+
details: {
|
|
133
|
+
tableName,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
error,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected getDefaultValue(type: StorageColumn['type']): string {
|
|
142
|
+
switch (type) {
|
|
143
|
+
case 'timestamp':
|
|
144
|
+
return 'DEFAULT NOW()';
|
|
145
|
+
case 'jsonb':
|
|
146
|
+
return "DEFAULT '{}'::jsonb";
|
|
147
|
+
default:
|
|
148
|
+
return super.getDefaultValue(type);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async createTable({
|
|
153
|
+
tableName,
|
|
154
|
+
schema,
|
|
155
|
+
}: {
|
|
156
|
+
tableName: TABLE_NAMES;
|
|
157
|
+
schema: Record<string, StorageColumn>;
|
|
158
|
+
}): Promise<void> {
|
|
159
|
+
try {
|
|
160
|
+
const timeZColumnNames = Object.entries(schema)
|
|
161
|
+
.filter(([_, def]) => def.type === 'timestamp')
|
|
162
|
+
.map(([name]) => name);
|
|
163
|
+
|
|
164
|
+
const timeZColumns = Object.entries(schema)
|
|
165
|
+
.filter(([_, def]) => def.type === 'timestamp')
|
|
166
|
+
.map(([name]) => {
|
|
167
|
+
const parsedName = parseSqlIdentifier(name, 'column name');
|
|
168
|
+
return `"${parsedName}Z" TIMESTAMPTZ DEFAULT NOW()`;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const columns = Object.entries(schema).map(([name, def]) => {
|
|
172
|
+
const parsedName = parseSqlIdentifier(name, 'column name');
|
|
173
|
+
const constraints = [];
|
|
174
|
+
if (def.primaryKey) constraints.push('PRIMARY KEY');
|
|
175
|
+
if (!def.nullable) constraints.push('NOT NULL');
|
|
176
|
+
return `"${parsedName}" ${def.type.toUpperCase()} ${constraints.join(' ')}`;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Create schema if it doesn't exist
|
|
180
|
+
if (this.schemaName) {
|
|
181
|
+
await this.setupSchema();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const finalColumns = [...columns, ...timeZColumns].join(',\n');
|
|
185
|
+
|
|
186
|
+
const sql = `
|
|
187
|
+
CREATE TABLE IF NOT EXISTS ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (
|
|
188
|
+
${finalColumns}
|
|
189
|
+
);
|
|
190
|
+
${
|
|
191
|
+
tableName === TABLE_WORKFLOW_SNAPSHOT
|
|
192
|
+
? `
|
|
193
|
+
DO $$ BEGIN
|
|
194
|
+
IF NOT EXISTS (
|
|
195
|
+
SELECT 1 FROM pg_constraint WHERE conname = 'mastra_workflow_snapshot_workflow_name_run_id_key'
|
|
196
|
+
) THEN
|
|
197
|
+
ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })}
|
|
198
|
+
ADD CONSTRAINT mastra_workflow_snapshot_workflow_name_run_id_key
|
|
199
|
+
UNIQUE (workflow_name, run_id);
|
|
200
|
+
END IF;
|
|
201
|
+
END $$;
|
|
202
|
+
`
|
|
203
|
+
: ''
|
|
204
|
+
}
|
|
205
|
+
`;
|
|
206
|
+
|
|
207
|
+
await this.client.none(sql);
|
|
208
|
+
|
|
209
|
+
console.log('Alter Table SQL', tableName);
|
|
210
|
+
console.log('timeZColumnNames', timeZColumnNames);
|
|
211
|
+
await this.alterTable({
|
|
212
|
+
tableName,
|
|
213
|
+
schema,
|
|
214
|
+
ifNotExists: timeZColumnNames,
|
|
215
|
+
});
|
|
216
|
+
} catch (error) {
|
|
217
|
+
throw new MastraError(
|
|
218
|
+
{
|
|
219
|
+
id: 'MASTRA_STORAGE_PG_STORE_CREATE_TABLE_FAILED',
|
|
220
|
+
domain: ErrorDomain.STORAGE,
|
|
221
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
222
|
+
details: {
|
|
223
|
+
tableName,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
error,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Alters table schema to add columns if they don't exist
|
|
233
|
+
* @param tableName Name of the table
|
|
234
|
+
* @param schema Schema of the table
|
|
235
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
236
|
+
*/
|
|
237
|
+
async alterTable({
|
|
238
|
+
tableName,
|
|
239
|
+
schema,
|
|
240
|
+
ifNotExists,
|
|
241
|
+
}: {
|
|
242
|
+
tableName: TABLE_NAMES;
|
|
243
|
+
schema: Record<string, StorageColumn>;
|
|
244
|
+
ifNotExists: string[];
|
|
245
|
+
}): Promise<void> {
|
|
246
|
+
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
for (const columnName of ifNotExists) {
|
|
250
|
+
if (schema[columnName]) {
|
|
251
|
+
const columnDef = schema[columnName];
|
|
252
|
+
const sqlType = this.getSqlType(columnDef.type);
|
|
253
|
+
const nullable = columnDef.nullable === false ? 'NOT NULL' : '';
|
|
254
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : '';
|
|
255
|
+
const parsedColumnName = parseSqlIdentifier(columnName, 'column name');
|
|
256
|
+
const alterSql =
|
|
257
|
+
`ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
258
|
+
|
|
259
|
+
await this.client.none(alterSql);
|
|
260
|
+
|
|
261
|
+
if (sqlType === 'TIMESTAMP') {
|
|
262
|
+
const alterSql =
|
|
263
|
+
`ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}Z" TIMESTAMPTZ DEFAULT NOW()`.trim();
|
|
264
|
+
await this.client.none(alterSql);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
throw new MastraError(
|
|
272
|
+
{
|
|
273
|
+
id: 'MASTRA_STORAGE_PG_STORE_ALTER_TABLE_FAILED',
|
|
274
|
+
domain: ErrorDomain.STORAGE,
|
|
275
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
276
|
+
details: {
|
|
277
|
+
tableName,
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
error,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
|
|
286
|
+
try {
|
|
287
|
+
const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, 'column name'), value]);
|
|
288
|
+
const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(' AND ');
|
|
289
|
+
const values = keyEntries.map(([_, value]) => value);
|
|
290
|
+
|
|
291
|
+
const result = await this.client.oneOrNone<R>(
|
|
292
|
+
`SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`,
|
|
293
|
+
values,
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
if (!result) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// If this is a workflow snapshot, parse the snapshot field
|
|
301
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
302
|
+
const snapshot = result as any;
|
|
303
|
+
if (typeof snapshot.snapshot === 'string') {
|
|
304
|
+
snapshot.snapshot = JSON.parse(snapshot.snapshot);
|
|
305
|
+
}
|
|
306
|
+
return snapshot;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return result;
|
|
310
|
+
} catch (error) {
|
|
311
|
+
throw new MastraError(
|
|
312
|
+
{
|
|
313
|
+
id: 'MASTRA_STORAGE_PG_STORE_LOAD_FAILED',
|
|
314
|
+
domain: ErrorDomain.STORAGE,
|
|
315
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
316
|
+
details: {
|
|
317
|
+
tableName,
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
error,
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
326
|
+
try {
|
|
327
|
+
await this.client.query('BEGIN');
|
|
328
|
+
for (const record of records) {
|
|
329
|
+
await this.insert({ tableName, record });
|
|
330
|
+
}
|
|
331
|
+
await this.client.query('COMMIT');
|
|
332
|
+
} catch (error) {
|
|
333
|
+
await this.client.query('ROLLBACK');
|
|
334
|
+
throw new MastraError(
|
|
335
|
+
{
|
|
336
|
+
id: 'MASTRA_STORAGE_PG_STORE_BATCH_INSERT_FAILED',
|
|
337
|
+
domain: ErrorDomain.STORAGE,
|
|
338
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
339
|
+
details: {
|
|
340
|
+
tableName,
|
|
341
|
+
numberOfRecords: records.length,
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
error,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async dropTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
350
|
+
try {
|
|
351
|
+
const schemaName = getSchemaName(this.schemaName);
|
|
352
|
+
const tableNameWithSchema = getTableName({ indexName: tableName, schemaName });
|
|
353
|
+
await this.client.none(`DROP TABLE IF EXISTS ${tableNameWithSchema}`);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
throw new MastraError(
|
|
356
|
+
{
|
|
357
|
+
id: 'MASTRA_STORAGE_PG_STORE_DROP_TABLE_FAILED',
|
|
358
|
+
domain: ErrorDomain.STORAGE,
|
|
359
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
360
|
+
details: {
|
|
361
|
+
tableName,
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
error,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import type { PaginationInfo, StoragePagination } from '@mastra/core';
|
|
2
|
+
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
3
|
+
import type { ScoreRowData } from '@mastra/core/scores';
|
|
4
|
+
import { ScoresStorage, TABLE_SCORERS } from '@mastra/core/storage';
|
|
5
|
+
import type { IDatabase } from 'pg-promise';
|
|
6
|
+
import type { StoreOperationsPG } from '../operations';
|
|
7
|
+
|
|
8
|
+
function transformScoreRow(row: Record<string, any>): ScoreRowData {
|
|
9
|
+
let input = undefined;
|
|
10
|
+
|
|
11
|
+
if (row.input) {
|
|
12
|
+
try {
|
|
13
|
+
input = JSON.parse(row.input);
|
|
14
|
+
} catch {
|
|
15
|
+
input = row.input;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
...row,
|
|
20
|
+
input,
|
|
21
|
+
createdAt: row.createdAtZ || row.createdAt,
|
|
22
|
+
updatedAt: row.updatedAtZ || row.updatedAt,
|
|
23
|
+
} as ScoreRowData;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class ScoresPG extends ScoresStorage {
|
|
27
|
+
public client: IDatabase<{}>;
|
|
28
|
+
private operations: StoreOperationsPG;
|
|
29
|
+
|
|
30
|
+
constructor({ client, operations }: { client: IDatabase<{}>; operations: StoreOperationsPG }) {
|
|
31
|
+
super();
|
|
32
|
+
this.client = client;
|
|
33
|
+
this.operations = operations;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async getScoreById({ id }: { id: string }): Promise<ScoreRowData | null> {
|
|
37
|
+
try {
|
|
38
|
+
const result = await this.client.oneOrNone<ScoreRowData>(`SELECT * FROM ${TABLE_SCORERS} WHERE id = $1`, [id]);
|
|
39
|
+
|
|
40
|
+
return transformScoreRow(result!);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new MastraError(
|
|
43
|
+
{
|
|
44
|
+
id: 'MASTRA_STORAGE_PG_STORE_GET_SCORE_BY_ID_FAILED',
|
|
45
|
+
domain: ErrorDomain.STORAGE,
|
|
46
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
47
|
+
},
|
|
48
|
+
error,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async getScoresByScorerId({
|
|
54
|
+
scorerId,
|
|
55
|
+
pagination,
|
|
56
|
+
}: {
|
|
57
|
+
scorerId: string;
|
|
58
|
+
pagination: StoragePagination;
|
|
59
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
60
|
+
try {
|
|
61
|
+
const total = await this.client.oneOrNone<{ count: string }>(
|
|
62
|
+
`SELECT COUNT(*) FROM ${TABLE_SCORERS} WHERE "scorerId" = $1`,
|
|
63
|
+
[scorerId],
|
|
64
|
+
);
|
|
65
|
+
if (total?.count === '0' || !total?.count) {
|
|
66
|
+
return {
|
|
67
|
+
pagination: {
|
|
68
|
+
total: 0,
|
|
69
|
+
page: pagination.page,
|
|
70
|
+
perPage: pagination.perPage,
|
|
71
|
+
hasMore: false,
|
|
72
|
+
},
|
|
73
|
+
scores: [],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const result = await this.client.manyOrNone<ScoreRowData>(
|
|
78
|
+
`SELECT * FROM ${TABLE_SCORERS} WHERE "scorerId" = $1 LIMIT $2 OFFSET $3`,
|
|
79
|
+
[scorerId, pagination.perPage, pagination.page * pagination.perPage],
|
|
80
|
+
);
|
|
81
|
+
return {
|
|
82
|
+
pagination: {
|
|
83
|
+
total: Number(total?.count) || 0,
|
|
84
|
+
page: pagination.page,
|
|
85
|
+
perPage: pagination.perPage,
|
|
86
|
+
hasMore: Number(total?.count) > (pagination.page + 1) * pagination.perPage,
|
|
87
|
+
},
|
|
88
|
+
scores: result,
|
|
89
|
+
};
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw new MastraError(
|
|
92
|
+
{
|
|
93
|
+
id: 'MASTRA_STORAGE_PG_STORE_GET_SCORES_BY_SCORER_ID_FAILED',
|
|
94
|
+
domain: ErrorDomain.STORAGE,
|
|
95
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
96
|
+
},
|
|
97
|
+
error,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async saveScore(score: Omit<ScoreRowData, 'createdAt' | 'updatedAt'>): Promise<{ score: ScoreRowData }> {
|
|
103
|
+
try {
|
|
104
|
+
const { input, ...rest } = score;
|
|
105
|
+
await this.operations.insert({
|
|
106
|
+
tableName: TABLE_SCORERS,
|
|
107
|
+
record: {
|
|
108
|
+
...rest,
|
|
109
|
+
input: JSON.stringify(input),
|
|
110
|
+
createdAt: new Date().toISOString(),
|
|
111
|
+
updatedAt: new Date().toISOString(),
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const scoreFromDb = await this.getScoreById({ id: score.id });
|
|
116
|
+
return { score: scoreFromDb! };
|
|
117
|
+
} catch (error) {
|
|
118
|
+
throw new MastraError(
|
|
119
|
+
{
|
|
120
|
+
id: 'MASTRA_STORAGE_PG_STORE_SAVE_SCORE_FAILED',
|
|
121
|
+
domain: ErrorDomain.STORAGE,
|
|
122
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
123
|
+
},
|
|
124
|
+
error,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async getScoresByRunId({
|
|
130
|
+
runId,
|
|
131
|
+
pagination,
|
|
132
|
+
}: {
|
|
133
|
+
runId: string;
|
|
134
|
+
pagination: StoragePagination;
|
|
135
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
136
|
+
try {
|
|
137
|
+
const total = await this.client.oneOrNone<{ count: string }>(
|
|
138
|
+
`SELECT COUNT(*) FROM ${TABLE_SCORERS} WHERE "runId" = $1`,
|
|
139
|
+
[runId],
|
|
140
|
+
);
|
|
141
|
+
console.log(`total: ${total?.count}`);
|
|
142
|
+
console.log(`typeof total: ${typeof total?.count}`);
|
|
143
|
+
if (total?.count === '0' || !total?.count) {
|
|
144
|
+
return {
|
|
145
|
+
pagination: {
|
|
146
|
+
total: 0,
|
|
147
|
+
page: pagination.page,
|
|
148
|
+
perPage: pagination.perPage,
|
|
149
|
+
hasMore: false,
|
|
150
|
+
},
|
|
151
|
+
scores: [],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result = await this.client.manyOrNone<ScoreRowData>(
|
|
156
|
+
`SELECT * FROM ${TABLE_SCORERS} WHERE "runId" = $1 LIMIT $2 OFFSET $3`,
|
|
157
|
+
[runId, pagination.perPage, pagination.page * pagination.perPage],
|
|
158
|
+
);
|
|
159
|
+
return {
|
|
160
|
+
pagination: {
|
|
161
|
+
total: Number(total?.count) || 0,
|
|
162
|
+
page: pagination.page,
|
|
163
|
+
perPage: pagination.perPage,
|
|
164
|
+
hasMore: Number(total?.count) > (pagination.page + 1) * pagination.perPage,
|
|
165
|
+
},
|
|
166
|
+
scores: result,
|
|
167
|
+
};
|
|
168
|
+
} catch (error) {
|
|
169
|
+
throw new MastraError(
|
|
170
|
+
{
|
|
171
|
+
id: 'MASTRA_STORAGE_PG_STORE_GET_SCORES_BY_RUN_ID_FAILED',
|
|
172
|
+
domain: ErrorDomain.STORAGE,
|
|
173
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
174
|
+
},
|
|
175
|
+
error,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async getScoresByEntityId({
|
|
181
|
+
entityId,
|
|
182
|
+
entityType,
|
|
183
|
+
pagination,
|
|
184
|
+
}: {
|
|
185
|
+
pagination: StoragePagination;
|
|
186
|
+
entityId: string;
|
|
187
|
+
entityType: string;
|
|
188
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
189
|
+
try {
|
|
190
|
+
const total = await this.client.oneOrNone<{ count: string }>(
|
|
191
|
+
`SELECT COUNT(*) FROM ${TABLE_SCORERS} WHERE "entityId" = $1 AND "entityType" = $2`,
|
|
192
|
+
[entityId, entityType],
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (total?.count === '0' || !total?.count) {
|
|
196
|
+
return {
|
|
197
|
+
pagination: {
|
|
198
|
+
total: 0,
|
|
199
|
+
page: pagination.page,
|
|
200
|
+
perPage: pagination.perPage,
|
|
201
|
+
hasMore: false,
|
|
202
|
+
},
|
|
203
|
+
scores: [],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const result = await this.client.manyOrNone<ScoreRowData>(
|
|
208
|
+
`SELECT * FROM ${TABLE_SCORERS} WHERE "entityId" = $1 AND "entityType" = $2 LIMIT $3 OFFSET $4`,
|
|
209
|
+
[entityId, entityType, pagination.perPage, pagination.page * pagination.perPage],
|
|
210
|
+
);
|
|
211
|
+
return {
|
|
212
|
+
pagination: {
|
|
213
|
+
total: Number(total?.count) || 0,
|
|
214
|
+
page: pagination.page,
|
|
215
|
+
perPage: pagination.perPage,
|
|
216
|
+
hasMore: Number(total?.count) > (pagination.page + 1) * pagination.perPage,
|
|
217
|
+
},
|
|
218
|
+
scores: result,
|
|
219
|
+
};
|
|
220
|
+
} catch (error) {
|
|
221
|
+
throw new MastraError(
|
|
222
|
+
{
|
|
223
|
+
id: 'MASTRA_STORAGE_PG_STORE_GET_SCORES_BY_ENTITY_ID_FAILED',
|
|
224
|
+
domain: ErrorDomain.STORAGE,
|
|
225
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
226
|
+
},
|
|
227
|
+
error,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|