@mastra/pg 1.32.0-alpha.2 → 1.32.0-alpha.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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +303 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +303 -21
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +4 -1
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/preview-compat.d.ts +1 -0
- package/dist/storage/domains/observability/v-next/preview-compat.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/span-query.d.ts +1 -0
- package/dist/storage/domains/observability/v-next/span-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/thread-query.d.ts +49 -0
- package/dist/storage/domains/observability/v-next/thread-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 +13 -0
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/dist/storage/domains/skills/index.d.ts.map +1 -1
- package/package.json +10 -11
package/dist/index.js
CHANGED
|
@@ -19185,6 +19185,28 @@ async function getScorePercentiles(client, schema, args) {
|
|
|
19185
19185
|
//#endregion
|
|
19186
19186
|
//#region src/storage/domains/observability/v-next/trace-query.ts
|
|
19187
19187
|
const TRACE_STATUS_SQL = `CASE WHEN r."error" IS NOT NULL THEN 'error' ELSE 'success' END`;
|
|
19188
|
+
/** Core versions without the field reject it in strict query results, so it is only returned when core knows it. */
|
|
19189
|
+
const timeToFirstChunkSupported = "timeToFirstChunkMs" in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;
|
|
19190
|
+
/** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
|
|
19191
|
+
const timeToFirstChunkSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
|
|
19192
|
+
const timeToFirstChunkFeatures = [...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [], ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : []];
|
|
19193
|
+
/**
|
|
19194
|
+
* Keyset bounds for a cursor's sort value: terms for rows strictly past it in `direction`, and the
|
|
19195
|
+
* condition for rows level with it. Empty values sort last in both directions, so they are past
|
|
19196
|
+
* every value and level only with each other.
|
|
19197
|
+
*/
|
|
19198
|
+
function keysetSortBounds(field, direction, sortValue, values) {
|
|
19199
|
+
if (sortValue === null) return {
|
|
19200
|
+
past: [],
|
|
19201
|
+
level: `${field} IS NULL`
|
|
19202
|
+
};
|
|
19203
|
+
values.push(sortValue);
|
|
19204
|
+
const parameter = `$${values.length}::${typeof sortValue === "number" ? "float8" : "timestamptz"}`;
|
|
19205
|
+
return {
|
|
19206
|
+
past: [`${field} ${direction === "asc" ? ">" : "<"} ${parameter}`, `${field} IS NULL`],
|
|
19207
|
+
level: `${field} = ${parameter}`
|
|
19208
|
+
};
|
|
19209
|
+
}
|
|
19188
19210
|
function durationMsSql(startedAt, endedAt) {
|
|
19189
19211
|
return `EXTRACT(EPOCH FROM (${endedAt} - ${startedAt}))::numeric * 1000`;
|
|
19190
19212
|
}
|
|
@@ -19199,8 +19221,11 @@ const TRACE_FIELDS = {
|
|
|
19199
19221
|
startedAt: "r.\"startedAt\"",
|
|
19200
19222
|
endedAt: "r.\"endedAt\"",
|
|
19201
19223
|
durationMs: durationMsSql("r.\"startedAt\"", "r.\"endedAt\""),
|
|
19224
|
+
timeToFirstChunkMs: "r.\"timeToFirstChunkMs\"",
|
|
19202
19225
|
entityName: "r.\"entityName\"",
|
|
19203
19226
|
entityType: "r.\"entityType\"",
|
|
19227
|
+
name: "r.\"name\"",
|
|
19228
|
+
spanType: "r.\"spanType\"",
|
|
19204
19229
|
environment: "r.\"environment\"",
|
|
19205
19230
|
status: TRACE_STATUS_SQL,
|
|
19206
19231
|
tags: "r.\"tags\""
|
|
@@ -19213,6 +19238,7 @@ const SPAN_FIELDS = {
|
|
|
19213
19238
|
startedAt: "s.\"startedAt\"",
|
|
19214
19239
|
endedAt: "s.\"endedAt\"",
|
|
19215
19240
|
durationMs: "s.\"durationMs\"",
|
|
19241
|
+
timeToFirstChunkMs: "s.\"timeToFirstChunkMs\"",
|
|
19216
19242
|
status: "s.\"status\"",
|
|
19217
19243
|
error: "s.\"error\"",
|
|
19218
19244
|
entityType: "s.\"entityType\"",
|
|
@@ -19282,6 +19308,7 @@ const TRACE_SELECT = `
|
|
|
19282
19308
|
r."resourceId" AS "resourceId",
|
|
19283
19309
|
r."startedAt" AS "startedAt",
|
|
19284
19310
|
r."endedAt" AS "endedAt",
|
|
19311
|
+
r."timeToFirstChunkMs" AS "timeToFirstChunkMs",
|
|
19285
19312
|
r."entityName" AS "entityName",
|
|
19286
19313
|
r."entityType" AS "entityType",
|
|
19287
19314
|
r."environment" AS "environment",
|
|
@@ -19405,11 +19432,11 @@ function compileScalarPredicate(predicate, registry, parameterOffset, allowMetad
|
|
|
19405
19432
|
gte: ">="
|
|
19406
19433
|
};
|
|
19407
19434
|
if (predicate.operator === "eq") return {
|
|
19408
|
-
sql: `${field} IS NOT
|
|
19435
|
+
sql: `${field} IS NOT NULL AND ${field} = ${parameter}`,
|
|
19409
19436
|
values: [...fieldValues, predicate.value]
|
|
19410
19437
|
};
|
|
19411
19438
|
if (predicate.operator === "ne") return {
|
|
19412
|
-
sql: `${field} IS
|
|
19439
|
+
sql: `${field} IS NULL OR ${field} <> ${parameter}`,
|
|
19413
19440
|
values: [...fieldValues, predicate.value]
|
|
19414
19441
|
};
|
|
19415
19442
|
const operator = operators[predicate.operator];
|
|
@@ -19498,6 +19525,8 @@ const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
|
|
|
19498
19525
|
"endedAt",
|
|
19499
19526
|
"entityName",
|
|
19500
19527
|
"entityType",
|
|
19528
|
+
"name",
|
|
19529
|
+
"spanType",
|
|
19501
19530
|
"environment"
|
|
19502
19531
|
]);
|
|
19503
19532
|
function isRootPushdownPredicate(predicate) {
|
|
@@ -19647,6 +19676,7 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
|
|
|
19647
19676
|
WHEN s."isPending" THEN NULL
|
|
19648
19677
|
ELSE ${durationMsSql("s.\"startedAt\"", "s.\"endedAt\"")}
|
|
19649
19678
|
END AS "durationMs",
|
|
19679
|
+
s."timeToFirstChunkMs",
|
|
19650
19680
|
CASE WHEN s."error" IS NOT NULL THEN 'error' ELSE 'success' END AS "status",
|
|
19651
19681
|
s."error",
|
|
19652
19682
|
s."entityType",
|
|
@@ -19768,8 +19798,8 @@ LIMIT $${values.length}`,
|
|
|
19768
19798
|
values
|
|
19769
19799
|
};
|
|
19770
19800
|
}
|
|
19771
|
-
const orderField = plan.orderBy.field
|
|
19772
|
-
const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC"
|
|
19801
|
+
const orderField = `"${plan.orderBy.field}"`;
|
|
19802
|
+
const direction = `${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST`;
|
|
19773
19803
|
if (plan.paginationMode === "page") {
|
|
19774
19804
|
values.push(plan.perPage, plan.page * plan.perPage);
|
|
19775
19805
|
return {
|
|
@@ -19783,10 +19813,9 @@ LIMIT $${values.length - 1} OFFSET $${values.length}`,
|
|
|
19783
19813
|
}
|
|
19784
19814
|
let pageCondition = "";
|
|
19785
19815
|
if (plan.cursor) {
|
|
19786
|
-
const
|
|
19787
|
-
|
|
19788
|
-
pageCondition = `WHERE (${
|
|
19789
|
-
values.push(plan.cursor.sortValue, plan.cursor.traceId);
|
|
19816
|
+
const { past, level } = keysetSortBounds(orderField, plan.orderBy.direction, plan.cursor.sortValue, values);
|
|
19817
|
+
values.push(plan.cursor.traceId);
|
|
19818
|
+
pageCondition = `WHERE (${[...past, `(${level} AND "traceId" > $${values.length})`].join(" OR ")})`;
|
|
19790
19819
|
}
|
|
19791
19820
|
values.push(plan.limit + 1);
|
|
19792
19821
|
return {
|
|
@@ -20021,6 +20050,7 @@ function traceRowToResult(row) {
|
|
|
20021
20050
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
20022
20051
|
startedAt: asIsoTimestamp$1(row.startedAt),
|
|
20023
20052
|
endedAt: asIsoTimestamp$1(row.endedAt),
|
|
20053
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) },
|
|
20024
20054
|
entityName: row.entityName == null ? null : String(row.entityName),
|
|
20025
20055
|
entityType: row.entityType == null ? null : String(row.entityType),
|
|
20026
20056
|
environment: row.environment == null ? null : String(row.environment),
|
|
@@ -20118,7 +20148,7 @@ async function queryTraces(client, schema, plan, timeoutMs) {
|
|
|
20118
20148
|
traces,
|
|
20119
20149
|
page: { next: rows.length > plan.limit && last ? coreStorage.encodeTraceQueryCursor(plan, {
|
|
20120
20150
|
result: "traces",
|
|
20121
|
-
sortValue: last[plan.orderBy.field],
|
|
20151
|
+
sortValue: last[plan.orderBy.field] ?? null,
|
|
20122
20152
|
traceId: last.traceId
|
|
20123
20153
|
}) : null }
|
|
20124
20154
|
});
|
|
@@ -20140,6 +20170,9 @@ async function queryThreads(client, schema, plan, timeoutMs) {
|
|
|
20140
20170
|
function compileSpanQueryPredicate(predicate, offset) {
|
|
20141
20171
|
return compileScalarPredicate(predicate, SPAN_QUERY_FIELDS, offset);
|
|
20142
20172
|
}
|
|
20173
|
+
function compileThreadQueryPredicate(predicate, offset) {
|
|
20174
|
+
return compileScalarPredicate(predicate, Object.fromEntries(Object.keys(coreStorage.THREAD_QUERY_FIELD_REGISTRY).map((field) => [field, `s."${field}"`])), offset);
|
|
20175
|
+
}
|
|
20143
20176
|
//#endregion
|
|
20144
20177
|
//#region src/storage/domains/observability/v-next/span-query.ts
|
|
20145
20178
|
const identityFields = [
|
|
@@ -20173,7 +20206,8 @@ const fields = [
|
|
|
20173
20206
|
"serviceName",
|
|
20174
20207
|
"experimentId",
|
|
20175
20208
|
"environment",
|
|
20176
|
-
"executionSource"
|
|
20209
|
+
"executionSource",
|
|
20210
|
+
"timeToFirstChunkMs"
|
|
20177
20211
|
];
|
|
20178
20212
|
const identities = identityFields.map((field) => `"${field}"`).join(", ");
|
|
20179
20213
|
const identityOrder = identityFields.map((field) => `"${field}" COLLATE "C" ASC NULLS LAST`).join(", ");
|
|
@@ -20186,11 +20220,8 @@ function selectedKeys(rows, values) {
|
|
|
20186
20220
|
function afterCursor(plan, values) {
|
|
20187
20221
|
if (!plan.cursor) return "TRUE";
|
|
20188
20222
|
const cursor = plan.cursor;
|
|
20189
|
-
|
|
20190
|
-
const
|
|
20191
|
-
const field = `s."${plan.orderBy.field}"`;
|
|
20192
|
-
const terms = [`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${timestamp}`];
|
|
20193
|
-
const equal = [`${field} = ${timestamp}`];
|
|
20223
|
+
const { past: terms, level } = keysetSortBounds(`s."${plan.orderBy.field}"`, plan.orderBy.direction, cursor.sortValue, values);
|
|
20224
|
+
const equal = [level];
|
|
20194
20225
|
for (const key of identityFields) {
|
|
20195
20226
|
const ref = `s."${key}" COLLATE "C"`;
|
|
20196
20227
|
const value = cursor[key];
|
|
@@ -20256,7 +20287,7 @@ function compilePostgresSpanQuery(schema, plan) {
|
|
|
20256
20287
|
"version"
|
|
20257
20288
|
].map((field) => `s."${field}"`).join(", ")}
|
|
20258
20289
|
FROM candidates s WHERE s."startedAt" >= $1 AND s."startedAt" < $2 AND (${predicate.sql}) AND ${after}
|
|
20259
|
-
ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityOrder}
|
|
20290
|
+
ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityOrder}
|
|
20260
20291
|
LIMIT $${values.length}`,
|
|
20261
20292
|
values
|
|
20262
20293
|
};
|
|
@@ -20289,8 +20320,9 @@ async function querySpans(client, schema, plan, timeoutMs) {
|
|
|
20289
20320
|
return runWithPostgresTraceQueryTimeout(client, timeoutMs, async (tx) => {
|
|
20290
20321
|
const query = compilePostgresSpanQuery(schema, plan);
|
|
20291
20322
|
const rows = await tx.any(query.text, query.values);
|
|
20292
|
-
const selected = rows.map(({ version: _version, startedAt, endedAt, ...row }) => ({
|
|
20323
|
+
const selected = rows.map(({ version: _version, startedAt, endedAt, timeToFirstChunkMs, ...row }) => ({
|
|
20293
20324
|
...row,
|
|
20325
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs },
|
|
20294
20326
|
startedAt: startedAt.toISOString(),
|
|
20295
20327
|
endedAt: endedAt.toISOString()
|
|
20296
20328
|
}));
|
|
@@ -20337,11 +20369,245 @@ async function querySpans(client, schema, plan, timeoutMs) {
|
|
|
20337
20369
|
}, { repeatableRead: true });
|
|
20338
20370
|
}
|
|
20339
20371
|
//#endregion
|
|
20372
|
+
//#region src/storage/domains/observability/v-next/thread-query.ts
|
|
20373
|
+
const contextFields = [
|
|
20374
|
+
"resourceId",
|
|
20375
|
+
"userId",
|
|
20376
|
+
"entityType",
|
|
20377
|
+
"entityId",
|
|
20378
|
+
"entityName",
|
|
20379
|
+
"entityVersionId",
|
|
20380
|
+
"runId",
|
|
20381
|
+
"sessionId",
|
|
20382
|
+
"requestId",
|
|
20383
|
+
"serviceName",
|
|
20384
|
+
"experimentId",
|
|
20385
|
+
"environment",
|
|
20386
|
+
"executionSource"
|
|
20387
|
+
];
|
|
20388
|
+
function predicateFields(predicate, fields = /* @__PURE__ */ new Set()) {
|
|
20389
|
+
if (!predicate) return fields;
|
|
20390
|
+
if (predicate.type === "boolean") for (const arg of predicate.args) predicateFields(arg, fields);
|
|
20391
|
+
else if (predicate.type === "not") predicateFields(predicate.arg, fields);
|
|
20392
|
+
else fields.add(predicate.field);
|
|
20393
|
+
return fields;
|
|
20394
|
+
}
|
|
20395
|
+
function canUseThreadCandidates(probe) {
|
|
20396
|
+
return Number(probe.threadCount) <= 16384 && Number(probe.traceCount) <= 1e5 && Number(probe.threadBytes) + Number(probe.traceBytes) <= 4 * 1024 * 1024;
|
|
20397
|
+
}
|
|
20398
|
+
function compilePostgresThreadRowsQuery(schema, plan, strategy = "full") {
|
|
20399
|
+
const values = [plan.timeRange.from, plan.timeRange.to];
|
|
20400
|
+
const scope = [];
|
|
20401
|
+
if (plan.scope) {
|
|
20402
|
+
values.push(plan.scope.organizationId);
|
|
20403
|
+
scope.push(`"organizationId" = $${values.length}`);
|
|
20404
|
+
if (plan.scope.resourceId !== void 0) {
|
|
20405
|
+
values.push(plan.scope.resourceId);
|
|
20406
|
+
scope.push(`"resourceId" = $${values.length}`);
|
|
20407
|
+
}
|
|
20408
|
+
}
|
|
20409
|
+
const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
|
|
20410
|
+
const timestamp = plan.timeRange.mode === "threads" ? plan.timeRange.field : "startedAt";
|
|
20411
|
+
const identity = (field) => `"organizationId" IS NULL, COALESCE("organizationId", ''), "${field}"`;
|
|
20412
|
+
const threadFilter = `(${identity("threadId")}) IN (SELECT ${identity("threadId")} FROM candidate_threads)`;
|
|
20413
|
+
const scopedRoots = `scoped_roots AS NOT MATERIALIZED (
|
|
20414
|
+
SELECT * FROM ${table} WHERE "parentSpanId" IS NULL ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
|
|
20415
|
+
)`;
|
|
20416
|
+
const candidates = `candidate_threads AS MATERIALIZED (
|
|
20417
|
+
SELECT DISTINCT "organizationId", "threadId" FROM scoped_roots
|
|
20418
|
+
WHERE "threadId" IS NOT NULL AND "${timestamp}" >= $1 AND "${timestamp}" < $2
|
|
20419
|
+
${strategy === "probe" ? "LIMIT 16385" : ""}
|
|
20420
|
+
), candidate_traces AS MATERIALIZED (
|
|
20421
|
+
SELECT DISTINCT "organizationId", "traceId" FROM scoped_roots WHERE ${threadFilter}
|
|
20422
|
+
${strategy === "probe" ? "LIMIT 100001" : ""}
|
|
20423
|
+
)`;
|
|
20424
|
+
if (strategy === "probe") return {
|
|
20425
|
+
text: `WITH ${scopedRoots}, ${candidates}
|
|
20426
|
+
SELECT (SELECT count(*) FROM candidate_threads) AS "threadCount",
|
|
20427
|
+
(SELECT COALESCE(sum(octet_length(COALESCE("organizationId", '')) + octet_length("threadId")), 0) FROM candidate_threads) AS "threadBytes",
|
|
20428
|
+
count(*) AS "traceCount", COALESCE(sum(octet_length(COALESCE("organizationId", '')) + octet_length("traceId")), 0) AS "traceBytes"
|
|
20429
|
+
FROM candidate_traces`,
|
|
20430
|
+
values
|
|
20431
|
+
};
|
|
20432
|
+
const predicate = plan.where ? compileThreadQueryPredicate(plan.where, values.length + 1) : {
|
|
20433
|
+
sql: "TRUE",
|
|
20434
|
+
values: []
|
|
20435
|
+
};
|
|
20436
|
+
values.push(...predicate.values);
|
|
20437
|
+
let after = "TRUE";
|
|
20438
|
+
if (plan.cursor) {
|
|
20439
|
+
values.push(plan.cursor.sortValue);
|
|
20440
|
+
const stamp = `$${values.length}::timestamptz`;
|
|
20441
|
+
const field = `s."${plan.orderBy.field}"`;
|
|
20442
|
+
const terms = [`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${stamp}`];
|
|
20443
|
+
const equal = [`${field} = ${stamp}`];
|
|
20444
|
+
for (const key of ["organizationId", "threadId"]) {
|
|
20445
|
+
const ref = `s."${key}" COLLATE "C"`;
|
|
20446
|
+
if (plan.cursor[key] === null) equal.push(`${ref} IS NULL`);
|
|
20447
|
+
else {
|
|
20448
|
+
values.push(plan.cursor[key]);
|
|
20449
|
+
const value = `$${values.length}::text COLLATE "C"`;
|
|
20450
|
+
terms.push(`(${equal.join(" AND ")} AND (${ref} > ${value} OR ${ref} IS NULL))`);
|
|
20451
|
+
equal.push(`${ref} = ${value}`);
|
|
20452
|
+
}
|
|
20453
|
+
}
|
|
20454
|
+
after = `(${terms.join(" OR ")})`;
|
|
20455
|
+
}
|
|
20456
|
+
const qualify = plan.timeRange.mode === "traces" ? "s.\"inWindow\"" : `s."${plan.timeRange.field}" >= $1 AND s."${plan.timeRange.field}" < $2`;
|
|
20457
|
+
const filtered = predicateFields(plan.where);
|
|
20458
|
+
const firstContext = [...contextFields, "timeToFirstChunkMs"].filter((field) => field === "resourceId" || filtered.has(field));
|
|
20459
|
+
const lastFields = [
|
|
20460
|
+
"traceId",
|
|
20461
|
+
"spanId",
|
|
20462
|
+
"cursorId",
|
|
20463
|
+
"resourceId",
|
|
20464
|
+
"endedAt",
|
|
20465
|
+
...filtered.has("status") ? ["status"] : []
|
|
20466
|
+
];
|
|
20467
|
+
values.push(plan.limit + 1);
|
|
20468
|
+
return {
|
|
20469
|
+
text: `WITH ${scopedRoots}, ${strategy === "candidates" ? `${candidates}, ` : ""}current_roots AS (
|
|
20470
|
+
SELECT DISTINCT ON ("organizationId", "traceId")
|
|
20471
|
+
"organizationId", "threadId", "traceId", "spanId", "cursorId", "startedAt", "endedAt", "isPending",
|
|
20472
|
+
${firstContext.map((field) => field === "timeToFirstChunkMs" ? `CASE WHEN "spanType" = 'agent_run' THEN "timeToFirstChunkMs" END AS "timeToFirstChunkMs"` : `"${field}"`).join(", ")}
|
|
20473
|
+
${filtered.has("status") ? ", CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status" : ""}
|
|
20474
|
+
FROM scoped_roots ${strategy === "candidates" ? `WHERE (${identity("traceId")}) IN (SELECT ${identity("traceId")} FROM candidate_traces)` : ""}
|
|
20475
|
+
ORDER BY "organizationId", "traceId", "startedAt" ASC, "spanId" COLLATE "C", "cursorId" DESC
|
|
20476
|
+
), ranked AS (
|
|
20477
|
+
SELECT *, row_number() OVER first_order AS rank,
|
|
20478
|
+
max("endedAt") OVER population AS "lastActivity", count(*) OVER population AS "traceCount",
|
|
20479
|
+
bool_or("startedAt" >= $1 AND "startedAt" < $2) OVER population AS "inWindow",
|
|
20480
|
+
${lastFields.map((field) => `first_value("${field}") OVER last_order AS "last${field[0].toUpperCase()}${field.slice(1)}"`).join(", ")}
|
|
20481
|
+
FROM current_roots WHERE NOT "isPending" AND "endedAt" IS NOT NULL AND "threadId" IS NOT NULL ${strategy === "candidates" ? `AND ${threadFilter}` : ""}
|
|
20482
|
+
WINDOW population AS (PARTITION BY "organizationId", "threadId"),
|
|
20483
|
+
first_order AS (PARTITION BY "organizationId", "threadId" ORDER BY "startedAt", "traceId" COLLATE "C"),
|
|
20484
|
+
last_order AS (PARTITION BY "organizationId", "threadId" ORDER BY "startedAt" DESC, "traceId" COLLATE "C" DESC)
|
|
20485
|
+
), rows AS (
|
|
20486
|
+
SELECT "organizationId", "threadId", "traceId" AS "firstTraceId", "spanId" AS "firstSpanId", "cursorId" AS "firstCursorId",
|
|
20487
|
+
"endedAt" AS "firstEndedAt", "startedAt", "lastActivity" AS "endedAt", "traceCount", "inWindow",
|
|
20488
|
+
${firstContext.map((field) => `"${field}"`).join(", ")},
|
|
20489
|
+
${lastFields.map((field) => `"last${field[0].toUpperCase()}${field.slice(1)}"${field === "status" ? " AS status" : ""}`).join(", ")}
|
|
20490
|
+
FROM ranked WHERE rank = 1
|
|
20491
|
+
) SELECT * FROM rows s WHERE ${qualify} AND (${predicate.sql}) AND ${after}
|
|
20492
|
+
ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"},
|
|
20493
|
+
s."organizationId" COLLATE "C" ASC NULLS LAST, s."threadId" COLLATE "C" ASC LIMIT $${values.length}`,
|
|
20494
|
+
values
|
|
20495
|
+
};
|
|
20496
|
+
}
|
|
20497
|
+
async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto") {
|
|
20498
|
+
const deadline = performance.now() + coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
20499
|
+
const run = async (text, values) => {
|
|
20500
|
+
const remaining = Math.floor(deadline - performance.now());
|
|
20501
|
+
if (remaining <= 0) throw new coreStorage.TraceQueryExecutionError();
|
|
20502
|
+
return await runWithPostgresTraceQueryTimeout(client, remaining, (tx) => tx.any(text, values));
|
|
20503
|
+
};
|
|
20504
|
+
if (strategy === "auto") {
|
|
20505
|
+
const probeQuery = compilePostgresThreadRowsQuery(schema, plan, "probe");
|
|
20506
|
+
const [probe] = await run(probeQuery.text, probeQuery.values);
|
|
20507
|
+
if (Number(probe.threadCount) === 0) return {
|
|
20508
|
+
threads: [],
|
|
20509
|
+
page: { next: null }
|
|
20510
|
+
};
|
|
20511
|
+
strategy = canUseThreadCandidates(probe) ? "candidates" : "full";
|
|
20512
|
+
}
|
|
20513
|
+
const compiled = compilePostgresThreadRowsQuery(schema, plan, strategy);
|
|
20514
|
+
const selected = await run(compiled.text, compiled.values);
|
|
20515
|
+
const page = selected.slice(0, plan.limit);
|
|
20516
|
+
if (!page.length) return {
|
|
20517
|
+
threads: [],
|
|
20518
|
+
page: { next: null }
|
|
20519
|
+
};
|
|
20520
|
+
const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
|
|
20521
|
+
const values = [];
|
|
20522
|
+
const keys = page.flatMap((row) => ["first", "last"].map((which) => {
|
|
20523
|
+
const entries = {
|
|
20524
|
+
organizationId: row.organizationId,
|
|
20525
|
+
resourceId: which === "first" ? row.resourceId : row.lastResourceId,
|
|
20526
|
+
traceId: row[`${which}TraceId`],
|
|
20527
|
+
spanId: row[`${which}SpanId`],
|
|
20528
|
+
cursorId: row[`${which}CursorId`],
|
|
20529
|
+
endedAt: row[`${which}EndedAt`]
|
|
20530
|
+
};
|
|
20531
|
+
return `(${Object.entries(entries).map(([field, value]) => {
|
|
20532
|
+
values.push(value);
|
|
20533
|
+
return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${values.length}`;
|
|
20534
|
+
}).join(" AND ")})`;
|
|
20535
|
+
}));
|
|
20536
|
+
const payloads = await run(`SELECT "organizationId", "traceId", "cursorId", "spanType", ${contextFields.map((field) => `"${field}"`).join(", ")},
|
|
20537
|
+
CASE WHEN "spanType" = 'agent_run' THEN "timeToFirstChunkMs" END AS "timeToFirstChunkMs",
|
|
20538
|
+
CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
|
|
20539
|
+
${[
|
|
20540
|
+
"input",
|
|
20541
|
+
"output",
|
|
20542
|
+
"error"
|
|
20543
|
+
].map((field) => `"${field}Preview", CASE WHEN ${missingPreviewSql(`"${field}Preview"`)} THEN "${field}" END AS "${field}"`).join(", ")}
|
|
20544
|
+
FROM ${table} WHERE ${keys.join(" OR ")}`, values);
|
|
20545
|
+
const key = (org, trace, version) => JSON.stringify([
|
|
20546
|
+
org,
|
|
20547
|
+
trace,
|
|
20548
|
+
String(version)
|
|
20549
|
+
]);
|
|
20550
|
+
const byRoot = new Map(payloads.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
|
|
20551
|
+
const agents = page.filter((row) => byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId))?.spanType === "agent_run");
|
|
20552
|
+
const models = /* @__PURE__ */ new Map();
|
|
20553
|
+
if (agents.length) {
|
|
20554
|
+
const modelValues = [];
|
|
20555
|
+
const rows = await run(`SELECT DISTINCT ON ("organizationId", "traceId") "organizationId", "traceId",
|
|
20556
|
+
CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
|
|
20557
|
+
CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider
|
|
20558
|
+
FROM ${table} WHERE "spanType" = 'model_generation' AND NOT "isPending" AND (${agents.map((row) => `(${Object.entries({
|
|
20559
|
+
organizationId: row.organizationId,
|
|
20560
|
+
resourceId: row.resourceId,
|
|
20561
|
+
traceId: row.firstTraceId,
|
|
20562
|
+
parentSpanId: row.firstSpanId
|
|
20563
|
+
}).map(([field, value]) => {
|
|
20564
|
+
modelValues.push(value);
|
|
20565
|
+
return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${modelValues.length}`;
|
|
20566
|
+
}).join(" AND ")})`).join(" OR ")})
|
|
20567
|
+
ORDER BY "organizationId", "traceId", "startedAt", "spanId" COLLATE "C", "cursorId" DESC`, modelValues);
|
|
20568
|
+
for (const row of rows) models.set(JSON.stringify([row.organizationId, row.traceId]), row);
|
|
20569
|
+
}
|
|
20570
|
+
const threads = page.flatMap((row) => {
|
|
20571
|
+
const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
|
|
20572
|
+
const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
|
|
20573
|
+
if (!first || !last) return [];
|
|
20574
|
+
const model = models.get(JSON.stringify([row.organizationId, row.firstTraceId]));
|
|
20575
|
+
return [coreStorage.threadQueryRowSchema.parse({
|
|
20576
|
+
organizationId: row.organizationId,
|
|
20577
|
+
threadId: row.threadId,
|
|
20578
|
+
firstTraceId: row.firstTraceId,
|
|
20579
|
+
lastTraceId: row.lastTraceId,
|
|
20580
|
+
...Object.fromEntries(contextFields.map((field) => [field, first[field]])),
|
|
20581
|
+
status: last.status,
|
|
20582
|
+
startedAt: row.startedAt.toISOString(),
|
|
20583
|
+
endedAt: row.endedAt.toISOString(),
|
|
20584
|
+
traceCount: Number(row.traceCount),
|
|
20585
|
+
timeToFirstChunkMs: first.timeToFirstChunkMs,
|
|
20586
|
+
model: model?.model ?? null,
|
|
20587
|
+
provider: model?.provider ?? null,
|
|
20588
|
+
inputPreview: readPreview(first.inputPreview, first.input),
|
|
20589
|
+
outputPreview: readPreview(last.outputPreview, last.output),
|
|
20590
|
+
errorPreview: readPreview(last.errorPreview, last.error)
|
|
20591
|
+
})];
|
|
20592
|
+
});
|
|
20593
|
+
const last = page.at(-1);
|
|
20594
|
+
return {
|
|
20595
|
+
threads,
|
|
20596
|
+
page: { next: selected.length > plan.limit ? coreStorage.encodeThreadQueryCursor(plan, {
|
|
20597
|
+
organizationId: last.organizationId,
|
|
20598
|
+
threadId: last.threadId,
|
|
20599
|
+
sortValue: last[plan.orderBy.field].toISOString()
|
|
20600
|
+
}) : null }
|
|
20601
|
+
};
|
|
20602
|
+
}
|
|
20603
|
+
//#endregion
|
|
20340
20604
|
//#region src/storage/domains/observability/v-next/trace-aggregate.ts
|
|
20341
20605
|
/** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
|
|
20342
20606
|
const DIMENSION_SQL = {
|
|
20343
20607
|
entityType: "r.\"entityType\"",
|
|
20344
20608
|
entityName: "r.\"entityName\"",
|
|
20609
|
+
name: "r.\"name\"",
|
|
20610
|
+
spanType: "r.\"spanType\"",
|
|
20345
20611
|
environment: "r.\"environment\"",
|
|
20346
20612
|
status: TRACE_STATUS_SQL,
|
|
20347
20613
|
serviceName: "r.\"serviceName\"",
|
|
@@ -21568,12 +21834,15 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
21568
21834
|
"trace-query",
|
|
21569
21835
|
"trace-aggregate",
|
|
21570
21836
|
...spanQueryFeatures,
|
|
21837
|
+
...typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
|
|
21571
21838
|
"trace-query-root-duration",
|
|
21572
21839
|
"trace-query-discovery",
|
|
21573
21840
|
"thread-query",
|
|
21574
21841
|
"trace-query-tenant-scope",
|
|
21575
21842
|
"feedback",
|
|
21576
|
-
"trace-query-context-ids"
|
|
21843
|
+
"trace-query-context-ids",
|
|
21844
|
+
...timeToFirstChunkFeatures,
|
|
21845
|
+
"trace-query-root-span-fields"
|
|
21577
21846
|
];
|
|
21578
21847
|
return [
|
|
21579
21848
|
"metrics",
|
|
@@ -21588,12 +21857,15 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
21588
21857
|
"trace-query",
|
|
21589
21858
|
"trace-aggregate",
|
|
21590
21859
|
...spanQueryFeatures,
|
|
21860
|
+
...typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
|
|
21591
21861
|
"trace-query-root-duration",
|
|
21592
21862
|
"trace-query-discovery",
|
|
21593
21863
|
"thread-query",
|
|
21594
21864
|
"trace-query-tenant-scope",
|
|
21595
21865
|
"feedback",
|
|
21596
|
-
"trace-query-context-ids"
|
|
21866
|
+
"trace-query-context-ids",
|
|
21867
|
+
...timeToFirstChunkFeatures,
|
|
21868
|
+
"trace-query-root-span-fields"
|
|
21597
21869
|
];
|
|
21598
21870
|
}
|
|
21599
21871
|
async #run(op, fn, details) {
|
|
@@ -21655,7 +21927,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
21655
21927
|
return this.#run("GET_TRACE_QUERY_VALUES", () => getTraceQueryValues(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
21656
21928
|
}
|
|
21657
21929
|
async queryThreads(plan) {
|
|
21658
|
-
return this.#run("QUERY_THREADS", () => queryThreads(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
21930
|
+
return this.#run("QUERY_THREADS", () => plan.result === "threadRows" ? queryThreadRows(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs) : queryThreads(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
21659
21931
|
}
|
|
21660
21932
|
async listBranches(args) {
|
|
21661
21933
|
return this.#run("LIST_BRANCHES", () => listBranches(this.#readClient, this.#schema, args));
|
|
@@ -24087,7 +24359,7 @@ var SkillsPG = class SkillsPG extends SkillsStorage {
|
|
|
24087
24359
|
}
|
|
24088
24360
|
}
|
|
24089
24361
|
async list(args) {
|
|
24090
|
-
const { page = 0, perPage: perPageInput, orderBy, authorId, visibility, status, entityIds, pinFavoritedFor, favoritedOnly } = args || {};
|
|
24362
|
+
const { page = 0, perPage: perPageInput, orderBy, authorId, visibility, status, entityIds, pinFavoritedFor, favoritedOnly, metadata } = args || {};
|
|
24091
24363
|
const { field, direction } = this.parseOrderBy(orderBy);
|
|
24092
24364
|
if (page < 0) throw new MastraError({
|
|
24093
24365
|
id: createStorageErrorId("PG", "LIST_SKILLS", "INVALID_PAGE"),
|
|
@@ -24132,6 +24404,16 @@ var SkillsPG = class SkillsPG extends SkillsStorage {
|
|
|
24132
24404
|
conditions.push(`s.visibility = $${paramIdx++}`);
|
|
24133
24405
|
queryParams.push(visibility);
|
|
24134
24406
|
}
|
|
24407
|
+
if (metadata && Object.keys(metadata).length > 0) {
|
|
24408
|
+
const versionsTable = getTableName$5({
|
|
24409
|
+
indexName: TABLE_SKILL_VERSIONS,
|
|
24410
|
+
schemaName: getSchemaName$5(this.#schema)
|
|
24411
|
+
});
|
|
24412
|
+
const latestVersionId = `(SELECT lv.id FROM ${versionsTable} lv WHERE lv."skillId" = s.id ORDER BY lv."versionNumber" DESC LIMIT 1)`;
|
|
24413
|
+
const resolvedVersionId = status === "draft" ? latestVersionId : `COALESCE(s."activeVersionId", ${latestVersionId})`;
|
|
24414
|
+
conditions.push(`EXISTS (SELECT 1 FROM ${versionsTable} mv WHERE mv."skillId" = s.id AND mv.id = ${resolvedVersionId} AND mv.metadata @> $${paramIdx++}::jsonb)`);
|
|
24415
|
+
queryParams.push(toPgJson(metadata));
|
|
24416
|
+
}
|
|
24135
24417
|
if (entityIds && entityIds.length > 0) {
|
|
24136
24418
|
const placeholders = entityIds.map(() => `$${paramIdx++}`).join(", ");
|
|
24137
24419
|
conditions.push(`s.id IN (${placeholders})`);
|