@mastra/pg 0.16.1 → 0.17.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/dist/index.cjs CHANGED
@@ -10,6 +10,7 @@ var filter = require('@mastra/core/vector/filter');
10
10
  var storage = require('@mastra/core/storage');
11
11
  var pgPromise = require('pg-promise');
12
12
  var agent = require('@mastra/core/agent');
13
+ var scores = require('@mastra/core/scores');
13
14
 
14
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
16
 
@@ -1266,6 +1267,68 @@ function getTableName({ indexName, schemaName }) {
1266
1267
  const quotedSchemaName = schemaName;
1267
1268
  return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
1268
1269
  }
1270
+ function buildDateRangeFilter(dateRange, fieldName) {
1271
+ const filters = {};
1272
+ if (dateRange?.start) {
1273
+ filters[`${fieldName}_gte`] = dateRange.start;
1274
+ }
1275
+ if (dateRange?.end) {
1276
+ filters[`${fieldName}_lte`] = dateRange.end;
1277
+ }
1278
+ return filters;
1279
+ }
1280
+ function prepareWhereClause(filters, _schema) {
1281
+ const conditions = [];
1282
+ const args = [];
1283
+ let paramIndex = 1;
1284
+ Object.entries(filters).forEach(([key, value]) => {
1285
+ if (value === void 0) return;
1286
+ if (key.endsWith("_gte")) {
1287
+ const fieldName = key.slice(0, -4);
1288
+ conditions.push(`"${utils.parseSqlIdentifier(fieldName, "field name")}" >= $${paramIndex++}`);
1289
+ args.push(value instanceof Date ? value.toISOString() : value);
1290
+ } else if (key.endsWith("_lte")) {
1291
+ const fieldName = key.slice(0, -4);
1292
+ conditions.push(`"${utils.parseSqlIdentifier(fieldName, "field name")}" <= $${paramIndex++}`);
1293
+ args.push(value instanceof Date ? value.toISOString() : value);
1294
+ } else if (value === null) {
1295
+ conditions.push(`"${utils.parseSqlIdentifier(key, "field name")}" IS NULL`);
1296
+ } else {
1297
+ conditions.push(`"${utils.parseSqlIdentifier(key, "field name")}" = $${paramIndex++}`);
1298
+ args.push(value instanceof Date ? value.toISOString() : value);
1299
+ }
1300
+ });
1301
+ return {
1302
+ sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
1303
+ args
1304
+ };
1305
+ }
1306
+ function transformFromSqlRow({
1307
+ tableName,
1308
+ sqlRow
1309
+ }) {
1310
+ const schema = storage.TABLE_SCHEMAS[tableName];
1311
+ const result = {};
1312
+ Object.entries(sqlRow).forEach(([key, value]) => {
1313
+ const columnSchema = schema?.[key];
1314
+ if (columnSchema?.type === "jsonb" && typeof value === "string") {
1315
+ try {
1316
+ result[key] = JSON.parse(value);
1317
+ } catch {
1318
+ result[key] = value;
1319
+ }
1320
+ } else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
1321
+ result[key] = new Date(value);
1322
+ } else if (columnSchema?.type === "timestamp" && value instanceof Date) {
1323
+ result[key] = value;
1324
+ } else if (columnSchema?.type === "boolean") {
1325
+ result[key] = Boolean(value);
1326
+ } else {
1327
+ result[key] = value;
1328
+ }
1329
+ });
1330
+ return result;
1331
+ }
1269
1332
 
1270
1333
  // src/storage/domains/legacy-evals/index.ts
1271
1334
  function transformEvalRow(row) {
@@ -2201,6 +2264,316 @@ var MemoryPG = class extends storage.MemoryStorage {
2201
2264
  return updatedResource;
2202
2265
  }
2203
2266
  };
