@mastra/datadog 1.1.1-alpha.1 → 1.2.0-alpha.3
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/CHANGELOG.md +22 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/features.d.ts +26 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/index.cjs +92 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +92 -8
- package/dist/index.js.map +1 -1
- package/dist/tracing.d.ts +15 -1
- package/dist/tracing.d.ts.map +1 -1
- package/dist/utils.d.ts +7 -3
- package/dist/utils.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2,8 +2,25 @@ import { SpanType } from '@mastra/core/observability';
|
|
|
2
2
|
import { omitKeys } from '@mastra/core/utils';
|
|
3
3
|
import { BaseExporter, getExternalParentId } from '@mastra/observability';
|
|
4
4
|
import tracer3 from 'dd-trace';
|
|
5
|
+
import { coreFeatures } from '@mastra/core/features';
|
|
5
6
|
|
|
6
7
|
// src/bridge.ts
|
|
8
|
+
var FEATURE = "model-inference-span";
|
|
9
|
+
var observabilityFeatures;
|
|
10
|
+
var featureLoadPromise;
|
|
11
|
+
function loadObservabilityFeatures() {
|
|
12
|
+
if (!featureLoadPromise) {
|
|
13
|
+
featureLoadPromise = import('@mastra/observability').then((mod) => {
|
|
14
|
+
observabilityFeatures = mod.observabilityFeatures;
|
|
15
|
+
}).catch(() => {
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return featureLoadPromise;
|
|
19
|
+
}
|
|
20
|
+
void loadObservabilityFeatures();
|
|
21
|
+
function isModelInferenceEnabled() {
|
|
22
|
+
return observabilityFeatures?.has(FEATURE) === true && coreFeatures.has(FEATURE);
|
|
23
|
+
}
|
|
7
24
|
|
|
8
25
|
// src/metrics.ts
|
|
9
26
|
function formatUsageMetrics(usage) {
|
|
@@ -29,18 +46,26 @@ function formatUsageMetrics(usage) {
|
|
|
29
46
|
}
|
|
30
47
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
31
48
|
}
|
|
32
|
-
var
|
|
49
|
+
var SPAN_TYPE_TO_KIND_LEGACY = {
|
|
33
50
|
[SpanType.AGENT_RUN]: "agent",
|
|
34
|
-
// MODEL_GENERATION is the wrapper around 1..N MODEL_STEPs (the actual API calls).
|
|
35
|
-
// It maps to 'workflow' so Datadog doesn't double-count it as an LLM call.
|
|
36
51
|
[SpanType.MODEL_GENERATION]: "workflow",
|
|
37
|
-
// MODEL_STEP is "Single model execution step within a generation (one API call)"
|
|
38
|
-
// per packages/core/src/observability/types/tracing.ts, so it is the real LLM span.
|
|
39
52
|
[SpanType.MODEL_STEP]: "llm",
|
|
40
53
|
[SpanType.TOOL_CALL]: "tool",
|
|
41
54
|
[SpanType.MCP_TOOL_CALL]: "tool",
|
|
42
55
|
[SpanType.WORKFLOW_RUN]: "workflow"
|
|
43
56
|
};
|
|
57
|
+
var SPAN_TYPE_TO_KIND_INFERENCE = {
|
|
58
|
+
[SpanType.AGENT_RUN]: "agent",
|
|
59
|
+
[SpanType.MODEL_GENERATION]: "workflow",
|
|
60
|
+
[SpanType.MODEL_STEP]: "workflow",
|
|
61
|
+
[SpanType.MODEL_INFERENCE]: "llm",
|
|
62
|
+
[SpanType.TOOL_CALL]: "tool",
|
|
63
|
+
[SpanType.MCP_TOOL_CALL]: "tool",
|
|
64
|
+
[SpanType.WORKFLOW_RUN]: "workflow"
|
|
65
|
+
};
|
|
66
|
+
function getSpanTypeToKind() {
|
|
67
|
+
return isModelInferenceEnabled() ? SPAN_TYPE_TO_KIND_INFERENCE : SPAN_TYPE_TO_KIND_LEGACY;
|
|
68
|
+
}
|
|
44
69
|
var tracerInitFlag = { done: false };
|
|
45
70
|
function ensureTracer(config) {
|
|
46
71
|
if (tracerInitFlag.done) return;
|
|
@@ -66,7 +91,7 @@ function ensureTracer(config) {
|
|
|
66
91
|
tracerInitFlag.done = true;
|
|
67
92
|
}
|
|
68
93
|
function kindFor(spanType) {
|
|
69
|
-
return
|
|
94
|
+
return getSpanTypeToKind()[spanType] || "task";
|
|
70
95
|
}
|
|
71
96
|
function toDate(value) {
|
|
72
97
|
return value instanceof Date ? value : new Date(value);
|
|
@@ -441,7 +466,8 @@ var DatadogBridge = class extends BaseExporter {
|
|
|
441
466
|
if (span.output !== void 0) {
|
|
442
467
|
annotations.outputData = formatOutput(span.output, span.type);
|
|
443
468
|
}
|
|
444
|
-
|
|
469
|
+
const usageSpanType = isModelInferenceEnabled() ? SpanType.MODEL_INFERENCE : SpanType.MODEL_STEP;
|
|
470
|
+
if (span.type === usageSpanType) {
|
|
445
471
|
const usage = span.attributes?.usage;
|
|
446
472
|
const metrics = formatUsageMetrics(usage);
|
|
447
473
|
if (metrics) {
|
|
@@ -707,7 +733,8 @@ var DatadogExporter = class extends BaseExporter {
|
|
|
707
733
|
if (span.output !== void 0) {
|
|
708
734
|
annotations.outputData = formatOutput(span.output, span.type);
|
|
709
735
|
}
|
|
710
|
-
|
|
736
|
+
const usageSpanType = isModelInferenceEnabled() ? SpanType.MODEL_INFERENCE : SpanType.MODEL_STEP;
|
|
737
|
+
if (span.type === usageSpanType) {
|
|
711
738
|
const usage = span.attributes?.usage;
|
|
712
739
|
const metrics = formatUsageMetrics(usage);
|
|
713
740
|
if (metrics) {
|
|
@@ -777,6 +804,63 @@ var DatadogExporter = class extends BaseExporter {
|
|
|
777
804
|
}
|
|
778
805
|
return annotations;
|
|
779
806
|
}
|
|
807
|
+
/**
|
|
808
|
+
* Submit an eval score to Datadog LLM Observability for the matching ddSpan.
|
|
809
|
+
*
|
|
810
|
+
* Ordering constraint: the matching span must have already been emitted to dd-trace
|
|
811
|
+
* (i.e. its `SPAN_ENDED` event must have been processed and the trace tree flushed).
|
|
812
|
+
* On Mastra's normal scoring path this is always true — scorer hooks fire after the
|
|
813
|
+
* scored entity completes, so the root span has ended by the time `onScoreEvent` runs.
|
|
814
|
+
*
|
|
815
|
+
* If a score arrives for an unexported span (either before `SPAN_ENDED` or after the
|
|
816
|
+
* `traceState` entry has been cleaned up), the event is dropped and a warning is logged
|
|
817
|
+
* so the misuse is observable. Scores must therefore only be submitted for spans whose
|
|
818
|
+
* lifecycle has completed.
|
|
819
|
+
*/
|
|
820
|
+
async onScoreEvent(event) {
|
|
821
|
+
if (this.isDisabled || !tracer3.llmobs?.submitEvaluation) return;
|
|
822
|
+
const { score } = event;
|
|
823
|
+
if (!score.traceId || !score.spanId) {
|
|
824
|
+
this.logger.warn("Datadog exporter: dropping score with no traceId/spanId", {
|
|
825
|
+
scorerId: score.scorerId
|
|
826
|
+
});
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
const ctx = this.traceState.get(score.traceId)?.contexts.get(score.spanId);
|
|
830
|
+
const exported = ctx?.exported;
|
|
831
|
+
if (!exported) {
|
|
832
|
+
this.logger.warn(
|
|
833
|
+
"Datadog exporter: dropping score for span that has not been emitted to dd-trace yet (span_ended must be processed before submitting a score for it)",
|
|
834
|
+
{
|
|
835
|
+
traceId: score.traceId,
|
|
836
|
+
spanId: score.spanId,
|
|
837
|
+
scorerId: score.scorerId
|
|
838
|
+
}
|
|
839
|
+
);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
try {
|
|
843
|
+
tracer3.llmobs.submitEvaluation(
|
|
844
|
+
{ traceId: exported.traceId, spanId: exported.spanId },
|
|
845
|
+
{
|
|
846
|
+
label: score.scorerName ?? score.scorerId,
|
|
847
|
+
value: score.score,
|
|
848
|
+
metricType: "score",
|
|
849
|
+
mlApp: this.config.mlApp,
|
|
850
|
+
timestampMs: score.timestamp instanceof Date ? score.timestamp.getTime() : Date.now(),
|
|
851
|
+
...score.reason ? { reasoning: score.reason } : {},
|
|
852
|
+
...score.metadata ? { metadata: score.metadata } : {}
|
|
853
|
+
}
|
|
854
|
+
);
|
|
855
|
+
} catch (err) {
|
|
856
|
+
this.logger.error("Datadog exporter: Failed to submit evaluation", {
|
|
857
|
+
error: err,
|
|
858
|
+
traceId: score.traceId,
|
|
859
|
+
spanId: score.spanId,
|
|
860
|
+
scorerId: score.scorerId
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
}
|
|
780
864
|
/**
|
|
781
865
|
* Force flush any buffered spans without shutting down the exporter.
|
|
782
866
|
* This is useful in serverless environments where you need to ensure spans
|