@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.
@@ -1,10 +1,36 @@
1
- import { t as DuckDBConnection } from "./db-b2jufH0V.js";
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-DEjVUB3O.js.map
4853
+ //# sourceMappingURL=observability-W6La6xQK.js.map