2267
+ var ObservabilityPG = class extends storage.ObservabilityStorage {
2268
+ client;
2269
+ operations;
2270
+ schema;
2271
+ constructor({
2272
+ client,
2273
+ operations,
2274
+ schema
2275
+ }) {
2276
+ super();
2277
+ this.client = client;
2278
+ this.operations = operations;
2279
+ this.schema = schema;
2280
+ }
2281
+ get aiTracingStrategy() {
2282
+ return {
2283
+ preferred: "batch-with-updates",
2284
+ supported: ["batch-with-updates", "insert-only"]
2285
+ };
2286
+ }
2287
+ async createAISpan(span) {
2288
+ try {
2289
+ const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
2290
+ const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
2291
+ const record = {
2292
+ ...span,
2293
+ startedAt,
2294
+ endedAt,
2295
+ startedAtZ: startedAt,
2296
+ endedAtZ: endedAt
2297
+ // Note: createdAt/updatedAt will be set by database triggers
2298
+ };
2299
+ return this.operations.insert({ tableName: storage.TABLE_AI_SPANS, record });
2300
+ } catch (error$1) {
2301
+ throw new error.MastraError(
2302
+ {
2303
+ id: "PG_STORE_CREATE_AI_SPAN_FAILED",
2304
+ domain: error.ErrorDomain.STORAGE,
2305
+ category: error.ErrorCategory.USER,
2306
+ details: {
2307
+ spanId: span.spanId,
2308
+ traceId: span.traceId,
2309
+ spanType: span.spanType,
2310
+ spanName: span.name
2311
+ }
2312
+ },
2313
+ error$1
2314
+ );
2315
+ }
2316
+ }
2317
+ async getAITrace(traceId) {
2318
+ try {
2319
+ const tableName = getTableName({
2320
+ indexName: storage.TABLE_AI_SPANS,
2321
+ schemaName: getSchemaName(this.schema)
2322
+ });
2323
+ const spans = await this.client.manyOrNone(
2324
+ `SELECT
2325
+ "traceId", "spanId", "parentSpanId", "name", "scope", "spanType",
2326
+ "attributes", "metadata", "links", "input", "output", "error", "isEvent",
2327
+ "startedAtZ" as "startedAt", "endedAtZ" as "endedAt",
2328
+ "createdAtZ" as "createdAt", "updatedAtZ" as "updatedAt"
2329
+ FROM ${tableName}
2330
+ WHERE "traceId" = $1
2331
+ ORDER BY "startedAtZ" DESC`,
2332
+ [traceId]
2333
+ );
2334
+ if (!spans || spans.length === 0) {
2335
+ return null;
2336
+ }
2337
+ return {
2338
+ traceId,
2339
+ spans: spans.map(
2340
+ (span) => transformFromSqlRow({
2341
+ tableName: storage.TABLE_AI_SPANS,
2342
+ sqlRow: span
2343
+ })
2344
+ )
2345
+ };
2346
+ } catch (error$1) {
2347
+ throw new error.MastraError(
2348
+ {
2349
+ id: "PG_STORE_GET_AI_TRACE_FAILED",
2350
+ domain: error.ErrorDomain.STORAGE,
2351
+ category: error.ErrorCategory.USER,
2352
+ details: {
2353
+ traceId
2354
+ }
2355
+ },
2356
+ error$1
2357
+ );
2358
+ }
2359
+ }
2360
+ async updateAISpan({
2361
+ spanId,
2362
+ traceId,
2363
+ updates
2364
+ }) {
2365
+ try {
2366
+ const data = { ...updates };
2367
+ if (data.endedAt instanceof Date) {
2368
+ data.endedAt = data.endedAt.toISOString();
2369
+ }
2370
+ if (data.startedAt instanceof Date) {
2371
+ data.startedAt = data.startedAt.toISOString();
2372
+ }
2373
+ await this.operations.update({
2374
+ tableName: storage.TABLE_AI_SPANS,
2375
+ keys: { spanId, traceId },
2376
+ data
2377
+ });
2378
+ } catch (error$1) {
2379
+ throw new error.MastraError(
2380
+ {
2381
+ id: "PG_STORE_UPDATE_AI_SPAN_FAILED",
2382
+ domain: error.ErrorDomain.STORAGE,
2383
+ category: error.ErrorCategory.USER,
2384
+ details: {
2385
+ spanId,
2386
+ traceId
2387
+ }
2388
+ },
2389
+ error$1
2390
+ );
2391
+ }
2392
+ }
2393
+ async getAITracesPaginated({
2394
+ filters,
2395
+ pagination
2396
+ }) {
2397
+ const page = pagination?.page ?? 0;
2398
+ const perPage = pagination?.perPage ?? 10;
2399
+ const { entityId, entityType, ...actualFilters } = filters || {};
2400
+ const filtersWithDateRange = {
2401
+ ...actualFilters,
2402
+ ...buildDateRangeFilter(pagination?.dateRange, "startedAtZ"),
2403
+ parentSpanId: null
2404
+ // Only get root spans for traces
2405
+ };
2406
+ const whereClause = prepareWhereClause(filtersWithDateRange);
2407
+ let actualWhereClause = whereClause.sql;
2408
+ let currentParamIndex = whereClause.args.length + 1;
2409
+ if (entityId && entityType) {
2410
+ let name = "";
2411
+ if (entityType === "workflow") {
2412
+ name = `workflow run: '${entityId}'`;
2413
+ } else if (entityType === "agent") {
2414
+ name = `agent run: '${entityId}'`;
2415
+ } else {
2416
+ const error$1 = new error.MastraError({
2417
+ id: "PG_STORE_GET_AI_TRACES_PAGINATED_FAILED",
2418
+ domain: error.ErrorDomain.STORAGE,
2419
+ category: error.ErrorCategory.USER,
2420
+ details: {
2421
+ entityType
2422
+ },
2423
+ text: `Cannot filter by entity type: ${entityType}`
2424
+ });
2425
+ this.logger?.trackException(error$1);
2426
+ throw error$1;
2427
+ }
2428
+ whereClause.args.push(name);
2429
+ const statement = `"name" = $${currentParamIndex++}`;
2430
+ if (actualWhereClause) {
2431
+ actualWhereClause += ` AND ${statement}`;
2432
+ } else {
2433
+ actualWhereClause = ` WHERE ${statement}`;
2434
+ }
2435
+ }
2436
+ const tableName = getTableName({
2437
+ indexName: storage.TABLE_AI_SPANS,
2438
+ schemaName: getSchemaName(this.schema)
2439
+ });
2440
+ try {
2441
+ const countResult = await this.client.oneOrNone(
2442
+ `SELECT COUNT(*) FROM ${tableName}${actualWhereClause}`,
2443
+ whereClause.args
2444
+ );
2445
+ const count = Number(countResult?.count ?? 0);
2446
+ if (count === 0) {
2447
+ return {
2448
+ pagination: {
2449
+ total: 0,
2450
+ page,
2451
+ perPage,
2452
+ hasMore: false
2453
+ },
2454
+ spans: []
2455
+ };
2456
+ }
2457
+ const spans = await this.client.manyOrNone(
2458
+ `SELECT
2459
+ "traceId", "spanId", "parentSpanId", "name", "scope", "spanType",
2460
+ "attributes", "metadata", "links", "input", "output", "error", "isEvent",
2461
+ "startedAtZ" as "startedAt", "endedAtZ" as "endedAt",
2462
+ "createdAtZ" as "createdAt", "updatedAtZ" as "updatedAt"
2463
+ FROM ${tableName}${actualWhereClause}
2464
+ ORDER BY "startedAtZ" DESC
2465
+ LIMIT $${currentParamIndex} OFFSET $${currentParamIndex + 1}`,
2466
+ [...whereClause.args, perPage, page * perPage]
2467
+ );
2468
+ return {
2469
+ pagination: {
2470
+ total: count,
2471
+ page,
2472
+ perPage,
2473
+ hasMore: spans.length === perPage
2474
+ },
2475
+ spans: spans.map(
2476
+ (span) => transformFromSqlRow({
2477
+ tableName: storage.TABLE_AI_SPANS,
2478
+ sqlRow: span
2479
+ })
2480
+ )
2481
+ };
2482
+ } catch (error$1) {
2483
+ throw new error.MastraError(
2484
+ {
2485
+ id: "PG_STORE_GET_AI_TRACES_PAGINATED_FAILED",
2486
+ domain: error.ErrorDomain.STORAGE,
2487
+ category: error.ErrorCategory.USER
2488
+ },
2489
+ error$1
2490
+ );
2491
+ }
2492
+ }
2493
+ async batchCreateAISpans(args) {
2494
+ try {
2495
+ const records = args.records.map((record) => {
2496
+ const startedAt = record.startedAt instanceof Date ? record.startedAt.toISOString() : record.startedAt;
2497
+ const endedAt = record.endedAt instanceof Date ? record.endedAt.toISOString() : record.endedAt;
2498
+ return {
2499
+ ...record,
2500
+ startedAt,
2501
+ endedAt,
2502
+ startedAtZ: startedAt,
2503
+ endedAtZ: endedAt
2504
+ // Note: createdAt/updatedAt will be set by database triggers
2505
+ };
2506
+ });
2507
+ return this.operations.batchInsert({
2508
+ tableName: storage.TABLE_AI_SPANS,
2509
+ records
2510
+ });
2511
+ } catch (error$1) {
2512
+ throw new error.MastraError(
2513
+ {
2514
+ id: "PG_STORE_BATCH_CREATE_AI_SPANS_FAILED",
2515
+ domain: error.ErrorDomain.STORAGE,
2516
+ category: error.ErrorCategory.USER
2517
+ },
2518
+ error$1
2519
+ );
2520
+ }
2521
+ }
2522
+ async batchUpdateAISpans(args) {
2523
+ try {
2524
+ return this.operations.batchUpdate({
2525
+ tableName: storage.TABLE_AI_SPANS,
2526
+ updates: args.records.map((record) => {
2527
+ const data = {
2528
+ ...record.updates
2529
+ };
2530
+ if (data.endedAt instanceof Date) {
2531
+ const endedAt = data.endedAt.toISOString();
2532
+ data.endedAt = endedAt;
2533
+ data.endedAtZ = endedAt;
2534
+ }
2535
+ if (data.startedAt instanceof Date) {
2536
+ const startedAt = data.startedAt.toISOString();
2537
+ data.startedAt = startedAt;
2538
+ data.startedAtZ = startedAt;
2539
+ }
2540
+ return {
2541
+ keys: { spanId: record.spanId, traceId: record.traceId },
2542
+ data
2543
+ };
2544
+ })
2545
+ });
2546
+ } catch (error$1) {
2547
+ throw new error.MastraError(
2548
+ {
2549
+ id: "PG_STORE_BATCH_UPDATE_AI_SPANS_FAILED",
2550
+ domain: error.ErrorDomain.STORAGE,
2551
+ category: error.ErrorCategory.USER
2552
+ },
2553
+ error$1
2554
+ );
2555
+ }
2556
+ }
2557
+ async batchDeleteAITraces(args) {
2558
+ try {
2559
+ const tableName = getTableName({
2560
+ indexName: storage.TABLE_AI_SPANS,
2561
+ schemaName: getSchemaName(this.schema)
2562
+ });
2563
+ const placeholders = args.traceIds.map((_, i) => `$${i + 1}`).join(", ");
2564
+ await this.client.none(`DELETE FROM ${tableName} WHERE "traceId" IN (${placeholders})`, args.traceIds);
2565
+ } catch (error$1) {
2566
+ throw new error.MastraError(
2567
+ {
2568
+ id: "PG_STORE_BATCH_DELETE_AI_TRACES_FAILED",
2569
+ domain: error.ErrorDomain.STORAGE,
2570
+ category: error.ErrorCategory.USER
2571
+ },
2572
+ error$1
2573
+ );
2574
+ }
2575
+ }
2576
+ };
2204
2577
  var StoreOperationsPG = class extends storage.StoreOperations {
2205
2578
  client;
2206
2579
  schemaName;
@@ -2219,6 +2592,45 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2219
2592
  );
