@mastra/duckdb 1.18.0 → 1.19.0-alpha.1
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 -5
- package/dist/docs/assets/SOURCE_MAP.json +23 -2
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/{observability-DRlTvozP.js → observability-BFd3wUzd.js} +108 -44
- package/dist/observability-BFd3wUzd.js.map +1 -0
- package/dist/{observability-De8nSw6B.cjs → observability-YXMSShsG.cjs} +108 -44
- package/dist/observability-YXMSShsG.cjs.map +1 -0
- package/dist/storage/domains/observability/feedback.d.ts +2 -0
- package/dist/storage/domains/observability/feedback.d.ts.map +1 -1
- package/dist/storage/domains/observability/helpers.d.ts +8 -0
- package/dist/storage/domains/observability/helpers.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +1 -0
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/preview-compat.d.ts +0 -2
- package/dist/storage/domains/observability/preview-compat.d.ts.map +1 -1
- package/dist/storage/domains/observability/span-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/thread-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/tracing.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/docs/references/reference-storage-retention.md +0 -310
- package/dist/observability-DRlTvozP.js.map +0 -1
- package/dist/observability-De8nSw6B.cjs.map +0 -1
|
@@ -733,10 +733,6 @@ function buildPaginationClause(pagination) {
|
|
|
733
733
|
// src/storage/domains/observability/preview-compat.ts
|
|
734
734
|
const generator = _mastra_core_storage.buildStoredPreview;
|
|
735
735
|
const canGeneratePreviews = typeof generator === "function";
|
|
736
|
-
/** SQL detection only; preview formatting stays in TypeScript. Empty strings are intentional. */
|
|
737
|
-
function missingPreviewSql(column) {
|
|
738
|
-
return canGeneratePreviews ? `${column} IS NULL` : "FALSE";
|
|
739
|
-
}
|
|
740
736
|
function needsPreview(value) {
|
|
741
737
|
return canGeneratePreviews && value == null;
|
|
742
738
|
}
|
|
@@ -823,6 +819,17 @@ function payloadColumnSql(column) {
|
|
|
823
819
|
return `NULLIF(${column}, '${JSON_NULL_PLACEHOLDER}')`;
|
|
824
820
|
}
|
|
825
821
|
/**
|
|
822
|
+
* The inclusive range of `dates`, used to bound lookups that find a page's rows by their keys.
|
|
823
|
+
*
|
|
824
|
+
* DuckDB cannot push an OR of key tuples (or a tuple semi-join) into the table scan, so without a
|
|
825
|
+
* range it decompresses the payload columns of the whole table. A range reaches the scan as a plain
|
|
826
|
+
* filter that zone maps can prune on, and it cannot change the result: it covers every looked-up row.
|
|
827
|
+
*/
|
|
828
|
+
function timeRangeOf(dates) {
|
|
829
|
+
const times = dates.map((date) => date.getTime());
|
|
830
|
+
return [new Date(Math.min(...times)), new Date(Math.max(...times))];
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
826
833
|
* Trim, drop blank, and dedupe tags before they are written, matching the
|
|
827
834
|
* PostgreSQL and ClickHouse stores so tag predicates and discovery agree.
|
|
828
835
|
*/
|
|
@@ -1147,7 +1154,7 @@ function getComparisonDateRange$1(comparePeriod, timestamp) {
|
|
|
1147
1154
|
async function createFeedback(db, args) {
|
|
1148
1155
|
const f = args.feedback;
|
|
1149
1156
|
const feedbackSource = f.feedbackSource ?? f.source ?? "";
|
|
1150
|
-
const feedbackUserId = f.feedbackUserId
|
|
1157
|
+
const feedbackUserId = f.feedbackUserId !== void 0 ? f.feedbackUserId : f.userId ?? null;
|
|
1151
1158
|
await db.execute(`INSERT INTO feedback_events (
|
|
1152
1159
|
feedbackId, timestamp, cursorId, traceId, spanId, experimentId,
|
|
1153
1160
|
entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
|
|
@@ -1204,7 +1211,7 @@ async function batchCreateFeedback(db, args) {
|
|
|
1204
1211
|
const tuples = [...new Map(args.feedbacks.map((feedback) => [feedback.feedbackId, feedback])).values()].map((f) => {
|
|
1205
1212
|
const legacyFeedback = f;
|
|
1206
1213
|
const feedbackSource = legacyFeedback.feedbackSource ?? legacyFeedback.source ?? "";
|
|
1207
|
-
const feedbackUserId = legacyFeedback.feedbackUserId
|
|
1214
|
+
const feedbackUserId = legacyFeedback.feedbackUserId !== void 0 ? legacyFeedback.feedbackUserId : legacyFeedback.userId ?? null;
|
|
1208
1215
|
return `(${[
|
|
1209
1216
|
v(legacyFeedback.feedbackId),
|
|
1210
1217
|
v(legacyFeedback.timestamp),
|
|
@@ -1276,6 +1283,11 @@ async function deleteFeedback(db, args) {
|
|
|
1276
1283
|
}
|
|
1277
1284
|
await db.execute(`DELETE FROM feedback_events WHERE ${conditions.join(" AND ")}`, params);
|
|
1278
1285
|
}
|
|
1286
|
+
/** Retrieves the latest feedback record with this ID, or null when missing. */
|
|
1287
|
+
async function getFeedbackById(db, feedbackId) {
|
|
1288
|
+
const rows = await db.query(`SELECT * FROM feedback_events WHERE feedbackId = ? ORDER BY timestamp DESC LIMIT 1`, [feedbackId]);
|
|
1289
|
+
return rows[0] ? rowToFeedbackRecord(rows[0]) : null;
|
|
1290
|
+
}
|
|
1279
1291
|
/** Update the review workflow status of a single feedback event. */
|
|
1280
1292
|
async function updateFeedbackReviewStatus(db, args) {
|
|
1281
1293
|
const { feedbackId, reviewStatus } = parseUpdateFeedbackReviewStatusArgs(args);
|
|
@@ -3897,7 +3909,7 @@ const TRACE_PREVIEW_PAYLOADS = [
|
|
|
3897
3909
|
async function fillMissingTracePreviews(db, rows) {
|
|
3898
3910
|
const flagged = rows.filter((row) => row.traceId != null && TRACE_PREVIEW_PAYLOADS.some(([preview]) => needsPreview(row[preview])));
|
|
3899
3911
|
if (flagged.length === 0) return;
|
|
3900
|
-
const values =
|
|
3912
|
+
const values = timeRangeOf(flagged.map((row) => row.rootTimestamp));
|
|
3901
3913
|
const where = flagged.map((row) => {
|
|
3902
3914
|
values.push(row.traceId, row.rootSpanId, row.rootEventType, row.rootTimestamp, row.rootCursorId);
|
|
3903
3915
|
return "(traceId = ? AND spanId = ? AND eventType = ? AND timestamp = ? AND cursorId IS NOT DISTINCT FROM ?)";
|
|
@@ -3907,7 +3919,7 @@ async function fillMissingTracePreviews(db, rows) {
|
|
|
3907
3919
|
${payloadColumnSql("output")} AS output,
|
|
3908
3920
|
error
|
|
3909
3921
|
FROM span_events
|
|
3910
|
-
WHERE parentSpanId IS NULL AND (${where})`, values);
|
|
3922
|
+
WHERE parentSpanId IS NULL AND timestamp BETWEEN ? AND ? AND (${where})`, values);
|
|
3911
3923
|
const byTrace = new Map(payloads.map((payload) => [String(payload.traceId), payload]));
|
|
3912
3924
|
for (const row of flagged) {
|
|
3913
3925
|
const payload = byTrace.get(String(row.traceId));
|
|
@@ -4174,6 +4186,17 @@ const previewPayloads = [
|
|
|
4174
4186
|
["outputPreview", payloadColumnSql("output")],
|
|
4175
4187
|
["errorPreview", "error"]
|
|
4176
4188
|
];
|
|
4189
|
+
function eventRowsFilter(rows) {
|
|
4190
|
+
const values = timeRangeOf(rows.map((row) => row.endedAt));
|
|
4191
|
+
return {
|
|
4192
|
+
sql: `endedAt BETWEEN ? AND ? AND (${rows.map((row) => {
|
|
4193
|
+
const key = keys([row], values);
|
|
4194
|
+
values.push(row.cursorId, row.endedAt);
|
|
4195
|
+
return `(${key} AND cursorId IS NOT DISTINCT FROM ? AND endedAt = ?)`;
|
|
4196
|
+
}).join(" OR ")})`,
|
|
4197
|
+
values
|
|
4198
|
+
};
|
|
4199
|
+
}
|
|
4177
4200
|
async function querySpans(db, plan) {
|
|
4178
4201
|
const deadline = performance.now() + _mastra_core_storage.TRACE_QUERY_DEFAULT_TIMEOUT_MS;
|
|
4179
4202
|
const query = async (sql, values) => {
|
|
@@ -4202,17 +4225,21 @@ async function querySpans(db, plan) {
|
|
|
4202
4225
|
}));
|
|
4203
4226
|
const page = rows.slice(0, plan.limit);
|
|
4204
4227
|
if (!page.length) return buildSpanQueryResponse(plan, [], []);
|
|
4205
|
-
const
|
|
4206
|
-
const payloadWhere = page.map((row) => {
|
|
4207
|
-
const key = keys([row], payloadValues);
|
|
4208
|
-
payloadValues.push(row.cursorId, row.endedAt);
|
|
4209
|
-
return `(${key} AND cursorId IS NOT DISTINCT FROM ? AND endedAt = ?)`;
|
|
4210
|
-
}).join(" OR ");
|
|
4211
|
-
const previewRows = await query(`SELECT DISTINCT ${identities}, CAST(stopReasonContext AS VARCHAR) AS stopReasonContext,
|
|
4228
|
+
const lookupColumns = `${identities}, CAST(stopReasonContext AS VARCHAR) AS stopReasonContext,
|
|
4212
4229
|
COALESCE(model, CASE WHEN json_type(attributes, '$.model') = 'VARCHAR' THEN json_extract_string(attributes, '$.model') END) AS model,
|
|
4213
4230
|
COALESCE(provider, CASE WHEN json_type(attributes, '$.provider') = 'VARCHAR' THEN json_extract_string(attributes, '$.provider') END) AS provider,
|
|
4214
|
-
${previewPayloads.map(([preview
|
|
4215
|
-
|
|
4231
|
+
${previewPayloads.map(([preview]) => preview).join(", ")}`;
|
|
4232
|
+
const pageFilter = eventRowsFilter(page);
|
|
4233
|
+
const storedRows = await query(`SELECT DISTINCT ${lookupColumns} FROM span_events WHERE ${pageFilter.sql}`, pageFilter.values);
|
|
4234
|
+
const withoutPreview = new Set(storedRows.filter((row) => previewPayloads.some(([preview]) => needsPreview(row[preview]))).map((row) => _mastra_core_storage.spanQueryIdentityKey(row)));
|
|
4235
|
+
const rowsWithoutPreview = page.filter((row) => withoutPreview.has(_mastra_core_storage.spanQueryIdentityKey(row)));
|
|
4236
|
+
let previewRows = storedRows;
|
|
4237
|
+
if (rowsWithoutPreview.length) {
|
|
4238
|
+
const payloadFilter = eventRowsFilter(rowsWithoutPreview);
|
|
4239
|
+
const payloadRows = await query(`SELECT DISTINCT ${lookupColumns}, ${previewPayloads.map(([preview, payload]) => `CAST(${payload} AS VARCHAR) AS ${preview}Payload`).join(", ")}
|
|
4240
|
+
FROM span_events WHERE ${payloadFilter.sql}`, payloadFilter.values);
|
|
4241
|
+
previewRows = [...storedRows.filter((row) => !withoutPreview.has(_mastra_core_storage.spanQueryIdentityKey(row))), ...payloadRows];
|
|
4242
|
+
}
|
|
4216
4243
|
const payloads = previewRows.map((row) => {
|
|
4217
4244
|
const payload = {
|
|
4218
4245
|
organizationId: row.organizationId,
|
|
@@ -4387,6 +4414,22 @@ function compileDuckDBThreadRowsQuery(plan, strategy = "full") {
|
|
|
4387
4414
|
values
|
|
4388
4415
|
};
|
|
4389
4416
|
}
|
|
4417
|
+
/**
|
|
4418
|
+
* A chat's first and last traces can be months apart, so the range alone can cover the whole table.
|
|
4419
|
+
* The IN list lets DuckDB also skip the row groups between them.
|
|
4420
|
+
*/
|
|
4421
|
+
function rootRowsFilter(roots) {
|
|
4422
|
+
const endedAts = roots.map((root) => root.endedAt);
|
|
4423
|
+
const values = [...timeRangeOf(endedAts), ...endedAts];
|
|
4424
|
+
const keys = roots.map((root) => `(${Object.entries(root).map(([field, value]) => {
|
|
4425
|
+
values.push(value);
|
|
4426
|
+
return `${field} ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} ?`;
|
|
4427
|
+
}).join(" AND ")})`);
|
|
4428
|
+
return {
|
|
4429
|
+
sql: `endedAt BETWEEN ? AND ? AND endedAt IN (${endedAts.map(() => "?").join(", ")}) AND (${keys.join(" OR ")})`,
|
|
4430
|
+
values
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4390
4433
|
async function queryThreadRows(db, plan, strategy = "auto") {
|
|
4391
4434
|
const deadline = performance.now() + _mastra_core_storage.TRACE_QUERY_DEFAULT_TIMEOUT_MS;
|
|
4392
4435
|
const run = async (sql, values) => {
|
|
@@ -4416,38 +4459,47 @@ async function queryThreadRows(db, plan, strategy = "auto") {
|
|
|
4416
4459
|
threads: [],
|
|
4417
4460
|
page: { next: null }
|
|
4418
4461
|
};
|
|
4419
|
-
const
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
return `(${Object.entries(entries).map(([field, value]) => {
|
|
4430
|
-
values.push(value);
|
|
4431
|
-
return `${field} ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} ?`;
|
|
4432
|
-
}).join(" AND ")})`;
|
|
4433
|
-
}));
|
|
4434
|
-
const payloads = await run(`SELECT organizationId, traceId, cursorId,
|
|
4462
|
+
const roots = page.flatMap((row) => ["first", "last"].map((which) => ({
|
|
4463
|
+
organizationId: row.organizationId,
|
|
4464
|
+
resourceId: which === "first" ? row.resourceId : row.lastResourceId,
|
|
4465
|
+
traceId: row[`${which}TraceId`],
|
|
4466
|
+
spanId: row[`${which}SpanId`],
|
|
4467
|
+
cursorId: row[`${which}CursorId`],
|
|
4468
|
+
endedAt: row[`${which}EndedAt`]
|
|
4469
|
+
})));
|
|
4470
|
+
const pageFilter = rootRowsFilter(roots);
|
|
4471
|
+
const rootRows = await run(`SELECT organizationId, traceId, cursorId,
|
|
4435
4472
|
${contextFields.map((field) => field === "executionSource" ? "source AS executionSource" : field).join(", ")},
|
|
4436
4473
|
CASE WHEN spanType = 'agent_run' THEN timeToFirstChunkMs END AS timeToFirstChunkMs,
|
|
4437
4474
|
CASE WHEN spanType = 'agent_run' THEN timeToFirstTokenMs END AS timeToFirstTokenMs,
|
|
4438
4475
|
CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
"error"
|
|
4443
|
-
].map((field) => `${field}Preview, CASE WHEN ${missingPreviewSql(`${field}Preview`)} THEN CAST(${field === "error" ? field : payloadColumnSql(field)} AS VARCHAR) END AS ${field}`).join(", ")}
|
|
4444
|
-
FROM span_events WHERE ${keys.join(" OR ")}`, values);
|
|
4476
|
+
inputPreview, outputPreview, errorPreview
|
|
4477
|
+
FROM span_events
|
|
4478
|
+
WHERE ${pageFilter.sql}`, pageFilter.values);
|
|
4445
4479
|
const key = (org, trace, version) => JSON.stringify([
|
|
4446
4480
|
org,
|
|
4447
4481
|
trace,
|
|
4448
4482
|
String(version)
|
|
4449
4483
|
]);
|
|
4450
|
-
const byRoot = new Map(
|
|
4484
|
+
const byRoot = new Map(rootRows.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
|
|
4485
|
+
const rootsWithoutPreview = roots.filter((root) => {
|
|
4486
|
+
const row = byRoot.get(key(root.organizationId, root.traceId, root.cursorId));
|
|
4487
|
+
return row && [
|
|
4488
|
+
row.inputPreview,
|
|
4489
|
+
row.outputPreview,
|
|
4490
|
+
row.errorPreview
|
|
4491
|
+
].some(needsPreview);
|
|
4492
|
+
});
|
|
4493
|
+
if (rootsWithoutPreview.length) {
|
|
4494
|
+
const payloadFilter = rootRowsFilter(rootsWithoutPreview);
|
|
4495
|
+
const payloads = await run(`SELECT organizationId, traceId, cursorId,
|
|
4496
|
+
CAST(${payloadColumnSql("input")} AS VARCHAR) AS input,
|
|
4497
|
+
CAST(${payloadColumnSql("output")} AS VARCHAR) AS output,
|
|
4498
|
+
CAST(error AS VARCHAR) AS error
|
|
4499
|
+
FROM span_events
|
|
4500
|
+
WHERE ${payloadFilter.sql}`, payloadFilter.values);
|
|
4501
|
+
for (const payload of payloads) Object.assign(byRoot.get(key(payload.organizationId, payload.traceId, payload.cursorId)), payload);
|
|
4502
|
+
}
|
|
4451
4503
|
const threads = page.flatMap((row) => {
|
|
4452
4504
|
const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
|
|
4453
4505
|
const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
|
|
@@ -5071,7 +5123,9 @@ const SPAN_RECONSTRUCT_SELECT_LIGHT_LIST = `
|
|
|
5071
5123
|
${argMaxNonNull("resourceId")},
|
|
5072
5124
|
${argMaxNonNull("error")},
|
|
5073
5125
|
${argMaxNonNull("metadata")},
|
|
5074
|
-
${RECONSTRUCT_PREVIEWS_SQL}
|
|
5126
|
+
${RECONSTRUCT_PREVIEWS_SQL},
|
|
5127
|
+
min(timestamp) AS firstEventAt,
|
|
5128
|
+
max(timestamp) AS lastEventAt
|
|
5075
5129
|
FROM span_events
|
|
5076
5130
|
`;
|
|
5077
5131
|
/**
|
|
@@ -5202,11 +5256,18 @@ async function fillMissingReconstructedPreviews(db, rows) {
|
|
|
5202
5256
|
const flagged = rows.filter((row) => PREVIEW_PAYLOADS.some(([preview]) => needsPreview(row[preview])));
|
|
5203
5257
|
if (flagged.length === 0) return;
|
|
5204
5258
|
const traceIds = [...new Set(flagged.map((row) => row.traceId))];
|
|
5259
|
+
const [minTs, maxTs] = timeRangeOf(flagged.flatMap((row) => [row.firstEventAt, row.lastEventAt]));
|
|
5205
5260
|
const payloads = await db.query(`SELECT traceId, spanId, ${PREVIEW_PAYLOADS.map(([, payload]) => argMaxNonNull(payload)).join(", ")}
|
|
5206
5261
|
FROM span_events
|
|
5207
|
-
WHERE
|
|
5262
|
+
WHERE timestamp BETWEEN ? AND ?
|
|
5263
|
+
AND traceId IN (${traceIds.map(() => "?").join(", ")})
|
|
5208
5264
|
AND (traceId, spanId) IN (SELECT * FROM (VALUES ${flagged.map(() => "(?, ?)").join(", ")}))
|
|
5209
|
-
GROUP BY traceId, spanId`, [
|
|
5265
|
+
GROUP BY traceId, spanId`, [
|
|
5266
|
+
minTs,
|
|
5267
|
+
maxTs,
|
|
5268
|
+
...traceIds,
|
|
5269
|
+
...flagged.flatMap((row) => [row.traceId, row.spanId])
|
|
5270
|
+
]);
|
|
5210
5271
|
const byKey = new Map(payloads.map((payload) => [`${payload.traceId}\u0000${payload.spanId}`, payload]));
|
|
5211
5272
|
for (const row of flagged) {
|
|
5212
5273
|
const payload = byKey.get(`${row.traceId}\u0000${row.spanId}`);
|
|
@@ -6591,6 +6652,9 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
|
|
|
6591
6652
|
async getScoreById(scoreId) {
|
|
6592
6653
|
return getScoreById(this.db, scoreId);
|
|
6593
6654
|
}
|
|
6655
|
+
async getFeedbackById(feedbackId) {
|
|
6656
|
+
return getFeedbackById(this.db, feedbackId);
|
|
6657
|
+
}
|
|
6594
6658
|
async getScoreAggregate(args) {
|
|
6595
6659
|
return getScoreAggregate(this.db, args);
|
|
6596
6660
|
}
|
|
@@ -6633,4 +6697,4 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
|
|
|
6633
6697
|
};
|
|
6634
6698
|
exports.ObservabilityStorageDuckDB = ObservabilityStorageDuckDB;
|
|
6635
6699
|
|
|
6636
|
-
//# sourceMappingURL=observability-
|
|
6700
|
+
//# sourceMappingURL=observability-YXMSShsG.cjs.map
|