@mastra/pg 0.12.3 → 0.12.4
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 +55 -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 +4 -4
- 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
package/src/storage/index.ts
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
1
|
-
import { MessageList } from '@mastra/core/agent';
|
|
2
1
|
import type { MastraMessageContentV2, MastraMessageV2 } from '@mastra/core/agent';
|
|
3
2
|
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
4
|
-
import type { MetricResult } from '@mastra/core/eval';
|
|
5
3
|
import type { MastraMessageV1, StorageThreadType } from '@mastra/core/memory';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
TABLE_MESSAGES,
|
|
9
|
-
TABLE_THREADS,
|
|
10
|
-
TABLE_TRACES,
|
|
11
|
-
TABLE_RESOURCES,
|
|
12
|
-
TABLE_WORKFLOW_SNAPSHOT,
|
|
13
|
-
TABLE_EVALS,
|
|
14
|
-
} from '@mastra/core/storage';
|
|
4
|
+
import type { ScoreRowData } from '@mastra/core/scores';
|
|
5
|
+
import { MastraStorage } from '@mastra/core/storage';
|
|
15
6
|
import type {
|
|
16
7
|
EvalRow,
|
|
17
8
|
PaginationInfo,
|
|
18
9
|
StorageColumn,
|
|
19
10
|
StorageGetMessagesArg,
|
|
11
|
+
StorageGetTracesArg,
|
|
12
|
+
StorageGetTracesPaginatedArg,
|
|
20
13
|
StorageResourceType,
|
|
21
14
|
TABLE_NAMES,
|
|
22
15
|
WorkflowRun,
|
|
23
16
|
WorkflowRuns,
|
|
24
17
|
PaginationArgs,
|
|
18
|
+
StoragePagination,
|
|
19
|
+
StorageDomains,
|
|
25
20
|
} from '@mastra/core/storage';
|
|
26
|
-
import {
|
|
21
|
+
import type { Trace } from '@mastra/core/telemetry';
|
|
27
22
|
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
28
23
|
import pgPromise from 'pg-promise';
|
|
29
24
|
import type { ISSLConfig } from 'pg-promise/typescript/pg-subset';
|
|
25
|
+
import { LegacyEvalsPG } from './domains/legacy-evals';
|
|
26
|
+
import { MemoryPG } from './domains/memory';
|
|
27
|
+
import { StoreOperationsPG } from './domains/operations';
|
|
28
|
+
import { ScoresPG } from './domains/scores';
|
|
29
|
+
import { TracesPG } from './domains/traces';
|
|
30
|
+
import { WorkflowsPG } from './domains/workflows';
|
|
30
31
|
|
|
31
32
|
export type PostgresConfig = {
|
|
32
33
|
schemaName?: string;
|
|
@@ -47,9 +48,10 @@ export type PostgresConfig = {
|
|
|
47
48
|
export class PostgresStore extends MastraStorage {
|
|
48
49
|
public db: pgPromise.IDatabase<{}>;
|
|
49
50
|
public pgp: pgPromise.IMain;
|
|
51
|
+
private client: pgPromise.IDatabase<{}>;
|
|
50
52
|
private schema?: string;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
|
|
54
|
+
stores: StorageDomains;
|
|
53
55
|
|
|
54
56
|
constructor(config: PostgresConfig) {
|
|
55
57
|
// Validation: connectionString or host/database/user/password must not be empty
|
|
@@ -76,7 +78,7 @@ export class PostgresStore extends MastraStorage {
|
|
|
76
78
|
}
|
|
77
79
|
super({ name: 'PostgresStore' });
|
|
78
80
|
this.pgp = pgPromise();
|
|
79
|
-
this.schema = config.schemaName;
|
|
81
|
+
this.schema = config.schemaName || 'public';
|
|
80
82
|
this.db = this.pgp(
|
|
81
83
|
`connectionString` in config
|
|
82
84
|
? { connectionString: config.connectionString }
|
|
@@ -89,6 +91,24 @@ export class PostgresStore extends MastraStorage {
|
|
|
89
91
|
ssl: config.ssl,
|
|
90
92
|
},
|
|
91
93
|
);
|
|
94
|
+
|
|
95
|
+
this.client = this.db;
|
|
96
|
+
|
|
97
|
+
const operations = new StoreOperationsPG({ client: this.client, schemaName: this.schema });
|
|
98
|
+
const scores = new ScoresPG({ client: this.client, operations });
|
|
99
|
+
const traces = new TracesPG({ client: this.client, operations, schema: this.schema });
|
|
100
|
+
const workflows = new WorkflowsPG({ client: this.client, operations, schema: this.schema });
|
|
101
|
+
const legacyEvals = new LegacyEvalsPG({ client: this.client, schema: this.schema });
|
|
102
|
+
const memory = new MemoryPG({ client: this.client, schema: this.schema, operations });
|
|
103
|
+
|
|
104
|
+
this.stores = {
|
|
105
|
+
operations,
|
|
106
|
+
scores,
|
|
107
|
+
traces,
|
|
108
|
+
workflows,
|
|
109
|
+
legacyEvals,
|
|
110
|
+
memory,
|
|
111
|
+
};
|
|
92
112
|
} catch (e) {
|
|
93
113
|
throw new MastraError(
|
|
94
114
|
{
|
|
@@ -101,304 +121,42 @@ export class PostgresStore extends MastraStorage {
|
|
|
101
121
|
}
|
|
102
122
|
}
|
|
103
123
|
|
|
104
|
-
public get supports()
|
|
105
|
-
selectByIncludeResourceScope: boolean;
|
|
106
|
-
resourceWorkingMemory: boolean;
|
|
107
|
-
} {
|
|
124
|
+
public get supports() {
|
|
108
125
|
return {
|
|
109
126
|
selectByIncludeResourceScope: true,
|
|
110
127
|
resourceWorkingMemory: true,
|
|
128
|
+
hasColumn: true,
|
|
129
|
+
createTable: true,
|
|
111
130
|
};
|
|
112
131
|
}
|
|
113
132
|
|
|
114
|
-
private getTableName(indexName: string) {
|
|
115
|
-
const parsedIndexName = parseSqlIdentifier(indexName, 'index name');
|
|
116
|
-
const quotedIndexName = `"${parsedIndexName}"`;
|
|
117
|
-
const quotedSchemaName = this.getSchemaName();
|
|
118
|
-
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
private getSchemaName() {
|
|
122
|
-
return this.schema ? `"${parseSqlIdentifier(this.schema, 'schema name')}"` : undefined;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
133
|
/** @deprecated use getEvals instead */
|
|
126
134
|
async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
|
|
127
|
-
|
|
128
|
-
const baseQuery = `SELECT * FROM ${this.getTableName(TABLE_EVALS)} WHERE agent_name = $1`;
|
|
129
|
-
const typeCondition =
|
|
130
|
-
type === 'test'
|
|
131
|
-
? " AND test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL"
|
|
132
|
-
: type === 'live'
|
|
133
|
-
? " AND (test_info IS NULL OR test_info->>'testPath' IS NULL)"
|
|
134
|
-
: '';
|
|
135
|
-
|
|
136
|
-
const query = `${baseQuery}${typeCondition} ORDER BY created_at DESC`;
|
|
137
|
-
|
|
138
|
-
const rows = await this.db.manyOrNone(query, [agentName]);
|
|
139
|
-
return rows?.map(row => this.transformEvalRow(row)) ?? [];
|
|
140
|
-
} catch (error) {
|
|
141
|
-
// Handle case where table doesn't exist yet
|
|
142
|
-
if (error instanceof Error && error.message.includes('relation') && error.message.includes('does not exist')) {
|
|
143
|
-
return [];
|
|
144
|
-
}
|
|
145
|
-
console.error('Failed to get evals for the specified agent: ' + (error as any)?.message);
|
|
146
|
-
throw error;
|
|
147
|
-
}
|
|
135
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
148
136
|
}
|
|
149
137
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
agentName: row.agent_name as string,
|
|
162
|
-
input: row.input as string,
|
|
163
|
-
output: row.output as string,
|
|
164
|
-
result: row.result as MetricResult,
|
|
165
|
-
metricName: row.metric_name as string,
|
|
166
|
-
instructions: row.instructions as string,
|
|
167
|
-
testInfo: testInfoValue,
|
|
168
|
-
globalRunId: row.global_run_id as string,
|
|
169
|
-
runId: row.run_id as string,
|
|
170
|
-
createdAt: row.created_at as string,
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
175
|
-
try {
|
|
176
|
-
await this.db.query('BEGIN');
|
|
177
|
-
for (const record of records) {
|
|
178
|
-
await this.insert({ tableName, record });
|
|
179
|
-
}
|
|
180
|
-
await this.db.query('COMMIT');
|
|
181
|
-
} catch (error) {
|
|
182
|
-
await this.db.query('ROLLBACK');
|
|
183
|
-
throw new MastraError(
|
|
184
|
-
{
|
|
185
|
-
id: 'MASTRA_STORAGE_PG_STORE_BATCH_INSERT_FAILED',
|
|
186
|
-
domain: ErrorDomain.STORAGE,
|
|
187
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
188
|
-
details: {
|
|
189
|
-
tableName,
|
|
190
|
-
numberOfRecords: records.length,
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
error,
|
|
194
|
-
);
|
|
195
|
-
}
|
|
138
|
+
async getEvals(
|
|
139
|
+
options: {
|
|
140
|
+
agentName?: string;
|
|
141
|
+
type?: 'test' | 'live';
|
|
142
|
+
} & PaginationArgs = {},
|
|
143
|
+
): Promise<PaginationInfo & { evals: EvalRow[] }> {
|
|
144
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
196
145
|
}
|
|
197
146
|
|
|
198
147
|
/**
|
|
199
148
|
* @deprecated use getTracesPaginated instead
|
|
200
149
|
*/
|
|
201
|
-
public async getTraces(args: {
|
|
202
|
-
|
|
203
|
-
scope?: string;
|
|
204
|
-
attributes?: Record<string, string>;
|
|
205
|
-
filters?: Record<string, any>;
|
|
206
|
-
page: number;
|
|
207
|
-
perPage?: number;
|
|
208
|
-
fromDate?: Date;
|
|
209
|
-
toDate?: Date;
|
|
210
|
-
}): Promise<any[]> {
|
|
211
|
-
if (args.fromDate || args.toDate) {
|
|
212
|
-
(args as any).dateRange = {
|
|
213
|
-
start: args.fromDate,
|
|
214
|
-
end: args.toDate,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
const result = await this.getTracesPaginated(args);
|
|
218
|
-
return result.traces;
|
|
150
|
+
public async getTraces(args: StorageGetTracesArg): Promise<Trace[]> {
|
|
151
|
+
return this.stores.traces.getTraces(args);
|
|
219
152
|
}
|
|
220
153
|
|
|
221
|
-
public async getTracesPaginated(
|
|
222
|
-
args
|
|
223
|
-
name?: string;
|
|
224
|
-
scope?: string;
|
|
225
|
-
attributes?: Record<string, string>;
|
|
226
|
-
filters?: Record<string, any>;
|
|
227
|
-
} & PaginationArgs,
|
|
228
|
-
): Promise<
|
|
229
|
-
PaginationInfo & {
|
|
230
|
-
traces: any[];
|
|
231
|
-
}
|
|
232
|
-
> {
|
|
233
|
-
const { name, scope, page = 0, perPage: perPageInput, attributes, filters, dateRange } = args;
|
|
234
|
-
const fromDate = dateRange?.start;
|
|
235
|
-
const toDate = dateRange?.end;
|
|
236
|
-
|
|
237
|
-
const perPage = perPageInput !== undefined ? perPageInput : 100; // Default perPage
|
|
238
|
-
const currentOffset = page * perPage;
|
|
239
|
-
|
|
240
|
-
const queryParams: any[] = [];
|
|
241
|
-
const conditions: string[] = [];
|
|
242
|
-
let paramIndex = 1;
|
|
243
|
-
|
|
244
|
-
if (name) {
|
|
245
|
-
conditions.push(`name LIKE $${paramIndex++}`);
|
|
246
|
-
queryParams.push(`${name}%`); // Add wildcard for LIKE
|
|
247
|
-
}
|
|
248
|
-
if (scope) {
|
|
249
|
-
conditions.push(`scope = $${paramIndex++}`);
|
|
250
|
-
queryParams.push(scope);
|
|
251
|
-
}
|
|
252
|
-
if (attributes) {
|
|
253
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
|
254
|
-
const parsedKey = parseFieldKey(key);
|
|
255
|
-
conditions.push(`attributes->>'${parsedKey}' = $${paramIndex++}`);
|
|
256
|
-
queryParams.push(value);
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
if (filters) {
|
|
260
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
261
|
-
const parsedKey = parseFieldKey(key);
|
|
262
|
-
conditions.push(`"${parsedKey}" = $${paramIndex++}`); // Ensure filter keys are quoted if they are column names
|
|
263
|
-
queryParams.push(value);
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
if (fromDate) {
|
|
267
|
-
conditions.push(`"createdAt" >= $${paramIndex++}`);
|
|
268
|
-
queryParams.push(fromDate);
|
|
269
|
-
}
|
|
270
|
-
if (toDate) {
|
|
271
|
-
conditions.push(`"createdAt" <= $${paramIndex++}`);
|
|
272
|
-
queryParams.push(toDate);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
276
|
-
|
|
277
|
-
// Get total count
|
|
278
|
-
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_TRACES)} ${whereClause}`;
|
|
279
|
-
let total = 0;
|
|
280
|
-
try {
|
|
281
|
-
const countResult = await this.db.one(countQuery, queryParams);
|
|
282
|
-
total = parseInt(countResult.count, 10);
|
|
283
|
-
} catch (error) {
|
|
284
|
-
throw new MastraError(
|
|
285
|
-
{
|
|
286
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_TRACES_PAGINATED_FAILED_TO_RETRIEVE_TOTAL_COUNT',
|
|
287
|
-
domain: ErrorDomain.STORAGE,
|
|
288
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
289
|
-
details: {
|
|
290
|
-
name: args.name ?? '',
|
|
291
|
-
scope: args.scope ?? '',
|
|
292
|
-
},
|
|
293
|
-
},
|
|
294
|
-
error,
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (total === 0) {
|
|
299
|
-
return {
|
|
300
|
-
traces: [],
|
|
301
|
-
total: 0,
|
|
302
|
-
page,
|
|
303
|
-
perPage,
|
|
304
|
-
hasMore: false,
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const dataQuery = `SELECT * FROM ${this.getTableName(
|
|
309
|
-
TABLE_TRACES,
|
|
310
|
-
)} ${whereClause} ORDER BY "createdAt" DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
311
|
-
const finalQueryParams = [...queryParams, perPage, currentOffset];
|
|
312
|
-
|
|
313
|
-
try {
|
|
314
|
-
const rows = await this.db.manyOrNone<any>(dataQuery, finalQueryParams);
|
|
315
|
-
const traces = rows.map(row => ({
|
|
316
|
-
id: row.id,
|
|
317
|
-
parentSpanId: row.parentSpanId,
|
|
318
|
-
traceId: row.traceId,
|
|
319
|
-
name: row.name,
|
|
320
|
-
scope: row.scope,
|
|
321
|
-
kind: row.kind,
|
|
322
|
-
status: row.status,
|
|
323
|
-
events: row.events,
|
|
324
|
-
links: row.links,
|
|
325
|
-
attributes: row.attributes,
|
|
326
|
-
startTime: row.startTime,
|
|
327
|
-
endTime: row.endTime,
|
|
328
|
-
other: row.other,
|
|
329
|
-
createdAt: row.createdAt,
|
|
330
|
-
}));
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
traces,
|
|
334
|
-
total,
|
|
335
|
-
page,
|
|
336
|
-
perPage,
|
|
337
|
-
hasMore: currentOffset + traces.length < total,
|
|
338
|
-
};
|
|
339
|
-
} catch (error) {
|
|
340
|
-
throw new MastraError(
|
|
341
|
-
{
|
|
342
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_TRACES_PAGINATED_FAILED_TO_RETRIEVE_TRACES',
|
|
343
|
-
domain: ErrorDomain.STORAGE,
|
|
344
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
345
|
-
details: {
|
|
346
|
-
name: args.name ?? '',
|
|
347
|
-
scope: args.scope ?? '',
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
error,
|
|
351
|
-
);
|
|
352
|
-
}
|
|
154
|
+
public async getTracesPaginated(args: StorageGetTracesPaginatedArg): Promise<PaginationInfo & { traces: Trace[] }> {
|
|
155
|
+
return this.stores.traces.getTracesPaginated(args);
|
|
353
156
|
}
|
|
354
157
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (!this.setupSchemaPromise) {
|
|
361
|
-
this.setupSchemaPromise = (async () => {
|
|
362
|
-
try {
|
|
363
|
-
// First check if schema exists and we have usage permission
|
|
364
|
-
const schemaExists = await this.db.oneOrNone(
|
|
365
|
-
`
|
|
366
|
-
SELECT EXISTS (
|
|
367
|
-
SELECT 1 FROM information_schema.schemata
|
|
368
|
-
WHERE schema_name = $1
|
|
369
|
-
)
|
|
370
|
-
`,
|
|
371
|
-
[this.schema],
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
if (!schemaExists?.exists) {
|
|
375
|
-
try {
|
|
376
|
-
await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${this.getSchemaName()}`);
|
|
377
|
-
this.logger.info(`Schema "${this.schema}" created successfully`);
|
|
378
|
-
} catch (error) {
|
|
379
|
-
this.logger.error(`Failed to create schema "${this.schema}"`, { error });
|
|
380
|
-
throw new Error(
|
|
381
|
-
`Unable to create schema "${this.schema}". This requires CREATE privilege on the database. ` +
|
|
382
|
-
`Either create the schema manually or grant CREATE privilege to the user.`,
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// If we got here, schema exists and we can use it
|
|
388
|
-
this.schemaSetupComplete = true;
|
|
389
|
-
this.logger.debug(`Schema "${this.schema}" is ready for use`);
|
|
390
|
-
} catch (error) {
|
|
391
|
-
// Reset flags so we can retry
|
|
392
|
-
this.schemaSetupComplete = undefined;
|
|
393
|
-
this.setupSchemaPromise = null;
|
|
394
|
-
throw error;
|
|
395
|
-
} finally {
|
|
396
|
-
this.setupSchemaPromise = null;
|
|
397
|
-
}
|
|
398
|
-
})();
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
await this.setupSchemaPromise;
|
|
158
|
+
async batchTraceInsert({ records }: { records: Record<string, any>[] }): Promise<void> {
|
|
159
|
+
return this.stores.traces.batchTraceInsert({ records });
|
|
402
160
|
}
|
|
403
161
|
|
|
404
162
|
async createTable({
|
|
@@ -408,76 +166,9 @@ export class PostgresStore extends MastraStorage {
|
|
|
408
166
|
tableName: TABLE_NAMES;
|
|
409
167
|
schema: Record<string, StorageColumn>;
|
|
410
168
|
}): Promise<void> {
|
|
411
|
-
|
|
412
|
-
const columns = Object.entries(schema)
|
|
413
|
-
.map(([name, def]) => {
|
|
414
|
-
const parsedName = parseSqlIdentifier(name, 'column name');
|
|
415
|
-
const constraints = [];
|
|
416
|
-
if (def.primaryKey) constraints.push('PRIMARY KEY');
|
|
417
|
-
if (!def.nullable) constraints.push('NOT NULL');
|
|
418
|
-
return `"${parsedName}" ${def.type.toUpperCase()} ${constraints.join(' ')}`;
|
|
419
|
-
})
|
|
420
|
-
.join(',\n');
|
|
421
|
-
|
|
422
|
-
// Create schema if it doesn't exist
|
|
423
|
-
if (this.schema) {
|
|
424
|
-
await this.setupSchema();
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const sql = `
|
|
428
|
-
CREATE TABLE IF NOT EXISTS ${this.getTableName(tableName)} (
|
|
429
|
-
${columns}
|
|
430
|
-
);
|
|
431
|
-
${
|
|
432
|
-
tableName === TABLE_WORKFLOW_SNAPSHOT
|
|
433
|
-
? `
|
|
434
|
-
DO $$ BEGIN
|
|
435
|
-
IF NOT EXISTS (
|
|
436
|
-
SELECT 1 FROM pg_constraint WHERE conname = 'mastra_workflow_snapshot_workflow_name_run_id_key'
|
|
437
|
-
) THEN
|
|
438
|
-
ALTER TABLE ${this.getTableName(tableName)}
|
|
439
|
-
ADD CONSTRAINT mastra_workflow_snapshot_workflow_name_run_id_key
|
|
440
|
-
UNIQUE (workflow_name, run_id);
|
|
441
|
-
END IF;
|
|
442
|
-
END $$;
|
|
443
|
-
`
|
|
444
|
-
: ''
|
|
445
|
-
}
|
|
446
|
-
`;
|
|
447
|
-
|
|
448
|
-
await this.db.none(sql);
|
|
449
|
-
} catch (error) {
|
|
450
|
-
throw new MastraError(
|
|
451
|
-
{
|
|
452
|
-
id: 'MASTRA_STORAGE_PG_STORE_CREATE_TABLE_FAILED',
|
|
453
|
-
domain: ErrorDomain.STORAGE,
|
|
454
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
455
|
-
details: {
|
|
456
|
-
tableName,
|
|
457
|
-
},
|
|
458
|
-
},
|
|
459
|
-
error,
|
|
460
|
-
);
|
|
461
|
-
}
|
|
169
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
462
170
|
}
|
|
463
171
|
|
|
464
|
-
protected getDefaultValue(type: StorageColumn['type']): string {
|
|
465
|
-
switch (type) {
|
|
466
|
-
case 'timestamp':
|
|
467
|
-
return 'DEFAULT NOW()';
|
|
468
|
-
case 'jsonb':
|
|
469
|
-
return "DEFAULT '{}'::jsonb";
|
|
470
|
-
default:
|
|
471
|
-
return super.getDefaultValue(type);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Alters table schema to add columns if they don't exist
|
|
477
|
-
* @param tableName Name of the table
|
|
478
|
-
* @param schema Schema of the table
|
|
479
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
480
|
-
*/
|
|
481
172
|
async alterTable({
|
|
482
173
|
tableName,
|
|
483
174
|
schema,
|
|
@@ -487,288 +178,54 @@ export class PostgresStore extends MastraStorage {
|
|
|
487
178
|
schema: Record<string, StorageColumn>;
|
|
488
179
|
ifNotExists: string[];
|
|
489
180
|
}): Promise<void> {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
try {
|
|
493
|
-
for (const columnName of ifNotExists) {
|
|
494
|
-
if (schema[columnName]) {
|
|
495
|
-
const columnDef = schema[columnName];
|
|
496
|
-
const sqlType = this.getSqlType(columnDef.type);
|
|
497
|
-
const nullable = columnDef.nullable === false ? 'NOT NULL' : '';
|
|
498
|
-
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : '';
|
|
499
|
-
const parsedColumnName = parseSqlIdentifier(columnName, 'column name');
|
|
500
|
-
const alterSql =
|
|
501
|
-
`ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
502
|
-
|
|
503
|
-
await this.db.none(alterSql);
|
|
504
|
-
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
} catch (error) {
|
|
508
|
-
throw new MastraError(
|
|
509
|
-
{
|
|
510
|
-
id: 'MASTRA_STORAGE_PG_STORE_ALTER_TABLE_FAILED',
|
|
511
|
-
domain: ErrorDomain.STORAGE,
|
|
512
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
513
|
-
details: {
|
|
514
|
-
tableName,
|
|
515
|
-
},
|
|
516
|
-
},
|
|
517
|
-
error,
|
|
518
|
-
);
|
|
519
|
-
}
|
|
181
|
+
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
520
182
|
}
|
|
521
183
|
|
|
522
184
|
async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
id: 'MASTRA_STORAGE_PG_STORE_CLEAR_TABLE_FAILED',
|
|
529
|
-
domain: ErrorDomain.STORAGE,
|
|
530
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
531
|
-
details: {
|
|
532
|
-
tableName,
|
|
533
|
-
},
|
|
534
|
-
},
|
|
535
|
-
error,
|
|
536
|
-
);
|
|
537
|
-
}
|
|
185
|
+
return this.stores.operations.clearTable({ tableName });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async dropTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
189
|
+
return this.stores.operations.dropTable({ tableName });
|
|
538
190
|
}
|
|
539
191
|
|
|
540
192
|
async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
const values = Object.values(record);
|
|
544
|
-
const placeholders = values.map((_, i) => `$${i + 1}`).join(', ');
|
|
193
|
+
return this.stores.operations.insert({ tableName, record });
|
|
194
|
+
}
|
|
545
195
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
values,
|
|
549
|
-
);
|
|
550
|
-
} catch (error) {
|
|
551
|
-
throw new MastraError(
|
|
552
|
-
{
|
|
553
|
-
id: 'MASTRA_STORAGE_PG_STORE_INSERT_FAILED',
|
|
554
|
-
domain: ErrorDomain.STORAGE,
|
|
555
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
556
|
-
details: {
|
|
557
|
-
tableName,
|
|
558
|
-
},
|
|
559
|
-
},
|
|
560
|
-
error,
|
|
561
|
-
);
|
|
562
|
-
}
|
|
196
|
+
async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
197
|
+
return this.stores.operations.batchInsert({ tableName, records });
|
|
563
198
|
}
|
|
564
199
|
|
|
565
200
|
async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
|
|
566
|
-
|
|
567
|
-
const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, 'column name'), value]);
|
|
568
|
-
const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(' AND ');
|
|
569
|
-
const values = keyEntries.map(([_, value]) => value);
|
|
570
|
-
|
|
571
|
-
const result = await this.db.oneOrNone<R>(
|
|
572
|
-
`SELECT * FROM ${this.getTableName(tableName)} WHERE ${conditions}`,
|
|
573
|
-
values,
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
if (!result) {
|
|
577
|
-
return null;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// If this is a workflow snapshot, parse the snapshot field
|
|
581
|
-
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
582
|
-
const snapshot = result as any;
|
|
583
|
-
if (typeof snapshot.snapshot === 'string') {
|
|
584
|
-
snapshot.snapshot = JSON.parse(snapshot.snapshot);
|
|
585
|
-
}
|
|
586
|
-
return snapshot;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
return result;
|
|
590
|
-
} catch (error) {
|
|
591
|
-
throw new MastraError(
|
|
592
|
-
{
|
|
593
|
-
id: 'MASTRA_STORAGE_PG_STORE_LOAD_FAILED',
|
|
594
|
-
domain: ErrorDomain.STORAGE,
|
|
595
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
596
|
-
details: {
|
|
597
|
-
tableName,
|
|
598
|
-
},
|
|
599
|
-
},
|
|
600
|
-
error,
|
|
601
|
-
);
|
|
602
|
-
}
|
|
201
|
+
return this.stores.operations.load({ tableName, keys });
|
|
603
202
|
}
|
|
604
203
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
`SELECT
|
|
609
|
-
id,
|
|
610
|
-
"resourceId",
|
|
611
|
-
title,
|
|
612
|
-
metadata,
|
|
613
|
-
"createdAt",
|
|
614
|
-
"updatedAt"
|
|
615
|
-
FROM ${this.getTableName(TABLE_THREADS)}
|
|
616
|
-
WHERE id = $1`,
|
|
617
|
-
[threadId],
|
|
618
|
-
);
|
|
619
|
-
|
|
620
|
-
if (!thread) {
|
|
621
|
-
return null;
|
|
622
|
-
}
|
|
204
|
+
/**
|
|
205
|
+
* Memory
|
|
206
|
+
*/
|
|
623
207
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
627
|
-
createdAt: thread.createdAt,
|
|
628
|
-
updatedAt: thread.updatedAt,
|
|
629
|
-
};
|
|
630
|
-
} catch (error) {
|
|
631
|
-
throw new MastraError(
|
|
632
|
-
{
|
|
633
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_THREAD_BY_ID_FAILED',
|
|
634
|
-
domain: ErrorDomain.STORAGE,
|
|
635
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
636
|
-
details: {
|
|
637
|
-
threadId,
|
|
638
|
-
},
|
|
639
|
-
},
|
|
640
|
-
error,
|
|
641
|
-
);
|
|
642
|
-
}
|
|
208
|
+
async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
|
|
209
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
643
210
|
}
|
|
644
211
|
|
|
645
212
|
/**
|
|
646
213
|
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
647
214
|
*/
|
|
648
215
|
public async getThreadsByResourceId(args: { resourceId: string }): Promise<StorageThreadType[]> {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
try {
|
|
652
|
-
const baseQuery = `FROM ${this.getTableName(TABLE_THREADS)} WHERE "resourceId" = $1`;
|
|
653
|
-
const queryParams: any[] = [resourceId];
|
|
654
|
-
|
|
655
|
-
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC`;
|
|
656
|
-
const rows = await this.db.manyOrNone(dataQuery, queryParams);
|
|
657
|
-
return (rows || []).map(thread => ({
|
|
658
|
-
...thread,
|
|
659
|
-
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
660
|
-
createdAt: thread.createdAt,
|
|
661
|
-
updatedAt: thread.updatedAt,
|
|
662
|
-
}));
|
|
663
|
-
} catch (error) {
|
|
664
|
-
this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
|
|
665
|
-
return [];
|
|
666
|
-
}
|
|
216
|
+
return this.stores.memory.getThreadsByResourceId(args);
|
|
667
217
|
}
|
|
668
218
|
|
|
669
|
-
public async getThreadsByResourceIdPaginated(
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
|
|
674
|
-
|
|
675
|
-
try {
|
|
676
|
-
const baseQuery = `FROM ${this.getTableName(TABLE_THREADS)} WHERE "resourceId" = $1`;
|
|
677
|
-
const queryParams: any[] = [resourceId];
|
|
678
|
-
const perPage = perPageInput !== undefined ? perPageInput : 100;
|
|
679
|
-
const currentOffset = page * perPage;
|
|
680
|
-
|
|
681
|
-
const countQuery = `SELECT COUNT(*) ${baseQuery}`;
|
|
682
|
-
const countResult = await this.db.one(countQuery, queryParams);
|
|
683
|
-
const total = parseInt(countResult.count, 10);
|
|
684
|
-
|
|
685
|
-
if (total === 0) {
|
|
686
|
-
return {
|
|
687
|
-
threads: [],
|
|
688
|
-
total: 0,
|
|
689
|
-
page,
|
|
690
|
-
perPage,
|
|
691
|
-
hasMore: false,
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC LIMIT $2 OFFSET $3`;
|
|
696
|
-
const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
|
|
697
|
-
|
|
698
|
-
const threads = (rows || []).map(thread => ({
|
|
699
|
-
...thread,
|
|
700
|
-
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
701
|
-
createdAt: thread.createdAt, // Assuming already Date objects or ISO strings
|
|
702
|
-
updatedAt: thread.updatedAt,
|
|
703
|
-
}));
|
|
704
|
-
|
|
705
|
-
return {
|
|
706
|
-
threads,
|
|
707
|
-
total,
|
|
708
|
-
page,
|
|
709
|
-
perPage,
|
|
710
|
-
hasMore: currentOffset + threads.length < total,
|
|
711
|
-
};
|
|
712
|
-
} catch (error) {
|
|
713
|
-
const mastraError = new MastraError(
|
|
714
|
-
{
|
|
715
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED',
|
|
716
|
-
domain: ErrorDomain.STORAGE,
|
|
717
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
718
|
-
details: {
|
|
719
|
-
resourceId,
|
|
720
|
-
page,
|
|
721
|
-
},
|
|
722
|
-
},
|
|
723
|
-
error,
|
|
724
|
-
);
|
|
725
|
-
this.logger?.error?.(mastraError.toString());
|
|
726
|
-
this.logger?.trackException(mastraError);
|
|
727
|
-
return { threads: [], total: 0, page, perPage: perPageInput || 100, hasMore: false };
|
|
728
|
-
}
|
|
219
|
+
public async getThreadsByResourceIdPaginated(args: {
|
|
220
|
+
resourceId: string;
|
|
221
|
+
page: number;
|
|
222
|
+
perPage: number;
|
|
223
|
+
}): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
|
|
224
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
729
225
|
}
|
|
730
226
|
|
|
731
227
|
async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
|
|
732
|
-
|
|
733
|
-
await this.db.none(
|
|
734
|
-
`INSERT INTO ${this.getTableName(TABLE_THREADS)} (
|
|
735
|
-
id,
|
|
736
|
-
"resourceId",
|
|
737
|
-
title,
|
|
738
|
-
metadata,
|
|
739
|
-
"createdAt",
|
|
740
|
-
"updatedAt"
|
|
741
|
-
) VALUES ($1, $2, $3, $4, $5, $6)
|
|
742
|
-
ON CONFLICT (id) DO UPDATE SET
|
|
743
|
-
"resourceId" = EXCLUDED."resourceId",
|
|
744
|
-
title = EXCLUDED.title,
|
|
745
|
-
metadata = EXCLUDED.metadata,
|
|
746
|
-
"createdAt" = EXCLUDED."createdAt",
|
|
747
|
-
"updatedAt" = EXCLUDED."updatedAt"`,
|
|
748
|
-
[
|
|
749
|
-
thread.id,
|
|
750
|
-
thread.resourceId,
|
|
751
|
-
thread.title,
|
|
752
|
-
thread.metadata ? JSON.stringify(thread.metadata) : null,
|
|
753
|
-
thread.createdAt,
|
|
754
|
-
thread.updatedAt,
|
|
755
|
-
],
|
|
756
|
-
);
|
|
757
|
-
|
|
758
|
-
return thread;
|
|
759
|
-
} catch (error) {
|
|
760
|
-
throw new MastraError(
|
|
761
|
-
{
|
|
762
|
-
id: 'MASTRA_STORAGE_PG_STORE_SAVE_THREAD_FAILED',
|
|
763
|
-
domain: ErrorDomain.STORAGE,
|
|
764
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
765
|
-
details: {
|
|
766
|
-
threadId: thread.id,
|
|
767
|
-
},
|
|
768
|
-
},
|
|
769
|
-
error,
|
|
770
|
-
);
|
|
771
|
-
}
|
|
228
|
+
return this.stores.memory.saveThread({ thread });
|
|
772
229
|
}
|
|
773
230
|
|
|
774
231
|
async updateThread({
|
|
@@ -780,150 +237,11 @@ export class PostgresStore extends MastraStorage {
|
|
|
780
237
|
title: string;
|
|
781
238
|
metadata: Record<string, unknown>;
|
|
782
239
|
}): Promise<StorageThreadType> {
|
|
783
|
-
|
|
784
|
-
const existingThread = await this.getThreadById({ threadId: id });
|
|
785
|
-
if (!existingThread) {
|
|
786
|
-
throw new MastraError({
|
|
787
|
-
id: 'MASTRA_STORAGE_PG_STORE_UPDATE_THREAD_FAILED',
|
|
788
|
-
domain: ErrorDomain.STORAGE,
|
|
789
|
-
category: ErrorCategory.USER,
|
|
790
|
-
text: `Thread ${id} not found`,
|
|
791
|
-
details: {
|
|
792
|
-
threadId: id,
|
|
793
|
-
title,
|
|
794
|
-
},
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
// Merge the existing metadata with the new metadata
|
|
799
|
-
const mergedMetadata = {
|
|
800
|
-
...existingThread.metadata,
|
|
801
|
-
...metadata,
|
|
802
|
-
};
|
|
803
|
-
|
|
804
|
-
try {
|
|
805
|
-
const thread = await this.db.one<StorageThreadType>(
|
|
806
|
-
`UPDATE ${this.getTableName(TABLE_THREADS)}
|
|
807
|
-
SET title = $1,
|
|
808
|
-
metadata = $2,
|
|
809
|
-
"updatedAt" = $3
|
|
810
|
-
WHERE id = $4
|
|
811
|
-
RETURNING *`,
|
|
812
|
-
[title, mergedMetadata, new Date().toISOString(), id],
|
|
813
|
-
);
|
|
814
|
-
|
|
815
|
-
return {
|
|
816
|
-
...thread,
|
|
817
|
-
metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
|
|
818
|
-
createdAt: thread.createdAt,
|
|
819
|
-
updatedAt: thread.updatedAt,
|
|
820
|
-
};
|
|
821
|
-
} catch (error) {
|
|
822
|
-
throw new MastraError(
|
|
823
|
-
{
|
|
824
|
-
id: 'MASTRA_STORAGE_PG_STORE_UPDATE_THREAD_FAILED',
|
|
825
|
-
domain: ErrorDomain.STORAGE,
|
|
826
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
827
|
-
details: {
|
|
828
|
-
threadId: id,
|
|
829
|
-
title,
|
|
830
|
-
},
|
|
831
|
-
},
|
|
832
|
-
error,
|
|
833
|
-
);
|
|
834
|
-
}
|
|
240
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
835
241
|
}
|
|
836
242
|
|
|
837
243
|
async deleteThread({ threadId }: { threadId: string }): Promise<void> {
|
|
838
|
-
|
|
839
|
-
await this.db.tx(async t => {
|
|
840
|
-
// First delete all messages associated with this thread
|
|
841
|
-
await t.none(`DELETE FROM ${this.getTableName(TABLE_MESSAGES)} WHERE thread_id = $1`, [threadId]);
|
|
842
|
-
|
|
843
|
-
// Then delete the thread
|
|
844
|
-
await t.none(`DELETE FROM ${this.getTableName(TABLE_THREADS)} WHERE id = $1`, [threadId]);
|
|
845
|
-
});
|
|
846
|
-
} catch (error) {
|
|
847
|
-
throw new MastraError(
|
|
848
|
-
{
|
|
849
|
-
id: 'MASTRA_STORAGE_PG_STORE_DELETE_THREAD_FAILED',
|
|
850
|
-
domain: ErrorDomain.STORAGE,
|
|
851
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
852
|
-
details: {
|
|
853
|
-
threadId,
|
|
854
|
-
},
|
|
855
|
-
},
|
|
856
|
-
error,
|
|
857
|
-
);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
private async _getIncludedMessages({
|
|
862
|
-
threadId,
|
|
863
|
-
selectBy,
|
|
864
|
-
orderByStatement,
|
|
865
|
-
}: {
|
|
866
|
-
threadId: string;
|
|
867
|
-
selectBy: StorageGetMessagesArg['selectBy'];
|
|
868
|
-
orderByStatement: string;
|
|
869
|
-
}) {
|
|
870
|
-
const include = selectBy?.include;
|
|
871
|
-
if (!include) return null;
|
|
872
|
-
|
|
873
|
-
const unionQueries: string[] = [];
|
|
874
|
-
const params: any[] = [];
|
|
875
|
-
let paramIdx = 1;
|
|
876
|
-
|
|
877
|
-
for (const inc of include) {
|
|
878
|
-
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
879
|
-
// if threadId is provided, use it, otherwise use threadId from args
|
|
880
|
-
const searchId = inc.threadId || threadId;
|
|
881
|
-
unionQueries.push(
|
|
882
|
-
`
|
|
883
|
-
SELECT * FROM (
|
|
884
|
-
WITH ordered_messages AS (
|
|
885
|
-
SELECT
|
|
886
|
-
*,
|
|
887
|
-
ROW_NUMBER() OVER (${orderByStatement}) as row_num
|
|
888
|
-
FROM ${this.getTableName(TABLE_MESSAGES)}
|
|
889
|
-
WHERE thread_id = $${paramIdx}
|
|
890
|
-
)
|
|
891
|
-
SELECT
|
|
892
|
-
m.id,
|
|
893
|
-
m.content,
|
|
894
|
-
m.role,
|
|
895
|
-
m.type,
|
|
896
|
-
m."createdAt",
|
|
897
|
-
m.thread_id AS "threadId",
|
|
898
|
-
m."resourceId"
|
|
899
|
-
FROM ordered_messages m
|
|
900
|
-
WHERE m.id = $${paramIdx + 1}
|
|
901
|
-
OR EXISTS (
|
|
902
|
-
SELECT 1 FROM ordered_messages target
|
|
903
|
-
WHERE target.id = $${paramIdx + 1}
|
|
904
|
-
AND (
|
|
905
|
-
-- Get previous messages based on the max withPreviousMessages
|
|
906
|
-
(m.row_num <= target.row_num + $${paramIdx + 2} AND m.row_num > target.row_num)
|
|
907
|
-
OR
|
|
908
|
-
-- Get next messages based on the max withNextMessages
|
|
909
|
-
(m.row_num >= target.row_num - $${paramIdx + 3} AND m.row_num < target.row_num)
|
|
910
|
-
)
|
|
911
|
-
)
|
|
912
|
-
) AS query_${paramIdx}
|
|
913
|
-
`, // Keep ASC for final sorting after fetching context
|
|
914
|
-
);
|
|
915
|
-
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
916
|
-
paramIdx += 4;
|
|
917
|
-
}
|
|
918
|
-
const finalQuery = unionQueries.join(' UNION ALL ') + ' ORDER BY "createdAt" ASC';
|
|
919
|
-
const includedRows = await this.db.manyOrNone(finalQuery, params);
|
|
920
|
-
const seen = new Set<string>();
|
|
921
|
-
const dedupedRows = includedRows.filter(row => {
|
|
922
|
-
if (seen.has(row.id)) return false;
|
|
923
|
-
seen.add(row.id);
|
|
924
|
-
return true;
|
|
925
|
-
});
|
|
926
|
-
return dedupedRows;
|
|
244
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
927
245
|
}
|
|
928
246
|
|
|
929
247
|
/**
|
|
@@ -936,73 +254,7 @@ export class PostgresStore extends MastraStorage {
|
|
|
936
254
|
format?: 'v1' | 'v2';
|
|
937
255
|
},
|
|
938
256
|
): Promise<MastraMessageV1[] | MastraMessageV2[]> {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId"`;
|
|
942
|
-
const orderByStatement = `ORDER BY "createdAt" DESC`;
|
|
943
|
-
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
944
|
-
|
|
945
|
-
try {
|
|
946
|
-
let rows: any[] = [];
|
|
947
|
-
const include = selectBy?.include || [];
|
|
948
|
-
|
|
949
|
-
if (include?.length) {
|
|
950
|
-
const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
|
|
951
|
-
if (includeMessages) {
|
|
952
|
-
rows.push(...includeMessages);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
const excludeIds = rows.map(m => m.id);
|
|
957
|
-
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + 2}`).join(', ');
|
|
958
|
-
let query = `${selectStatement} FROM ${this.getTableName(TABLE_MESSAGES)} WHERE thread_id = $1
|
|
959
|
-
${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ''}
|
|
960
|
-
${orderByStatement}
|
|
961
|
-
LIMIT $${excludeIds.length + 2}
|
|
962
|
-
`;
|
|
963
|
-
const queryParams: any[] = [threadId, ...excludeIds, limit];
|
|
964
|
-
const remainingRows = await this.db.manyOrNone(query, queryParams);
|
|
965
|
-
rows.push(...remainingRows);
|
|
966
|
-
|
|
967
|
-
const fetchedMessages = (rows || []).map(message => {
|
|
968
|
-
if (typeof message.content === 'string') {
|
|
969
|
-
try {
|
|
970
|
-
message.content = JSON.parse(message.content);
|
|
971
|
-
} catch {
|
|
972
|
-
/* ignore */
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
if (message.type === 'v2') delete message.type;
|
|
976
|
-
return message as MastraMessageV1;
|
|
977
|
-
});
|
|
978
|
-
|
|
979
|
-
// Sort all messages by creation date
|
|
980
|
-
const sortedMessages = fetchedMessages.sort(
|
|
981
|
-
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),
|
|
982
|
-
);
|
|
983
|
-
|
|
984
|
-
return format === 'v2'
|
|
985
|
-
? sortedMessages.map(
|
|
986
|
-
m =>
|
|
987
|
-
({ ...m, content: m.content || { format: 2, parts: [{ type: 'text', text: '' }] } }) as MastraMessageV2,
|
|
988
|
-
)
|
|
989
|
-
: sortedMessages;
|
|
990
|
-
} catch (error) {
|
|
991
|
-
const mastraError = new MastraError(
|
|
992
|
-
{
|
|
993
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_MESSAGES_FAILED',
|
|
994
|
-
domain: ErrorDomain.STORAGE,
|
|
995
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
996
|
-
details: {
|
|
997
|
-
threadId,
|
|
998
|
-
},
|
|
999
|
-
},
|
|
1000
|
-
error,
|
|
1001
|
-
);
|
|
1002
|
-
this.logger?.error?.(mastraError.toString());
|
|
1003
|
-
this.logger?.trackException(mastraError);
|
|
1004
|
-
return [];
|
|
1005
|
-
}
|
|
257
|
+
return this.stores.memory.getMessages(args);
|
|
1006
258
|
}
|
|
1007
259
|
|
|
1008
260
|
public async getMessagesPaginated(
|
|
@@ -1010,220 +262,54 @@ export class PostgresStore extends MastraStorage {
|
|
|
1010
262
|
format?: 'v1' | 'v2';
|
|
1011
263
|
},
|
|
1012
264
|
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
1013
|
-
|
|
1014
|
-
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
1015
|
-
const fromDate = dateRange?.start;
|
|
1016
|
-
const toDate = dateRange?.end;
|
|
1017
|
-
|
|
1018
|
-
const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId"`;
|
|
1019
|
-
const orderByStatement = `ORDER BY "createdAt" DESC`;
|
|
1020
|
-
|
|
1021
|
-
const messages: MastraMessageV2[] = [];
|
|
1022
|
-
|
|
1023
|
-
if (selectBy?.include?.length) {
|
|
1024
|
-
const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
|
|
1025
|
-
if (includeMessages) {
|
|
1026
|
-
messages.push(...includeMessages);
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
try {
|
|
1031
|
-
const perPage =
|
|
1032
|
-
perPageInput !== undefined
|
|
1033
|
-
? perPageInput
|
|
1034
|
-
: this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1035
|
-
const currentOffset = page * perPage;
|
|
1036
|
-
|
|
1037
|
-
const conditions: string[] = [`thread_id = $1`];
|
|
1038
|
-
const queryParams: any[] = [threadId];
|
|
1039
|
-
let paramIndex = 2;
|
|
1040
|
-
|
|
1041
|
-
if (fromDate) {
|
|
1042
|
-
conditions.push(`"createdAt" >= $${paramIndex++}`);
|
|
1043
|
-
queryParams.push(fromDate);
|
|
1044
|
-
}
|
|
1045
|
-
if (toDate) {
|
|
1046
|
-
conditions.push(`"createdAt" <= $${paramIndex++}`);
|
|
1047
|
-
queryParams.push(toDate);
|
|
1048
|
-
}
|
|
1049
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
1050
|
-
|
|
1051
|
-
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_MESSAGES)} ${whereClause}`;
|
|
1052
|
-
const countResult = await this.db.one(countQuery, queryParams);
|
|
1053
|
-
const total = parseInt(countResult.count, 10);
|
|
1054
|
-
|
|
1055
|
-
if (total === 0 && messages.length === 0) {
|
|
1056
|
-
return {
|
|
1057
|
-
messages: [],
|
|
1058
|
-
total: 0,
|
|
1059
|
-
page,
|
|
1060
|
-
perPage,
|
|
1061
|
-
hasMore: false,
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
const excludeIds = messages.map(m => m.id);
|
|
1066
|
-
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + paramIndex}`).join(', ');
|
|
1067
|
-
paramIndex += excludeIds.length;
|
|
1068
|
-
|
|
1069
|
-
const dataQuery = `${selectStatement} FROM ${this.getTableName(
|
|
1070
|
-
TABLE_MESSAGES,
|
|
1071
|
-
)} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ''}${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
1072
|
-
const rows = await this.db.manyOrNone(dataQuery, [...queryParams, ...excludeIds, perPage, currentOffset]);
|
|
1073
|
-
messages.push(...(rows || []));
|
|
1074
|
-
|
|
1075
|
-
// Parse content back to objects if they were stringified during storage
|
|
1076
|
-
const messagesWithParsedContent = messages.map(message => {
|
|
1077
|
-
if (typeof message.content === 'string') {
|
|
1078
|
-
try {
|
|
1079
|
-
return { ...message, content: JSON.parse(message.content) };
|
|
1080
|
-
} catch {
|
|
1081
|
-
// If parsing fails, leave as string (V1 message)
|
|
1082
|
-
return message;
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
return message;
|
|
1086
|
-
});
|
|
1087
|
-
|
|
1088
|
-
const list = new MessageList().add(messagesWithParsedContent, 'memory');
|
|
1089
|
-
const messagesToReturn = format === `v2` ? list.get.all.v2() : list.get.all.v1();
|
|
1090
|
-
|
|
1091
|
-
return {
|
|
1092
|
-
messages: messagesToReturn,
|
|
1093
|
-
total,
|
|
1094
|
-
page,
|
|
1095
|
-
perPage,
|
|
1096
|
-
hasMore: currentOffset + rows.length < total,
|
|
1097
|
-
};
|
|
1098
|
-
} catch (error) {
|
|
1099
|
-
const mastraError = new MastraError(
|
|
1100
|
-
{
|
|
1101
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_MESSAGES_PAGINATED_FAILED',
|
|
1102
|
-
domain: ErrorDomain.STORAGE,
|
|
1103
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1104
|
-
details: {
|
|
1105
|
-
threadId,
|
|
1106
|
-
page,
|
|
1107
|
-
},
|
|
1108
|
-
},
|
|
1109
|
-
error,
|
|
1110
|
-
);
|
|
1111
|
-
this.logger?.error?.(mastraError.toString());
|
|
1112
|
-
this.logger?.trackException(mastraError);
|
|
1113
|
-
return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
|
|
1114
|
-
}
|
|
265
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
1115
266
|
}
|
|
1116
267
|
|
|
1117
268
|
async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
|
|
1118
269
|
async saveMessages(args: { messages: MastraMessageV2[]; format: 'v2' }): Promise<MastraMessageV2[]>;
|
|
1119
|
-
async saveMessages(
|
|
1120
|
-
messages,
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
| { messages: MastraMessageV2[]; format: 'v2' }): Promise<MastraMessageV2[] | MastraMessageV1[]> {
|
|
1125
|
-
if (messages.length === 0) return messages;
|
|
1126
|
-
|
|
1127
|
-
const threadId = messages[0]?.threadId;
|
|
1128
|
-
if (!threadId) {
|
|
1129
|
-
throw new MastraError({
|
|
1130
|
-
id: 'MASTRA_STORAGE_PG_STORE_SAVE_MESSAGES_FAILED',
|
|
1131
|
-
domain: ErrorDomain.STORAGE,
|
|
1132
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1133
|
-
text: `Thread ID is required`,
|
|
1134
|
-
});
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
// Check if thread exists
|
|
1138
|
-
const thread = await this.getThreadById({ threadId });
|
|
1139
|
-
if (!thread) {
|
|
1140
|
-
throw new MastraError({
|
|
1141
|
-
id: 'MASTRA_STORAGE_PG_STORE_SAVE_MESSAGES_FAILED',
|
|
1142
|
-
domain: ErrorDomain.STORAGE,
|
|
1143
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1144
|
-
text: `Thread ${threadId} not found`,
|
|
1145
|
-
details: {
|
|
1146
|
-
threadId,
|
|
1147
|
-
},
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
try {
|
|
1152
|
-
await this.db.tx(async t => {
|
|
1153
|
-
// Execute message inserts and thread update in parallel for better performance
|
|
1154
|
-
const messageInserts = messages.map(message => {
|
|
1155
|
-
if (!message.threadId) {
|
|
1156
|
-
throw new Error(
|
|
1157
|
-
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`,
|
|
1158
|
-
);
|
|
1159
|
-
}
|
|
1160
|
-
if (!message.resourceId) {
|
|
1161
|
-
throw new Error(
|
|
1162
|
-
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`,
|
|
1163
|
-
);
|
|
1164
|
-
}
|
|
1165
|
-
return t.none(
|
|
1166
|
-
`INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
|
|
1167
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
1168
|
-
ON CONFLICT (id) DO UPDATE SET
|
|
1169
|
-
thread_id = EXCLUDED.thread_id,
|
|
1170
|
-
content = EXCLUDED.content,
|
|
1171
|
-
role = EXCLUDED.role,
|
|
1172
|
-
type = EXCLUDED.type,
|
|
1173
|
-
"resourceId" = EXCLUDED."resourceId"`,
|
|
1174
|
-
[
|
|
1175
|
-
message.id,
|
|
1176
|
-
message.threadId,
|
|
1177
|
-
typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
|
|
1178
|
-
message.createdAt || new Date().toISOString(),
|
|
1179
|
-
message.role,
|
|
1180
|
-
message.type || 'v2',
|
|
1181
|
-
message.resourceId,
|
|
1182
|
-
],
|
|
1183
|
-
);
|
|
1184
|
-
});
|
|
270
|
+
async saveMessages(
|
|
271
|
+
args: { messages: MastraMessageV1[]; format?: undefined | 'v1' } | { messages: MastraMessageV2[]; format: 'v2' },
|
|
272
|
+
): Promise<MastraMessageV2[] | MastraMessageV1[]> {
|
|
273
|
+
return this.stores.memory.saveMessages(args);
|
|
274
|
+
}
|
|
1185
275
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
276
|
+
async updateMessages({
|
|
277
|
+
messages,
|
|
278
|
+
}: {
|
|
279
|
+
messages: (Partial<Omit<MastraMessageV2, 'createdAt'>> & {
|
|
280
|
+
id: string;
|
|
281
|
+
content?: {
|
|
282
|
+
metadata?: MastraMessageContentV2['metadata'];
|
|
283
|
+
content?: MastraMessageContentV2['content'];
|
|
284
|
+
};
|
|
285
|
+
})[];
|
|
286
|
+
}): Promise<MastraMessageV2[]> {
|
|
287
|
+
return this.stores.memory.updateMessages({ messages });
|
|
288
|
+
}
|
|
1192
289
|
|
|
1193
|
-
|
|
1194
|
-
|
|
290
|
+
async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
|
|
291
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
292
|
+
}
|
|
1195
293
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
try {
|
|
1200
|
-
return { ...message, content: JSON.parse(message.content) };
|
|
1201
|
-
} catch {
|
|
1202
|
-
// If parsing fails, leave as string (V1 message)
|
|
1203
|
-
return message;
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
return message;
|
|
1207
|
-
});
|
|
294
|
+
async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
|
|
295
|
+
return this.stores.memory.saveResource({ resource });
|
|
296
|
+
}
|
|
1208
297
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
threadId,
|
|
1220
|
-
},
|
|
1221
|
-
},
|
|
1222
|
-
error,
|
|
1223
|
-
);
|
|
1224
|
-
}
|
|
298
|
+
async updateResource({
|
|
299
|
+
resourceId,
|
|
300
|
+
workingMemory,
|
|
301
|
+
metadata,
|
|
302
|
+
}: {
|
|
303
|
+
resourceId: string;
|
|
304
|
+
workingMemory?: string;
|
|
305
|
+
metadata?: Record<string, unknown>;
|
|
306
|
+
}): Promise<StorageResourceType> {
|
|
307
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
1225
308
|
}
|
|
1226
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Workflows
|
|
312
|
+
*/
|
|
1227
313
|
async persistWorkflowSnapshot({
|
|
1228
314
|
workflowName,
|
|
1229
315
|
runId,
|
|
@@ -1233,35 +319,7 @@ export class PostgresStore extends MastraStorage {
|
|
|
1233
319
|
runId: string;
|
|
1234
320
|
snapshot: WorkflowRunState;
|
|
1235
321
|
}): Promise<void> {
|
|
1236
|
-
|
|
1237
|
-
const now = new Date().toISOString();
|
|
1238
|
-
await this.db.none(
|
|
1239
|
-
`INSERT INTO ${this.getTableName(TABLE_WORKFLOW_SNAPSHOT)} (
|
|
1240
|
-
workflow_name,
|
|
1241
|
-
run_id,
|
|
1242
|
-
snapshot,
|
|
1243
|
-
"createdAt",
|
|
1244
|
-
"updatedAt"
|
|
1245
|
-
) VALUES ($1, $2, $3, $4, $5)
|
|
1246
|
-
ON CONFLICT (workflow_name, run_id) DO UPDATE
|
|
1247
|
-
SET snapshot = EXCLUDED.snapshot,
|
|
1248
|
-
"updatedAt" = EXCLUDED."updatedAt"`,
|
|
1249
|
-
[workflowName, runId, JSON.stringify(snapshot), now, now],
|
|
1250
|
-
);
|
|
1251
|
-
} catch (error) {
|
|
1252
|
-
throw new MastraError(
|
|
1253
|
-
{
|
|
1254
|
-
id: 'MASTRA_STORAGE_PG_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED',
|
|
1255
|
-
domain: ErrorDomain.STORAGE,
|
|
1256
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1257
|
-
details: {
|
|
1258
|
-
workflowName,
|
|
1259
|
-
runId,
|
|
1260
|
-
},
|
|
1261
|
-
},
|
|
1262
|
-
error,
|
|
1263
|
-
);
|
|
1264
|
-
}
|
|
322
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
1265
323
|
}
|
|
1266
324
|
|
|
1267
325
|
async loadWorkflowSnapshot({
|
|
@@ -1271,65 +329,7 @@ export class PostgresStore extends MastraStorage {
|
|
|
1271
329
|
workflowName: string;
|
|
1272
330
|
runId: string;
|
|
1273
331
|
}): Promise<WorkflowRunState | null> {
|
|
1274
|
-
|
|
1275
|
-
const result = await this.load({
|
|
1276
|
-
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1277
|
-
keys: {
|
|
1278
|
-
workflow_name: workflowName,
|
|
1279
|
-
run_id: runId,
|
|
1280
|
-
},
|
|
1281
|
-
});
|
|
1282
|
-
|
|
1283
|
-
if (!result) {
|
|
1284
|
-
return null;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
return (result as any).snapshot;
|
|
1288
|
-
} catch (error) {
|
|
1289
|
-
throw new MastraError(
|
|
1290
|
-
{
|
|
1291
|
-
id: 'MASTRA_STORAGE_PG_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED',
|
|
1292
|
-
domain: ErrorDomain.STORAGE,
|
|
1293
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1294
|
-
details: {
|
|
1295
|
-
workflowName,
|
|
1296
|
-
runId,
|
|
1297
|
-
},
|
|
1298
|
-
},
|
|
1299
|
-
error,
|
|
1300
|
-
);
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
private async hasColumn(table: string, column: string): Promise<boolean> {
|
|
1305
|
-
// Use this.schema to scope the check
|
|
1306
|
-
const schema = this.schema || 'public';
|
|
1307
|
-
const result = await this.db.oneOrNone(
|
|
1308
|
-
`SELECT 1 FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND (column_name = $3 OR column_name = $4)`,
|
|
1309
|
-
[schema, table, column, column.toLowerCase()],
|
|
1310
|
-
);
|
|
1311
|
-
return !!result;
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
private parseWorkflowRun(row: any): WorkflowRun {
|
|
1315
|
-
let parsedSnapshot: WorkflowRunState | string = row.snapshot as string;
|
|
1316
|
-
if (typeof parsedSnapshot === 'string') {
|
|
1317
|
-
try {
|
|
1318
|
-
parsedSnapshot = JSON.parse(row.snapshot as string) as WorkflowRunState;
|
|
1319
|
-
} catch (e) {
|
|
1320
|
-
// If parsing fails, return the raw snapshot string
|
|
1321
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
return {
|
|
1326
|
-
workflowName: row.workflow_name,
|
|
1327
|
-
runId: row.run_id,
|
|
1328
|
-
snapshot: parsedSnapshot,
|
|
1329
|
-
createdAt: row.createdAt,
|
|
1330
|
-
updatedAt: row.updatedAt,
|
|
1331
|
-
resourceId: row.resourceId,
|
|
1332
|
-
};
|
|
332
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
1333
333
|
}
|
|
1334
334
|
|
|
1335
335
|
async getWorkflowRuns({
|
|
@@ -1347,82 +347,7 @@ export class PostgresStore extends MastraStorage {
|
|
|
1347
347
|
offset?: number;
|
|
1348
348
|
resourceId?: string;
|
|
1349
349
|
} = {}): Promise<WorkflowRuns> {
|
|
1350
|
-
|
|
1351
|
-
const conditions: string[] = [];
|
|
1352
|
-
const values: any[] = [];
|
|
1353
|
-
let paramIndex = 1;
|
|
1354
|
-
|
|
1355
|
-
if (workflowName) {
|
|
1356
|
-
conditions.push(`workflow_name = $${paramIndex}`);
|
|
1357
|
-
values.push(workflowName);
|
|
1358
|
-
paramIndex++;
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
if (resourceId) {
|
|
1362
|
-
const hasResourceId = await this.hasColumn(TABLE_WORKFLOW_SNAPSHOT, 'resourceId');
|
|
1363
|
-
if (hasResourceId) {
|
|
1364
|
-
conditions.push(`"resourceId" = $${paramIndex}`);
|
|
1365
|
-
values.push(resourceId);
|
|
1366
|
-
paramIndex++;
|
|
1367
|
-
} else {
|
|
1368
|
-
console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
if (fromDate) {
|
|
1373
|
-
conditions.push(`"createdAt" >= $${paramIndex}`);
|
|
1374
|
-
values.push(fromDate);
|
|
1375
|
-
paramIndex++;
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
if (toDate) {
|
|
1379
|
-
conditions.push(`"createdAt" <= $${paramIndex}`);
|
|
1380
|
-
values.push(toDate);
|
|
1381
|
-
paramIndex++;
|
|
1382
|
-
}
|
|
1383
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
1384
|
-
|
|
1385
|
-
let total = 0;
|
|
1386
|
-
// Only get total count when using pagination
|
|
1387
|
-
if (limit !== undefined && offset !== undefined) {
|
|
1388
|
-
const countResult = await this.db.one(
|
|
1389
|
-
`SELECT COUNT(*) as count FROM ${this.getTableName(TABLE_WORKFLOW_SNAPSHOT)} ${whereClause}`,
|
|
1390
|
-
values,
|
|
1391
|
-
);
|
|
1392
|
-
total = Number(countResult.count);
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
// Get results
|
|
1396
|
-
const query = `
|
|
1397
|
-
SELECT * FROM ${this.getTableName(TABLE_WORKFLOW_SNAPSHOT)}
|
|
1398
|
-
${whereClause}
|
|
1399
|
-
ORDER BY "createdAt" DESC
|
|
1400
|
-
${limit !== undefined && offset !== undefined ? ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}` : ''}
|
|
1401
|
-
`;
|
|
1402
|
-
|
|
1403
|
-
const queryValues = limit !== undefined && offset !== undefined ? [...values, limit, offset] : values;
|
|
1404
|
-
|
|
1405
|
-
const result = await this.db.manyOrNone(query, queryValues);
|
|
1406
|
-
|
|
1407
|
-
const runs = (result || []).map(row => {
|
|
1408
|
-
return this.parseWorkflowRun(row);
|
|
1409
|
-
});
|
|
1410
|
-
|
|
1411
|
-
// Use runs.length as total when not paginating
|
|
1412
|
-
return { runs, total: total || runs.length };
|
|
1413
|
-
} catch (error) {
|
|
1414
|
-
throw new MastraError(
|
|
1415
|
-
{
|
|
1416
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_WORKFLOW_RUNS_FAILED',
|
|
1417
|
-
domain: ErrorDomain.STORAGE,
|
|
1418
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1419
|
-
details: {
|
|
1420
|
-
workflowName: workflowName || 'all',
|
|
1421
|
-
},
|
|
1422
|
-
},
|
|
1423
|
-
error,
|
|
1424
|
-
);
|
|
1425
|
-
}
|
|
350
|
+
return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
1426
351
|
}
|
|
1427
352
|
|
|
1428
353
|
async getWorkflowRunById({
|
|
@@ -1432,370 +357,57 @@ export class PostgresStore extends MastraStorage {
|
|
|
1432
357
|
runId: string;
|
|
1433
358
|
workflowName?: string;
|
|
1434
359
|
}): Promise<WorkflowRun | null> {
|
|
1435
|
-
|
|
1436
|
-
const conditions: string[] = [];
|
|
1437
|
-
const values: any[] = [];
|
|
1438
|
-
let paramIndex = 1;
|
|
1439
|
-
|
|
1440
|
-
if (runId) {
|
|
1441
|
-
conditions.push(`run_id = $${paramIndex}`);
|
|
1442
|
-
values.push(runId);
|
|
1443
|
-
paramIndex++;
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
if (workflowName) {
|
|
1447
|
-
conditions.push(`workflow_name = $${paramIndex}`);
|
|
1448
|
-
values.push(workflowName);
|
|
1449
|
-
paramIndex++;
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
1453
|
-
|
|
1454
|
-
// Get results
|
|
1455
|
-
const query = `
|
|
1456
|
-
SELECT * FROM ${this.getTableName(TABLE_WORKFLOW_SNAPSHOT)}
|
|
1457
|
-
${whereClause}
|
|
1458
|
-
`;
|
|
1459
|
-
|
|
1460
|
-
const queryValues = values;
|
|
1461
|
-
|
|
1462
|
-
const result = await this.db.oneOrNone(query, queryValues);
|
|
1463
|
-
|
|
1464
|
-
if (!result) {
|
|
1465
|
-
return null;
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
return this.parseWorkflowRun(result);
|
|
1469
|
-
} catch (error) {
|
|
1470
|
-
throw new MastraError(
|
|
1471
|
-
{
|
|
1472
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED',
|
|
1473
|
-
domain: ErrorDomain.STORAGE,
|
|
1474
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1475
|
-
details: {
|
|
1476
|
-
runId,
|
|
1477
|
-
workflowName: workflowName || '',
|
|
1478
|
-
},
|
|
1479
|
-
},
|
|
1480
|
-
error,
|
|
1481
|
-
);
|
|
1482
|
-
}
|
|
360
|
+
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
1483
361
|
}
|
|
1484
362
|
|
|
1485
363
|
async close(): Promise<void> {
|
|
1486
364
|
this.pgp.end();
|
|
1487
365
|
}
|
|
1488
366
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
): Promise<PaginationInfo & { evals: EvalRow[] }> {
|
|
1495
|
-
const { agentName, type, page = 0, perPage = 100, dateRange } = options;
|
|
1496
|
-
const fromDate = dateRange?.start;
|
|
1497
|
-
const toDate = dateRange?.end;
|
|
1498
|
-
|
|
1499
|
-
const conditions: string[] = [];
|
|
1500
|
-
const queryParams: any[] = [];
|
|
1501
|
-
let paramIndex = 1;
|
|
1502
|
-
|
|
1503
|
-
if (agentName) {
|
|
1504
|
-
conditions.push(`agent_name = $${paramIndex++}`);
|
|
1505
|
-
queryParams.push(agentName);
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
if (type === 'test') {
|
|
1509
|
-
conditions.push(`(test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL)`);
|
|
1510
|
-
} else if (type === 'live') {
|
|
1511
|
-
conditions.push(`(test_info IS NULL OR test_info->>'testPath' IS NULL)`);
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
if (fromDate) {
|
|
1515
|
-
conditions.push(`created_at >= $${paramIndex++}`);
|
|
1516
|
-
queryParams.push(fromDate);
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
if (toDate) {
|
|
1520
|
-
conditions.push(`created_at <= $${paramIndex++}`);
|
|
1521
|
-
queryParams.push(toDate);
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
1525
|
-
|
|
1526
|
-
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_EVALS)} ${whereClause}`;
|
|
1527
|
-
try {
|
|
1528
|
-
const countResult = await this.db.one(countQuery, queryParams);
|
|
1529
|
-
const total = parseInt(countResult.count, 10);
|
|
1530
|
-
const currentOffset = page * perPage;
|
|
1531
|
-
|
|
1532
|
-
if (total === 0) {
|
|
1533
|
-
return {
|
|
1534
|
-
evals: [],
|
|
1535
|
-
total: 0,
|
|
1536
|
-
page,
|
|
1537
|
-
perPage,
|
|
1538
|
-
hasMore: false,
|
|
1539
|
-
};
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
const dataQuery = `SELECT * FROM ${this.getTableName(
|
|
1543
|
-
TABLE_EVALS,
|
|
1544
|
-
)} ${whereClause} ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
1545
|
-
const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
|
|
1546
|
-
|
|
1547
|
-
return {
|
|
1548
|
-
evals: rows?.map(row => this.transformEvalRow(row)) ?? [],
|
|
1549
|
-
total,
|
|
1550
|
-
page,
|
|
1551
|
-
perPage,
|
|
1552
|
-
hasMore: currentOffset + (rows?.length ?? 0) < total,
|
|
1553
|
-
};
|
|
1554
|
-
} catch (error) {
|
|
1555
|
-
const mastraError = new MastraError(
|
|
1556
|
-
{
|
|
1557
|
-
id: 'MASTRA_STORAGE_PG_STORE_GET_EVALS_FAILED',
|
|
1558
|
-
domain: ErrorDomain.STORAGE,
|
|
1559
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1560
|
-
details: {
|
|
1561
|
-
agentName: agentName || 'all',
|
|
1562
|
-
type: type || 'all',
|
|
1563
|
-
page,
|
|
1564
|
-
perPage,
|
|
1565
|
-
},
|
|
1566
|
-
},
|
|
1567
|
-
error,
|
|
1568
|
-
);
|
|
1569
|
-
this.logger?.error?.(mastraError.toString());
|
|
1570
|
-
this.logger?.trackException(mastraError);
|
|
1571
|
-
throw mastraError;
|
|
1572
|
-
}
|
|
367
|
+
/**
|
|
368
|
+
* Scorers
|
|
369
|
+
*/
|
|
370
|
+
async getScoreById({ id: _id }: { id: string }): Promise<ScoreRowData | null> {
|
|
371
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
1573
372
|
}
|
|
1574
373
|
|
|
1575
|
-
async
|
|
1576
|
-
|
|
374
|
+
async getScoresByScorerId({
|
|
375
|
+
scorerId: _scorerId,
|
|
376
|
+
pagination: _pagination,
|
|
1577
377
|
}: {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
content?: MastraMessageContentV2['content'];
|
|
1583
|
-
};
|
|
1584
|
-
})[];
|
|
1585
|
-
}): Promise<MastraMessageV2[]> {
|
|
1586
|
-
if (messages.length === 0) {
|
|
1587
|
-
return [];
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
const messageIds = messages.map(m => m.id);
|
|
1591
|
-
|
|
1592
|
-
const selectQuery = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId" FROM ${this.getTableName(
|
|
1593
|
-
TABLE_MESSAGES,
|
|
1594
|
-
)} WHERE id IN ($1:list)`;
|
|
1595
|
-
|
|
1596
|
-
const existingMessagesDb = await this.db.manyOrNone(selectQuery, [messageIds]);
|
|
1597
|
-
|
|
1598
|
-
if (existingMessagesDb.length === 0) {
|
|
1599
|
-
return [];
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
// Parse content from string to object for merging
|
|
1603
|
-
const existingMessages: MastraMessageV2[] = existingMessagesDb.map(msg => {
|
|
1604
|
-
if (typeof msg.content === 'string') {
|
|
1605
|
-
try {
|
|
1606
|
-
msg.content = JSON.parse(msg.content);
|
|
1607
|
-
} catch {
|
|
1608
|
-
// ignore if not valid json
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
return msg as MastraMessageV2;
|
|
1612
|
-
});
|
|
1613
|
-
|
|
1614
|
-
const threadIdsToUpdate = new Set<string>();
|
|
1615
|
-
|
|
1616
|
-
await this.db.tx(async t => {
|
|
1617
|
-
const queries = [];
|
|
1618
|
-
const columnMapping: Record<string, string> = {
|
|
1619
|
-
threadId: 'thread_id',
|
|
1620
|
-
};
|
|
1621
|
-
|
|
1622
|
-
for (const existingMessage of existingMessages) {
|
|
1623
|
-
const updatePayload = messages.find(m => m.id === existingMessage.id);
|
|
1624
|
-
if (!updatePayload) continue;
|
|
1625
|
-
|
|
1626
|
-
const { id, ...fieldsToUpdate } = updatePayload;
|
|
1627
|
-
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
1628
|
-
|
|
1629
|
-
threadIdsToUpdate.add(existingMessage.threadId!);
|
|
1630
|
-
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
1631
|
-
threadIdsToUpdate.add(updatePayload.threadId);
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
const setClauses: string[] = [];
|
|
1635
|
-
const values: any[] = [];
|
|
1636
|
-
let paramIndex = 1;
|
|
1637
|
-
|
|
1638
|
-
const updatableFields = { ...fieldsToUpdate };
|
|
1639
|
-
|
|
1640
|
-
// Special handling for content: merge in code, then update the whole field
|
|
1641
|
-
if (updatableFields.content) {
|
|
1642
|
-
const newContent = {
|
|
1643
|
-
...existingMessage.content,
|
|
1644
|
-
...updatableFields.content,
|
|
1645
|
-
// Deep merge metadata if it exists on both
|
|
1646
|
-
...(existingMessage.content?.metadata && updatableFields.content.metadata
|
|
1647
|
-
? {
|
|
1648
|
-
metadata: {
|
|
1649
|
-
...existingMessage.content.metadata,
|
|
1650
|
-
...updatableFields.content.metadata,
|
|
1651
|
-
},
|
|
1652
|
-
}
|
|
1653
|
-
: {}),
|
|
1654
|
-
};
|
|
1655
|
-
setClauses.push(`content = $${paramIndex++}`);
|
|
1656
|
-
values.push(newContent);
|
|
1657
|
-
delete updatableFields.content;
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
for (const key in updatableFields) {
|
|
1661
|
-
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
1662
|
-
const dbColumn = columnMapping[key] || key;
|
|
1663
|
-
setClauses.push(`"${dbColumn}" = $${paramIndex++}`);
|
|
1664
|
-
values.push(updatableFields[key as keyof typeof updatableFields]);
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
if (setClauses.length > 0) {
|
|
1669
|
-
values.push(id);
|
|
1670
|
-
const sql = `UPDATE ${this.getTableName(
|
|
1671
|
-
TABLE_MESSAGES,
|
|
1672
|
-
)} SET ${setClauses.join(', ')} WHERE id = $${paramIndex}`;
|
|
1673
|
-
queries.push(t.none(sql, values));
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
if (threadIdsToUpdate.size > 0) {
|
|
1678
|
-
queries.push(
|
|
1679
|
-
t.none(`UPDATE ${this.getTableName(TABLE_THREADS)} SET "updatedAt" = NOW() WHERE id IN ($1:list)`, [
|
|
1680
|
-
Array.from(threadIdsToUpdate),
|
|
1681
|
-
]),
|
|
1682
|
-
);
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
if (queries.length > 0) {
|
|
1686
|
-
await t.batch(queries);
|
|
1687
|
-
}
|
|
1688
|
-
});
|
|
1689
|
-
|
|
1690
|
-
// Re-fetch to return the fully updated messages
|
|
1691
|
-
const updatedMessages = await this.db.manyOrNone<MastraMessageV2>(selectQuery, [messageIds]);
|
|
1692
|
-
|
|
1693
|
-
return (updatedMessages || []).map(message => {
|
|
1694
|
-
if (typeof message.content === 'string') {
|
|
1695
|
-
try {
|
|
1696
|
-
message.content = JSON.parse(message.content);
|
|
1697
|
-
} catch {
|
|
1698
|
-
/* ignore */
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
return message;
|
|
1702
|
-
});
|
|
378
|
+
scorerId: string;
|
|
379
|
+
pagination: StoragePagination;
|
|
380
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
381
|
+
return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
|
|
1703
382
|
}
|
|
1704
383
|
|
|
1705
|
-
async
|
|
1706
|
-
|
|
1707
|
-
const result = await this.db.oneOrNone<StorageResourceType>(`SELECT * FROM ${tableName} WHERE id = $1`, [
|
|
1708
|
-
resourceId,
|
|
1709
|
-
]);
|
|
1710
|
-
|
|
1711
|
-
if (!result) {
|
|
1712
|
-
return null;
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
return {
|
|
1716
|
-
...result,
|
|
1717
|
-
// Ensure workingMemory is always returned as a string, regardless of automatic parsing
|
|
1718
|
-
workingMemory:
|
|
1719
|
-
typeof result.workingMemory === 'object' ? JSON.stringify(result.workingMemory) : result.workingMemory,
|
|
1720
|
-
metadata: typeof result.metadata === 'string' ? JSON.parse(result.metadata) : result.metadata,
|
|
1721
|
-
};
|
|
384
|
+
async saveScore(_score: ScoreRowData): Promise<{ score: ScoreRowData }> {
|
|
385
|
+
return this.stores.scores.saveScore(_score);
|
|
1722
386
|
}
|
|
1723
387
|
|
|
1724
|
-
async
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
JSON.stringify(resource.metadata),
|
|
1733
|
-
resource.createdAt.toISOString(),
|
|
1734
|
-
resource.updatedAt.toISOString(),
|
|
1735
|
-
],
|
|
1736
|
-
);
|
|
1737
|
-
|
|
1738
|
-
return resource;
|
|
388
|
+
async getScoresByRunId({
|
|
389
|
+
runId: _runId,
|
|
390
|
+
pagination: _pagination,
|
|
391
|
+
}: {
|
|
392
|
+
runId: string;
|
|
393
|
+
pagination: StoragePagination;
|
|
394
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
395
|
+
return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
|
|
1739
396
|
}
|
|
1740
397
|
|
|
1741
|
-
async
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
398
|
+
async getScoresByEntityId({
|
|
399
|
+
entityId: _entityId,
|
|
400
|
+
entityType: _entityType,
|
|
401
|
+
pagination: _pagination,
|
|
1745
402
|
}: {
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
}): Promise<
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
id: resourceId,
|
|
1756
|
-
workingMemory,
|
|
1757
|
-
metadata: metadata || {},
|
|
1758
|
-
createdAt: new Date(),
|
|
1759
|
-
updatedAt: new Date(),
|
|
1760
|
-
};
|
|
1761
|
-
return this.saveResource({ resource: newResource });
|
|
1762
|
-
}
|
|
1763
|
-
|
|
1764
|
-
const updatedResource = {
|
|
1765
|
-
...existingResource,
|
|
1766
|
-
workingMemory: workingMemory !== undefined ? workingMemory : existingResource.workingMemory,
|
|
1767
|
-
metadata: {
|
|
1768
|
-
...existingResource.metadata,
|
|
1769
|
-
...metadata,
|
|
1770
|
-
},
|
|
1771
|
-
updatedAt: new Date(),
|
|
1772
|
-
};
|
|
1773
|
-
|
|
1774
|
-
const tableName = this.getTableName(TABLE_RESOURCES);
|
|
1775
|
-
const updates: string[] = [];
|
|
1776
|
-
const values: any[] = [];
|
|
1777
|
-
let paramIndex = 1;
|
|
1778
|
-
|
|
1779
|
-
if (workingMemory !== undefined) {
|
|
1780
|
-
updates.push(`"workingMemory" = $${paramIndex}`);
|
|
1781
|
-
values.push(workingMemory);
|
|
1782
|
-
paramIndex++;
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
if (metadata) {
|
|
1786
|
-
updates.push(`metadata = $${paramIndex}`);
|
|
1787
|
-
values.push(JSON.stringify(updatedResource.metadata));
|
|
1788
|
-
paramIndex++;
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
updates.push(`"updatedAt" = $${paramIndex}`);
|
|
1792
|
-
values.push(updatedResource.updatedAt.toISOString());
|
|
1793
|
-
paramIndex++;
|
|
1794
|
-
|
|
1795
|
-
values.push(resourceId);
|
|
1796
|
-
|
|
1797
|
-
await this.db.none(`UPDATE ${tableName} SET ${updates.join(', ')} WHERE id = $${paramIndex}`, values);
|
|
1798
|
-
|
|
1799
|
-
return updatedResource;
|
|
403
|
+
pagination: StoragePagination;
|
|
404
|
+
entityId: string;
|
|
405
|
+
entityType: string;
|
|
406
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
407
|
+
return this.stores.scores.getScoresByEntityId({
|
|
408
|
+
entityId: _entityId,
|
|
409
|
+
entityType: _entityType,
|
|
410
|
+
pagination: _pagination,
|
|
411
|
+
});
|
|
1800
412
|
}
|
|
1801
413
|
}
|