@mastra/pg 1.32.0-alpha.1 → 1.32.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/docs/references/docs-memory-semantic-recall.md +2 -0
- package/dist/index.cjs +286 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +286 -8
- 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/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/helpers.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/signal-schema.d.ts +4 -0
- package/dist/storage/domains/observability/v-next/signal-schema.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 +4 -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 +9 -10
package/dist/docs/SKILL.md
CHANGED
|
@@ -403,6 +403,8 @@ Memory uses a single embedder for both storing and recalling messages, so pick o
|
|
|
403
403
|
|
|
404
404
|
Multilingual E5 produces 1024-dimensional vectors. Your vector index must be created with matching dimensions, and E5 vectors can't be mixed with vectors from another embedder in the same index.
|
|
405
405
|
|
|
406
|
+
FastEmbed models embed at most 512 tokens per input (roughly 2,000 to 2,500 characters of English text). Longer inputs are truncated, so text past the limit doesn't affect the embedding and can't be found by semantic recall. The AI SDK v3 FastEmbed models return a warning when this happens. The legacy v1 and v2 models don't. If you embed long documents with FastEmbed, chunk them to fit the window, for example with `maxSize: 1500` in `MDocument.chunk()`.
|
|
407
|
+
|
|
406
408
|
## PostgreSQL index optimization
|
|
407
409
|
|
|
408
410
|
When using PostgreSQL as your vector store, you can optimize semantic recall performance by configuring the vector index. This is particularly important for large-scale deployments with thousands of messages.
|
package/dist/index.cjs
CHANGED
|
@@ -16034,6 +16034,11 @@ const SPAN_EVENT_COLUMNS = [
|
|
|
16034
16034
|
name: "endedAt",
|
|
16035
16035
|
type: "timestamptz"
|
|
16036
16036
|
},
|
|
16037
|
+
{
|
|
16038
|
+
name: "timeToFirstChunkMs",
|
|
16039
|
+
type: "double precision",
|
|
16040
|
+
nullable: true
|
|
16041
|
+
},
|
|
16037
16042
|
{
|
|
16038
16043
|
name: "tags",
|
|
16039
16044
|
type: "text[]",
|
|
@@ -16768,6 +16773,11 @@ function additiveColumns(schema) {
|
|
|
16768
16773
|
column,
|
|
16769
16774
|
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "${column}" text`
|
|
16770
16775
|
})),
|
|
16776
|
+
{
|
|
16777
|
+
table: TABLE_SPAN_EVENTS,
|
|
16778
|
+
column: "timeToFirstChunkMs",
|
|
16779
|
+
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "timeToFirstChunkMs" double precision`
|
|
16780
|
+
},
|
|
16771
16781
|
{
|
|
16772
16782
|
table: TABLE_FEEDBACK_EVENTS,
|
|
16773
16783
|
column: "reviewStatus",
|
|
@@ -17489,6 +17499,7 @@ function spanRecordToRow(span) {
|
|
|
17489
17499
|
isPending,
|
|
17490
17500
|
startedAt: toIsoOrDate(span.startedAt),
|
|
17491
17501
|
endedAt: toIsoOrDate(endedAt),
|
|
17502
|
+
timeToFirstChunkMs: span.timeToFirstChunkMs ?? null,
|
|
17492
17503
|
tags: normalizeTags(span.tags),
|
|
17493
17504
|
metadataSearch: buildMetadataSearch(metadata),
|
|
17494
17505
|
metadataRaw: jsonField(metadata),
|
|
@@ -19198,6 +19209,9 @@ async function getScorePercentiles(client, schema, args) {
|
|
|
19198
19209
|
//#endregion
|
|
19199
19210
|
//#region src/storage/domains/observability/v-next/trace-query.ts
|
|
19200
19211
|
const TRACE_STATUS_SQL = `CASE WHEN r."error" IS NOT NULL THEN 'error' ELSE 'success' END`;
|
|
19212
|
+
/** Core versions without the field reject it in strict query results, so it is only returned when core knows it. */
|
|
19213
|
+
const timeToFirstChunkSupported = "timeToFirstChunkMs" in _mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY.trace;
|
|
19214
|
+
const timeToFirstChunkFeatures = timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [];
|
|
19201
19215
|
function durationMsSql(startedAt, endedAt) {
|
|
19202
19216
|
return `EXTRACT(EPOCH FROM (${endedAt} - ${startedAt}))::numeric * 1000`;
|
|
19203
19217
|
}
|
|
@@ -19212,8 +19226,11 @@ const TRACE_FIELDS = {
|
|
|
19212
19226
|
startedAt: "r.\"startedAt\"",
|
|
19213
19227
|
endedAt: "r.\"endedAt\"",
|
|
19214
19228
|
durationMs: durationMsSql("r.\"startedAt\"", "r.\"endedAt\""),
|
|
19229
|
+
timeToFirstChunkMs: "r.\"timeToFirstChunkMs\"",
|
|
19215
19230
|
entityName: "r.\"entityName\"",
|
|
19216
19231
|
entityType: "r.\"entityType\"",
|
|
19232
|
+
name: "r.\"name\"",
|
|
19233
|
+
spanType: "r.\"spanType\"",
|
|
19217
19234
|
environment: "r.\"environment\"",
|
|
19218
19235
|
status: TRACE_STATUS_SQL,
|
|
19219
19236
|
tags: "r.\"tags\""
|
|
@@ -19226,6 +19243,7 @@ const SPAN_FIELDS = {
|
|
|
19226
19243
|
startedAt: "s.\"startedAt\"",
|
|
19227
19244
|
endedAt: "s.\"endedAt\"",
|
|
19228
19245
|
durationMs: "s.\"durationMs\"",
|
|
19246
|
+
timeToFirstChunkMs: "s.\"timeToFirstChunkMs\"",
|
|
19229
19247
|
status: "s.\"status\"",
|
|
19230
19248
|
error: "s.\"error\"",
|
|
19231
19249
|
entityType: "s.\"entityType\"",
|
|
@@ -19295,6 +19313,7 @@ const TRACE_SELECT = `
|
|
|
19295
19313
|
r."resourceId" AS "resourceId",
|
|
19296
19314
|
r."startedAt" AS "startedAt",
|
|
19297
19315
|
r."endedAt" AS "endedAt",
|
|
19316
|
+
r."timeToFirstChunkMs" AS "timeToFirstChunkMs",
|
|
19298
19317
|
r."entityName" AS "entityName",
|
|
19299
19318
|
r."entityType" AS "entityType",
|
|
19300
19319
|
r."environment" AS "environment",
|
|
@@ -19418,11 +19437,11 @@ function compileScalarPredicate(predicate, registry, parameterOffset, allowMetad
|
|
|
19418
19437
|
gte: ">="
|
|
19419
19438
|
};
|
|
19420
19439
|
if (predicate.operator === "eq") return {
|
|
19421
|
-
sql: `${field} IS NOT
|
|
19440
|
+
sql: `${field} IS NOT NULL AND ${field} = ${parameter}`,
|
|
19422
19441
|
values: [...fieldValues, predicate.value]
|
|
19423
19442
|
};
|
|
19424
19443
|
if (predicate.operator === "ne") return {
|
|
19425
|
-
sql: `${field} IS
|
|
19444
|
+
sql: `${field} IS NULL OR ${field} <> ${parameter}`,
|
|
19426
19445
|
values: [...fieldValues, predicate.value]
|
|
19427
19446
|
};
|
|
19428
19447
|
const operator = operators[predicate.operator];
|
|
@@ -19511,6 +19530,8 @@ const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
|
|
|
19511
19530
|
"endedAt",
|
|
19512
19531
|
"entityName",
|
|
19513
19532
|
"entityType",
|
|
19533
|
+
"name",
|
|
19534
|
+
"spanType",
|
|
19514
19535
|
"environment"
|
|
19515
19536
|
]);
|
|
19516
19537
|
function isRootPushdownPredicate(predicate) {
|
|
@@ -19660,6 +19681,7 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
|
|
|
19660
19681
|
WHEN s."isPending" THEN NULL
|
|
19661
19682
|
ELSE ${durationMsSql("s.\"startedAt\"", "s.\"endedAt\"")}
|
|
19662
19683
|
END AS "durationMs",
|
|
19684
|
+
s."timeToFirstChunkMs",
|
|
19663
19685
|
CASE WHEN s."error" IS NOT NULL THEN 'error' ELSE 'success' END AS "status",
|
|
19664
19686
|
s."error",
|
|
19665
19687
|
s."entityType",
|
|
@@ -20034,6 +20056,7 @@ function traceRowToResult(row) {
|
|
|
20034
20056
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
20035
20057
|
startedAt: asIsoTimestamp$1(row.startedAt),
|
|
20036
20058
|
endedAt: asIsoTimestamp$1(row.endedAt),
|
|
20059
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) },
|
|
20037
20060
|
entityName: row.entityName == null ? null : String(row.entityName),
|
|
20038
20061
|
entityType: row.entityType == null ? null : String(row.entityType),
|
|
20039
20062
|
environment: row.environment == null ? null : String(row.environment),
|
|
@@ -20153,6 +20176,9 @@ async function queryThreads(client, schema, plan, timeoutMs) {
|
|
|
20153
20176
|
function compileSpanQueryPredicate(predicate, offset) {
|
|
20154
20177
|
return compileScalarPredicate(predicate, SPAN_QUERY_FIELDS, offset);
|
|
20155
20178
|
}
|
|
20179
|
+
function compileThreadQueryPredicate(predicate, offset) {
|
|
20180
|
+
return compileScalarPredicate(predicate, Object.fromEntries(Object.keys(_mastra_core_storage.THREAD_QUERY_FIELD_REGISTRY).map((field) => [field, `s."${field}"`])), offset);
|
|
20181
|
+
}
|
|
20156
20182
|
//#endregion
|
|
20157
20183
|
//#region src/storage/domains/observability/v-next/span-query.ts
|
|
20158
20184
|
const identityFields = [
|
|
@@ -20186,7 +20212,8 @@ const fields = [
|
|
|
20186
20212
|
"serviceName",
|
|
20187
20213
|
"experimentId",
|
|
20188
20214
|
"environment",
|
|
20189
|
-
"executionSource"
|
|
20215
|
+
"executionSource",
|
|
20216
|
+
"timeToFirstChunkMs"
|
|
20190
20217
|
];
|
|
20191
20218
|
const identities = identityFields.map((field) => `"${field}"`).join(", ");
|
|
20192
20219
|
const identityOrder = identityFields.map((field) => `"${field}" COLLATE "C" ASC NULLS LAST`).join(", ");
|
|
@@ -20302,8 +20329,9 @@ async function querySpans(client, schema, plan, timeoutMs) {
|
|
|
20302
20329
|
return runWithPostgresTraceQueryTimeout(client, timeoutMs, async (tx) => {
|
|
20303
20330
|
const query = compilePostgresSpanQuery(schema, plan);
|
|
20304
20331
|
const rows = await tx.any(query.text, query.values);
|
|
20305
|
-
const selected = rows.map(({ version: _version, startedAt, endedAt, ...row }) => ({
|
|
20332
|
+
const selected = rows.map(({ version: _version, startedAt, endedAt, timeToFirstChunkMs, ...row }) => ({
|
|
20306
20333
|
...row,
|
|
20334
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs },
|
|
20307
20335
|
startedAt: startedAt.toISOString(),
|
|
20308
20336
|
endedAt: endedAt.toISOString()
|
|
20309
20337
|
}));
|
|
@@ -20350,11 +20378,245 @@ async function querySpans(client, schema, plan, timeoutMs) {
|
|
|
20350
20378
|
}, { repeatableRead: true });
|
|
20351
20379
|
}
|
|
20352
20380
|
//#endregion
|
|
20381
|
+
//#region src/storage/domains/observability/v-next/thread-query.ts
|
|
20382
|
+
const contextFields = [
|
|
20383
|
+
"resourceId",
|
|
20384
|
+
"userId",
|
|
20385
|
+
"entityType",
|
|
20386
|
+
"entityId",
|
|
20387
|
+
"entityName",
|
|
20388
|
+
"entityVersionId",
|
|
20389
|
+
"runId",
|
|
20390
|
+
"sessionId",
|
|
20391
|
+
"requestId",
|
|
20392
|
+
"serviceName",
|
|
20393
|
+
"experimentId",
|
|
20394
|
+
"environment",
|
|
20395
|
+
"executionSource"
|
|
20396
|
+
];
|
|
20397
|
+
function predicateFields(predicate, fields = /* @__PURE__ */ new Set()) {
|
|
20398
|
+
if (!predicate) return fields;
|
|
20399
|
+
if (predicate.type === "boolean") for (const arg of predicate.args) predicateFields(arg, fields);
|
|
20400
|
+
else if (predicate.type === "not") predicateFields(predicate.arg, fields);
|
|
20401
|
+
else fields.add(predicate.field);
|
|
20402
|
+
return fields;
|
|
20403
|
+
}
|
|
20404
|
+
function canUseThreadCandidates(probe) {
|
|
20405
|
+
return Number(probe.threadCount) <= 16384 && Number(probe.traceCount) <= 1e5 && Number(probe.threadBytes) + Number(probe.traceBytes) <= 4 * 1024 * 1024;
|
|
20406
|
+
}
|
|
20407
|
+
function compilePostgresThreadRowsQuery(schema, plan, strategy = "full") {
|
|
20408
|
+
const values = [plan.timeRange.from, plan.timeRange.to];
|
|
20409
|
+
const scope = [];
|
|
20410
|
+
if (plan.scope) {
|
|
20411
|
+
values.push(plan.scope.organizationId);
|
|
20412
|
+
scope.push(`"organizationId" = $${values.length}`);
|
|
20413
|
+
if (plan.scope.resourceId !== void 0) {
|
|
20414
|
+
values.push(plan.scope.resourceId);
|
|
20415
|
+
scope.push(`"resourceId" = $${values.length}`);
|
|
20416
|
+
}
|
|
20417
|
+
}
|
|
20418
|
+
const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
|
|
20419
|
+
const timestamp = plan.timeRange.mode === "threads" ? plan.timeRange.field : "startedAt";
|
|
20420
|
+
const identity = (field) => `"organizationId" IS NULL, COALESCE("organizationId", ''), "${field}"`;
|
|
20421
|
+
const threadFilter = `(${identity("threadId")}) IN (SELECT ${identity("threadId")} FROM candidate_threads)`;
|
|
20422
|
+
const scopedRoots = `scoped_roots AS NOT MATERIALIZED (
|
|
20423
|
+
SELECT * FROM ${table} WHERE "parentSpanId" IS NULL ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
|
|
20424
|
+
)`;
|
|
20425
|
+
const candidates = `candidate_threads AS MATERIALIZED (
|
|
20426
|
+
SELECT DISTINCT "organizationId", "threadId" FROM scoped_roots
|
|
20427
|
+
WHERE "threadId" IS NOT NULL AND "${timestamp}" >= $1 AND "${timestamp}" < $2
|
|
20428
|
+
${strategy === "probe" ? "LIMIT 16385" : ""}
|
|
20429
|
+
), candidate_traces AS MATERIALIZED (
|
|
20430
|
+
SELECT DISTINCT "organizationId", "traceId" FROM scoped_roots WHERE ${threadFilter}
|
|
20431
|
+
${strategy === "probe" ? "LIMIT 100001" : ""}
|
|
20432
|
+
)`;
|
|
20433
|
+
if (strategy === "probe") return {
|
|
20434
|
+
text: `WITH ${scopedRoots}, ${candidates}
|
|
20435
|
+
SELECT (SELECT count(*) FROM candidate_threads) AS "threadCount",
|
|
20436
|
+
(SELECT COALESCE(sum(octet_length(COALESCE("organizationId", '')) + octet_length("threadId")), 0) FROM candidate_threads) AS "threadBytes",
|
|
20437
|
+
count(*) AS "traceCount", COALESCE(sum(octet_length(COALESCE("organizationId", '')) + octet_length("traceId")), 0) AS "traceBytes"
|
|
20438
|
+
FROM candidate_traces`,
|
|
20439
|
+
values
|
|
20440
|
+
};
|
|
20441
|
+
const predicate = plan.where ? compileThreadQueryPredicate(plan.where, values.length + 1) : {
|
|
20442
|
+
sql: "TRUE",
|
|
20443
|
+
values: []
|
|
20444
|
+
};
|
|
20445
|
+
values.push(...predicate.values);
|
|
20446
|
+
let after = "TRUE";
|
|
20447
|
+
if (plan.cursor) {
|
|
20448
|
+
values.push(plan.cursor.sortValue);
|
|
20449
|
+
const stamp = `$${values.length}::timestamptz`;
|
|
20450
|
+
const field = `s."${plan.orderBy.field}"`;
|
|
20451
|
+
const terms = [`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${stamp}`];
|
|
20452
|
+
const equal = [`${field} = ${stamp}`];
|
|
20453
|
+
for (const key of ["organizationId", "threadId"]) {
|
|
20454
|
+
const ref = `s."${key}" COLLATE "C"`;
|
|
20455
|
+
if (plan.cursor[key] === null) equal.push(`${ref} IS NULL`);
|
|
20456
|
+
else {
|
|
20457
|
+
values.push(plan.cursor[key]);
|
|
20458
|
+
const value = `$${values.length}::text COLLATE "C"`;
|
|
20459
|
+
terms.push(`(${equal.join(" AND ")} AND (${ref} > ${value} OR ${ref} IS NULL))`);
|
|
20460
|
+
equal.push(`${ref} = ${value}`);
|
|
20461
|
+
}
|
|
20462
|
+
}
|
|
20463
|
+
after = `(${terms.join(" OR ")})`;
|
|
20464
|
+
}
|
|
20465
|
+
const qualify = plan.timeRange.mode === "traces" ? "s.\"inWindow\"" : `s."${plan.timeRange.field}" >= $1 AND s."${plan.timeRange.field}" < $2`;
|
|
20466
|
+
const filtered = predicateFields(plan.where);
|
|
20467
|
+
const firstContext = [...contextFields, "timeToFirstChunkMs"].filter((field) => field === "resourceId" || filtered.has(field));
|
|
20468
|
+
const lastFields = [
|
|
20469
|
+
"traceId",
|
|
20470
|
+
"spanId",
|
|
20471
|
+
"cursorId",
|
|
20472
|
+
"resourceId",
|
|
20473
|
+
"endedAt",
|
|
20474
|
+
...filtered.has("status") ? ["status"] : []
|
|
20475
|
+
];
|
|
20476
|
+
values.push(plan.limit + 1);
|
|
20477
|
+
return {
|
|
20478
|
+
text: `WITH ${scopedRoots}, ${strategy === "candidates" ? `${candidates}, ` : ""}current_roots AS (
|
|
20479
|
+
SELECT DISTINCT ON ("organizationId", "traceId")
|
|
20480
|
+
"organizationId", "threadId", "traceId", "spanId", "cursorId", "startedAt", "endedAt", "isPending",
|
|
20481
|
+
${firstContext.map((field) => field === "timeToFirstChunkMs" ? `CASE WHEN "spanType" = 'agent_run' THEN "timeToFirstChunkMs" END AS "timeToFirstChunkMs"` : `"${field}"`).join(", ")}
|
|
20482
|
+
${filtered.has("status") ? ", CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status" : ""}
|
|
20483
|
+
FROM scoped_roots ${strategy === "candidates" ? `WHERE (${identity("traceId")}) IN (SELECT ${identity("traceId")} FROM candidate_traces)` : ""}
|
|
20484
|
+
ORDER BY "organizationId", "traceId", "startedAt" ASC, "spanId" COLLATE "C", "cursorId" DESC
|
|
20485
|
+
), ranked AS (
|
|
20486
|
+
SELECT *, row_number() OVER first_order AS rank,
|
|
20487
|
+
max("endedAt") OVER population AS "lastActivity", count(*) OVER population AS "traceCount",
|
|
20488
|
+
bool_or("startedAt" >= $1 AND "startedAt" < $2) OVER population AS "inWindow",
|
|
20489
|
+
${lastFields.map((field) => `first_value("${field}") OVER last_order AS "last${field[0].toUpperCase()}${field.slice(1)}"`).join(", ")}
|
|
20490
|
+
FROM current_roots WHERE NOT "isPending" AND "endedAt" IS NOT NULL AND "threadId" IS NOT NULL ${strategy === "candidates" ? `AND ${threadFilter}` : ""}
|
|
20491
|
+
WINDOW population AS (PARTITION BY "organizationId", "threadId"),
|
|
20492
|
+
first_order AS (PARTITION BY "organizationId", "threadId" ORDER BY "startedAt", "traceId" COLLATE "C"),
|
|
20493
|
+
last_order AS (PARTITION BY "organizationId", "threadId" ORDER BY "startedAt" DESC, "traceId" COLLATE "C" DESC)
|
|
20494
|
+
), rows AS (
|
|
20495
|
+
SELECT "organizationId", "threadId", "traceId" AS "firstTraceId", "spanId" AS "firstSpanId", "cursorId" AS "firstCursorId",
|
|
20496
|
+
"endedAt" AS "firstEndedAt", "startedAt", "lastActivity" AS "endedAt", "traceCount", "inWindow",
|
|
20497
|
+
${firstContext.map((field) => `"${field}"`).join(", ")},
|
|
20498
|
+
${lastFields.map((field) => `"last${field[0].toUpperCase()}${field.slice(1)}"${field === "status" ? " AS status" : ""}`).join(", ")}
|
|
20499
|
+
FROM ranked WHERE rank = 1
|
|
20500
|
+
) SELECT * FROM rows s WHERE ${qualify} AND (${predicate.sql}) AND ${after}
|
|
20501
|
+
ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"},
|
|
20502
|
+
s."organizationId" COLLATE "C" ASC NULLS LAST, s."threadId" COLLATE "C" ASC LIMIT $${values.length}`,
|
|
20503
|
+
values
|
|
20504
|
+
};
|
|
20505
|
+
}
|
|
20506
|
+
async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto") {
|
|
20507
|
+
const deadline = performance.now() + _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
20508
|
+
const run = async (text, values) => {
|
|
20509
|
+
const remaining = Math.floor(deadline - performance.now());
|
|
20510
|
+
if (remaining <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
|
|
20511
|
+
return await runWithPostgresTraceQueryTimeout(client, remaining, (tx) => tx.any(text, values));
|
|
20512
|
+
};
|
|
20513
|
+
if (strategy === "auto") {
|
|
20514
|
+
const probeQuery = compilePostgresThreadRowsQuery(schema, plan, "probe");
|
|
20515
|
+
const [probe] = await run(probeQuery.text, probeQuery.values);
|
|
20516
|
+
if (Number(probe.threadCount) === 0) return {
|
|
20517
|
+
threads: [],
|
|
20518
|
+
page: { next: null }
|
|
20519
|
+
};
|
|
20520
|
+
strategy = canUseThreadCandidates(probe) ? "candidates" : "full";
|
|
20521
|
+
}
|
|
20522
|
+
const compiled = compilePostgresThreadRowsQuery(schema, plan, strategy);
|
|
20523
|
+
const selected = await run(compiled.text, compiled.values);
|
|
20524
|
+
const page = selected.slice(0, plan.limit);
|
|
20525
|
+
if (!page.length) return {
|
|
20526
|
+
threads: [],
|
|
20527
|
+
page: { next: null }
|
|
20528
|
+
};
|
|
20529
|
+
const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
|
|
20530
|
+
const values = [];
|
|
20531
|
+
const keys = page.flatMap((row) => ["first", "last"].map((which) => {
|
|
20532
|
+
const entries = {
|
|
20533
|
+
organizationId: row.organizationId,
|
|
20534
|
+
resourceId: which === "first" ? row.resourceId : row.lastResourceId,
|
|
20535
|
+
traceId: row[`${which}TraceId`],
|
|
20536
|
+
spanId: row[`${which}SpanId`],
|
|
20537
|
+
cursorId: row[`${which}CursorId`],
|
|
20538
|
+
endedAt: row[`${which}EndedAt`]
|
|
20539
|
+
};
|
|
20540
|
+
return `(${Object.entries(entries).map(([field, value]) => {
|
|
20541
|
+
values.push(value);
|
|
20542
|
+
return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${values.length}`;
|
|
20543
|
+
}).join(" AND ")})`;
|
|
20544
|
+
}));
|
|
20545
|
+
const payloads = await run(`SELECT "organizationId", "traceId", "cursorId", "spanType", ${contextFields.map((field) => `"${field}"`).join(", ")},
|
|
20546
|
+
CASE WHEN "spanType" = 'agent_run' THEN "timeToFirstChunkMs" END AS "timeToFirstChunkMs",
|
|
20547
|
+
CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
|
|
20548
|
+
${[
|
|
20549
|
+
"input",
|
|
20550
|
+
"output",
|
|
20551
|
+
"error"
|
|
20552
|
+
].map((field) => `"${field}Preview", CASE WHEN ${missingPreviewSql(`"${field}Preview"`)} THEN "${field}" END AS "${field}"`).join(", ")}
|
|
20553
|
+
FROM ${table} WHERE ${keys.join(" OR ")}`, values);
|
|
20554
|
+
const key = (org, trace, version) => JSON.stringify([
|
|
20555
|
+
org,
|
|
20556
|
+
trace,
|
|
20557
|
+
String(version)
|
|
20558
|
+
]);
|
|
20559
|
+
const byRoot = new Map(payloads.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
|
|
20560
|
+
const agents = page.filter((row) => byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId))?.spanType === "agent_run");
|
|
20561
|
+
const models = /* @__PURE__ */ new Map();
|
|
20562
|
+
if (agents.length) {
|
|
20563
|
+
const modelValues = [];
|
|
20564
|
+
const rows = await run(`SELECT DISTINCT ON ("organizationId", "traceId") "organizationId", "traceId",
|
|
20565
|
+
CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
|
|
20566
|
+
CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider
|
|
20567
|
+
FROM ${table} WHERE "spanType" = 'model_generation' AND NOT "isPending" AND (${agents.map((row) => `(${Object.entries({
|
|
20568
|
+
organizationId: row.organizationId,
|
|
20569
|
+
resourceId: row.resourceId,
|
|
20570
|
+
traceId: row.firstTraceId,
|
|
20571
|
+
parentSpanId: row.firstSpanId
|
|
20572
|
+
}).map(([field, value]) => {
|
|
20573
|
+
modelValues.push(value);
|
|
20574
|
+
return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${modelValues.length}`;
|
|
20575
|
+
}).join(" AND ")})`).join(" OR ")})
|
|
20576
|
+
ORDER BY "organizationId", "traceId", "startedAt", "spanId" COLLATE "C", "cursorId" DESC`, modelValues);
|
|
20577
|
+
for (const row of rows) models.set(JSON.stringify([row.organizationId, row.traceId]), row);
|
|
20578
|
+
}
|
|
20579
|
+
const threads = page.flatMap((row) => {
|
|
20580
|
+
const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
|
|
20581
|
+
const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
|
|
20582
|
+
if (!first || !last) return [];
|
|
20583
|
+
const model = models.get(JSON.stringify([row.organizationId, row.firstTraceId]));
|
|
20584
|
+
return [_mastra_core_storage.threadQueryRowSchema.parse({
|
|
20585
|
+
organizationId: row.organizationId,
|
|
20586
|
+
threadId: row.threadId,
|
|
20587
|
+
firstTraceId: row.firstTraceId,
|
|
20588
|
+
lastTraceId: row.lastTraceId,
|
|
20589
|
+
...Object.fromEntries(contextFields.map((field) => [field, first[field]])),
|
|
20590
|
+
status: last.status,
|
|
20591
|
+
startedAt: row.startedAt.toISOString(),
|
|
20592
|
+
endedAt: row.endedAt.toISOString(),
|
|
20593
|
+
traceCount: Number(row.traceCount),
|
|
20594
|
+
timeToFirstChunkMs: first.timeToFirstChunkMs,
|
|
20595
|
+
model: model?.model ?? null,
|
|
20596
|
+
provider: model?.provider ?? null,
|
|
20597
|
+
inputPreview: readPreview(first.inputPreview, first.input),
|
|
20598
|
+
outputPreview: readPreview(last.outputPreview, last.output),
|
|
20599
|
+
errorPreview: readPreview(last.errorPreview, last.error)
|
|
20600
|
+
})];
|
|
20601
|
+
});
|
|
20602
|
+
const last = page.at(-1);
|
|
20603
|
+
return {
|
|
20604
|
+
threads,
|
|
20605
|
+
page: { next: selected.length > plan.limit ? _mastra_core_storage.encodeThreadQueryCursor(plan, {
|
|
20606
|
+
organizationId: last.organizationId,
|
|
20607
|
+
threadId: last.threadId,
|
|
20608
|
+
sortValue: last[plan.orderBy.field].toISOString()
|
|
20609
|
+
}) : null }
|
|
20610
|
+
};
|
|
20611
|
+
}
|
|
20612
|
+
//#endregion
|
|
20353
20613
|
//#region src/storage/domains/observability/v-next/trace-aggregate.ts
|
|
20354
20614
|
/** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
|
|
20355
20615
|
const DIMENSION_SQL = {
|
|
20356
20616
|
entityType: "r.\"entityType\"",
|
|
20357
20617
|
entityName: "r.\"entityName\"",
|
|
20618
|
+
name: "r.\"name\"",
|
|
20619
|
+
spanType: "r.\"spanType\"",
|
|
20358
20620
|
environment: "r.\"environment\"",
|
|
20359
20621
|
status: TRACE_STATUS_SQL,
|
|
20360
20622
|
serviceName: "r.\"serviceName\"",
|
|
@@ -21581,12 +21843,15 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
21581
21843
|
"trace-query",
|
|
21582
21844
|
"trace-aggregate",
|
|
21583
21845
|
...spanQueryFeatures,
|
|
21846
|
+
...typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
|
|
21584
21847
|
"trace-query-root-duration",
|
|
21585
21848
|
"trace-query-discovery",
|
|
21586
21849
|
"thread-query",
|
|
21587
21850
|
"trace-query-tenant-scope",
|
|
21588
21851
|
"feedback",
|
|
21589
|
-
"trace-query-context-ids"
|
|
21852
|
+
"trace-query-context-ids",
|
|
21853
|
+
...timeToFirstChunkFeatures,
|
|
21854
|
+
"trace-query-root-span-fields"
|
|
21590
21855
|
];
|
|
21591
21856
|
return [
|
|
21592
21857
|
"metrics",
|
|
@@ -21601,12 +21866,15 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
21601
21866
|
"trace-query",
|
|
21602
21867
|
"trace-aggregate",
|
|
21603
21868
|
...spanQueryFeatures,
|
|
21869
|
+
...typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
|
|
21604
21870
|
"trace-query-root-duration",
|
|
21605
21871
|
"trace-query-discovery",
|
|
21606
21872
|
"thread-query",
|
|
21607
21873
|
"trace-query-tenant-scope",
|
|
21608
21874
|
"feedback",
|
|
21609
|
-
"trace-query-context-ids"
|
|
21875
|
+
"trace-query-context-ids",
|
|
21876
|
+
...timeToFirstChunkFeatures,
|
|
21877
|
+
"trace-query-root-span-fields"
|
|
21610
21878
|
];
|
|
21611
21879
|
}
|
|
21612
21880
|
async #run(op, fn, details) {
|
|
@@ -21668,7 +21936,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
21668
21936
|
return this.#run("GET_TRACE_QUERY_VALUES", () => getTraceQueryValues(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
21669
21937
|
}
|
|
21670
21938
|
async queryThreads(plan) {
|
|
21671
|
-
return this.#run("QUERY_THREADS", () => queryThreads(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
21939
|
+
return this.#run("QUERY_THREADS", () => plan.result === "threadRows" ? queryThreadRows(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs) : queryThreads(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
21672
21940
|
}
|
|
21673
21941
|
async listBranches(args) {
|
|
21674
21942
|
return this.#run("LIST_BRANCHES", () => listBranches(this.#readClient, this.#schema, args));
|
|
@@ -24100,7 +24368,7 @@ var SkillsPG = class SkillsPG extends _mastra_core_storage.SkillsStorage {
|
|
|
24100
24368
|
}
|
|
24101
24369
|
}
|
|
24102
24370
|
async list(args) {
|
|
24103
|
-
const { page = 0, perPage: perPageInput, orderBy, authorId, visibility, status, entityIds, pinFavoritedFor, favoritedOnly } = args || {};
|
|
24371
|
+
const { page = 0, perPage: perPageInput, orderBy, authorId, visibility, status, entityIds, pinFavoritedFor, favoritedOnly, metadata } = args || {};
|
|
24104
24372
|
const { field, direction } = this.parseOrderBy(orderBy);
|
|
24105
24373
|
if (page < 0) throw new _mastra_core_error.MastraError({
|
|
24106
24374
|
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "LIST_SKILLS", "INVALID_PAGE"),
|
|
@@ -24145,6 +24413,16 @@ var SkillsPG = class SkillsPG extends _mastra_core_storage.SkillsStorage {
|
|
|
24145
24413
|
conditions.push(`s.visibility = $${paramIdx++}`);
|
|
24146
24414
|
queryParams.push(visibility);
|
|
24147
24415
|
}
|
|
24416
|
+
if (metadata && Object.keys(metadata).length > 0) {
|
|
24417
|
+
const versionsTable = getTableName$5({
|
|
24418
|
+
indexName: _mastra_core_storage.TABLE_SKILL_VERSIONS,
|
|
24419
|
+
schemaName: getSchemaName$5(this.#schema)
|
|
24420
|
+
});
|
|
24421
|
+
const latestVersionId = `(SELECT lv.id FROM ${versionsTable} lv WHERE lv."skillId" = s.id ORDER BY lv."versionNumber" DESC LIMIT 1)`;
|
|
24422
|
+
const resolvedVersionId = status === "draft" ? latestVersionId : `COALESCE(s."activeVersionId", ${latestVersionId})`;
|
|
24423
|
+
conditions.push(`EXISTS (SELECT 1 FROM ${versionsTable} mv WHERE mv."skillId" = s.id AND mv.id = ${resolvedVersionId} AND mv.metadata @> $${paramIdx++}::jsonb)`);
|
|
24424
|
+
queryParams.push(toPgJson(metadata));
|
|
24425
|
+
}
|
|
24148
24426
|
if (entityIds && entityIds.length > 0) {
|
|
24149
24427
|
const placeholders = entityIds.map(() => `$${paramIdx++}`).join(", ");
|
|
24150
24428
|
conditions.push(`s.id IN (${placeholders})`);
|