@mastra/duckdb 1.5.1 → 1.5.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,3808 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkSMRZJTCI_cjs = require('./chunk-SMRZJTCI.cjs');
4
- var error = require('@mastra/core/error');
5
- var storage = require('@mastra/core/storage');
6
- var observability = require('@mastra/core/observability');
7
- var utils = require('@mastra/core/utils');
8
- var features = require('@mastra/core/features');
9
-
10
- // src/storage/domains/observability/ddl.ts
11
- var SPAN_EVENTS_CURSOR_SEQUENCE_DDL = `
12
- CREATE SEQUENCE IF NOT EXISTS span_events_cursor_id_seq START 1
13
- `;
14
- var METRIC_EVENTS_CURSOR_SEQUENCE_DDL = `
15
- CREATE SEQUENCE IF NOT EXISTS metric_events_cursor_id_seq START 1
16
- `;
17
- var LOG_EVENTS_CURSOR_SEQUENCE_DDL = `
18
- CREATE SEQUENCE IF NOT EXISTS log_events_cursor_id_seq START 1
19
- `;
20
- var SCORE_EVENTS_CURSOR_SEQUENCE_DDL = `
21
- CREATE SEQUENCE IF NOT EXISTS score_events_cursor_id_seq START 1
22
- `;
23
- var FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL = `
24
- CREATE SEQUENCE IF NOT EXISTS feedback_events_cursor_id_seq START 1
25
- `;
26
- var SPAN_EVENTS_DDL = `
27
- CREATE TABLE IF NOT EXISTS span_events (
28
- -- Event metadata
29
- eventType VARCHAR NOT NULL,
30
- timestamp TIMESTAMP NOT NULL,
31
- cursorId BIGINT,
32
-
33
- -- IDs
34
- traceId VARCHAR NOT NULL,
35
- spanId VARCHAR NOT NULL,
36
- parentSpanId VARCHAR,
37
- experimentId VARCHAR,
38
-
39
- -- Entity
40
- entityType VARCHAR,
41
- entityId VARCHAR,
42
- entityName VARCHAR,
43
- entityVersionId VARCHAR,
44
-
45
- -- Context
46
- userId VARCHAR,
47
- organizationId VARCHAR,
48
- resourceId VARCHAR,
49
- runId VARCHAR,
50
- sessionId VARCHAR,
51
- threadId VARCHAR,
52
- requestId VARCHAR,
53
- environment VARCHAR,
54
- source VARCHAR,
55
- serviceName VARCHAR,
56
- requestContext JSON,
57
-
58
- -- Span-specific scalars
59
- name VARCHAR,
60
- spanType VARCHAR,
61
- isEvent BOOLEAN,
62
- endedAt TIMESTAMP,
63
-
64
- -- JSON fields
65
- attributes JSON,
66
- metadata JSON,
67
- tags JSON,
68
- scope JSON,
69
- links JSON,
70
- input JSON,
71
- output JSON,
72
- error JSON
73
- )`;
74
- var METRIC_EVENTS_DDL = `
75
- CREATE TABLE IF NOT EXISTS metric_events (
76
- -- Event metadata
77
- timestamp TIMESTAMP NOT NULL,
78
- cursorId BIGINT,
79
-
80
- -- IDs
81
- metricId VARCHAR NOT NULL PRIMARY KEY,
82
- traceId VARCHAR,
83
- spanId VARCHAR,
84
- experimentId VARCHAR,
85
-
86
- -- Entity hierarchy
87
- entityType VARCHAR,
88
- entityId VARCHAR,
89
- entityName VARCHAR,
90
- entityVersionId VARCHAR,
91
- parentEntityVersionId VARCHAR,
92
- parentEntityType VARCHAR,
93
- parentEntityId VARCHAR,
94
- parentEntityName VARCHAR,
95
- rootEntityVersionId VARCHAR,
96
- rootEntityType VARCHAR,
97
- rootEntityId VARCHAR,
98
- rootEntityName VARCHAR,
99
-
100
- -- Context
101
- userId VARCHAR,
102
- organizationId VARCHAR,
103
- resourceId VARCHAR,
104
- runId VARCHAR,
105
- sessionId VARCHAR,
106
- threadId VARCHAR,
107
- requestId VARCHAR,
108
- environment VARCHAR,
109
- executionSource VARCHAR,
110
- serviceName VARCHAR,
111
-
112
- -- Metric-specific scalars
113
- name VARCHAR NOT NULL,
114
- value DOUBLE NOT NULL,
115
- provider VARCHAR,
116
- model VARCHAR,
117
- estimatedCost DOUBLE,
118
- costUnit VARCHAR,
119
-
120
- -- JSON fields
121
- tags JSON,
122
- labels JSON,
123
- costMetadata JSON,
124
- metadata JSON,
125
- scope JSON
126
- )`;
127
- var LOG_EVENTS_DDL = `
128
- CREATE TABLE IF NOT EXISTS log_events (
129
- -- Event metadata
130
- timestamp TIMESTAMP NOT NULL,
131
- cursorId BIGINT,
132
-
133
- -- IDs
134
- logId VARCHAR NOT NULL PRIMARY KEY,
135
- traceId VARCHAR,
136
- spanId VARCHAR,
137
- experimentId VARCHAR,
138
-
139
- -- Entity hierarchy
140
- entityType VARCHAR,
141
- entityId VARCHAR,
142
- entityName VARCHAR,
143
- entityVersionId VARCHAR,
144
- parentEntityVersionId VARCHAR,
145
- parentEntityType VARCHAR,
146
- parentEntityId VARCHAR,
147
- parentEntityName VARCHAR,
148
- rootEntityVersionId VARCHAR,
149
- rootEntityType VARCHAR,
150
- rootEntityId VARCHAR,
151
- rootEntityName VARCHAR,
152
-
153
- -- Context
154
- userId VARCHAR,
155
- organizationId VARCHAR,
156
- resourceId VARCHAR,
157
- runId VARCHAR,
158
- sessionId VARCHAR,
159
- threadId VARCHAR,
160
- requestId VARCHAR,
161
- environment VARCHAR,
162
- executionSource VARCHAR,
163
- serviceName VARCHAR,
164
-
165
- -- Log-specific scalars
166
- level VARCHAR NOT NULL,
167
- message VARCHAR NOT NULL,
168
-
169
- -- JSON fields
170
- data JSON,
171
- tags JSON,
172
- metadata JSON,
173
- scope JSON
174
- )`;
175
- var SCORE_EVENTS_DDL = `
176
- CREATE TABLE IF NOT EXISTS score_events (
177
- -- Event metadata
178
- timestamp TIMESTAMP NOT NULL,
179
- cursorId BIGINT,
180
-
181
- -- IDs
182
- scoreId VARCHAR NOT NULL PRIMARY KEY,
183
- traceId VARCHAR,
184
- spanId VARCHAR,
185
- experimentId VARCHAR,
186
- scoreTraceId VARCHAR,
187
-
188
- -- Entity hierarchy
189
- entityType VARCHAR,
190
- entityId VARCHAR,
191
- entityName VARCHAR,
192
- entityVersionId VARCHAR,
193
- parentEntityVersionId VARCHAR,
194
- parentEntityType VARCHAR,
195
- parentEntityId VARCHAR,
196
- parentEntityName VARCHAR,
197
- rootEntityVersionId VARCHAR,
198
- rootEntityType VARCHAR,
199
- rootEntityId VARCHAR,
200
- rootEntityName VARCHAR,
201
-
202
- -- Context
203
- userId VARCHAR,
204
- organizationId VARCHAR,
205
- resourceId VARCHAR,
206
- runId VARCHAR,
207
- sessionId VARCHAR,
208
- threadId VARCHAR,
209
- requestId VARCHAR,
210
- environment VARCHAR,
211
- executionSource VARCHAR,
212
- serviceName VARCHAR,
213
-
214
- -- Score-specific scalars
215
- scorerId VARCHAR NOT NULL,
216
- scorerVersion VARCHAR,
217
- source VARCHAR,
218
- scoreSource VARCHAR,
219
- score DOUBLE NOT NULL,
220
- reason VARCHAR,
221
-
222
- -- JSON fields
223
- tags JSON,
224
- metadata JSON,
225
- scope JSON
226
- )`;
227
- var FEEDBACK_EVENTS_DDL = `
228
- CREATE TABLE IF NOT EXISTS feedback_events (
229
- -- Event metadata
230
- timestamp TIMESTAMP NOT NULL,
231
- cursorId BIGINT,
232
-
233
- -- IDs
234
- feedbackId VARCHAR NOT NULL PRIMARY KEY,
235
- traceId VARCHAR,
236
- spanId VARCHAR,
237
- experimentId VARCHAR,
238
- -- Entity hierarchy
239
- entityType VARCHAR,
240
- entityId VARCHAR,
241
- entityName VARCHAR,
242
- entityVersionId VARCHAR,
243
- parentEntityVersionId VARCHAR,
244
- parentEntityType VARCHAR,
245
- parentEntityId VARCHAR,
246
- parentEntityName VARCHAR,
247
- rootEntityVersionId VARCHAR,
248
- rootEntityType VARCHAR,
249
- rootEntityId VARCHAR,
250
- rootEntityName VARCHAR,
251
-
252
- -- Context
253
- userId VARCHAR,
254
- organizationId VARCHAR,
255
- resourceId VARCHAR,
256
- runId VARCHAR,
257
- sessionId VARCHAR,
258
- threadId VARCHAR,
259
- requestId VARCHAR,
260
- environment VARCHAR,
261
- executionSource VARCHAR,
262
- serviceName VARCHAR,
263
-
264
- -- Feedback actor / linkage
265
- feedbackUserId VARCHAR,
266
- sourceId VARCHAR,
267
-
268
- -- Feedback-specific scalars
269
- source VARCHAR,
270
- feedbackSource VARCHAR NOT NULL,
271
- feedbackType VARCHAR NOT NULL,
272
- value VARCHAR NOT NULL,
273
- comment VARCHAR,
274
-
275
- -- JSON fields
276
- tags JSON,
277
- metadata JSON,
278
- scope JSON
279
- )`;
280
- var ALL_DDL = [
281
- SPAN_EVENTS_CURSOR_SEQUENCE_DDL,
282
- METRIC_EVENTS_CURSOR_SEQUENCE_DDL,
283
- LOG_EVENTS_CURSOR_SEQUENCE_DDL,
284
- SCORE_EVENTS_CURSOR_SEQUENCE_DDL,
285
- FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL,
286
- SPAN_EVENTS_DDL,
287
- METRIC_EVENTS_DDL,
288
- LOG_EVENTS_DDL,
289
- SCORE_EVENTS_DDL,
290
- FEEDBACK_EVENTS_DDL
291
- ];
292
- var ALL_MIGRATIONS = [
293
- `CREATE SEQUENCE IF NOT EXISTS span_events_cursor_id_seq START 1`,
294
- `CREATE SEQUENCE IF NOT EXISTS metric_events_cursor_id_seq START 1`,
295
- `CREATE SEQUENCE IF NOT EXISTS log_events_cursor_id_seq START 1`,
296
- `CREATE SEQUENCE IF NOT EXISTS score_events_cursor_id_seq START 1`,
297
- `CREATE SEQUENCE IF NOT EXISTS feedback_events_cursor_id_seq START 1`,
298
- // Existing rows intentionally keep NULL cursorId values; delta polling only
299
- // applies to rows written by insert paths that explicitly call nextval().
300
- // Databases upgraded from a prior version may still carry a
301
- // `DEFAULT nextval(...)` on cursorId, which breaks DuckDB WAL replay; that
302
- // remediation lives in `dropLegacyCursorIdDefaults` and only runs when the
303
- // bad default is detected in information_schema.
304
- `ALTER TABLE span_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
305
- `ALTER TABLE span_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
306
- // Metrics. Legacy rows remain page-visible but are not part of delta polling.
307
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
308
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
309
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
310
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
311
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS experimentId VARCHAR`,
312
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS parentEntityType VARCHAR`,
313
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS parentEntityId VARCHAR`,
314
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS parentEntityName VARCHAR`,
315
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS rootEntityType VARCHAR`,
316
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS rootEntityId VARCHAR`,
317
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS rootEntityName VARCHAR`,
318
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS userId VARCHAR`,
319
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS organizationId VARCHAR`,
320
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS resourceId VARCHAR`,
321
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS runId VARCHAR`,
322
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS sessionId VARCHAR`,
323
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS threadId VARCHAR`,
324
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS requestId VARCHAR`,
325
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS environment VARCHAR`,
326
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS executionSource VARCHAR`,
327
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS serviceName VARCHAR`,
328
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS costMetadata JSON`,
329
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS metadata JSON`,
330
- `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS scope JSON`,
331
- // Logs. Legacy rows remain page-visible but are not part of delta polling.
332
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
333
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
334
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
335
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
336
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS experimentId VARCHAR`,
337
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS parentEntityType VARCHAR`,
338
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS parentEntityId VARCHAR`,
339
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS parentEntityName VARCHAR`,
340
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS rootEntityType VARCHAR`,
341
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS rootEntityId VARCHAR`,
342
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS rootEntityName VARCHAR`,
343
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS userId VARCHAR`,
344
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS organizationId VARCHAR`,
345
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS resourceId VARCHAR`,
346
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS runId VARCHAR`,
347
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS sessionId VARCHAR`,
348
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS threadId VARCHAR`,
349
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS requestId VARCHAR`,
350
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS environment VARCHAR`,
351
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS executionSource VARCHAR`,
352
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS serviceName VARCHAR`,
353
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS tags JSON`,
354
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS metadata JSON`,
355
- `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS scope JSON`,
356
- // Scores. Legacy rows remain page-visible but are not part of delta polling.
357
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
358
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
359
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
360
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
361
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS entityType VARCHAR`,
362
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS entityId VARCHAR`,
363
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS entityName VARCHAR`,
364
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS parentEntityType VARCHAR`,
365
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS parentEntityId VARCHAR`,
366
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS parentEntityName VARCHAR`,
367
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS rootEntityType VARCHAR`,
368
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS rootEntityId VARCHAR`,
369
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS rootEntityName VARCHAR`,
370
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS userId VARCHAR`,
371
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS organizationId VARCHAR`,
372
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS resourceId VARCHAR`,
373
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS runId VARCHAR`,
374
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS sessionId VARCHAR`,
375
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS threadId VARCHAR`,
376
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS requestId VARCHAR`,
377
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS environment VARCHAR`,
378
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS executionSource VARCHAR`,
379
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS serviceName VARCHAR`,
380
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS tags JSON`,
381
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS scope JSON`,
382
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS source VARCHAR`,
383
- `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS scoreSource VARCHAR`,
384
- `ALTER TABLE score_events ALTER COLUMN traceId DROP NOT NULL`,
385
- // Feedback. Legacy rows remain page-visible but are not part of delta polling.
386
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
387
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
388
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
389
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
390
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS entityType VARCHAR`,
391
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS entityId VARCHAR`,
392
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS entityName VARCHAR`,
393
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS parentEntityType VARCHAR`,
394
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS parentEntityId VARCHAR`,
395
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS parentEntityName VARCHAR`,
396
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS rootEntityType VARCHAR`,
397
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS rootEntityId VARCHAR`,
398
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS rootEntityName VARCHAR`,
399
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS organizationId VARCHAR`,
400
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS resourceId VARCHAR`,
401
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS runId VARCHAR`,
402
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS sessionId VARCHAR`,
403
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS threadId VARCHAR`,
404
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS requestId VARCHAR`,
405
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS environment VARCHAR`,
406
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS executionSource VARCHAR`,
407
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS serviceName VARCHAR`,
408
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS feedbackUserId VARCHAR`,
409
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS sourceId VARCHAR`,
410
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS tags JSON`,
411
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS scope JSON`,
412
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS source VARCHAR`,
413
- `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS feedbackSource VARCHAR`,
414
- `ALTER TABLE feedback_events ALTER COLUMN traceId DROP NOT NULL`
415
- ];
416
- function unionDistinctQueries(selects, orderBy) {
417
- return `${selects.join("\nUNION\n")}
418
- ORDER BY ${orderBy}`;
419
- }
420
- async function getEntityTypes(db, _args) {
421
- const rows = await db.query(
422
- unionDistinctQueries(
423
- [
424
- `SELECT entityType FROM span_events WHERE entityType IS NOT NULL`,
425
- `SELECT entityType FROM metric_events WHERE entityType IS NOT NULL`,
426
- `SELECT entityType FROM log_events WHERE entityType IS NOT NULL`
427
- ],
428
- "entityType"
429
- )
430
- );
431
- const validTypes = new Set(Object.values(observability.EntityType));
432
- const typeSet = /* @__PURE__ */ new Set();
433
- for (const row of rows) {
434
- if (row.entityType && validTypes.has(row.entityType)) {
435
- typeSet.add(row.entityType);
436
- }
437
- }
438
- return { entityTypes: Array.from(typeSet).sort() };
439
- }
440
- async function getEntityNames(db, args) {
441
- const buildSelect = (table) => {
442
- const conditions = [`entityName IS NOT NULL`];
443
- if (args.entityType) {
444
- conditions.push(`entityType = ?`);
445
- }
446
- return `SELECT entityName FROM ${table} WHERE ${conditions.join(" AND ")}`;
447
- };
448
- const params = args.entityType ? [args.entityType, args.entityType, args.entityType] : [];
449
- const rows = await db.query(
450
- unionDistinctQueries(
451
- [buildSelect("span_events"), buildSelect("metric_events"), buildSelect("log_events")],
452
- "entityName"
453
- ),
454
- params
455
- );
456
- return { names: rows.map((r) => r.entityName) };
457
- }
458
- async function getServiceNames(db, _args) {
459
- const rows = await db.query(
460
- unionDistinctQueries(
461
- [
462
- `SELECT serviceName FROM span_events WHERE serviceName IS NOT NULL`,
463
- `SELECT serviceName FROM metric_events WHERE serviceName IS NOT NULL`,
464
- `SELECT serviceName FROM log_events WHERE serviceName IS NOT NULL`
465
- ],
466
- "serviceName"
467
- )
468
- );
469
- return { serviceNames: rows.map((r) => r.serviceName) };
470
- }
471
- async function getEnvironments(db, _args) {
472
- const rows = await db.query(
473
- unionDistinctQueries(
474
- [
475
- `SELECT environment FROM span_events WHERE environment IS NOT NULL`,
476
- `SELECT environment FROM metric_events WHERE environment IS NOT NULL`,
477
- `SELECT environment FROM log_events WHERE environment IS NOT NULL`
478
- ],
479
- "environment"
480
- )
481
- );
482
- return { environments: rows.map((r) => r.environment) };
483
- }
484
- async function getTags(db, args) {
485
- const buildSelect = (table) => {
486
- const conditions = [`tags IS NOT NULL`];
487
- if (args.entityType) {
488
- conditions.push(`entityType = ?`);
489
- }
490
- return `SELECT unnest(CAST(tags AS VARCHAR[])) AS tag FROM ${table} WHERE ${conditions.join(" AND ")}`;
491
- };
492
- const params = args.entityType ? [args.entityType, args.entityType, args.entityType] : [];
493
- const rows = await db.query(
494
- unionDistinctQueries([buildSelect("span_events"), buildSelect("metric_events"), buildSelect("log_events")], "tag"),
495
- params
496
- );
497
- return { tags: rows.map((r) => r.tag) };
498
- }
499
- function buildJsonPath(key) {
500
- try {
501
- return `$.${utils.parseFieldKey(key)}`;
502
- } catch {
503
- const escaped = key.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
504
- return `$."${escaped}"`;
505
- }
506
- }
507
- function normalizeJsonFilterValue(value) {
508
- if (value === void 0) return null;
509
- if (typeof value === "string") return value;
510
- const json = JSON.stringify(value);
511
- return json ?? null;
512
- }
513
- function sanitizeColumn(column) {
514
- return utils.parseFieldKey(column);
515
- }
516
- function buildWhereClause(filters, fieldMappings) {
517
- if (!filters) return { clause: "", params: [] };
518
- const conditions = [];
519
- const params = [];
520
- for (const [key, value] of Object.entries(filters)) {
521
- if (value === void 0 || value === null) continue;
522
- const column = sanitizeColumn(fieldMappings?.[key] ?? key);
523
- if (key === "timestamp" || key === "startedAt" || key === "endedAt") {
524
- const dateRange = value;
525
- if (dateRange.start) {
526
- const op = dateRange.startExclusive ? ">" : ">=";
527
- conditions.push(`${column} ${op} ?`);
528
- params.push(dateRange.start);
529
- }
530
- if (dateRange.end) {
531
- const op = dateRange.endExclusive ? "<" : "<=";
532
- conditions.push(`${column} ${op} ?`);
533
- params.push(dateRange.end);
534
- }
535
- continue;
536
- }
537
- if (key === "labels") {
538
- const labelsObj = value;
539
- for (const [labelKey, labelValue] of Object.entries(labelsObj)) {
540
- conditions.push(`json_extract_string(${column}, ?) = ?`);
541
- params.push(buildJsonPath(labelKey), labelValue);
542
- }
543
- continue;
544
- }
545
- if (key === "tags") {
546
- const tags = value;
547
- for (const tag of tags) {
548
- conditions.push(`list_contains(CAST(${column} AS VARCHAR[]), ?)`);
549
- params.push(tag);
550
- }
551
- continue;
552
- }
553
- if (key === "status") {
554
- const status = value;
555
- if (status === "error") {
556
- conditions.push(`error IS NOT NULL`);
557
- } else if (status === "running") {
558
- conditions.push(`endedAt IS NULL AND error IS NULL`);
559
- } else if (status === "success") {
560
- conditions.push(`endedAt IS NOT NULL AND error IS NULL`);
561
- }
562
- continue;
563
- }
564
- if (key === "hasChildError") {
565
- continue;
566
- }
567
- if (key === "metadata" || key === "scope") {
568
- const jsonObj = value;
569
- for (const [jsonKey, jsonValue] of Object.entries(jsonObj)) {
570
- const normalized = normalizeJsonFilterValue(jsonValue);
571
- if (normalized === null) continue;
572
- conditions.push(`json_extract_string(${column}, ?) = ?`);
573
- params.push(buildJsonPath(jsonKey), normalized);
574
- }
575
- continue;
576
- }
577
- if (Array.isArray(value)) {
578
- if (value.length === 0) continue;
579
- const placeholders = value.map(() => "?").join(", ");
580
- conditions.push(`${column} IN (${placeholders})`);
581
- params.push(...value);
582
- continue;
583
- }
584
- conditions.push(`${column} = ?`);
585
- params.push(value);
586
- }
587
- const clause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
588
- return { clause, params };
589
- }
590
- function buildOrderByClause(orderBy) {
591
- if (!orderBy) return "";
592
- const dir = orderBy.direction.toUpperCase();
593
- if (dir !== "ASC" && dir !== "DESC") {
594
- throw new Error(`Invalid sort direction: ${orderBy.direction}`);
595
- }
596
- const field = utils.parseFieldKey(orderBy.field);
597
- return `ORDER BY ${field} ${dir}`;
598
- }
599
- function buildPaginationClause(pagination) {
600
- if (!pagination) return { clause: "", params: [] };
601
- if (!Number.isInteger(pagination.page) || pagination.page < 0) {
602
- throw new Error(`Invalid page: ${pagination.page}`);
603
- }
604
- if (!Number.isInteger(pagination.perPage) || pagination.perPage <= 0) {
605
- throw new Error(`Invalid perPage: ${pagination.perPage}`);
606
- }
607
- const offset = pagination.page * pagination.perPage;
608
- return {
609
- clause: `LIMIT ? OFFSET ?`,
610
- params: [pagination.perPage, offset]
611
- };
612
- }
613
-
614
- // src/storage/domains/observability/helpers.ts
615
- var v = chunkSMRZJTCI_cjs.DuckDBConnection.sqlValue;
616
- function jsonV(val) {
617
- if (val === null || val === void 0) return "NULL";
618
- return chunkSMRZJTCI_cjs.DuckDBConnection.sqlValue(JSON.stringify(val));
619
- }
620
- function toDate(val) {
621
- if (val === null || val === void 0) {
622
- throw new Error("Expected date value but received null/undefined");
623
- }
624
- const date = val instanceof Date ? val : new Date(String(val));
625
- if (Number.isNaN(date.getTime())) {
626
- throw new Error("Expected valid date but received invalid date");
627
- }
628
- return date;
629
- }
630
- function toDateOrNull(val) {
631
- if (val === null || val === void 0) return null;
632
- return val instanceof Date ? val : new Date(String(val));
633
- }
634
- function parseJson(value) {
635
- if (value === null || value === void 0) return null;
636
- if (typeof value === "string") {
637
- try {
638
- return JSON.parse(value);
639
- } catch {
640
- return value;
641
- }
642
- }
643
- return value;
644
- }
645
- function parseJsonArray(value) {
646
- if (value === null || value === void 0) return null;
647
- const parsed = parseJson(value);
648
- return Array.isArray(parsed) ? parsed : null;
649
- }
650
- var OBSERVABILITY_DELTA_POLLING_FEATURE = "observability-delta-polling";
651
- function deltaPollingFeatureEnabled() {
652
- return features.coreFeatures.has(OBSERVABILITY_DELTA_POLLING_FEATURE);
653
- }
654
- function assertDeltaPollingEnabled() {
655
- if (deltaPollingFeatureEnabled()) {
656
- return;
657
- }
658
- throw new error.MastraError({
659
- id: "OBSERVABILITY_DELTA_POLLING_NOT_SUPPORTED",
660
- domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
661
- category: error.ErrorCategory.SYSTEM,
662
- text: "This storage provider does not support observability delta polling"
663
- });
664
- }
665
- function encodeDeltaCursor(value) {
666
- return String(value ?? 0);
667
- }
668
- function validateCursorId(cursor) {
669
- if (!/^\d+$/.test(cursor)) {
670
- throw new error.MastraError({
671
- id: "OBSERVABILITY_INVALID_DELTA_CURSOR",
672
- domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
673
- category: error.ErrorCategory.USER,
674
- text: "Invalid observability delta cursor"
675
- });
676
- }
677
- return cursor;
678
- }
679
- function extendWhereClause(baseClause, extraConditions) {
680
- const conditions = extraConditions.filter(Boolean);
681
- if (conditions.length === 0) {
682
- return baseClause;
683
- }
684
- if (!baseClause) {
685
- return `WHERE ${conditions.join(" AND ")}`;
686
- }
687
- return `${baseClause} AND ${conditions.join(" AND ")}`;
688
- }
689
-
690
- // src/storage/domains/observability/feedback.ts
691
- var FEEDBACK_GROUP_BY_COLUMNS = /* @__PURE__ */ new Set([
692
- "timestamp",
693
- "traceId",
694
- "spanId",
695
- "experimentId",
696
- "entityType",
697
- "entityId",
698
- "entityName",
699
- "entityVersionId",
700
- "parentEntityVersionId",
701
- "parentEntityType",
702
- "parentEntityId",
703
- "parentEntityName",
704
- "rootEntityVersionId",
705
- "rootEntityType",
706
- "rootEntityId",
707
- "rootEntityName",
708
- "userId",
709
- "organizationId",
710
- "resourceId",
711
- "runId",
712
- "sessionId",
713
- "threadId",
714
- "requestId",
715
- "environment",
716
- "executionSource",
717
- "serviceName",
718
- "feedbackUserId",
719
- "sourceId",
720
- "feedbackSource",
721
- "feedbackType",
722
- "value",
723
- "comment"
724
- ]);
725
- function getAggregationSql(aggregation, measure = "TRY_CAST(value AS DOUBLE)") {
726
- switch (aggregation) {
727
- case "sum":
728
- return `SUM(${measure})`;
729
- case "avg":
730
- return `AVG(${measure})`;
731
- case "min":
732
- return `MIN(${measure})`;
733
- case "max":
734
- return `MAX(${measure})`;
735
- case "count":
736
- return `CAST(COUNT(${measure}) AS DOUBLE)`;
737
- case "last":
738
- return `arg_max(${measure}, timestamp)`;
739
- default:
740
- return `SUM(${measure})`;
741
- }
742
- }
743
- function getIntervalSql(interval) {
744
- switch (interval) {
745
- case "1m":
746
- return "1 minute";
747
- case "5m":
748
- return "5 minutes";
749
- case "15m":
750
- return "15 minutes";
751
- case "1h":
752
- return "1 hour";
753
- case "1d":
754
- return "1 day";
755
- default:
756
- return "1 hour";
757
- }
758
- }
759
- function getValidatedPercentiles(percentiles) {
760
- if (!Array.isArray(percentiles) || percentiles.length === 0) {
761
- throw new Error("Percentiles must include at least one value between 0 and 1.");
762
- }
763
- return percentiles.map((percentile) => {
764
- if (!Number.isFinite(percentile) || percentile < 0 || percentile > 1) {
765
- throw new Error("Percentiles must be finite numbers between 0 and 1.");
766
- }
767
- return percentile;
768
- });
769
- }
770
- function buildFeedbackWhereClause(args, includeNumericGuard = false) {
771
- const conditions = ["feedbackType = ?"];
772
- const params = [args.feedbackType];
773
- if (args.feedbackSource !== void 0) {
774
- conditions.push("feedbackSource = ?");
775
- params.push(args.feedbackSource);
776
- }
777
- const { clause: filterClause, params: filterParams } = buildWhereClause(
778
- args.filters,
779
- { source: "feedbackSource" }
780
- );
781
- if (filterClause) {
782
- conditions.push(filterClause.replace("WHERE ", ""));
783
- params.push(...filterParams);
784
- }
785
- if (includeNumericGuard) {
786
- conditions.push("TRY_CAST(value AS DOUBLE) IS NOT NULL");
787
- }
788
- return { clause: `WHERE ${conditions.join(" AND ")}`, params };
789
- }
790
- function resolveFeedbackGroupBy(groupBy) {
791
- return groupBy.map((key, index) => {
792
- const column = utils.parseFieldKey(key);
793
- if (!FEEDBACK_GROUP_BY_COLUMNS.has(column)) {
794
- throw new Error(`Invalid groupBy column(s): ${key}`);
795
- }
796
- const alias = `group_by_${index}`;
797
- return {
798
- key,
799
- selectSql: `${column} AS ${alias}`,
800
- groupSql: alias
801
- };
802
- });
803
- }
804
- function toSeriesName(values) {
805
- return values.map((value) => value === null || value === void 0 ? "" : String(value)).join("|");
806
- }
807
- function rowToFeedbackRecord(row) {
808
- const rawValue = row.value;
809
- let value = rawValue;
810
- const numValue = Number(rawValue);
811
- if (!isNaN(numValue)) value = numValue;
812
- return {
813
- feedbackId: row.feedbackId,
814
- timestamp: toDate(row.timestamp),
815
- traceId: row.traceId ?? null,
816
- spanId: row.spanId ?? null,
817
- experimentId: row.experimentId ?? null,
818
- entityType: row.entityType ?? null,
819
- entityId: row.entityId ?? null,
820
- entityName: row.entityName ?? null,
821
- entityVersionId: row.entityVersionId ?? null,
822
- parentEntityVersionId: row.parentEntityVersionId ?? null,
823
- parentEntityType: row.parentEntityType ?? null,
824
- parentEntityId: row.parentEntityId ?? null,
825
- parentEntityName: row.parentEntityName ?? null,
826
- rootEntityVersionId: row.rootEntityVersionId ?? null,
827
- rootEntityType: row.rootEntityType ?? null,
828
- rootEntityId: row.rootEntityId ?? null,
829
- rootEntityName: row.rootEntityName ?? null,
830
- userId: row.userId ?? null,
831
- organizationId: row.organizationId ?? null,
832
- resourceId: row.resourceId ?? null,
833
- runId: row.runId ?? null,
834
- sessionId: row.sessionId ?? null,
835
- threadId: row.threadId ?? null,
836
- requestId: row.requestId ?? null,
837
- environment: row.environment ?? null,
838
- executionSource: row.executionSource ?? null,
839
- serviceName: row.serviceName ?? null,
840
- feedbackUserId: row.feedbackUserId ?? null,
841
- sourceId: row.sourceId ?? null,
842
- source: row.feedbackSource,
843
- feedbackSource: row.feedbackSource,
844
- feedbackType: row.feedbackType,
845
- value,
846
- comment: row.comment ?? null,
847
- tags: parseJsonArray(row.tags),
848
- metadata: parseJson(row.metadata),
849
- scope: parseJson(row.scope)
850
- };
851
- }
852
- function getComparisonDateRange(comparePeriod, timestamp) {
853
- if (!timestamp.start || !timestamp.end) return null;
854
- const duration = timestamp.end.getTime() - timestamp.start.getTime();
855
- switch (comparePeriod) {
856
- case "previous_period":
857
- return {
858
- start: new Date(timestamp.start.getTime() - duration),
859
- end: new Date(timestamp.end.getTime() - duration),
860
- startExclusive: timestamp.startExclusive,
861
- endExclusive: timestamp.endExclusive
862
- };
863
- case "previous_day":
864
- return {
865
- start: new Date(timestamp.start.getTime() - 864e5),
866
- end: new Date(timestamp.end.getTime() - 864e5),
867
- startExclusive: timestamp.startExclusive,
868
- endExclusive: timestamp.endExclusive
869
- };
870
- case "previous_week":
871
- return {
872
- start: new Date(timestamp.start.getTime() - 6048e5),
873
- end: new Date(timestamp.end.getTime() - 6048e5),
874
- startExclusive: timestamp.startExclusive,
875
- endExclusive: timestamp.endExclusive
876
- };
877
- }
878
- }
879
- async function createFeedback(db, args) {
880
- const f = args.feedback;
881
- const feedbackSource = f.feedbackSource ?? f.source ?? "";
882
- const feedbackUserId = f.feedbackUserId ?? f.userId ?? null;
883
- await db.execute(
884
- `INSERT INTO feedback_events (
885
- feedbackId, timestamp, cursorId, traceId, spanId, experimentId,
886
- entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
887
- userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
888
- feedbackUserId, sourceId, feedbackSource, feedbackType, value, comment, tags, metadata, scope
889
- )
890
- VALUES (${[
891
- v(f.feedbackId),
892
- v(f.timestamp),
893
- "nextval('feedback_events_cursor_id_seq')",
894
- v(f.traceId),
895
- v(f.spanId ?? null),
896
- v(f.experimentId ?? null),
897
- v(f.entityType ?? null),
898
- v(f.entityId ?? null),
899
- v(f.entityName ?? null),
900
- v(f.entityVersionId ?? null),
901
- v(f.parentEntityVersionId ?? null),
902
- v(f.parentEntityType ?? null),
903
- v(f.parentEntityId ?? null),
904
- v(f.parentEntityName ?? null),
905
- v(f.rootEntityVersionId ?? null),
906
- v(f.rootEntityType ?? null),
907
- v(f.rootEntityId ?? null),
908
- v(f.rootEntityName ?? null),
909
- v(f.userId ?? null),
910
- v(f.organizationId ?? null),
911
- v(f.resourceId ?? null),
912
- v(f.runId ?? null),
913
- v(f.sessionId ?? null),
914
- v(f.threadId ?? null),
915
- v(f.requestId ?? null),
916
- v(f.environment ?? null),
917
- v(f.executionSource ?? null),
918
- v(f.serviceName ?? null),
919
- v(feedbackUserId),
920
- v(f.sourceId ?? null),
921
- v(feedbackSource),
922
- v(f.feedbackType),
923
- v(String(f.value)),
924
- v(f.comment ?? null),
925
- jsonV(f.tags ?? null),
926
- jsonV(f.metadata),
927
- jsonV(f.scope ?? null)
928
- ].join(", ")})
929
- ON CONFLICT DO NOTHING`
930
- );
931
- }
932
- async function batchCreateFeedback(db, args) {
933
- if (args.feedbacks.length === 0) return;
934
- const tuples = args.feedbacks.map((f) => {
935
- const legacyFeedback = f;
936
- const feedbackSource = legacyFeedback.feedbackSource ?? legacyFeedback.source ?? "";
937
- const feedbackUserId = legacyFeedback.feedbackUserId ?? legacyFeedback.userId ?? null;
938
- return `(${[
939
- v(legacyFeedback.feedbackId),
940
- v(legacyFeedback.timestamp),
941
- "nextval('feedback_events_cursor_id_seq')",
942
- v(legacyFeedback.traceId),
943
- v(legacyFeedback.spanId ?? null),
944
- v(legacyFeedback.experimentId ?? null),
945
- v(legacyFeedback.entityType ?? null),
946
- v(legacyFeedback.entityId ?? null),
947
- v(legacyFeedback.entityName ?? null),
948
- v(legacyFeedback.entityVersionId ?? null),
949
- v(legacyFeedback.parentEntityVersionId ?? null),
950
- v(legacyFeedback.parentEntityType ?? null),
951
- v(legacyFeedback.parentEntityId ?? null),
952
- v(legacyFeedback.parentEntityName ?? null),
953
- v(legacyFeedback.rootEntityVersionId ?? null),
954
- v(legacyFeedback.rootEntityType ?? null),
955
- v(legacyFeedback.rootEntityId ?? null),
956
- v(legacyFeedback.rootEntityName ?? null),
957
- v(legacyFeedback.userId ?? null),
958
- v(legacyFeedback.organizationId ?? null),
959
- v(legacyFeedback.resourceId ?? null),
960
- v(legacyFeedback.runId ?? null),
961
- v(legacyFeedback.sessionId ?? null),
962
- v(legacyFeedback.threadId ?? null),
963
- v(legacyFeedback.requestId ?? null),
964
- v(legacyFeedback.environment ?? null),
965
- v(legacyFeedback.executionSource ?? null),
966
- v(legacyFeedback.serviceName ?? null),
967
- v(feedbackUserId),
968
- v(legacyFeedback.sourceId ?? null),
969
- v(feedbackSource),
970
- v(legacyFeedback.feedbackType),
971
- v(String(legacyFeedback.value)),
972
- v(legacyFeedback.comment ?? null),
973
- jsonV(legacyFeedback.tags ?? null),
974
- jsonV(legacyFeedback.metadata),
975
- jsonV(legacyFeedback.scope ?? null)
976
- ].join(", ")})`;
977
- });
978
- await db.execute(
979
- `INSERT INTO feedback_events (
980
- feedbackId, timestamp, cursorId, traceId, spanId, experimentId,
981
- entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
982
- userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
983
- feedbackUserId, sourceId, feedbackSource, feedbackType, value, comment, tags, metadata, scope
984
- )
985
- VALUES ${tuples.join(",\n ")}
986
- ON CONFLICT DO NOTHING`
987
- );
988
- }
989
- async function listFeedback(db, args) {
990
- const { mode, filters, pagination, orderBy, after, limit } = storage.listFeedbackArgsSchema.parse(args);
991
- const page = Number(pagination.page);
992
- const perPage = Number(pagination.perPage);
993
- const { clause: filterClause, params: filterParams } = buildWhereClause(filters, {
994
- source: "feedbackSource"
995
- });
996
- if (mode === "delta") {
997
- assertDeltaPollingEnabled();
998
- const streamHeadCursor = await getStreamHeadCursor(db);
999
- if (after === void 0) {
1000
- return {
1001
- feedback: [],
1002
- delta: { limit, hasMore: false },
1003
- deltaCursor: streamHeadCursor
1004
- };
1005
- }
1006
- const afterCursorId = validateCursorId(after);
1007
- const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
1008
- const rows2 = await db.query(
1009
- `SELECT * FROM feedback_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
1010
- [...filterParams, afterCursorId, limit + 1]
1011
- );
1012
- const visibleRows = rows2.slice(0, limit).map((row) => ({
1013
- cursorId: row.cursorId,
1014
- feedback: rowToFeedbackRecord(row)
1015
- }));
1016
- return {
1017
- feedback: visibleRows.map((row) => row.feedback),
1018
- delta: { limit, hasMore: rows2.length > limit },
1019
- deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
1020
- };
1021
- }
1022
- const orderByClause = buildOrderByClause(orderBy);
1023
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
1024
- const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor(db, filterClause, filterParams) : void 0;
1025
- const countResult = await db.query(
1026
- `SELECT COUNT(*) as total FROM feedback_events ${filterClause}`,
1027
- filterParams
1028
- );
1029
- const total = Number(countResult[0]?.total ?? 0);
1030
- const rows = await db.query(
1031
- `SELECT * FROM feedback_events ${filterClause} ${orderByClause} ${paginationClause}`,
1032
- [...filterParams, ...paginationParams]
1033
- );
1034
- return {
1035
- pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
1036
- feedback: rows.map((row) => rowToFeedbackRecord(row)),
1037
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
1038
- };
1039
- }
1040
- async function getDeltaCursor(db, filterClause, filterParams) {
1041
- const rows = await db.query(
1042
- `SELECT max(cursorId) AS cursorId FROM feedback_events ${filterClause}`,
1043
- filterParams
1044
- );
1045
- const cursorId = rows[0]?.cursorId;
1046
- if (cursorId !== null && cursorId !== void 0) {
1047
- return encodeDeltaCursor(cursorId);
1048
- }
1049
- const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM feedback_events`);
1050
- return encodeDeltaCursor(streamRows[0]?.cursorId);
1051
- }
1052
- async function getStreamHeadCursor(db) {
1053
- const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM feedback_events`);
1054
- return encodeDeltaCursor(streamRows[0]?.cursorId);
1055
- }
1056
- async function getFeedbackAggregate(db, args) {
1057
- const aggSql = getAggregationSql(args.aggregation);
1058
- const { clause, params } = buildFeedbackWhereClause(args, true);
1059
- const rows = await db.query(
1060
- `SELECT ${aggSql} AS value FROM feedback_events ${clause}`,
1061
- params
1062
- );
1063
- const value = rows[0]?.value === null || rows[0]?.value === void 0 ? null : Number(rows[0]?.value);
1064
- if (args.comparePeriod && args.filters?.timestamp) {
1065
- const previousTimestamp = getComparisonDateRange(args.comparePeriod, args.filters.timestamp);
1066
- if (previousTimestamp) {
1067
- const previousWhere = buildFeedbackWhereClause(
1068
- {
1069
- ...args,
1070
- filters: { ...args.filters ?? {}, timestamp: previousTimestamp }
1071
- },
1072
- true
1073
- );
1074
- const prevRows = await db.query(
1075
- `SELECT ${aggSql} AS value FROM feedback_events ${previousWhere.clause}`,
1076
- previousWhere.params
1077
- );
1078
- const previousValue = prevRows[0]?.value === null || prevRows[0]?.value === void 0 ? null : Number(prevRows[0]?.value);
1079
- let changePercent = null;
1080
- if (previousValue !== null && previousValue !== 0 && value !== null) {
1081
- changePercent = (value - previousValue) / Math.abs(previousValue) * 100;
1082
- }
1083
- return { value, previousValue, changePercent };
1084
- }
1085
- }
1086
- return { value };
1087
- }
1088
- async function getFeedbackBreakdown(db, args) {
1089
- const aggSql = getAggregationSql(args.aggregation);
1090
- const { clause, params } = buildFeedbackWhereClause(args, true);
1091
- const resolvedGroupBy = resolveFeedbackGroupBy(args.groupBy);
1092
- const sql = `SELECT ${resolvedGroupBy.map((entry) => entry.selectSql).join(", ")}, ${aggSql} AS value FROM feedback_events ${clause} GROUP BY ${resolvedGroupBy.map((entry) => entry.groupSql).join(", ")} ORDER BY value DESC`;
1093
- const rows = await db.query(sql, params);
1094
- return {
1095
- groups: rows.map((row) => ({
1096
- dimensions: Object.fromEntries(
1097
- resolvedGroupBy.map((entry, index) => {
1098
- const value = row[`group_by_${index}`];
1099
- return [entry.key, value === null || value === void 0 ? null : String(value)];
1100
- })
1101
- ),
1102
- value: Number(row.value ?? 0)
1103
- }))
1104
- };
1105
- }
1106
- async function getFeedbackTimeSeries(db, args) {
1107
- const aggSql = getAggregationSql(args.aggregation);
1108
- const intervalSql = getIntervalSql(args.interval);
1109
- const { clause, params } = buildFeedbackWhereClause(args, true);
1110
- if (args.groupBy && args.groupBy.length > 0) {
1111
- const resolvedGroupBy = resolveFeedbackGroupBy(args.groupBy);
1112
- const sql = `
1113
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
1114
- ${resolvedGroupBy.map((entry) => entry.selectSql).join(", ")},
1115
- ${aggSql} AS value
1116
- FROM feedback_events ${clause}
1117
- GROUP BY bucket, ${resolvedGroupBy.map((entry) => entry.groupSql).join(", ")}
1118
- ORDER BY bucket
1119
- `;
1120
- const rows2 = await db.query(sql, params);
1121
- const seriesMap = /* @__PURE__ */ new Map();
1122
- for (const row of rows2) {
1123
- const groupValues = resolvedGroupBy.map((_, index) => row[`group_by_${index}`]);
1124
- const key = JSON.stringify(groupValues);
1125
- if (!seriesMap.has(key)) {
1126
- seriesMap.set(key, { name: toSeriesName(groupValues), points: [] });
1127
- }
1128
- seriesMap.get(key).points.push({
1129
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
1130
- value: Number(row.value ?? 0)
1131
- });
1132
- }
1133
- return { series: Array.from(seriesMap.values()) };
1134
- }
1135
- const rows = await db.query(
1136
- `
1137
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
1138
- ${aggSql} AS value
1139
- FROM feedback_events ${clause}
1140
- GROUP BY bucket
1141
- ORDER BY bucket
1142
- `,
1143
- params
1144
- );
1145
- return {
1146
- series: [
1147
- {
1148
- name: args.feedbackSource ? `${args.feedbackType}|${args.feedbackSource}` : args.feedbackType,
1149
- points: rows.map((row) => ({
1150
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
1151
- value: Number(row.value ?? 0)
1152
- }))
1153
- }
1154
- ]
1155
- };
1156
- }
1157
- async function getFeedbackPercentiles(db, args) {
1158
- const intervalSql = getIntervalSql(args.interval);
1159
- const { clause, params } = buildFeedbackWhereClause(args, true);
1160
- const percentiles = getValidatedPercentiles(args.percentiles);
1161
- const series = [];
1162
- for (const percentile of percentiles) {
1163
- const rows = await db.query(
1164
- `
1165
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
1166
- percentile_cont(${percentile}) WITHIN GROUP (ORDER BY TRY_CAST(value AS DOUBLE)) AS pvalue
1167
- FROM feedback_events ${clause}
1168
- GROUP BY bucket
1169
- ORDER BY bucket
1170
- `,
1171
- params
1172
- );
1173
- series.push({
1174
- percentile,
1175
- points: rows.map((row) => ({
1176
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
1177
- value: Number(row.pvalue ?? 0)
1178
- }))
1179
- });
1180
- }
1181
- return { series };
1182
- }
1183
- var COLUMNS = [
1184
- "logId",
1185
- "timestamp",
1186
- "cursorId",
1187
- "level",
1188
- "message",
1189
- "data",
1190
- "traceId",
1191
- "spanId",
1192
- "entityType",
1193
- "entityId",
1194
- "entityName",
1195
- "entityVersionId",
1196
- "parentEntityVersionId",
1197
- "parentEntityType",
1198
- "parentEntityId",
1199
- "parentEntityName",
1200
- "rootEntityVersionId",
1201
- "rootEntityType",
1202
- "rootEntityId",
1203
- "rootEntityName",
1204
- "userId",
1205
- "organizationId",
1206
- "resourceId",
1207
- "runId",
1208
- "sessionId",
1209
- "threadId",
1210
- "requestId",
1211
- "environment",
1212
- "executionSource",
1213
- "serviceName",
1214
- "experimentId",
1215
- "tags",
1216
- "metadata",
1217
- "scope"
1218
- ];
1219
- var COLUMNS_SQL = COLUMNS.join(", ");
1220
- function rowToLogRecord(row) {
1221
- return {
1222
- logId: row.logId,
1223
- timestamp: toDate(row.timestamp),
1224
- level: row.level,
1225
- message: row.message,
1226
- data: parseJson(row.data),
1227
- traceId: row.traceId ?? null,
1228
- spanId: row.spanId ?? null,
1229
- entityType: row.entityType ?? null,
1230
- entityId: row.entityId ?? null,
1231
- entityName: row.entityName ?? null,
1232
- entityVersionId: row.entityVersionId ?? null,
1233
- parentEntityVersionId: row.parentEntityVersionId ?? null,
1234
- parentEntityType: row.parentEntityType ?? null,
1235
- parentEntityId: row.parentEntityId ?? null,
1236
- parentEntityName: row.parentEntityName ?? null,
1237
- rootEntityVersionId: row.rootEntityVersionId ?? null,
1238
- rootEntityType: row.rootEntityType ?? null,
1239
- rootEntityId: row.rootEntityId ?? null,
1240
- rootEntityName: row.rootEntityName ?? null,
1241
- userId: row.userId ?? null,
1242
- organizationId: row.organizationId ?? null,
1243
- resourceId: row.resourceId ?? null,
1244
- runId: row.runId ?? null,
1245
- sessionId: row.sessionId ?? null,
1246
- threadId: row.threadId ?? null,
1247
- requestId: row.requestId ?? null,
1248
- environment: row.environment ?? null,
1249
- executionSource: row.executionSource ?? null,
1250
- serviceName: row.serviceName ?? null,
1251
- experimentId: row.experimentId ?? null,
1252
- tags: parseJsonArray(row.tags),
1253
- metadata: parseJson(row.metadata),
1254
- scope: parseJson(row.scope)
1255
- };
1256
- }
1257
- async function batchCreateLogs(db, args) {
1258
- if (args.logs.length === 0) return;
1259
- const tuples = args.logs.map((log) => {
1260
- return `(${[
1261
- v(log.logId),
1262
- v(log.timestamp),
1263
- "nextval('log_events_cursor_id_seq')",
1264
- v(log.level),
1265
- v(log.message),
1266
- jsonV(log.data),
1267
- v(log.traceId ?? null),
1268
- v(log.spanId ?? null),
1269
- v(log.entityType ?? null),
1270
- v(log.entityId ?? null),
1271
- v(log.entityName ?? null),
1272
- v(log.entityVersionId ?? null),
1273
- v(log.parentEntityVersionId ?? null),
1274
- v(log.parentEntityType ?? null),
1275
- v(log.parentEntityId ?? null),
1276
- v(log.parentEntityName ?? null),
1277
- v(log.rootEntityVersionId ?? null),
1278
- v(log.rootEntityType ?? null),
1279
- v(log.rootEntityId ?? null),
1280
- v(log.rootEntityName ?? null),
1281
- v(log.userId ?? null),
1282
- v(log.organizationId ?? null),
1283
- v(log.resourceId ?? null),
1284
- v(log.runId ?? null),
1285
- v(log.sessionId ?? null),
1286
- v(log.threadId ?? null),
1287
- v(log.requestId ?? null),
1288
- v(log.environment ?? null),
1289
- v(log.executionSource ?? null),
1290
- v(log.serviceName ?? null),
1291
- v(log.experimentId ?? null),
1292
- jsonV(log.tags),
1293
- jsonV(log.metadata),
1294
- jsonV(log.scope)
1295
- ].join(", ")})`;
1296
- });
1297
- await db.execute(`INSERT INTO log_events (${COLUMNS_SQL}) VALUES ${tuples.join(",\n")} ON CONFLICT DO NOTHING`);
1298
- }
1299
- async function listLogs(db, args) {
1300
- const { mode, filters, pagination, orderBy, after, limit } = storage.listLogsArgsSchema.parse(args);
1301
- const filterRecord = filters;
1302
- const page = Number(pagination.page);
1303
- const perPage = Number(pagination.perPage);
1304
- const { clause: filterClause, params: filterParams } = buildWhereClause(filterRecord);
1305
- if (mode === "delta") {
1306
- assertDeltaPollingEnabled();
1307
- const streamHeadCursor = await getStreamHeadCursor2(db);
1308
- if (after === void 0) {
1309
- return {
1310
- logs: [],
1311
- delta: { limit, hasMore: false },
1312
- deltaCursor: streamHeadCursor
1313
- };
1314
- }
1315
- const afterCursorId = validateCursorId(after);
1316
- const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
1317
- const rows2 = await db.query(
1318
- `SELECT * FROM log_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
1319
- [...filterParams, afterCursorId, limit + 1]
1320
- );
1321
- const visibleRows = rows2.slice(0, limit).map((row) => ({
1322
- cursorId: row.cursorId,
1323
- log: rowToLogRecord(row)
1324
- }));
1325
- return {
1326
- logs: visibleRows.map((row) => row.log),
1327
- delta: { limit, hasMore: rows2.length > limit },
1328
- deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
1329
- };
1330
- }
1331
- const orderByClause = buildOrderByClause(orderBy);
1332
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
1333
- const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor2(db, filterClause, filterParams) : void 0;
1334
- const countResult = await db.query(
1335
- `SELECT COUNT(*) as total FROM log_events ${filterClause}`,
1336
- filterParams
1337
- );
1338
- const total = Number(countResult[0]?.total ?? 0);
1339
- const rows = await db.query(`SELECT * FROM log_events ${filterClause} ${orderByClause} ${paginationClause}`, [
1340
- ...filterParams,
1341
- ...paginationParams
1342
- ]);
1343
- const logs = rows.map((row) => rowToLogRecord(row));
1344
- return {
1345
- pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
1346
- logs,
1347
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
1348
- };
1349
- }
1350
- async function getDeltaCursor2(db, filterClause, filterParams) {
1351
- const rows = await db.query(
1352
- `SELECT max(cursorId) AS cursorId FROM log_events ${filterClause}`,
1353
- filterParams
1354
- );
1355
- const cursorId = rows[0]?.cursorId;
1356
- if (cursorId !== null && cursorId !== void 0) {
1357
- return encodeDeltaCursor(cursorId);
1358
- }
1359
- const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM log_events`);
1360
- return encodeDeltaCursor(streamRows[0]?.cursorId);
1361
- }
1362
- async function getStreamHeadCursor2(db) {
1363
- const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM log_events`);
1364
- return encodeDeltaCursor(streamRows[0]?.cursorId);
1365
- }
1366
- function resolveDistinctColumnSql(distinctColumn) {
1367
- if (!distinctColumn) {
1368
- throw new Error(`count_distinct aggregation requires a 'distinctColumn' argument`);
1369
- }
1370
- if (!storage.METRIC_DISTINCT_COLUMNS.includes(distinctColumn)) {
1371
- throw new Error(`Invalid distinctColumn: ${distinctColumn}`);
1372
- }
1373
- return utils.parseFieldKey(distinctColumn);
1374
- }
1375
- function getAggregationSql2(aggregation, measure = "value", distinctColumn) {
1376
- switch (aggregation) {
1377
- case "sum":
1378
- return `SUM(${measure})`;
1379
- case "avg":
1380
- return `AVG(${measure})`;
1381
- case "min":
1382
- return `MIN(${measure})`;
1383
- case "max":
1384
- return `MAX(${measure})`;
1385
- case "count":
1386
- return `CAST(COUNT(${measure}) AS DOUBLE)`;
1387
- case "count_distinct": {
1388
- return `CAST(approx_count_distinct(${resolveDistinctColumnSql(distinctColumn)}) AS DOUBLE)`;
1389
- }
1390
- case "last":
1391
- return `arg_max(${measure}, timestamp)`;
1392
- default:
1393
- return `SUM(${measure})`;
1394
- }
1395
- }
1396
- function getIntervalSql2(interval) {
1397
- switch (interval) {
1398
- case "1m":
1399
- return "1 minute";
1400
- case "5m":
1401
- return "5 minutes";
1402
- case "15m":
1403
- return "15 minutes";
1404
- case "1h":
1405
- return "1 hour";
1406
- case "1d":
1407
- return "1 day";
1408
- default:
1409
- return "1 hour";
1410
- }
1411
- }
1412
- function buildMetricNameFilter(name) {
1413
- if (Array.isArray(name)) {
1414
- const placeholders = name.map(() => "?").join(", ");
1415
- return { clause: `name IN (${placeholders})`, params: name };
1416
- }
1417
- return { clause: `name = ?`, params: [name] };
1418
- }
1419
- var METRIC_COLUMNS = [
1420
- "metricId",
1421
- "timestamp",
1422
- "cursorId",
1423
- "name",
1424
- "value",
1425
- "traceId",
1426
- "spanId",
1427
- "entityType",
1428
- "entityId",
1429
- "entityName",
1430
- "entityVersionId",
1431
- "parentEntityVersionId",
1432
- "parentEntityType",
1433
- "parentEntityId",
1434
- "parentEntityName",
1435
- "rootEntityVersionId",
1436
- "rootEntityType",
1437
- "rootEntityId",
1438
- "rootEntityName",
1439
- "userId",
1440
- "organizationId",
1441
- "resourceId",
1442
- "runId",
1443
- "sessionId",
1444
- "threadId",
1445
- "requestId",
1446
- "environment",
1447
- "executionSource",
1448
- "serviceName",
1449
- "experimentId",
1450
- "provider",
1451
- "model",
1452
- "estimatedCost",
1453
- "costUnit",
1454
- "tags",
1455
- "labels",
1456
- "costMetadata",
1457
- "metadata",
1458
- "scope"
1459
- ];
1460
- var METRIC_COLUMNS_SQL = METRIC_COLUMNS.join(", ");
1461
- var METRIC_COLUMN_SET = new Set(METRIC_COLUMNS);
1462
- var METRIC_LABEL_ONLY_GROUP_BY_EXCLUDED = /* @__PURE__ */ new Set(["metadata", "scope", "costMetadata", "tags"]);
1463
- function buildGroupByAlias(index) {
1464
- return `group_by_${index}`;
1465
- }
1466
- function toSeriesDisplayValue(value) {
1467
- return value === null || value === void 0 ? "" : String(value);
1468
- }
1469
- function getCostSummarySelect(prefix = "") {
1470
- const ref = (column) => `${prefix}${column}`;
1471
- return [
1472
- `SUM(${ref("estimatedCost")}) FILTER (WHERE ${ref("estimatedCost")} IS NOT NULL) AS estimatedCost`,
1473
- `,`,
1474
- `CASE`,
1475
- ` WHEN COUNT(DISTINCT ${ref("costUnit")}) FILTER (WHERE ${ref("costUnit")} IS NOT NULL) = 1`,
1476
- ` THEN MIN(${ref("costUnit")}) FILTER (WHERE ${ref("costUnit")} IS NOT NULL)`,
1477
- ` ELSE NULL`,
1478
- `END AS costUnit`
1479
- ].join(" ");
1480
- }
1481
- function normalizeCostSummaryRow(row) {
1482
- return {
1483
- estimatedCost: row.estimatedCost === null || row.estimatedCost === void 0 ? null : Number(row.estimatedCost),
1484
- costUnit: row.costUnit === null || row.costUnit === void 0 ? null : String(row.costUnit)
1485
- };
1486
- }
1487
- function buildCombinedWhereClause(nameClause, nameParams, filterClause, filterParams) {
1488
- const conditions = [nameClause];
1489
- const params = [...nameParams];
1490
- if (filterClause) {
1491
- conditions.push(filterClause.replace("WHERE ", ""));
1492
- params.push(...filterParams);
1493
- }
1494
- return { clause: `WHERE ${conditions.join(" AND ")}`, params };
1495
- }
1496
- function resolveGroupBy(groupBy) {
1497
- return groupBy.map((key, index) => {
1498
- if (METRIC_COLUMN_SET.has(key)) {
1499
- const parsed = utils.parseFieldKey(key);
1500
- if (METRIC_LABEL_ONLY_GROUP_BY_EXCLUDED.has(parsed)) {
1501
- throw new Error(`Invalid groupBy column(s): ${key}`);
1502
- }
1503
- return {
1504
- kind: "column",
1505
- key,
1506
- selectSql: `${parsed} AS "${key}"`,
1507
- groupSql: parsed,
1508
- resultKey: key
1509
- };
1510
- }
1511
- const labelPath = buildJsonPath(key).replace(/'/g, "''");
1512
- const labelExpr = `json_extract_string(labels, '${labelPath}')`;
1513
- const alias = buildGroupByAlias(index);
1514
- return {
1515
- kind: "label",
1516
- key,
1517
- selectSql: `${labelExpr} AS ${alias}`,
1518
- groupSql: alias,
1519
- resultKey: alias
1520
- };
1521
- });
1522
- }
1523
- function rowToMetricRecord(row) {
1524
- return {
1525
- metricId: row.metricId,
1526
- timestamp: toDate(row.timestamp),
1527
- name: row.name,
1528
- value: Number(row.value),
1529
- traceId: row.traceId ?? null,
1530
- spanId: row.spanId ?? null,
1531
- entityType: row.entityType ?? null,
1532
- entityId: row.entityId ?? null,
1533
- entityName: row.entityName ?? null,
1534
- entityVersionId: row.entityVersionId ?? null,
1535
- parentEntityVersionId: row.parentEntityVersionId ?? null,
1536
- parentEntityType: row.parentEntityType ?? null,
1537
- parentEntityId: row.parentEntityId ?? null,
1538
- parentEntityName: row.parentEntityName ?? null,
1539
- rootEntityVersionId: row.rootEntityVersionId ?? null,
1540
- rootEntityType: row.rootEntityType ?? null,
1541
- rootEntityId: row.rootEntityId ?? null,
1542
- rootEntityName: row.rootEntityName ?? null,
1543
- userId: row.userId ?? null,
1544
- organizationId: row.organizationId ?? null,
1545
- resourceId: row.resourceId ?? null,
1546
- runId: row.runId ?? null,
1547
- sessionId: row.sessionId ?? null,
1548
- threadId: row.threadId ?? null,
1549
- requestId: row.requestId ?? null,
1550
- environment: row.environment ?? null,
1551
- executionSource: row.executionSource ?? null,
1552
- serviceName: row.serviceName ?? null,
1553
- experimentId: row.experimentId ?? null,
1554
- provider: row.provider ?? null,
1555
- model: row.model ?? null,
1556
- estimatedCost: row.estimatedCost === null || row.estimatedCost === void 0 ? null : Number(row.estimatedCost),
1557
- costUnit: row.costUnit ?? null,
1558
- costMetadata: parseJson(row.costMetadata),
1559
- tags: parseJsonArray(row.tags),
1560
- labels: parseJson(row.labels) ?? {},
1561
- metadata: parseJson(row.metadata),
1562
- scope: parseJson(row.scope)
1563
- };
1564
- }
1565
- async function batchCreateMetrics(db, args) {
1566
- if (args.metrics.length === 0) return;
1567
- const tuples = args.metrics.map((m) => {
1568
- return `(${[
1569
- v(m.metricId),
1570
- v(m.timestamp),
1571
- "nextval('metric_events_cursor_id_seq')",
1572
- v(m.name),
1573
- v(m.value),
1574
- v(m.traceId ?? null),
1575
- v(m.spanId ?? null),
1576
- v(m.entityType ?? null),
1577
- v(m.entityId ?? null),
1578
- v(m.entityName ?? null),
1579
- v(m.entityVersionId ?? null),
1580
- v(m.parentEntityVersionId ?? null),
1581
- v(m.parentEntityType ?? null),
1582
- v(m.parentEntityId ?? null),
1583
- v(m.parentEntityName ?? null),
1584
- v(m.rootEntityVersionId ?? null),
1585
- v(m.rootEntityType ?? null),
1586
- v(m.rootEntityId ?? null),
1587
- v(m.rootEntityName ?? null),
1588
- v(m.userId ?? null),
1589
- v(m.organizationId ?? null),
1590
- v(m.resourceId ?? null),
1591
- v(m.runId ?? null),
1592
- v(m.sessionId ?? null),
1593
- v(m.threadId ?? null),
1594
- v(m.requestId ?? null),
1595
- v(m.environment ?? null),
1596
- v(m.executionSource ?? null),
1597
- v(m.serviceName ?? null),
1598
- v(m.experimentId ?? null),
1599
- v(m.provider ?? null),
1600
- v(m.model ?? null),
1601
- v(m.estimatedCost ?? null),
1602
- v(m.costUnit ?? null),
1603
- jsonV(m.tags ?? null),
1604
- v(JSON.stringify(m.labels ?? {})),
1605
- jsonV(m.costMetadata ?? null),
1606
- jsonV(m.metadata ?? null),
1607
- jsonV(m.scope ?? null)
1608
- ].join(", ")})`;
1609
- });
1610
- await db.execute(
1611
- `INSERT INTO metric_events (${METRIC_COLUMNS_SQL}) VALUES ${tuples.join(",\n")} ON CONFLICT DO NOTHING`
1612
- );
1613
- }
1614
- async function listMetrics(db, args) {
1615
- const { mode, filters, pagination, orderBy, after, limit } = storage.listMetricsArgsSchema.parse(args);
1616
- const filterRecord = filters;
1617
- const page = Number(pagination.page);
1618
- const perPage = Number(pagination.perPage);
1619
- const { clause: filterClause, params: filterParams } = buildWhereClause(filterRecord);
1620
- if (mode === "delta") {
1621
- assertDeltaPollingEnabled();
1622
- const streamHeadCursor = await getStreamHeadCursor3(db);
1623
- if (after === void 0) {
1624
- return {
1625
- metrics: [],
1626
- delta: { limit, hasMore: false },
1627
- deltaCursor: streamHeadCursor
1628
- };
1629
- }
1630
- const afterCursorId = validateCursorId(after);
1631
- const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
1632
- const rows2 = await db.query(
1633
- `SELECT * FROM metric_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
1634
- [...filterParams, afterCursorId, limit + 1]
1635
- );
1636
- const visibleRows = rows2.slice(0, limit).map((row) => ({
1637
- cursorId: row.cursorId,
1638
- metric: rowToMetricRecord(row)
1639
- }));
1640
- return {
1641
- metrics: visibleRows.map((row) => row.metric),
1642
- delta: { limit, hasMore: rows2.length > limit },
1643
- deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
1644
- };
1645
- }
1646
- const orderByClause = buildOrderByClause(orderBy);
1647
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
1648
- const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor3(db, filterClause, filterParams) : void 0;
1649
- const countResult = await db.query(
1650
- `SELECT COUNT(*) AS total FROM metric_events ${filterClause}`,
1651
- filterParams
1652
- );
1653
- const total = Number(countResult[0]?.total ?? 0);
1654
- const rows = await db.query(
1655
- `SELECT * FROM metric_events ${filterClause} ${orderByClause} ${paginationClause}`,
1656
- [...filterParams, ...paginationParams]
1657
- );
1658
- return {
1659
- pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
1660
- metrics: rows.map((row) => rowToMetricRecord(row)),
1661
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
1662
- };
1663
- }
1664
- async function getDeltaCursor3(db, filterClause, filterParams) {
1665
- const rows = await db.query(
1666
- `SELECT max(cursorId) AS cursorId FROM metric_events ${filterClause}`,
1667
- filterParams
1668
- );
1669
- const cursorId = rows[0]?.cursorId;
1670
- if (cursorId !== null && cursorId !== void 0) {
1671
- return encodeDeltaCursor(cursorId);
1672
- }
1673
- const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM metric_events`);
1674
- return encodeDeltaCursor(streamRows[0]?.cursorId);
1675
- }
1676
- async function getStreamHeadCursor3(db) {
1677
- const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM metric_events`);
1678
- return encodeDeltaCursor(streamRows[0]?.cursorId);
1679
- }
1680
- async function getMetricAggregate(db, args) {
1681
- const aggSql = getAggregationSql2(args.aggregation, "value", args.distinctColumn);
1682
- const { clause: nameClause, params: nameParams } = buildMetricNameFilter(args.name);
1683
- const { clause: filterClause, params: filterParams } = buildWhereClause(
1684
- args.filters
1685
- );
1686
- const { clause: whereClause, params: allParams } = buildCombinedWhereClause(
1687
- nameClause,
1688
- nameParams,
1689
- filterClause,
1690
- filterParams
1691
- );
1692
- const sql = `SELECT ${aggSql} AS value, ${getCostSummarySelect()} FROM metric_events ${whereClause}`;
1693
- const result = await db.query(sql, allParams);
1694
- const row = result[0] ?? {};
1695
- const value = row.value === null || row.value === void 0 ? null : Number(row.value);
1696
- const costSummary = normalizeCostSummaryRow(row);
1697
- if (args.comparePeriod && args.filters?.timestamp) {
1698
- const ts = args.filters.timestamp;
1699
- if (ts.start && ts.end) {
1700
- const duration = ts.end.getTime() - ts.start.getTime();
1701
- let prevStart;
1702
- let prevEnd;
1703
- switch (args.comparePeriod) {
1704
- case "previous_period":
1705
- prevStart = new Date(ts.start.getTime() - duration);
1706
- prevEnd = new Date(ts.end.getTime() - duration);
1707
- break;
1708
- case "previous_day":
1709
- prevStart = new Date(ts.start.getTime() - 864e5);
1710
- prevEnd = new Date(ts.end.getTime() - 864e5);
1711
- break;
1712
- case "previous_week":
1713
- prevStart = new Date(ts.start.getTime() - 6048e5);
1714
- prevEnd = new Date(ts.end.getTime() - 6048e5);
1715
- break;
1716
- default:
1717
- prevStart = new Date(ts.start.getTime() - duration);
1718
- prevEnd = new Date(ts.end.getTime() - duration);
1719
- }
1720
- const prevFilters = {
1721
- ...args.filters ?? {},
1722
- timestamp: {
1723
- start: prevStart,
1724
- end: prevEnd,
1725
- startExclusive: ts.startExclusive,
1726
- endExclusive: ts.endExclusive
1727
- }
1728
- };
1729
- const { clause: prevFilterClause, params: prevFilterParams } = buildWhereClause(
1730
- prevFilters
1731
- );
1732
- const { clause: prevWhereClause, params: prevParams } = buildCombinedWhereClause(
1733
- nameClause,
1734
- nameParams,
1735
- prevFilterClause,
1736
- prevFilterParams
1737
- );
1738
- const prevSql = `SELECT ${aggSql} AS value, ${getCostSummarySelect()} FROM metric_events ${prevWhereClause}`;
1739
- const prevResult = await db.query(prevSql, prevParams);
1740
- const prevRow = prevResult[0] ?? {};
1741
- const previousValue = prevRow.value === null || prevRow.value === void 0 ? null : Number(prevRow.value);
1742
- const previousCostSummary = normalizeCostSummaryRow(prevRow);
1743
- let changePercent = null;
1744
- if (previousValue !== null && previousValue !== 0 && value !== null) {
1745
- changePercent = (value - previousValue) / Math.abs(previousValue) * 100;
1746
- }
1747
- let costChangePercent = null;
1748
- if (previousCostSummary.estimatedCost !== null && previousCostSummary.estimatedCost !== 0 && costSummary.estimatedCost !== null) {
1749
- costChangePercent = (costSummary.estimatedCost - previousCostSummary.estimatedCost) / Math.abs(previousCostSummary.estimatedCost) * 100;
1750
- }
1751
- return {
1752
- value,
1753
- estimatedCost: costSummary.estimatedCost,
1754
- costUnit: costSummary.costUnit,
1755
- previousValue,
1756
- previousEstimatedCost: previousCostSummary.estimatedCost,
1757
- changePercent,
1758
- costChangePercent
1759
- };
1760
- }
1761
- }
1762
- return { value, estimatedCost: costSummary.estimatedCost, costUnit: costSummary.costUnit };
1763
- }
1764
- async function getMetricBreakdown(db, args) {
1765
- const aggSql = getAggregationSql2(args.aggregation, "value", args.distinctColumn);
1766
- const { clause: nameClause, params: nameParams } = buildMetricNameFilter(args.name);
1767
- const { clause: filterClause, params: filterParams } = buildWhereClause(
1768
- args.filters
1769
- );
1770
- const { clause: whereClause, params: allParams } = buildCombinedWhereClause(
1771
- nameClause,
1772
- nameParams,
1773
- filterClause,
1774
- filterParams
1775
- );
1776
- const resolvedGroupBy = resolveGroupBy(args.groupBy);
1777
- const selectGroupBy = resolvedGroupBy.map((entry) => entry.selectSql).join(", ");
1778
- const groupByCols = resolvedGroupBy.map((entry) => entry.groupSql).join(", ");
1779
- const orderDirection = args.orderDirection === "ASC" ? "ASC" : "DESC";
1780
- const limitClause = typeof args.limit === "number" ? `LIMIT ?` : "";
1781
- const limitParams = typeof args.limit === "number" ? [args.limit] : [];
1782
- const sql = `SELECT ${selectGroupBy}, ${aggSql} AS value, ${getCostSummarySelect()} FROM metric_events ${whereClause} GROUP BY ${groupByCols} ORDER BY value ${orderDirection} ${limitClause}`;
1783
- const rows = await db.query(sql, [...allParams, ...limitParams]);
1784
- const groups = rows.map((row) => {
1785
- const dimensions = {};
1786
- for (const entry of resolvedGroupBy) {
1787
- const value = row[entry.resultKey];
1788
- dimensions[entry.key] = value === null || value === void 0 ? null : String(value);
1789
- }
1790
- const costSummary = normalizeCostSummaryRow(row);
1791
- return {
1792
- dimensions,
1793
- value: Number(row.value ?? 0),
1794
- estimatedCost: costSummary.estimatedCost,
1795
- costUnit: costSummary.costUnit
1796
- };
1797
- });
1798
- return { groups };
1799
- }
1800
- async function getMetricTimeSeries(db, args) {
1801
- const aggSql = getAggregationSql2(args.aggregation, "value", args.distinctColumn);
1802
- const intervalSql = getIntervalSql2(args.interval);
1803
- const { clause: nameClause, params: nameParams } = buildMetricNameFilter(args.name);
1804
- const { clause: filterClause, params: filterParams } = buildWhereClause(
1805
- args.filters
1806
- );
1807
- const { clause: whereClause, params: allParams } = buildCombinedWhereClause(
1808
- nameClause,
1809
- nameParams,
1810
- filterClause,
1811
- filterParams
1812
- );
1813
- if (args.groupBy && args.groupBy.length > 0) {
1814
- const resolvedGroupBy = resolveGroupBy(args.groupBy);
1815
- const selectGroupBy = resolvedGroupBy.map((entry) => entry.selectSql).join(", ");
1816
- const groupByCols = resolvedGroupBy.map((entry) => entry.groupSql).join(", ");
1817
- const sql2 = `
1818
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
1819
- ${selectGroupBy},
1820
- ${aggSql} AS value,
1821
- ${getCostSummarySelect()}
1822
- FROM metric_events ${whereClause}
1823
- GROUP BY bucket, ${groupByCols}
1824
- ORDER BY bucket
1825
- `;
1826
- const rows2 = await db.query(sql2, allParams);
1827
- const seriesMap = /* @__PURE__ */ new Map();
1828
- for (const row of rows2) {
1829
- const dimensionValues = resolvedGroupBy.map((entry) => row[entry.resultKey]);
1830
- const seriesKey = JSON.stringify(dimensionValues);
1831
- const name = dimensionValues.map(toSeriesDisplayValue).join("|");
1832
- const costSummary = normalizeCostSummaryRow(row);
1833
- if (!seriesMap.has(seriesKey)) {
1834
- seriesMap.set(seriesKey, {
1835
- name,
1836
- costUnits: /* @__PURE__ */ new Set(),
1837
- points: []
1838
- });
1839
- }
1840
- if (costSummary.costUnit) {
1841
- seriesMap.get(seriesKey).costUnits.add(costSummary.costUnit);
1842
- }
1843
- seriesMap.get(seriesKey).points.push({
1844
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
1845
- value: Number(row.value ?? 0),
1846
- estimatedCost: costSummary.estimatedCost
1847
- });
1848
- }
1849
- return {
1850
- series: Array.from(seriesMap.values()).map((series) => ({
1851
- name: series.name,
1852
- costUnit: series.costUnits.size === 1 ? Array.from(series.costUnits)[0] : null,
1853
- points: series.points
1854
- }))
1855
- };
1856
- }
1857
- const sql = `
1858
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
1859
- ${aggSql} AS value,
1860
- ${getCostSummarySelect()}
1861
- FROM metric_events ${whereClause}
1862
- GROUP BY bucket
1863
- ORDER BY bucket
1864
- `;
1865
- const rows = await db.query(sql, allParams);
1866
- const metricName = Array.isArray(args.name) ? args.name.join(",") : args.name;
1867
- const overallCostUnits = new Set(
1868
- rows.map((row) => row.costUnit).filter((value) => typeof value === "string")
1869
- );
1870
- return {
1871
- series: [
1872
- {
1873
- name: metricName,
1874
- costUnit: overallCostUnits.size === 1 ? Array.from(overallCostUnits)[0] : null,
1875
- points: rows.map((row) => {
1876
- const costSummary = normalizeCostSummaryRow(row);
1877
- return {
1878
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
1879
- value: Number(row.value ?? 0),
1880
- estimatedCost: costSummary.estimatedCost
1881
- };
1882
- })
1883
- }
1884
- ]
1885
- };
1886
- }
1887
- async function getMetricPercentiles(db, args) {
1888
- const intervalSql = getIntervalSql2(args.interval);
1889
- const { clause: filterClause, params: filterParams } = buildWhereClause(
1890
- args.filters
1891
- );
1892
- const allConditions = [`name = ?`];
1893
- const allParams = [args.name];
1894
- if (filterClause) {
1895
- allConditions.push(filterClause.replace("WHERE ", ""));
1896
- allParams.push(...filterParams);
1897
- }
1898
- const whereClause = `WHERE ${allConditions.join(" AND ")}`;
1899
- const series = [];
1900
- for (const p of args.percentiles) {
1901
- const sql = `
1902
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
1903
- percentile_cont(${p}) WITHIN GROUP (ORDER BY value) AS pvalue
1904
- FROM metric_events ${whereClause}
1905
- GROUP BY bucket
1906
- ORDER BY bucket
1907
- `;
1908
- const rows = await db.query(sql, allParams);
1909
- series.push({
1910
- percentile: p,
1911
- points: rows.map((row) => ({
1912
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
1913
- value: Number(row.pvalue ?? 0)
1914
- }))
1915
- });
1916
- }
1917
- return { series };
1918
- }
1919
- async function getMetricNames(db, args) {
1920
- const conditions = [];
1921
- const params = [];
1922
- if (args.prefix) {
1923
- conditions.push(`name LIKE ?`);
1924
- params.push(`${args.prefix}%`);
1925
- }
1926
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1927
- const limitClause = args.limit ? `LIMIT ?` : "";
1928
- if (args.limit) params.push(args.limit);
1929
- const rows = await db.query(
1930
- `SELECT DISTINCT name FROM metric_events ${whereClause} ORDER BY name ${limitClause}`,
1931
- params
1932
- );
1933
- return { names: rows.map((r) => r.name) };
1934
- }
1935
- async function getMetricLabelKeys(db, args) {
1936
- const rows = await db.query(
1937
- `SELECT DISTINCT unnest(json_keys(labels)) AS key FROM metric_events WHERE name = ? AND labels IS NOT NULL`,
1938
- [args.metricName]
1939
- );
1940
- return { keys: rows.map((r) => r.key) };
1941
- }
1942
- async function getMetricLabelValues(db, args) {
1943
- const labelPath = buildJsonPath(args.labelKey);
1944
- const conditions = [`name = ?`, `json_extract_string(labels, ?) IS NOT NULL`];
1945
- const params = [args.metricName, labelPath];
1946
- if (args.prefix) {
1947
- conditions.push(`json_extract_string(labels, ?) LIKE ?`);
1948
- params.push(labelPath, `${args.prefix}%`);
1949
- }
1950
- const limitClause = args.limit ? `LIMIT ?` : "";
1951
- if (args.limit) params.push(args.limit);
1952
- const rows = await db.query(
1953
- `SELECT DISTINCT json_extract_string(labels, ?) AS val FROM metric_events WHERE ${conditions.join(" AND ")} ORDER BY val ${limitClause}`,
1954
- [labelPath, ...params]
1955
- );
1956
- return { values: rows.map((r) => r.val) };
1957
- }
1958
- var CURSOR_ID_TABLES = ["span_events", "metric_events", "log_events", "score_events", "feedback_events"];
1959
- async function dropLegacyCursorIdDefaults(db) {
1960
- const rows = await db.query(
1961
- `SELECT table_name FROM information_schema.columns
1962
- WHERE column_name = 'cursorId'
1963
- AND column_default IS NOT NULL
1964
- AND table_name IN (${CURSOR_ID_TABLES.map((t) => `'${t}'`).join(", ")})`
1965
- );
1966
- if (rows.length === 0) return;
1967
- await db.executeBatch(rows.map((row) => `ALTER TABLE ${row.table_name} ALTER COLUMN cursorId DROP DEFAULT`));
1968
- }
1969
- var SIGNAL_MIGRATIONS = [
1970
- {
1971
- table: "metric_events",
1972
- createDDL: METRIC_EVENTS_DDL,
1973
- idColumn: "metricId",
1974
- cursorSequenceDDL: METRIC_EVENTS_CURSOR_SEQUENCE_DDL
1975
- },
1976
- {
1977
- table: "log_events",
1978
- createDDL: LOG_EVENTS_DDL,
1979
- idColumn: "logId",
1980
- cursorSequenceDDL: LOG_EVENTS_CURSOR_SEQUENCE_DDL
1981
- },
1982
- {
1983
- table: "score_events",
1984
- createDDL: SCORE_EVENTS_DDL,
1985
- idColumn: "scoreId",
1986
- cursorSequenceDDL: SCORE_EVENTS_CURSOR_SEQUENCE_DDL
1987
- },
1988
- {
1989
- table: "feedback_events",
1990
- createDDL: FEEDBACK_EVENTS_DDL,
1991
- idColumn: "feedbackId",
1992
- cursorSequenceDDL: FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL
1993
- }
1994
- ];
1995
- async function tableExists(db, table) {
1996
- const rows = await db.query(
1997
- `SELECT table_name FROM information_schema.tables WHERE table_name = ?`,
1998
- [table]
1999
- );
2000
- return rows.length > 0;
2001
- }
2002
- async function hasPrimaryKey(db, table) {
2003
- const rows = await db.query(
2004
- `SELECT constraint_type FROM information_schema.table_constraints
2005
- WHERE table_name = ? AND constraint_type = 'PRIMARY KEY'`,
2006
- [table]
2007
- );
2008
- return rows.length > 0;
2009
- }
2010
- async function getColumns(db, table) {
2011
- const rows = await db.query(
2012
- `SELECT column_name FROM information_schema.columns WHERE table_name = ?`,
2013
- [table]
2014
- );
2015
- return rows.map((r) => r.column_name);
2016
- }
2017
- function buildTemporaryTableDDL(createDDL, table, tempTable) {
2018
- return createDDL.replace(`CREATE TABLE IF NOT EXISTS ${table}`, `CREATE TABLE ${tempTable}`);
2019
- }
2020
- async function dropTableIfExists(db, table) {
2021
- if (await tableExists(db, table)) {
2022
- await db.execute(`DROP TABLE ${table}`);
2023
- }
2024
- }
2025
- function createMigrationError(args, error$1) {
2026
- return new error.MastraError(
2027
- {
2028
- id: storage.createStorageErrorId("DUCKDB", "MIGRATE_SIGNAL_TABLES", "FAILED"),
2029
- domain: error.ErrorDomain.STORAGE,
2030
- category: error.ErrorCategory.THIRD_PARTY,
2031
- details: args
2032
- },
2033
- error$1
2034
- );
2035
- }
2036
- async function checkSignalTablesMigrationStatus(db) {
2037
- const tables = [];
2038
- for (const { table, idColumn } of SIGNAL_MIGRATIONS) {
2039
- if (!await tableExists(db, table)) {
2040
- continue;
2041
- }
2042
- if (await hasPrimaryKey(db, table)) {
2043
- continue;
2044
- }
2045
- tables.push({ table, idColumn });
2046
- }
2047
- return {
2048
- needsMigration: tables.length > 0,
2049
- tables
2050
- };
2051
- }
2052
- async function migrateSignalTables(db, logger) {
2053
- for (const { table, createDDL, idColumn, cursorSequenceDDL } of SIGNAL_MIGRATIONS) {
2054
- if (!await tableExists(db, table)) continue;
2055
- if (await hasPrimaryKey(db, table)) continue;
2056
- logger?.info?.(`Migrating ${table} to schema with ${idColumn} PRIMARY KEY`);
2057
- const temp = `${table}_migrating_${Date.now()}`;
2058
- const backup = `${table}_backup_${Date.now()}`;
2059
- let originalRenamed = false;
2060
- let swapCompleted = false;
2061
- try {
2062
- await db.execute(cursorSequenceDDL);
2063
- await db.execute(buildTemporaryTableDDL(createDDL, table, temp));
2064
- const newColumns = await getColumns(db, temp);
2065
- const currentColumns = new Set(await getColumns(db, table));
2066
- const columnList = newColumns.map((c) => `"${c}"`).join(", ");
2067
- const selectExprs = newColumns.map((c) => {
2068
- if (c === idColumn) {
2069
- return currentColumns.has(c) ? `COALESCE(NULLIF("${c}", ''), CAST(uuid() AS VARCHAR)) AS "${c}"` : `CAST(uuid() AS VARCHAR) AS "${c}"`;
2070
- }
2071
- return currentColumns.has(c) ? `"${c}"` : `NULL AS "${c}"`;
2072
- }).join(", ");
2073
- await db.execute(`INSERT INTO ${temp} (${columnList}) SELECT ${selectExprs} FROM ${table}`);
2074
- await db.execute(`ALTER TABLE ${table} RENAME TO ${backup}`);
2075
- originalRenamed = true;
2076
- await db.execute(`ALTER TABLE ${temp} RENAME TO ${table}`);
2077
- swapCompleted = true;
2078
- try {
2079
- await db.execute(`DROP TABLE ${backup}`);
2080
- } catch (cleanupError) {
2081
- logger?.warn?.(
2082
- `Migration of ${table} completed, but failed to drop backup ${backup}: ${cleanupError.message}`
2083
- );
2084
- }
2085
- logger?.info?.(`Successfully migrated ${table}`);
2086
- } catch (error) {
2087
- logger?.error?.(`Migration of ${table} failed: ${error.message}`);
2088
- try {
2089
- await dropTableIfExists(db, temp);
2090
- } catch (restoreError) {
2091
- logger?.error?.(`Failed to clean up temporary table ${temp}: ${restoreError.message}`);
2092
- }
2093
- if (originalRenamed && !swapCompleted) {
2094
- try {
2095
- await db.execute(`ALTER TABLE ${backup} RENAME TO ${table}`);
2096
- } catch (restoreError) {
2097
- logger?.error?.(
2098
- `Failed to restore original table ${table} from backup ${backup}: ${restoreError.message}`
2099
- );
2100
- }
2101
- }
2102
- throw createMigrationError({ table, idColumn }, error);
2103
- }
2104
- }
2105
- }
2106
- var SCORE_GROUP_BY_COLUMNS = /* @__PURE__ */ new Set([
2107
- "timestamp",
2108
- "traceId",
2109
- "spanId",
2110
- "experimentId",
2111
- "scoreTraceId",
2112
- "entityType",
2113
- "entityId",
2114
- "entityName",
2115
- "entityVersionId",
2116
- "parentEntityVersionId",
2117
- "parentEntityType",
2118
- "parentEntityId",
2119
- "parentEntityName",
2120
- "rootEntityVersionId",
2121
- "rootEntityType",
2122
- "rootEntityId",
2123
- "rootEntityName",
2124
- "userId",
2125
- "organizationId",
2126
- "resourceId",
2127
- "runId",
2128
- "sessionId",
2129
- "threadId",
2130
- "requestId",
2131
- "environment",
2132
- "executionSource",
2133
- "serviceName",
2134
- "scorerId",
2135
- "scorerVersion",
2136
- "scoreSource",
2137
- "score",
2138
- "reason"
2139
- ]);
2140
- function getAggregationSql3(aggregation, measure = "score") {
2141
- switch (aggregation) {
2142
- case "sum":
2143
- return `SUM(${measure})`;
2144
- case "avg":
2145
- return `AVG(${measure})`;
2146
- case "min":
2147
- return `MIN(${measure})`;
2148
- case "max":
2149
- return `MAX(${measure})`;
2150
- case "count":
2151
- return `CAST(COUNT(${measure}) AS DOUBLE)`;
2152
- case "last":
2153
- return `arg_max(${measure}, timestamp)`;
2154
- default:
2155
- return `SUM(${measure})`;
2156
- }
2157
- }
2158
- function getIntervalSql3(interval) {
2159
- switch (interval) {
2160
- case "1m":
2161
- return "1 minute";
2162
- case "5m":
2163
- return "5 minutes";
2164
- case "15m":
2165
- return "15 minutes";
2166
- case "1h":
2167
- return "1 hour";
2168
- case "1d":
2169
- return "1 day";
2170
- default:
2171
- return "1 hour";
2172
- }
2173
- }
2174
- function getValidatedPercentiles2(percentiles) {
2175
- if (!Array.isArray(percentiles) || percentiles.length === 0) {
2176
- throw new Error("Percentiles must include at least one value between 0 and 1.");
2177
- }
2178
- return percentiles.map((percentile) => {
2179
- if (!Number.isFinite(percentile) || percentile < 0 || percentile > 1) {
2180
- throw new Error("Percentiles must be finite numbers between 0 and 1.");
2181
- }
2182
- return percentile;
2183
- });
2184
- }
2185
- function buildScoreWhereClause(args) {
2186
- const conditions = ["scorerId = ?"];
2187
- const params = [args.scorerId];
2188
- if (args.scoreSource !== void 0) {
2189
- conditions.push("scoreSource = ?");
2190
- params.push(args.scoreSource);
2191
- }
2192
- const { clause: filterClause, params: filterParams } = buildWhereClause(
2193
- args.filters,
2194
- { source: "scoreSource" }
2195
- );
2196
- if (filterClause) {
2197
- conditions.push(filterClause.replace("WHERE ", ""));
2198
- params.push(...filterParams);
2199
- }
2200
- return { clause: `WHERE ${conditions.join(" AND ")}`, params };
2201
- }
2202
- function resolveScoreGroupBy(groupBy) {
2203
- return groupBy.map((key, index) => {
2204
- const column = utils.parseFieldKey(key);
2205
- if (!SCORE_GROUP_BY_COLUMNS.has(column)) {
2206
- throw new Error(`Invalid groupBy column(s): ${key}`);
2207
- }
2208
- const alias = `group_by_${index}`;
2209
- return {
2210
- key,
2211
- selectSql: `${column} AS ${alias}`,
2212
- groupSql: alias
2213
- };
2214
- });
2215
- }
2216
- function toSeriesName2(values) {
2217
- return values.map((value) => value === null || value === void 0 ? "" : String(value)).join("|");
2218
- }
2219
- function rowToScoreRecord(row) {
2220
- return {
2221
- scoreId: row.scoreId,
2222
- timestamp: toDate(row.timestamp),
2223
- traceId: row.traceId ?? null,
2224
- spanId: row.spanId ?? null,
2225
- experimentId: row.experimentId ?? null,
2226
- scoreTraceId: row.scoreTraceId ?? null,
2227
- entityType: row.entityType ?? null,
2228
- entityId: row.entityId ?? null,
2229
- entityName: row.entityName ?? null,
2230
- entityVersionId: row.entityVersionId ?? null,
2231
- parentEntityVersionId: row.parentEntityVersionId ?? null,
2232
- parentEntityType: row.parentEntityType ?? null,
2233
- parentEntityId: row.parentEntityId ?? null,
2234
- parentEntityName: row.parentEntityName ?? null,
2235
- rootEntityVersionId: row.rootEntityVersionId ?? null,
2236
- rootEntityType: row.rootEntityType ?? null,
2237
- rootEntityId: row.rootEntityId ?? null,
2238
- rootEntityName: row.rootEntityName ?? null,
2239
- userId: row.userId ?? null,
2240
- organizationId: row.organizationId ?? null,
2241
- resourceId: row.resourceId ?? null,
2242
- runId: row.runId ?? null,
2243
- sessionId: row.sessionId ?? null,
2244
- threadId: row.threadId ?? null,
2245
- requestId: row.requestId ?? null,
2246
- environment: row.environment ?? null,
2247
- executionSource: row.executionSource ?? null,
2248
- serviceName: row.serviceName ?? null,
2249
- scorerId: row.scorerId,
2250
- scorerVersion: row.scorerVersion ?? null,
2251
- source: row.scoreSource ?? null,
2252
- scoreSource: row.scoreSource ?? null,
2253
- score: Number(row.score),
2254
- reason: row.reason ?? null,
2255
- tags: parseJsonArray(row.tags),
2256
- metadata: parseJson(row.metadata),
2257
- scope: parseJson(row.scope)
2258
- };
2259
- }
2260
- function getComparisonDateRange2(comparePeriod, timestamp) {
2261
- if (!timestamp.start || !timestamp.end) return null;
2262
- const duration = timestamp.end.getTime() - timestamp.start.getTime();
2263
- switch (comparePeriod) {
2264
- case "previous_period":
2265
- return {
2266
- start: new Date(timestamp.start.getTime() - duration),
2267
- end: new Date(timestamp.end.getTime() - duration),
2268
- startExclusive: timestamp.startExclusive,
2269
- endExclusive: timestamp.endExclusive
2270
- };
2271
- case "previous_day":
2272
- return {
2273
- start: new Date(timestamp.start.getTime() - 864e5),
2274
- end: new Date(timestamp.end.getTime() - 864e5),
2275
- startExclusive: timestamp.startExclusive,
2276
- endExclusive: timestamp.endExclusive
2277
- };
2278
- case "previous_week":
2279
- return {
2280
- start: new Date(timestamp.start.getTime() - 6048e5),
2281
- end: new Date(timestamp.end.getTime() - 6048e5),
2282
- startExclusive: timestamp.startExclusive,
2283
- endExclusive: timestamp.endExclusive
2284
- };
2285
- }
2286
- }
2287
- async function createScore(db, args) {
2288
- const s = args.score;
2289
- const scoreSource = s.scoreSource ?? s.source ?? null;
2290
- await db.execute(
2291
- `INSERT INTO score_events (
2292
- scoreId, timestamp, cursorId, traceId, spanId, experimentId, scoreTraceId,
2293
- entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
2294
- userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
2295
- scorerId, scorerVersion, scoreSource, score, reason, tags, metadata, scope
2296
- )
2297
- VALUES (${[
2298
- v(s.scoreId),
2299
- v(s.timestamp),
2300
- "nextval('score_events_cursor_id_seq')",
2301
- v(s.traceId),
2302
- v(s.spanId ?? null),
2303
- v(s.experimentId ?? null),
2304
- v(s.scoreTraceId ?? null),
2305
- v(s.entityType ?? null),
2306
- v(s.entityId ?? null),
2307
- v(s.entityName ?? null),
2308
- v(s.entityVersionId ?? null),
2309
- v(s.parentEntityVersionId ?? null),
2310
- v(s.parentEntityType ?? null),
2311
- v(s.parentEntityId ?? null),
2312
- v(s.parentEntityName ?? null),
2313
- v(s.rootEntityVersionId ?? null),
2314
- v(s.rootEntityType ?? null),
2315
- v(s.rootEntityId ?? null),
2316
- v(s.rootEntityName ?? null),
2317
- v(s.userId ?? null),
2318
- v(s.organizationId ?? null),
2319
- v(s.resourceId ?? null),
2320
- v(s.runId ?? null),
2321
- v(s.sessionId ?? null),
2322
- v(s.threadId ?? null),
2323
- v(s.requestId ?? null),
2324
- v(s.environment ?? null),
2325
- v(s.executionSource ?? null),
2326
- v(s.serviceName ?? null),
2327
- v(s.scorerId),
2328
- v(s.scorerVersion ?? null),
2329
- v(scoreSource),
2330
- v(s.score),
2331
- v(s.reason ?? null),
2332
- jsonV(s.tags ?? null),
2333
- jsonV(s.metadata),
2334
- jsonV(s.scope ?? null)
2335
- ].join(", ")})
2336
- ON CONFLICT DO NOTHING`
2337
- );
2338
- }
2339
- async function batchCreateScores(db, args) {
2340
- if (args.scores.length === 0) return;
2341
- const tuples = args.scores.map((s) => {
2342
- const legacyScore = s;
2343
- const scoreSource = legacyScore.scoreSource ?? legacyScore.source ?? null;
2344
- return `(${[
2345
- v(legacyScore.scoreId),
2346
- v(legacyScore.timestamp),
2347
- "nextval('score_events_cursor_id_seq')",
2348
- v(legacyScore.traceId),
2349
- v(legacyScore.spanId ?? null),
2350
- v(legacyScore.experimentId ?? null),
2351
- v(legacyScore.scoreTraceId ?? null),
2352
- v(legacyScore.entityType ?? null),
2353
- v(legacyScore.entityId ?? null),
2354
- v(legacyScore.entityName ?? null),
2355
- v(legacyScore.entityVersionId ?? null),
2356
- v(legacyScore.parentEntityVersionId ?? null),
2357
- v(legacyScore.parentEntityType ?? null),
2358
- v(legacyScore.parentEntityId ?? null),
2359
- v(legacyScore.parentEntityName ?? null),
2360
- v(legacyScore.rootEntityVersionId ?? null),
2361
- v(legacyScore.rootEntityType ?? null),
2362
- v(legacyScore.rootEntityId ?? null),
2363
- v(legacyScore.rootEntityName ?? null),
2364
- v(legacyScore.userId ?? null),
2365
- v(legacyScore.organizationId ?? null),
2366
- v(legacyScore.resourceId ?? null),
2367
- v(legacyScore.runId ?? null),
2368
- v(legacyScore.sessionId ?? null),
2369
- v(legacyScore.threadId ?? null),
2370
- v(legacyScore.requestId ?? null),
2371
- v(legacyScore.environment ?? null),
2372
- v(legacyScore.executionSource ?? null),
2373
- v(legacyScore.serviceName ?? null),
2374
- v(legacyScore.scorerId),
2375
- v(legacyScore.scorerVersion ?? null),
2376
- v(scoreSource),
2377
- v(legacyScore.score),
2378
- v(legacyScore.reason ?? null),
2379
- jsonV(legacyScore.tags ?? null),
2380
- jsonV(legacyScore.metadata),
2381
- jsonV(legacyScore.scope ?? null)
2382
- ].join(", ")})`;
2383
- });
2384
- await db.execute(
2385
- `INSERT INTO score_events (
2386
- scoreId, timestamp, cursorId, traceId, spanId, experimentId, scoreTraceId,
2387
- entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
2388
- userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
2389
- scorerId, scorerVersion, scoreSource, score, reason, tags, metadata, scope
2390
- )
2391
- VALUES ${tuples.join(",\n ")}
2392
- ON CONFLICT DO NOTHING`
2393
- );
2394
- }
2395
- async function listScores(db, args) {
2396
- const { mode, filters, pagination, orderBy, after, limit } = storage.listScoresArgsSchema.parse(args);
2397
- const page = Number(pagination.page);
2398
- const perPage = Number(pagination.perPage);
2399
- const { clause: filterClause, params: filterParams } = buildWhereClause(filters, {
2400
- source: "scoreSource"
2401
- });
2402
- if (mode === "delta") {
2403
- assertDeltaPollingEnabled();
2404
- const streamHeadCursor = await getStreamHeadCursor4(db);
2405
- if (after === void 0) {
2406
- return {
2407
- scores: [],
2408
- delta: { limit, hasMore: false },
2409
- deltaCursor: streamHeadCursor
2410
- };
2411
- }
2412
- const afterCursorId = validateCursorId(after);
2413
- const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
2414
- const rows2 = await db.query(
2415
- `SELECT * FROM score_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
2416
- [...filterParams, afterCursorId, limit + 1]
2417
- );
2418
- const visibleRows = rows2.slice(0, limit).map((row) => ({
2419
- cursorId: row.cursorId,
2420
- score: rowToScoreRecord(row)
2421
- }));
2422
- return {
2423
- scores: visibleRows.map((row) => row.score),
2424
- delta: { limit, hasMore: rows2.length > limit },
2425
- deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
2426
- };
2427
- }
2428
- const orderByClause = buildOrderByClause(orderBy);
2429
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
2430
- const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor4(db, filterClause, filterParams) : void 0;
2431
- const countResult = await db.query(
2432
- `SELECT COUNT(*) as total FROM score_events ${filterClause}`,
2433
- filterParams
2434
- );
2435
- const total = Number(countResult[0]?.total ?? 0);
2436
- const rows = await db.query(
2437
- `SELECT * FROM score_events ${filterClause} ${orderByClause} ${paginationClause}`,
2438
- [...filterParams, ...paginationParams]
2439
- );
2440
- return {
2441
- pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
2442
- scores: rows.map((row) => rowToScoreRecord(row)),
2443
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
2444
- };
2445
- }
2446
- async function getDeltaCursor4(db, filterClause, filterParams) {
2447
- const rows = await db.query(
2448
- `SELECT max(cursorId) AS cursorId FROM score_events ${filterClause}`,
2449
- filterParams
2450
- );
2451
- const cursorId = rows[0]?.cursorId;
2452
- if (cursorId !== null && cursorId !== void 0) {
2453
- return encodeDeltaCursor(cursorId);
2454
- }
2455
- const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM score_events`);
2456
- return encodeDeltaCursor(streamRows[0]?.cursorId);
2457
- }
2458
- async function getStreamHeadCursor4(db) {
2459
- const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM score_events`);
2460
- return encodeDeltaCursor(streamRows[0]?.cursorId);
2461
- }
2462
- async function getScoreById(db, scoreId) {
2463
- const rows = await db.query(`SELECT * FROM score_events WHERE scoreId = ? LIMIT 1`, [
2464
- scoreId
2465
- ]);
2466
- return rows[0] ? rowToScoreRecord(rows[0]) : null;
2467
- }
2468
- async function getScoreAggregate(db, args) {
2469
- const aggSql = getAggregationSql3(args.aggregation);
2470
- const { clause, params } = buildScoreWhereClause(args);
2471
- const rows = await db.query(`SELECT ${aggSql} AS value FROM score_events ${clause}`, params);
2472
- const value = rows[0]?.value === null || rows[0]?.value === void 0 ? null : Number(rows[0]?.value);
2473
- if (args.comparePeriod && args.filters?.timestamp) {
2474
- const previousTimestamp = getComparisonDateRange2(args.comparePeriod, args.filters.timestamp);
2475
- if (previousTimestamp) {
2476
- const prevRows = await db.query(
2477
- `SELECT ${aggSql} AS value FROM score_events ${buildScoreWhereClause({
2478
- ...args,
2479
- filters: { ...args.filters ?? {}, timestamp: previousTimestamp }
2480
- }).clause}`,
2481
- buildScoreWhereClause({
2482
- ...args,
2483
- filters: { ...args.filters ?? {}, timestamp: previousTimestamp }
2484
- }).params
2485
- );
2486
- const previousValue = prevRows[0]?.value === null || prevRows[0]?.value === void 0 ? null : Number(prevRows[0]?.value);
2487
- let changePercent = null;
2488
- if (previousValue !== null && previousValue !== 0 && value !== null) {
2489
- changePercent = (value - previousValue) / Math.abs(previousValue) * 100;
2490
- }
2491
- return { value, previousValue, changePercent };
2492
- }
2493
- }
2494
- return { value };
2495
- }
2496
- async function getScoreBreakdown(db, args) {
2497
- const aggSql = getAggregationSql3(args.aggregation);
2498
- const { clause, params } = buildScoreWhereClause(args);
2499
- const resolvedGroupBy = resolveScoreGroupBy(args.groupBy);
2500
- const sql = `SELECT ${resolvedGroupBy.map((entry) => entry.selectSql).join(", ")}, ${aggSql} AS value FROM score_events ${clause} GROUP BY ${resolvedGroupBy.map((entry) => entry.groupSql).join(", ")} ORDER BY value DESC`;
2501
- const rows = await db.query(sql, params);
2502
- return {
2503
- groups: rows.map((row) => ({
2504
- dimensions: Object.fromEntries(
2505
- resolvedGroupBy.map((entry, index) => {
2506
- const value = row[`group_by_${index}`];
2507
- return [entry.key, value === null || value === void 0 ? null : String(value)];
2508
- })
2509
- ),
2510
- value: Number(row.value ?? 0)
2511
- }))
2512
- };
2513
- }
2514
- async function getScoreTimeSeries(db, args) {
2515
- const aggSql = getAggregationSql3(args.aggregation);
2516
- const intervalSql = getIntervalSql3(args.interval);
2517
- const { clause, params } = buildScoreWhereClause(args);
2518
- if (args.groupBy && args.groupBy.length > 0) {
2519
- const resolvedGroupBy = resolveScoreGroupBy(args.groupBy);
2520
- const sql = `
2521
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
2522
- ${resolvedGroupBy.map((entry) => entry.selectSql).join(", ")},
2523
- ${aggSql} AS value
2524
- FROM score_events ${clause}
2525
- GROUP BY bucket, ${resolvedGroupBy.map((entry) => entry.groupSql).join(", ")}
2526
- ORDER BY bucket
2527
- `;
2528
- const rows2 = await db.query(sql, params);
2529
- const seriesMap = /* @__PURE__ */ new Map();
2530
- for (const row of rows2) {
2531
- const groupValues = resolvedGroupBy.map((_, index) => row[`group_by_${index}`]);
2532
- const key = JSON.stringify(groupValues);
2533
- if (!seriesMap.has(key)) {
2534
- seriesMap.set(key, { name: toSeriesName2(groupValues), points: [] });
2535
- }
2536
- seriesMap.get(key).points.push({
2537
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
2538
- value: Number(row.value ?? 0)
2539
- });
2540
- }
2541
- return { series: Array.from(seriesMap.values()) };
2542
- }
2543
- const rows = await db.query(
2544
- `
2545
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
2546
- ${aggSql} AS value
2547
- FROM score_events ${clause}
2548
- GROUP BY bucket
2549
- ORDER BY bucket
2550
- `,
2551
- params
2552
- );
2553
- return {
2554
- series: [
2555
- {
2556
- name: args.scoreSource ? `${args.scorerId}|${args.scoreSource}` : args.scorerId,
2557
- points: rows.map((row) => ({
2558
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
2559
- value: Number(row.value ?? 0)
2560
- }))
2561
- }
2562
- ]
2563
- };
2564
- }
2565
- async function getScorePercentiles(db, args) {
2566
- const intervalSql = getIntervalSql3(args.interval);
2567
- const { clause, params } = buildScoreWhereClause(args);
2568
- const percentiles = getValidatedPercentiles2(args.percentiles);
2569
- const series = [];
2570
- for (const percentile of percentiles) {
2571
- const rows = await db.query(
2572
- `
2573
- SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
2574
- percentile_cont(${percentile}) WITHIN GROUP (ORDER BY score) AS pvalue
2575
- FROM score_events ${clause}
2576
- GROUP BY bucket
2577
- ORDER BY bucket
2578
- `,
2579
- params
2580
- );
2581
- series.push({
2582
- percentile,
2583
- points: rows.map((row) => ({
2584
- timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
2585
- value: Number(row.pvalue ?? 0)
2586
- }))
2587
- });
2588
- }
2589
- return { series };
2590
- }
2591
- var COLUMNS2 = [
2592
- "eventType",
2593
- "timestamp",
2594
- "cursorId",
2595
- "traceId",
2596
- "spanId",
2597
- "parentSpanId",
2598
- "name",
2599
- "spanType",
2600
- "isEvent",
2601
- "endedAt",
2602
- "experimentId",
2603
- "entityType",
2604
- "entityId",
2605
- "entityName",
2606
- "entityVersionId",
2607
- "userId",
2608
- "organizationId",
2609
- "resourceId",
2610
- "runId",
2611
- "sessionId",
2612
- "threadId",
2613
- "requestId",
2614
- "environment",
2615
- "source",
2616
- "serviceName",
2617
- "attributes",
2618
- "metadata",
2619
- "tags",
2620
- "scope",
2621
- "links",
2622
- "input",
2623
- "output",
2624
- "error",
2625
- "requestContext"
2626
- ];
2627
- var COLUMNS_SQL2 = COLUMNS2.join(", ");
2628
- function argMaxNonNull(col) {
2629
- return `arg_max(${col}, timestamp) FILTER (WHERE ${col} IS NOT NULL) as ${col}`;
2630
- }
2631
- var SPAN_RECONSTRUCT_SELECT = `
2632
- SELECT
2633
- traceId, spanId,
2634
- ${argMaxNonNull("name")},
2635
- ${argMaxNonNull("spanType")},
2636
- ${argMaxNonNull("parentSpanId")},
2637
- ${argMaxNonNull("isEvent")},
2638
- coalesce(min(timestamp) FILTER (WHERE eventType = 'start'), min(timestamp)) as startedAt,
2639
- ${argMaxNonNull("endedAt")},
2640
- ${argMaxNonNull("experimentId")},
2641
- ${argMaxNonNull("entityType")},
2642
- ${argMaxNonNull("entityId")},
2643
- ${argMaxNonNull("entityName")},
2644
- ${argMaxNonNull("entityVersionId")},
2645
- ${argMaxNonNull("userId")},
2646
- ${argMaxNonNull("organizationId")},
2647
- ${argMaxNonNull("resourceId")},
2648
- ${argMaxNonNull("runId")},
2649
- ${argMaxNonNull("sessionId")},
2650
- ${argMaxNonNull("threadId")},
2651
- ${argMaxNonNull("requestId")},
2652
- ${argMaxNonNull("environment")},
2653
- ${argMaxNonNull("source")},
2654
- ${argMaxNonNull("serviceName")},
2655
- ${argMaxNonNull("attributes")},
2656
- ${argMaxNonNull("metadata")},
2657
- ${argMaxNonNull("tags")},
2658
- ${argMaxNonNull("scope")},
2659
- ${argMaxNonNull("links")},
2660
- ${argMaxNonNull("input")},
2661
- ${argMaxNonNull("output")},
2662
- ${argMaxNonNull("error")},
2663
- ${argMaxNonNull("requestContext")}
2664
- FROM span_events
2665
- `;
2666
- var SPAN_RECONSTRUCT_SELECT_LIGHT = `
2667
- SELECT
2668
- traceId, spanId,
2669
- ${argMaxNonNull("name")},
2670
- ${argMaxNonNull("spanType")},
2671
- ${argMaxNonNull("parentSpanId")},
2672
- ${argMaxNonNull("isEvent")},
2673
- coalesce(min(timestamp) FILTER (WHERE eventType = 'start'), min(timestamp)) as startedAt,
2674
- ${argMaxNonNull("endedAt")},
2675
- ${argMaxNonNull("entityType")},
2676
- ${argMaxNonNull("entityId")},
2677
- ${argMaxNonNull("entityName")},
2678
- ${argMaxNonNull("error")}
2679
- FROM span_events
2680
- `;
2681
- function rowToLightSpanRecord(row) {
2682
- return {
2683
- traceId: row.traceId,
2684
- spanId: row.spanId,
2685
- name: row.name,
2686
- spanType: row.spanType,
2687
- parentSpanId: row.parentSpanId ?? null,
2688
- isEvent: row.isEvent,
2689
- startedAt: toDate(row.startedAt),
2690
- endedAt: toDateOrNull(row.endedAt),
2691
- entityType: row.entityType ?? null,
2692
- entityId: row.entityId ?? null,
2693
- entityName: row.entityName ?? null,
2694
- error: parseJson(row.error),
2695
- createdAt: toDate(row.startedAt),
2696
- // DuckDB event-sourced — use startedAt as proxy
2697
- updatedAt: toDateOrNull(row.endedAt)
2698
- };
2699
- }
2700
- function rowToSpanRecord(row) {
2701
- return {
2702
- traceId: row.traceId,
2703
- spanId: row.spanId,
2704
- name: row.name,
2705
- spanType: row.spanType,
2706
- parentSpanId: row.parentSpanId ?? null,
2707
- isEvent: row.isEvent,
2708
- startedAt: toDate(row.startedAt),
2709
- endedAt: toDateOrNull(row.endedAt),
2710
- experimentId: row.experimentId ?? null,
2711
- entityType: row.entityType ?? null,
2712
- entityId: row.entityId ?? null,
2713
- entityName: row.entityName ?? null,
2714
- entityVersionId: row.entityVersionId ?? null,
2715
- userId: row.userId ?? null,
2716
- organizationId: row.organizationId ?? null,
2717
- resourceId: row.resourceId ?? null,
2718
- runId: row.runId ?? null,
2719
- sessionId: row.sessionId ?? null,
2720
- threadId: row.threadId ?? null,
2721
- requestId: row.requestId ?? null,
2722
- environment: row.environment ?? null,
2723
- source: row.source ?? null,
2724
- serviceName: row.serviceName ?? null,
2725
- attributes: parseJson(row.attributes),
2726
- metadata: parseJson(row.metadata),
2727
- tags: parseJsonArray(row.tags),
2728
- scope: parseJson(row.scope),
2729
- links: parseJsonArray(row.links),
2730
- input: parseJson(row.input),
2731
- output: parseJson(row.output),
2732
- error: parseJson(row.error),
2733
- requestContext: parseJson(row.requestContext),
2734
- createdAt: toDate(row.startedAt),
2735
- updatedAt: null
2736
- };
2737
- }
2738
- function buildHasChildErrorClause(hasChildError, rootAlias) {
2739
- if (hasChildError === void 0) return "";
2740
- const base = `SELECT 1 FROM span_events c WHERE c.traceId = ${rootAlias}.traceId AND c.spanId != ${rootAlias}.spanId AND c.error IS NOT NULL`;
2741
- return hasChildError ? `EXISTS (${base})` : `NOT EXISTS (${base})`;
2742
- }
2743
- var PREFILTER_KEYS = /* @__PURE__ */ new Set([
2744
- "traceId",
2745
- "spanId",
2746
- "parentSpanId",
2747
- "name",
2748
- "spanType",
2749
- "source",
2750
- "entityType",
2751
- "entityId",
2752
- "entityName",
2753
- "entityVersionId",
2754
- "experimentId",
2755
- "userId",
2756
- "organizationId",
2757
- "resourceId",
2758
- "runId",
2759
- "sessionId",
2760
- "threadId",
2761
- "requestId",
2762
- "environment",
2763
- "serviceName"
2764
- ]);
2765
- var SAFE_PREFILTER_ORDER_FIELDS = /* @__PURE__ */ new Set(["startedAt"]);
2766
- function intersectTimestampRange(existing, incoming) {
2767
- if (!existing) return { ...incoming };
2768
- const merged = { ...existing };
2769
- if (incoming.start !== void 0) {
2770
- if (merged.start === void 0 || incoming.start.getTime() > merged.start.getTime()) {
2771
- merged.start = incoming.start;
2772
- merged.startExclusive = incoming.startExclusive;
2773
- } else if (incoming.start.getTime() === merged.start.getTime()) {
2774
- merged.startExclusive = (merged.startExclusive ?? false) || (incoming.startExclusive ?? false);
2775
- }
2776
- }
2777
- if (incoming.end !== void 0) {
2778
- if (merged.end === void 0 || incoming.end.getTime() < merged.end.getTime()) {
2779
- merged.end = incoming.end;
2780
- merged.endExclusive = incoming.endExclusive;
2781
- } else if (incoming.end.getTime() === merged.end.getTime()) {
2782
- merged.endExclusive = (merged.endExclusive ?? false) || (incoming.endExclusive ?? false);
2783
- }
2784
- }
2785
- return merged;
2786
- }
2787
- function partitionAnchorFilters(filters) {
2788
- const prefilter = {};
2789
- const postAgg = {};
2790
- let hasChildError;
2791
- for (const [key, value] of Object.entries(filters)) {
2792
- if (value === void 0 || value === null) continue;
2793
- if (key === "hasChildError") {
2794
- if (typeof value === "boolean") hasChildError = value;
2795
- continue;
2796
- }
2797
- if (key === "startedAt") {
2798
- prefilter.timestamp = intersectTimestampRange(
2799
- prefilter.timestamp,
2800
- value
2801
- );
2802
- continue;
2803
- }
2804
- if (key === "endedAt") {
2805
- postAgg.endedAt = value;
2806
- const dateRange = value;
2807
- if (dateRange?.end) {
2808
- prefilter.timestamp = intersectTimestampRange(prefilter.timestamp, {
2809
- end: dateRange.end,
2810
- endExclusive: dateRange.endExclusive
2811
- });
2812
- }
2813
- continue;
2814
- }
2815
- if (PREFILTER_KEYS.has(key)) {
2816
- prefilter[key] = value;
2817
- continue;
2818
- }
2819
- postAgg[key] = value;
2820
- }
2821
- return { prefilter, postAgg, hasChildError };
2822
- }
2823
- function toValuesTuple(row) {
2824
- return [
2825
- v(row.eventType),
2826
- v(row.timestamp),
2827
- "nextval('span_events_cursor_id_seq')",
2828
- v(row.traceId),
2829
- v(row.spanId),
2830
- v(row.parentSpanId),
2831
- v(row.name),
2832
- v(row.spanType),
2833
- v(row.isEvent),
2834
- v(row.endedAt),
2835
- v(row.experimentId),
2836
- v(row.entityType),
2837
- v(row.entityId),
2838
- v(row.entityName),
2839
- v(row.entityVersionId),
2840
- v(row.userId),
2841
- v(row.organizationId),
2842
- v(row.resourceId),
2843
- v(row.runId),
2844
- v(row.sessionId),
2845
- v(row.threadId),
2846
- v(row.requestId),
2847
- v(row.environment),
2848
- v(row.source),
2849
- v(row.serviceName),
2850
- jsonV(row.attributes),
2851
- jsonV(row.metadata),
2852
- jsonV(row.tags),
2853
- jsonV(row.scope),
2854
- jsonV(row.links),
2855
- jsonV(row.input),
2856
- jsonV(row.output),
2857
- jsonV(row.error),
2858
- jsonV(row.requestContext)
2859
- ].join(", ");
2860
- }
2861
- async function insertSpanEvents(db, rows) {
2862
- if (rows.length === 0) return;
2863
- const tuples = rows.map((row) => `(${toValuesTuple(row)})`).join(",\n");
2864
- await db.execute(`INSERT INTO span_events (${COLUMNS_SQL2}) VALUES ${tuples}`);
2865
- }
2866
- function createStartSpanRow(s) {
2867
- return {
2868
- eventType: "start",
2869
- timestamp: s.startedAt,
2870
- traceId: s.traceId,
2871
- spanId: s.spanId,
2872
- parentSpanId: s.parentSpanId ?? null,
2873
- name: s.name,
2874
- spanType: s.spanType,
2875
- isEvent: s.isEvent,
2876
- endedAt: null,
2877
- experimentId: s.experimentId ?? null,
2878
- entityType: s.entityType ?? null,
2879
- entityId: s.entityId ?? null,
2880
- entityName: s.entityName ?? null,
2881
- entityVersionId: s.entityVersionId ?? null,
2882
- userId: s.userId ?? null,
2883
- organizationId: s.organizationId ?? null,
2884
- resourceId: s.resourceId ?? null,
2885
- runId: s.runId ?? null,
2886
- sessionId: s.sessionId ?? null,
2887
- threadId: s.threadId ?? null,
2888
- requestId: s.requestId ?? null,
2889
- environment: s.environment ?? null,
2890
- source: s.source ?? null,
2891
- serviceName: s.serviceName ?? null,
2892
- attributes: s.attributes ?? null,
2893
- metadata: s.metadata ?? null,
2894
- tags: s.tags ?? null,
2895
- scope: s.scope ?? null,
2896
- links: null,
2897
- input: s.input ?? null,
2898
- output: null,
2899
- error: null,
2900
- requestContext: s.requestContext ?? null
2901
- };
2902
- }
2903
- function createEndSpanRow(s) {
2904
- return {
2905
- eventType: "end",
2906
- timestamp: s.endedAt,
2907
- traceId: s.traceId,
2908
- spanId: s.spanId,
2909
- parentSpanId: s.parentSpanId ?? null,
2910
- name: s.name,
2911
- spanType: s.spanType,
2912
- isEvent: s.isEvent,
2913
- endedAt: s.endedAt ?? null,
2914
- experimentId: s.experimentId ?? null,
2915
- entityType: s.entityType ?? null,
2916
- entityId: s.entityId ?? null,
2917
- entityName: s.entityName ?? null,
2918
- entityVersionId: s.entityVersionId ?? null,
2919
- userId: s.userId ?? null,
2920
- organizationId: s.organizationId ?? null,
2921
- resourceId: s.resourceId ?? null,
2922
- runId: s.runId ?? null,
2923
- sessionId: s.sessionId ?? null,
2924
- threadId: s.threadId ?? null,
2925
- requestId: s.requestId ?? null,
2926
- environment: s.environment ?? null,
2927
- source: s.source ?? null,
2928
- serviceName: s.serviceName ?? null,
2929
- attributes: s.attributes ?? null,
2930
- metadata: s.metadata ?? null,
2931
- tags: s.tags ?? null,
2932
- scope: s.scope ?? null,
2933
- links: s.links ?? null,
2934
- input: s.input ?? null,
2935
- output: s.output ?? null,
2936
- error: s.error ?? null,
2937
- requestContext: s.requestContext ?? null
2938
- };
2939
- }
2940
- async function createSpan(db, args) {
2941
- const rows = [createStartSpanRow(args.span)];
2942
- if (args.span.endedAt) {
2943
- rows.push(createEndSpanRow(args.span));
2944
- }
2945
- await insertSpanEvents(db, rows);
2946
- }
2947
- async function batchCreateSpans(db, args) {
2948
- if (args.records.length === 0) return;
2949
- const rows = args.records.flatMap((record) => {
2950
- const events = [createStartSpanRow(record)];
2951
- if (record.endedAt) {
2952
- events.push(createEndSpanRow(record));
2953
- }
2954
- return events;
2955
- });
2956
- await insertSpanEvents(db, rows);
2957
- }
2958
- async function batchDeleteTraces(db, args) {
2959
- if (args.traceIds.length === 0) return;
2960
- const placeholders = args.traceIds.map(() => "?").join(", ");
2961
- await db.execute(`DELETE FROM span_events WHERE traceId IN (${placeholders})`, args.traceIds);
2962
- }
2963
- async function getSpan(db, args) {
2964
- const rows = await db.query(`${SPAN_RECONSTRUCT_SELECT} WHERE traceId = ? AND spanId = ? GROUP BY traceId, spanId`, [
2965
- args.traceId,
2966
- args.spanId
2967
- ]);
2968
- if (rows.length === 0) return null;
2969
- return { span: rowToSpanRecord(rows[0]) };
2970
- }
2971
- async function getRootSpan(db, args) {
2972
- const rows = await db.query(
2973
- `${SPAN_RECONSTRUCT_SELECT} WHERE traceId = ? GROUP BY traceId, spanId HAVING arg_max(parentSpanId, timestamp) IS NULL LIMIT 1`,
2974
- [args.traceId]
2975
- );
2976
- if (rows.length === 0) return null;
2977
- return { span: rowToSpanRecord(rows[0]) };
2978
- }
2979
- async function getTrace(db, args) {
2980
- const rows = await db.query(`${SPAN_RECONSTRUCT_SELECT} WHERE traceId = ? GROUP BY traceId, spanId`, [args.traceId]);
2981
- if (rows.length === 0) return null;
2982
- return {
2983
- traceId: args.traceId,
2984
- spans: rows.map((row) => rowToSpanRecord(row))
2985
- };
2986
- }
2987
- async function getTraceLight(db, args) {
2988
- const rows = await db.query(`${SPAN_RECONSTRUCT_SELECT_LIGHT} WHERE traceId = ? GROUP BY traceId, spanId`, [
2989
- args.traceId
2990
- ]);
2991
- if (rows.length === 0) return null;
2992
- return {
2993
- traceId: args.traceId,
2994
- spans: rows.map((row) => rowToLightSpanRecord(row))
2995
- };
2996
- }
2997
- async function listTraceRows(db, args, reconstructSelect, mapRow, toSpans) {
2998
- const filters = args.filters ?? {};
2999
- const page = Number(args.pagination?.page ?? 0);
3000
- const perPage = Number(args.pagination?.perPage ?? 10);
3001
- const orderBy = { field: args.orderBy?.field ?? "startedAt", direction: args.orderBy?.direction ?? "DESC" };
3002
- const { prefilter, postAgg, hasChildError } = partitionAnchorFilters(filters);
3003
- const { clause: prefilterClause, params: prefilterParams } = buildWhereClause(prefilter);
3004
- const prefilterParts = [`eventType = 'start'`, `parentSpanId IS NULL`];
3005
- if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
3006
- const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
3007
- const outerAlias = "outer_root";
3008
- const orderDir = orderBy.direction.toUpperCase();
3009
- if (orderDir !== "ASC" && orderDir !== "DESC") {
3010
- throw new Error(`Invalid sort direction: ${orderBy.direction}`);
3011
- }
3012
- const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
3013
- const hasPostAggFilters = Object.keys(postAgg).length > 0 || hasChildError !== void 0 || !canOrderInPrefilter;
3014
- if (!hasPostAggFilters) {
3015
- const prefilterOrderBy = `ORDER BY timestamp ${orderDir}`;
3016
- const offset = page * perPage;
3017
- const countSql2 = `
3018
- SELECT COUNT(*) as total
3019
- FROM span_events AS ${outerAlias}
3020
- ${prefilterWhere}
3021
- `;
3022
- const countResult2 = await db.query(countSql2, prefilterParams);
3023
- const total2 = Number(countResult2[0]?.total ?? 0);
3024
- const pageSql = `
3025
- WITH page_roots AS (
3026
- SELECT traceId, spanId
3027
- FROM span_events AS ${outerAlias}
3028
- ${prefilterWhere}
3029
- ${prefilterOrderBy}
3030
- LIMIT ? OFFSET ?
3031
- )
3032
- ${reconstructSelect}
3033
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM page_roots)
3034
- GROUP BY traceId, spanId
3035
- ${buildOrderByClause(orderBy)}
3036
- `;
3037
- const rows2 = await db.query(pageSql, [...prefilterParams, perPage, offset]);
3038
- const spans2 = rows2.map((row) => mapRow(row));
3039
- return {
3040
- pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
3041
- spans: toSpans(spans2)
3042
- };
3043
- }
3044
- const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
3045
- const postAggParts = [];
3046
- if (postAggClause) postAggParts.push(postAggClause.replace(/^WHERE\s+/i, ""));
3047
- const childErrorClause = buildHasChildErrorClause(hasChildError, "root_spans");
3048
- if (childErrorClause) postAggParts.push(childErrorClause);
3049
- const postAggWhere = postAggParts.length > 0 ? `WHERE ${postAggParts.join(" AND ")}` : "";
3050
- const cteSql = `
3051
- WITH candidate_roots AS (
3052
- SELECT traceId, spanId
3053
- FROM span_events AS ${outerAlias}
3054
- ${prefilterWhere}
3055
- ),
3056
- root_spans AS (
3057
- ${reconstructSelect}
3058
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
3059
- GROUP BY traceId, spanId
3060
- )
3061
- `;
3062
- const orderByClause = buildOrderByClause(orderBy);
3063
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
3064
- const countSql = `
3065
- ${cteSql}
3066
- SELECT COUNT(*) as total FROM root_spans ${postAggWhere}
3067
- `;
3068
- const countResult = await db.query(countSql, [...prefilterParams, ...postAggParams]);
3069
- const total = Number(countResult[0]?.total ?? 0);
3070
- const dataSql = `
3071
- ${cteSql}
3072
- SELECT * FROM root_spans ${postAggWhere} ${orderByClause} ${paginationClause}
3073
- `;
3074
- const rows = await db.query(dataSql, [...prefilterParams, ...postAggParams, ...paginationParams]);
3075
- const spans = rows.map((row) => mapRow(row));
3076
- return {
3077
- pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
3078
- spans: toSpans(spans)
3079
- };
3080
- }
3081
- async function listTraces(db, args) {
3082
- const { mode, filters, pagination, orderBy, after, limit } = storage.listTracesArgsSchema.parse(args);
3083
- const filterRecord = filters ?? {};
3084
- const page = Number(pagination.page);
3085
- const perPage = Number(pagination.perPage);
3086
- if (mode === "delta") {
3087
- assertDeltaPollingEnabled();
3088
- const streamHeadCursor = await getTraceStreamHeadCursor(db);
3089
- if (after === void 0) {
3090
- return {
3091
- spans: [],
3092
- delta: { limit, hasMore: false },
3093
- deltaCursor: streamHeadCursor
3094
- };
3095
- }
3096
- const afterCursorId = validateCursorId(after);
3097
- const { prefilter: prefilter2, postAgg: postAgg2, hasChildError: hasChildError2 } = partitionAnchorFilters(filterRecord);
3098
- const { clause: prefilterClause2, params: prefilterParams2 } = buildWhereClause(prefilter2);
3099
- const prefilterParts2 = [
3100
- `eventType = 'start'`,
3101
- `parentSpanId IS NULL`,
3102
- `cursorId IS NOT NULL`,
3103
- `cursorId > CAST(? AS BIGINT)`
3104
- ];
3105
- if (prefilterClause2) prefilterParts2.push(prefilterClause2.replace(/^WHERE\s+/i, ""));
3106
- const prefilterWhere2 = `WHERE ${prefilterParts2.join(" AND ")}`;
3107
- const { clause: postAggClause2, params: postAggParams2 } = buildWhereClause(postAgg2);
3108
- const postAggParts2 = [];
3109
- if (postAggClause2) postAggParts2.push(postAggClause2.replace(/^WHERE\s+/i, ""));
3110
- const childErrorClause2 = buildHasChildErrorClause(hasChildError2, "root_spans");
3111
- if (childErrorClause2) postAggParts2.push(childErrorClause2);
3112
- const postAggWhere2 = postAggParts2.length > 0 ? `WHERE ${postAggParts2.join(" AND ")}` : "";
3113
- const outerAlias2 = "outer_root";
3114
- const dataSql2 = `
3115
- WITH candidate_roots AS (
3116
- SELECT traceId, spanId, cursorId
3117
- FROM span_events AS ${outerAlias2}
3118
- ${prefilterWhere2}
3119
- ),
3120
- root_spans AS (
3121
- SELECT reconstructed.*, candidate_roots.cursorId AS anchorCursorId
3122
- FROM (
3123
- ${SPAN_RECONSTRUCT_SELECT}
3124
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
3125
- GROUP BY traceId, spanId
3126
- ) AS reconstructed
3127
- INNER JOIN candidate_roots USING (traceId, spanId)
3128
- )
3129
- SELECT * FROM root_spans ${postAggWhere2} ORDER BY anchorCursorId ASC LIMIT ?
3130
- `;
3131
- const rows2 = await db.query(dataSql2, [
3132
- afterCursorId,
3133
- ...prefilterParams2,
3134
- ...postAggParams2,
3135
- limit + 1
3136
- ]);
3137
- const visibleRows = rows2.slice(0, limit).map((row) => ({
3138
- cursorId: row.anchorCursorId,
3139
- span: rowToSpanRecord(row)
3140
- }));
3141
- return {
3142
- spans: storage.toTraceSpans(visibleRows.map((row) => row.span)),
3143
- delta: { limit, hasMore: rows2.length > limit },
3144
- deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
3145
- };
3146
- }
3147
- const { prefilter, postAgg, hasChildError } = partitionAnchorFilters(filterRecord);
3148
- const { clause: prefilterClause, params: prefilterParams } = buildWhereClause(prefilter);
3149
- const prefilterParts = [`eventType = 'start'`, `parentSpanId IS NULL`];
3150
- if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
3151
- const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
3152
- const outerAlias = "outer_root";
3153
- const orderDir = orderBy.direction.toUpperCase();
3154
- if (orderDir !== "ASC" && orderDir !== "DESC") {
3155
- throw new Error(`Invalid sort direction: ${orderBy.direction}`);
3156
- }
3157
- const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getTraceDeltaCursor(db, filters) : void 0;
3158
- const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
3159
- const hasPostAggFilters = Object.keys(postAgg).length > 0 || hasChildError !== void 0 || !canOrderInPrefilter;
3160
- if (!hasPostAggFilters) {
3161
- const prefilterOrderBy = `ORDER BY timestamp ${orderDir}`;
3162
- const offset = page * perPage;
3163
- const countSql2 = `
3164
- SELECT COUNT(*) as total
3165
- FROM span_events AS ${outerAlias}
3166
- ${prefilterWhere}
3167
- `;
3168
- const countResult2 = await db.query(countSql2, prefilterParams);
3169
- const total2 = Number(countResult2[0]?.total ?? 0);
3170
- const pageSql = `
3171
- WITH page_roots AS (
3172
- SELECT traceId, spanId
3173
- FROM span_events AS ${outerAlias}
3174
- ${prefilterWhere}
3175
- ${prefilterOrderBy}
3176
- LIMIT ? OFFSET ?
3177
- )
3178
- ${SPAN_RECONSTRUCT_SELECT}
3179
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM page_roots)
3180
- GROUP BY traceId, spanId
3181
- ${buildOrderByClause(orderBy)}
3182
- `;
3183
- const rows2 = await db.query(pageSql, [...prefilterParams, perPage, offset]);
3184
- const spans2 = rows2.map((row) => rowToSpanRecord(row));
3185
- return {
3186
- pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
3187
- spans: storage.toTraceSpans(spans2),
3188
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
3189
- };
3190
- }
3191
- const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
3192
- const postAggParts = [];
3193
- if (postAggClause) postAggParts.push(postAggClause.replace(/^WHERE\s+/i, ""));
3194
- const childErrorClause = buildHasChildErrorClause(hasChildError, "root_spans");
3195
- if (childErrorClause) postAggParts.push(childErrorClause);
3196
- const postAggWhere = postAggParts.length > 0 ? `WHERE ${postAggParts.join(" AND ")}` : "";
3197
- const cteSql = `
3198
- WITH candidate_roots AS (
3199
- SELECT traceId, spanId
3200
- FROM span_events AS ${outerAlias}
3201
- ${prefilterWhere}
3202
- ),
3203
- root_spans AS (
3204
- ${SPAN_RECONSTRUCT_SELECT}
3205
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
3206
- GROUP BY traceId, spanId
3207
- )
3208
- `;
3209
- const orderByClause = buildOrderByClause(orderBy);
3210
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
3211
- const countSql = `
3212
- ${cteSql}
3213
- SELECT COUNT(*) as total FROM root_spans ${postAggWhere}
3214
- `;
3215
- const countResult = await db.query(countSql, [...prefilterParams, ...postAggParams]);
3216
- const total = Number(countResult[0]?.total ?? 0);
3217
- const dataSql = `
3218
- ${cteSql}
3219
- SELECT * FROM root_spans ${postAggWhere} ${orderByClause} ${paginationClause}
3220
- `;
3221
- const rows = await db.query(dataSql, [...prefilterParams, ...postAggParams, ...paginationParams]);
3222
- const spans = rows.map((row) => rowToSpanRecord(row));
3223
- return {
3224
- pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
3225
- spans: storage.toTraceSpans(spans),
3226
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
3227
- };
3228
- }
3229
- async function listTracesLight(db, args) {
3230
- return listTraceRows(
3231
- db,
3232
- args,
3233
- SPAN_RECONSTRUCT_SELECT_LIGHT,
3234
- rowToLightSpanRecord,
3235
- (spans) => spans
3236
- );
3237
- }
3238
- var BRANCH_SPAN_TYPE_PLACEHOLDERS = storage.BRANCH_SPAN_TYPES.map(() => "?").join(", ");
3239
- async function getSpans(db, args) {
3240
- if (args.spanIds.length === 0) {
3241
- return { traceId: args.traceId, spans: [] };
3242
- }
3243
- const placeholders = args.spanIds.map(() => "?").join(", ");
3244
- const rows = await db.query(
3245
- `${SPAN_RECONSTRUCT_SELECT}
3246
- WHERE traceId = ? AND spanId IN (${placeholders})
3247
- GROUP BY traceId, spanId`,
3248
- [args.traceId, ...args.spanIds]
3249
- );
3250
- return {
3251
- traceId: args.traceId,
3252
- spans: rows.map((row) => rowToSpanRecord(row))
3253
- };
3254
- }
3255
- async function listBranches(db, args) {
3256
- const { mode, filters, pagination, orderBy, after, limit } = storage.listBranchesArgsSchema.parse(args);
3257
- const filterRecord = filters ?? {};
3258
- const page = Number(pagination.page);
3259
- const perPage = Number(pagination.perPage);
3260
- const userSpanType = filterRecord.spanType;
3261
- if (typeof userSpanType === "string" && !storage.BRANCH_SPAN_TYPES.includes(userSpanType)) {
3262
- const currentDeltaCursor2 = deltaPollingFeatureEnabled() ? await getBranchDeltaCursor(db, filters) : void 0;
3263
- if (mode === "delta") {
3264
- assertDeltaPollingEnabled();
3265
- return {
3266
- branches: [],
3267
- delta: { limit, hasMore: false },
3268
- deltaCursor: currentDeltaCursor2
3269
- };
3270
- }
3271
- return {
3272
- pagination: { total: 0, page, perPage, hasMore: false },
3273
- branches: [],
3274
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor2 } : {}
3275
- };
3276
- }
3277
- if (mode === "delta") {
3278
- assertDeltaPollingEnabled();
3279
- const streamHeadCursor = await getBranchStreamHeadCursor(
3280
- db,
3281
- typeof userSpanType === "string" ? userSpanType : null
3282
- );
3283
- if (after === void 0) {
3284
- return {
3285
- branches: [],
3286
- delta: { limit, hasMore: false },
3287
- deltaCursor: streamHeadCursor
3288
- };
3289
- }
3290
- const afterCursorId = validateCursorId(after);
3291
- const { spanType: _spanType2, ...rest2 } = filterRecord;
3292
- const { prefilter: prefilter2, postAgg: postAgg2} = partitionAnchorFilters(rest2);
3293
- const { clause: prefilterClause2, params: prefilterFilterParams2 } = buildWhereClause(prefilter2);
3294
- const prefilterParts2 = [`eventType = 'start'`, `cursorId IS NOT NULL`, `cursorId > CAST(? AS BIGINT)`];
3295
- let spanTypeParams2;
3296
- if (typeof userSpanType === "string") {
3297
- prefilterParts2.push(`spanType = ?`);
3298
- spanTypeParams2 = [userSpanType];
3299
- } else {
3300
- prefilterParts2.push(`spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`);
3301
- spanTypeParams2 = [...storage.BRANCH_SPAN_TYPES];
3302
- }
3303
- if (prefilterClause2) prefilterParts2.push(prefilterClause2.replace(/^WHERE\s+/i, ""));
3304
- const prefilterWhere2 = `WHERE ${prefilterParts2.join(" AND ")}`;
3305
- const prefilterParams2 = [afterCursorId, ...spanTypeParams2, ...prefilterFilterParams2];
3306
- const { clause: postAggClause2, params: postAggParams2 } = buildWhereClause(postAgg2);
3307
- const postAggWhere2 = postAggClause2 ? postAggClause2 : "";
3308
- const outerAlias2 = "outer_anchor";
3309
- const dataSql2 = `
3310
- WITH candidate_anchors AS (
3311
- SELECT traceId, spanId, cursorId
3312
- FROM span_events AS ${outerAlias2}
3313
- ${prefilterWhere2}
3314
- ),
3315
- branch_anchors AS (
3316
- SELECT reconstructed.*, candidate_anchors.cursorId AS anchorCursorId
3317
- FROM (
3318
- ${SPAN_RECONSTRUCT_SELECT}
3319
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_anchors)
3320
- GROUP BY traceId, spanId
3321
- ) AS reconstructed
3322
- INNER JOIN candidate_anchors USING (traceId, spanId)
3323
- )
3324
- SELECT * FROM branch_anchors ${postAggWhere2} ORDER BY anchorCursorId ASC LIMIT ?
3325
- `;
3326
- const rows2 = await db.query(dataSql2, [...prefilterParams2, ...postAggParams2, limit + 1]);
3327
- const visibleRows = rows2.slice(0, limit).map((row) => ({
3328
- cursorId: row.anchorCursorId,
3329
- branch: rowToSpanRecord(row)
3330
- }));
3331
- return {
3332
- branches: storage.toTraceSpans(visibleRows.map((row) => row.branch)),
3333
- delta: { limit, hasMore: rows2.length > limit },
3334
- deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
3335
- };
3336
- }
3337
- const { spanType: _spanType, ...rest } = filterRecord;
3338
- const { prefilter, postAgg} = partitionAnchorFilters(rest);
3339
- const { clause: prefilterClause, params: prefilterFilterParams } = buildWhereClause(prefilter);
3340
- const prefilterParts = [`eventType = 'start'`];
3341
- let spanTypeParams;
3342
- if (typeof userSpanType === "string") {
3343
- prefilterParts.push(`spanType = ?`);
3344
- spanTypeParams = [userSpanType];
3345
- } else {
3346
- prefilterParts.push(`spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`);
3347
- spanTypeParams = [...storage.BRANCH_SPAN_TYPES];
3348
- }
3349
- if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
3350
- const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
3351
- const prefilterParams = [...spanTypeParams, ...prefilterFilterParams];
3352
- const outerAlias = "outer_anchor";
3353
- const orderDir = orderBy.direction.toUpperCase();
3354
- if (orderDir !== "ASC" && orderDir !== "DESC") {
3355
- throw new Error(`Invalid sort direction: ${orderBy.direction}`);
3356
- }
3357
- const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getBranchDeltaCursor(db, filters) : void 0;
3358
- const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
3359
- const hasPostAggFilters = Object.keys(postAgg).length > 0 || !canOrderInPrefilter;
3360
- if (!hasPostAggFilters) {
3361
- const prefilterOrderBy = `ORDER BY timestamp ${orderDir}`;
3362
- const offset = page * perPage;
3363
- const countSql2 = `
3364
- SELECT COUNT(*) as total
3365
- FROM span_events AS ${outerAlias}
3366
- ${prefilterWhere}
3367
- `;
3368
- const countResult2 = await db.query(countSql2, prefilterParams);
3369
- const total2 = Number(countResult2[0]?.total ?? 0);
3370
- if (total2 === 0) {
3371
- return {
3372
- pagination: { total: 0, page, perPage, hasMore: false },
3373
- branches: [],
3374
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
3375
- };
3376
- }
3377
- const pageSql = `
3378
- WITH page_anchors AS (
3379
- SELECT traceId, spanId
3380
- FROM span_events AS ${outerAlias}
3381
- ${prefilterWhere}
3382
- ${prefilterOrderBy}
3383
- LIMIT ? OFFSET ?
3384
- )
3385
- ${SPAN_RECONSTRUCT_SELECT}
3386
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM page_anchors)
3387
- GROUP BY traceId, spanId
3388
- ${buildOrderByClause(orderBy)}
3389
- `;
3390
- const rows2 = await db.query(pageSql, [...prefilterParams, perPage, offset]);
3391
- const spans2 = rows2.map((row) => rowToSpanRecord(row));
3392
- return {
3393
- pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
3394
- branches: storage.toTraceSpans(spans2),
3395
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
3396
- };
3397
- }
3398
- const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
3399
- const postAggWhere = postAggClause ? postAggClause : "";
3400
- const orderByClause = buildOrderByClause(orderBy);
3401
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
3402
- const cteSql = `
3403
- WITH candidate_anchors AS (
3404
- SELECT traceId, spanId
3405
- FROM span_events AS ${outerAlias}
3406
- ${prefilterWhere}
3407
- ),
3408
- branch_anchors AS (
3409
- ${SPAN_RECONSTRUCT_SELECT}
3410
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_anchors)
3411
- GROUP BY traceId, spanId
3412
- )
3413
- `;
3414
- const countSql = `
3415
- ${cteSql}
3416
- SELECT COUNT(*) as total FROM branch_anchors ${postAggWhere}
3417
- `;
3418
- const countResult = await db.query(countSql, [...prefilterParams, ...postAggParams]);
3419
- const total = Number(countResult[0]?.total ?? 0);
3420
- if (total === 0) {
3421
- return {
3422
- pagination: { total: 0, page, perPage, hasMore: false },
3423
- branches: [],
3424
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
3425
- };
3426
- }
3427
- const dataSql = `
3428
- ${cteSql}
3429
- SELECT * FROM branch_anchors ${postAggWhere} ${orderByClause} ${paginationClause}
3430
- `;
3431
- const rows = await db.query(dataSql, [...prefilterParams, ...postAggParams, ...paginationParams]);
3432
- const spans = rows.map((row) => rowToSpanRecord(row));
3433
- return {
3434
- pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
3435
- branches: storage.toTraceSpans(spans),
3436
- ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
3437
- };
3438
- }
3439
- async function getTraceDeltaCursor(db, filters) {
3440
- const { prefilter, postAgg, hasChildError } = partitionAnchorFilters(filters ?? {});
3441
- const { clause: prefilterClause, params: prefilterParams } = buildWhereClause(prefilter);
3442
- const prefilterParts = [`eventType = 'start'`, `parentSpanId IS NULL`, `cursorId IS NOT NULL`];
3443
- if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
3444
- const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
3445
- const outerAlias = "outer_root";
3446
- const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
3447
- const postAggParts = [];
3448
- if (postAggClause) postAggParts.push(postAggClause.replace(/^WHERE\s+/i, ""));
3449
- const childErrorClause = buildHasChildErrorClause(hasChildError, "root_spans");
3450
- if (childErrorClause) postAggParts.push(childErrorClause);
3451
- const postAggWhere = postAggParts.length > 0 ? `WHERE ${postAggParts.join(" AND ")}` : "";
3452
- if (postAggWhere === "") {
3453
- const rows2 = await db.query(
3454
- `SELECT max(cursorId) AS cursorId FROM span_events AS ${outerAlias} ${prefilterWhere}`,
3455
- prefilterParams
3456
- );
3457
- const cursorId2 = rows2[0]?.cursorId;
3458
- if (cursorId2 !== null && cursorId2 !== void 0) {
3459
- return encodeDeltaCursor(cursorId2);
3460
- }
3461
- const streamRows2 = await db.query(
3462
- `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
3463
- );
3464
- return encodeDeltaCursor(streamRows2[0]?.cursorId);
3465
- }
3466
- const cteSql = `
3467
- WITH candidate_roots AS (
3468
- SELECT traceId, spanId, cursorId
3469
- FROM span_events AS ${outerAlias}
3470
- ${prefilterWhere}
3471
- ),
3472
- root_spans AS (
3473
- SELECT reconstructed.*, candidate_roots.cursorId AS anchorCursorId
3474
- FROM (
3475
- ${SPAN_RECONSTRUCT_SELECT}
3476
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
3477
- GROUP BY traceId, spanId
3478
- ) AS reconstructed
3479
- INNER JOIN candidate_roots USING (traceId, spanId)
3480
- )
3481
- `;
3482
- const rows = await db.query(
3483
- `${cteSql} SELECT max(anchorCursorId) AS cursorId FROM root_spans ${postAggWhere}`,
3484
- [...prefilterParams, ...postAggParams]
3485
- );
3486
- const cursorId = rows[0]?.cursorId;
3487
- if (cursorId !== null && cursorId !== void 0) {
3488
- return encodeDeltaCursor(cursorId);
3489
- }
3490
- const streamRows = await db.query(
3491
- `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
3492
- );
3493
- return encodeDeltaCursor(streamRows[0]?.cursorId);
3494
- }
3495
- async function getTraceStreamHeadCursor(db) {
3496
- const streamRows = await db.query(
3497
- `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
3498
- );
3499
- return encodeDeltaCursor(streamRows[0]?.cursorId);
3500
- }
3501
- async function getBranchDeltaCursor(db, filters) {
3502
- const filterRecord = filters ?? {};
3503
- const userSpanType = filterRecord.spanType;
3504
- const { spanType: _spanType, ...rest } = filterRecord;
3505
- const { prefilter, postAgg } = partitionAnchorFilters(rest);
3506
- const { clause: prefilterClause, params: prefilterFilterParams } = buildWhereClause(prefilter);
3507
- const prefilterParts = [`eventType = 'start'`, `cursorId IS NOT NULL`];
3508
- let spanTypeParams;
3509
- if (typeof userSpanType === "string") {
3510
- prefilterParts.push(`spanType = ?`);
3511
- spanTypeParams = [userSpanType];
3512
- } else {
3513
- prefilterParts.push(`spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`);
3514
- spanTypeParams = [...storage.BRANCH_SPAN_TYPES];
3515
- }
3516
- if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
3517
- const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
3518
- const prefilterParams = [...spanTypeParams, ...prefilterFilterParams];
3519
- const outerAlias = "outer_anchor";
3520
- const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
3521
- if (!postAggClause) {
3522
- const rows2 = await db.query(
3523
- `SELECT max(cursorId) AS cursorId FROM span_events AS ${outerAlias} ${prefilterWhere}`,
3524
- prefilterParams
3525
- );
3526
- const cursorId2 = rows2[0]?.cursorId;
3527
- if (cursorId2 !== null && cursorId2 !== void 0) {
3528
- return encodeDeltaCursor(cursorId2);
3529
- }
3530
- const streamRows2 = await db.query(
3531
- `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
3532
- [...storage.BRANCH_SPAN_TYPES]
3533
- );
3534
- return encodeDeltaCursor(streamRows2[0]?.cursorId);
3535
- }
3536
- const cteSql = `
3537
- WITH candidate_anchors AS (
3538
- SELECT traceId, spanId, cursorId
3539
- FROM span_events AS ${outerAlias}
3540
- ${prefilterWhere}
3541
- ),
3542
- branch_anchors AS (
3543
- SELECT reconstructed.*, candidate_anchors.cursorId AS anchorCursorId
3544
- FROM (
3545
- ${SPAN_RECONSTRUCT_SELECT}
3546
- WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_anchors)
3547
- GROUP BY traceId, spanId
3548
- ) AS reconstructed
3549
- INNER JOIN candidate_anchors USING (traceId, spanId)
3550
- )
3551
- `;
3552
- const rows = await db.query(
3553
- `${cteSql} SELECT max(anchorCursorId) AS cursorId FROM branch_anchors ${postAggClause}`,
3554
- [...prefilterParams, ...postAggParams]
3555
- );
3556
- const cursorId = rows[0]?.cursorId;
3557
- if (cursorId !== null && cursorId !== void 0) {
3558
- return encodeDeltaCursor(cursorId);
3559
- }
3560
- const streamRows = await db.query(
3561
- `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
3562
- [...storage.BRANCH_SPAN_TYPES]
3563
- );
3564
- return encodeDeltaCursor(streamRows[0]?.cursorId);
3565
- }
3566
- async function getBranchStreamHeadCursor(db, userSpanType) {
3567
- if (userSpanType) {
3568
- const rows2 = await db.query(
3569
- `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType = ? AND cursorId IS NOT NULL`,
3570
- [userSpanType]
3571
- );
3572
- return encodeDeltaCursor(rows2[0]?.cursorId);
3573
- }
3574
- const rows = await db.query(
3575
- `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
3576
- [...storage.BRANCH_SPAN_TYPES]
3577
- );
3578
- return encodeDeltaCursor(rows[0]?.cursorId);
3579
- }
3580
-
3581
- // src/storage/domains/observability/index.ts
3582
- function buildSignalMigrationRequiredMessage(args) {
3583
- const tableList = args.tables.map((table) => ` - ${table.table}`).join("\n");
3584
- return `
3585
- ===========================================================================
3586
- MIGRATION REQUIRED: DuckDB observability signal tables need signal IDs
3587
- ===========================================================================
3588
-
3589
- The following signal tables still use the legacy schema and must be migrated
3590
- before observability storage can initialize:
3591
-
3592
- ${tableList}
3593
-
3594
- To fix this, run the manual migration command:
3595
-
3596
- npx mastra migrate
3597
-
3598
- This command will:
3599
- 1. Create replacement signal tables with signal-ID primary keys
3600
- 2. Backfill missing signal IDs for legacy rows
3601
- 3. Swap the migrated tables into place
3602
-
3603
- WARNING: This migration recreates the signal tables and may take significant
3604
- time for large databases. Please ensure you have a backup before proceeding.
3605
- ===========================================================================
3606
- `;
3607
- }
3608
- var ObservabilityStorageDuckDB = class extends storage.ObservabilityStorage {
3609
- db;
3610
- constructor(config) {
3611
- super();
3612
- this.db = config.db;
3613
- }
3614
- /** Create all observability tables if they don't exist. */
3615
- async init() {
3616
- const migrationStatus = await checkSignalTablesMigrationStatus(this.db);
3617
- if (migrationStatus.needsMigration) {
3618
- throw new error.MastraError({
3619
- id: storage.createStorageErrorId("DUCKDB", "MIGRATION_REQUIRED", "SIGNAL_TABLES"),
3620
- domain: error.ErrorDomain.STORAGE,
3621
- category: error.ErrorCategory.USER,
3622
- text: buildSignalMigrationRequiredMessage({
3623
- tables: migrationStatus.tables.map(({ table }) => ({ table }))
3624
- })
3625
- });
3626
- }
3627
- await this.db.executeBatch([...ALL_DDL, ...ALL_MIGRATIONS]);
3628
- await dropLegacyCursorIdDefaults(this.db);
3629
- }
3630
- /**
3631
- * Manually migrate legacy signal tables to the signal-ID primary-key schema.
3632
- * The public method name is historical; the CLI still calls `migrateSpans()`
3633
- * for observability migrations even though this now also migrates signal tables.
3634
- */
3635
- async migrateSpans() {
3636
- const migrationStatus = await checkSignalTablesMigrationStatus(this.db);
3637
- if (!migrationStatus.needsMigration) {
3638
- return {
3639
- success: true,
3640
- alreadyMigrated: true,
3641
- duplicatesRemoved: 0,
3642
- message: "Migration already complete. Signal tables already use signal-ID primary keys."
3643
- };
3644
- }
3645
- await migrateSignalTables(this.db, this.logger);
3646
- return {
3647
- success: true,
3648
- alreadyMigrated: false,
3649
- duplicatesRemoved: 0,
3650
- message: `Migration complete. Migrated signal tables: ${migrationStatus.tables.map((t) => t.table).join(", ")}.`
3651
- };
3652
- }
3653
- /** Delete all rows from every observability table. Use with caution. */
3654
- async dangerouslyClearAll() {
3655
- for (const table of ["span_events", "metric_events", "log_events", "score_events", "feedback_events"]) {
3656
- await this.db.execute(`TRUNCATE TABLE ${table}`);
3657
- }
3658
- }
3659
- get observabilityStrategy() {
3660
- return {
3661
- preferred: "event-sourced",
3662
- supported: ["event-sourced"]
3663
- };
3664
- }
3665
- getFeatures() {
3666
- if (!deltaPollingFeatureEnabled()) {
3667
- return void 0;
3668
- }
3669
- return ["delta-polling"];
3670
- }
3671
- // Tracing
3672
- async createSpan(args) {
3673
- return createSpan(this.db, args);
3674
- }
3675
- async batchCreateSpans(args) {
3676
- return batchCreateSpans(this.db, args);
3677
- }
3678
- async batchDeleteTraces(args) {
3679
- return batchDeleteTraces(this.db, args);
3680
- }
3681
- async getSpan(args) {
3682
- return getSpan(this.db, args);
3683
- }
3684
- async getSpans(args) {
3685
- return getSpans(this.db, args);
3686
- }
3687
- async getRootSpan(args) {
3688
- return getRootSpan(this.db, args);
3689
- }
3690
- async getTrace(args) {
3691
- return getTrace(this.db, args);
3692
- }
3693
- async getTraceLight(args) {
3694
- return getTraceLight(this.db, args);
3695
- }
3696
- async listTraces(args) {
3697
- return listTraces(this.db, args);
3698
- }
3699
- async listTracesLight(args) {
3700
- return listTracesLight(this.db, args);
3701
- }
3702
- async listBranches(args) {
3703
- return listBranches(this.db, args);
3704
- }
3705
- // Logs
3706
- async batchCreateLogs(args) {
3707
- return batchCreateLogs(this.db, args);
3708
- }
3709
- async listLogs(args) {
3710
- return listLogs(this.db, args);
3711
- }
3712
- // Metrics
3713
- async batchCreateMetrics(args) {
3714
- return batchCreateMetrics(this.db, args);
3715
- }
3716
- async listMetrics(args) {
3717
- return listMetrics(this.db, args);
3718
- }
3719
- async getMetricAggregate(args) {
3720
- return getMetricAggregate(this.db, args);
3721
- }
3722
- async getMetricBreakdown(args) {
3723
- return getMetricBreakdown(this.db, args);
3724
- }
3725
- async getMetricTimeSeries(args) {
3726
- return getMetricTimeSeries(this.db, args);
3727
- }
3728
- async getMetricPercentiles(args) {
3729
- return getMetricPercentiles(this.db, args);
3730
- }
3731
- // Metric Discovery
3732
- async getMetricNames(args) {
3733
- return getMetricNames(this.db, args);
3734
- }
3735
- async getMetricLabelKeys(args) {
3736
- return getMetricLabelKeys(this.db, args);
3737
- }
3738
- async getMetricLabelValues(args) {
3739
- return getMetricLabelValues(this.db, args);
3740
- }
3741
- // Span Discovery
3742
- async getEntityTypes(args) {
3743
- return getEntityTypes(this.db);
3744
- }
3745
- async getEntityNames(args) {
3746
- return getEntityNames(this.db, args);
3747
- }
3748
- async getServiceNames(args) {
3749
- return getServiceNames(this.db);
3750
- }
3751
- async getEnvironments(args) {
3752
- return getEnvironments(this.db);
3753
- }
3754
- async getTags(args) {
3755
- return getTags(this.db, args);
3756
- }
3757
- // Scores
3758
- async createScore(args) {
3759
- return createScore(this.db, args);
3760
- }
3761
- async batchCreateScores(args) {
3762
- return batchCreateScores(this.db, args);
3763
- }
3764
- async listScores(args) {
3765
- return listScores(this.db, args);
3766
- }
3767
- async getScoreById(scoreId) {
3768
- return getScoreById(this.db, scoreId);
3769
- }
3770
- async getScoreAggregate(args) {
3771
- return getScoreAggregate(this.db, args);
3772
- }
3773
- async getScoreBreakdown(args) {
3774
- return getScoreBreakdown(this.db, args);
3775
- }
3776
- async getScoreTimeSeries(args) {
3777
- return getScoreTimeSeries(this.db, args);
3778
- }
3779
- async getScorePercentiles(args) {
3780
- return getScorePercentiles(this.db, args);
3781
- }
3782
- // Feedback
3783
- async createFeedback(args) {
3784
- return createFeedback(this.db, args);
3785
- }
3786
- async batchCreateFeedback(args) {
3787
- return batchCreateFeedback(this.db, args);
3788
- }
3789
- async listFeedback(args) {
3790
- return listFeedback(this.db, args);
3791
- }
3792
- async getFeedbackAggregate(args) {
3793
- return getFeedbackAggregate(this.db, args);
3794
- }
3795
- async getFeedbackBreakdown(args) {
3796
- return getFeedbackBreakdown(this.db, args);
3797
- }
3798
- async getFeedbackTimeSeries(args) {
3799
- return getFeedbackTimeSeries(this.db, args);
3800
- }
3801
- async getFeedbackPercentiles(args) {
3802
- return getFeedbackPercentiles(this.db, args);
3803
- }
3804
- };
3805
-
3806
- exports.ObservabilityStorageDuckDB = ObservabilityStorageDuckDB;
3807
- //# sourceMappingURL=observability-V2KYD7UF.cjs.map
3808
- //# sourceMappingURL=observability-V2KYD7UF.cjs.map