@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.
@@ -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.
@@ -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 tuples = args.scores.map((s) => {
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 query = compileDuckDBTraceQueryObservedFields(plan);
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 query = compileDuckDBTraceQueryValues(plan);
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-CCUvqiBv.js.map
4912
+ //# sourceMappingURL=observability-VRBKtkKz.js.map