2220
2593
  return !!result;
2221
2594
  }
2595
+ /**
2596
+ * Prepares values for insertion, handling JSONB columns by stringifying them
2597
+ */
2598
+ prepareValuesForInsert(record, tableName) {
2599
+ return Object.entries(record).map(([key, value]) => {
2600
+ const schema = storage.TABLE_SCHEMAS[tableName];
2601
+ const columnSchema = schema?.[key];
2602
+ if (columnSchema?.type === "jsonb" && value !== null && typeof value === "object") {
2603
+ return JSON.stringify(value);
2604
+ }
2605
+ return value;
2606
+ });
2607
+ }
2608
+ /**
2609
+ * Adds timestamp Z columns to a record if timestamp columns exist
2610
+ */
2611
+ addTimestampZColumns(record) {
2612
+ if (record.createdAt) {
2613
+ record.createdAtZ = record.createdAt;
2614
+ }
2615
+ if (record.created_at) {
2616
+ record.created_atZ = record.created_at;
2617
+ }
2618
+ if (record.updatedAt) {
2619
+ record.updatedAtZ = record.updatedAt;
2620
+ }
2621
+ }
2622
+ /**
2623
+ * Prepares a value for database operations, handling Date objects and JSON serialization
2624
+ */
2625
+ prepareValue(value) {
2626
+ if (value instanceof Date) {
2627
+ return value.toISOString();
2628
+ } else if (typeof value === "object" && value !== null) {
2629
+ return JSON.stringify(value);
2630
+ } else {
2631
+ return value;
2632
+ }
2633
+ }
2222
2634
  async setupSchema() {
2223
2635
  if (!this.schemaName || this.schemaSetupComplete) {
2224
2636
  return;
@@ -2262,18 +2674,10 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2262
2674
  }
2263
2675
  async insert({ tableName, record }) {
2264
2676
  try {
2265
- if (record.createdAt) {
2266
- record.createdAtZ = record.createdAt;
2267
- }
2268
- if (record.created_at) {
2269
- record.created_atZ = record.created_at;
2270
- }
2271
- if (record.updatedAt) {
2272
- record.updatedAtZ = record.updatedAt;
2273
- }
2677
+ this.addTimestampZColumns(record);
2274
2678
  const schemaName = getSchemaName(this.schemaName);
2275
2679
  const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
2276
- const values = Object.values(record);
2680
+ const values = this.prepareValuesForInsert(record, tableName);
2277
2681
  const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
2278
2682
  await this.client.none(
2279
2683
  `INSERT INTO ${getTableName({ indexName: tableName, schemaName })} (${columns.map((c) => `"${c}"`).join(", ")}) VALUES (${placeholders})`,
@@ -2368,6 +2772,9 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2368
2772
  schema,
2369
2773
  ifNotExists: timeZColumnNames
2370
2774
  });
2775
+ if (tableName === storage.TABLE_AI_SPANS) {
2776
+ await this.setupTimestampTriggers(tableName);
2777
+ }
2371
2778
  } catch (error$1) {
2372
2779
  throw new error.MastraError(
2373
2780
  {
@@ -2382,6 +2789,48 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2382
2789
  );
2383
2790
  }
2384
2791
  }
2792
+ /**
2793
+ * Set up timestamp triggers for a table to automatically manage createdAt/updatedAt
2794
+ */
2795
+ async setupTimestampTriggers(tableName) {
2796
+ const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
2797
+ try {
2798
+ const triggerSQL = `
2799
+ -- Create or replace the trigger function
2800
+ CREATE OR REPLACE FUNCTION trigger_set_timestamps()
2801
+ RETURNS TRIGGER AS $$
2802
+ BEGIN
2803
+ IF TG_OP = 'INSERT' THEN
2804
+ NEW."createdAt" = NOW();
2805
+ NEW."updatedAt" = NOW();
2806
+ NEW."createdAtZ" = NOW();
2807
+ NEW."updatedAtZ" = NOW();
2808
+ ELSIF TG_OP = 'UPDATE' THEN
2809
+ NEW."updatedAt" = NOW();
2810
+ NEW."updatedAtZ" = NOW();
2811
+ -- Prevent createdAt from being changed
2812
+ NEW."createdAt" = OLD."createdAt";
2813
+ NEW."createdAtZ" = OLD."createdAtZ";
2814
+ END IF;
2815
+ RETURN NEW;
2816
+ END;
2817
+ $$ LANGUAGE plpgsql;
2818
+
2819
+ -- Drop existing trigger if it exists
2820
+ DROP TRIGGER IF EXISTS ${tableName}_timestamps ON ${fullTableName};
2821
+
2822
+ -- Create the trigger
2823
+ CREATE TRIGGER ${tableName}_timestamps
2824
+ BEFORE INSERT OR UPDATE ON ${fullTableName}
2825
+ FOR EACH ROW
2826
+ EXECUTE FUNCTION trigger_set_timestamps();
2827
+ `;
2828
+ await this.client.none(triggerSQL);
2829
+ this.logger?.debug?.(`Set up timestamp triggers for table ${fullTableName}`);
2830
+ } catch (error) {
2831
+ this.logger?.warn?.(`Failed to set up timestamp triggers for ${fullTableName}:`, error);
2832
+ }
2833
+ }
2385
2834
  /**
2386
2835
  * Alters table schema to add columns if they don't exist
2387
2836
  * @param tableName Name of the table
@@ -2524,8 +2973,8 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2524
2973
  schemaName: getSchemaName(this.schemaName)
2525
2974
  });
2526
2975
  const indexExists = await this.client.oneOrNone(
2527
- `SELECT 1 FROM pg_indexes
2528
- WHERE indexname = $1
2976
+ `SELECT 1 FROM pg_indexes
2977
+ WHERE indexname = $1
2529
2978
  AND schemaname = $2`,
2530
2979
  [name, schemaName]
2531
2980
  );
@@ -2582,8 +3031,8 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2582
3031
  try {
2583
3032
  const schemaName = this.schemaName || "public";
2584
3033
  const indexExists = await this.client.oneOrNone(
2585
- `SELECT 1 FROM pg_indexes
2586
- WHERE indexname = $1
3034
+ `SELECT 1 FROM pg_indexes
3035
+ WHERE indexname = $1
2587
3036
  AND schemaname = $2`,
2588
3037
  [indexName, schemaName]
2589
3038
  );
@@ -2616,7 +3065,7 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2616
3065
  let params;
2617
3066
  if (tableName) {
2618
3067
  query = `
2619
- SELECT
3068
+ SELECT
2620
3069
  i.indexname as name,
2621
3070
  i.tablename as table,
2622
3071
  i.indexdef as definition,
@@ -2627,14 +3076,14 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2627
3076
  JOIN pg_class c ON c.relname = i.indexname AND c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = i.schemaname)
2628
3077
  JOIN pg_index ix ON ix.indexrelid = c.oid
2629
3078
  JOIN pg_attribute a ON a.attrelid = ix.indrelid AND a.attnum = ANY(ix.indkey)
2630
- WHERE i.schemaname = $1
3079
+ WHERE i.schemaname = $1
2631
3080
  AND i.tablename = $2
2632
3081
  GROUP BY i.indexname, i.tablename, i.indexdef, ix.indisunique, c.oid
2633
3082
  `;
2634
3083
  params = [schemaName, tableName];
2635
3084
  } else {
2636
3085
  query = `
2637
- SELECT
3086
+ SELECT
2638
3087
  i.indexname as name,
2639
3088
  i.tablename as table,
2640
3089
  i.indexdef as definition,
@@ -2713,6 +3162,33 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2713
3162
  name: `${schemaPrefix}mastra_evals_agent_name_created_at_idx`,
2714
3163
  table: storage.TABLE_EVALS,
2715
3164
  columns: ["agent_name", "created_at DESC"]
3165
+ },
3166
+ // Composite index for scores (filter + sort)
3167
+ {
3168
+ name: `${schemaPrefix}mastra_scores_trace_id_span_id_created_at_idx`,
3169
+ table: storage.TABLE_SCORERS,
3170
+ columns: ["trace_id", "span_id", "created_at DESC"]
3171
+ },
3172
+ // AI Spans indexes for optimal trace querying
3173
+ {
3174
+ name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
3175
+ table: storage.TABLE_AI_SPANS,
3176
+ columns: ["traceId", "startedAt DESC"]
3177
+ },
3178
+ {
3179
+ name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
3180
+ table: storage.TABLE_AI_SPANS,
3181
+ columns: ["parentSpanId", "startedAt DESC"]
3182
+ },
3183
+ {
3184
+ name: `${schemaPrefix}mastra_ai_spans_name_idx`,
3185
+ table: storage.TABLE_AI_SPANS,
3186
+ columns: ["name"]
3187
+ },
3188
+ {
3189
+ name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
3190
+ table: storage.TABLE_AI_SPANS,
3191
+ columns: ["spanType", "startedAt DESC"]
2716
3192
  }
2717
3193
  ];
2718
3194
  for (const indexOptions of indexes) {
@@ -2740,7 +3216,7 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2740
3216
  try {
2741
3217
  const schemaName = this.schemaName || "public";
2742
3218
  const query = `
2743
- SELECT
3219
+ SELECT
2744
3220
  i.indexname as name,
2745
3221
  i.tablename as table,
2746
3222
  i.indexdef as definition,
@@ -2757,7 +3233,7 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2757
3233
  JOIN pg_attribute a ON a.attrelid = ix.indrelid AND a.attnum = ANY(ix.indkey)
2758
3234
  JOIN pg_am am ON c.relam = am.oid
2759
3235
  LEFT JOIN pg_stat_user_indexes s ON s.indexrelname = i.indexname AND s.schemaname = i.schemaname
2760
- WHERE i.schemaname = $1
3236
+ WHERE i.schemaname = $1
2761
3237
  AND i.indexname = $2
2762
3238
  GROUP BY i.indexname, i.tablename, i.indexdef, ix.indisunique, c.oid, am.amname, s.idx_scan, s.idx_tup_read, s.idx_tup_fetch
2763
3239
  `;
@@ -2798,6 +3274,121 @@ var StoreOperationsPG = class extends storage.StoreOperations {
2798
3274
  );
2799
3275
  }
2800
3276
  }
3277
+ /**
3278
+ * Update a single record in the database
3279
+ */
3280
+ async update({
3281
+ tableName,
3282
+ keys,
3283
+ data
3284
+ }) {
3285
+ try {
3286
+ const setColumns = [];
3287
+ const setValues = [];
3288
+ let paramIndex = 1;
3289
+ Object.entries(data).forEach(([key, value]) => {
3290
+ const parsedKey = utils.parseSqlIdentifier(key, "column name");
3291
+ setColumns.push(`"${parsedKey}" = $${paramIndex++}`);
3292
+ setValues.push(this.prepareValue(value));
3293
+ });
3294
+ const whereConditions = [];
3295
+ const whereValues = [];
3296
+ Object.entries(keys).forEach(([key, value]) => {
3297
+ const parsedKey = utils.parseSqlIdentifier(key, "column name");
3298
+ whereConditions.push(`"${parsedKey}" = $${paramIndex++}`);
3299
+ whereValues.push(this.prepareValue(value));
3300
+ });
3301
+ const tableName_ = getTableName({
3302
+ indexName: tableName,
3303
+ schemaName: getSchemaName(this.schemaName)
3304
+ });
3305
+ const sql = `UPDATE ${tableName_} SET ${setColumns.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
3306
+ const values = [...setValues, ...whereValues];
3307
+ await this.client.none(sql, values);
3308
+ } catch (error$1) {
3309
+ throw new error.MastraError(
3310
+ {
3311
+ id: "MASTRA_STORAGE_PG_STORE_UPDATE_FAILED",
3312
+ domain: error.ErrorDomain.STORAGE,
3313
+ category: error.ErrorCategory.THIRD_PARTY,
3314
+ details: {
3315
+ tableName
3316
+ }
3317
+ },
3318
+ error$1
3319
+ );
3320
+ }
3321
+ }
3322
+ /**
3323
+ * Update multiple records in a single batch transaction
3324
+ */
3325
+ async batchUpdate({
3326
+ tableName,
3327
+ updates
3328
+ }) {
3329
+ try {
3330
+ await this.client.query("BEGIN");
3331
+ for (const { keys, data } of updates) {
3332
+ await this.update({ tableName, keys, data });
3333
+ }
3334
+ await this.client.query("COMMIT");
3335
+ } catch (error$1) {
3336
+ await this.client.query("ROLLBACK");
3337
+ throw new error.MastraError(
3338
+ {
3339
+ id: "MASTRA_STORAGE_PG_STORE_BATCH_UPDATE_FAILED",
3340
+ domain: error.ErrorDomain.STORAGE,
3341
+ category: error.ErrorCategory.THIRD_PARTY,
3342
+ details: {
3343
+ tableName,
3344
+ numberOfRecords: updates.length
3345
+ }
3346
+ },
3347
+ error$1
3348
+ );
3349
+ }
3350
+ }
3351
+ /**
3352
+ * Delete multiple records by keys
3353
+ */
3354
+ async batchDelete({ tableName, keys }) {
3355
+ try {
3356
+ if (keys.length === 0) {
3357
+ return;
3358
+ }
3359
+ const tableName_ = getTableName({
3360
+ indexName: tableName,
3361
+ schemaName: getSchemaName(this.schemaName)
3362
+ });
3363
+ await this.client.tx(async (t) => {
3364
+ for (const keySet of keys) {
3365
+ const conditions = [];
3366
+ const values = [];
3367
+ let paramIndex = 1;
3368
+ Object.entries(keySet).forEach(([key, value]) => {
3369
+ const parsedKey = utils.parseSqlIdentifier(key, "column name");
3370
+ conditions.push(`"${parsedKey}" = $${paramIndex++}`);
3371
+ values.push(value);
3372
+ });
3373
+ const sql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
3374
+ await t.none(sql, values);
3375
+ }
3376
+ });
3377
+ } catch (error$1) {
3378
+ throw new error.MastraError(
3379
+ {
3380
+ id: "MASTRA_STORAGE_PG_STORE_BATCH_DELETE_FAILED",
3381
+ domain: error.ErrorDomain.STORAGE,
3382
+ category: error.ErrorCategory.THIRD_PARTY,
3383
+ details: {
3384
+ tableName,
3385
+ numberOfRecords: keys.length
3386
+ }
3387
+ },
3388
+ error$1
3389
+ );
3390
+ }
3391
+ }
2801
3392
  };
2802
3393
  function transformScoreRow(row) {
2803
3394
  return {
@@ -2911,6 +3502,26 @@ var ScoresPG = class extends storage.ScoresStorage {
2911
3502
  }
2912
3503
  }
2913
3504
  async saveScore(score) {
3505
+ let parsedScore;
3506
+ try {
3507
+ parsedScore = scores.saveScorePayloadSchema.parse(score);
3508
+ } catch (error$1) {
3509
+ throw new error.MastraError(
3510
+ {
3511
+ id: "MASTRA_STORAGE_PG_STORE_SAVE_SCORE_FAILED_INVALID_SCORE_PAYLOAD",
3512
+ domain: error.ErrorDomain.STORAGE,
3513
+ category: error.ErrorCategory.USER,
3514
+ details: {
3515
+ scorer: score.scorer.name,
3516
+ entityId: score.entityId,
3517
+ entityType: score.entityType,
3518
+ traceId: score.traceId || "",
3519
+ spanId: score.spanId || ""
3520
+ }
3521
+ },
3522
+ error$1
3523
+ );
3524
+ }
2914
3525
  try {
2915
3526
  const id = crypto.randomUUID();
2916
3527
  const {
@@ -2924,7 +3535,7 @@ var ScoresPG = class extends storage.ScoresStorage {
2924
3535
  runtimeContext,
2925
3536
  entity,
2926
3537
  ...rest
2927
- } = score;
3538
+ } = parsedScore;
2928
3539
  await this.operations.insert({
2929
3540
  tableName: storage.TABLE_SCORERS,
2930
3541
  record: {
@@ -3045,6 +3656,44 @@ var ScoresPG = class extends storage.ScoresStorage {
3045
3656
  );
3046
3657
  }
3047
3658
  }
3659
+ async getScoresBySpan({
3660
+ traceId,
3661
+ spanId,
3662
+ pagination
3663
+ }) {
3664
+ try {
3665
+ const tableName = getTableName({ indexName: storage.TABLE_SCORERS, schemaName: this.schema });
3666
+ const countSQLResult = await this.client.oneOrNone(
3667
+ `SELECT COUNT(*) as count FROM ${tableName} WHERE "traceId" = $1 AND "spanId" = $2`,
3668
+ [traceId, spanId]
3669
+ );
3670
+ const total = Number(countSQLResult?.count ?? 0);
3671
+ const result = await this.client.manyOrNone(
3672
+ `SELECT * FROM ${tableName} WHERE "traceId" = $1 AND "spanId" = $2 ORDER BY "createdAt" DESC LIMIT $3 OFFSET $4`,
3673
+ [traceId, spanId, pagination.perPage + 1, pagination.page * pagination.perPage]
3674
+ );
3675
+ const hasMore = result.length > pagination.perPage;
3676
+ const scores = result.slice(0, pagination.perPage).map((row) => transformScoreRow(row)) ?? [];
3677
+ return {
3678
+ scores,
3679
+ pagination: {
3680
+ total,
3681
+ page: pagination.page,
3682
+ perPage: pagination.perPage,
3683
+ hasMore
3684
+ }
3685
+ };
3686
+ } catch (error$1) {
3687
+ throw new error.MastraError(
3688
+ {
3689
+ id: "MASTRA_STORAGE_PG_STORE_GET_SCORES_BY_SPAN_FAILED",
3690
+ domain: error.ErrorDomain.STORAGE,
3691
+ category: error.ErrorCategory.THIRD_PARTY
3692
+ },
3693
+ error$1
3694
+ );
3695
+ }
3696
+ }
3048
3697
  };
3049
3698
  var TracesPG = class extends storage.TracesStorage {
3050
3699
  client;
@@ -3440,7 +4089,8 @@ var PostgresStore = class extends storage.MastraStorage {
3440
4089
  this.#config = {
3441
4090
  connectionString: config.connectionString,
3442
4091
  max: config.max,
3443
- idleTimeoutMillis: config.idleTimeoutMillis
4092
+ idleTimeoutMillis: config.idleTimeoutMillis,
4093
+ ssl: config.ssl
3444
4094
  };
3445
4095
  } else if (isCloudSqlConfig(config)) {
3446
4096
  this.#config = {
@@ -3460,11 +4110,9 @@ var PostgresStore = class extends storage.MastraStorage {
3460
4110
  idleTimeoutMillis: config.idleTimeoutMillis
3461
4111
  };
3462
4112
  } else {
3463
- this.#config = {
3464
- ...config,
3465
- max: config.max,
3466
- idleTimeoutMillis: config.idleTimeoutMillis
3467
- };
4113
+ throw new Error(
4114
+ "PostgresStore: invalid config. Provide either {connectionString}, {host,port,database,user,password}, or a pg ClientConfig (e.g., Cloud SQL connector with `stream`)."
4115
+ );
3468
4116
  }
3469
4117
  this.stores = {};
3470
4118
  } catch (e) {
@@ -3492,13 +4140,15 @@ var PostgresStore = class extends storage.MastraStorage {
3492
4140
  const workflows = new WorkflowsPG({ client: this.#db, operations, schema: this.schema });
3493
4141
  const legacyEvals = new LegacyEvalsPG({ client: this.#db, schema: this.schema });
3494
4142
  const memory = new MemoryPG({ client: this.#db, schema: this.schema, operations });
4143
+ const observability = new ObservabilityPG({ client: this.#db, operations, schema: this.schema });
3495
4144
  this.stores = {
3496
4145
  operations,
3497
4146
  scores,
3498
4147
  traces,
3499
4148
  workflows,
3500
4149
  legacyEvals,
3501
- memory
4150
+ memory,
4151
+ observability
3502
4152
  };
3503
4153
  await super.init();
3504
4154
  try {
@@ -3537,8 +4187,9 @@ var PostgresStore = class extends storage.MastraStorage {
3537
4187
  hasColumn: true,
3538
4188
  createTable: true,
3539
4189
  deleteMessages: true,
3540
- aiTracing: false,
3541
- indexManagement: true
4190
+ aiTracing: true,
4191
+ indexManagement: true,
4192
+ getScoresBySpan: true
3542
4193
  };
3543
4194
  }
3544
4195
  /** @deprecated use getEvals instead */
@@ -3704,11 +4355,95 @@ var PostgresStore = class extends storage.MastraStorage {
3704
4355
  async close() {
3705
4356
  this.pgp.end();
3706
4357
  }
4358
+ /**
4359
+ * AI Tracing / Observability
4360
+ */
4361
+ async createAISpan(span) {
4362
+ if (!this.stores.observability) {
4363
+ throw new error.MastraError({
4364
+ id: "PG_STORE_OBSERVABILITY_NOT_INITIALIZED",
4365
+ domain: error.ErrorDomain.STORAGE,
4366
+ category: error.ErrorCategory.SYSTEM,
4367
+ text: "Observability storage is not initialized"
4368
+ });
4369
+ }
4370
+ return this.stores.observability.createAISpan(span);
4371
+ }
4372
+ async updateAISpan({
4373
+ spanId,
4374
+ traceId,
4375
+ updates
4376
+ }) {
4377
+ if (!this.stores.observability) {
4378
+ throw new error.MastraError({
4379
+ id: "PG_STORE_OBSERVABILITY_NOT_INITIALIZED",
4380
+ domain: error.ErrorDomain.STORAGE,
4381
+ category: error.ErrorCategory.SYSTEM,
4382
+ text: "Observability storage is not initialized"
4383
+ });
4384
+ }
4385
+ return this.stores.observability.updateAISpan({ spanId, traceId, updates });
4386
+ }
4387
+ async getAITrace(traceId) {
4388
+ if (!this.stores.observability) {
4389
+ throw new error.MastraError({
4390
+ id: "PG_STORE_OBSERVABILITY_NOT_INITIALIZED",
4391
+ domain: error.ErrorDomain.STORAGE,
4392
+ category: error.ErrorCategory.SYSTEM,
4393
+ text: "Observability storage is not initialized"
4394
+ });
4395
+ }
4396
+ return this.stores.observability.getAITrace(traceId);
4397
+ }
4398
+ async getAITracesPaginated(args) {
4399
+ if (!this.stores.observability) {
4400
+ throw new error.MastraError({
4401
+ id: "PG_STORE_OBSERVABILITY_NOT_INITIALIZED",
4402
+ domain: error.ErrorDomain.STORAGE,
4403
+ category: error.ErrorCategory.SYSTEM,
4404
+ text: "Observability storage is not initialized"
4405
+ });
4406
+ }
4407
+ return this.stores.observability.getAITracesPaginated(args);
4408
+ }
4409
+ async batchCreateAISpans(args) {
4410
+ if (!this.stores.observability) {
4411
+ throw new error.MastraError({
4412
+ id: "PG_STORE_OBSERVABILITY_NOT_INITIALIZED",
4413
+ domain: error.ErrorDomain.STORAGE,
4414
+ category: error.ErrorCategory.SYSTEM,
4415
+ text: "Observability storage is not initialized"
4416
+ });
4417
+ }
4418
+ return this.stores.observability.batchCreateAISpans(args);
4419
+ }
4420
+ async batchUpdateAISpans(args) {
4421
+ if (!this.stores.observability) {
4422
+ throw new error.MastraError({
4423
+ id: "PG_STORE_OBSERVABILITY_NOT_INITIALIZED",
4424
+ domain: error.ErrorDomain.STORAGE,
4425
+ category: error.ErrorCategory.SYSTEM,
4426
+ text: "Observability storage is not initialized"
4427
+ });
4428
+ }
4429
+ return this.stores.observability.batchUpdateAISpans(args);
4430
+ }
4431
+ async batchDeleteAITraces(args) {
4432
+ if (!this.stores.observability) {
4433
+ throw new error.MastraError({
4434
+ id: "PG_STORE_OBSERVABILITY_NOT_INITIALIZED",
4435
+ domain: error.ErrorDomain.STORAGE,
4436
+ category: error.ErrorCategory.SYSTEM,
4437
+ text: "Observability storage is not initialized"
4438
+ });
4439
+ }
4440
+ return this.stores.observability.batchDeleteAITraces(args);
4441
+ }
3707
4442
  /**
3708
4443
  * Scorers
3709
4444
  */
3710
- async getScoreById({ id: _id }) {
3711
- return this.stores.scores.getScoreById({ id: _id });
4445
+ async getScoreById({ id }) {
4446
+ return this.stores.scores.getScoreById({ id });
3712
4447
  }
3713
4448
  async getScoresByScorerId({
3714
4449
  scorerId,
@@ -3719,26 +4454,33 @@ var PostgresStore = class extends storage.MastraStorage {
3719
4454
  }) {
3720
4455
  return this.stores.scores.getScoresByScorerId({ scorerId, pagination, entityId, entityType, source });
3721
4456
  }
3722
- async saveScore(_score) {
3723
- return this.stores.scores.saveScore(_score);
4457
+ async saveScore(score) {
4458
+ return this.stores.scores.saveScore(score);
3724
4459
  }
3725
4460
  async getScoresByRunId({
3726
- runId: _runId,
3727
- pagination: _pagination
4461
+ runId,
4462
+ pagination
3728
4463
  }) {
3729
- return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
4464
+ return this.stores.scores.getScoresByRunId({ runId, pagination });
3730
4465
  }
3731
4466
  async getScoresByEntityId({
3732
- entityId: _entityId,
3733
- entityType: _entityType,
3734
- pagination: _pagination
4467
+ entityId,
4468
+ entityType,
4469
+ pagination
3735
4470
  }) {
3736
4471
  return this.stores.scores.getScoresByEntityId({
3737
- entityId: _entityId,
3738
- entityType: _entityType,
3739
- pagination: _pagination
4472
+ entityId,
4473
+ entityType,
4474
+ pagination
3740
4475
  });
3741
4476
  }
4477
+ async getScoresBySpan({
4478
+ traceId,
4479
+ spanId,
4480
+ pagination
4481
+ }) {
4482
+ return this.stores.scores.getScoresBySpan({ traceId, spanId, pagination });
4483
+ }
3742
4484
  };
3743
4485
 
3744
4486
  // src/vector/prompt.ts