@mastra/duckdb 1.9.0 → 1.10.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/{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 +15 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/dist/{observability-BB4mjnZR.cjs → observability-CL_Bv53d.cjs} +191 -3
- package/dist/observability-CL_Bv53d.cjs.map +1 -0
- package/dist/{observability-DEjVUB3O.js → observability-W6La6xQK.js} +192 -4
- package/dist/observability-W6La6xQK.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 +7 -2
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/trace-query.d.ts +5 -1
- package/dist/storage/domains/observability/trace-query.d.ts.map +1 -1
- package/dist/storage/index.d.ts +9 -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 +7 -7
- package/dist/db-CJBcanx4.cjs.map +0 -1
- package/dist/db-b2jufH0V.js.map +0 -1
- package/dist/observability-BB4mjnZR.cjs.map +0 -1
- package/dist/observability-DEjVUB3O.js.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.
|
|
@@ -2830,6 +2856,11 @@ const FEEDBACK_FIELDS = {
|
|
|
2830
2856
|
const TRACE_SELECT = `
|
|
2831
2857
|
r.traceId AS traceId,
|
|
2832
2858
|
r.spanId AS rootSpanId,
|
|
2859
|
+
r.name AS name,
|
|
2860
|
+
r.entityId AS entityId,
|
|
2861
|
+
r.parentSpanId AS parentSpanId,
|
|
2862
|
+
r.metadata AS metadata,
|
|
2863
|
+
r.input AS input,
|
|
2833
2864
|
r.threadId AS threadId,
|
|
2834
2865
|
r.resourceId AS resourceId,
|
|
2835
2866
|
r.startedAt AS startedAt,
|
|
@@ -3213,6 +3244,102 @@ LIMIT ?`,
|
|
|
3213
3244
|
values
|
|
3214
3245
|
};
|
|
3215
3246
|
}
|
|
3247
|
+
function discoveryRegistry(scope) {
|
|
3248
|
+
if (scope === "trace") return TRACE_FIELDS;
|
|
3249
|
+
if (scope === "spans") return SPAN_FIELDS;
|
|
3250
|
+
if (scope === "scores") return SCORE_FIELDS;
|
|
3251
|
+
return FEEDBACK_FIELDS;
|
|
3252
|
+
}
|
|
3253
|
+
function discoverySource(scope) {
|
|
3254
|
+
if (scope === "trace") return "root_scope r";
|
|
3255
|
+
if (scope === "spans") return "current_spans s";
|
|
3256
|
+
if (scope === "scores") return "current_scores s";
|
|
3257
|
+
return "current_feedback s";
|
|
3258
|
+
}
|
|
3259
|
+
function discoveryCollections(scope) {
|
|
3260
|
+
return scope === "trace" ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([scope]);
|
|
3261
|
+
}
|
|
3262
|
+
function compileDuckDBTraceQueryObservedFields(plan) {
|
|
3263
|
+
const values = [plan.timeRange.from, plan.timeRange.to];
|
|
3264
|
+
if (plan.search) values.push(plan.search);
|
|
3265
|
+
values.push(plan.limit + 1);
|
|
3266
|
+
const search = plan.search ? `AND strpos(lower('metadata.' || entry.key), lower(?)) > 0` : "";
|
|
3267
|
+
return {
|
|
3268
|
+
sql: `WITH ${compileDuckDBTraceScope(/* @__PURE__ */ new Set()).join(",\n ")}
|
|
3269
|
+
SELECT 'metadata.' || entry.key AS path, count(*) AS occurrences
|
|
3270
|
+
FROM root_scope r, LATERAL json_each(r.metadata) entry
|
|
3271
|
+
WHERE entry.type = 'VARCHAR'
|
|
3272
|
+
AND trim(json_extract_string(entry.value, '$')) <> ''
|
|
3273
|
+
AND entry.key <> ''
|
|
3274
|
+
AND strpos(entry.key, '.') = 0
|
|
3275
|
+
AND octet_length(encode('metadata.' || entry.key)) <= ${coreStorage.TRACE_QUERY_MAX_PATH_BYTES}
|
|
3276
|
+
AND octet_length(encode(json_extract_string(entry.value, '$'))) <= ${coreStorage.TRACE_QUERY_MAX_STRING_BYTES}
|
|
3277
|
+
${search}
|
|
3278
|
+
GROUP BY entry.key
|
|
3279
|
+
ORDER BY occurrences DESC, path ASC
|
|
3280
|
+
LIMIT ?`,
|
|
3281
|
+
values
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
function compileDuckDBTraceQueryValues(plan) {
|
|
3285
|
+
const values = [plan.timeRange.from, plan.timeRange.to];
|
|
3286
|
+
const ctes = compileDuckDBTraceScope(discoveryCollections(plan.predicateScope));
|
|
3287
|
+
let fieldSql;
|
|
3288
|
+
if (plan.predicateScope === "trace" && plan.path.startsWith("metadata.")) {
|
|
3289
|
+
const jsonPath = `$.${JSON.stringify(plan.path.slice(9))}`;
|
|
3290
|
+
fieldSql = `NULLIF(trim(CASE WHEN json_type(r.metadata, ?) = 'VARCHAR' THEN json_extract_string(r.metadata, ?) END), '')`;
|
|
3291
|
+
values.push(jsonPath, jsonPath);
|
|
3292
|
+
} else fieldSql = fieldDefinition(discoveryRegistry(plan.predicateScope), plan.path).sql;
|
|
3293
|
+
if (plan.search) values.push(plan.search);
|
|
3294
|
+
values.push(plan.limit + 1);
|
|
3295
|
+
const search = plan.search ? "AND strpos(lower(CAST(value AS VARCHAR)), lower(?)) > 0" : "";
|
|
3296
|
+
return {
|
|
3297
|
+
sql: `WITH ${ctes.join(",\n ")}, extracted AS (
|
|
3298
|
+
SELECT ${fieldSql} AS value FROM ${discoverySource(plan.predicateScope)}
|
|
3299
|
+
)
|
|
3300
|
+
SELECT CAST(value AS VARCHAR) AS value, count(*) AS count
|
|
3301
|
+
FROM extracted
|
|
3302
|
+
WHERE value IS NOT NULL
|
|
3303
|
+
AND octet_length(encode(CAST(value AS VARCHAR))) <= ${coreStorage.TRACE_QUERY_MAX_STRING_BYTES}
|
|
3304
|
+
${search}
|
|
3305
|
+
GROUP BY value
|
|
3306
|
+
ORDER BY count DESC, value ASC
|
|
3307
|
+
LIMIT ?`,
|
|
3308
|
+
values
|
|
3309
|
+
};
|
|
3310
|
+
}
|
|
3311
|
+
function isDuckDBResourceLimit(error) {
|
|
3312
|
+
return error instanceof Error && error.message.toLowerCase().includes("out of memory");
|
|
3313
|
+
}
|
|
3314
|
+
async function runDuckDBDiscoveryQuery(db, query) {
|
|
3315
|
+
try {
|
|
3316
|
+
return await db.query(query.sql, query.values);
|
|
3317
|
+
} catch (error) {
|
|
3318
|
+
if (isDuckDBResourceLimit(error)) throw new coreStorage.TraceQueryResourceLimitError();
|
|
3319
|
+
throw error;
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
async function getTraceQueryObservedFields(db, plan) {
|
|
3323
|
+
if (plan.predicateScope !== "trace") return {
|
|
3324
|
+
observedFields: [],
|
|
3325
|
+
observedFieldsTruncated: false
|
|
3326
|
+
};
|
|
3327
|
+
const rows = await runDuckDBDiscoveryQuery(db, compileDuckDBTraceQueryObservedFields(plan));
|
|
3328
|
+
return {
|
|
3329
|
+
observedFields: rows.slice(0, plan.limit).map((row) => coreStorage.createTraceQueryObservedFieldDescriptor(String(row.path), Number(row.occurrences))),
|
|
3330
|
+
observedFieldsTruncated: rows.length > plan.limit
|
|
3331
|
+
};
|
|
3332
|
+
}
|
|
3333
|
+
async function getTraceQueryValues(db, plan) {
|
|
3334
|
+
const rows = await runDuckDBDiscoveryQuery(db, compileDuckDBTraceQueryValues(plan));
|
|
3335
|
+
return coreStorage.getTraceQueryValuesResponseSchema.parse({
|
|
3336
|
+
values: rows.slice(0, plan.limit).map((row) => ({
|
|
3337
|
+
value: String(row.value),
|
|
3338
|
+
count: Number(row.count)
|
|
3339
|
+
})),
|
|
3340
|
+
valuesTruncated: rows.length > plan.limit
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3216
3343
|
function asIsoTimestamp(value) {
|
|
3217
3344
|
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
|
3218
3345
|
}
|
|
@@ -3234,6 +3361,12 @@ async function queryTraces(db, plan) {
|
|
|
3234
3361
|
const traces = visibleRows.map((row) => ({
|
|
3235
3362
|
traceId: String(row.traceId),
|
|
3236
3363
|
rootSpanId: String(row.rootSpanId),
|
|
3364
|
+
name: row.name,
|
|
3365
|
+
entityId: row.entityId ?? null,
|
|
3366
|
+
parentSpanId: row.parentSpanId ?? null,
|
|
3367
|
+
createdAt: asIsoTimestamp(row.startedAt),
|
|
3368
|
+
metadata: parseJson(row.metadata) ?? null,
|
|
3369
|
+
inputPreview: coreStorage.buildInputPreview(row.input) ?? null,
|
|
3237
3370
|
threadId: row.threadId == null ? null : String(row.threadId),
|
|
3238
3371
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
3239
3372
|
startedAt: asIsoTimestamp(row.startedAt),
|
|
@@ -4446,12 +4579,59 @@ ${args.tables.map((table) => ` - ${table.table}`).join("\n")}\n\nTo fix this, r
|
|
|
4446
4579
|
* DuckDB-backed observability storage for traces, metrics, logs, scores, and feedback.
|
|
4447
4580
|
* Uses an append-only event-sourced model with SQL-based reconstruction for spans.
|
|
4448
4581
|
*/
|
|
4449
|
-
var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
|
|
4582
|
+
var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends ObservabilityStorage {
|
|
4583
|
+
static retentionTables = {
|
|
4584
|
+
spans: {
|
|
4585
|
+
table: "span_events",
|
|
4586
|
+
column: "timestamp",
|
|
4587
|
+
indexed: false
|
|
4588
|
+
},
|
|
4589
|
+
metrics: {
|
|
4590
|
+
table: "metric_events",
|
|
4591
|
+
column: "timestamp",
|
|
4592
|
+
indexed: false
|
|
4593
|
+
},
|
|
4594
|
+
logs: {
|
|
4595
|
+
table: "log_events",
|
|
4596
|
+
column: "timestamp",
|
|
4597
|
+
indexed: false
|
|
4598
|
+
},
|
|
4599
|
+
scores: {
|
|
4600
|
+
table: "score_events",
|
|
4601
|
+
column: "timestamp",
|
|
4602
|
+
indexed: false
|
|
4603
|
+
},
|
|
4604
|
+
feedback: {
|
|
4605
|
+
table: "feedback_events",
|
|
4606
|
+
column: "timestamp",
|
|
4607
|
+
indexed: false
|
|
4608
|
+
}
|
|
4609
|
+
};
|
|
4450
4610
|
db;
|
|
4451
4611
|
constructor(config) {
|
|
4452
4612
|
super();
|
|
4453
4613
|
this.db = config.db;
|
|
4454
4614
|
}
|
|
4615
|
+
/** Delete observability events older than their configured max age. */
|
|
4616
|
+
async prune(policies, options) {
|
|
4617
|
+
const targets = resolveTargets({
|
|
4618
|
+
policies,
|
|
4619
|
+
descriptor: ObservabilityStorageDuckDB.retentionTables,
|
|
4620
|
+
order: [
|
|
4621
|
+
"spans",
|
|
4622
|
+
"metrics",
|
|
4623
|
+
"logs",
|
|
4624
|
+
"scores",
|
|
4625
|
+
"feedback"
|
|
4626
|
+
]
|
|
4627
|
+
});
|
|
4628
|
+
return runPrune({
|
|
4629
|
+
db: this.db,
|
|
4630
|
+
domain: "observability",
|
|
4631
|
+
targets,
|
|
4632
|
+
options
|
|
4633
|
+
});
|
|
4634
|
+
}
|
|
4455
4635
|
/** Create all observability tables if they don't exist. */
|
|
4456
4636
|
async init() {
|
|
4457
4637
|
const migrationStatus = await checkSignalTablesMigrationStatus(this.db);
|
|
@@ -4506,6 +4686,7 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
|
|
|
4506
4686
|
"metrics",
|
|
4507
4687
|
"logs",
|
|
4508
4688
|
"trace-query",
|
|
4689
|
+
"trace-query-discovery",
|
|
4509
4690
|
"thread-query"
|
|
4510
4691
|
];
|
|
4511
4692
|
return [
|
|
@@ -4513,6 +4694,7 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
|
|
|
4513
4694
|
"logs",
|
|
4514
4695
|
"delta-polling",
|
|
4515
4696
|
"trace-query",
|
|
4697
|
+
"trace-query-discovery",
|
|
4516
4698
|
"thread-query"
|
|
4517
4699
|
];
|
|
4518
4700
|
}
|
|
@@ -4546,6 +4728,12 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
|
|
|
4546
4728
|
async queryTraces(plan) {
|
|
4547
4729
|
return queryTraces(this.db, plan);
|
|
4548
4730
|
}
|
|
4731
|
+
async getTraceQueryObservedFields(plan) {
|
|
4732
|
+
return getTraceQueryObservedFields(this.db, plan);
|
|
4733
|
+
}
|
|
4734
|
+
async getTraceQueryValues(plan) {
|
|
4735
|
+
return getTraceQueryValues(this.db, plan);
|
|
4736
|
+
}
|
|
4549
4737
|
async queryThreads(plan) {
|
|
4550
4738
|
return queryThreads(this.db, plan);
|
|
4551
4739
|
}
|
|
@@ -4662,4 +4850,4 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
|
|
|
4662
4850
|
//#endregion
|
|
4663
4851
|
export { ObservabilityStorageDuckDB };
|
|
4664
4852
|
|
|
4665
|
-
//# sourceMappingURL=observability-
|
|
4853
|
+
//# sourceMappingURL=observability-W6La6xQK.js.map
|