@mastra/pg 1.30.0-alpha.1 → 1.30.0-alpha.3
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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +255 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +255 -3
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +3 -2
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/span-query.d.ts +43 -0
- package/dist/storage/domains/observability/v-next/span-query.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/trace-aggregate.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts +7 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -18256,6 +18256,166 @@ async function queryThreads(client, schema, plan, timeoutMs) {
|
|
|
18256
18256
|
}) : null }
|
|
18257
18257
|
});
|
|
18258
18258
|
}
|
|
18259
|
+
/** Compile a span-row filter with exactly the same rules as trace span predicates. */
|
|
18260
|
+
function compileSpanQueryPredicate(predicate, offset) {
|
|
18261
|
+
return compileScalarPredicate(predicate, SPAN_FIELDS, offset);
|
|
18262
|
+
}
|
|
18263
|
+
//#endregion
|
|
18264
|
+
//#region src/storage/domains/observability/v-next/span-query.ts
|
|
18265
|
+
const identityFields = [
|
|
18266
|
+
"organizationId",
|
|
18267
|
+
"resourceId",
|
|
18268
|
+
"traceId",
|
|
18269
|
+
"spanId"
|
|
18270
|
+
];
|
|
18271
|
+
const fields = [
|
|
18272
|
+
...identityFields,
|
|
18273
|
+
"parentSpanId",
|
|
18274
|
+
"name",
|
|
18275
|
+
"spanType",
|
|
18276
|
+
"entityType",
|
|
18277
|
+
"entityId",
|
|
18278
|
+
"entityName",
|
|
18279
|
+
"entityVersionId",
|
|
18280
|
+
"parentEntityVersionId",
|
|
18281
|
+
"rootEntityVersionId",
|
|
18282
|
+
"runId",
|
|
18283
|
+
"sessionId",
|
|
18284
|
+
"userId"
|
|
18285
|
+
];
|
|
18286
|
+
const identities = identityFields.map((field) => `"${field}"`).join(", ");
|
|
18287
|
+
const identityOrder = identityFields.map((field) => `"${field}" COLLATE "C" ASC NULLS LAST`).join(", ");
|
|
18288
|
+
function selectedKeys(rows, values) {
|
|
18289
|
+
return rows.map((row) => `(${identityFields.map((field) => {
|
|
18290
|
+
values.push(row[field]);
|
|
18291
|
+
return `"${field}" ${field === "traceId" || field === "spanId" ? "=" : "IS NOT DISTINCT FROM"} $${values.length}::text`;
|
|
18292
|
+
}).join(" AND ")})`).join(" OR ");
|
|
18293
|
+
}
|
|
18294
|
+
function afterCursor(plan, values) {
|
|
18295
|
+
if (!plan.cursor) return "TRUE";
|
|
18296
|
+
const cursor = plan.cursor;
|
|
18297
|
+
values.push(cursor.sortValue);
|
|
18298
|
+
const timestamp = `$${values.length}::timestamptz`;
|
|
18299
|
+
const field = `s."${plan.orderBy.field}"`;
|
|
18300
|
+
const terms = [`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${timestamp}`];
|
|
18301
|
+
const equal = [`${field} = ${timestamp}`];
|
|
18302
|
+
for (const key of identityFields) {
|
|
18303
|
+
const ref = `s."${key}" COLLATE "C"`;
|
|
18304
|
+
const value = cursor[key];
|
|
18305
|
+
if (value === null) equal.push(`${ref} IS NULL`);
|
|
18306
|
+
else {
|
|
18307
|
+
values.push(value);
|
|
18308
|
+
const parameter = `$${values.length}::text COLLATE "C"`;
|
|
18309
|
+
terms.push(`(${equal.join(" AND ")} AND (${ref} > ${parameter} OR ${ref} IS NULL))`);
|
|
18310
|
+
equal.push(`${ref} = ${parameter}`);
|
|
18311
|
+
}
|
|
18312
|
+
}
|
|
18313
|
+
return `(${terms.join(" OR ")})`;
|
|
18314
|
+
}
|
|
18315
|
+
function compilePostgresSpanQuery(schema, plan) {
|
|
18316
|
+
const values = [plan.timeRange.from, plan.timeRange.to];
|
|
18317
|
+
const scope = [];
|
|
18318
|
+
if (plan.scope) {
|
|
18319
|
+
values.push(plan.scope.organizationId);
|
|
18320
|
+
scope.push(`"organizationId" = $${values.length}`);
|
|
18321
|
+
if (plan.scope.resourceId !== void 0) {
|
|
18322
|
+
values.push(plan.scope.resourceId);
|
|
18323
|
+
scope.push(`"resourceId" = $${values.length}`);
|
|
18324
|
+
}
|
|
18325
|
+
}
|
|
18326
|
+
const predicate = plan.where ? compileSpanQueryPredicate(plan.where, values.length + 1) : {
|
|
18327
|
+
sql: "TRUE",
|
|
18328
|
+
values: []
|
|
18329
|
+
};
|
|
18330
|
+
values.push(...predicate.values);
|
|
18331
|
+
const after = afterCursor(plan, values);
|
|
18332
|
+
values.push(plan.limit + 1);
|
|
18333
|
+
const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
|
|
18334
|
+
return {
|
|
18335
|
+
text: `WITH candidate_ids AS MATERIALIZED (
|
|
18336
|
+
SELECT DISTINCT ${identities} FROM ${table}
|
|
18337
|
+
WHERE "startedAt" >= $1 AND "startedAt" < $2 AND "endedAt" >= $1 AND NOT "isPending"
|
|
18338
|
+
${scope.length ? `AND ${scope.join(" AND ")}` : ""}
|
|
18339
|
+
), current_spans AS MATERIALIZED (
|
|
18340
|
+
SELECT DISTINCT ON (${identities})
|
|
18341
|
+
${fields.map((field) => `"${field}"`).join(", ")}, "startedAt", "endedAt", "cursorId"::text AS version,
|
|
18342
|
+
CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
|
|
18343
|
+
CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider,
|
|
18344
|
+
(EXTRACT(EPOCH FROM ("endedAt" - "startedAt")) * 1000)::float8 AS "durationMs",
|
|
18345
|
+
CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
|
|
18346
|
+
error IS NOT NULL AS "hasError"
|
|
18347
|
+
FROM ${table} e
|
|
18348
|
+
WHERE NOT "isPending" AND "endedAt" >= $1 AND (
|
|
18349
|
+
"organizationId" IS NULL, COALESCE("organizationId", ''), "resourceId" IS NULL, COALESCE("resourceId", ''), "traceId", "spanId"
|
|
18350
|
+
) IN (
|
|
18351
|
+
SELECT "organizationId" IS NULL, COALESCE("organizationId", ''), "resourceId" IS NULL, COALESCE("resourceId", ''), "traceId", "spanId" FROM candidate_ids
|
|
18352
|
+
) ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
|
|
18353
|
+
ORDER BY ${identities}, "endedAt" DESC, "cursorId" DESC
|
|
18354
|
+
), candidates AS (
|
|
18355
|
+
SELECT *, CASE WHEN "hasError" THEN 'error' ELSE NULL END AS error FROM current_spans
|
|
18356
|
+
)
|
|
18357
|
+
SELECT ${[
|
|
18358
|
+
...identityFields,
|
|
18359
|
+
"parentSpanId",
|
|
18360
|
+
"name",
|
|
18361
|
+
"spanType",
|
|
18362
|
+
"entityType",
|
|
18363
|
+
"entityId",
|
|
18364
|
+
"entityName",
|
|
18365
|
+
"model",
|
|
18366
|
+
"provider",
|
|
18367
|
+
"startedAt",
|
|
18368
|
+
"endedAt",
|
|
18369
|
+
"durationMs",
|
|
18370
|
+
"status",
|
|
18371
|
+
"version"
|
|
18372
|
+
].map((field) => `s."${field}"`).join(", ")}
|
|
18373
|
+
FROM candidates s WHERE s."startedAt" >= $1 AND s."startedAt" < $2 AND (${predicate.sql}) AND ${after}
|
|
18374
|
+
ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityOrder}
|
|
18375
|
+
LIMIT $${values.length}`,
|
|
18376
|
+
values
|
|
18377
|
+
};
|
|
18378
|
+
}
|
|
18379
|
+
async function querySpans(client, schema, plan, timeoutMs) {
|
|
18380
|
+
const deadline = performance.now() + coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
18381
|
+
return runWithPostgresTraceQueryTimeout(client, timeoutMs, async (tx) => {
|
|
18382
|
+
const query = compilePostgresSpanQuery(schema, plan);
|
|
18383
|
+
const rows = await tx.any(query.text, query.values);
|
|
18384
|
+
const selected = rows.map(({ version: _version, startedAt, endedAt, ...row }) => ({
|
|
18385
|
+
...row,
|
|
18386
|
+
startedAt: startedAt.toISOString(),
|
|
18387
|
+
endedAt: endedAt.toISOString()
|
|
18388
|
+
}));
|
|
18389
|
+
const page = rows.slice(0, plan.limit);
|
|
18390
|
+
if (!page.length) return coreStorage.buildSpanQueryResponse(plan, selected, [], []);
|
|
18391
|
+
const remaining = async () => {
|
|
18392
|
+
const ms = Math.floor(deadline - performance.now());
|
|
18393
|
+
if (ms <= 0) throw new coreStorage.TraceQueryExecutionError();
|
|
18394
|
+
await tx.query(`SELECT set_config('statement_timeout', $1, true)`, [`${ms}ms`]);
|
|
18395
|
+
};
|
|
18396
|
+
await remaining();
|
|
18397
|
+
const payloadValues = [];
|
|
18398
|
+
const payloadWhere = page.map((row) => {
|
|
18399
|
+
const key = selectedKeys([row], payloadValues);
|
|
18400
|
+
payloadValues.push(row.version);
|
|
18401
|
+
return `(${key} AND "cursorId" = $${payloadValues.length}::bigint)`;
|
|
18402
|
+
}).join(" OR ");
|
|
18403
|
+
const payloads = await tx.any(`SELECT ${identities},
|
|
18404
|
+
left(input::text, ${coreStorage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS "inputPreview", left(output::text, ${coreStorage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS "outputPreview"
|
|
18405
|
+
FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} WHERE ${payloadWhere}`, payloadValues);
|
|
18406
|
+
await remaining();
|
|
18407
|
+
const metricValues = [];
|
|
18408
|
+
const metricWhere = selectedKeys(page, metricValues);
|
|
18409
|
+
const metrics = await tx.any(`SELECT ${identities}, name, "estimatedCost", "costUnit",
|
|
18410
|
+
"costMetadata" ->> 'allocation' AS allocation, COALESCE("costMetadata" ? 'error', false) AS "costError"
|
|
18411
|
+
FROM (SELECT DISTINCT ON (${identities}, "metricId") ${identities}, name, "estimatedCost", "costUnit", "costMetadata" FROM ${qualifiedTable(schema, TABLE_METRIC_EVENTS)}
|
|
18412
|
+
WHERE (${metricWhere}) AND name LIKE 'mastra_model_%'
|
|
18413
|
+
ORDER BY ${identities}, "metricId", "cursorId" DESC) m
|
|
18414
|
+
LIMIT ${coreStorage.SPAN_QUERY_MAX_COST_METRICS + 1}`, metricValues);
|
|
18415
|
+
if (metrics.length > coreStorage.SPAN_QUERY_MAX_COST_METRICS) throw new coreStorage.TraceQueryResourceLimitError();
|
|
18416
|
+
return coreStorage.buildSpanQueryResponse(plan, selected, payloads, metrics);
|
|
18417
|
+
}, { repeatableRead: true });
|
|
18418
|
+
}
|
|
18259
18419
|
//#endregion
|
|
18260
18420
|
//#region src/storage/domains/observability/v-next/trace-aggregate.ts
|
|
18261
18421
|
/** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
|
|
@@ -18324,13 +18484,32 @@ function compilePostgresTraceAggregate(schema, plan) {
|
|
|
18324
18484
|
if (intervalMs === void 0) throw new Error(`Unsupported trusted trace-aggregate interval: ${plan.interval}`);
|
|
18325
18485
|
factColumns.push(`to_timestamp(floor(EXTRACT(EPOCH FROM r."startedAt") * 1000 / ${intervalMs}) * ${intervalMs} / 1000.0) AS "bucket"`);
|
|
18326
18486
|
}
|
|
18487
|
+
const measureKinds = new Set(plan.measures.map((measure) => measureRule(measure.name)?.kind));
|
|
18488
|
+
const hasCost = measureKinds.has("cost");
|
|
18489
|
+
let usageJoin = "";
|
|
18490
|
+
if (hasCost || measureKinds.has("tokens")) {
|
|
18491
|
+
ctes.push(...compileUsageCtes(schema, plan, param));
|
|
18492
|
+
usageJoin = "\n LEFT JOIN usage u ON u.\"traceId\" = r.\"traceId\"";
|
|
18493
|
+
USAGE_METRIC_NAMES.forEach((_, index) => factColumns.push(`CASE WHEN u."traceId" IS NOT NULL THEN COALESCE(u."t${index}", 0) END AS "t${index}"`));
|
|
18494
|
+
factColumns.push(`u."traceId" IS NOT NULL AS "usageBearing"`, `CASE WHEN u."priced" THEN u."cost" END AS "traceCost"`, `COALESCE(u."priced" AND NOT u."pricingFailure", FALSE) AS "covered"`, `u."unitMin"`, `u."unitMax"`);
|
|
18495
|
+
}
|
|
18327
18496
|
ctes.push(`facts AS (
|
|
18328
18497
|
SELECT ${factColumns.join(",\n ")}
|
|
18329
|
-
FROM candidates r
|
|
18498
|
+
FROM candidates r${usageJoin}
|
|
18330
18499
|
)`);
|
|
18500
|
+
const mixedUnitsSql = (alias) => `MIN(${alias}"unitMin" COLLATE "C") <> MAX(${alias}"unitMax" COLLATE "C")`;
|
|
18331
18501
|
const measureSql = (name, alias = "") => {
|
|
18332
18502
|
const column = (name) => `${alias}"${name}"`;
|
|
18333
18503
|
if (name === "count") return "COUNT(*)::float8";
|
|
18504
|
+
const rule = measureRule(name);
|
|
18505
|
+
if (rule?.kind === "tokens" && rule.metricNames) {
|
|
18506
|
+
const value = rule.metricNames.map((metricName) => column(`t${usageMetricIndex(metricName)}`)).join(" + ");
|
|
18507
|
+
return `(${rule.statistic === "avg" ? "AVG" : "SUM"}(${value}))::float8`;
|
|
18508
|
+
}
|
|
18509
|
+
if (rule?.kind === "cost") {
|
|
18510
|
+
const aggregate = rule.statistic === "avg" ? "AVG" : "SUM";
|
|
18511
|
+
return `(CASE WHEN ${mixedUnitsSql(alias)} THEN NULL ELSE ${aggregate}(${column("traceCost")}) END)::float8`;
|
|
18512
|
+
}
|
|
18334
18513
|
if (name === "errorCount") return `(COUNT(*) FILTER (WHERE ${column("isError")}))::float8`;
|
|
18335
18514
|
if (name === "errorRate") return `(COUNT(*) FILTER (WHERE ${column("isError")}))::float8 / COUNT(*)`;
|
|
18336
18515
|
if (name === "duration.avg") return `AVG(${column("durationMs")})`;
|
|
@@ -18368,8 +18547,17 @@ function compilePostgresTraceAggregate(schema, plan) {
|
|
|
18368
18547
|
const orderSql = [`${orderTarget} ${direction} NULLS LAST`, ...dimensionColumns.map((column) => `${column} COLLATE "C" ASC NULLS LAST`)].join(", ");
|
|
18369
18548
|
const groupSql = dimensionColumns.length > 0 ? dimensionColumns.join(", ") : "()";
|
|
18370
18549
|
const limitParameter = param(plan.limit + 1);
|
|
18550
|
+
const costColumns = (alias) => {
|
|
18551
|
+
if (!hasCost) return [];
|
|
18552
|
+
const mixedUnit = param(coreStorage.TRACE_AGGREGATE_MIXED_COST_UNIT);
|
|
18553
|
+
return [`(COUNT(*) FILTER (WHERE ${alias}"covered"))::float8 / NULLIF(COUNT(*) FILTER (WHERE ${alias}"usageBearing"), 0) AS "costCoverage"`, `CASE WHEN ${mixedUnitsSql(alias)} THEN ${mixedUnit}::text ELSE MIN(${alias}"unitMin" COLLATE "C") END AS "costUnit"`];
|
|
18554
|
+
};
|
|
18371
18555
|
if (plan.interval === void 0) {
|
|
18372
|
-
const projected = [
|
|
18556
|
+
const projected = [
|
|
18557
|
+
...dimensionColumns,
|
|
18558
|
+
...plan.measures.map((measure, index) => `${measureSql(measure.name)} AS "m${index}"`),
|
|
18559
|
+
...costColumns("")
|
|
18560
|
+
];
|
|
18373
18561
|
return {
|
|
18374
18562
|
text: `WITH ${ctes.join(",\n")}
|
|
18375
18563
|
SELECT ${projected.join(", ")}
|
|
@@ -18395,6 +18583,7 @@ LIMIT ${limitParameter}`,
|
|
|
18395
18583
|
...dimensionColumns.map((column) => `f.${column}`),
|
|
18396
18584
|
"f.\"bucket\"",
|
|
18397
18585
|
...plan.measures.map((measure, index) => `${measureSql(measure.name, "f.")} AS "m${index}"`),
|
|
18586
|
+
...costColumns("f."),
|
|
18398
18587
|
`(SELECT COUNT(*) FROM ranked) > ${maxRankParameter} AS "truncated"`
|
|
18399
18588
|
];
|
|
18400
18589
|
return {
|
|
@@ -18412,6 +18601,59 @@ ORDER BY g."rank" ASC, f."bucket" ASC`,
|
|
|
18412
18601
|
values
|
|
18413
18602
|
};
|
|
18414
18603
|
}
|
|
18604
|
+
const USAGE_METRIC_NAMES = coreStorage.TRACE_AGGREGATE_USAGE_METRIC_NAMES;
|
|
18605
|
+
function measureRule(name) {
|
|
18606
|
+
return coreStorage.isTraceAggregateCanonicalMeasure(name) ? coreStorage.TRACE_AGGREGATE_MEASURE_REGISTRY[name] : void 0;
|
|
18607
|
+
}
|
|
18608
|
+
function usageMetricIndex(name) {
|
|
18609
|
+
const index = USAGE_METRIC_NAMES.indexOf(name);
|
|
18610
|
+
if (index === -1) throw new Error(`Unsupported trusted trace-aggregate token metric: ${name}`);
|
|
18611
|
+
return index;
|
|
18612
|
+
}
|
|
18613
|
+
/**
|
|
18614
|
+
* Builds `usage`: one row per candidate trace that has at least one token metric row, holding
|
|
18615
|
+
* per-name token sums (`t<i>`, indexed by `TRACE_AGGREGATE_USAGE_METRIC_NAMES`) and the trace's
|
|
18616
|
+
* pricing state. Metric rows are matched by traceId, usage metric name, tenant scope, and
|
|
18617
|
+
* `timestamp >= from` only: a trace that starts in the window can emit metrics after `to`, and
|
|
18618
|
+
* the candidates semi-join already bounds the scan.
|
|
18619
|
+
*
|
|
18620
|
+
* Rows are deduplicated on `metricId` alone. The primary key is (`metricId`, `timestamp`)
|
|
18621
|
+
* because the table is partitioned by timestamp, so retried copies with different timestamps
|
|
18622
|
+
* are both stored.
|
|
18623
|
+
*/
|
|
18624
|
+
function compileUsageCtes(schema, plan, param) {
|
|
18625
|
+
const scopeConditions = plan.scope ? [`m."organizationId" = ${param(plan.scope.organizationId)}`, ...plan.scope.resourceId === void 0 ? [] : [`m."resourceId" = ${param(plan.scope.resourceId)}`]] : [];
|
|
18626
|
+
const rowConditions = [
|
|
18627
|
+
`m."traceId" IN (SELECT "traceId" FROM candidates)`,
|
|
18628
|
+
`m."name" = ANY(${param(USAGE_METRIC_NAMES)}::text[])`,
|
|
18629
|
+
`m."timestamp" >= ${param(plan.timeRange.from)}::timestamptz`,
|
|
18630
|
+
...scopeConditions
|
|
18631
|
+
];
|
|
18632
|
+
const costRow = `"name" = ANY(${param(coreStorage.TRACE_AGGREGATE_COST_METRIC_NAMES)}::text[])`;
|
|
18633
|
+
const priced = `${costRow} AND "estimatedCost" IS NOT NULL AND "costUnit" IS NOT NULL AND NOT "hasError"`;
|
|
18634
|
+
const failed = `${costRow} AND ("hasError" OR ("estimatedCost" IS NOT NULL AND "costUnit" IS NULL))`;
|
|
18635
|
+
const tokenColumns = USAGE_METRIC_NAMES.map((name, index) => `SUM("value") FILTER (WHERE "name" = ${param(name)}) AS "t${index}"`);
|
|
18636
|
+
return [`usage_rows AS (
|
|
18637
|
+
SELECT DISTINCT ON (m."metricId") m."traceId", m."name", m."value", m."estimatedCost", m."costUnit",
|
|
18638
|
+
COALESCE(jsonb_typeof(m."costMetadata" -> 'error'), 'null') <> 'null' AS "hasError"
|
|
18639
|
+
FROM ${qualifiedTable(schema, TABLE_METRIC_EVENTS)} m
|
|
18640
|
+
WHERE ${rowConditions.join("\n AND ")}
|
|
18641
|
+
ORDER BY m."metricId", m."timestamp"
|
|
18642
|
+
)`, `usage AS (
|
|
18643
|
+
SELECT "traceId",
|
|
18644
|
+
${tokenColumns.join(",\n ")},
|
|
18645
|
+
SUM("estimatedCost") FILTER (WHERE ${priced}) AS "cost",
|
|
18646
|
+
bool_or(${priced}) AS "priced",
|
|
18647
|
+
bool_or(${failed}) AS "pricingFailure",
|
|
18648
|
+
MIN("costUnit" COLLATE "C") FILTER (WHERE ${priced}) AS "unitMin",
|
|
18649
|
+
MAX("costUnit" COLLATE "C") FILTER (WHERE ${priced}) AS "unitMax"
|
|
18650
|
+
FROM usage_rows
|
|
18651
|
+
GROUP BY "traceId"
|
|
18652
|
+
)`];
|
|
18653
|
+
}
|
|
18654
|
+
function nullableNumber(value) {
|
|
18655
|
+
return value === null || value === void 0 ? null : Number(value);
|
|
18656
|
+
}
|
|
18415
18657
|
function toIsoTimestamp(value) {
|
|
18416
18658
|
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
|
18417
18659
|
}
|
|
@@ -18429,7 +18671,11 @@ async function aggregateTraces(client, schema, plan, timeoutMs) {
|
|
|
18429
18671
|
}
|
|
18430
18672
|
return {
|
|
18431
18673
|
rows: resultRows.map((row) => {
|
|
18432
|
-
const shaped = { measures: Object.fromEntries(plan.measures.map((measure, index) => [measure.name,
|
|
18674
|
+
const shaped = { measures: Object.fromEntries(plan.measures.map((measure, index) => [measure.name, nullableNumber(row[`m${index}`])])) };
|
|
18675
|
+
if ("costCoverage" in row) shaped.cost = {
|
|
18676
|
+
coverage: nullableNumber(row.costCoverage),
|
|
18677
|
+
unit: row.costUnit ?? null
|
|
18678
|
+
};
|
|
18433
18679
|
if (plan.dimensions.length > 0) shaped.dimensions = Object.fromEntries(plan.dimensions.map((dimension, index) => [dimension, row[`d${index}`] ?? null]));
|
|
18434
18680
|
if (plan.interval !== void 0) shaped.bucket = toIsoTimestamp(row.bucket);
|
|
18435
18681
|
return shaped;
|
|
@@ -19151,6 +19397,7 @@ async function dangerouslyClearTracing(client, schema) {
|
|
|
19151
19397
|
* application database — observability writes will degrade app performance.
|
|
19152
19398
|
* Use it through `MastraCompositeStore` with a dedicated Postgres connection.
|
|
19153
19399
|
*/
|
|
19400
|
+
const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query"] : [];
|
|
19154
19401
|
function wrapError(op, error, details) {
|
|
19155
19402
|
if (error instanceof MastraError || error instanceof coreStorage.TraceQueryCursorError || error instanceof coreStorage.TraceQueryExecutionError || error instanceof coreStorage.TraceQueryResourceLimitError) throw error;
|
|
19156
19403
|
throw new MastraError({
|
|
@@ -19340,6 +19587,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
19340
19587
|
"metric-discovery",
|
|
19341
19588
|
"trace-query",
|
|
19342
19589
|
"trace-aggregate",
|
|
19590
|
+
...spanQueryFeatures,
|
|
19343
19591
|
"trace-query-root-duration",
|
|
19344
19592
|
"trace-query-discovery",
|
|
19345
19593
|
"thread-query",
|
|
@@ -19359,6 +19607,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
19359
19607
|
"delta-polling",
|
|
19360
19608
|
"trace-query",
|
|
19361
19609
|
"trace-aggregate",
|
|
19610
|
+
...spanQueryFeatures,
|
|
19362
19611
|
"trace-query-root-duration",
|
|
19363
19612
|
"trace-query-discovery",
|
|
19364
19613
|
"thread-query",
|
|
@@ -19407,6 +19656,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
19407
19656
|
async listTraces(args) {
|
|
19408
19657
|
return this.#run("LIST_TRACES", () => listTraces(this.#readClient, this.#schema, args));
|
|
19409
19658
|
}
|
|
19659
|
+
async querySpans(plan) {
|
|
19660
|
+
return this.#run("QUERY_SPANS", () => querySpans(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
19661
|
+
}
|
|
19410
19662
|
async queryTraces(plan) {
|
|
19411
19663
|
return this.#run("QUERY_TRACES", () => queryTraces(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
19412
19664
|
}
|