@mastra/duckdb 1.10.0-alpha.0 → 1.10.0-alpha.2
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/{db-b2jufH0V.js → db-BWgrbB_u.js} +14 -1
- package/dist/db-BWgrbB_u.js.map +1 -0
- package/dist/{db-CJBcanx4.cjs → db-zp2htaur.cjs} +14 -1
- package/dist/db-zp2htaur.cjs.map +1 -0
- package/dist/docs/SKILL.md +2 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/reference-storage-retention.md +302 -0
- package/dist/index.cjs +7 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/{observability-DT-R9Xkn.cjs → observability-CCPCADER.cjs} +160 -8
- package/dist/observability-CCPCADER.cjs.map +1 -0
- package/dist/{observability-CCUvqiBv.js → observability-VRBKtkKz.js} +161 -9
- package/dist/observability-VRBKtkKz.js.map +1 -0
- package/dist/storage/db/index.d.ts +7 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +4 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/scores.d.ts.map +1 -1
- package/dist/storage/domains/observability/trace-query.d.ts.map +1 -1
- package/dist/storage/index.d.ts +7 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/retention.d.ts +23 -0
- package/dist/storage/retention.d.ts.map +1 -0
- package/package.json +3 -3
- package/dist/db-CJBcanx4.cjs.map +0 -1
- package/dist/db-b2jufH0V.js.map +0 -1
- package/dist/observability-CCUvqiBv.js.map +0 -1
- package/dist/observability-DT-R9Xkn.cjs.map +0 -1
|
@@ -1,10 +1,36 @@
|
|
|
1
|
-
import { t as DuckDBConnection } from "./db-
|
|
1
|
+
import { t as DuckDBConnection } from "./db-BWgrbB_u.js";
|
|
2
2
|
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
3
3
|
import * as coreStorage from "@mastra/core/storage";
|
|
4
|
-
import { BRANCH_SPAN_TYPES, METRIC_DISTINCT_COLUMNS, ObservabilityStorage, buildInputPreview, computeTraceStatus, createStorageErrorId, feedbackRecordSchema, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, toTraceSpans } from "@mastra/core/storage";
|
|
4
|
+
import { BRANCH_SPAN_TYPES, METRIC_DISTINCT_COLUMNS, ObservabilityStorage, buildInputPreview, computeTraceStatus, createStorageErrorId, executeRetentionPrune, feedbackRecordSchema, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, resolveRetentionTargets, retentionCutoffMs, toTraceSpans } from "@mastra/core/storage";
|
|
5
5
|
import { coreFeatures } from "@mastra/core/features";
|
|
6
6
|
import { EntityType } from "@mastra/core/observability";
|
|
7
7
|
import { parseFieldKey } from "@mastra/core/utils";
|
|
8
|
+
//#region src/storage/retention.ts
|
|
9
|
+
function cutoffFor(policy, now = Date.now()) {
|
|
10
|
+
return new Date(retentionCutoffMs(policy, now));
|
|
11
|
+
}
|
|
12
|
+
function runPrune({ db, domain, targets, options }) {
|
|
13
|
+
return executeRetentionPrune({
|
|
14
|
+
domain,
|
|
15
|
+
targets,
|
|
16
|
+
options,
|
|
17
|
+
cutoffFor: (target, now) => cutoffFor(target.policy, now),
|
|
18
|
+
deleteBatch: (target, cutoff, limit) => db.pruneBatch({
|
|
19
|
+
tableName: target.table,
|
|
20
|
+
column: target.column,
|
|
21
|
+
cutoff,
|
|
22
|
+
limit
|
|
23
|
+
})
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function resolveTargets({ policies, descriptor, order }) {
|
|
27
|
+
return resolveRetentionTargets({
|
|
28
|
+
policies,
|
|
29
|
+
descriptor,
|
|
30
|
+
order
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
8
34
|
//#region src/storage/domains/observability/ddl.ts
|
|
9
35
|
/**
|
|
10
36
|
* DDL statements for DuckDB observability tables.
|
|
@@ -2394,7 +2420,13 @@ async function createScore(db, args) {
|
|
|
2394
2420
|
/** Insert multiple score events in a single statement. */
|
|
2395
2421
|
async function batchCreateScores(db, args) {
|
|
2396
2422
|
if (args.scores.length === 0) return;
|
|
2397
|
-
const
|
|
2423
|
+
const currentScores = /* @__PURE__ */ new Map();
|
|
2424
|
+
for (const score of args.scores) {
|
|
2425
|
+
const key = score.scoreId ?? Symbol();
|
|
2426
|
+
currentScores.delete(key);
|
|
2427
|
+
currentScores.set(key, score);
|
|
2428
|
+
}
|
|
2429
|
+
const tuples = [...currentScores.values()].map((s) => {
|
|
2398
2430
|
const legacyScore = s;
|
|
2399
2431
|
const scoreSource = legacyScore.scoreSource ?? legacyScore.source ?? null;
|
|
2400
2432
|
return `(${[
|
|
@@ -2830,6 +2862,11 @@ const FEEDBACK_FIELDS = {
|
|
|
2830
2862
|
const TRACE_SELECT = `
|
|
2831
2863
|
r.traceId AS traceId,
|
|
2832
2864
|
r.spanId AS rootSpanId,
|
|
2865
|
+
r.name AS name,
|
|
2866
|
+
r.entityId AS entityId,
|
|
2867
|
+
r.parentSpanId AS parentSpanId,
|
|
2868
|
+
r.metadata AS metadata,
|
|
2869
|
+
r.input AS input,
|
|
2833
2870
|
r.threadId AS threadId,
|
|
2834
2871
|
r.resourceId AS resourceId,
|
|
2835
2872
|
r.startedAt AS startedAt,
|
|
@@ -3151,6 +3188,27 @@ LIMIT ?`,
|
|
|
3151
3188
|
}
|
|
3152
3189
|
const orderField = plan.orderBy.field;
|
|
3153
3190
|
const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
|
|
3191
|
+
if (plan.paginationMode === "page") {
|
|
3192
|
+
values.push(plan.perPage, plan.page * plan.perPage);
|
|
3193
|
+
return {
|
|
3194
|
+
sql: `${candidates},
|
|
3195
|
+
page_rows AS (
|
|
3196
|
+
SELECT *, row_number() OVER (ORDER BY ${orderField} ${direction}, traceId ASC) AS __row_position
|
|
3197
|
+
FROM candidates
|
|
3198
|
+
ORDER BY ${orderField} ${direction}, traceId ASC
|
|
3199
|
+
LIMIT ? OFFSET ?
|
|
3200
|
+
),
|
|
3201
|
+
page_total AS (
|
|
3202
|
+
SELECT COUNT(*) AS total
|
|
3203
|
+
FROM candidates
|
|
3204
|
+
)
|
|
3205
|
+
SELECT page_rows.*, page_total.total
|
|
3206
|
+
FROM page_total
|
|
3207
|
+
LEFT JOIN page_rows ON TRUE
|
|
3208
|
+
ORDER BY page_rows.__row_position ASC NULLS LAST`,
|
|
3209
|
+
values
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3154
3212
|
let pageCondition = "";
|
|
3155
3213
|
if (plan.cursor) {
|
|
3156
3214
|
pageCondition = `WHERE (${orderField} ${plan.orderBy.direction === "asc" ? ">" : "<"} CAST(? AS TIMESTAMP) OR (${orderField} = CAST(? AS TIMESTAMP) AND traceId > ?))`;
|
|
@@ -3277,21 +3335,30 @@ LIMIT ?`,
|
|
|
3277
3335
|
values
|
|
3278
3336
|
};
|
|
3279
3337
|
}
|
|
3338
|
+
function isDuckDBResourceLimit(error) {
|
|
3339
|
+
return error instanceof Error && error.message.toLowerCase().includes("out of memory");
|
|
3340
|
+
}
|
|
3341
|
+
async function runDuckDBDiscoveryQuery(db, query) {
|
|
3342
|
+
try {
|
|
3343
|
+
return await db.query(query.sql, query.values);
|
|
3344
|
+
} catch (error) {
|
|
3345
|
+
if (isDuckDBResourceLimit(error)) throw new coreStorage.TraceQueryResourceLimitError();
|
|
3346
|
+
throw error;
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3280
3349
|
async function getTraceQueryObservedFields(db, plan) {
|
|
3281
3350
|
if (plan.predicateScope !== "trace") return {
|
|
3282
3351
|
observedFields: [],
|
|
3283
3352
|
observedFieldsTruncated: false
|
|
3284
3353
|
};
|
|
3285
|
-
const
|
|
3286
|
-
const rows = await db.query(query.sql, query.values);
|
|
3354
|
+
const rows = await runDuckDBDiscoveryQuery(db, compileDuckDBTraceQueryObservedFields(plan));
|
|
3287
3355
|
return {
|
|
3288
3356
|
observedFields: rows.slice(0, plan.limit).map((row) => coreStorage.createTraceQueryObservedFieldDescriptor(String(row.path), Number(row.occurrences))),
|
|
3289
3357
|
observedFieldsTruncated: rows.length > plan.limit
|
|
3290
3358
|
};
|
|
3291
3359
|
}
|
|
3292
3360
|
async function getTraceQueryValues(db, plan) {
|
|
3293
|
-
const
|
|
3294
|
-
const rows = await db.query(query.sql, query.values);
|
|
3361
|
+
const rows = await runDuckDBDiscoveryQuery(db, compileDuckDBTraceQueryValues(plan));
|
|
3295
3362
|
return coreStorage.getTraceQueryValuesResponseSchema.parse({
|
|
3296
3363
|
values: rows.slice(0, plan.limit).map((row) => ({
|
|
3297
3364
|
value: String(row.value),
|
|
@@ -3304,6 +3371,38 @@ function asIsoTimestamp(value) {
|
|
|
3304
3371
|
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
|
3305
3372
|
}
|
|
3306
3373
|
async function queryTraces(db, plan) {
|
|
3374
|
+
if (plan.paginationMode === "page") {
|
|
3375
|
+
const query = compileDuckDBTraceQuery(plan);
|
|
3376
|
+
const rows = await db.query(query.sql, query.values);
|
|
3377
|
+
const total = Number(rows[0]?.total ?? 0);
|
|
3378
|
+
const traces = rows.filter((row) => row.traceId != null).map((row) => ({
|
|
3379
|
+
traceId: String(row.traceId),
|
|
3380
|
+
rootSpanId: String(row.rootSpanId),
|
|
3381
|
+
name: row.name,
|
|
3382
|
+
entityId: row.entityId ?? null,
|
|
3383
|
+
parentSpanId: row.parentSpanId ?? null,
|
|
3384
|
+
createdAt: asIsoTimestamp(row.startedAt),
|
|
3385
|
+
metadata: parseJson(row.metadata) ?? null,
|
|
3386
|
+
inputPreview: coreStorage.buildInputPreview(row.input) ?? null,
|
|
3387
|
+
threadId: row.threadId == null ? null : String(row.threadId),
|
|
3388
|
+
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
3389
|
+
startedAt: asIsoTimestamp(row.startedAt),
|
|
3390
|
+
endedAt: asIsoTimestamp(row.endedAt),
|
|
3391
|
+
entityName: row.entityName == null ? null : String(row.entityName),
|
|
3392
|
+
entityType: row.entityType == null ? null : String(row.entityType),
|
|
3393
|
+
environment: row.environment == null ? null : String(row.environment),
|
|
3394
|
+
status: row.status
|
|
3395
|
+
}));
|
|
3396
|
+
return coreStorage.traceQueryResponseSchema.parse({
|
|
3397
|
+
traces,
|
|
3398
|
+
pagination: {
|
|
3399
|
+
total,
|
|
3400
|
+
page: plan.page,
|
|
3401
|
+
perPage: plan.perPage,
|
|
3402
|
+
hasMore: (plan.page + 1) * plan.perPage < total
|
|
3403
|
+
}
|
|
3404
|
+
});
|
|
3405
|
+
}
|
|
3307
3406
|
const query = compileDuckDBTraceQuery(plan);
|
|
3308
3407
|
const rows = await db.query(query.sql, query.values);
|
|
3309
3408
|
const visibleRows = rows.slice(0, plan.limit);
|
|
@@ -3321,6 +3420,12 @@ async function queryTraces(db, plan) {
|
|
|
3321
3420
|
const traces = visibleRows.map((row) => ({
|
|
3322
3421
|
traceId: String(row.traceId),
|
|
3323
3422
|
rootSpanId: String(row.rootSpanId),
|
|
3423
|
+
name: row.name,
|
|
3424
|
+
entityId: row.entityId ?? null,
|
|
3425
|
+
parentSpanId: row.parentSpanId ?? null,
|
|
3426
|
+
createdAt: asIsoTimestamp(row.startedAt),
|
|
3427
|
+
metadata: parseJson(row.metadata) ?? null,
|
|
3428
|
+
inputPreview: coreStorage.buildInputPreview(row.input) ?? null,
|
|
3324
3429
|
threadId: row.threadId == null ? null : String(row.threadId),
|
|
3325
3430
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
3326
3431
|
startedAt: asIsoTimestamp(row.startedAt),
|
|
@@ -4533,12 +4638,59 @@ ${args.tables.map((table) => ` - ${table.table}`).join("\n")}\n\nTo fix this, r
|
|
|
4533
4638
|
* DuckDB-backed observability storage for traces, metrics, logs, scores, and feedback.
|
|
4534
4639
|
* Uses an append-only event-sourced model with SQL-based reconstruction for spans.
|
|
4535
4640
|
*/
|
|
4536
|
-
var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
|
|
4641
|
+
var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends ObservabilityStorage {
|
|
4642
|
+
static retentionTables = {
|
|
4643
|
+
spans: {
|
|
4644
|
+
table: "span_events",
|
|
4645
|
+
column: "timestamp",
|
|
4646
|
+
indexed: false
|
|
4647
|
+
},
|
|
4648
|
+
metrics: {
|
|
4649
|
+
table: "metric_events",
|
|
4650
|
+
column: "timestamp",
|
|
4651
|
+
indexed: false
|
|
4652
|
+
},
|
|
4653
|
+
logs: {
|
|
4654
|
+
table: "log_events",
|
|
4655
|
+
column: "timestamp",
|
|
4656
|
+
indexed: false
|
|
4657
|
+
},
|
|
4658
|
+
scores: {
|
|
4659
|
+
table: "score_events",
|
|
4660
|
+
column: "timestamp",
|
|
4661
|
+
indexed: false
|
|
4662
|
+
},
|
|
4663
|
+
feedback: {
|
|
4664
|
+
table: "feedback_events",
|
|
4665
|
+
column: "timestamp",
|
|
4666
|
+
indexed: false
|
|
4667
|
+
}
|
|
4668
|
+
};
|
|
4537
4669
|
db;
|
|
4538
4670
|
constructor(config) {
|
|
4539
4671
|
super();
|
|
4540
4672
|
this.db = config.db;
|
|
4541
4673
|
}
|
|
4674
|
+
/** Delete observability events older than their configured max age. */
|
|
4675
|
+
async prune(policies, options) {
|
|
4676
|
+
const targets = resolveTargets({
|
|
4677
|
+
policies,
|
|
4678
|
+
descriptor: ObservabilityStorageDuckDB.retentionTables,
|
|
4679
|
+
order: [
|
|
4680
|
+
"spans",
|
|
4681
|
+
"metrics",
|
|
4682
|
+
"logs",
|
|
4683
|
+
"scores",
|
|
4684
|
+
"feedback"
|
|
4685
|
+
]
|
|
4686
|
+
});
|
|
4687
|
+
return runPrune({
|
|
4688
|
+
db: this.db,
|
|
4689
|
+
domain: "observability",
|
|
4690
|
+
targets,
|
|
4691
|
+
options
|
|
4692
|
+
});
|
|
4693
|
+
}
|
|
4542
4694
|
/** Create all observability tables if they don't exist. */
|
|
4543
4695
|
async init() {
|
|
4544
4696
|
const migrationStatus = await checkSignalTablesMigrationStatus(this.db);
|
|
@@ -4757,4 +4909,4 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
|
|
|
4757
4909
|
//#endregion
|
|
4758
4910
|
export { ObservabilityStorageDuckDB };
|
|
4759
4911
|
|
|
4760
|
-
//# sourceMappingURL=observability-
|
|
4912
|
+
//# sourceMappingURL=observability-VRBKtkKz.js.map
|