@mastra/datadog 1.4.15 → 1.4.16-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge.d.ts +24 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/index.cjs +94 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +94 -4
- package/dist/index.js.map +1 -1
- package/dist/tracing.d.ts +22 -0
- package/dist/tracing.d.ts.map +1 -1
- package/dist/utils.d.ts +11 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/bridge.d.ts
CHANGED
|
@@ -70,6 +70,15 @@ export interface DatadogBridgeConfig extends BaseExporterConfig {
|
|
|
70
70
|
* LLM Observability tags instead of being nested in annotations.metadata.
|
|
71
71
|
*/
|
|
72
72
|
requestContextKeys?: string[];
|
|
73
|
+
/**
|
|
74
|
+
* How long `flush()` waits, in milliseconds, after handing data to dd-trace.
|
|
75
|
+
* dd-trace sends LLM Observability spans and evaluations from a background
|
|
76
|
+
* writer and offers no way to await that request, so the wait keeps a process
|
|
77
|
+
* that exits right after `flush()` from cutting the send off. The wait only
|
|
78
|
+
* happens when something was sent since the last flush. Set to 0 to disable.
|
|
79
|
+
* Defaults to 2000.
|
|
80
|
+
*/
|
|
81
|
+
flushGraceMs?: number;
|
|
73
82
|
}
|
|
74
83
|
/**
|
|
75
84
|
* Datadog Bridge for Mastra Observability.
|
|
@@ -82,8 +91,15 @@ export declare class DatadogBridge extends BaseExporter implements Observability
|
|
|
82
91
|
name: string;
|
|
83
92
|
private config;
|
|
84
93
|
private ddSpanMap;
|
|
94
|
+
/** Keys of the links each dd span was started with, so links set later are added once at finish. */
|
|
95
|
+
private startLinkKeys;
|
|
85
96
|
private traceContext;
|
|
86
97
|
private openSpanCounts;
|
|
98
|
+
private flushGraceMs;
|
|
99
|
+
/** True when spans or evaluations were handed to dd-trace since the last flush() */
|
|
100
|
+
private pendingSend;
|
|
101
|
+
/** Grace wait in progress, shared by flush() calls that overlap it */
|
|
102
|
+
private graceWait;
|
|
87
103
|
constructor(config?: DatadogBridgeConfig);
|
|
88
104
|
/**
|
|
89
105
|
* Create a dd-trace span eagerly when a Mastra span is constructed.
|
|
@@ -124,6 +140,14 @@ export declare class DatadogBridge extends BaseExporter implements Observability
|
|
|
124
140
|
private buildAnnotations;
|
|
125
141
|
private setErrorTags;
|
|
126
142
|
flush(): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* `llmobs.flush()` returns before the background writer's HTTP request
|
|
145
|
+
* completes and dd-trace gives nothing to await, so wait a bounded time
|
|
146
|
+
* instead. Starts one wait per batch of new data, so the repeated flushes
|
|
147
|
+
* during shutdown don't stack. A flush that overlaps a running wait joins
|
|
148
|
+
* it, so no caller resolves before the data it produced had its wait.
|
|
149
|
+
*/
|
|
150
|
+
private waitForBackgroundSend;
|
|
127
151
|
shutdown(): Promise<void>;
|
|
128
152
|
}
|
|
129
153
|
//# sourceMappingURL=bridge.d.ts.map
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,YAAY,EAKZ,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,QAAQ,EACR,OAAO,EACR,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAuB,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AA+DhE;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,YAAY,EAKZ,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,QAAQ,EACR,OAAO,EACR,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,YAAY,EAAuB,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AA+DhE;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE9B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,YAAa,YAAW,mBAAmB;IAC5E,IAAI,SAAoB;IAExB,OAAO,CAAC,MAAM,CAA8E;IAC5F,OAAO,CAAC,SAAS,CAA0B;IAC3C,oGAAoG;IACpG,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,YAAY,CAAS;IAC7B,oFAAoF;IACpF,OAAO,CAAC,WAAW,CAAS;IAC5B,sEAAsE;IACtE,OAAO,CAAC,SAAS,CAA8B;IAE/C,YAAY,MAAM,GAAE,mBAAwB,EA+C3C;IAED;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAAG,OAAO,GAAG,SAAS,CA8EpE;IAED;;OAEG;IACH,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEpE;IAED;;OAEG;IACH,oBAAoB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEtD;IAEK,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCnD;IAED,OAAO,CAAC,sBAAsB;IAmB9B;;OAEG;IACH,UAAgB,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BtE;IAED,OAAO,CAAC,kBAAkB;IA0C1B,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAqE7B;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAIjD;IAED,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,gBAAgB;IAiGxB,OAAO,CAAC,YAAY;IASd,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAoB3B;IAED;;;;;;OAMG;YACW,qBAAqB;IAY7B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CA2B9B;CACF"}
|
package/dist/index.cjs
CHANGED
|
@@ -83,6 +83,20 @@ function formatUsageMetrics(usage) {
|
|
|
83
83
|
* Utility functions for Datadog LLM Observability Exporter
|
|
84
84
|
*/
|
|
85
85
|
/**
|
|
86
|
+
* Datadog span links for Mastra span links, such as the caller of a served MCP
|
|
87
|
+
* request. Each link is resolved from a W3C `traceparent`, so only full-length
|
|
88
|
+
* W3C IDs are kept, and links dd-trace can't resolve are skipped.
|
|
89
|
+
*/
|
|
90
|
+
function toDatadogLinks(links) {
|
|
91
|
+
const ddLinks = [];
|
|
92
|
+
for (const link of links ?? []) {
|
|
93
|
+
if (!/^[0-9a-f]{32}$/.test(link.traceId) || !/^[0-9a-f]{16}$/.test(link.spanId)) continue;
|
|
94
|
+
const context = dd_trace.default.extract("text_map", { traceparent: `00-${link.traceId}-${link.spanId}-01` });
|
|
95
|
+
if (context) ddLinks.push({ context });
|
|
96
|
+
}
|
|
97
|
+
return ddLinks;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
86
100
|
* Maps Mastra SpanTypes to Datadog LLMObs span kinds for the legacy hierarchy
|
|
87
101
|
* (no `model-inference-span` feature). MODEL_STEP is the actual API call here
|
|
88
102
|
* because MODEL_INFERENCE doesn't exist.
|
|
@@ -333,10 +347,18 @@ var DatadogBridge = class extends _mastra_observability.BaseExporter {
|
|
|
333
347
|
name = "datadog-bridge";
|
|
334
348
|
config;
|
|
335
349
|
ddSpanMap = /* @__PURE__ */ new Map();
|
|
350
|
+
/** Keys of the links each dd span was started with, so links set later are added once at finish. */
|
|
351
|
+
startLinkKeys = /* @__PURE__ */ new WeakMap();
|
|
336
352
|
traceContext = /* @__PURE__ */ new Map();
|
|
337
353
|
openSpanCounts = /* @__PURE__ */ new Map();
|
|
354
|
+
flushGraceMs;
|
|
355
|
+
/** True when spans or evaluations were handed to dd-trace since the last flush() */
|
|
356
|
+
pendingSend = false;
|
|
357
|
+
/** Grace wait in progress, shared by flush() calls that overlap it */
|
|
358
|
+
graceWait = null;
|
|
338
359
|
constructor(config = {}) {
|
|
339
360
|
super(config);
|
|
361
|
+
this.flushGraceMs = config.flushGraceMs ?? 2e3;
|
|
340
362
|
const mlApp = config.mlApp ?? process.env.DD_LLMOBS_ML_APP;
|
|
341
363
|
const apiKey = config.apiKey ?? process.env.DD_API_KEY;
|
|
342
364
|
const site = config.site ?? process.env.DD_SITE ?? "datadoghq.com";
|
|
@@ -396,10 +418,13 @@ var DatadogBridge = class extends _mastra_observability.BaseExporter {
|
|
|
396
418
|
if (apmParentDdSpan) parentSource = "active-scope";
|
|
397
419
|
}
|
|
398
420
|
if (!llmobsParentDdSpan && externalParentId) llmobsParentDdSpan = apmParentDdSpan;
|
|
421
|
+
const links = toDatadogLinks(options.links);
|
|
399
422
|
const ddSpan = dd_trace.default.startSpan(options.name, {
|
|
400
423
|
...apmParentDdSpan ? { childOf: apmParentDdSpan } : {},
|
|
401
|
-
...options.startTime ? { startTime: toDate(options.startTime).getTime() } : {}
|
|
424
|
+
...options.startTime ? { startTime: toDate(options.startTime).getTime() } : {},
|
|
425
|
+
...links.length > 0 ? { links } : {}
|
|
402
426
|
});
|
|
427
|
+
if (options.links?.length) this.startLinkKeys.set(ddSpan, new Set(options.links.map((link) => `${link.traceId}:${link.spanId}`)));
|
|
403
428
|
const ddContext = ddSpan.context?.();
|
|
404
429
|
const spanId = ddContext?.toSpanId?.(true) ?? generateSpanId();
|
|
405
430
|
const traceId = ddContext?.toTraceId?.(true) ?? (externalParentId ? options.parent?.traceId ?? generateTraceId() : generateTraceId());
|
|
@@ -440,6 +465,7 @@ var DatadogBridge = class extends _mastra_observability.BaseExporter {
|
|
|
440
465
|
return;
|
|
441
466
|
}
|
|
442
467
|
try {
|
|
468
|
+
this.pendingSend = true;
|
|
443
469
|
dd_trace.default.llmobs.submitEvaluation({
|
|
444
470
|
traceId: score.traceId,
|
|
445
471
|
spanId: toDatadogSpanId(score.spanId)
|
|
@@ -562,7 +588,20 @@ var DatadogBridge = class extends _mastra_observability.BaseExporter {
|
|
|
562
588
|
});
|
|
563
589
|
}
|
|
564
590
|
try {
|
|
565
|
-
|
|
591
|
+
const startKeys = this.startLinkKeys.get(ddSpan);
|
|
592
|
+
const lateLinks = toDatadogLinks(span.links?.filter((link) => !startKeys?.has(`${link.traceId}:${link.spanId}`)));
|
|
593
|
+
if (lateLinks.length > 0 && typeof ddSpan.addLinks === "function") ddSpan.addLinks(lateLinks);
|
|
594
|
+
} catch (error) {
|
|
595
|
+
this.logger.error("[DatadogBridge] Failed to add span links", {
|
|
596
|
+
error,
|
|
597
|
+
spanId: span.id
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
try {
|
|
601
|
+
if (typeof ddSpan.finish === "function") {
|
|
602
|
+
this.pendingSend = true;
|
|
603
|
+
ddSpan.finish(endTime.getTime());
|
|
604
|
+
}
|
|
566
605
|
} catch (error) {
|
|
567
606
|
this.logger.error("[DatadogBridge] Failed to finish dd span", {
|
|
568
607
|
error,
|
|
@@ -667,6 +706,7 @@ var DatadogBridge = class extends _mastra_observability.BaseExporter {
|
|
|
667
706
|
} catch (e) {
|
|
668
707
|
this.logger.error("Error flushing llmobs", { error: e });
|
|
669
708
|
}
|
|
709
|
+
await this.waitForBackgroundSend();
|
|
670
710
|
try {
|
|
671
711
|
await flushApmExporter();
|
|
672
712
|
this.logger.debug("Datadog APM exporter flushed");
|
|
@@ -674,11 +714,32 @@ var DatadogBridge = class extends _mastra_observability.BaseExporter {
|
|
|
674
714
|
this.logger.error("Error flushing Datadog APM exporter", { error: e });
|
|
675
715
|
}
|
|
676
716
|
}
|
|
717
|
+
/**
|
|
718
|
+
* `llmobs.flush()` returns before the background writer's HTTP request
|
|
719
|
+
* completes and dd-trace gives nothing to await, so wait a bounded time
|
|
720
|
+
* instead. Starts one wait per batch of new data, so the repeated flushes
|
|
721
|
+
* during shutdown don't stack. A flush that overlaps a running wait joins
|
|
722
|
+
* it, so no caller resolves before the data it produced had its wait.
|
|
723
|
+
*/
|
|
724
|
+
async waitForBackgroundSend() {
|
|
725
|
+
if (!(this.flushGraceMs > 0) || !Number.isFinite(this.flushGraceMs)) return;
|
|
726
|
+
if (this.pendingSend) {
|
|
727
|
+
this.pendingSend = false;
|
|
728
|
+
const wait = new Promise((resolve) => setTimeout(resolve, this.flushGraceMs)).finally(() => {
|
|
729
|
+
if (this.graceWait === wait) this.graceWait = null;
|
|
730
|
+
});
|
|
731
|
+
this.graceWait = wait;
|
|
732
|
+
}
|
|
733
|
+
await this.graceWait;
|
|
734
|
+
}
|
|
677
735
|
async shutdown() {
|
|
678
736
|
for (const [spanId, ddSpan] of this.ddSpanMap) {
|
|
679
737
|
this.logger.warn(`[DatadogBridge] Force-finishing APM span that was not properly closed [id=${spanId}]`);
|
|
680
738
|
try {
|
|
681
|
-
if (typeof ddSpan.finish === "function")
|
|
739
|
+
if (typeof ddSpan.finish === "function") {
|
|
740
|
+
this.pendingSend = true;
|
|
741
|
+
ddSpan.finish();
|
|
742
|
+
}
|
|
682
743
|
} catch {}
|
|
683
744
|
}
|
|
684
745
|
this.ddSpanMap.clear();
|
|
@@ -743,8 +804,14 @@ var DatadogExporter = class extends _mastra_observability.BaseExporter {
|
|
|
743
804
|
config;
|
|
744
805
|
traceContext = /* @__PURE__ */ new Map();
|
|
745
806
|
traceState = /* @__PURE__ */ new Map();
|
|
807
|
+
flushGraceMs;
|
|
808
|
+
/** True when spans or evaluations were handed to dd-trace since the last flush() */
|
|
809
|
+
pendingSend = false;
|
|
810
|
+
/** Grace wait in progress, shared by flush() calls that overlap it */
|
|
811
|
+
graceWait = null;
|
|
746
812
|
constructor(config = {}) {
|
|
747
813
|
super(config);
|
|
814
|
+
this.flushGraceMs = config.flushGraceMs ?? 2e3;
|
|
748
815
|
const mlApp = config.mlApp ?? process.env.DD_LLMOBS_ML_APP;
|
|
749
816
|
const apiKey = config.apiKey ?? process.env.DD_API_KEY;
|
|
750
817
|
const site = config.site ?? process.env.DD_SITE ?? "datadoghq.com";
|
|
@@ -922,6 +989,7 @@ var DatadogExporter = class extends _mastra_observability.BaseExporter {
|
|
|
922
989
|
return;
|
|
923
990
|
}
|
|
924
991
|
try {
|
|
992
|
+
this.pendingSend = true;
|
|
925
993
|
dd_trace.default.llmobs.submitEvaluation({
|
|
926
994
|
traceId: exported.traceId,
|
|
927
995
|
spanId: exported.spanId
|
|
@@ -956,6 +1024,25 @@ var DatadogExporter = class extends _mastra_observability.BaseExporter {
|
|
|
956
1024
|
} catch (e) {
|
|
957
1025
|
this.logger.error("Error flushing llmobs", { error: e });
|
|
958
1026
|
}
|
|
1027
|
+
await this.waitForBackgroundSend();
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* `llmobs.flush()` returns before the background writer's HTTP request
|
|
1031
|
+
* completes and dd-trace gives nothing to await, so wait a bounded time
|
|
1032
|
+
* instead. Starts one wait per batch of new data, so the repeated flushes
|
|
1033
|
+
* during shutdown don't stack. A flush that overlaps a running wait joins
|
|
1034
|
+
* it, so no caller resolves before the data it produced had its wait.
|
|
1035
|
+
*/
|
|
1036
|
+
async waitForBackgroundSend() {
|
|
1037
|
+
if (!(this.flushGraceMs > 0) || !Number.isFinite(this.flushGraceMs)) return;
|
|
1038
|
+
if (this.pendingSend) {
|
|
1039
|
+
this.pendingSend = false;
|
|
1040
|
+
const wait = new Promise((resolve) => setTimeout(resolve, this.flushGraceMs)).finally(() => {
|
|
1041
|
+
if (this.graceWait === wait) this.graceWait = null;
|
|
1042
|
+
});
|
|
1043
|
+
this.graceWait = wait;
|
|
1044
|
+
}
|
|
1045
|
+
await this.graceWait;
|
|
959
1046
|
}
|
|
960
1047
|
/**
|
|
961
1048
|
* Gracefully shuts down the exporter.
|
|
@@ -1132,6 +1219,7 @@ var DatadogExporter = class extends _mastra_observability.BaseExporter {
|
|
|
1132
1219
|
provider: ownAttrs?.provider ?? inheritedModelAttrs?.provider
|
|
1133
1220
|
};
|
|
1134
1221
|
const startTime = toDate(span.startTime);
|
|
1222
|
+
const links = toDatadogLinks(span.links);
|
|
1135
1223
|
const endTime = span.endTime ? toDate(span.endTime) : span.isEvent ? startTime : /* @__PURE__ */ new Date();
|
|
1136
1224
|
return {
|
|
1137
1225
|
traceOptions: {
|
|
@@ -1141,7 +1229,8 @@ var DatadogExporter = class extends _mastra_observability.BaseExporter {
|
|
|
1141
1229
|
userId: traceCtx.userId,
|
|
1142
1230
|
startTime,
|
|
1143
1231
|
...kind === "llm" && attrs?.model ? { modelName: attrs.model } : {},
|
|
1144
|
-
...kind === "llm" && attrs?.provider ? { modelProvider: attrs.provider } : {}
|
|
1232
|
+
...kind === "llm" && attrs?.provider ? { modelProvider: attrs.provider } : {},
|
|
1233
|
+
...links.length > 0 ? { links } : {}
|
|
1145
1234
|
},
|
|
1146
1235
|
endTimeMs: endTime.getTime()
|
|
1147
1236
|
};
|
|
@@ -1158,6 +1247,7 @@ var DatadogExporter = class extends _mastra_observability.BaseExporter {
|
|
|
1158
1247
|
model: span.attributes?.model,
|
|
1159
1248
|
provider: span.attributes?.provider
|
|
1160
1249
|
} : inheritedModelAttrs;
|
|
1250
|
+
this.pendingSend = true;
|
|
1161
1251
|
dd_trace.default.llmobs.trace(traceOptions, (ddSpan) => {
|
|
1162
1252
|
const annotations = this.buildAnnotations(span);
|
|
1163
1253
|
if (Object.keys(annotations).length > 0) dd_trace.default.llmobs.annotate(ddSpan, annotations);
|